Course 3:推荐系统
课程简介
协同过滤、基于内容的推荐、矩阵分解。
一、推荐系统概述
推荐系统是机器学习在商业中应用最广泛的领域之一。从亚马逊的商品推荐到 Netflix 的影视推荐,从抖音的视频推荐到 Spotify 的音乐推荐——推荐系统直接推动着现代互联网经济的核心体验。
1.1 问题定义
推荐系统的目标是预测用户对未交互过的物品的偏好,并推荐最可能被用户喜欢的物品。
形式化定义:给定 m 个用户和 n 个物品,构建一个 m×n 的评分预测矩阵 R,其中 R_{ij} 表示用户 i 对物品 j 的预测评分。
1.2 推荐系统的类型
- 基于内容的推荐:根据用户和物品的特征做匹配
- 协同过滤:利用用户群体的集体行为来做推荐
- 混合方法:结合多种方法
二、协同过滤
2.1 基于用户的协同过滤
核心思想:找到与当前用户兴趣相似的其他用户,推荐那些相似用户喜欢但当前用户没见过的物品。
用户相似度计算(使用余弦相似度):
$$\text{sim}(u, v) = \frac{r_u \cdot r_v}{|r_u| \cdot |r_v|} = \frac{\sum_{i} r_{ui} r_{vi}}{\sqrt{\sum_i r_{ui}^2} \sqrt{\sum_i r_{vi}^2}}$$
2.2 基于物品的协同过滤
核心思想:计算物品之间的相似度,推荐与用户历史喜欢的物品相似的物品。
$$\text{pred}(u, i) = \frac{\sum_{j \in N_i} \text{sim}(i, j) \cdot r_{uj}}{\sum_{j \in N_i} |\text{sim}(i, j)|}$$
其中 N_i 是与物品 i 最相似的 k 个物品的集合。
基于物品的协同过滤在实践中往往比基于用户的效果更好,因为物品相似度相对稳定。
三、矩阵分解
3.1 基本思想
矩阵分解将用户-物品评分矩阵 R(m×n)分解为两个低维矩阵的乘积:
$$R \approx P \cdot Q^T$$
其中 P 是 m×k 的用户隐因子矩阵,Q 是 n×k 的物品隐因子矩阵,k 是隐因子维度(通常 10-100)。
用户 u 对物品 i 的预测评分:
$$\hat{r}{ui} = p_u \cdot q_i^T = \sum{f=1}^{k} p_{uf} q_{if}$$
3.2 学习算法
通过最小化正则化平方误差学习 P 和 Q:
$$\min_{P, Q} \sum_{(u,i) \in K} (r_{ui} - p_u q_i^T)^2 + \lambda (|p_u|^2 + |q_i|^2)$$
使用随机梯度下降更新参数:
$$e_{ui} = r_{ui} - p_u q_i^T$$
$$p_u \leftarrow p_u + \alpha (e_{ui} q_i - \lambda p_u)$$
$$q_i \leftarrow q_i + \alpha (e_{ui} p_u - \lambda q_i)$$
3.3 偏差项
实际预测中,用户有自己的评分习惯(有人严苛有人宽容),物品也有自身的流行度。加入偏差项可以提高预测精度:
$$\hat{r}_{ui} = \mu + b_u + b_i + p_u \cdot q_i^T$$
其中 μ 是全局平均评分,b_u 是用户偏差,b_i 是物品偏差。
四、冷启动问题
推荐系统的最大挑战之一是冷启动——新用户或新物品没有任何交互历史。
解决方案:
- 基于内容的推荐:利用用户/物品的属性特征
- 混合方法:先用内容推荐获取初期数据,再切换到协同过滤
- 主动学习:向新用户推荐一些信息丰富的物品获取反馈
class MatrixFactorization:
def __init__(self, n_users, n_items, n_factors=20, lr=0.01, reg=0.02):
self.P = np.random.normal(0, 0.1, (n_users, n_factors))
self.Q = np.random.normal(0, 0.1, (n_items, n_factors))
self.b_u = np.zeros(n_users)
self.b_i = np.zeros(n_items)
self.mu = 0
self.lr = lr
self.reg = reg
def fit(self, ratings, n_epochs=20):
# ratings: list of (user_id, item_id, rating)
self.mu = np.mean([r for _, _, r in ratings])
for epoch in range(n_epochs):
np.random.shuffle(ratings)
total_loss = 0
for u, i, r in ratings:
pred = self.mu + self.b_u[u] + self.b_i[i] + np.dot(self.P[u], self.Q[i])
err = r - pred
# SGD 更新
self.b_u[u] += self.lr * (err - self.reg * self.b_u[u])
self.b_i[i] += self.lr * (err - self.reg * self.b_i[i])
self.P[u] += self.lr * (err * self.Q[i] - self.reg * self.P[u])
self.Q[i] += self.lr * (err * self.P[u] - self.reg * self.Q[i])
total_loss += err ** 2
if epoch % 5 == 0:
print(f"Epoch {epoch}, RMSE: {np.sqrt(total_loss / len(ratings)):.4f}")
def predict(self, u, i):
return self.mu + self.b_u[u] + self.b_i[i] + np.dot(self.P[u], self.Q[i])
延伸阅读
- 📺 B 站播放列表:Machine Learning Specialization (2022) — 新版机器学习
- 📚 更多学习资源,请访问 deeplearning.ai 官网