用Python手把手实现矩阵分解推荐算法(附完整代码与数据集)

推荐系统已经成为现代互联网服务的核心组件之一,从电商平台到流媒体服务,无处不在。在众多推荐算法中,矩阵分解因其简洁性和有效性而备受青睐。本文将带你从零开始,用Python实现两种主流的矩阵分解优化方法——交替最小二乘法(ALS)和随机梯度下降(SGD),并使用MovieLens数据集进行实战验证。

1. 矩阵分解基础与核心概念

矩阵分解(Matrix Factorization, MF)是协同过滤推荐系统中的一种经典技术。它的核心思想是将用户-物品评分矩阵分解为两个低维矩阵的乘积,从而发现用户和物品之间的潜在特征。

关键术语解释

  • 用户矩阵:每行代表一个用户在潜在特征空间中的表示
  • 物品矩阵:每列代表一个物品在相同潜在特征空间中的表示
  • 潜在因子:隐含的特征维度,通常远小于用户或物品的数量

为什么矩阵分解有效?想象一下电影推荐场景:

用户A: [动作片爱好者]
用户B: [浪漫喜剧爱好者]

电影X: [动作元素强烈]
电影Y: [浪漫元素浓厚]

即使没有明确的标签,矩阵分解也能自动学习到这些隐含特征,并预测用户对未评分电影的偏好。

注意:潜在因子数量(k)是一个超参数,需要根据数据集大小和计算资源合理选择。通常k值在10-200之间。

2. 环境准备与数据加载

在开始实现算法前,我们需要准备Python环境和数据集。以下是所需的工具和库:

# 必需库安装
pip install numpy pandas scikit-learn

我们将使用经典的MovieLens 100K数据集,它包含:

  • 100,000条评分(1-5星)
  • 943位用户
  • 1682部电影
import numpy as np
import pandas as pd

# 加载数据集
ratings = pd.read_csv('ml-100k/u.data', sep='\t', 
                     names=['user_id', 'item_id', 'rating', 'timestamp'])

数据预处理步骤:

  1. 创建用户-物品评分矩阵
  2. 处理缺失值(通常用平均分填充)
  3. 必要时进行归一化处理
# 创建评分矩阵
n_users = ratings['user_id'].nunique()
n_items = ratings['item_id'].nunique()

R = np.zeros((n_users, n_items))
for row in ratings.itertuples():
    R[row.user_id-1, row.item_id-1] = row.rating

3. ALS算法实现与优化

交替最小二乘法(ALS)是矩阵分解中最常用的优化方法之一。其核心思想是固定一个矩阵,优化另一个矩阵,交替进行直到收敛。

ALS算法步骤

  1. 随机初始化用户矩阵U和物品矩阵V
  2. 固定V,优化U
  3. 固定U,优化V
  4. 重复2-3步直到收敛或达到最大迭代次数

数学推导:

目标是最小化损失函数:

L = Σ(r_ui - u_i·v_j)^2 + λ(||U||^2 + ||V||^2)

其中λ是正则化系数,防止过拟合。

Python实现关键代码:

def als(R, k=20, max_iter=100, lambda_=0.1):
    m, n = R.shape
    U = np.random.rand(m, k)
    V = np.random.rand(n, k)
    
    for iter in range(max_iter):
        # 固定V,优化U
        for i in range(m):
            V_i = V[R[i, :] > 0]
            A = V_i.T @ V_i + lambda_ * np.eye(k)
            b = V_i.T @ R[i, R[i, :] > 0]
            U[i, :] = np.linalg.solve(A, b)
        
        # 固定U,优化V
        for j in range(n):
            U_j = U[R[:, j] > 0]
            A = U_j.T @ U_j + lambda_ * np.eye(k)
            b = U_j.T @ R[R[:, j] > 0, j]
            V[j, :] = np.linalg.solve(A, b)
        
        # 计算当前损失
        error = compute_error(R, U, V, lambda_)
        if error < 0.01:  # 收敛条件
            break
            
    return U, V

ALS的优点:

  • 每次迭代都有闭式解,保证收敛
  • 适合并行化处理
  • 对稀疏矩阵友好

4. SGD算法实现与调优

随机梯度下降(SGD)是另一种常用的优化方法,特别适合大规模数据集。与ALS不同,SGD通过逐个样本更新参数,计算效率更高。

SGD算法步骤

  1. 随机初始化U和V
  2. 随机选择一个评分样本(r_ij)
  3. 计算预测误差 e_ij = r_ij - u_i·v_j
  4. 更新u_i和v_j: u_i = u_i + γ(e_ij * v_j - λu_i) v_j = v_j + γ(e_ij * u_i - λv_j)
  5. 重复2-4步直到收敛

Python实现关键代码:

def sgd(R, k=20, max_iter=100, gamma=0.01, lambda_=0.1):
    m, n = R.shape
    U = np.random.rand(m, k)
    V = np.random.rand(n, k)
    
    # 获取所有已知评分的索引
    rows, cols = np.where(R > 0)
    indices = list(zip(rows, cols))
    
    for iter in range(max_iter):
        np.random.shuffle(indices)
        
        for i, j in indices:
            # 计算预测误差
            prediction = np.dot(U[i, :], V[j, :])
            e_ij = R[i, j] - prediction
            
            # 更新规则
            U[i, :] += gamma * (e_ij * V[j, :] - lambda_ * U[i, :])
            V[j, :] += gamma * (e_ij * U[i, :] - lambda_ * V[j, :])
        
        # 计算当前损失
        error = compute_error(R, U, V, lambda_)
        if error < 0.01:  # 收敛条件
            break
            
    return U, V

SGD参数调优建议:

参数 推荐范围 影响
学习率(γ) 0.001-0.1 太大导致震荡,太小收敛慢
正则化系数(λ) 0.01-0.2 控制模型复杂度
潜在因子数(k) 10-200 影响模型表达能力

5. 算法评估与实战对比

实现完两种算法后,我们需要评估它们的性能和推荐质量。常用的评估指标包括:

  1. 均方根误差(RMSE):衡量预测评分与实际评分的差异
  2. 平均绝对误差(MAE):另一种误差度量方式
  3. Top-N推荐准确率:评估推荐列表的质量

计算RMSE的函数:

def compute_rmse(R, U, V):
    pred = U @ V.T
    mask = R > 0
    return np.sqrt(np.mean((pred[mask] - R[mask])**2))

在实际项目中,我们还需要考虑:

  • 冷启动问题:如何处理新用户或新物品
  • 可扩展性:算法在大规模数据上的表现
  • 实时性:能否支持实时推荐

ALS与SGD对比实验:

# 运行两种算法
U_als, V_als = als(R, k=50, max_iter=50)
U_sgd, V_sgd = sgd(R, k=50, max_iter=50)

# 计算误差
rmse_als = compute_rmse(R, U_als, V_als)
rmse_sgd = compute_rmse(R, U_sgd, V_sgd)

print(f"ALS RMSE: {rmse_als:.4f}")
print(f"SGD RMSE: {rmse_sgd:.4f}")

典型结果对比:

指标 ALS SGD
RMSE 0.92 0.89
训练时间 较长 较短
内存使用 较高 较低

6. 生产环境优化建议

将矩阵分解算法应用到实际生产环境时,还需要考虑以下优化方向:

性能优化技巧

  • 使用稀疏矩阵存储格式(如CSR)处理大型数据集
  • 实现增量更新,避免每次全量训练
  • 利用GPU加速矩阵运算
# 使用稀疏矩阵示例
from scipy.sparse import csr_matrix

R_sparse = csr_matrix(R)

推荐系统架构设计

  1. 离线训练:定期更新用户和物品矩阵
  2. 近线计算:处理用户实时行为
  3. 在线服务:快速响应推荐请求

混合推荐策略

  • 结合基于内容的推荐
  • 加入时间衰减因子
  • 融合多种推荐算法结果

实际部署时,一个常见的架构是将训练好的模型导出为服务:

# 保存模型
np.save('user_matrix.npy', U)
np.save('item_matrix.npy', V)

# 加载模型进行预测
def predict_rating(user_id, item_id):
    return np.dot(U[user_id], V[item_id])

7. 进阶方向与扩展阅读

掌握了基础矩阵分解后,你可以进一步探索以下高级技术:

  1. 带偏置的矩阵分解

    • 考虑用户和物品的偏差项
    • 改进的预测公式:r̂_ui = μ + b_i + b_j + u_i·v_j
  2. 时间敏感的矩阵分解

    • 加入时间衰减因子
    • 捕捉用户兴趣随时间的变化
  3. 深度学习扩展

    • 神经矩阵分解(NeuMF)
    • 使用自编码器进行非线性分解

推荐阅读资源:

  • Matrix Factorization Techniques for Recommender Systems (Koren et al.)
  • Collaborative Filtering for Implicit Feedback Datasets (Hu et al.)
  • Surprise库(https://surpriselib.com) - Python推荐系统工具包

实现一个完整的推荐系统需要考虑的远不止算法本身,还包括数据管道、AB测试、监控报警等工程实践。但矩阵分解作为推荐系统的基石之一,掌握它将为你打下坚实的基础。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐