稀疏矩阵乘法应优先使用CSR/CSC格式,COO/LIL格式会隐式转换导致慢5–10倍;推荐系统中需构建后立即转为tocsr()或tocsc()并验证类型;避免pairwise_distances全量展开、ALS中未归一化致CG不收敛、预测时用toarray()引发内存暴涨。

稀疏矩阵乘法慢?优先检查 dot() 是否用了 CSR/CSC 格式
Scipy 中 sparse.dot()(或 @ 运算符)在 CSR 和 CSC 格式下才真正高效;用 COO 或 LIL 直接相乘会先隐式转换,但这个过程不报错、不警告,只默默拖慢 5–10 倍。推荐系统里常见的用户-物品交互矩阵(如 user_item_matrix)必须在构建后立刻转成 CSR(行优先,适合按用户查)或 CSC(列优先,适合按物品查)。
- 构建完立即调用
user_item_matrix.tocsr()或.tocsc(),别等到第一次@才转 - 验证格式:打印
type(user_item_matrix),确保是scipy.sparse.csr_matrix而非coo_matrix - COO 格式只适合构造阶段追加数据;LIL 适合逐行赋值;二者都不适合计算——哪怕只是
.sum(axis=1)也会比 CSR 慢 3 倍以上
协同过滤中频繁计算相似度?避免 pairwise_distances 对稀疏矩阵全量展开
用 sklearn.metrics.pairwise_distances 计算用户相似度时,如果传入的是 csr_matrix,默认会先转成密集数组(np.array),内存直接爆掉,或触发 MemoryError。这不是 bug,是设计如此——它没为稀疏输入做路径优化。
- 改用
scipy.sparse.linalg.norm+ 手动广播:例如余弦相似度可拆解为(A @ B.T).sum(axis=1) / (norm(A, axis=1) * norm(B, axis=1)) - 或直接用
sklearn.metrics.pairwise.cosine_similarity—— 它对sparse输入有专用分支,且返回仍是稀疏矩阵 - 若需 top-K 相似用户,别先算全矩阵再
argsort;用scipy.sparse.linalg.expm_multiply不适用,但可用scipy.sparse.csr_matrix.sum_duplicates()配合np.argpartition做局部筛选
ALS 训练卡在 linalg.cg?确认求解器输入是否已预处理
隐语义模型(如 Spark MLlib 的 ALS 或自研实现)常调用 scipy.sparse.linalg.cg 解线性系统,但该函数对矩阵条件数极度敏感。原始交互矩阵若含大量零值+极少数正样本,未归一化会导致收敛极慢甚至不收敛,错误信息可能是 ConvergenceWarning: CG iteration did not converge。
- 在构建
X(用户特征)和Y(物品特征)前,对每行/列做 L2 归一化:user_item_matrix = normalize(user_item_matrix, norm='l2', axis=1) - 避免用
dtype=float32——cg在 float32 下迭代误差累积快,建议全程用float64 - 若仍不收敛,换
scipy.sparse.linalg.lsqr,它对病态系统更鲁棒,且支持显式指定damp参数加岭惩罚
预测时批量打分变慢?别用 toarray() 展开稀疏结果
生成推荐列表时,常见写法是 score_matrix.toarray()[user_id],这会把整行(可能百万列)转成 dense array,内存占用飙升,GC 压力大。实际只需要 top-K 索引,完全不必展开。
立即学习“Python免费学习笔记(深入)”;
- 用
scipy.sparse.csr_matrix.indices和.data配合np.argpartition:例如row = score_matrix.getrow(user_id); k_idx = np.argpartition(row.data, -K)[-K:];top_items = row.indices[k_idx] - 若需按分排序,只对
row.data[k_idx]再argsort,而非全量排序 - 注意:
getrow()返回仍是 sparse,但.data是 dense numpy array,长度仅等于该行非零元个数,通常远小于总物品数
稀疏运算的性能陷阱往往不在算法本身,而在格式误用、隐式转换和“看起来安全”的 dense 回退操作。只要守住 CSR/CSC 主线、绕开 toarray() 和全量 pairwise_distances,90% 的卡顿就消失了。


















