TensorFlow使用pearsonr作损失时首轮epoch损失大于1的原因
DNN使用皮尔逊相关系数作为损失首轮训练值大于1的问题排查
自定义损失实现与离线校验
目标是在深度网络中使用皮尔逊相关系数作为损失函数,参照scipy.stats.pearsonr逻辑实现的自定义损失如下,离线测试时计算结果与scipy官方接口输出几乎完全一致,函数逻辑验证正确:
def correlationLoss2(x, y): x = tf.convert_to_tensor(x) y = math_ops.cast(y, x.dtype) xmean = tf.reduce_mean(x) ymean = tf.reduce_mean(y) xsqsum = tf.reduce_sum( tf.math.squared_difference(x, xmean)) ysqsum = tf.reduce_sum( tf.math.squared_difference(y, ymean)) cov = tf.reduce_sum( (x - xmean) * (y - ymean)) corr = cov / tf.sqrt(xsqsum * ysqsum) return - corr x = data[:, 22] y = data[:, -1] print(- float(correlationLoss(x, y))) print(stats.pearsonr(x, y)[0])
校验输出(x、y为项目自有数据集):
-0.017431042527298025 -0.017431042527298084
训练异常表现
损失函数接入网络训练后,最终训练效果符合预期,但首轮epoch输出的损失值大于1,不符合皮尔逊相关系数对应损失的[-1, 1]取值范围,评估指标采用1-corr,首轮及后续训练日志如下:
Epoch 1/50 196/196 [==============================] - 35s 172ms/step - loss: 1.0501 - correlationMetric: 0.8766 - val_loss: -0.0295 - val_correlationMetric: 0.9123 Epoch 2/50 196/196 [==============================] - 11s 55ms/step - loss: -0.1150 - correlationMetric: 0.8465 - val_loss: -0.0598 - val_correlationMetric: 0.9115 Epoch 3/50 196/196 [==============================] - 11s 56ms/step - loss: -0.1299 - correlationMetric: 0.8352 - val_loss: -0.0674 - val_correlationMetric: 0.9061 Epoch 4/50 196/196 [==============================] - 10s 53ms/step - loss: -0.1383 - correlationMetric: 0.8270 - val_loss: -0.0734 - val_correlationMetric: 0.9013
使用的网络结构代码:
def get_model(): features_inputs = tf.keras.Input((Config.input_size, ), dtype=tf.float32) gn = tf.keras.layers.GaussianNoise(0.035)(features_inputs) feature_x = layers.Dense(256, activation='swish')(gn) feature_x = layers.Dropout(0.5)(feature_x) feature_x = layers.Dense(512, activation='swish', kernel_regularizer="l2")(feature_x) feature_x = layers.Dropout(0.44)(feature_x) feature_x = layers.Dense(128, activation='swish', kernel_regularizer="l2")(feature_x) feature_x = layers.Dropout(0.33)(feature_x) feature_x = layers.Dense(32, activation='swish', kernel_regularizer="l2")(feature_x) feature_x = layers.Dropout(0.3)(feature_x) output = layers.Dense(1)(feature_x) model = tf.keras.Model(inputs=[features_inputs], outputs=[output]) model.compile(optimizer=tf.optimizers.Adam(0.001), loss=correlationLoss2, metrics=[correlationMetric]) return model
首轮损失大于1的根本原因
- Keras训练过程中打印的
loss是自定义皮尔逊损失 + 所有层正则化惩罚项的总和,不是单纯的皮尔逊损失值。网络中3层Dense加了L2正则,首轮模型权重为随机初始化值,权重数值整体偏大,L2正则计算的是权重平方和乘以正则系数,这部分惩罚值很容易超过1,直接将总loss抬到1以上。后续训练过程中权重被优化缩小,L2惩罚值降低,总loss就回到了皮尔逊损失对应的[-1,1]区间。 - 训练阶段启用的GaussianNoise、Dropout层仅在训练时生效,会给前向传播过程加入随机扰动,也会让单批次计算的皮尔逊损失和离线全量计算结果有小幅偏差,但这部分影响远小于L2正则的贡献。
- 验证方式很简单:临时去掉所有Dense层的
kernel_regularizer="l2"参数再跑训练,首轮loss就会落在皮尔逊损失的正常取值区间;也可以单独打印训练步的原始损失和正则项数值,能直接看到首轮超出1的部分全部来自L2正则惩罚。
内容的提问来源于stack exchange,提问作者Mengeshall Wu
相关产品推荐
相关产品推荐

