Notes
keras 中不同形状的 loss 相加,如标量(scalar)和向量(vector)相加,会产生广播,可能同 python 和 numpy 中的广播规则差不多。
写 loss 时这会影响到不同损失项之间的 trade-off 权重。如:
- 一个用了
loss_1 = K.sum(...)
对所有维度都求和了,得一个scalar - 另一个用了
loss_2 = K.sum(..., axis=-1)
只对最后一个维度求和,得到的是 vector
这时loss = loss_1 + lambda * loss_2
会先将 loss_1 扩展成同 loss_2 一样形状的向量,再求和,相当于把 loss_1 乘多了一个 batch_size 的权重,或者把 loss_2 除了个 batch_size 的权重,可能会导致模型真正的 trade-off 系数同想像中的不一样。
(我另外的一个实验表明 keras 对总 loss 沿 batch 那一维取mean
而不是取sum
)
Experiment
code
其中:
loss_struct
是 scalarloss_cosine
是 vector
import tensorflow as tf
import numpy as np
import keras
import keras.backend as K
from keras.layers import Input
from keras.losses import cosine
# 随便的例子
x = Input(tensor=tf.convert_to_tensor(
np.array([[3., -2., 0.5], [0., -1., 10.]])))
gnd = Input(tensor=tf.convert_to_tensor(
np.array([[19., -8., 0.001], [0.2, 2.0, 3.4]])))
all_x = Input(tensor=tf.convert_to_tensor(
np.array([[0.7, -1., 3.], [-3., -2., 1.]])))
s_big = Input(tensor=tf.convert_to_tensor(
np.array([[0., 1.], [1., 1.]])))
# loss_struct 全维度求和 -> scalar
theta = 0.5 * K.dot(x, K.transpose(all_x))
s_bat = s_big * 2. - 1.
loss_struct = - K.sum(K.log(0.5 * (1. - s_bat) +
s_bat * K.sigmoid(theta) + 1e-9))
# loss_cosine 只求和 axis = -1 -> vector
loss_cosine = cosine(x, gnd)
# scalar + vector
loss = loss_struct + 0.5 * loss_cosine
print(loss_struct) # Tensor("Neg:0", shape=(), dtype=float64)
print(loss_cosine) # Tensor("Neg_1:0", shape=(2,), dtype=float64)
print(loss) # Tensor("add_2:0", shape=(2,), dtype=float64)
with tf.Session() as sess:
print(loss.eval()) # [4.72531537 4.80862989]
print(loss_cosine.eval()) # [-0.9727997 -0.80617067]
print(loss_struct.eval()) # 5.2117152259467066
result
可以看出,把 loss_struct 的值分别加到 0.5 * loss_consine 的各个分量上,就得到总 loss 的各个分量。