余弦相似度计算难点在于文本向量化:tf-idf轻量但需规范预处理和fit/transform分离,sentence-bert效果好但需gpu;检索须批量计算相似度并取top-k而非设固定阈值。

余弦相似度计算本身不难,难点在向量怎么来
直接用 sklearn.metrics.pairwise.cosine_similarity 算两个向量?那只是数学验证,不是文档检索。真实场景里,文档是文本,得先变成固定长度的稠密向量——靠 TF-IDF 或词嵌入(如 Sentence-BERT)生成。TF-IDF 快、轻量,适合中小规模语料;BERT 类模型效果好但慢,且需 GPU 加速。别一上来就上 all-MiniLM-L6-v2,先跑通 TF-IDF 版本,确认 pipeline 没漏掉预处理(比如小写、去停用词、词干化)。
TF-IDF 向量化时,fit 和 transform 必须分开用
常见错误:对查询文本也调用 fit_transform,导致查询向量维度和文档库不一致,cosine_similarity 直接报 ValueError: matrices are not aligned。正确做法是:只对文档集调一次 vectorizer.fit_transform(documents),之后所有查询都用同一个 vectorizer.transform(query)。注意 transform 输入必须是 list of str,单个字符串要包成 [query],否则会按字符切分。
检索时别用 for 循环遍历算相似度
用 cosine_similarity 一次性传入查询向量和整个文档矩阵,它返回一行数组,每个值对应一个文档的相似分。循环调用 cosine_similarity 每次只算一个文档,性能差几十倍。示例:
import numpy as np<br>from sklearn.feature_extraction.text import TfidfVectorizer<br>from sklearn.metrics.pairwise import cosine_similarity<br><br>docs = ["apple pie recipe", "banana bread baking", "chocolate cake"]<br>vectorizer = TfidfVectorizer()<br>doc_vecs = vectorizer.fit_transform(docs)<br><br>query = ["apple cake"]<br>query_vec = vectorizer.transform(query)<br>sim_scores = cosine_similarity(query_vec, doc_vecs).flatten() # 一行,三个分数
相似度高 ≠ 文档相关,得看阈值和排序稳定性
余弦值在 [−1, 1] 区间,但 TF-IDF 向量全非负,实际输出总在 [0, 1]。0.3 可能已经是最高分了,硬设 0.7 阈值会漏掉所有结果。更稳妥的是取 top-k(比如 k=5),再人工抽检。另外,TF-IDF 对词序、同义词不敏感,两篇内容相近但用词差异大的文档可能得分很低——这不是代码 bug,是方法局限。真要提升召回,得换 Sentence-BERT,但记住:它的 encode() 输出默认是 384 维,别和 TF-IDF 的几千维向量混着算相似度。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











