当在Keras模型中定义自定义损失函数时,网络资源似乎表明损失函数应返回一个值数组(批次中每个样本的损失)。类似于这样
def custom_loss_function(y_true, y_pred): squared_difference = tf.square(y_true - y_pred) return tf.reduce_mean(squared_difference, axis=-1)model.compile(optimizer='adam', loss=custom_loss_function)
在上面的例子中,我不知道模型何时或是否使用tf.reduce_sum()
或tf.reduce_mean()
来计算批次的总和或均值
在另一种情况下,当我们想要实现带有自定义函数的自定义训练循环时,根据Keras文档应遵循的模板是这样的
for epoch in range(epochs): for step, (x_batch_train, y_batch_train) in enumerate(train_dataset): with tf.GradientTape() as tape: y_batch_pred = model(x_batch_train, training=True) loss_value = custom_loss_function(y_batch_train, y_batch_pred) grads = tape.gradient(loss_value, model.trainable_weights) optimizer.apply_gradients(zip(grads, model.trainable_weights))
所以,按照书本,如果我理解正确的话,我们应该取批次梯度的均值。因此,上面的损失值应该是每个批次的一个单一值。
然而,该示例可以同时适用于以下两种变化:
tf.reduce_mean(squared_difference, axis=-1) # 每个样本的损失数组
tf.reduce_mean(squared_difference) # 批次的均值损失
那么,为什么上面的第一个选项(数组损失)仍然有效?apply_gradients
是否对每个值顺序应用小的变化?虽然它能工作,但这是错误的吗?
没有自定义循环和有自定义循环时,正确的方式是什么?
回答:
很好的问题。在我看来,这在TensorFlow/Keras API中没有得到很好的文档说明。默认情况下,如果您不提供标量loss_value
,TensorFlow将将它们加总(且更新不是顺序进行的)。本质上,这相当于沿着批次轴对损失进行求和。
目前,TensorFlow API中的损失函数包括一个reduction
参数(例如,tf.losses.MeanSquaredError),允许指定如何沿批次轴聚合损失。