Reputation: 4991
I am having 3 tensors in keras:
a
of shape: (?,4)
b
of shape: (?,4)
c
of shape: (?, 2, 4)
where ?
is the batch size. And I want to compute the cosinus similarity/ies between:
a
and b
and,a
and each row of c
( for this case 2 rows)To compute the cos between a
and b
the following can be done which is straight forward:
from keras.layers import Reshape, merge
cos_a_b = Reshape((1,))(merge([a, b], mode='cos', dot_axes=1))
The output is of format (?,1)
as expected.
But I am unable to find the cos similarities between a
and c
(which output should be of the format (?, 2)
). Using the same merge
function (merge([a, c],mode='cos',dot_axes=1)
) I have the error
ValueError: Only layers of same output shape can be merged using cos mode. Layer shapes: [(None, 4), (None, 2, 4)]
Any idea how to accomplish it?
Edit
Following blackplant's answer:
IF I do the following:
c_rep = RepeatVector(2)(c)
cos_a_c = merge([a,c_rep],mode='cos', dot_axes=1)
I got the following error:
Only layers of same output shape can be merged using cos mode. Layer shapes: [(None, Dimension(2), 4), (None, 2, 4)]
But if I do the following:
cos_a_c = merge([a, RepeatVector(2)(c)],mode='cos', dot_axes=1)
the code runs normally but the output is a tensor of shape (?, 1, 4, 4)
.
With dot_axes=2
the output is of shape (?,1, 2, 2)
.
Shouldn't it be (?,2)
?
Upvotes: 0
Views: 497
Reputation: 86600
I'm not sure this merge
approach is well documented. I prefer using documented layers and perform the similarity manually. Formula taken from Wikipedia:
import keras.backend as K
def getDivisor(x):
return K.sqrt(K.sum(K.square(x),axis=-1,keepdims=True))
def similarity(a, b):
dividend = K.sum(a*b,axis=-1,keepdims=True)
return dividend / (getDivisor(a) * getDivisor(b))
Now, we should just take some care to make the shapes of a
and c
compatible.
abSim = Lambda(similarity, output_shape=(1,))([a,b])
aCompatible = Reshape((1,4))(a)
acSim = Lambda(similarity, output_shape=(2,1))([aCompatible,c])
Maybe just applying the Reshape((1,4))
to a
could be enough for using with your merge
method. But I can't be sure.
Upvotes: 1
Reputation: 1502
Have you tried using RepeatVector? Using this you can repeat the "a " tensor and turn it into a tensor with shape (2,4), at which point you can merge it with c.
# Repeat a twice
a_r = RepeatVector(2)(a)
merge([a_r, b], mode='cos', dot_axes=1)
EDIT: So it seems that instead of repeating 'a' in order to be able to merge it with 'c' you should instead split up c and apply the dot operation to each slice.
# Select first row
c_0 = Lambda(lambda x: x[0,:], output_shape=(4,))(c)
# Compute cos between first row and a
merge([a, c_0], mode='cos', dot_axes=1)
Upvotes: 0