TF2/Keras 中自动编码器 MSE 损失函数的正确实现
Correct implementation of Autoencoder MSE loss function in TF2/Keras
谁能解释一下下面两个的区别?
根据 this and this 消息来源,假设一个具有实值输入的普通自动编码器,其损失函数应如下所示。换句话说,a) 对于示例中的每个元素,我们计算平方差,b) 我们对示例的所有元素进行求和,c) 我们对所有示例取平均值。
def MSE_custom(y_true, y_pred):
return tf.reduce_mean(
0.5 * tf.reduce_sum(
tf.square(tf.subtract(y_true, y_pred)),
axis=1
)
)
但是,在我看到的大多数实现中:autoencoder.compile(loss='mse', ...)
。
我看不出两者有什么相同之处。考虑这个例子:
y_true = [[0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 1.0, 1.0]]
y_pred = [[0.0, 0.8, 0.9], [0.5, 0.7, 0.6], [0.8, 0.7, 0.5]]
result1 = MSE_custom(y_true, y_pred) # 0.355
mse = tf.keras.losses.MeanSquaredError(reduction=tf.keras.losses.Reduction.AUTO)
result2 = mse(y_true, y_pred) # 0.237
我错过了什么?非常感谢!
如 Tensorflow documentation 中所述,MSE 是通过对张量大小(SUM
损失减少)或批量大小(SUM_OVER_BATCH_SIZE
损失减少)的平方误差求平均来计算的).下面的代码显示了一些如何复制 MSE 计算的示例。
import tensorflow as tf
y_true = [[0.0, 1.0, 0.0], [0.8, 0.9, 1.0], [1.0, 1.0, 1.0], [1.0, 0.0, 0.0]]
y_pred = [[0.0, 0.8, 0.9], [0.5, 0.7, 0.6], [0.8, 0.7, 0.5], [0.9, 0.1, 0.3]]
##############################################
# Loss reduction: "SUM"
##############################################
reduction = tf.keras.losses.Reduction.SUM
mse_1 = tf.keras.losses.MeanSquaredError(reduction=reduction)
print(mse_1(y_true, y_pred))
# tf.Tensor(0.54333335, shape=(), dtype=float32)
def MSE_1(y_true, y_pred):
x = tf.reduce_sum(tf.square(tf.subtract(y_true, y_pred)))
y = tf.cast(tf.shape(y_true)[1], tf.float32) # divide by the shape of the tensor
return tf.divide(x, y)
print(MSE_1(y_true, y_pred))
# tf.Tensor(0.54333335, shape=(), dtype=float32)
##############################################
# Loss reduction: "SUM_OVER_BATCH_SIZE"
##############################################
reduction = tf.keras.losses.Reduction.SUM_OVER_BATCH_SIZE
mse_2 = tf.keras.losses.MeanSquaredError(reduction=reduction)
print(mse_2(y_true, y_pred))
# tf.Tensor(0.13583334, shape=(), dtype=float32)
def MSE_2(y_true, y_pred):
x = tf.reduce_sum(tf.square(tf.subtract(y_true, y_pred)))
y = tf.cast(tf.multiply(tf.shape(y_true)[0], tf.shape(y_true)[1]), tf.float32) # divide by the size of the tensor
return tf.divide(x, y)
print(MSE_2(y_true, y_pred))
# tf.Tensor(0.13583334, shape=(), dtype=float32)
##############################################
# Loss reduction: "NONE"
##############################################
reduction = tf.keras.losses.Reduction.NONE
mse_3 = tf.keras.losses.MeanSquaredError(reduction=reduction)
print(mse_3(y_true, y_pred))
# tf.Tensor([0.28333333 0.09666666 0.12666667 0.03666667], shape=(4,), dtype=float32)
def MSE_3(y_true, y_pred):
x = tf.reduce_sum(tf.square(tf.subtract(y_true, y_pred)), axis=1)
y = tf.cast(tf.shape(y_true)[1], tf.float32) # divide by the shape of the tensor
return tf.divide(x, y)
print(MSE_3(y_true, y_pred))
# tf.Tensor([0.28333333 0.09666666 0.12666667 0.03666667], shape=(4,), dtype=float32)
# recover "SUM" loss reduction
print(tf.reduce_sum(mse_3(y_true, y_pred)))
# tf.Tensor(0.54333335, shape=(), dtype=float32)
print(tf.reduce_sum(MSE_3(y_true, y_pred)))
# tf.Tensor(0.54333335, shape=(), dtype=float32)
# recover "SUM_OVER_BATCH_SIZE" loss reduction
print(tf.divide(tf.reduce_sum(mse_3(y_true, y_pred)), tf.cast(tf.shape(y_true)[0], tf.float32)))
# tf.Tensor(0.13583334, shape=(), dtype=float32)
print(tf.divide(tf.reduce_sum(MSE_3(y_true, y_pred)), tf.cast(tf.shape(y_true)[0], tf.float32)))
# tf.Tensor(0.13583334, shape=(), dtype=float32)
有两点不同。
- Keras 在所有维度上的损失 平均值,即您的
reduce_sum
应替换为 reduce_mean
。
- Keras 损失不是乘以 0.5。
在你的例子中,你有三个维度,所以我们可以通过除以 3(以模拟平均)并乘以 2 来从你的结果中得到 Keras 损失。事实证明,0.355 * 2/3 == 0.237
(大致)。
这些变化可能会让你失望,但它们最终是无关紧要的,因为除以 N 和乘以 2 都是常数因子,因此也只为梯度提供常数因子。
编辑:以下计算应该得到与 Keras 损失相同的结果:
mse_custom = tf.reduce_mean((y_true - y_pred)**2)
为了简单起见,我使用重载的 Python 运算符而不是 TF 运算符 (subtract/square)。这只是一次对整个 2D 矩阵进行平均,这与计算轴 1 的平均值,然后在轴 0 上计算 that 的平均值相同。
谁能解释一下下面两个的区别?
根据 this and this 消息来源,假设一个具有实值输入的普通自动编码器,其损失函数应如下所示。换句话说,a) 对于示例中的每个元素,我们计算平方差,b) 我们对示例的所有元素进行求和,c) 我们对所有示例取平均值。
def MSE_custom(y_true, y_pred):
return tf.reduce_mean(
0.5 * tf.reduce_sum(
tf.square(tf.subtract(y_true, y_pred)),
axis=1
)
)
但是,在我看到的大多数实现中:autoencoder.compile(loss='mse', ...)
。
我看不出两者有什么相同之处。考虑这个例子:
y_true = [[0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 1.0, 1.0]]
y_pred = [[0.0, 0.8, 0.9], [0.5, 0.7, 0.6], [0.8, 0.7, 0.5]]
result1 = MSE_custom(y_true, y_pred) # 0.355
mse = tf.keras.losses.MeanSquaredError(reduction=tf.keras.losses.Reduction.AUTO)
result2 = mse(y_true, y_pred) # 0.237
我错过了什么?非常感谢!
如 Tensorflow documentation 中所述,MSE 是通过对张量大小(SUM
损失减少)或批量大小(SUM_OVER_BATCH_SIZE
损失减少)的平方误差求平均来计算的).下面的代码显示了一些如何复制 MSE 计算的示例。
import tensorflow as tf
y_true = [[0.0, 1.0, 0.0], [0.8, 0.9, 1.0], [1.0, 1.0, 1.0], [1.0, 0.0, 0.0]]
y_pred = [[0.0, 0.8, 0.9], [0.5, 0.7, 0.6], [0.8, 0.7, 0.5], [0.9, 0.1, 0.3]]
##############################################
# Loss reduction: "SUM"
##############################################
reduction = tf.keras.losses.Reduction.SUM
mse_1 = tf.keras.losses.MeanSquaredError(reduction=reduction)
print(mse_1(y_true, y_pred))
# tf.Tensor(0.54333335, shape=(), dtype=float32)
def MSE_1(y_true, y_pred):
x = tf.reduce_sum(tf.square(tf.subtract(y_true, y_pred)))
y = tf.cast(tf.shape(y_true)[1], tf.float32) # divide by the shape of the tensor
return tf.divide(x, y)
print(MSE_1(y_true, y_pred))
# tf.Tensor(0.54333335, shape=(), dtype=float32)
##############################################
# Loss reduction: "SUM_OVER_BATCH_SIZE"
##############################################
reduction = tf.keras.losses.Reduction.SUM_OVER_BATCH_SIZE
mse_2 = tf.keras.losses.MeanSquaredError(reduction=reduction)
print(mse_2(y_true, y_pred))
# tf.Tensor(0.13583334, shape=(), dtype=float32)
def MSE_2(y_true, y_pred):
x = tf.reduce_sum(tf.square(tf.subtract(y_true, y_pred)))
y = tf.cast(tf.multiply(tf.shape(y_true)[0], tf.shape(y_true)[1]), tf.float32) # divide by the size of the tensor
return tf.divide(x, y)
print(MSE_2(y_true, y_pred))
# tf.Tensor(0.13583334, shape=(), dtype=float32)
##############################################
# Loss reduction: "NONE"
##############################################
reduction = tf.keras.losses.Reduction.NONE
mse_3 = tf.keras.losses.MeanSquaredError(reduction=reduction)
print(mse_3(y_true, y_pred))
# tf.Tensor([0.28333333 0.09666666 0.12666667 0.03666667], shape=(4,), dtype=float32)
def MSE_3(y_true, y_pred):
x = tf.reduce_sum(tf.square(tf.subtract(y_true, y_pred)), axis=1)
y = tf.cast(tf.shape(y_true)[1], tf.float32) # divide by the shape of the tensor
return tf.divide(x, y)
print(MSE_3(y_true, y_pred))
# tf.Tensor([0.28333333 0.09666666 0.12666667 0.03666667], shape=(4,), dtype=float32)
# recover "SUM" loss reduction
print(tf.reduce_sum(mse_3(y_true, y_pred)))
# tf.Tensor(0.54333335, shape=(), dtype=float32)
print(tf.reduce_sum(MSE_3(y_true, y_pred)))
# tf.Tensor(0.54333335, shape=(), dtype=float32)
# recover "SUM_OVER_BATCH_SIZE" loss reduction
print(tf.divide(tf.reduce_sum(mse_3(y_true, y_pred)), tf.cast(tf.shape(y_true)[0], tf.float32)))
# tf.Tensor(0.13583334, shape=(), dtype=float32)
print(tf.divide(tf.reduce_sum(MSE_3(y_true, y_pred)), tf.cast(tf.shape(y_true)[0], tf.float32)))
# tf.Tensor(0.13583334, shape=(), dtype=float32)
有两点不同。
- Keras 在所有维度上的损失 平均值,即您的
reduce_sum
应替换为reduce_mean
。 - Keras 损失不是乘以 0.5。
在你的例子中,你有三个维度,所以我们可以通过除以 3(以模拟平均)并乘以 2 来从你的结果中得到 Keras 损失。事实证明,0.355 * 2/3 == 0.237
(大致)。
这些变化可能会让你失望,但它们最终是无关紧要的,因为除以 N 和乘以 2 都是常数因子,因此也只为梯度提供常数因子。
编辑:以下计算应该得到与 Keras 损失相同的结果:
mse_custom = tf.reduce_mean((y_true - y_pred)**2)
为了简单起见,我使用重载的 Python 运算符而不是 TF 运算符 (subtract/square)。这只是一次对整个 2D 矩阵进行平均,这与计算轴 1 的平均值,然后在轴 0 上计算 that 的平均值相同。