在Keras中设计自定义损失函数(在Keras中以张量为单位的索引)

时间:2019-01-30 19:22:42

标签: python tensorflow keras

原始问题

我正在尝试在Keras中设计自定义损失函数。目标损失函数类似于Kears中的“ mean_squared_error”,如下所示。

y_true和y_pred的形状为[batch_size,system_size],而system_size是整数,例如system_size =5。y_true和y_pred中的元素在[-1,1]的范围内。在计算损失之前,我需要根据y_true的最大绝对值的符号和y_pred中的对应值来更改每个样本的y_pred的符号。 对于每个样本,我需要首先选择最大绝对值的索引(假设索引为i)。如果y_pred [:,i]与y_true [:,i]具有相同的符号,则损失为正常的“ mean_squared_error”。如果y_pred [:,i]的符号与y_true [:,i]的符号不同,则y_pred中此样本的所有元素都乘以-1。

我尝试了以下函数来定义损失。但是它不起作用。

def normalized_mse(y_true, y_pred):

    y_pred = K.l2_normalize(y_pred, axis = -1) # normalize the y_pred

    loss_minus = K.square(y_true - y_pred)
    loss_plus = K.square(y_true + y_pred) 

    loss = K.mean(tf.where(tf.greater(
                            tf.div(y_true[:, K.argmax(K.abs(y_true), axis = -1))],
                            y_pred[:, K.argmax(K.abs(y_true), axis = -1))]), 0), 
                       loss_minus, loss_plus), axis = -1)

    return loss

如果我用整数替换“ K.argmax(K.abs(y_true),轴= -1))”,则该函数运行良好。似乎该命令在y_pred中选择最大绝对值的索引是有问题的。

您是否遇到过此类问题?您能给我一些有关这个问题的建议和指导吗?

非常感谢您。

艾尔文

已解决

由于@AnnaKrogager的指导,此问题已解决。如下所述,K.argmax返回张量而不是整数。根据@AnnaKrogager的回答,我将损失函数修改为

def normalized_mse(y_true, y_pred):

    y_pred = K.l2_normalize(y_pred, axis = -1)
    y_true = K.l2_normalize(y_true, axis = -1)

    loss_minus = K.square(y_pred - y_true)
    loss_plus = K.square(y_pred + y_true)

    index = K.argmax(K.abs(y_true), axis = -1)
    y_true_slice = tf.diag_part(tf.gather(y_true, index, axis = 1))
    y_pred_slice = tf.diag_part(tf.gather(y_pred, index, axis = 1))

    loss = K.mean(tf.where(tf.greater(tf.div(y_true_slice, y_pred_slice), 0), 
                       loss_minus, loss_plus), axis = -1)

    return loss

为了验证它,我用numpy定义了另一个函数

def normalized_mse_numpy(y_true, y_pred):
    import operator

    batch_size = y_true.shape[0]
    sample_size = y_true.shape[1]
    loss = np.zeros((batch_size))

    for i in range(batch_size):
        index = np.argmax(abs(y_true[i, :]))
        y_pred[i, :] = y_pred[i, :]/linalg.norm(y_pred[i, :])
        y_true[i, :] = y_true[i, :]/linalg.norm(y_true[i, :])

        sign_flag = y_true[i, index] / y_pred[i, index]
        if sign_flag < 0:
           for j in range(sample_size):
               loss[i] = loss[i] + (y_true[i, j] + y_pred[i, j])**2
        else:
           for j in range(sample_size):
               loss[i] = loss[i] + (y_true[i, j] - y_pred[i, j])**2

        loss[i] = loss[i] / SystemSize

     return loss

SystemSize = 5
batch_size = 10
sample_size = 5
y_true = 100 * np.random.rand(batch_size, sample_size)
y_pred = 100 * np.random.rand(batch_size, sample_size)

numpy_result = normalized_mse_numpy(y_true, y_pred)
keras_result = K.eval(normalized_mse(K.variable(y_true), K.variable(y_pred)))

print(numpy_result.sum())
0.9979743490342015

print(keras_result.sum())
0.9979742

numpy_result - keras_result
array([ 4.57889131e-08,  1.27995520e-08,  5.66398740e-09,  1.07868497e-08,
    4.41975839e-09,  7.89889471e-09,  6.68819598e-09,  1.05113101e-08,
   -9.91241045e-09, -1.20345756e-09])

我也受益于Yu-Yang在Implementing custom loss function in keras with different sizes for y_true and y_pred中的回答。

请注意,tf.gather()在某些早期版本的张量流(例如1.0.1)中不支持``轴''。它适用于1.11.0。如果tensorflow版本低,您可能会得到错误"gather() got an unexpected keyword argument 'axis'"

1 个答案:

答案 0 :(得分:0)

问题在于K.argmax(K.abs(y_pred), axis = -1))是张量,而不是整数,因此切片不起作用。您可以改用tf.gather进行切片:

index = K.argmax(K.abs(y_true), axis = -1)
y_true_slice = tf.diag_part(tf.gather(y, index, axis=1))

这等效于y_true[:,index]