最稳妥方式是直接调用sklearn.metrics.pairwise.cosine_similarity,它自动归一化并处理零向量、稀疏矩阵和维度对齐;输入必须为二维数组(如[a]、[b]或X.reshape(1,-1)),否则报错。

用 cosine_similarity 计算两个向量的余弦相似度
直接调用 sklearn.metrics.pairwise.cosine_similarity 是最稳妥的方式,它内部会自动归一化向量(L2范数为1),再计算点积。别自己手写公式——容易漏掉归一化或处理零向量。
常见错误是传入一维数组:该函数默认把每行当作一个样本,所以必须确保输入是二维的(哪怕只有一对向量):
from sklearn.metrics.pairwise import cosine_similarity import numpy as np <p>a = np.array([1, 2, 3]) b = np.array([4, 5, 6])</p><h1>❌ 错误:一维数组会报 ValueError: Expected 2D array</h1><h1>cosine_similarity(a, b)</h1><h1>✅ 正确:reshape 成 (1, -1) 或用 np.vstack</h1><p>sim = cosine_similarity([a], [b])[0][0] # 输出约 0.974</p>
批量计算多个样本两两之间的余弦相似度矩阵
当有 N 个样本(比如文本 TF-IDF 向量),想一次性得到 N×N 的相似度矩阵,直接传入二维 X 即可。注意输出是方阵,result[i][j] 表示第 i 个样本与第 j 个样本的相似度。
立即学习“Python免费学习笔记(深入)”;
- 输入
X形状应为(n_samples, n_features),例如 TF-IDF 矩阵、嵌入向量堆叠结果 - 如果只传一个
X,结果是对称矩阵;传X和Y则返回(len(X), len(Y))的矩形矩阵 - 性能上,该函数底层调用稀疏矩阵优化(若输入是
scipy.sparse类型),所以对高维稀疏特征(如文本)很友好
X = np.array([[1, 0, 1],
[0, 1, 1],
[1, 1, 0]])
<p>sim_matrix = cosine_similarity(X)</p><h1>sim_matrix[0, 1] 就是第一和第二个样本的余弦相似度</h1>为什么不用 numpy.dot 手动实现?
手动算 np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)) 看似简单,但实际容易出问题:
- 没处理零向量:一旦
a或b全为 0,np.linalg.norm返回 0,导致除零警告甚至 NaN - 没考虑稀疏性:对大型稀疏矩阵,
np.dot会转成稠密计算,内存爆炸 - 没对齐维度:一维 vs 二维、行向量 vs 列向量混淆,容易得到标量而非期望形状
-
cosine_similarity内部做了这些防护,还支持dtype保持、NaN 忽略策略(通过force_all_finite=False)
遇到 ValueError: Found array with 0 sample(s) 怎么办?
这个错误通常不是数据为空,而是输入被误判为“0 个样本”——根本原因是传了空列表、None、或者 shape 为 (0, n) 的数组。
- 检查是否用了
list()或[]直接传入,比如cosine_similarity([]) - 检查是否从 DataFrame 取列后忘了
.values或.to_numpy(),导致传入的是pandas.Series(某些旧版本不兼容) - 检查稀疏矩阵是否真的非空:
if X.nnz == 0:,空稀疏矩阵也会触发此错 - 调试时加一句
print(X.shape, type(X)),比猜更快
余弦相似度本身不关心量纲,但极度依赖向量方向;如果你发现结果全是 1 或 0,大概率是输入向量被意外标准化过两次,或特征预处理阶段就做了 L2 归一化——这时再套一层 cosine_similarity 就多余了。


















