Keras 中的自定义损失函数 return 是批量的单个损失值还是训练批次中每个样本的一系列损失?
Should the custom loss function in Keras return a single loss value for the batch or an arrary of losses for every sample in the training batch?
我正在学习 tensorflow(2.3) 中的 keras API。在tensorflow网站上的这个guide中,我找到了一个自定义损失函数的例子:
def custom_mean_squared_error(y_true, y_pred):
return tf.math.reduce_mean(tf.square(y_true - y_pred))
此自定义损失函数中的 reduce_mean
函数将 return 一个标量。
这样定义损失函数对吗?据我所知,y_true
和 y_pred
形状的第一个维度是批量大小。我认为损失函数应该 return 批次中每个样本的损失值。所以损失函数应该给出一个形状为 (batch_size,)
的数组。但是上面的函数为整个批次给出了一个值。
也许上面的例子是错误的?谁能帮我解决这个问题?
p.s。 为什么我认为损失函数应该return一个数组而不是单个值?
我阅读了Modelclass的源代码。当您向 Model.compile()
方法提供损失函数(请注意它是 function,而不是损失 class)时,这个损失函数用来构造一个LossesContainer
对象,存储在Model.compiled_loss
中。这个传递给 LossesContainer
class 的构造函数的损失函数再次用于构造一个 LossFunctionWrapper
对象,该对象存储在 LossesContainer._losses
.
根据LossFunctionWrapperclass的源码,训练batch的整体损失值是通过LossFunctionWrapper.__call__()
方法计算的(继承自Loss
class),即return是整个batch的单个损失值。但是LossFunctionWrapper.__call__()
首先调用LossFunctionWrapper.call()
方法得到一个训练批次中每个样本的损失数组。然后对这些损失进行最终平均以获得整批的单个损失值。正是在LossFunctionWrapper.call()
方法中调用了提供给Model.compile()
方法的损失函数。
这就是为什么我认为自定义损失函数应该 return 一系列损失,而不是单个标量值。另外,如果我们为Model.compile()
方法写一个自定义Loss
class,那么我们自定义Loss
class的call()
方法也应该return 一个数组,而不是一个信号值。
我在 github 上打开了一个 issue。已确认自定义损失函数需要 return 每个样本一个损失值。该示例需要更新以反映这一点。
tf.math.reduce_mean
取批次的平均值,returns 取平均值。这就是为什么它是标量。
Tensorflow网站上给出的损失函数绝对正确。
def custom_mean_squared_error(y_true, y_pred):
return tf.math.reduce_mean(tf.square(y_true - y_pred))
在机器学习中,我们使用的loss是单个训练样例的loss之和,所以应该是一个标量值。 (因为对于所有示例,我们都使用单个网络,因此我们需要单个损失值来更新参数。)
关于制作损失容器:
当使用并行计算时,制作容器是一种更简单可行的方法来跟踪计算的损失指数,因为我们使用批次进行训练而不是整个训练集。
我在 github 上打开了一个 issue。已确认自定义损失函数需要 return 每个样本一个损失值。该示例将需要更新以反映这一点。
其实,据我所知,损失函数的return值的形状并不重要,即它可以是标量张量,也可以是每个样本一个或多个值的张量。重要的是它应该如何减少为标量值,以便它可以用于优化过程或显示给用户。为此,您可以在 Reduction
documentation.
中检查缩减类型
此外,这里是 compile
方法 documentation 对 loss
参数的说明,部分解决了这一点:
loss: String (name of objective function), objective function or tf.keras.losses.Loss
instance. See tf.keras.losses
. An objective function is any callable with the signature loss = fn(y_true,y_pred)
, where y_true
= ground truth values with shape = [batch_size, d0, .. dN]
, except sparse loss functions such as sparse categorical crossentropy where shape = [batch_size, d0, .. dN-1]
. y_pred
= predicted values with shape = [batch_size, d0, .. dN]
. It returns a weighted loss float tensor. If a custom Loss
instance is used and reduction is set to NONE
, return value has the shape [batch_size, d0, .. dN-1]
ie. per-sample or per-timestep loss values; otherwise, it is a scalar. If the model has multiple outputs, you can use a different loss on each output by passing a dictionary or a list of losses. The loss value that will be minimized by the model will then be the sum of all individual losses.
此外,值得注意的是 TF/Keras 中的大多数 built-in 损失函数通常在最后一个维度上减少(即 axis=-1
)。
对于那些怀疑 return 标量值的自定义损失函数是否有效的人:您可以 运行 以下代码段,您会发现模型会正确训练和收敛。
import tensorflow as tf
import numpy as np
def custom_loss(y_true, y_pred):
return tf.reduce_sum(tf.square(y_true - y_pred))
inp = tf.keras.layers.Input(shape=(3,))
out = tf.keras.layers.Dense(3)(inp)
model = tf.keras.Model(inp, out)
model.compile(loss=custom_loss, optimizer=tf.keras.optimizers.Adam(lr=0.1))
x = np.random.rand(1000, 3)
y = x * 10 + 2.5
model.fit(x, y, epochs=20)
维度可以因为多个通道而增加...但是,每个通道应该只有一个标量值用于损失。
我认为@Gödel 提出的问题是完全合法和正确的。自定义损失函数应该 return 每个样本的损失值。而且,@today 提供的解释也是正确的。最后,这完全取决于使用的减少的种类。
因此,如果使用 class API 创建损失函数,则自定义 class 中会自动继承缩减参数。使用其默认值“sum_over_batch_size”(这只是给定批次中所有损失值的平均值)。其他选项是“sum”,它计算总和而不是求平均值,最后一个选项是“none”,其中一个数组的损失值是 returned.
Keras 文档中还提到,当使用 model.fit()
时,这些缩减差异是不敬的,因为缩减随后由 TF/Keras 自动处理。
最后,还提到在创建自定义损失函数时,应该return编辑一组损失(单个样本损失)。它们的减少由框架处理。
链接:
我正在学习 tensorflow(2.3) 中的 keras API。在tensorflow网站上的这个guide中,我找到了一个自定义损失函数的例子:
def custom_mean_squared_error(y_true, y_pred):
return tf.math.reduce_mean(tf.square(y_true - y_pred))
此自定义损失函数中的 reduce_mean
函数将 return 一个标量。
这样定义损失函数对吗?据我所知,y_true
和 y_pred
形状的第一个维度是批量大小。我认为损失函数应该 return 批次中每个样本的损失值。所以损失函数应该给出一个形状为 (batch_size,)
的数组。但是上面的函数为整个批次给出了一个值。
也许上面的例子是错误的?谁能帮我解决这个问题?
p.s。 为什么我认为损失函数应该return一个数组而不是单个值?
我阅读了Modelclass的源代码。当您向 Model.compile()
方法提供损失函数(请注意它是 function,而不是损失 class)时,这个损失函数用来构造一个LossesContainer
对象,存储在Model.compiled_loss
中。这个传递给 LossesContainer
class 的构造函数的损失函数再次用于构造一个 LossFunctionWrapper
对象,该对象存储在 LossesContainer._losses
.
根据LossFunctionWrapperclass的源码,训练batch的整体损失值是通过LossFunctionWrapper.__call__()
方法计算的(继承自Loss
class),即return是整个batch的单个损失值。但是LossFunctionWrapper.__call__()
首先调用LossFunctionWrapper.call()
方法得到一个训练批次中每个样本的损失数组。然后对这些损失进行最终平均以获得整批的单个损失值。正是在LossFunctionWrapper.call()
方法中调用了提供给Model.compile()
方法的损失函数。
这就是为什么我认为自定义损失函数应该 return 一系列损失,而不是单个标量值。另外,如果我们为Model.compile()
方法写一个自定义Loss
class,那么我们自定义Loss
class的call()
方法也应该return 一个数组,而不是一个信号值。
我在 github 上打开了一个 issue。已确认自定义损失函数需要 return 每个样本一个损失值。该示例需要更新以反映这一点。
tf.math.reduce_mean
取批次的平均值,returns 取平均值。这就是为什么它是标量。
Tensorflow网站上给出的损失函数绝对正确。
def custom_mean_squared_error(y_true, y_pred):
return tf.math.reduce_mean(tf.square(y_true - y_pred))
在机器学习中,我们使用的loss是单个训练样例的loss之和,所以应该是一个标量值。 (因为对于所有示例,我们都使用单个网络,因此我们需要单个损失值来更新参数。)
关于制作损失容器:
当使用并行计算时,制作容器是一种更简单可行的方法来跟踪计算的损失指数,因为我们使用批次进行训练而不是整个训练集。
我在 github 上打开了一个 issue。已确认自定义损失函数需要 return 每个样本一个损失值。该示例将需要更新以反映这一点。
其实,据我所知,损失函数的return值的形状并不重要,即它可以是标量张量,也可以是每个样本一个或多个值的张量。重要的是它应该如何减少为标量值,以便它可以用于优化过程或显示给用户。为此,您可以在 Reduction
documentation.
此外,这里是 compile
方法 documentation 对 loss
参数的说明,部分解决了这一点:
loss: String (name of objective function), objective function or
tf.keras.losses.Loss
instance. Seetf.keras.losses
. An objective function is any callable with the signatureloss = fn(y_true,y_pred)
, wherey_true
= ground truth values with shape =[batch_size, d0, .. dN]
, except sparse loss functions such as sparse categorical crossentropy where shape =[batch_size, d0, .. dN-1]
.y_pred
= predicted values with shape =[batch_size, d0, .. dN]
. It returns a weighted loss float tensor. If a customLoss
instance is used and reduction is set toNONE
, return value has the shape[batch_size, d0, .. dN-1]
ie. per-sample or per-timestep loss values; otherwise, it is a scalar. If the model has multiple outputs, you can use a different loss on each output by passing a dictionary or a list of losses. The loss value that will be minimized by the model will then be the sum of all individual losses.
此外,值得注意的是 TF/Keras 中的大多数 built-in 损失函数通常在最后一个维度上减少(即 axis=-1
)。
对于那些怀疑 return 标量值的自定义损失函数是否有效的人:您可以 运行 以下代码段,您会发现模型会正确训练和收敛。
import tensorflow as tf
import numpy as np
def custom_loss(y_true, y_pred):
return tf.reduce_sum(tf.square(y_true - y_pred))
inp = tf.keras.layers.Input(shape=(3,))
out = tf.keras.layers.Dense(3)(inp)
model = tf.keras.Model(inp, out)
model.compile(loss=custom_loss, optimizer=tf.keras.optimizers.Adam(lr=0.1))
x = np.random.rand(1000, 3)
y = x * 10 + 2.5
model.fit(x, y, epochs=20)
维度可以因为多个通道而增加...但是,每个通道应该只有一个标量值用于损失。
我认为@Gödel 提出的问题是完全合法和正确的。自定义损失函数应该 return 每个样本的损失值。而且,@today 提供的解释也是正确的。最后,这完全取决于使用的减少的种类。
因此,如果使用 class API 创建损失函数,则自定义 class 中会自动继承缩减参数。使用其默认值“sum_over_batch_size”(这只是给定批次中所有损失值的平均值)。其他选项是“sum”,它计算总和而不是求平均值,最后一个选项是“none”,其中一个数组的损失值是 returned.
Keras 文档中还提到,当使用 model.fit()
时,这些缩减差异是不敬的,因为缩减随后由 TF/Keras 自动处理。
最后,还提到在创建自定义损失函数时,应该return编辑一组损失(单个样本损失)。它们的减少由框架处理。
链接: