最可靠的方法是手动用 numpy.dot 和 numpy.linalg.norm 计算余弦相似度:cosθ = dot(a, b) / (norm(a) * norm(b)),需确保向量为1D、非零且已归一化,避免隐式行为和形状错误。
用 numpy.dot 和 numpy.linalg.norm 手动计算最可靠
直接调用现成的余弦相似度函数容易踩坑——比如 sklearn.metrics.pairwise.cosine_similarity 默认返回二维数组,而你可能只想要一个标量;又或者输入向量没做归一化,结果就错了。手动算反而更可控。
核心公式是:cosθ = dot(a, b) / (norm(a) * norm(b)),只要两个向量非零,这个值就在 [-1, 1] 范围内。
-
numpy.dot(a, b)计算点积,要求两个向量维度一致且为 1D(shape 为(n,)) -
numpy.linalg.norm(a)默认计算 L2 范数,等价于sqrt(sum(a**2)) - 如果输入是行向量(shape
(1, n)),先用a.flatten()或a.squeeze()降维,否则dot可能返回矩阵而非标量 - 务必检查分母是否为 0:任一向量全为 0 时
norm返回 0,会导致除零警告甚至 NaN
避免 np.inner 和 np.vdot 的隐式行为
np.inner 对 1D 向量等价于 dot,但对高维数组行为不同(例如对 shape (n, 1) 的列向量,inner 会广播并返回 shape (n, n) 结果);np.vdot 则自动对第一个参数取共轭,对实数虽无影响,但一旦后续接入复数数据就会出错。
- 坚持用
np.dot+np.linalg.norm组合,语义清晰、行为稳定 - 若已知向量为列向量(如从
pandas.DataFrame.values直接取的),别直接传给dot,先做a.ravel() - 不要依赖
np.matmul:它不支持纯 1D 输入,必须至少有一维是 2D,易触发ValueError: matmul: Input operand X does not have enough dimensions
处理批量向量对时注意广播与形状对齐
如果要一次性算多对向量(比如 100 个 query 向量 vs 100 个 doc 向量),别用循环套 dot,而是用广播或矩阵乘法加速。
Python 3.14.3
微软官方的 Python 扩展,是 VS Code 安装量最高的扩展(209M+)。集成 IntelliSense(通过 Pylance)、调试(通过 Python Debugger)、代码检查、格式化、重构和单元测试等功能。支持 Jupyter Notebook、虚拟环境管理和多 Python 版本切换。
- 两组行向量:shape 分别为
(m, d)和(n, d),可用result = (A @ B.T) / (np.linalg.norm(A, axis=1, keepdims=True) * np.linalg.norm(B, axis=1, keepdims=True).T) - 如果只是单对向量但存成二维(如
a.shape == (1, d)),别用@直接乘,先a[0]取出再算,否则结果 shape 是(1, 1),不是你要的标量 -
keepdims=True在norm中很关键:它让除法能正确广播;漏掉会导致ValueError: operands could not be broadcast together
NaN 或 inf 出现时优先查向量范数和原始数据
得到 nan 或 inf 不代表公式写错了,大概率是输入本身有问题。
- 运行
np.any(np.isnan(a)) or np.any(np.isinf(a))检查原始向量 - 打印
np.linalg.norm(a)和np.linalg.norm(b),看是否为 0 或极小值(如1e-18),这说明向量几乎为零,数值不稳定 - 避免用
np.nan_to_num粗暴替换:它会把nan变成 0,但余弦相似度为 0 和未定义是两回事;更稳妥的是提前过滤或打日志告警
实际用的时候,最常被忽略的是向量形状和零向量检查——尤其当数据来自文件读取或模型输出时,看似是向量,实则多了一维或含缺失值。公式再简单,输进去的数据不对,结果就不可信。
就爱读