正则化是机器学习中的一个重要概念,它可以帮助我们防止模型过拟合。在这篇文章中,我将详细介绍两种常见的正则化技术:L1和L2正则项。然后会基于PyTorch平台讲解如何向自己的网络模型中添加上述两种技术,将正则化真正为己所用!!!
在机器学习中,我们的目标是找到一个能够最小化损失函数的模型。然而,如果我们只关注最小化损失函数,我们可能会得到一个过于复杂的模型,这个模型在训练数据上表现得很好,但在新的数据上可能表现得很差。这就是所谓的过拟合。
为了防止过拟合,我们可以在损失函数中添加一个正则项,这个正则项会惩罚模型的复杂度。L1和L2正则项就是两种常见的正则项。
关于正则化的公式推导,网上有很多大牛进行过生动的介绍和讲解,为避免重复造轮子,大家可以在阅读下面的内容前去看一看这篇博客:一篇文章完全搞懂正则化(Regularization)
在大致了解了L1和L2的技术后,我们再做一个简单的梳理与回顾:
对于L1来说,就是在原有的loss函数中加入:
L 1 = λ ∑ ∣ w i ∣ L1 = \lambda\sum\left|w_i\right| L1=λ∑∣wi∣
同理,L2就是加入:
L 2 = λ ∑ w i 2 L2 = \lambda\sum w_i^2 L2=λ∑wi2
其中, w w w表示网络模型中的参数。
所谓添加正则项,就是在loss函数上再加入一项。因此我们可以定义一个附加的函数,来计算L1和L2所产生的额外损失数值:
# 定义L1正则化函数
def l1_regularizer(weight, lambda_l1):
return lambda_l1 * torch.norm(weight, 1)
# 定义L2正则化函数
def l2_regularizer(weight, lambda_l2):
return lambda_l2 * torch.norm(weight, 2)
上面的程序中定义了L1和L2的计算方法,也就是对所有网络参数weight做取绝对值和平方操作。在实际使用中,只需要把函数返回的值加入的原始的loss结果中便实现了正则操作。
为了更好的展示如何在自己的网络训练模型中使用正则化技术,首先给出一个没有加入正则项的网络训练程序(方便大家去和下面加入正则项的程序对比;也可以根据这个框架比对自己写的程序,快速定位应该修改的地方):
import torch
import torch.nn as nn
#定义网络结构
class CNN(nn.Module):
pass
# 实例化网络模型
model = CNN()
# 定义损失函数
criterion = nn.MSELoss()
# 定义优化器
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
# 迭代训练
for epoch in range(1000):
#训练模型
model.train()
for i, data in enumerate(train_loader, 0):
#1 解析数据并加载到GPU
inputs, labels = data
inputs, labels = inputs.to(device), labels.to(device)
#2 梯度清0
optimizer.zero_grad()
#3 前向传播
outputs = model(inputs)
#4 计算损失
loss = criterion(outputs, labels)
#5 反向传播和优化
loss.backward()
optimizer.step()
下面的程序实现了从loss损失函数层面加入正则项:
import torch
import torch.nn as nn
#定义网络结构
class CNN(nn.Module):
pass
# 实例化网络模型
model = CNN()
# 定义损失函数
criterion = nn.MSELoss()
# 定义优化器
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
# 迭代训练
for epoch in range(1000):
#训练模型
model.train()
for i, data in enumerate(train_loader, 0):
#1 解析数据并加载到GPU
inputs, labels = data
inputs, labels = inputs.to(device), labels.to(device)
#2 梯度清0
optimizer.zero_grad()
#3 前向传播
outputs = model(inputs)
#4 计算损失
#4.1 定义L1和L2正则化参数
lambda_l1 = 0.01
lambda_l2 = 0.01
#4.2 计算L1和L2正则化
l1_regularization = l1_regularizer(model.weight, lambda_l1)
l2_regularization = l2_regularizer(model.weight, lambda_l2)
#4.3 向loss中加入L1和L2
loss = criterion(outputs, labels)
loss += l1_regularization + l2_regularization
#5 反向传播和优化
loss.backward()
optimizer.step()
程序中的l1_regularizer()
和l2_regularizer()
函数是3.1节中手动实现的L1和L2计算函数。
注:本示例中是将L1和L2独立在loss函数外实现的。但是在实际操作中,最保险的做法是将L1和L2的计算过程放在loss函数里,也就是重写loss函数。具体操作就是在loss里面调用上面的两个计算函数。
在本章的前三节,介绍了如何手动实现正则计算并将它嵌入至神经网络中,但是作为网络基本处理技术中的一种,PyTorch自带了正则化技术。它被集成在了优化器里。
以上面程序中用到的优化器torch.optim.SGD
为例,我们下面来介绍如何通过它直接对网络进行正则化处理。
optimizer = torch.optim.SGD(model.parameters(), lr=lr, weight_decay=1e-4)
上面的是SGD的通用定义方式,其中参数的含义为:
其中,权重衰减系数weight_decay就是一种正则方法。具体而言:权重衰减等价于 L 2 范数正则化(regularization)。具体的推导和解析可见博客:
神经网络中的权重衰减weight_decay
正则化之WEIGHT_DECAY
在使用L1和L2正则项时,需要注意以下几点:
正则化参数 λ λ λ的选择很重要。如果 λ λ λ太大,模型可能会过于简单,导致欠拟合。如果 λ λ λ太小,正则化的效果可能会不明显。
L1和L2正则项可以同时使用,这就是所谓的Elastic Net。
L1正则项可能会导致模型不稳定,因为它会使得一些模型参数变为0。如果数据有轻微的变化,模型的参数可能会有大的变化。
总的来说,使用正则项是一种有效的防止模型过拟合的技术。它可以帮助我们在保证模型拟合能力的同时,避免出现过拟合现象,从而提高模型的泛化能力。同时,正则项的使用也可以帮助我们进行特征选择,减小模型的复杂度,提高模型的可解释性。
但是,正则项的使用也存在一些限制。例如,在某些情况下,正则项可能会对模型的性能造成一定的影响。此外,正则项的系数需要进行合理调整,过大或过小都可能导致模型性能下降。同时,一个好的网络训练结果是多种因素共同作用决定的,在使用正则项的同时,也不能忽视其它技术(例如dropout)。