You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python高效查找n个最近邻向量:词嵌入相似度计算优化问询

高效计算大规模词嵌入的最近邻单词

嘿,我太懂你现在的困扰了——两百万个200维向量挨个算余弦相似度,完全是暴力解法,时间复杂度O(n),数据量上来之后速度肯定崩。咱换点高效的思路,下面几个方案从易到难,你可以按需选:

1. 先归一化向量 + 矩阵批量运算(快速优化现有逻辑)

余弦相似度的计算可以转化为归一化向量的点积(因为cosθ = (A·B)/(|A||B|),如果A和B都归一化到模长1,那直接算点积就是余弦相似度)。用numpy的矩阵运算代替Python循环,能把速度提升好几个数量级。

先预处理你的词嵌入向量(只做一次就行,后续查询直接复用):

import numpy as np

def normalize_vectors(word_vectors):
    vectors = np.array(list(word_vectors.values()))
    norms = np.linalg.norm(vectors, axis=1, keepdims=True)
    normalized_vectors = vectors / norms
    word_list = list(word_vectors.keys())
    vec_matrix = normalized_vectors
    # 返回单词列表、归一化向量矩阵、单词-向量映射
    return word_list, vec_matrix, {word: vec for word, vec in zip(word_list, normalized_vectors)}

然后修改最近邻查询函数:

def n_nearest_words(word, n, word_list, vec_matrix):
    """
    基于预归一化的向量矩阵快速计算最近邻
    :param word: 目标单词
    :param n: 需要返回的最近邻数量
    :param word_list: 所有单词的列表(和vec_matrix的行一一对应)
    :param vec_matrix: 所有归一化向量组成的numpy矩阵(每行是一个单词的向量)
    """
    target_idx = word_list.index(word)
    target_vec = vec_matrix[target_idx]
    
    # 批量计算所有向量与目标向量的点积(即余弦相似度)
    similarities = np.dot(vec_matrix, target_vec)
    
    # 取相似度最高的n+1个(去掉目标单词自己)
    top_indices = np.argsort(similarities)[::-1][1:n+1]
    
    return [word_list[idx] for idx in top_indices]

2. 用近似最近邻(ANN)库(超大规模数据的最优解)

如果两百万个向量还是觉得慢,就得用专门的近似最近邻库,比如FAISS(Facebook开源,适合大规模数据)或者Annoy(Spotify开源,轻量易用)。这些库会给向量建立索引,查询时间复杂度降到O(log n),速度提升非常明显。

示例:用FAISS实现

import faiss
import numpy as np

def build_faiss_index(word_vectors):
    word_list = list(word_vectors.keys())
    vectors = np.array(list(word_vectors.values())).astype('float32')
    faiss.normalize_L2(vectors)
    
    d = vectors.shape[1]  # 向量维度(这里是200)
    nlist = 1000  # 聚类中心数量,可根据数据量调整
    # 用内积度量(对应归一化向量的余弦相似度)
    index = faiss.IndexIVFFlat(faiss.IndexFlatIP(d), d, nlist, faiss.METRIC_INNER_PRODUCT)
    index.train(vectors)
    index.add(vectors)
    
    return word_list, index

def n_nearest_words_faiss(word, n, word_list, index, word_vectors):
    target_vec = np.array([word_vectors[word]]).astype('float32')
    faiss.normalize_L2(target_vec)
    
    # 查询n+1个最近邻,过滤掉目标单词自己
    distances, indices = index.search(target_vec, n+1)
    return [word_list[idx] for idx in indices[0] if word_list[idx] != word][:n]

示例:用Annoy实现

from annoy import AnnoyIndex

def build_annoy_index(word_vectors):
    d = 200  # 向量维度
    # angular度量对应余弦相似度
    index = AnnoyIndex(d, metric='angular')
    
    word_list = list(word_vectors.keys())
    for i, word in enumerate(word_list):
        index.add_item(i, word_vectors[word])
    
    index.build(10)  # 树的数量,值越大精度越高,速度稍慢
    return word_list, index

def n_nearest_words_annoy(word, n, word_list, index):
    target_idx = word_list.index(word)
    # 查询n+1个最近邻,过滤掉目标单词自己
    nearest_indices = index.get_nns_by_item(target_idx, n+1)
    return [word_list[idx] for idx in nearest_indices if idx != target_idx][:n]

3. 额外小技巧

  • 频繁查询时,一定要提前建好索引/归一化矩阵,不要每次查询都重复处理
  • 如果有GPU,FAISS支持GPU加速,速度还能再上一个台阶
  • 可以把词向量和单词列表保存到磁盘(比如用numpy的save/load),避免每次重启都重新处理

你原本的暴力解法代码:

def n_nearest_words(word, n, word_vectors):
    """ 
    Return a list of the n nearest words to param word, based on cosine similarity
    param wo...
    """
    # 逐一计算余弦相似度的慢逻辑

内容的提问来源于stack exchange,提问作者Mike S

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:35:16