我在计算大量100维向量之间的余弦相似度时遇到麻烦。使用from sklearn.metrics.pairwise import cosine_similarity
时,我在16 GB的计算机上得到MemoryError
。每个数组都非常适合我的记忆,但是我在MemoryError
内部调用
np.dot()
这是我的用例以及当前的处理方式。
这是我的100维父向量,我需要将其与其他500,000个相同尺寸(即100)的不同向量进行比较
parent_vector = [1, 2, 3, 4 ..., 100]
这是我的子向量(此示例中包含一些虚构的随机数)
child_vector_1 = [2, 3, 4, ....., 101]
child_vector_2 = [3, 4, 5, ....., 102]
child_vector_3 = [4, 5, 6, ....., 103]
.......
.......
child_vector_500000 = [3, 4, 5, ....., 103]
我的最终目标是获得与父向量的余弦相似度非常高的前N个子向量(其名称如child_vector_1
及其对应的余弦分数)。
我目前的方法(我知道这种方法效率低下且消耗内存):
步骤1:创建以下形状的超级数据框
parent_vector 1, 2, 3, ....., 100
child_vector_1 2, 3, 4, ....., 101
child_vector_2 3, 4, 5, ....., 102
child_vector_3 4, 5, 6, ....., 103
......................................
child_vector_500000 3, 4, 5, ....., 103
步骤2:使用
from sklearn.metrics.pairwise import cosine_similarity
cosine_similarity(df)
获取所有向量之间的成对余弦相似度(如上数据框所示)
步骤3:列出一个元组列表,以存储所有此类组合的key
(例如child_vector_1
)和值(例如余弦相似度数字)。
第4步:使用列表的sort()
获得前N位,以便获得子向量名称以及它与父向量的余弦相似度得分。>
PS:我知道这效率很低,但是我想不到更好的方法 快速计算每个子向量之间的余弦相似度的方法 和父向量,并获得前N个值。
任何帮助将不胜感激。
答案 0 :(得分:0)
即使您的(500000,100)数组(父级及其子级)适合内存 任何成对度量都不会。这样做的原因是顾名思义,成对度量可计算任意两个子代的距离。为了存储这些距离,您将需要一个(500000,500000)大小的浮点数组,如果我的计算正确的话,它将需要大约100 GB的内存。
很高兴为您解决问题。如果我对您的理解正确,那么您只希望孩子和父母之间有一段距离,这将导致长度为500000的向量易于存储在内存中。
为此,您只需要向cosine_similarity提供第二个参数,仅包含parent_vector
import pandas as pd
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
df = pd.DataFrame(np.random.rand(500000,100))
df['distances'] = cosine_similarity(df, df.iloc[0:1]) # Here I assume that the parent vector is stored as the first row in the dataframe, but you could also store it separately
n = 10 # or however many you want
n_largest = df['distances'].nlargest(n + 1) # this contains the parent itself as the most similar entry, hence n+1 to get n children
希望能解决您的问题。
答案 1 :(得分:0)
我什至无法将整个语料库容纳在内存中,所以对我来说,一个解决方案是逐渐加载它,并在较小的批次上计算余弦相似度,始终保持最少/最多n
(取决于您的用例)相似度项目:
data = []
iterations = 0
with open('/media/corpus.txt', 'r') as f:
for line in f:
data.append(line)
if len(data) <= 1000:
pass
else:
print('Getting bottom k, iteration {x}'.format(x=iterations))
data = get_bottom_k(data, 500)
iterations += 1
filtered = get_bottom_k(data, 500) # final most different 500 texts in corpus
def get_bottom_k(corpus:list, k:int):
pairwise_similarity = make_similarity_matrix(corpus) # returns pairwise similarity matrix
sums = csr_matrix.sum(pairwise_similarity, axis=1) # Similarity index for each item in corpus. Bigger > more
sums = np.squeeze(np.asarray(sums))
# similar to other txt.
indexes = np.argpartition(sums, k, axis=0)[:k] # Bottom k in terms of similarity (-k for top and [-k:])
return [corpus[i] for i in indexes]
到目前为止,这是一个最佳解决方案,但这是我到目前为止发现的最简单的方法,也许会对某人有所帮助。
答案 2 :(得分:-1)
此解决方案非常快
child_vectors = np.array(child_vector_1, child_vector_2, ....., child_vector_500000)
input_norm = parent_vector / np.linalg.norm(parent_vector, axis=-1)[:, np.newaxis]
embed_norm = child_vectors/ np.linalg.norm(child_vectors, axis=-1)[:, np.newaxis]
cosine_similarities = np.sort(np.round(np.dot(input_norm, embed_norm.T), 3)[0])[::-1]
paiswise_distances = 1 - cosine_similarities