TianduAI

Action speak louder than words

0%

Tensorflow实现cosine similarity

一种简单优雅的实现方法

余弦相似度计算公式

$ cos(\vec x,\vec y)=\frac{<\vec x,\vec y>}{|\vec x| |\vec y|} $

最直观实现方式

user_side_representationitem_side_representationshape=[None, 4],其中None表示batch_size,4表示embedding_size

计算user_side_representationitem_side_representation两者的cosine similarity最直观的实现方式为:
1)求user_side_representationitem_side_representation的内积inner_product;
2)分别计算user_side_representationitem_side_representationL2-normnorm_usernorm_item;
3)计算inner_product / (norm_user*norm_item)

以上思路是完全按照余弦相似度计算公式进行的,利用TensorFlow实现如下:

1
2
3
4
5
6
7
8
user_side_representation_expand = tf.expand_dims(user_side_representation, axis=1)
item_side_representation_expand = tf.expand_dims(item_side_representation, axis=2)
inner_product = tf.matmul(user_side_representation_expand, item_side_representation_expand)

user_side_representation_norm = tf.sqrt(tf.reduce_sum(tf.square(user_side_representation), axis=1))
item_side_representation_norm = tf.sqrt(tf.reduce_sum(tf.square(item_side_representation), axis=1))
denominoator_norm = tf.expand_dims(user_side_representation_norm * item_side_representation_norm, axis=1)
output = tf.squeeze(inner_product, axis=1) / denominoator_norm

但是该实现方式有个致命的问题:在训练过程中output或者loss值会出现为nan的情况。其原因在与tf.sqrt(x)x过小,导致sqrt()函数输出为nan。最终会导致模型无法正确训练。

最实用&优雅实现方式

既然tf.sqrt函数存在输出为nan的问题,那么我们就要避免使用tf.sqrt函数。

再来看下余弦相似度计算公式,其实可以进一步变形:

$ cos(\vec x,\vec y)=\frac{<\vec x,\vec y>}{|\vec x| |\vec y|}=<\vec e_x, \vec e_y> $​

我们直接计算$\vec x$与$\vec y$​两个向量方向上的单位向量的内积就可以了,这样不需要使用tf.sqrt函数。

利用TensorFlow实现如下:

1
2
3
4
user_side_representation_l2norm = tf.nn.l2_normalize(user_side_representation, axis=1)
item_side_representation_l2norm = tf.nn.l2_normalize(item_side_representation, axis=1)
inner_product = tf.reduce_sum(user_side_representation_l2norm * item_side_representation_l2norm, axis=1)
output = tf.clip_by_value(inner_product, clip_value_min=-1.0, clip_value_max=1.0)

总结

利用TensorFlow计算余弦相似度,计算两向量的单位向量的内积即可。