目录
1.过拟合与欠拟合
1.1 过拟合
1.2 欠拟合
1.2 解决欠拟合
1.2.1 L2正则化
1.2.2 L1正则化
1.2.3 Dropout
1.2.4 简化模型
1.2.5 数据增强
1.2.6 早停
1.2.7 模型集成
1.2.8 交叉验证
2.批量标准化
2.1 实现过程
2.1.1 计算均值和方差
2.1.2 标准化
2.1.3 缩放和平移
2.1.4 标准化公式
2.2 训练和推理阶段
2.3 BatchNorm
1.过拟合与欠拟合
在训练深层神经网络时,由于模型参数较多,在数据量不足时很容易过拟合。而正则化技术主要就是用于防止过拟合,提升模型的泛化能力(对新数据表现良好)和鲁棒性(对异常数据表现良好)。
1.1 过拟合
过拟合是指模型对训练数据拟合能力很强、表现很好,但在测试数据上表现较差。
过拟合常见原因有:
-
数据量不足:当训练数据较少时,模型可能会过度学习数据中的噪声和细节。
-
模型太复杂:如果模型很复杂,也会过度学习训练数据中的细节和噪声。
-
正则化强度不足:如果正则化强度不足,可能会导致模型过度学习训练数据中的细节和噪声。
1.2 欠拟合
欠拟合是由于模型学习能力不足,无法充分捕捉数据中的复杂关系。
1.2 解决欠拟合
欠拟合的解决思路比较直接:
-
增加模型复杂度:引入更多的参数、增加神经网络的层数或节点数量,使模型能够捕捉到数据中的复杂模式。
-
增加特征:通过特征工程添加更多有意义的特征,使模型能够更好地理解数据。
-
减少正则化强度:适当减小 L1、L2 正则化强度,允许模型有更多自由度来拟合数据。
-
训练更长时间:如果是因为训练不足导致的欠拟合,可以增加训练的轮数或时间.
1.2.1 L2正则化
L2 正则化通过在损失函数中添加权重参数的平方和来实现,目标是惩罚过大的参数值。
数学表示:
设损失函数为 L(\theta),其中 \theta 表示权重参数,加入L2正则化后的损失函数表示为:
其中:
-
L(\theta) 是原始损失函数(比如均方误差、交叉熵等)。
-
\lambda 是正则化强度,控制正则化的力度。
-
\theta_i 是模型的第 i 个权重参数。
-
\frac{1}{2} \sum_{i} \theta_i^2 是所有权重参数的平方和,称为 L2 正则化项。
L2 正则化会惩罚权重参数过大的情况,通过参数平方值对损失函数进行约束。
梯度更新:
在 L2 正则化下,梯度更新时,不仅要考虑原始损失函数的梯度,还要考虑正则化项的影响。更新公式:
其中:
-
\eta 是学习率。
-
\nabla L(\theta_t) 是损失函数关于参数 \theta_t 的梯度。
-
\lambda \theta_t 是 L2 正则化项的梯度,对应的是参数值本身的衰减。
很明显,参数越大惩罚力度就越大,从而让参数逐渐趋向于较小值,避免出现过大的参数。
API:
optimizer = optim.SGD(model.parameters(), lr, weight_decay)
import torch
import torch.nn as nn
import torch.optim as optim
class Net(nn.Module):
def __init__(self):
super(Net,self).__init__()
# 输入层
self.linear1 = nn.Linear(2,2)
self.linear1.weight.data=torch.tensor([[0.15,0.20],
[0.25,0.30]])
self.linear1.bias.data = torch.tensor([0.35], dtype=torch.float32)
# 输出层
self.linear2 = nn.Linear(2,2)
self.linear2.weight.data=torch.tensor([[0.40,0.45],
[0.50,0.55]])
self.linear2.bias.data = torch.tensor([0.60], dtype=torch.float32)
self.activation = nn.Sigmoid()
def forward(self,input):
x = self.linear1(input)
x = self.activation(x)
x = self.linear2(x)
output = self.activation(x)
return output
def backward():
model = Net()
optimizer = optim.SGD(model.parameters(),lr=0.1,weight_decay = 0.01)
for epoch in range(100):
input = torch.tensor([0.05,0.10])
true = torch.tensor([0.01,0.99])
predict = model.forward(input)
mse = nn.MSELoss()
loss = mse(predict,true)
print(f"{epoch}",loss)
optimizer.zero_grad()
loss.backward()
optimizer.step()
backward()
1.2.2 L1正则化
L1 正则化通过在损失函数中添加权重参数的绝对值之和来约束模型的复杂度。
数学表示:
设模型的原始损失函数为 L(\theta),其中 \theta 表示模型权重参数,则加入 L1 正则化后的损失函数表示为:
其中:
-
L(\theta) 是原始损失函数。
-
\lambda 是正则化强度,控制正则化的力度。
-
|\theta_i| 是模型第i 个参数的绝对值。
-
\sum_{i} |\theta_i| 是所有权重参数的绝对值之和,这个项即为 L1 正则化项。
梯度更新:
在 L1 正则化下,梯度更新公式:
其中:
-
\eta 是学习率。
-
\nabla L(\theta_t) 是损失函数关于参数 \theta_t 的梯度。
-
\text{sign}(\theta_t) 是参数 \theta_t 的符号函数,表示当 \theta_t 为正时取值为 1,为负时取值为 -1,等于 0 时为 0。
因为 L1 正则化依赖于参数的绝对值,其梯度更新时不是简单的线性缩小,而是通过符号函数来直接调整参数的方向。
L1与L2对比:
-
L1 正则化 更适合用于产生稀疏模型,会让部分权重完全为零,适合做特征选择。
-
L2 正则化 更适合平滑模型的参数,避免过大参数,但不会使权重变为零,适合处理高维特征较为密集的场景。
import torch
import torch.nn as nn
import torch.optim as optim
model = nn.Linear(5,1)
loss_fun = torch.nn.MSELoss()
optimizer = optim.SGD(model.parameters(),lr = 0.01)
for epoch in range(100):
train_x = torch.tensor([[0.40,0.45,0.84,0.54,0.2],
[0.50,0.55,0.92,0.34,0.6]])
predict = model(train_x)
target = torch.tensor([[0.5],[0.8]])
# L1正则化项并将其加入到总损失中
l1_lambda = 0.001
l1_norm = sum(p.abs().sum() for p in model.parameters())
loss = loss_fun(predict,target) + l1_lambda*l1_norm
print(loss)
if model.weight.grad is not None:
model.weight.grad.zero_()
loss.backward()
optimizer.step()
1.2.3 Dropout
Dropout 是一种在训练过程中随机丢弃部分神经元的技术。它通过减少神经元之间的依赖来防止模型过于复杂,从而避免过拟合。
nn.Dropout(p)
参数:p:每一个神经元被丢弃的概率
x = torch.randint(0, 10, (5, 6), dtype=torch.float)
# 每一个神经元有p的概率被丢弃
dropout = nn.Dropout(p=0.5)
x = dropout(x)
print(x)
print(x.shape)
print(x==0)
# p不一定等于 死亡神经元占总神经元的比例
print(sum(sum(x==0))/(x.shape[0]*x.shape[1]))
1.2.4 简化模型
-
减少网络层数和参数: 通过减少网络的层数、每层的神经元数量或减少卷积层的滤波器数量,可以降低模型的复杂度,减少过拟合的风险。
-
使用更简单的模型: 对于复杂问题,使用更简单的模型或较小的网络架构可以减少参数数量,从而降低过拟合的可能性。
1.2.5 数据增强
通过对训练数据进行各种变换(如旋转、裁剪、翻转、缩放等),可以增加数据的多样性,提高模型的泛化能力。
1.2.6 早停
一种在训练过程中监控模型在验证集上的表现,并在验证误差不再改善时停止训练的技术。这样可避免训练过度,防止模型过拟合。
1.2.7 模型集成
通过将多个不同模型的预测结果进行集成,可以减少单个模型过拟合的风险。常见的集成方法包括投票法、平均法和堆叠法。
1.2.8 交叉验证
使用交叉验证技术可以帮助评估模型的泛化能力,并调整模型超参数,以防止模型在训练数据上过拟合。
2.批量标准化
2.1 实现过程
批量标准化的基本思路是在每一层的输入上执行标准化操作,并学习两个可训练的参数:缩放因子 \lambda 偏移量 \beta。
2.1.1 计算均值和方差
对于给定的神经网络层,假设输入数据为 \mathbf{x} = {x_1, x_2, \ldots, x_m},其中 m是批次大小。首先计算该批次数据的均值和方差。
2.1.2 标准化
使用计算得到的均值和方差对数据进行标准化,使得每个特征的均值为0,方差为1。
2.1.3 缩放和平移
标准化后的数据通常会通过可训练的参数进行缩放和平移,以恢复模型的表达能力。
2.1.4 标准化公式
其中,\gamma 和 \beta 是在训练过程中学习到的参数。
-
λ 和 β 是可学习的参数,它相当于对标准化后的值做了一个线性变换,λ 为系数,β 为偏置;
-
\epsilon通常指为 1e-5,避免分母为 0;
-
\mu_B 表示变量的均值;
-
\sigma_B^2 表示变量的方差;
2.2 训练和推理阶段
-
训练阶段: 在训练过程中,均值和方差是基于当前批次的数据计算得到的。
-
推理阶段: 在推理阶段,批量标准化使用的是训练过程中计算得到的全局均值和方差,而不是当前批次的数据。这些全局均值和方差通常会被保存在模型中,用于推理时的标准化过程。
2.3 BatchNorm
数据在经过 BN 层之后,无论数据以前的分布是什么,都会被归一化成均值为 β,标准差为 γ 的分布。
注意:BN 层不会改变输入数据的维度,只改变输入数据的的分布. 在实际使用过程中,BN 常常和卷积神经网络结合使用,卷积层的输出结果后接 BN 层。
API:
torch.nn.BatchNorm2d(num_features, eps=1e-05, momentum=0.1, affine=True)
参数:
-
由于每次使用的 mini batch 的数据集,所以 BN 使用移动加权平均来近似计算均值和方差,而 momentum 参数则调节移动加权平均值的计算;
-
affine = False 表示 \lambda=1,β=0,反之,则表示 γ 和 β 要进行学习;
-
BatchNorm2d 适用于输入的数据为 4D,输入数据的形状 [N,C,H,W]
其中:N 表示批次,C 代表通道数,H 代表高度,W 代表宽度
由于每次输入到网络中的时小批量的样本,我们使用指数加权平均来近似表示整体的样本的均值和方差,其更新公式如下:
running_mean = momentum * running_mean + (1.0 – momentum) * batch_mean
running_var = momentum * running_var + (1.0 – momentum) * batch_var
batch_mean 和 batch_var 表示当前批次的均值和方差。而 running_mean 和 running_var 是近似的整体的均值和方差的表示。当我们进行评估时,可以使用该均值和方差对输入数据进行归一化。
x = torch.randint(0,10,(4,3,4,5)).float()
# 批量标准化
bn = nn.BatchNorm2d(num_features = x.shape[1],eps =1e-8,affine =True,momentum=0.9)
print(bn(x))