深度模型中的正则化、梯度裁剪、偏置初始化操作

最近调试代码,发现怎么调试都不行,就想着用一些优化方式,然后又不是很清楚这些优化方式的具体细节,然后就学习了一下,这里记录下来,方便以后查阅。

正则化

常用的正则化方法

L1正则化

L1正则化是基于L1范数的正则化方法,其数学公式为:

L = L d a t a + λ ∑ i = 1 n ∣ w i ∣ L = L_{data} + \lambda \sum_{i=1}^n |w_i| L=Ldata+λi=1nwi

其中 L d a t a L_{data} Ldata为数据损失, w i w_i wi为模型参数, λ \lambda λ为正则化参数。L1正则化的作用是惩罚模型参数的绝对值,使得一些参数变为0,从而实现特征选择的效果,减少模型的复杂度。

L2正则化

L2正则化是基于L2范数的正则化方法,其数学公式为:

L = L d a t a + λ ∑ i = 1 n w i 2 L = L_{data} + \lambda \sum_{i=1}^n w_i^2 L=Ldata+λi=1nwi2

其中 L d a t a L_{data} Ldata为数据损失, w i w_i wi为模型参数, λ \lambda λ为正则化参数。L2正则化的作用是惩罚模型参数的平方和,使得模型的权重分布更加平滑,减少模型的复杂度,避免过拟合。

Dropout正则化

Dropout正则化是一种随机失活正则化方法,其数学公式为:

y = 1 1 − p × x × m y = \frac{1}{1-p} \times x \times m y=1p1×x×m

其中 p p p为保留节点的概率, x x x为输入, m m m为二值化的掩码,表示哪些节点被保留,哪些节点被随机失活。Dropout正则化的作用是随机丢弃一些节点,从而减少模型中的共适应性,避免过拟合。

数据增强

数据增强是一种基于数据扩充的正则化方法,其数学公式为:

x a u g = f ( x ) x_{aug} = f(x) xaug=f(x)

其中 x x x为原始数据, f f f为数据增强函数, x a u g x_{aug} xaug为增强后的数据。数据增强可以通过随机裁剪、旋转、翻转、缩放等方式扩充数据集,从而提高模型的泛化能力,防止过拟合。

Kernel max-norm regularization

https://github.com/kevinzakka/pytorch-goodies#max-norm-constraint

Improving neural networks by preventing co-adaptation of feature detectors

Kernel max-norm regularization是一种常用的正则化方法,它可以限制神经网络中每个卷积核的权重值的最大范数,从而可以控制过拟合的程度。

Kernel max-norm regularization只能在训练时有效,因此需要在模型编译时设置相应的参数。在测试或预测时,不需要使用这个正则化方法。

If a hidden unit’s weight vector’s L2 norm L L L ever gets bigger than a certain max value c c c, multiply the weight vector by c / L c/L c/L. Enforce it immediately after each weight vector update or after every X X X gradient update.

This constraint is another form of regularization. While L2 penalizes high weights using the loss function, “max norm” acts directly on the weights. L2 exerts a constant pressure to move the weights near zero which could throw away useful information when the loss function doesn’t provide incentive for the weights to remain far from zero. On the other hand, “max norm” never drives the weights to near zero. As long as the norm is less than the constraint value, the constraint has no effect.

第一种实现方式:

def max_norm(model, max_val=3, eps=1e-8):
    for name, param in model.named_parameters():
        if 'bias' not in name:
            norm = param.norm(2, dim=0, keepdim=True)
            desired = torch.clamp(norm, 0, max_val)
            param = param * (desired / (eps + norm))

第二种实现方式:

class Conv2dWithConstraint(nn.Conv2d):
    def __init__(self, *args, doWeightNorm = True, max_norm=1, **kwargs):
        self.max_norm = max_norm
        self.doWeightNorm = doWeightNorm
        super(Conv2dWithConstraint, self).__init__(*args, **kwargs)

    def forward(self, x):
        if self.doWeightNorm: 
            self.weight.data = torch.renorm(
                self.weight.data, p=2, dim=0, maxnorm=self.max_norm
            )
        return super(Conv2dWithConstraint, self).forward(x)

class Conv1dWithConstraint(nn.Conv1d):
    def __init__(self, *args, doWeightNorm = True, max_norm=1, **kwargs):
        self.max_norm = max_norm
        self.doWeightNorm = doWeightNorm
        super(Conv1dWithConstraint, self).__init__(*args, **kwargs)

    def forward(self, x):
        if self.doWeightNorm: 
            self.weight.data = torch.renorm(
                self.weight.data, p=2, dim=0, maxnorm=self.max_norm
            )
        return super(Conv1dWithConstraint, self).forward(x)


class LinearWithConstraint(nn.Linear):
    def __init__(self, *args, doWeightNorm = True, max_norm=1, **kwargs):
        self.max_norm = max_norm
        self.doWeightNorm = doWeightNorm
        super(LinearWithConstraint, self).__init__(*args, **kwargs)

    def forward(self, x):
        if self.doWeightNorm: 
            self.weight.data = torch.renorm(
                self.weight.data, p=2, dim=0, maxnorm=self.max_norm
            )
        return super(LinearWithConstraint, self).forward(x)

在损失中加入L1、L2正则化从而实现防止过拟合的效果的原理是什么

在损失函数中加入正则化项,是一种常见的防止过拟合的方法。其基本原理是通过对模型参数进行约束,来减小模型的复杂度,从而避免模型过度拟合训练数据。

具体来说,正则化项通常有两种形式:L1正则化和L2正则化。L1正则化是将模型参数的绝对值作为正则化项,L2正则化是将模型参数的平方作为正则化项。在损失函数中加入正则化项后,优化器在训练模型时不仅需要最小化损失函数的输出值,还需要最小化正则化项的输出值,从而使得模型参数尽量接近于0

加入正则化项的效果是使得模型参数的值不会变得过大,从而避免模型过度拟合训练数据。这是因为模型参数过大的情况下,模型会过度适应训练数据,而无法泛化到测试数据。通过正则化项的约束,模型参数的值会被控制在一个较小的范围内,使得模型更具有泛化性能。

需要注意的是,正则化项的约束力度由正则化参数控制,即正则化参数越大,模型参数的值越接近于0。但是,正则化参数过大也会导致模型欠拟合,因此需要根据具体的情况选择合适的正则化参数。

L1正则化和L2正则化有什么区别

L1正则化是通过对权重参数施加L1范数的约束来实现的。具体地说,L1正则化是将权重参数中每个元素的绝对值相加,然后乘以一个正则化系数λ,得到一个正则化项,加到目标函数中。通过L1正则化可以使得部分权重参数变成0,从而实现特征选择的效果,即去除对模型影响较小的特征

L2正则化是通过对权重参数施加L2范数的约束来实现的。具体地说,L2正则化是将权重参数中每个元素的平方相加,然后乘以一个正则化系数λ,得到一个正则化项,加到目标函数中。通过L2正则化可以使得权重参数的值变得更加平滑,从而减少模型的复杂度,提高模型的泛化性能

正则化系数λ的取值对模型有什么影响

  1. 当正则化系数λ较小时,模型的拟合能力较强,可以更好地拟合训练数据,但可能会出现过拟合的问题,导致模型在测试数据上表现不佳
  2. 当正则化系数λ较大时,模型的拟合能力较弱,可以避免过拟合的问题,但可能会出现欠拟合的问题,导致模型在训练数据上表现不佳。

如何确定正则化系数λ的最佳取值

确定正则化系数λ的最佳取值是深度学习中常见的问题,有多种方法可以用来解决这个问题,下面介绍几种常用的方法:

网格搜索(Grid Search)

网格搜索是一种简单但有效的方法,可以用来寻找最佳的正则化系数λ。具体来说,可以先定义一组候选的正则化系数λ的取值,然后在这些取值中进行穷举搜索,最后选择使得模型在验证集上表现最好的正则化系数λ。

随机搜索(Random Search)

随机搜索是一种更加高效的方法,可以用来寻找最佳的正则化系数λ。具体来说,可以先定义一组正则化系数λ的取值的分布,然后在这些分布中随机采样,最后选择使得模型在验证集上表现最好的正则化系数λ。

交叉验证(Cross-Validation)

交叉验证是一种常用的方法,可以用来评估模型的泛化性能和选择最佳的正则化系数λ。具体来说,可以将数据集分为训练集和验证集,然后在训练集上训练模型,使用验证集来选择最佳的正则化系数λ,最后使用测试集来评估模型的泛化性能。

带正则化的自适应学习率(Adaptive Regularization of Weights)

带正则化的自适应学习率是一种有效的方法,可以同时优化权重参数和正则化系数λ。具体来说,可以在损失函数中添加一个惩罚项,使得权重参数和正则化系数λ一起进行优化。这种方法可以自动调整正则化系数λ的取值,从而获得更好的泛化性能。

如何在模型中加入L2正则化

下面是一个使用PyTorch定义一个简单的深度学习框架,并加入L2正则化的示例代码:

import torch
import torch.nn as nn
import torch.optim as optim

# 定义模型
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.fc1 = nn.Linear(784, 64)
        self.fc2 = nn.Linear(64, 10)

    def forward(self, x):
        x = x.view(-1, 784)
        x = nn.functional.relu(self.fc1(x))
        x = self.fc2(x)
        return x

# 创建模型实例
model = Net()

# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01, weight_decay=0.01)

# 训练模型
for epoch in range(10):
    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):
        inputs, labels = data

        # 梯度清零
        optimizer.zero_grad()

        # 前向传播
        outputs = model(inputs)

        # 计算损失
        loss = criterion(outputs, labels)

        # 反向传播
        loss.backward()

        # 更新参数
        optimizer.step()

        running_loss += loss.item()

    print('Epoch %d, loss: %.3f' % (epoch + 1, running_loss / len(trainloader)))

在优化器中设置了weight_decay参数后,优化器会自动将正则化项添加到梯度更新中,从而实现L2正则化。

pytorch中优化函数中的weight_decay 参数是对网络中所有的参数进行正则化吗

在PyTorch中,优化器中的weight_decay参数是用来控制L2正则化(也称为权重衰减)的强度的。当设置了weight_decay参数时,优化器会在每次更新参数时对所有的参数进行L2正则化,即对每个参数的更新值乘以一个小于1的因子,这个因子就是weight_decay参数的值。因此,weight_decay参数对网络中所有参数进行了正则化。

需要注意的是,weight_decay参数对于不同的优化器有不同的含义。在SGD和Adam等优化器中,weight_decay参数控制的是L2正则化的强度;在RMSprop等优化器中,weight_decay参数控制的是L2正则化的系数。因此,在使用不同的优化器时,需要根据具体的情况来调整weight_decay参数的取值,以获得更好的泛化性能。此外,有些优化器中还提供了其他的正则化方法,例如AdamW和LAMB等优化器,可以在使用这些优化器时进一步控制正则化的效果。

SGD和Adam等优化器中和RMSprop等优化器中weight_decay参数的意义有什么不同

在SGD和Adam等优化器中,weight_decay参数通常用来控制L2正则化的强度。具体地说,weight_decay参数会在每次参数更新时对参数值进行衰减,从而使得权重参数尽量分散,防止过拟合。在SGD和Adam中,weight_decay参数的作用相当于在损失函数中添加L2正则化项,即将权重的平方和乘以一个权重衰减系数,从而约束权重参数的范数。

而在RMSprop等优化器中,weight_decay参数的含义有所不同,它被用来控制L2正则化的系数。具体地说,weight_decay参数会在计算梯度平方的移动平均值时,对其进行加权衰减,从而使得梯度的范数尽量分散,防止过拟合。在RMSprop中,weight_decay参数的作用相当于在梯度上方添加一个L2正则化项,即将权重的平方和乘以一个权重衰减系数,从而约束权重参数的范数。

Adam优化算法和AdamW优化算法的区别是什么

Adam优化算法中的权重衰减是基于L2正则化实现的,即在每次参数更新时,将权重参数乘以一个权重衰减系数。但是,这种方式会导致权重参数的更新受到了较大的约束,特别是在学习率较小时,可能会导致模型的收敛速度减慢

为了解决这个问题,AdamW优化算法提出了一种新的权重衰减方式。在AdamW中,权重衰减是基于L2正则化和权重衰减的加权和实现的,即在每次参数更新时,将权重参数乘以一个L2正则化系数和一个权重衰减系数的加权和。这种方式可以缓解权重参数更新受到较大约束的问题,同时还可以防止过拟合

除了权重衰减的处理方式不同之外,AdamW和Adam在其他方面的处理方式基本相同。它们都是基于自适应学习率的优化算法,可以自动调整学习率大小,以适应不同的模型和数据集。此外,它们都可以处理稀疏梯度和非平稳目标函数等问题。

在应用方面,Adam优化算法比较适合应用于深度学习中的大多数任务,特别是对于参数较多的模型,Adam的表现通常比SGD等基本优化算法要好。而AdamW优化算法则更适合于处理权重衰减问题,特别是在学习率较小时,AdamW可以更好地控制权重参数的更新,从而提高模型的泛化性能。因此,在需要进行权重衰减的任务中,使用AdamW可以获得更好的性能。

如何对模型的某一层加入正则化项

下面是一个示例代码,演示了如何在PyTorch中实现对某一层的正则化:

import torch
import torch.nn as nn
import torch.optim as optim

# 定义模型
class MyModel(nn.Module):
    def __init__(self):
        super(MyModel, self).__init__()
        self.lin1 = nn.Linear(10, 10)
        self.lin2 = nn.Linear(10, 5)
    
    def forward(self, x):
        x = self.lin1(x)
        x = nn.functional.relu(x)
        x = self.lin2(x)
        return x

model = MyModel()

# 定义正则化项的权重
weight_decay = 0.01

# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)

# 训练模型
for epoch in range(10):
    running_loss = 0.0
    for i in range(100):
        # 获取数据和标签
        inputs = torch.randn(10)
        labels = torch.randint(0, 5, (1,)).long()
        
        # 清空梯度
        optimizer.zero_grad()
        
        # 前向传播和计算损失
        outputs = model(inputs)
        loss = criterion(outputs, labels)
        
        # 计算正则化项
        reg_loss = 0.0
        for name, param in model.named_parameters():
            if 'weight' in name:
                reg_loss += torch.norm(param, p=2)**2
                
        # 总损失为交叉熵损失加上正则化项
        total_loss = loss + weight_decay * reg_loss
        
        # 反向传播和计算梯度
        total_loss.backward()
        
        # 更新参数
        optimizer.step()
        
        running_loss += loss.item()
    
    print("Epoch %d, loss: %.3f" % (epoch+1, running_loss/100))

在上述代码中,我们首先定义了一个名为MyModel的简单模型,该模型包含两个全连接层。接着,我们定义了正则化项的权重weight_decay。在训练过程中,我们在计算总损失时,将交叉熵损失和正则化项的乘积添加到总损失中,从而实现对某一层的正则化。

需要注意的是,对于不同的模型和任务,最适合的正则化项类型和权重可能会有所不同。通常情况下,我们可以通过尝试不同的正则化方法和权重值,来找到一个合适的正则化策略,从而实现更好的模型性能。

模型中加入clip_grad_norm_

下面是一个使用PyTorch框架的示例代码,演示了如何在模型训练过程中使用torch.nn.utils.clip_grad_norm_()函数对梯度进行裁剪:

import torch
import torch.nn as nn
import torch.optim as optim

# 定义模型
class MyModel(nn.Module):
    def __init__(self):
        super(MyModel, self).__init__()
        self.lin1 = nn.Linear(10, 10)
        self.lin2 = nn.Linear(10, 5)
    
    def forward(self, x):
        x = self.lin1(x)
        x = nn.functional.relu(x)
        x = self.lin2(x)
        return x

model = MyModel()

# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)

# 训练模型
for epoch in range(10):
    running_loss = 0.0
    for i in range(100):
        # 获取数据和标签
        inputs = torch.randn(10)
        labels = torch.randint(0, 5, (1,)).long()
        
        # 清空梯度
        optimizer.zero_grad()
        
        # 前向传播和计算损失
        outputs = model(inputs)
        loss = criterion(outputs, labels)
        
        # 反向传播和计算梯度
        loss.backward()
        
        # 对梯度进行裁剪
        nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
        
        # 更新参数
        optimizer.step()
        
        running_loss += loss.item()
    
    print("Epoch %d, loss: %.3f" % (epoch+1, running_loss/100))

在上述代码中,我们首先定义了一个名为MyModel的简单模型,该模型包含两个全连接层。接着,我们定义了损失函数和优化器,并开始训练模型。在每个小批量数据的反向传播过程中,我们使用nn.utils.clip_grad_norm_()函数对模型的梯度进行裁剪,以避免梯度爆炸的问题。在此之后,我们调用优化器的step()函数来更新模型的参数。

需要注意的是,对于不同的模型和任务,最适合的梯度裁剪阈值可能会有所不同。通常情况下,我们可以通过调整阈值的大小来找到一个合适的裁剪范围,从而实现更好的模型性能。

正则化和梯度裁剪的作用是什么,有什么区别,分别在什么情况下使用

正则化和梯度裁剪是常用的模型优化技术,它们的作用是为了避免模型过拟合或者梯度爆炸的问题。虽然这两种技术都有类似的目的,但是它们的实现方式和使用情况略有不同。

正则化的作用是通过在损失函数中加入对模型参数的约束,以避免模型过拟合的问题。常见的正则化方法包括L1正则化、L2正则化等。在实现过程中,我们可以通过在损失函数中添加正则化项(如权重的范数),来惩罚模型参数的大小,从而实现对模型的约束。正则化通常应用于模型训练过程中,以减少模型的泛化误差。

梯度裁剪的作用是通过对模型梯度进行限制,以避免梯度爆炸的问题。当模型的梯度过大时,我们可以通过对梯度进行剪裁,将其限制在一个合理的范围内,从而避免对模型参数的过度更新。梯度裁剪通常应用于优化器的反向传播过程中,以避免梯度爆炸对模型的影响。

区别:

  1. 正则化是对模型参数进行约束,而梯度裁剪是对梯度进行限制。
  2. 正则化可以避免模型过拟合,而梯度裁剪可以避免梯度爆炸。
  3. 正则化通常应用于模型训练过程中,而梯度裁剪通常应用于优化器的反向传播过程中。

使用场景:

  1. 正则化通常适用于模型过拟合的情况,当模型在训练集上表现良好,但在测试集上表现不佳时,可以尝试使用正则化技术。
  2. 梯度裁剪通常适用于模型出现梯度爆炸的情况,当模型的梯度过大,导致模型参数的更新过于剧烈而影响模型性能时,可以尝试使用梯度裁剪技术。

将卷积层的偏置初始化为0

在大多数深度学习框架中,可以通过设置卷积层的偏置初始化参数为0来实现该操作。下面是一个使用Python和PyTorch框架的示例代码:

import torch.nn as nn

# 定义卷积层(具体参数可以根据实际情况进行修改)
conv_layer = nn.Conv2d(in_channels=3, out_channels=16, kernel_size=3, stride=1, padding=1, bias=True)

# 将卷积层的偏置初始化为0
conv_layer.bias.data.fill_(0.0)

在上述代码中,我们首先使用PyTorch框架定义了一个卷积层conv_layer,并通过bias=True参数指定了该层需要包含偏置。接下来,我们通过conv_layer.bias.data.fill_(0.0)将卷积层的偏置初始化为0。

猜你喜欢

转载自blog.csdn.net/qq_41990294/article/details/130240722