- Published on
协同过滤推荐系统实践实验
- Authors

- Name
- Allen Wang
欢迎来到这场关于协同过滤推荐系统的实践之旅!在本次实验中,您将实现协同过滤算法,并将其应用于电影评分数据集。通过学习,您将了解如何通过用户的评分数据生成个性化的电影推荐。
大纲
包
在本实验中,我们将使用以下 Python 包:
- NumPy:用于高效的数值计算和数组操作。
- TensorFlow:用于构建和训练机器学习模型。
recsys_utils.py:包含辅助函数,用于加载数据和处理。
import numpy as np
import tensorflow as tf
from tensorflow import keras
from recsys_utils import *
1 - 符号说明
在本实验中,我们将使用以下符号来表示变量和参数:
| 通用符号 | 描述 | Python(如果有) |
|---|---|---|
| 标量;如果用户 评分了电影 ,则 = 1,否则 = 0 | ||
| 标量;用户 对电影 的评分(如果 ) | ||
| 向量;用户 的参数 | ||
| 标量;用户 的偏置参数 | ||
| 向量;电影 的特征向量 | ||
| 用户数 | num_users | |
| 电影数 | num_movies | |
| 特征数 | num_features | |
| 矩阵,由 组成 | X | |
| 矩阵,由 组成 | W | |
| 向量,由 组成 | b | |
| 矩阵,由 组成 | R |
2 - 推荐系统
协同过滤是一种强大的推荐系统技术,它通过分析用户之间的相似性来预测用户对未评分项目的偏好。在本实验中,我们将实现协同过滤算法,并将其应用于电影评分数据集。
协同过滤的目标是为每个用户生成一个“参数向量”,反映其对电影的偏好,同时为每个电影生成一个“特征向量”。通过计算用户参数向量和电影特征向量的点积(加上偏置项),我们可以预测用户对特定电影的评分。
以下是协同过滤的学习过程:
- 学习阶段:使用现有的用户评分数据(如矩阵 和指示矩阵 )来训练模型,学习用户参数 、电影特征 和偏置 。
- 预测阶段:使用训练好的模型为用户推荐未评分的电影。
3 - 电影评分数据集
本实验使用的数据集来自 MovieLens "ml-latest-small" 数据集。该数据集包含了从 2000 年以来的一些电影的评分信息,共包括 个用户和 部电影。
- 数据加载:我们将使用
load_ratings_small()函数加载评分数据到矩阵 和 中。 - :一个 的矩阵,存储用户对电影的评分(评分范围为 0.5 ~ 5,步长为 0.5;未评分的电影为 0)。
- :一个二进制指示矩阵, 如果用户 对电影 进行了评分,否则为 0。
此外,我们还将加载预计算的参数 、 和 ,这些参数将在后续的训练中被优化。
# 加载数据
X, W, b, num_movies, num_features, num_users = load_precalc_params_small()
Y, R = load_ratings_small()
print("Y", Y.shape, "R", R.shape)
print("X", X.shape)
print("W", W.shape)
print("b", b.shape)
print("num_features", num_features)
print("num_movies", num_movies)
print("num_users", num_users)
输出:
Y (4778, 443) R (4778, 443)
X (4778, 10)
W (443, 10)
b (1, 443)
num_features 10
num_movies 4778
num_users 443
- 数据统计:例如,我们可以计算第一部电影的平均评分。
tsmean = np.mean(Y[0, R[0, :].astype(bool)])
print(f"电影 1 的平均评分:{tsmean:0.3f} / 5")
输出:
电影 1 的平均评分:3.400 / 5
4 - 协同过滤学习算法
协同过滤的核心在于学习参数 、 和 ,以最小化预测误差。具体来说,我们需要实现协同过滤的成本函数,并使用梯度下降法优化参数。
4.1 协同过滤成本函数
协同过滤的成本函数定义如下:
- 第一部分:衡量模型预测与实际评分之间的误差。
- 第二部分:正则化项,用于防止过拟合。
练习 1:实现协同过滤成本函数
以下是需要实现的成本函数 cofi_cost_func:
def cofi_cost_func(X, W, b, Y, R, lambda_):
"""
返回协同过滤的成本。
参数:
X (ndarray (num_movies, num_features)): 电影特征矩阵
W (ndarray (num_users, num_features)): 用户参数矩阵
b (ndarray (1, num_users)): 用户偏置向量
Y (ndarray (num_movies, num_users)): 用户对电影的评分矩阵
R (ndarray (num_movies, num_users)): 指示矩阵,R(i,j) = 1 如果用户 j 评分了电影 i
lambda_ (float): 正则化参数
返回:
J (float): 成本值
"""
nm, nu = Y.shape
J = 0
### START CODE HERE ###
for j in range(nu):
w = W[j, :]
b_j = b[0, j]
for i in range(nm):
x = X[i, :]
y = Y[i, j]
r = R[i, j]
J += np.square(r * (np.dot(w, x) + b_j - y))
J = J / 2
J += (lambda_ / 2) * (np.sum(np.square(W)) + np.sum(np.square(X)))
### END CODE HERE ###
return J
5 - 学习电影推荐
现在,我们将使用梯度下降法优化参数 、 和 。以下是训练过程:
# 初始化参数
tf.random.set_seed(1234)
W = tf.Variable(tf.random.normal((num_users, num_features), dtype=tf.float64), name='W')
X = tf.Variable(tf.random.normal((num_movies, num_features), dtype=tf.float64), name='X')
b = tf.Variable(tf.random.normal((1, num_users), dtype=tf.float64), name='b')
# 优化器
optimizer = keras.optimizers.Adam(learning_rate=1e-1)
# 训练循环
iterations = 200
lambda_ = 1
for iter in range(iterations):
with tf.GradientTape() as tape:
cost_value = cofi_cost_func_v(X, W, b, Ynorm, R, lambda_)
grads = tape.gradient(cost_value, [X, W, b])
optimizer.apply_gradients(zip(grads, [X, W, b]))
if iter % 20 == 0:
print(f"Training loss at iteration {iter}: {cost_value:0.1f}")
6 - 推荐
使用训练好的模型,我们可以为用户生成电影推荐。例如:
# 预测所有电影的评分
p = np.matmul(X.numpy(), np.transpose(W.numpy())) + b.numpy()
my_predictions = p[:, 0]
# 排序并显示推荐
ix = tf.argsort(my_predictions, direction='DESCENDING')
for i in range(10):
j = ix[i]
print(f'Predicting rating {my_predictions[j]:0.2f} for movie {movieList[j]}')
6.1 为什么协同过滤有时推荐得很“像”
如果你看到推荐列表风格过于集中(比如都是同类电影),通常不是程序出错,而是模型偏向“最稳妥的偏好方向”。
常见原因:
- 用户历史评分覆盖面窄,向量空间信息不足
- 热门电影样本更多,梯度更新更稳定
- 正则化偏大,导致模型偏保守
可以尝试:
- 轻微降低
lambda_,观察个性化变化 - 在重排阶段增加多样性约束(同题材去重)
- 对冷门高分电影增加探索权重
6.2 协同过滤调试检查清单
-
Y与R的 shape 是否一致 - 评分是否先做了均值归一化
- 训练损失是否稳定下降
- 是否出现
nan/inf - 推荐结果是否被“热门偏差”主导
6.3 交互式可视化
7 - 总结 通过本次实验,你成功掌握了协同过滤推荐系统的核心实现方法。以下是本次学习的关键收获: -
算法实践:亲手实现了协同过滤的成本函数与梯度下降优化,深入理解了用户参数与电影特征之间的交互原理。 - 模型训练:运用TensorFlow搭建推荐模型,通过200次迭代优化,显著降低了预测评分与实际评分的误差。 - 实战应用:基于MovieLens数据集,构建了能输出个性化电影推荐的系统,并为用户生成TOP10推荐列表。 - 正则化技巧:在成本函数中引入L2正则化项,有效避免了过拟合问题,提升了模型的泛化能力。 下一步,你可以尝试调整学习率(如0.05或0.2)、正则化参数(如0.5或2),观察模型效果变化,或在完整版MovieLens数据集(包含27000部电影)上挑战更大规模的推荐任务。
完整代码已开源在GitHub仓库
本篇文章的部分内容和思想参考了 吴恩达 (Andrew Ng) 在 Coursera 机器学习课程 中的讲解,感谢他对机器学习领域的卓越贡献。