Python:如何计算两个单词列表之间的余弦相似度?

7
我想要计算两个列表的余弦相似度,例如以下内容:
A = [u'home (private)', u'bank', u'bank', u'building(condo/apartment)','factory']

B = [u'home (private)', u'school', u'bank', u'shopping mall']

我知道A和B的余弦相似度应该是:
3/(sqrt(7)*sqrt(4)).

我尝试将列表改成像“家银行银行建筑工厂”这样的形式,看起来像一个句子,但是一些元素(如home(私人))本身有空格,而一些元素有括号,所以我发现很难计算单词出现次数。
你知道如何计算这个复杂列表中单词出现次数吗?这样对于列表B,单词出现次数可以表示为:
{'home (private):1, 'school':1, 'bank': 1, 'shopping mall':1}? 

你知道如何计算这两个列表的余弦相似度吗?

非常感谢。


你会如何定义“余弦相似度”?这些变量3/(sqrt(7)*sqrt(4))是从哪里来的? - ZdaR
我只知道一种定义余弦相似度的方法,就是像A = [2, 1, 1, 1, 0, 0]和B = [1,1,0,0,1,1]这样,余弦相似度就等于点积(A,B)除以|A|.|B|,它们的余弦相似度为3/(sqrt(7)*sqrt(4))。 - gladys0313
2个回答

8
from collections import Counter

# word-lists to compare
a = [u'home (private)', u'bank', u'bank', u'building(condo/apartment)','factory']
b = [u'home (private)', u'school', u'bank', u'shopping mall']

# count word occurrences
a_vals = Counter(a)
b_vals = Counter(b)

# convert to word-vectors
words  = list(a_vals.keys() | b_vals.keys())
a_vect = [a_vals.get(word, 0) for word in words]        # [0, 0, 1, 1, 2, 1]
b_vect = [b_vals.get(word, 0) for word in words]        # [1, 1, 1, 0, 1, 0]

# find cosine
len_a  = sum(av*av for av in a_vect) ** 0.5             # sqrt(7)
len_b  = sum(bv*bv for bv in b_vect) ** 0.5             # sqrt(4)
dot    = sum(av*bv for av,bv in zip(a_vect, b_vect))    # 3
cosine = dot / (len_a * len_b)                          # 0.5669467

非常感谢您的回答。看起来很酷,但是在这个语句中:list(a_vals.keys() | b_vals.keys()),解释器报错'TypeError: unsupported operand type(s) for |: 'list' and 'list'。有什么想法吗? - gladys0313
抱歉,我是在Python 3.4中测试的。对于2.x版本,您可以执行word = list(set(a_vals) | set(b_vals)) - Hugh Bothwell

2
首先建立一个词典(这是指在集合或语料库中所有不同单词的列表的技术术语)。
vocab = {}
i = 0

# loop through each list, find distinct words and map them to a
# unique number starting at zero

for word in A:
    if word not in vocab:
        vocab[word] = i
        i += 1


for word in B:
    if word not in vocab:
        vocab[word] = i
        i += 1
< p > vocab 字典现在将每个单词映射到从零开始的唯一数字。 我们将使用这些数字作为数组(或向量)中的索引。

在下一步中,我们将为每个输入列表创建一个称为术语频率向量的东西。 我们将在这里使用名为numpy的库。 它是进行此类科学计算的非常流行的方法。 如果您对余弦相似性(或其他机器学习技术)感兴趣,它值得您的时间。

import numpy as np

# create a numpy array (vector) for each input, filled with zeros
a = np.zeros(len(vocab))
b = np.zeros(len(vocab))

# loop through each input and create a corresponding vector for it
# this vector counts occurrences of each word in the dictionary

for word in A:
    index = vocab[word] # get index from dictionary
    a[index] += 1 # increment count for that index

for word in B:
    index = vocab[word]
    b[index] += 1

最后一步是计算余弦相似度。
# use numpy's dot product to calculate the cosine similarity
sim = np.dot(a, b) / np.sqrt(np.dot(a, a) * np.dot(b, b))

变量sim现在包含您的答案。您可以提取每个子表达式并验证它们是否符合您的原始公式。
通过一些重构,这种技术相当可扩展(具有相对较多的输入列表和相对较多的不同单词)。对于真正大的语料库(如维基百科),您应该查看专门为此类事情设计的自然语言处理库。以下是一些好的库:
  1. nltk
  2. gensim
  3. spaCy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接