Вычисление косинусного сходства между двумя тензорами в Керасе
Я следовал учебному пособию, которое показывает, как сделать модель word2vec.
Этот учебник использует этот кусок кода:
similarity = merge([target, context], mode='cos', dot_axes=0)
(никакой другой информации предоставлено не было, но я полагаю, что это keras.layers
)
Теперь я немного исследовал merge
метод, но я не мог найти много об этом. Из того, что я понимаю, было заменено много функций, таких как layers.Add(), layers.Concat()...
,
Что я должен использовать? Там в .Dot()
, который имеет axis
параметр (который кажется правильным), но нет mode
параметр.
Что я могу использовать в этом случае?
5 ответов
Есть несколько вещей, которые неясны из документации Keras, которые я считаю крайне важными для понимания:
Для каждой функции в документации keras для Merge
есть нижний и верхний регистр, т.е. add()
а также Add()
,
На Github, farizrahman4u
выделяет различия:
Merge is a layer.
Merge takes layers as input
Merge is usually used with Sequential models
merge is a function.
merge takes tensors as input.
merge is a wrapper around Merge.
merge is used in Functional API
Using Merge:
left = Sequential()
left.add(...)
left.add(...)
right = Sequential()
right.add(...)
right.add(...)
model = Sequential()
model.add(Merge([left, right]))
model.add(...)
using merge:
a = Input((10,))
b = Dense(10)(a)
c = Dense(10)(a)
d = merge([b, c])
model = Model(a, d)
Чтобы ответить на ваш вопрос, так как Merge
устарела, мы должны сами определить и построить слой для cosine similarity
, В общем случае это будет связано с использованием тех строчных функций, которые мы заключаем в Lambda
создать слой, который мы можем использовать в модели.
Я нашел решение здесь:
def cosine_distance(vests):
x, y = vests
x = K.l2_normalize(x, axis=-1)
y = K.l2_normalize(y, axis=-1)
return -K.mean(x * y, axis=-1, keepdims=True)
def cos_dist_output_shape(shapes):
shape1, shape2 = shapes
return (shape1[0],1)
distance = Lambda(cosine_distance, output_shape=cos_dist_output_shape)([processed_a, processed_b]
В зависимости от ваших данных, вы можете удалить нормализацию L2. Что важно отметить в решении, так это то, что оно построено с использованием функции Keras, например: K.mean()
- Я думаю, что это необходимо при определении пользовательского слоя или даже функций потерь.
Надеюсь, мне было ясно, это был мой первый ТАК ответ!
Dot
слой в Keras теперь поддерживает встроенное сходство косинусов, используя normalize = True
параметр.
Из документов Кераса:
keras.layers.Dot(axes, normalize=True)
нормализовать: следует ли L2-нормализовать образцы вдоль оси точечного произведения, прежде чем брать точечное произведение. Если установлено значение "Истина", то выход точечного произведения представляет собой косинусную близость между двумя выборками.
Может это вам поможет (много времени потратил, чтобы убедиться, что это одни и те же вещи)
import tensorflow as tf
with tf.device('/CPU:' + str(0)):
print(tf.losses.CosineSimilarity()([1.0,1.0,1.0,-1.0],[4.0,4.0,4.0,5.0]))
print(tf.keras.layers.dot([tf.Variable([[1.0,1.0,1.0,-1.0]]),tf.Variable([[4.0,4.0,4.0,5.0]])], axes=1, normalize=True))
Выход (обратите внимание на знак):
tf.Tensor(-0.40964404, shape=(), dtype=float32)
tf.Tensor([[0.40964404]], shape=(1, 1), dtype=float32)
Это должно помочь:
cos = tf.keras.layers.dot( [ tensor_a, tensor_b ], axes = 1, normalize = True )
cos_similarity = (-1.0) * cos
Кроме того, имейте это в виду:
tf.keras.layers.dot
- 1, если два вектора имеют угол 0 (минимальное угловое расстояние).
- 0, если два вектора имеют угол 90 (половина углового расстояния).
- -1, если два вектора имеют угол 180 (максимальное угловое расстояние).
tf.keras.losses.CosineSimilarity()
- -1, если два вектора имеют угол 0 (минимальное угловое расстояние).
- 0, если два вектора имеют угол 90 (половина углового расстояния).
- 1, если два вектора имеют угол 180 (максимальное угловое расстояние).
Если вы измените последний блок кода учебника следующим образом, вы увидите, что (средняя) потеря хорошо уменьшается с решением Dot, предложенным SantoshGuptaz7 (комментарий в вопросе выше):
display_after_epoch = 10000
display_after_epoch_2 = 10 * display_after_epoch
loss_sum = 0
for cnt in range(epochs):
idx = np.random.randint(0, len(labels)-1)
arr_1[0,] = word_target[idx]
arr_2[0,] = word_context[idx]
arr_3[0,] = labels[idx]
loss = model.train_on_batch([arr_1, arr_2], arr_3)
loss_sum += loss
if cnt % display_after_epoch == 0 and cnt != 0:
print("\nIteration {}, loss={}".format(cnt, loss_sum / cnt))
loss_sum = 0
if cnt % display_after_epoch_2 == 0:
sim_cb.run_sim()