【问题标题】:Correlation-based loss function for sequence labelling in KerasKeras 中基于相关性的序列标记损失函数
【发布时间】:2018-10-15 05:36:46
【问题描述】:

我有一个关于在 Keras(Tensorflow 后端)中为序列标记任务实现基于相关性的损失函数的问题。

假设我们有一个序列标记问题,例如,输入是一个形状为 (20,100,5) 的张量,输出是一个形状为 (20,100,1) 的张量。 在文档中写道,损失函数需要返回一个“每个数据点的标量”。默认的 MSE 损失对形状为 (20,100,1) 的张量之间的损失所做的是返回一个形状为 (20,100) 的损失张量。

现在,如果我们对每个序列使用基于相关系数的损失函数,理论上,我们将只得到每个序列的单个值,即形状为 (20,) 的张量。

但是,在 Keras 中使用 this 作为损失函数时,fit() 会返回错误,因为预期形状为 (20,100) 的张量。 另一方面,我也没有错误

  • 仅返回张量的平均值(整个数据的单个标量),或
  • 重复张量(使用 K.repeat_elements),最终形成形状为 (20,100) 的张量。

框架不返回错误(Tensorflow 后端),并且在 epochs 上减少了损失,在独立的测试数据上,性能也不错。

我的问题是:

  • 在序列的情况下,“拟合”函数通常假设目标/损失的哪个维度?
  • Tensorflow 后端是否能够仅返回平均值也能正确导出梯度?

请在下面找到我实现的基于相关的损失函数的可执行示例。 my_loss_1 仅返回所有 (20) 个序列的相关系数的平均值。 my_loss_2 只为每个序列返回一个损失(在实际训练中不起作用)。 my_loss_3 对每个序列中的每个样本重复损失。

非常感谢和最良好的祝愿

from keras import backend as K
from keras.losses import mean_squared_error

import numpy as np
import tensorflow as tf


def my_loss_1(seq1, seq2):  # Correlation-based loss function - version 1 - return scalar
    seq1        = K.squeeze(seq1, axis=-1)
    seq2        = K.squeeze(seq2, axis=-1)
    seq1_mean   = K.mean(seq1, axis=-1, keepdims=True)
    seq2_mean   = K.mean(seq2, axis=-1, keepdims=True)
    nominator   = K.sum((seq1-seq1_mean) * (seq2-seq2_mean), axis=-1)
    denominator = K.sqrt( K.sum(K.square(seq1-seq1_mean), axis=-1) * K.sum(K.square(seq2-seq2_mean), axis=-1) )
    corr        = nominator / (denominator + K.common.epsilon())
    corr_loss   = K.constant(1.) - corr
    corr_loss   = K.mean(corr_loss)
    return corr_loss

def my_loss_2(seq1, seq2):  # Correlation-based loss function - version 2 - return 1D array
    seq1        = K.squeeze(seq1, axis=-1)
    seq2        = K.squeeze(seq2, axis=-1)
    seq1_mean   = K.mean(seq1, axis=-1, keepdims=True)
    seq2_mean   = K.mean(seq2, axis=-1, keepdims=True)
    nominator   = K.sum((seq1-seq1_mean) * (seq2-seq2_mean), axis=-1)
    denominator = K.sqrt( K.sum(K.square(seq1-seq1_mean), axis=-1) * K.sum(K.square(seq2-seq2_mean), axis=-1) )
    corr        = nominator / (denominator + K.common.epsilon())
    corr_loss   = K.constant(1.) - corr
    return corr_loss

def my_loss_3(seq1, seq2):  # Correlation-based loss function - version 3 - return 2D array
    seq1        = K.squeeze(seq1, axis=-1)
    seq2        = K.squeeze(seq2, axis=-1)
    seq1_mean   = K.mean(seq1, axis=-1, keepdims=True)
    seq2_mean   = K.mean(seq2, axis=-1, keepdims=True)
    nominator   = K.sum((seq1-seq1_mean) * (seq2-seq2_mean), axis=-1)
    denominator = K.sqrt( K.sum(K.square(seq1-seq1_mean), axis=-1) * K.sum(K.square(seq2-seq2_mean), axis=-1) )
    corr        = nominator / (denominator + K.common.epsilon())
    corr_loss   = K.constant(1.) - corr
    corr_loss   = K.reshape(corr_loss, (-1,1))
    corr_loss   = K.repeat_elements(corr_loss, K.int_shape(seq1)[1], 1)  # Does not work for fit(). It seems that NO dimension may be None in order to get a value!=None from int_shape().
    return corr_loss


# Test
sess = tf.Session()

# input (20,100,1)
a1 = np.random.rand(20,100,1)
a2 = np.random.rand(20,100,1)
print('\nInput: ' + str(a1.shape))

p1 = K.placeholder(shape=a1.shape, dtype=tf.float32)
p2 = K.placeholder(shape=a1.shape, dtype=tf.float32)

loss0 = mean_squared_error(p1,p2)
print('\nMSE:')                      # output: (20,100)
print(sess.run(loss0, feed_dict={p1: a1, p2: a2}))

loss1 = my_loss_1(p1,p2)
print('\nCorrelation coefficient:')  # output: ()
print(sess.run(loss1, feed_dict={p1: a1, p2: a2}))

loss2 = my_loss_2(p1,p2)
print('\nCorrelation coefficient:')  # output: (20,)
print(sess.run(loss2, feed_dict={p1: a1, p2: a2}))

loss3 = my_loss_3(p1,p2)
print('\nCorrelation coefficient:')  # output: (20,100)
print(sess.run(loss3, feed_dict={p1: a1, p2: a2}))

【问题讨论】:

    标签: tensorflow keras loss-function


    【解决方案1】:

    现在,如果我们使用基于相关系数的损失函数 对于每个序列,理论上,我们只会得到每个序列的单个值 序列,即形状为 (20,) 的张量。

    那不是真的。系数类似于

    average((avg_label - label_value)(average_prediction - prediction_value)) / 
            (var(label_value)*var(prediction_value))
    

    删除总体平均值,您将得到序列中每个元素的相关系数的组成部分,即正确的形状。 您也可以插入其他相关公式,在计算单个值之前停止。

    【讨论】:

      【解决方案2】:

      非常感谢! 好吧,我认为系数已经是样本序列的整体(平均)指标,但您的解决方案确实是有道理的。

      下面是我正在运行的代码(分母中的总和现在也已更改为平均,否则结果会随着序列越长而变小,这可能不是因为整体损失是所有损失的平均值)。应用于实际任务时效果很好(此处未显示)。

      我唯一的问题是损失函数开始时的挤压步骤不是很好,但我找不到更好的解决方案。

      from keras import backend as K
      from keras.losses import mean_squared_error
      
      import numpy as np
      import tensorflow as tf
      
      def my_loss(seq1, seq2):  # Correlation-based loss function
          seq1        = K.squeeze(seq1, axis=-1)  # To remove the last dimension
          seq2        = K.squeeze(seq2, axis=-1)  # To remove the last dimension
          seq1_mean   = K.mean(seq1, axis=-1, keepdims=True)
          seq2_mean   = K.mean(seq2, axis=-1, keepdims=True)
          nominator   = (seq1-seq1_mean) * (seq2-seq2_mean)
          denominator = K.sqrt( K.mean(K.square(seq1-seq1_mean), axis=-1, keepdims=True) * K.mean(K.square(seq2-seq2_mean), axis=-1, keepdims=True) )
          corr        = nominator / (denominator + K.common.epsilon())
          corr_loss   = K.constant(1.) - corr
          return corr_loss
      
      # Test
      sess = tf.Session()
      
      # Input (20,100,1)
      a1 = np.random.rand(20,100,1)
      a2 = np.random.rand(20,100,1)
      print('\nInput: ' + str(a1.shape))
      
      p1 = K.placeholder(shape=a1.shape, dtype=tf.float32)
      p2 = K.placeholder(shape=a1.shape, dtype=tf.float32)
      
      loss0 = mean_squared_error(p1,p2)
      print('\nMSE:')                      # output: (20,100)
      print(sess.run(loss0, feed_dict={p1: a1, p2: a2}))
      
      loss1 = my_loss(p1,p2)
      print('\nCorrelation coefficient-based loss:')  # output: (20,100)
      print(sess.run(loss1, feed_dict={p1: a1, p2: a2}))
      

      【讨论】:

        猜你喜欢
        • 2017-08-17
        • 2019-08-22
        • 2019-11-30
        • 2020-10-05
        • 2018-04-03
        • 2019-01-20
        • 1970-01-01
        • 2018-03-19
        • 2018-10-10
        相关资源
        最近更新 更多