我是Keras的新手。我需要一些帮助,使用TensorFlow后端在keras中编写自定义损失函数,以获得以下损失方程。
传递给损失函数的参数是:
y_true
形状(batch_size, N, 2)
。在这里,我们在批次中的每个样本中传递N (x, y)
个坐标。 y_pred
的形状为(batch_size, 256, 256, N)
。在这里,我们在批次中的每个样本中传递了256 x 256
像素的N个预测热图。 i
∈[0, 255]
j
∈[0, 255]
Mn(i, j)
表示n th 预测热图的像素位置(i, j)
处的值。
Mn∼(i, j) = Guassian2D((i, j), y_truen, std)
其中
std = standard deviation
,两个维度的标准偏差相同(5 px)。
y_true n 是n th (x,y)坐标。这就是平均值。
有关详情,请查看本文所述的l 2 损失 Human Pose Estimation
注意:我在y_true和y_pred的形状中提到了batch_size。我假设Keras称整个批次的损失函数,而不是批次中的单个样本。如果我错了,请纠正我。
def l2_loss(y_true, y_pred):
loss = 0
n = y_true.shape[0]
for j in range(n):
for i in range(num_joints):
yv, xv = tf.meshgrid(tf.arange(0, im_height), tf.arange(0, im_width))
z = np.array([xv, yv]).transpose(1, 2, 0)
ground = np.exp(-0.5*(((z - y_true[j, i, :])**2).sum(axis=2))/(sigma**2))
loss = loss + np.sum((ground - y_pred[j,:, :, i])**2)
return loss/num_joints
这是我到目前为止编写的代码。我知道这不会运行,因为我们无法在keras损失函数中使用直接numpy ndarray。另外,我需要消除循环!
答案 0 :(得分:7)
您几乎可以将numpy函数转换为Keras后端函数。唯一需要注意的是设置正确的广播形状。
def l2_loss_keras(y_true, y_pred):
# set up meshgrid: (height, width, 2)
meshgrid = K.tf.meshgrid(K.arange(im_height), K.arange(im_width))
meshgrid = K.cast(K.transpose(K.stack(meshgrid)), K.floatx())
# set up broadcast shape: (batch_size, height, width, num_joints, 2)
meshgrid_broadcast = K.expand_dims(K.expand_dims(meshgrid, 0), -2)
y_true_broadcast = K.expand_dims(K.expand_dims(y_true, 1), 2)
diff = meshgrid_broadcast - y_true_broadcast
# compute loss: first sum over (height, width), then take average over num_joints
ground = K.exp(-0.5 * K.sum(K.square(diff), axis=-1) / sigma ** 2)
loss = K.sum(K.square(ground - y_pred), axis=[1, 2])
return K.mean(loss, axis=-1)
验证它:
def l2_loss_numpy(y_true, y_pred):
loss = 0
n = y_true.shape[0]
for j in range(n):
for i in range(num_joints):
yv, xv = np.meshgrid(np.arange(0, im_height), np.arange(0, im_width))
z = np.stack([xv, yv]).transpose(1, 2, 0)
ground = np.exp(-0.5*(((z - y_true[j, i, :])**2).sum(axis=2))/(sigma**2))
loss = loss + np.sum((ground - y_pred[j,:, :, i])**2)
return loss/num_joints
batch_size = 32
num_joints = 10
sigma = 5
im_width = 256
im_height = 256
y_true = 255 * np.random.rand(batch_size, num_joints, 2)
y_pred = 255 * np.random.rand(batch_size, im_height, im_width, num_joints)
print(l2_loss_numpy(y_true, y_pred))
45448272129.0
print(K.eval(l2_loss_keras(K.variable(y_true), K.variable(y_pred))).sum())
4.5448e+10
该数字在默认的dtype
float32下被截断。如果在dtype
设置为float64:
y_true = 255 * np.random.rand(batch_size, num_joints, 2)
y_pred = 255 * np.random.rand(batch_size, im_height, im_width, num_joints)
print(l2_loss_numpy(y_true, y_pred))
45460126940.6
print(K.eval(l2_loss_keras(K.variable(y_true), K.variable(y_pred))).sum())
45460126940.6
修改强>
似乎Keras要求y_true
和y_pred
具有相同数量的维度。例如,在以下测试模型上:
X = np.random.rand(batch_size, 256, 256, 3)
model = Sequential([Dense(10, input_shape=(256, 256, 3))])
model.compile(loss=l2_loss_keras, optimizer='adam')
model.fit(X, y_true, batch_size=8)
ValueError: Cannot feed value of shape (8, 10, 2) for Tensor 'dense_2_target:0', which has shape '(?, ?, ?, ?)'
要解决此问题,您可以在将expand_dims
提供给模型之前添加y_true
的虚拟维度:
def l2_loss_keras(y_true, y_pred):
...
y_true_broadcast = K.expand_dims(y_true, 1) # change this line
...
model.fit(X, np.expand_dims(y_true, axis=1), batch_size=8)
答案 1 :(得分:0)
Keras的最新版本实际上支持y_pred
和y_true
不同形状的损失。内建损失sparse_categorical_crossentropy
就是一个例子。这种损失的TensorFlow实现在这里:https://github.com/keras-team/keras/blob/0fc33feb5f4efe3bb823c57a8390f52932a966ab/keras/backend/tensorflow_backend.py#L3570
请注意target: An integer tensor.
,而不是target: A tensor of the same shape as `output`.
。我尝试了使自己丧生的自定义损失,但似乎效果很好。
我正在使用Keras 2.2.4。
答案 2 :(得分:0)
我仍然想分享我的经验,于的答案是正确的。每当您要编写自定义损失函数时,请注意以下几点:
y_pred
具有3D形状,则y_true
将默认为3D形状。但是,在运行时(即fit
期间),如果您像许多人一样以二维方式传递目标数据,则可能最终会导致损失函数出错。例如,如果您正在计算sigmoid_crossentropy_with_logits
,它将抱怨。因此,请通过np.expand_dims将目标作为3-D传递。 y_true
和y_pred
作为参数(如果有人知道我们可以使用其他任何名称作为参数,请大喊)。