PyTorch程序实现L1和L2正则项

正则化是机器学习中的一个重要概念,它可以帮助我们防止模型过拟合。在这篇文章中,我将详细介绍两种常见的正则化技术:L1和L2正则项。然后会基于PyTorch平台讲解如何向自己的网络模型中添加上述两种技术,将正则化真正为己所用!!!

1 背景介绍

在机器学习中,我们的目标是找到一个能够最小化损失函数的模型。然而,如果我们只关注最小化损失函数,我们可能会得到一个过于复杂的模型,这个模型在训练数据上表现得很好,但在新的数据上可能表现得很差。这就是所谓的过拟合

为了防止过拟合,我们可以在损失函数中添加一个正则项,这个正则项会惩罚模型的复杂度。L1和L2正则项就是两种常见的正则项。

2 公式推导

关于正则化的公式推导,网上有很多大牛进行过生动的介绍和讲解,为避免重复造轮子,大家可以在阅读下面的内容前去看一看这篇博客:一篇文章完全搞懂正则化(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表示网络模型中的参数。

3 程序实现

3.1 正则化实现

所谓添加正则项,就是在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结果中便实现了正则操作。

3.2 网络实例

为了更好的展示如何在自己的网络训练模型中使用正则化技术,首先给出一个没有加入正则项的网络训练程序(方便大家去和下面加入正则项的程序对比;也可以根据这个框架比对自己写的程序,快速定位应该修改的地方):

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()

3.3 在网络中加入正则项

下面的程序实现了从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里面调用上面的两个计算函数。

3.4 PyTorch中自带的正则方法:权重衰减

在本章的前三节,介绍了如何手动实现正则计算并将它嵌入至神经网络中,但是作为网络基本处理技术中的一种,PyTorch自带了正则化技术。它被集成在了优化器里。
以上面程序中用到的优化器torch.optim.SGD为例,我们下面来介绍如何通过它直接对网络进行正则化处理。

 optimizer = torch.optim.SGD(model.parameters(), lr=lr, weight_decay=1e-4)

上面的是SGD的通用定义方式,其中参数的含义为:

  1. model.parameters(): 模型的所有可学习参数
  2. lr:学习率
  3. weight_decay:权重衰减系数

其中,权重衰减系数weight_decay就是一种正则方法。具体而言:权重衰减等价于 L 2 范数正则化(regularization)。具体的推导和解析可见博客:
神经网络中的权重衰减weight_decay
正则化之WEIGHT_DECAY

4 正则项的使用注意事项

在使用L1和L2正则项时,需要注意以下几点:

  1. 正则化参数 λ λ λ的选择很重要。如果 λ λ λ太大,模型可能会过于简单,导致欠拟合。如果 λ λ λ太小,正则化的效果可能会不明显。

  2. L1和L2正则项可以同时使用,这就是所谓的Elastic Net

  3. L1正则项可能会导致模型不稳定,因为它会使得一些模型参数变为0。如果数据有轻微的变化,模型的参数可能会有大的变化。

5 总结

总的来说,使用正则项是一种有效的防止模型过拟合的技术。它可以帮助我们在保证模型拟合能力的同时,避免出现过拟合现象,从而提高模型的泛化能力。同时,正则项的使用也可以帮助我们进行特征选择,减小模型的复杂度,提高模型的可解释性。
但是,正则项的使用也存在一些限制。例如,在某些情况下,正则项可能会对模型的性能造成一定的影响。此外,正则项的系数需要进行合理调整,过大或过小都可能导致模型性能下降。同时,一个好的网络训练结果是多种因素共同作用决定的,在使用正则项的同时,也不能忽视其它技术(例如dropout)。

猜你喜欢

转载自blog.csdn.net/qq_44949041/article/details/131538200