web-dev-qa-db-fra.com

Comment calculer la cosinus_similarity en pytorch pour toutes les lignes d'une matrice par rapport à toutes les lignes d'une autre matrice

Dans pytorch, étant donné que j'ai 2 matrices, comment calculer la similitude en cosinus de toutes les lignes de chacune avec toutes les lignes de l'autre.

Par exemple

Étant donné l'entrée =

matrix_1 = [a b] 
           [c d] 
matrix_2 = [e f] 
           [g h]

J'aimerais que la sortie soit

sortie =

 [cosine_sim([a b] [e f])  cosine_sim([a b] [g h])]
 [cosine_sim([c d] [e f])  cosine_sim([c d] [g h])] 

Pour le moment, j'utilise torch.nn.functional.cosine_similarity (matrix_1, matrix_2) qui renvoie le cosinus de la ligne avec uniquement la ligne correspondante dans l'autre matrice.

Dans mon exemple, je n'ai que 2 lignes, mais j'aimerais une solution qui fonctionne pour de nombreuses lignes. Je voudrais même gérer le cas où le nombre de lignes dans chaque matrice est différent.

Je me rends compte que je pouvais utiliser l'extension, mais je veux le faire sans utiliser une telle empreinte mémoire.

8
Funzo

En calculant manuellement la similitude et en jouant avec la multiplication matricielle + la transposition:

import torch
from scipy import spatial
import numpy as np

a = torch.randn(2, 2)
b = torch.randn(3, 2) # different row number, for the fun

# Given that cos_sim(u, v) = dot(u, v) / (norm(u) * norm(v))
#                          = dot(u / norm(u), v / norm(v))
# We fist normalize the rows, before computing their dot products via transposition:
a_norm = a / a.norm(dim=1)[:, None]
b_norm = b / b.norm(dim=1)[:, None]
res = torch.mm(a_norm, b_norm.transpose(0,1))
print(res)
#  0.9978 -0.9986 -0.9985
# -0.8629  0.9172  0.9172

# -------
# Let's verify with numpy/scipy if our computations are correct:
a_n = a.numpy()
b_n = b.numpy()
res_n = np.zeros((2, 3))
for i in range(2):
    for j in range(3):
        # cos_sim(u, v) = 1 - cos_dist(u, v)
        res_n[i, j] = 1 - spatial.distance.cosine(a_n[i], b_n[j])
print(res_n)
# [[ 0.9978022  -0.99855876 -0.99854881]
#  [-0.86285472  0.91716063  0.9172349 ]]
7
benjaminplanche

Ajout de eps pour la stabilité numérique basée sur la réponse de benjaminplanche:

def sim_matrix(a, b, eps=1e-8):
    """
    added eps for numerical stability
    """
    a_n, b_n = a.norm(dim=1)[:, None], b.norm(dim=1)[:, None]
    a_norm = a / torch.max(a_n, eps * torch.ones_like(a_n))
    b_norm = b / torch.max(b_n, eps * torch.ones_like(b_n))
    sim_mt = torch.mm(a_norm, b_norm.transpose(0, 1))
    return sim_mt
3
Zhang Yu