必须先向量化再用numpy计算文本相似度;用sklearn的TfidfVectorizer或CountVectorizer转稀疏矩阵,再调.toarray()得ndarray,避免object数组;余弦相似度需防零向量和精度问题,推荐用sklearn校验手写结果。

直接用 numpy 做文本相似度,必须先完成向量化——numpy 本身不处理文本,它只算向量;跳过向量化直接调 np.dot 或 np.linalg.norm 会报 TypeError: unsupported operand type(s)。
怎么把句子转成 numpy 向量(词袋/TF-IDF)
核心是用 sklearn.feature_extraction.text 配合 numpy:先用 TfidfVectorizer 或 CountVectorizer 得到稀疏矩阵,再用 .toarray() 转成 ndarray。
常见错误:直接对原始字符串列表调 np.array(),结果得到的是 object 类型数组,后续无法做点积运算。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 用
TfidfVectorizer(max_features=10000, stop_words='english')控制维度,避免内存爆炸 - 调用
fit_transform(sentences)后立刻接.toarray(),别留着稀疏矩阵直接传给np.cosine(scipy.spatial.distance.cosine虽能处理稀疏矩阵,但numpy原生函数不行) - 如果语料小且词表稳定,可用
CountVectorizer(vocabulary=known_vocab)固定列顺序,方便多批次向量对齐
用 numpy 算余弦相似度的三种写法及坑
余弦相似度 = np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)),但要注意分母为 0(零向量)和浮点精度问题。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 批量计算时别用 for 循环两两调
np.dot,改用矩阵乘法:sim_matrix = vectors @ vectors.T,再除以模长外积(用np.outer(norms, norms)) - 零向量必须提前过滤或加极小值保护,否则出现
nan或inf;可写成norms = np.linalg.norm(vectors, axis=1, keepdims=True); norms = np.where(norms == 0, 1e-8, norms) -
np.einsum('ij,ij->i', a, b)比np.sum(a * b, axis=1)略快,但可读性差,小规模没必要
为什么不用 scikit-learn 的 cosine_similarity?
它底层确实调了 numpy,但封装了归一化、稀疏支持和广播逻辑;自己手写容易漏掉边界情况,比如单个向量输入时维度丢失(vectors[0] 是 1D,但 np.dot(vectors[0], vectors[0]) 正确,np.dot(vectors[0:1], vectors[0:1].T) 才得 2D 结果)。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 调试阶段用
sklearn.metrics.pairwise.cosine_similarity校验手写结果,尤其检查 shape 是否一致(如(n, n)vs(n,)) - 生产环境若已确定输入无零向量、且向量数固定,手写能省掉 sklearn 依赖,但需补全
dtype=float32强制转换,防止 int64 向量点积溢出 - 注意
cosine_similarity返回的是相似度(越大越相似),而scipy.spatial.distance.cosine返回的是距离(越小越相似),别混用
真正麻烦的从来不是公式,而是向量化后词表不一致、停用词没清干净、或者某句全是标点导致变成零向量——这些都会让 np.linalg.norm 返回 0,后续所有相似度崩成 nan。


















