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

时间:2019-09-02 21:11:33

标签: python-3.x numpy gensim word2vec cosine-similarity

我有两个列表:

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. 使用当前方法或矩阵乘法会更高效,更快吗?

我希望我的问题足够清楚,请让我知道是否可以进一步澄清。

2 个答案:

答案 0 :(得分:0)

这是一种方法,但是从这个问题尚不清楚您是否理解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中的第五个字符串有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。

答案 1 :(得分:-1)

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

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的创建者在编写库时会考虑效率,因此任何其他乘法方法极有可能不会发挥作用。