第 7 课
← 返回系列列表

Course 3:推荐系统

Machine Learning Specialization (2022) — 新版机器学习

协同过滤、基于内容的推荐、矩阵分解。

Course 3:推荐系统

课程简介

协同过滤、基于内容的推荐、矩阵分解。

🎬 本课程视频:Machine Learning Specialization (2022) — 新版机器学习


一、推荐系统概述

推荐系统是机器学习在商业中应用最广泛的领域之一。从亚马逊的商品推荐到 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])

延伸阅读

← Course 3:无监督学习 Course 3:强化学习入门 →