You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

构建图书推荐系统计算余弦相似度遇内存不足问题求助

解决大规模图书推荐系统中余弦相似度计算的内存问题

以下是针对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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 08:15:44