基于PyTorch的深度学习基础课程之七:过拟合及其抑止(2|2)

本文对深度学习中的过拟合问题进行了探讨,详细分析了过拟合的概念和原因,并给出了深度学习中抑制过拟合的常用方法。本文通过详细示例来解释概念、分析原因、提供参考解决方法。本文最后附有全部示例代码,读者可下载后自行试验。

2 过拟合的抑制

解决过拟合问题时,常提到一个所谓的“奥卡姆剃刀(Occam’s Razor)定律”,它是由14世纪逻辑学家奥卡姆提出的。这个定律称为“如无必要,勿增实体”,即“简单有效原理”。在模型选择中,就是在所有可以选择的模型中,能够很好地解释已知数据并且简单的模型才是最好的模型。基于这个思路,人们常采用正则化(regularization)、早停(early stopping)、随机失活(dropout)等方法来抑制过拟合。

2.1 早停法

早停法是在模型迭代训练中,在模型对训练样本集收敛之前就停止迭代以防止过拟合的方法。

前面讨论过,模型泛化能力评估的思路是将样本集划分为训练集和验证集,用训练集来训练模型,训练完成后,用验证集来验证模型的泛化能力。而早停法提前引入验证集来验证模型的泛化能力,即在每一轮训练(一轮是指遍历所有训练样本一次)完后,就用验证集来验证泛化能力,如果n轮训练都没有使泛化能力得到提高,就停止训练。n是根据经验提前设定的参数,常取10、20、30等值。这种策略称为“No-improvement-in-n”。

PyTorch中实现早停法抑制过拟合的示例如代码7-1.7所示。n(代码中为patience)设置为1000。另设置了一个很小的参数delta,用于评估测试误差是否保持不变。当测试误差满足1000轮保持不变的条件时,训练中止,拟合结果如输出的图形所示。

代码7-1.7 早停法抑制过拟合

model = RegressionModel_5_5()
criterion = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters())

# 训练模型
train_losses = []
val_losses = []
best_loss = float('inf')
patience = 1000 # 验证集损失函数持续不降的训练轮数
delta = 0.00000000001
counter = 0
epochs_stop = 0
for epoch in range(n_epoch):
    model.train()
    optimizer.zero_grad()
    outputs = model(x_tensor)
    loss = criterion(outputs, y_tensor)
    loss.backward()
    optimizer.step()
    
    # 验证
    model.eval()
    with torch.no_grad():
        val_outputs = model(x1_tensor)
        val_loss = criterion(val_outputs, y0_tensor)
        train_losses.append(loss.item())
        val_losses.append(val_loss.item())
        # 早停
        if val_loss < best_loss - delta:
            best_loss = val_loss
            counter = 0
        else:
            counter += 1
            if counter >= patience:
                print(f'早停法停止训练于第{epoch}轮!')
                epochs_stop = epoch
                break
    
    if epoch % 1000 == 0:
        print(f'Epoch {epoch}, Train Loss: {loss.item():.4f}, Val Loss: {val_loss.item():.4f}')

# 绘图
plt.rcParams['axes.unicode_minus']=False
plt.rc('font', family='SimHei', size=13)
epochs = np.arange(epochs_stop+1) + 1
plt.plot(epochs, train_losses)
plt.plot(epochs, val_losses, "r--")
plt.show()

predict_view(model, x, yy, myfun)

输出:

Epoch 0, Train Loss: 0.3304, Val Loss: 0.2386
Epoch 1000, Train Loss: 0.0471, Val Loss: 0.0465
Epoch 2000, Train Loss: 0.0231, Val Loss: 0.0267
Epoch 3000, Train Loss: 0.0166, Val Loss: 0.0231
Epoch 4000, Train Loss: 0.0139, Val Loss: 0.0217
Epoch 5000, Train Loss: 0.0119, Val Loss: 0.0205
Epoch 6000, Train Loss: 0.0082, Val Loss: 0.0181
Epoch 7000, Train Loss: 0.0051, Val Loss: 0.0165
Epoch 8000, Train Loss: 0.0039, Val Loss: 0.0162
Epoch 9000, Train Loss: 0.0035, Val Loss: 0.0163
早停法停止训练于第9028轮!

在这里插入图片描述

在这里插入图片描述

2.2 正则法

既然过拟合是由于模型过于复杂导致的,那么想办法减少模型的复杂程度是不是可以抑制过拟合?这正是正则法抑制过拟合的思路。

正则化方法是在样本集的损失函数中增加一个正则化项(regularizer),或者称罚项(penalty term),来对冲模型的复杂度。正则化项一般是模型复杂度的单调递增函数,模型越复杂,正则化值就越大。有关正则法方法的理论分析,参见文献[1]。

在PyTorch中实现正则法方法抑制过拟合非常简单,只需要设置优化器的weight_decay参数值即可。示例代码见代码7-1.8所示。weight_decay参数值要设置合理,过大会导致模型欠拟合,参见示例。

代码7-1.8 正则化抑制过拟合

model = RegressionModel_5_5()
criterion = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), weight_decay=0.0001) # 通过设置weight_decay参数引入L2正则化
# 训练20000轮
for epoch in range(20000):
    optimizer.zero_grad()
    outputs = model(x_tensor)
    loss = criterion(outputs, y_tensor)
    loss.backward()
    optimizer.step()

predict_view(model, x, yy, myfun)

输出:

在这里插入图片描述

# 参数设置过大,变成欠拟合
model = RegressionModel_5_5()
criterion = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), weight_decay=0.001)
# 训练20000轮
for epoch in range(20000):
    optimizer.zero_grad()
    outputs = model(x_tensor)
    loss = criterion(outputs, y_tensor)
    loss.backward()
    optimizer.step()

predict_view(model, x, yy, myfun)

输出:

在这里插入图片描述

2.3 随机失活法

随机失活法是将神经元随机失活,即按预先设定的概率随机选择某些神经元进行失效,不参与本次训练。该方法可以一定程度上抑制过拟合问题。

PyTorch中实现随机失活是通过专门的Dropout层来实现,它的参数p是失活率,即神经元随机失活的概率。

随机失活法的示例见代码7-1.9。失活率也要适当设置,过大的话也会带来欠拟合的问题。本文最后附有全部示例代码,读者可下载后自行试验,不再赘述。

代码7-1.9 Dropout抑制过拟合

# 定义增加了Dropout层的模型
class RegressionModel_5_5_dropout(nn.Module):
    def __init__(self):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(1, 5),
            nn.Sigmoid(),
            nn.Dropout(0.001),  # 添加Dropout层
            nn.Linear(5, 5),
            nn.Sigmoid(),
            nn.Linear(5, 1),
            nn.Sigmoid()
        )
        # 初始化权重
        for layer in self.net:
            if isinstance(layer, nn.Linear):
                nn.init.uniform_(layer.weight)
                nn.init.zeros_(layer.bias)

    def forward(self, x):
        return self.net(x)
        
model = RegressionModel_5_5_dropout()
criterion = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters())
# 训练20000轮
for epoch in range(20000):
    optimizer.zero_grad()
    outputs = model(x_tensor)
    loss = criterion(outputs, y_tensor)
    loss.backward()
    optimizer.step()

predict_view(model, x, yy, myfun)

输出:

在这里插入图片描述

参考文献:

[1] 王衡军,机器学习[M].北京:清华大学出版社,2020.

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值