使用嵌入列表快速执行余弦相似度

Quickly performing cosine similarity with list of embeddings

我有一个列表 phrases,我想从一组 25k 嵌入向量 (emb2_list) 中获得最匹配的每个列表。为此,我使用余弦相似度。以下是代码:

from sentence_transformers import SentenceTransformer, util
import numpy as np
import torch

model = SentenceTransformer('bert-base-nli-stsb-mean-tokens')

emb2_list = np.load("emb2_list.npy") #already encoded, len = 25K

phrases = ['phrase 1','phrase 2','phrase 3','phrase 4',]

for phrase in phrases:
    
    emb1 = model.encode(phrase)

    cos_sim = []

    for emb2 in emb2_list:
        cos_sim.append(util.pytorch_cos_sim(emb1, emb2)[0][0].item())


    v, i = torch.Tensor(cos_sim).topk(1)

    print(f'phrase:{phrase} match index:{i}')

问题是每次迭代大约需要 1 秒(本例中总共需要 4 秒)。一旦 phrases 的大小增加(因为这是在线 API 的一部分),它确实会出现问题。

在数据结构、批处理技术或某种可能加速此过程的 approximation/Nearest 邻域算法方面,是否有更好的方法来查找余弦相似性?

您需要批量计算 (1) 句子编码和 (2) 余弦相似度。

1

sentence_transformers 的文档指出您可以对句子列表调用编码:

emb1 = model.encode(phrases)

2

余弦相似度是矩阵-矩阵乘法。

emb2 = torch.tensor(emb2_list)                   # cast to torch tensor
emb2 /= emb2.norm(dim=-1, p=2).unsqueeze(-1)     # normalize to vector length 
emb1 /= emb1.norm(dim=-1, p=2).unsqueeze(-1)     # ditto
sims = emb1 @ emb2.t()                           # matrix-matrix multiply the normalized embeddings

现在 sims[a,b] 将包含 phrases[a] 与嵌入 emb_list[b].

的相似性

请注意,对于 m 个短语和 n 个预先计算的嵌入,矩阵乘法的内存成本为 O(mn)。根据您的用例,您可能需要将其分解成块。