Функция потерь на основе корреляции для маркировки последовательности в Keras
У меня есть вопрос, касающийся реализации корреляционной функции потерь для задачи маркировки последовательности в Keras (серверная часть Tensorflow).
Предположим, у нас есть проблема с маркировкой последовательности, например, входной сигнал - это тензор формы (20,100,5), а выходной - это тензор формы (20,100,1). В документации написано, что функция потерь должна возвращать "скаляр для каждой точки данных". Что потеря MSE по умолчанию делает для потери между тензорами формы (20,100,1), так это возвращает тензор потерь формы (20,100).
Теперь, если мы используем функцию потерь, основанную на коэффициенте корреляции для каждой последовательности, теоретически мы получим только одно значение для каждой последовательности, т. Е. Тензор формы (20,).
Однако, используя это в Keras в качестве функции потерь, fit() возвращает ошибку, поскольку ожидается тензор формы (20 100). С другой стороны, нет ошибки, когда я либо
- Вернуть только среднее значение тензора (один скаляр для целых данных) или
- Повторите тензор (используя K.repeat_elements), получив в итоге тензор формы (20,100).
Фреймворк не возвращает ошибку (серверная часть Tensorflow), и потери уменьшаются в течение эпох, также на независимых данных испытаний, производительность хорошая.
Мои вопросы:
- Какую размерность целей / потерь обычно принимает функция "соответствия" в случае последовательностей?
- Может ли серверная часть 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}))
2 ответа
Теперь, если мы используем функцию потерь, основанную на коэффициенте корреляции для каждой последовательности, теоретически мы получим только одно значение для каждой последовательности, т. Е. Тензор формы (20,).
Это не правда. коэффициент что-то вроде
average((avg_label - label_value)(average_prediction - prediction_value)) /
(var(label_value)*var(prediction_value))
Удалите общее среднее, и у вас останутся составляющие коэффициента корреляции для каждого элемента последовательности, который является правильной формой. Вы можете подключить и другие формулы корреляции, просто остановитесь перед вычислением одного значения.
Большое спасибо! Что ж, я думал, что коэффициент уже является общей (усредненной) метрикой по последовательности выборки, но ваше решение действительно имеет смысл.
Ниже приведен мой рабочий код (суммирование в знаменателе также было изменено на усреднение, в противном случае результат будет тем меньше, чем длиннее последовательность, а это может быть не так, поскольку общие потери являются средними по всем потерям). Он хорошо работает применительно к реальным задачам (здесь не показано).
Единственная проблема, которая у меня осталась, заключается в том, что шаг сжатия в начале функции потерь не так хорош, но я не смог найти более подходящего решения.
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}))