协同过滤深入解析:从基础到深度学习
立即解锁
发布时间: 2025-09-03 00:38:39 阅读量: 9 订阅数: 17 AIGC 

### 协同过滤深入解析:从基础到深度学习
#### 1. 模型训练问题与权重衰减
在模型训练过程中,有时会出现训练效果不升反降的情况,尤其是在训练后期。仔细观察训练过程会发现,验证损失在中途停止改善并开始变差,这是过拟合的明显迹象。当无法使用数据增强时,就需要采用其他正则化技术,其中权重衰减是一种有效的方法。
权重衰减,也称为L2正则化,是在损失函数中添加所有权重的平方和。这样做的原因是,在计算梯度时,它会促使权重尽可能小。因为系数越大,损失函数中的峡谷就越陡峭。以抛物线 $y = a * (x^2)$ 为例,$a$ 越大,抛物线就越窄。如果让模型学习高参数,可能会导致它用一个过于复杂且变化剧烈的函数来拟合训练集中的所有数据点,从而引发过拟合。
限制权重过度增长会阻碍模型的训练,但能使模型具有更好的泛化能力。权重衰减(wd)是一个控制添加到损失中的平方和的参数,公式如下:
```python
loss_with_wd = loss + wd * (parameters**2).sum()
```
在实际应用中,直接计算这个大的平方和并添加到损失中效率较低且可能数值不稳定。根据求导知识,$p^2$ 关于 $p$ 的导数是 $2*p$,所以添加平方和等价于:
```python
parameters.grad += wd * 2 * parameters
```
由于wd是我们可以选择的参数,可将其设置为原来的两倍,从而去掉公式中的 *2。在fastai中使用权重衰减,可在调用 `fit` 或 `fit_one_cycle` 时传入 `wd` 参数,示例代码如下:
```python
model = DotProductBias(n_users, n_movies, 50)
learn = Learner(dls, model, loss_func=MSELossFlat())
learn.fit_one_cycle(5, 5e-3, wd=0.1)
```
训练结果如下表所示:
| epoch | train_loss | valid_loss | time |
| --- | --- | --- | --- |
| 0 | 0.972090 | 0.962366 | 00:13 |
| 1 | 0.875591 | 0.885106 | 00:13 |
| 2 | 0.723798 | 0.839880 | 00:13 |
| 3 | 0.586002 | 0.823225 | 00:13 |
| 4 | 0.490980 | 0.823060 | 00:13 |
从结果可以看出,使用权重衰减后模型性能有了明显提升。
#### 2. 创建自定义嵌入模块
之前使用 `Embedding` 时可能没有深入思考其工作原理,现在尝试不使用该类重新创建 `DotProductBias`。需要为每个嵌入创建一个随机初始化的权重矩阵,但要注意,直接将张量作为模块的属性添加时,它不会被包含在参数中,示例代码如下:
```python
class T(Module):
def __init__(self): self.a = torch.ones(3)
L(T().parameters()) # 输出为空列表
```
要将张量视为参数,需要将其包装在 `nn.Parameter` 类中,该类只是作为一个“标记”,用于指示哪些张量应包含在参数中,示例代码如下:
```python
class T(Module):
def __init__(self): self.a = nn.Parameter(torch.ones(3))
L(T().parameters()) # 输出包含参数的列表
```
所有PyTorch模块都使用 `nn.Parameter` 来表示可训练参数。可以使用以下函数创建一个随机初始化的张量作为参数:
```python
def create_params(size):
return nn.Parameter(torch.zeros(*size).normal_(0, 0.01))
```
使用该函数重新创建 `DotProductBias` 类:
```python
class DotProductBias(Module):
def __init__(self, n_users, n_movies, n_factors, y_range=(0,5.5)):
self.user_factors = create_params([n_users, n_factors])
self.user_bias = create_params([n_users])
self.movie_factors = create_params([n_movies, n_factors])
self.movie_bias = create_params([n_movies])
self.y_range = y_range
def forward(self, x):
users = self.user_factors[x[:,0]]
movies = self.movie_factors[x[:,1]]
res = (users*movies).sum(dim=1)
res += self.user_bias[x[:,0]] + self.movie_bias[x[:,1]]
return sigmoid_range(res, *self.y_range)
```
再次训练该模型进行验证:
```python
model = DotProductBias(n_users, n_movies, 50)
learn = Learner(dls, model, loss_func=MSELossFlat())
learn.fit_one_cycle(5, 5e-3, wd=0.1)
```
训练结果如下表所示:
| epoch | train_loss | valid_loss | time |
| --- | --- | --- | --- |
| 0 | 0.962146 | 0.936952 | 00:14 |
| 1 | 0.858084 | 0.884951 | 00:14 |
| 2 | 0.740883 | 0.838549 | 00:14 |
| 3 | 0.592497 | 0.823599 | 00:14 |
| 4 | 0.473570 | 0.824263 | 00:14 |
#### 3. 解释嵌入和偏差
模型不仅可以为用户提供电影推荐,还能让我们了解其学习到的参数。其中,偏差是最容易解释的。以下是偏差向量中值最低的电影:
```python
movie_bias = learn.model.movie_bias.squeeze()
idxs = movie_bias.argsort()[:5]
[dls.classes['title'][i] for i in idxs]
```
输出结果为:
```plaintext
['Children of the Corn: The Gathering (1996)',
'Lawnmo
```
0
0
复制全文
相关推荐










