任务
我有一个pandas数据框,其中:
我需要计算doc1-doc相似度的新矩阵,其中:
余弦距离由script.spatial.distance.cosine方便地提供。
我目前正在这样做:
问题
但这需要很长时间。下面显示了MacBook Pro 13的当前速度,16GB内存和2.9GHz i5cpu运行最新的anaconda python 3.5 ...绘制了对文档组合的时间。
您可以看到100,000个组合需要1200秒。将其推断到 7944 文档的语料库,创建3个 1,549,596 组合,将需要 5天来计算此相似度矩阵!
有什么想法吗?
以下是代码(github)
docs_combinations = itertools.combinations(docs_sample, 2)
for doc1, doc2 in docs_combinations:
# scipy cosine similarity function includes normalising the vectors but is a distance .. so we need to take it from 1.0
doc_similarity_dict[doc2].update({doc1: 1.0 - scipy.spatial.distance.cosine(relevance_index[doc1],relevance_index[doc2])})
pass
#convert dict to pandas dataframe
doc_similarity_matrix = pandas.DataFrame(doc_similarity_dict)
简单示例
@MaxU要求提供一个说明性的例子。
相关性矩阵(此处为wordcount,只是为了保持简单):
... doc1 doc2 doc3
wheel 2. 3. 0.
seat 2. 2. 0.
lights 0. 1. 1.
cake 0. 0. 5.
基于2组合计算的相似度矩阵(doc1,doc2),(doc2,doc3),(doc1,doc3)
... doc2 doc3
doc1 0.9449 0.
doc2 - 0.052
取左上角的值0.889 ..这就是点积(2 * 3 + 2 * 2 + 0 + 0)= 10但是按矢量的长度归一化...所以除以sqrt(8)和sqrt (14)给出0.9449。你可以看到doc1和doc3之间没有相似之处。点积为零。
将此文件从包含4个字的3个文档扩展到 7944 文档,这会创建3个 1,549,596 组合......
答案 0 :(得分:2)
这与我可以制作算法一样有效,而不会进入多处理(bleh)。该函数使用numpy数组进行所有计算。
def cos_sim(data_frame):
# create a numpy array from the data frame
a = data_frame.values
# get the number of documents
n = a.shape[-1]
# create an array of size docs x docs to populate
out = np.ravel(np.zeros(shape=(n, n)))
for i in range(n):
# roll the array one step at a time, calculating the cosine similarity each time
r = np.roll(a, -i, axis=1)
cs = np.sum(a[:,:n-i]*r[:,:n-i], axis=0) / (
np.sqrt(np.sum(a[:,:n-i]*a[:,:n-i], axis=0))
*np.sqrt(np.sum(r[:,:n-i]*r[:,:n-i], axis=0)))
# push the cosine similarity to the output array's i-th off-diagonal
out[i:n*n-i*n:n+1] = cs
return out.reshape((n,n))
答案 1 :(得分:1)
Numba将是一个很好的解决方案。我想你知道,它不支持Pandas DataFrames,但它是围绕NumPy数组构建的。这不是问题 - 你可以轻松快速地将你的DataFrame转换为2D数组并将其传递给Numba函数(这几乎就是你已经拥有的代码,只是顶部装有@njit
)
另请注意,您可以使用方形矩阵的一个三角形来存储它们,而不是结果的dict-of-dicts:
doc1 doc2 doc3
doc1 NAN NAN NAN
doc2 ... NAN NAN
doc3 ... ... NAN
编辑:你现在已经使用Numba实现了它,但只获得了2.5倍的加速。我做了一些实验,发现了一个巨大的胜利:
In [66]: x = np.random.random((1000,1000))
In [67]: y = np.array(x, order='F')
In [68]: %timeit similarity_jit(x)
1 loop, best of 3: 13.7 s per loop
In [69]: %timeit similarity_jit(y)
1 loop, best of 3: 433 ms per loop
也就是说,如果由于缓存操作连续的数据块,您的算法会快得多。由于算法的内核为numpy.dot(m[:,i], m[:,j])
,而m[:,i]
占用一列,因此最好先将数据定位到"Fortran order"(列主要顺序),以便{{1}给出一个连续的数组(因为数组在内存中“转置”)。