Мой набор данных - это одномерный временной ряд со многими пропущенными значениями.Есть 120 строк, в каждой строке 211 временных отметок (в днях).Я тренирую автоэнкодер LSTM для вменения значимых значений.Исходный набор данных находится в диапазоне 0-10.Я все раз с 0,1, чтобы нормализовать данные в диапазоне [0,1].Я использовал Учебное пособие по автоэнкодеру и Учебное пособие по автоэнкодеру , чтобы построить сеть.Поскольку нет реальной метки для пропущенного значения, я сначала использую линейную интерполяцию для вменения значений, притворяюсь, что это правда, и устанавливаю пропорцию пропущенных значений, чтобы проверить производительность авто-кодировщика.Я добавил все пропущенные значения с константой (в данном случае среднее).Я оцениваю только среднюю абсолютную ошибку по наблюдаемым значениям (не учитываю пропущенное значение) для потери обучения.Для ошибки тестирования я оцениваю среднюю абсолютную ошибку только для пропущенных значений.
Гиперпараметры:
- recurrent_weight = 0.5 # для каждой эпохи, насколько я хочу сбрасывать со счетов предыдущий прогноз (новое значение = 0,5 * предыдущее прогнозирование + 0,5 * текущее прогнозирование
- один скрытый слой с latent_dim = 32
- оптимизатор Adam с lr = 0,01
- batch_size = 30
- эпоха = 20
- фракция = 0,5 (доля значений, установленных как пропущенные)
Я пробовал различные значения для этих параметров, результат в значительной степени одинаков.что-нибудь еще, чего я здесь упускаю, что приводило к тому, что тренировочные и проверочные потери не менялись? Почему тренировочные потери настолько малы?
настраиваемая функция обучения и проверки потерь:
def make_reconstruction_loss(n_features):
'''customized training loss function: only MSE for observed values (values not marked 1 in missing_mask)'''
def reconstruction_loss(input_and_mask, y_pred):
X_values = input_and_mask[:, :n_features,:]
missing_mask = input_and_mask[:, n_features:,:]
observed_mask = 1 - missing_mask
X_values_observed = X_values * observed_mask
y_pred = y_pred[:, :n_features,:]
pred_observed = y_pred * observed_mask
return mean_absolute_error(y_true=X_values_observed, y_pred=pred_observed)
return reconstruction_loss
def masked_mae(X_true, X_pred, mask): #opposite of reconstruction_loss, MAE of only missing values
masked_diff = X_true[mask] - X_pred[mask]
return np.mean(np.abs(masked_diff))
Класс автоэнкодера:
class Autoencoder:
def __init__(self, data,
recurrent_weight=0.5,
optimizer= Adam(lr=0.01),
latent_dim = 32,
n_features = 1):
self.data = data.copy()
self.recurrent_weight = recurrent_weight
self.optimizer = optimizer
self.latent_dim = latent_dim
self.n_features = n_features
def _create_model(self):
timesteps = self.data.shape[1]*2 #211*2 == n_dims
inputs = Input(shape=(timesteps, self.n_features)) #number of days, pain level is the only feature so n_features == 1
encoded = LSTM(self.latent_dim, return_sequences=False, name="encoder")(inputs)
encoder = Model(inputs, encoded)
print(encoder.summary())
decoded = RepeatVector(timesteps)(encoded) ##Repeats the provided 2D input multiple times to create a 3D output.
decoded = LSTM(self.n_features, return_sequences=True, name='decoder')(decoded)
autoencoder = Model(inputs, decoded)
loss_function = make_reconstruction_loss(n_features =self.data.shape[1]) # n_features = 211
autoencoder.compile(optimizer=self.optimizer, loss=loss_function)
return autoencoder
def _create_missing_mask(self, fraction): #a matrix of binary value, 1= missing, 0 = observed
missing_m = actual_missing_mask.values
idx = np.flatnonzero(missing_m) # get the nonzero indices
totalnum_missing = np.count_nonzero(missing_m!=0)
print(totalnum_missing)
N = int(np.count_nonzero(missing_m!=0)*(1-fraction))
np.put(missing_m,np.random.choice(idx,size=N,replace=False),0) #replace (1-frac)% of to be zero(not missing)
newnum_missing = np.count_nonzero(missing_m!=0)
print('percentage of missing value',newnum_missing/totalnum_missing)
return missing_m
def fill(self, missing_mask):
self.data[missing_mask] = overall_mean # fill NA with overall_mean
def _train_epoch(self, model, missing_mask, batch_size):
input_with_mask = np.hstack([self.data, missing_mask]) # append two matrix side by side
n_samples = len(input_with_mask)
n_batches = int(np.ceil(n_samples / batch_size))
timesteps = self.data.shape[1]*2
X_reshaped = input_with_mask.reshape((input_with_mask.shape[0],timesteps,self.n_features)) #shape (112, 422, 1)
for batch_idx in range(n_batches):
batch_start = batch_idx * batch_size
batch_end = (batch_idx + 1) * batch_size
batch_data = X_reshaped[batch_start:batch_end, :,:]
loss = model.train_on_batch(batch_data, batch_data) #all missing values == mean now
print('train loss =', loss*10) #normalize back
return model.predict(X_reshaped)
def train(self, train_epochs = 10, batch_size=256):
missing_mask = self._create_missing_mask(fraction = 0.5) #! fraction # a binary matrix of (missing = 1, else 0)
missing_mask = missing_mask + earlydeath_mask #! don't account early death into loss
self.fill(missing_mask = missing_mask)
self.data = self.data*0.1 #normalize
self.model = self._create_model()
for epoch in range(train_epochs):
print('epoch', epoch)
X_pred = self._train_epoch(self.model, missing_mask, batch_size) #* error
X_pred = X_pred.reshape(X_pred.shape[0], X_pred.shape[1]) #reshape for mased_mae dimension match, from 3D back to 2D
X_pred = X_pred[:, :211] #remove the 2nd half binary matrix, left with original data
missing_mae = masked_mae(X_true=myinput.values,
X_pred=X_pred*10,
mask=missing_mask-earlydeath_mask) #all numpy array, shape=(112, 211)
print("missing mae:", missing_mae)
#all missing values which is the overall_mean now divide by half + half of prediction
old_weight = (1.0 - self.recurrent_weight)
self.data[missing_mask] *= old_weight
pred_missing = X_pred[missing_mask]
self.data[missing_mask] += self.recurrent_weight * pred_missing
self.data = self.data * 10 #normalize back
return self.data.copy(), missing_mask
Поезд:
np.random.seed(1337) # for reproducibility
imputer = Autoencoder(myinput.values)
output, missing_mask = imputer.train(train_epochs=20, batch_size=30)