构建图书推荐系统计算余弦相似度遇内存不足问题求助
解决大规模图书推荐系统中余弦相似度计算的内存问题
以下是针对10万级样本余弦相似度计算内存溢出问题的实用解决方案:
1. 放弃全量相似度矩阵,只计算Top-N相似对
不需要生成完整的100,000×100,000矩阵,只保留每个图书最相似的N个结果(比如Top50),内存占用直接从百GB级降到MB/GB级。
示例代码(结合sklearn和numpy高效实现):
import numpy as np from sklearn.metrics.pairwise import cosine_similarity # 假设features是形状为(100000, feature_dim)的特征矩阵 top_n = 50 block_size = 1000 # 分块处理,避免单次计算内存过载 results = [] for start_idx in range(0, features.shape[0], block_size): end_idx = min(start_idx + block_size, features.shape[0]) current_block = features[start_idx:end_idx] # 计算当前块与全量特征的相似度 sim_scores = cosine_similarity(current_block, features) # 排除自身相似度(对角线值),取Top-N的索引和分数 top_indices = np.argpartition(sim_scores, -top_n-1, axis=1)[:, -top_n-1:-1] top_scores = sim_scores[np.arange(current_block.shape[0])[:, None], top_indices] # 记录当前块的图书索引、相似图书索引及分数 for i in range(current_block.shape[0]): book_id = start_idx + i results.append({ "book_id": book_id, "similar_book_ids": top_indices[i], "similarity_scores": top_scores[i] }) # 后续可将results存入数据库或Parquet文件
2. 用稀疏矩阵存储与计算
如果你的特征本身是稀疏的(比如词袋编码、多热编码),直接用稀疏矩阵处理,避免稠密矩阵的内存浪费,sklearn的余弦相似度计算支持稀疏输入。
示例代码:
from scipy.sparse import csr_matrix from sklearn.metrics.pairwise import cosine_similarity # 将稠密特征转为稀疏矩阵(假设features是稠密矩阵) sparse_features = csr_matrix(features) # 计算稀疏矩阵的余弦相似度,结果也是稀疏矩阵 sparse_sim_matrix = cosine_similarity(sparse_features) # 过滤低相似度值,进一步压缩矩阵(比如只保留相似度>0.5的对) sparse_sim_matrix = sparse_sim_matrix.multiply(sparse_sim_matrix > 0.5) sparse_sim_matrix.eliminate_zeros() # 稀疏矩阵可直接存入磁盘,内存占用仅为非零元素的大小 sparse_sim_matrix.save_npz("cosine_similarity_sparse.npz")
3. 降低数值精度
将特征矩阵的数值类型从float64转为float32(甚至float16,如果精度要求不高),内存占用直接减半或降至1/4,计算相似度时也用低精度存储结果。
示例代码:
# 转换特征矩阵精度 features_float32 = features.astype(np.float32) # 计算相似度时结果也为float32 sim_matrix = cosine_similarity(features_float32).astype(np.float32)
4. 分块计算并持久化到磁盘
把全量样本分成若干块,计算块与块之间的相似度,每计算完一块就写入磁盘(比如用HDF5、Parquet格式),不把所有结果放在内存中。后续查询时再读取对应块的数据。
示例代码(用Pandas分块写入):
import pandas as pd from sklearn.metrics.pairwise import cosine_similarity block_size = 5000 total_blocks = (100000 + block_size - 1) // block_size for i in range(total_blocks): for j in range(i, total_blocks): # 只计算上三角,避免重复 block_i = features[i*block_size : (i+1)*block_size] block_j = features[j*block_size : (j+1)*block_size] sim_block = cosine_similarity(block_i, block_j) # 构建DataFrame存储块内相似度 sim_df = pd.DataFrame(sim_block) sim_df.index = [f"book_{x}" for x in range(i*block_size, (i+1)*block_size)] sim_df.columns = [f"book_{x}" for x in range(j*block_size, (j+1)*block_size)] # 写入HDF5文件 sim_df.to_hdf("similarity_blocks.h5", key=f"block_{i}_{j}", mode="a")
5. 先降维再计算相似度
用PCA、截断奇异值分解(TSVD)等方法把高维特征降到低维(比如200维),再计算余弦相似度。降维后特征矩阵的内存占用降低,相似度计算的复杂度也会大幅下降,同时可以结合前面的Top-N或稀疏矩阵方法进一步优化。
示例代码(用TSVD降维):
from sklearn.decomposition import TruncatedSVD # 降维到200维 svd = TruncatedSVD(n_components=200, random_state=42) reduced_features = svd.fit_transform(features) # 基于降维后的特征计算Top-N相似度(参考方法1的代码)
6. 用近似近邻搜索替代精确计算
如果不需要绝对精确的相似度结果,用局部敏感哈希(LSH)、Annoy、Faiss等工具做近似近邻搜索,这些工具专门针对大规模数据设计,能在内存友好的前提下快速找到相似样本。
示例代码(用Annoy):
from annoy import AnnoyIndex # 假设特征维度是feature_dim feature_dim = features.shape[1] index = AnnoyIndex(feature_dim, metric='angular') # angular等价于余弦相似度 # 构建索引 for i in range(features.shape[0]): index.add_item(i, features[i]) index.build(10) # 10棵树,平衡速度与精度 # 保存索引到磁盘 index.save('book_recommendation_index.ann') # 查询某本书的Top50相似书 similar_book_ids = index.get_nns_by_item(0, 51)[1:] # 排除自身
内容的提问来源于stack exchange,提问作者Alisahib Qadimov
相关产品推荐
相关产品推荐

