使用自定义损失函数,我很难让回归器正常工作。我目前正在使用几个数据集,其中包含用于跨精度计算基准实验的数据,这是其中一个的片段:
| var_0 | var_1 | var_2 | var_3 | err_ds_0 | err_ds_1 | err_ds_2 | err_ds_3 | err_ds_4 | err_mean | err_std |
|-------|-------|-------|-------|---------------|---------------|---------------|---------------|---------------|----------------|-------------------|
| 27 | 45 | 35 | 40 | 16.0258634564 | 15.9905086513 | 15.9665402702 | 15.9654006879 | 15.9920739469 | 15.98807740254 | 0.02203520210917 |
| 42 | 23 | 4 | 10 | 0.82257142551 | 0.91889119458 | 0.93573069325 | 0.81276879271 | 0.87065388914 | 0.872123199038 | 0.049423964650445 |
| 7 | 52 | 45 | 4 | 2.39566262913 | 2.4233107563 | 2.45756544291 | 2.37961745294 | 2.42859839621 | 2.416950935498 | 0.027102139332226 |
(提前为降价表抱歉,找不到更好的方法来做到这一点)
每个 err_ds_* 列都是使用指定的 var_* 配置从不同的基准执行获得的(每个 var 包含用于特定变量的精度位数);每个错误单元格实际上包含错误的负自然对数(因为实际值非常小),并且每行的 err_mean 和 err_std 都是根据这些值计算的。
在网络数据准备期间,我重塑了数据集,以便将每个基准测试作为单独的行执行(这意味着我们将有多个行具有相同的 var_* 值,但错误值不同);然后我将数据(我们通常给 fit 函数的东西作为 x)和目标(我们通常给 fit 函数的东西作为 y)分开,因此分别获得:
| var_0 | var_1 | var_2 | var_3 |
|-------|-------|-------|-------|
| 27 | 45 | 35 | 40 |
| 27 | 45 | 35 | 40 |
| 27 | 45 | 35 | 40 |
| 27 | 45 | 35 | 40 |
| 27 | 45 | 35 | 40 |
| 42 | 23 | 4 | 10 |
| 42 | 23 | 4 | 10 |
| 42 | 23 | 4 | 10 |
| 42 | 23 | 4 | 10 |
| 42 | 23 | 4 | 10 |
| 7 | 52 | 45 | 4 |
| 7 | 52 | 45 | 4 |
| 7 | 52 | 45 | 4 |
| 7 | 52 | 45 | 4 |
| 7 | 52 | 45 | 4 |
和
| log_err |
|---------------|
| 16.0258634564 |
| 15.9905086513 |
| 15.9665402702 |
| 15.9654006879 |
| 15.9654006879 |
| 0.82257142551 |
| 0.91889119458 |
| 0.93573069325 |
| 0.81276879271 |
| 0.87065388914 |
| 2.39566262913 |
| 2.4233107563 |
| 2.45756544291 |
| 2.37961745294 |
| 2.42859839621 |
最后,我们再次拆分集合以获得训练数据(我们将调用train_data_regr
and train_target_tensor
)和测试数据(我们将调用test_data_regr
and test_target_tensor
),所有这些都使用scaler_regr_*.fit_transform(df)
(其中 scaler_regr.* 是 StandardScaler( ) 来自 sklearn.preprocessing),并输入网络:
n_features = train_data_regr.shape
input_shape = (train_data_regr.shape[1],)
pred_model = Sequential()
# Input layer
pred_model.add(Dense(n_features * 3, activation='relu',
activity_regularizer=regularizers.l1(1e-5), input_shape=input_shape))
# Hidden dense layers
pred_model.add(Dense(n_features * 8, activation='relu',
activity_regularizer=regularizers.l1(1e-5)))
pred_model.add(Dense(n_features * 4, activation='relu',
activity_regularizer=regularizers.l1(1e-5)))
# Output layer (two neurons, one for the mean, one for the std)
pred_model.add(Dense(2, activation='linear'))
# Loss function
def neg_log_likelihood_loss(y_true, y_pred):
sep = y_pred.shape[1] // 2
mu, logvar = y_pred[:, :sep], y_pred[:, sep:]
return K.sum(0.5*(logvar+np.log(2*np.pi)+K.square((y_true-mu)/K.exp(0.5*logvar))), axis=-1)
# Callbacks
early_stopping = EarlyStopping(
monitor='val_loss', patience=10, min_delta=1e-5)
reduce_lr = ReduceLROnPlateau(
monitor='val_loss', patience=5, min_lr=1e-5, factor=0.2)
terminate_nan = TerminateOnNaN()
# Compiling
adam = optimizers.Adam(lr=0.001, decay=0.005)
pred_model.compile(optimizer=adam, loss=neg_log_likelihood_loss)
# Training
history = pred_model.fit(train_data_regr, train_target_tensor,
epochs=20, batch_size=64, shuffle=True,
validation_split=0.1, verbose=True,
callbacks=[early_stopping, reduce_lr, terminate_nan])
predicted = pred_model.predict(test_data_regr)
actual = test_target_regr
actual_rescaled = scaler_regr_target.inverse_transform(actual)
predicted_rescaled = scaler_regr_target.inverse_transform(predicted)
test_data_rescaled = scaler_regr_data.inverse_transform(test_data_regr)
最后,通过自定义函数评估获得的数据,该函数将实际数据与预测数据(即真实均值与预测均值和真实标准与预测标准)与多个指标(如 MAE 和 MSE)进行比较,并使用 matplotlib 绘制结果。
这个想法是,给定 var_* 配置作为输入,网络的两个输出将预测错误的均值和标准差。
现在,让我们提出一个问题:因为使用这段代码,我在预测平均值时得到了非常好的结果(即使使用不同的基准测试),但在预测标准差时结果很糟糕,我想问这是否正确预测这两个值的方法。我敢肯定我在这里遗漏了一些非常基本的东西,但两周后我认为我被卡住了。