使用余弦相似函数执行矩阵乘法

Perform matrix multiplication with cosine similarity function

我有两个列表:

list_1 = [['flavor', 'flavors', 'fruity_flavor', 'taste'],
          ['scent', 'scents', 'aroma', 'smell', 'odor'],
          ['mental_illness', 'mental_disorders','bipolar_disorder']
          ['romance', 'romances', 'romantic', 'budding_romance']]

list_2 = [['love', 'eating', 'spicy', 'hand', 'pulled', 'noodles'],
          ['also', 'like', 'buy', 'perfumes'],
          ['suffer', 'from', 'clinical', 'depression'],
          ['really', 'love', 'my', 'wife']]

我想计算上面两个列表之间的余弦相似度,其中列表 1 中的第一个子列表与列表 2 的所有子列表之间的余弦相似度是相互测量的。然后是同样的事情,但列表 1 中的第二个子列表和列表 2 中的所有子列表等

目标是通过 len(list_1) 创建一个 len(list_2) 矩阵,并且该矩阵中的每个条目都是余弦相似度分数。目前我是通过以下方式完成的:

import gensim
import numpy as np
from gensim.models import KeyedVectors

model = KeyedVectors.load_word2vec_format('./data/GoogleNews-vectors-negative300.bin.gz', binary=True) 
similarity_mat = np.zeros([len(list_2), len(list_1)])

for i, L2 in enumerate(list_2):
    for j, L1 in enumerate(list_1):
        similarity_mat[i, j] = model.n_similarity(L2, L1)

但是,我想用矩阵乘法而不是 for 循环来实现它。

我的两个问题是:

  1. 有没有一种方法可以进行某种元素矩阵乘法,但使用 gensim's n_similiarity() method 来生成所需的矩阵?
  2. 使用当前方法或矩阵乘法会更高效、更快速吗?

我希望我的问题足够清楚,如果我能进一步澄清,请告诉我。

代码中有两个问题,倒数第二行和最后一行。

import gensim
import numpy as np
from gensim.models import KeyedVectors

model = KeyedVectors.load_word2vec_format('/root/input/GoogleNews-vectors-negative300.bin.gz', binary=True) 
similarity_mat = np.zeros([len(list_2), len(list_1)])

for i, L2 in enumerate(list_2):
    for j, L1 in enumerate(list_1):
        similarity_mat[i, j] = model.n_similarity(L2, L1)

您问题的答案:
1.您已经在使用直接函数来计算首先转换为两个向量的两个句子(L1 和 L2)之间的相似度,然后计算这两个向量的余弦相似度。一切都已经在 n_similarity() 内部完成,因此您不能进行任何类型的矩阵乘法。
如果你想做自己的矩阵乘法,那么不要直接使用 n_similarity() 计算句子的向量,然后在计算余弦相似度时应用矩阵乘法。
2. 正如我在 (1) 中所说,一切都在 n_similarity() 中完成,gensim 的创建者在编写库时会注意效率,因此任何其他乘法方法很可能不会产生影响。

这是一种方法,但是从问题中看不清楚the underlying mechanics of the calculation,这可能会导致阻塞。

我更改了输入字符串以提供更精确的单词匹配,并为两个字符串提供了不同的维度以使其更清晰:

from sklearn.feature_extraction.text import CountVectorizer
import numpy as np

list_1 = [['flavor', 'flavors', 'fruity_flavor', 'taste'],
         ['scent', 'my', 'aroma', 'smell', 'odor'],
         ['mental_illness', 'mental_disorders','bipolar_disorder'],
         ['romance', 'romances', 'romantic', 'budding_romance']]

list_2 = [['love', 'eating', 'spicy', 'hand', 'pulled', 'noodles'],
          ['also', 'like', 'buy', 'perfumes'],
          ['suffer', 'from', 'clinical', 'depression'],
          ['really', 'love', 'my', 'wife'],
          ['flavor', 'taste', 'romantic', 'aroma', 'what']]

cnt = CountVectorizer()

# Combine each sublist into single str, and join everything into corpus
combined_lists = ([' '.join(item) for item in list_1] +
                  [' '.join(item) for item in list_2])
count_matrix = cnt.fit_transform(combined_lists).toarray()

# Split them again into list_1 and list_2 word counts
count_matrix_1 = count_matrix[:len(list_1),]
count_matrix_2 = count_matrix[len(list_1):,]
match_matrix = np.matmult(count_matrix_1, count_matrix_2.T)

match_matrix 的输出:

array([[0, 0, 0, 0, 2],
       [0, 0, 0, 1, 1],
       [0, 0, 0, 0, 0],
       [0, 0, 0, 0, 1]], dtype=int64)

你可以看到list_1中的第一个字符串与list_2中的第5个字符串有2个匹配,依此类推。

所以计算的第一部分(点积)已经计算出来了。现在我们需要震级:

magnitudes = np.array([np.linalg.norm(count_matrix[i,:])
                       for i in range(len(count_matrix))])

现在我们可以使用矩阵乘法将其转换为除数矩阵(我们需要将大小重塑为 n x 1 和 1 x n 矩阵,以生成 n x n 矩阵:

divisor_matrix = np.matmul(magnitudes.reshape(len(magnitudes),1),
                           magnitudes.reshape(1,len(magnitudes)))

现在因为我们没有比较每个子列表,而只比较 list_1 和 list_2 子列表,我们需要取这个除数矩阵的一个子部分以获得正确的大小:

divisor_matrix = divisor_matrix[:len(list_1), len(list_1):]

输出:

array([[4.89897949, 4.        , 4.        , 4.        , 4.47213595],
       [5.47722558, 4.47213595, 4.47213595, 4.47213595, 5.        ],
       [4.24264069, 3.46410162, 3.46410162, 3.46410162, 3.87298335],
       [4.89897949, 4.        , 4.        , 4.        , 4.47213595]])

现在我们可以计算余弦相似度得分的最终矩阵:

cos_sim = match_matrix / divisor_matrix

输出:

array([[0.       , 0.       , 0.       , 0.       , 0.4472136],
       [0.       , 0.       , 0.       , 0.2236068, 0.2      ],
       [0.       , 0.       , 0.       , 0.       , 0.       ],
       [0.       , 0.       , 0.       , 0.       , 0.2236068]])

请注意这些分数与给定的示例不同,因为在示例中每个余弦相似度分数都是 0。