人工智能之深度学习_[5]-神经网络优化学习率衰减优化正则化方法
文章目录
- 神经网络入门二
- 3 神经网络优化方法
- 3.1 梯度下降算法回顾
- 3.2 反向传播(BP算法)
- 3.2.1 反向传播概念
- 3.2.2 反向传播详解
- 3.3 梯度下降优化方法
- 3.3.1 指数加权平均
- 3.3.2 动量算法Momentum
- 3.3.3 AdaGrad
- 3.3.4 RMSProp
- 3.3.5 Adam
- 3.3.6 小结
- 4 学习率衰减优化方法
- 4.1 为什么要进行学习率优化
- 4.2 等间隔学习率衰减
- 4.3 指定间隔学习率衰减
- 4.4 按指数学习率衰减
- 4.5 小结
- 5 正则化方法
- 5.1 什么是正则化
- 5.2 Dropout正则化
- 5.3 批量归一正则化(Batch Normalization)
神经网络入门二
3 神经网络优化方法
多层神经网络的学习能力比单层网络强得多。想要训练多层网络,需要更强大的学习算法。误差反向传播算法(Back Propagation)是其中最杰出的代表,它是目前最成功的神经网络学习算法。现实任务使用神经网络时,大多是在使用 BP 算法进行训练,值得指出的是 BP 算法不仅可用于多层前馈神经网络,还可以用于其他类型的神经网络。通常说 BP 网络时,一般是指用 BP 算法训练的多层前馈神经网络。
这就需要了解两个概念:
- 正向传播:指的是数据通过网络从输入层到输出层的传递过程。这个过程的目的是计算网络的输出值(预测值),从而与目标值(真实值)比较以计算误差。
- 反向传播:指的是计算损失函数相对于网络中各参数(权重和偏置)的梯度,指导优化器更新参数,从而使神经网络的预测更接近目标值。
3.1 梯度下降算法回顾
梯度下降法简单来说就是一种寻找使损失函数最小化的方法。
从数学角度来看,梯度的方向是函数增长速度最快的方向,那么梯度的反方向就是函数减少最快的方向,所以有:
其中,η是学习率,如果学习率太小,那么每次训练之后得到的效果都太小,增大训练的时间成本。如果,学习率太大,那就有可能直接跳过最优解,进入无限的训练中。解决的方法就是,学习率也需要随着训练的进行而变化。
在上图中我们展示了一维和多维的损失函数,损失函数呈碗状。在训练过程中损失函数对权重的偏导数就是损失函数在该位置点的梯度。我们可以看到,沿着负梯度方向移动,就可以到达损失函数底部,从而使损失函数最小化。这种利用损失函数的梯度迭代地寻找最小值的过程就是梯度下降的过程。
在进行模型训练时,有三个基础的概念:
- Epoch: 使用全部数据对模型进行以此完整训练,训练次数
- Batch: 使用训练集中的小部分样本对模型权重进行以此反向传播的参数更新,每次训练每批次样本数量
- Iteration: 使用一个 Batch 数据对模型进行一次参数更新的过程,每次训练批次数
假设数据集有 50000 个训练样本,现在选择 Batch Size = 256 对模型进行训练。
每个 Epoch 要训练的图片数量:50000
训练集具有的 Batch 个数:50000/256+1=196
每个 Epoch 具有的 Iteration 个数:196
10个 Epoch 具有的 Iteration 个数:1960
在深度学习中,梯度下降的几种方式的根本区别就在于Batch Size不同,如下表所示:
注:上表中 Mini-Batch 的 Batch 个数为 N / B + 1 是针对未整除的情况。整除则是 N / B。
3.2 反向传播(BP算法)
利用反向传播算法对神经网络进行训练。该方法与梯度下降算法相结合,对网络中所有权重计算损失函数的梯度,并利用梯度值来更新权值以最小化损失函数。
3.2.1 反向传播概念
前向传播:指的是数据输入到神经网络中,逐层向前传输,一直运算到输出层为止。
反向传播(Back Propagation):利用损失函数ERROR值,从后往前,结合梯度下降算法,依次求各个参数的偏导,并进行参数更新。
在网络的训练过程中经过前向传播后得到的最终结果跟训练样本的真实值总是存在一定误差,这个误差便是损失函数 ERROR。想要减小这个误差,就用损失函数 ERROR,从后往前,依次求各个参数的偏导,这就是反向传播(Back Propagation)。
3.2.2 反向传播详解
反向传播算法利用链式法则对神经网络中的各个节点的权重进行更新。
【举个栗子🌰:】
如下图是一个简单的神经网络用来举例:激活函数为sigmoid
前向传播运算:
接下来是反向传播(求网络误差对各个权重参数的梯度):
我们先来求最简单的,求误差E对w5的导数。首先明确这是一个链式法则的求导过程,要求误差E对w5的导数,需要先求误差E对 o u t o 1 out_{o1} outo1的导数,再求 o u t o 1 out_{o1} outo1对 n e t o 1 net_{o1} neto1的导数,最后再求 n e t o 1 net_{o1} neto1对 w 5 w_5 w5的导数,经过这个链式法则,我们就可以求出误差E对 w 5 w_5 w5的导数(偏导),如下图所示:
导数(梯度)已经计算出来了,下面就是反向传播与参数更新过程:
如果要想求误差E对w1的导数,误差E对w1的求导路径不止一条,这会稍微复杂一点,但换汤不换药,计算过程如下所示:
至此,反向传播算法的过程就讲完了啦!
3.3 梯度下降优化方法
梯度下降优化算法中,可能会碰到以下情况:
- 碰到平缓区域,梯度值较小,参数优化变慢
- 碰到 “鞍点” ,梯度为0,参数无法优化
- 碰到局部最小值,参数不是最优
对于这些问题, 出现了一些对梯度下降算法的优化方法,例如:Momentum、AdaGrad、RMSprop、Adam 等
3.3.1 指数加权平均
我们最常见的算数平均指的是将所有数加起来除以数的个数,每个数的权重是相同的。指数加权平均指的是给每个数赋予不同的权重求得平均数。移动平均数,指的是计算最近邻的 N 个数来获得平均数。
指数移动加权平均则是参考各数值,并且各数值的权重都不同,距离越远的数字对平均数计算的贡献就越小(权重较小),距离越近则对平均数的计算贡献就越大(权重越大)。
比如:明天气温怎么样,和昨天气温有很大关系,而和一个月前的气温关系就小一些。
计算公式可以用下面的式子来表示:
- S t S_t St 表示指数加权平均值;
- Y t Y_t Yt 表示t时刻的值;
- β 调节权重系数,该值越大平均数越平缓。
第100天的指数加权平均值为:
下面通过代码来看结果,随机产生 30 天的气温数据:
import torch
import matplotlib.pyplot as pltELEMENT_NUMBER = 30# 1. 实际平均温度
def test01():# 固定随机数种子torch.manual_seed(0)# 产生30天的随机温度temperature = torch.randn(size=[ELEMENT_NUMBER,]) * 10print(temperature)# 绘制平均温度days = torch.arange(1, ELEMENT_NUMBER + 1, 1)plt.plot(days, temperature, color='r')plt.scatter(days, temperature)plt.show()# 2. 指数加权平均温度
def test02(beta=0.9):# 固定随机数种子torch.manual_seed(0)# 产生30天的随机温度temperature = torch.randn(size=[ELEMENT_NUMBER,]) * 10print(temperature)exp_weight_avg = []# idx从1开始for idx, temp in enumerate(temperature, 1):# 第一个元素的 EWA 值等于自身if idx == 1:exp_weight_avg.append(temp)continue# 第二个元素的 EWA 值等于上一个 EWA 乘以 β + 当前气温乘以 (1-β)# idx-2:2-2=0,exp_weight_avg列表中第一个值的下标值new_temp = exp_weight_avg[idx - 2] * beta + (1 - beta) * tempexp_weight_avg.append(new_temp)days = torch.arange(1, ELEMENT_NUMBER + 1, 1)plt.plot(days, exp_weight_avg, color='r')plt.scatter(days, temperature)plt.show()if __name__ == '__main__':test01()test02(0.5)test02(0.9)
从程序运行结果可以看到:
- 指数加权平均绘制出的气温变化曲线更加平缓
- β 的值越大,则绘制出的折线越加平缓,波动越小(1-β越小,t时刻的 S t S_t St越不依赖 Y t Y_t Yt的值)
- β 值一般默认都是 0.9
3.3.2 动量算法Momentum
当梯度下降碰到 “峡谷” 、”平缓”、”鞍点” 区域时, 参数更新速度变慢。 Momentum 通过指数加权平均法,累计历史梯度值,进行参数更新,越近的梯度值对当前参数更新的重要性越大。
梯度计算公式:
s t = β s t − 1 + ( 1 − β ) g t s_t=βs_{t−1}+(1−β)g_t st=βst−1+(1−β)gt
参数更新公式:
w t = w t − 1 − η s t w_t=w_{t−1}−ηs_t wt=wt−1−ηst
s t s_t st是当前时刻指数加权平均梯度值
s t − 1 s_{t-1} st−1是历史指数加权平均梯度值
g t g_t gt是当前时刻的梯度值
β 是调节权重系数,通常取 0.9 或 0.99
η是学习率
w t w_t wt是当前时刻模型权重参数
咱们举个例子,假设:权重 β 为 0.9,例如:
第一次梯度值:s1 = g1 = w1
第二次梯度值:s2 = 0.9*s1 + g2*0.1
第三次梯度值:s3 = 0.9*s2 + g3*0.1
第四次梯度值:s4 = 0.9*s3 + g4*0.1
1. w 表示初始梯度
2. g 表示当前轮数计算出的梯度值
3. s 表示历史梯度移动加权平均值梯度下降公式中梯度的计算,就不再是当前时刻t的梯度值,而是历史梯度值的指数移动加权平均值。
公式修改为:
Wt = Wt-1 - η*St
Wt:当前时刻模型权重参数
St:当前时刻指数加权平均梯度值
η:学习率
Monmentum 优化方法是如何一定程度上克服 “平缓”、”鞍点”、”峡谷” 的问题呢?
- 当处于鞍点位置时,由于当前的梯度为 0,参数无法更新。但是 Momentum 动量梯度下降算法已经在先前积累了一些梯度值,很有可能使得跨过鞍点。
- 由于 mini-batch 普通的梯度下降算法,每次选取少数的样本梯度确定前进方向,可能会出现震荡,使得训练时间变长。Momentum 使用移动加权平均,平滑了梯度的变化,使得前进方向更加平缓,有利于加快训练过程。一定程度上有利于降低 “峡谷” 问题的影响。
在pytorch中动量梯度优化法编程实践如下:
def test01():# 1 初始化权重参数w = torch.tensor([1.0], requires_grad=True, dtype=torch.float32)loss = ((w ** 2) / 2.0).sum()# 2 实例化优化方法:SGD 指定参数beta=0.9optimizer = torch.optim.SGD([w], lr=0.01, momentum=0.9)# 3 第1次更新 计算梯度,并对参数进行更新optimizer.zero_grad()loss.backward()optimizer.step()print('第1次: 梯度w.grad: %f, 更新后的权重:%f' % (w.grad.numpy(), w.detach().numpy()))# 4 第2次更新 计算梯度,并对参数进行更新# 使用更新后的参数机选输出结果loss = ((w ** 2) / 2.0).sum()optimizer.zero_grad()loss.backward()optimizer.step()print('第2次: 梯度w.grad: %f, 更新后的权重:%f' % (w.grad.numpy(), w.detach().numpy()))
结果显示:
第1次: 梯度w.grad: 1.000000, 更新后的权重:0.990000
第2次: 梯度w.grad: 0.990000, 更新后的权重:0.971100
3.3.3 AdaGrad
AdaGrad 通过对不同的参数分量使用不同的学习率,AdaGrad 的学习率总体会逐渐减小,这是因为 AdaGrad 认为:在起初时,我们距离最优目标仍较远,可以使用较大的学习率,加快训练速度,随着迭代次数的增加,学习率逐渐下降。
其计算步骤如下:
-
初始化学习率 η、初始化参数w、小常数 σ = 1e-10
-
初始化梯度累计变量 s = 0
-
从训练集中采样 m 个样本的小批量,计算梯度 g t g_t gt
-
累积平方梯度: s t s_t st = s t − 1 s_{t-1} st−1 + g t g_t gt ⊙ g t g_t gt,⊙ 表示各个分量相乘
-
学习率 η 的计算公式如下:
η = η s t + σ η\over\sqrt{s_t}+σ st+ση
-
权重参数更新公式如下:
w t w_t wt = w t − 1 w_{t-1} wt−1 - η s t + σ η\over\sqrt{s_t}+σ st+ση * g t g_t gt
-
重复 3-7 步骤
AdaGrad 缺点是可能会使得学习率过早、过量的降低,导致模型训练后期学习率太小,较难找到最优解。
在PyTorch中AdaGrad优化法编程实践如下:
def test02():# 1 初始化权重参数w = torch.tensor([1.0], requires_grad=True, dtype=torch.float32)loss = ((w ** 2) / 2.0).sum()# 2 实例化优化方法:adagrad优化方法optimizer = torch.optim.Adagrad([w], lr=0.01)# 3 第1次更新 计算梯度,并对参数进行更新optimizer.zero_grad()loss.backward()optimizer.step()print('第1次: 梯度w.grad: %f, 更新后的权重:%f' % (w.grad.numpy(), w.detach().numpy()))# 4 第2次更新 计算梯度,并对参数进行更新# 使用更新后的参数机选输出结果loss = ((w ** 2) / 2.0).sum()optimizer.zero_grad()loss.backward()optimizer.step()print('第2次: 梯度w.grad: %f, 更新后的权重:%f' % (w.grad.numpy(), w.detach().numpy()))
结果显示:
第1次: 梯度w.grad: 1.000000, 更新后的权重:0.990000
第2次: 梯度w.grad: 0.990000, 更新后的权重:0.982965
3.3.4 RMSProp
RMSProp 优化算法是对 AdaGrad 的优化。最主要的不同是,其使用指数加权平均梯度替换历史梯度的平方和。
其计算过程如下:
-
初始化学习率 η、初始化权重参数w、小常数 σ = 1e-10
-
初始化梯度累计变量 s = 0
-
从训练集中采样 m 个样本的小批量,计算梯度 g t g_t gt
-
使用指数加权平均累计历史梯度,⊙ 表示各个分量相乘,公式如下:
s t s_t st = β s t − 1 s_{t-1} st−1 + (1-β) g t g_t gt⊙ g t g_t gt
-
学习率 η 的计算公式如下:
η = η s t + σ η\over\sqrt{s_t}+σ st+ση
-
权重参数更新公式如下:
w t w_t wt = w t − 1 w_{t-1} wt−1 - η s t + σ η\over\sqrt{s_t}+σ st+ση * g t g_t gt
-
重复 3-7 步骤
RMSProp 与 AdaGrad 最大的区别是对梯度的累积方式不同,对于每个梯度分量仍然使用不同的学习率。
RMSProp 通过引入衰减系数β,控制历史梯度对历史梯度信息获取的多少. 被证明在神经网络非凸条件下的优化更好,学习率衰减更加合理一些。
需要注意的是:AdaGrad 和 RMSProp 都是对于不同的参数分量使用不同的学习率,如果某个参数分量的梯度值较大,则对应的学习率就会较小,如果某个参数分量的梯度较小,则对应的学习率就会较大一些。
在PyTorch中RMSprop梯度优化法,编程实践如下:
def test03():# 1 初始化权重参数w = torch.tensor([1.0], requires_grad=True, dtype=torch.float32)loss = ((w ** 2) / 2.0).sum()# 2 实例化优化方法:RMSprop算法,其中alpha对应betaoptimizer = torch.optim.RMSprop([w], lr=0.01, alpha=0.9)# 3 第1次更新 计算梯度,并对参数进行更新optimizer.zero_grad()loss.backward()optimizer.step()print('第1次: 梯度w.grad: %f, 更新后的权重:%f' % (w.grad.numpy(), w.detach().numpy()))# 4 第2次更新 计算梯度,并对参数进行更新# 使用更新后的参数机选输出结果loss = ((w ** 2) / 2.0).sum()optimizer.zero_grad()loss.backward()optimizer.step()print('第2次: 梯度w.grad: %f, 更新后的权重:%f' % (w.grad.numpy(), w.detach().numpy()))
结果显示:
第1次: 梯度w.grad: 1.000000, 更新后的权重:0.968377
第2次: 梯度w.grad: 0.968377, 更新后的权重:0.945788
3.3.5 Adam
-
Momentum 使用指数加权平均计算当前的梯度值
-
AdaGrad、RMSProp 使用自适应的学习率
-
Adam优化算法(Adaptive Moment Estimation,自适应矩估计)将 Momentum 和 RMSProp 算法结合在一起
- 修正梯度: 使⽤梯度的指数加权平均
- 修正学习率: 使⽤梯度平⽅的指数加权平均
-
原理:Adam 是结合了 Momentum 和 RMSProp 优化算法的优点的自适应学习率算法。它计算了梯度的一阶矩(平均值)和二阶矩(梯度的方差)的自适应估计,从而动态调整学习率。
-
梯度计算公式:
m t = β 1 m t − 1 + ( 1 − β 1 ) g t m_t=β_1m_{t−1}+(1−β_1)g_t mt=β1mt−1+(1−β1)gt
s t = β 2 s t − 1 + ( 1 − β 2 ) g t 2 s_t=β_2s_{t−1}+(1−β_2)gt^2 st=β2st−1+(1−β2)gt2
m t ^ \hat{m_t} mt^ = m t 1 − β 1 t m_t\over1−β_1^t 1−β1tmt, s t ^ \hat{s_t} st^= s t 1 − β 2 t s_t\over1−β_2^t 1−β2tst
-
权重参数更新公式:
w t w_t wt = w t − 1 w_{t−1} wt−1 − η s t ^ + ϵ η\over\sqrt{\hat{s_t}}+ϵ st^+ϵη m t ^ \hat{m_t} mt^
其中, m t m_t mt 是梯度的一阶矩估计, s t s_t st 是梯度的二阶矩估计,$ \hat{m_t}$和 s t ^ \hat{s_t} st^ 是偏差校正后的估计。
在PyTroch中,Adam梯度优化法编程实践如下:
def test04():# 1 初始化权重参数w = torch.tensor([1.0], requires_grad=True)loss = ((w ** 2) / 2.0).sum()# 2 实例化优化方法:Adam算法,其中betas是指数加权的系数optimizer = torch.optim.Adam([w], lr=0.01, betas=[0.9, 0.99])# 3 第1次更新 计算梯度,并对参数进行更新optimizer.zero_grad()loss.backward()optimizer.step()print('第1次: 梯度w.grad: %f, 更新后的权重:%f' % (w.grad.numpy(), w.detach().numpy()))# 4 第2次更新 计算梯度,并对参数进行更新# 使用更新后的参数机选输出结果loss = ((w ** 2) / 2.0).sum()optimizer.zero_grad()loss.backward()optimizer.step()print('第2次: 梯度w.grad: %f, 更新后的权重:%f' % (w.grad.numpy(), w.detach().numpy()))
结果显示:
第1次: 梯度w.grad: 1.000000, 更新后的权重:0.990000
第2次: 梯度w.grad: 0.990000, 更新后的权重:0.980003
3.3.6 小结
优化算法 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
SGD | 简单、容易实现。 | 收敛速度较慢,容易震荡,特别是在复杂问题中。 | 用于简单任务,或者当数据特征分布相对稳定时。 |
Momentum | 可以加速收敛,减少震荡,特别是在高曲率区域。 | 需要手动调整动量超参数,可能会在小步长训练中过度更新。 | 用于非平稳优化问题,尤其是深度学习中的应用。 |
AdaGrad | 自适应调整学习率,适用于稀疏数据。 | 学习率会在训练过程中逐渐衰减,可能导致早期停滞。 | 适合稀疏数据,如 NLP 或推荐系统中的特征。 |
RMSProp | 解决了 AdaGrad 学习率过早衰减的问题,适应性强。 | 需要选择合适的超参数,更新可能会过于激进。 | 适用于动态问题、非平稳目标函数,如深度学习训练。 |
Adam | 结合了 Momentum 和 RMSProp 的优点,适应性强且稳定。 | 需要调节更多的超参数,训练过程中可能会产生较大波动。 | 广泛适用于各种深度学习任务,特别是非平稳和复杂问题。 |
-
简单任务和较小的模型:SGD 或 Momentum
-
复杂任务或有大量数据:Adam 是最常用的选择,因其在大部分任务上都表现优秀
-
需要处理稀疏数据或文本数据:Adagrad 或 RMSProp
4 学习率衰减优化方法
4.1 为什么要进行学习率优化
在训练神经网络时,一般情况下学习率都会随着训练而变化。这主要是由于,在神经网络训练的后期,如果学习率过高,会造成loss的振荡,但是如果学习率减小的过慢,又会造成收敛变慢的情况。
运行下面代码,观察学习率设置不同对网络训练的影响:
# x看成是权重,y看成是loss,下面通过代码来理解学习率的作用
def func(x_t):return torch.pow(2*x_t, 2) # y = 4 x ^2# 采用较小的学习率,梯度下降的速度慢
# 采用较大的学习率,梯度下降太快越过了最小值点,导致不收敛,甚至震荡
def test():x = torch.tensor([2.], requires_grad=True)# 记录loss迭代次数,画曲线iter_rec, loss_rec, x_rec = list(), list(), list()# 实验学习率: 0.01 0.02 0.03 0.1 0.2 0.3 0.4# lr = 0.1 # 正常的梯度下降# lr = 0.125 # 当学习率设置0.125 一下子求出一个最优解# x=0 y=0 在x=0处梯度等于0 x的值x=x-lr*x.grad就不用更新了# 后续再多少次迭代 都固定在最优点lr = 0.2 # x从2.0一下子跨过0点,到了左侧负数区域# lr = 0.3 # 梯度越来越大 梯度爆炸max_iteration = 4for i in range(max_iteration):y = func(x) # 得出loss值y.backward() # 计算x的梯度print("Iter:{}, X:{:8}, X.grad:{:8}, loss:{:10}".format(i, x.detach().numpy()[0], x.grad.detach().numpy()[0], y.item()))x_rec.append(x.item()) # 梯度下降点 列表# 更新参数x.data.sub_(lr * x.grad) # x = x - x.gradx.grad.zero_()iter_rec.append(i) # 迭代次数 列表loss_rec.append(y) # 损失值 列表# 迭代次数-损失值 关系图plt.subplot(121).plot(iter_rec, loss_rec, '-ro')plt.grid()plt.xlabel("Iteration X")plt.ylabel("Loss value Y")# 函数曲线-下降轨迹 显示图x_t = torch.linspace(-3, 3, 100)y = func(x_t)plt.subplot(122).plot(x_t.numpy(), y.numpy(), label="y = 4*x^2")y_rec = [func(torch.tensor(i)).item() for i in x_rec]print('x_rec--->', x_rec)print('y_rec--->', y_rec)# 指定线的颜色和样式(-ro:红色圆圈,b-:蓝色实线等)plt.subplot(122).plot(x_rec, y_rec, '-ro')plt.grid()plt.legend()plt.show()
运行效果图如下:
可以看出:采用较小的学习率,梯度下降的速度慢;采用较大的学习率,梯度下降太快越过了最小值点,导致震荡,甚至不收敛(梯度爆炸)。
4.2 等间隔学习率衰减
等间隔学习率衰减方式如下所示:
在PyTorch中实现时使用:
# step_size:调整间隔数=50
# gamma:调整系数=0.5
# 调整方式:lr = lr * gamma
optim.lr_scheduler.StepLR(optimizer, step_size, gamma=0.1)
具体使用方式如下:
import torch
from torch import optim
import matplotlib.pyplot as pltdef test_StepLR():# 0.参数初始化LR = 0.1 # 设置学习率初始化值为0.1iteration = 10max_epoch = 200# 1 初始化参数y_true = torch.tensor([0])x = torch.tensor([1.0])w = torch.tensor([1.0], requires_grad=True)# 2.优化器optimizer = optim.SGD([w], lr=LR, momentum=0.9)# 3.设置学习率下降策略scheduler_lr = optim.lr_scheduler.StepLR(optimizer, step_size=50, gamma=0.5)# 4.获取学习率的值和当前的epochlr_list, epoch_list = [], []for epoch in range(max_epoch):lr_list.append(scheduler_lr.get_last_lr()) # 获取当前lrepoch_list.append(epoch) # 获取当前的epochfor i in range(iteration): # 遍历每一个batch数据loss = (w*x-y_true)**2 # 目标函数optimizer.zero_grad()# 反向传播loss.backward()optimizer.step()# 更新下一个epoch的学习率scheduler_lr.step()# 5.绘制学习率变化的曲线plt.plot(epoch_list, lr_list, label="Step LR Scheduler")plt.xlabel("Epoch")plt.ylabel("Learning rate")plt.legend()plt.show()
4.3 指定间隔学习率衰减
指定间隔学习率衰减的效果如下:
在PyTorch中实现时使用:
# milestones:设定调整轮次:[50, 125, 160]
# gamma:调整系数
# 调整方式:lr = lr * gamma
optim.lr_scheduler.MultiStepLR(optimizer, milestones, gamma=0.1, last_epoch=-1)
具体使用方式如下所示:
import torch
from torch import optim
import matplotlib.pyplot as pltdef test_MultiStepLR():torch.manual_seed(1)LR = 0.1iteration = 10max_epoch = 200y_true = torch.tensor([0])x = torch.tensor([1.0])w = torch.tensor([1.0], requires_grad=True)optimizer = optim.SGD([w], lr=LR, momentum=0.9)# 设定调整时刻数milestones = [50, 125, 160]# 设置学习率下降策略scheduler_lr = optim.lr_scheduler.MultiStepLR(optimizer, milestones=milestones, gamma=0.5)lr_list, epoch_list = list(), list()for epoch in range(max_epoch):lr_list.append(scheduler_lr.get_last_lr())epoch_list.append(epoch)for i in range(iteration):loss = (w*x-y_true)**2optimizer.zero_grad()# 反向传播loss.backward()# 参数更新optimizer.step()# 更新下一个epoch的学习率scheduler_lr.step()plt.plot(epoch_list, lr_list, label="Multi Step LR Scheduler\nmilestones:{}".format(milestones))plt.xlabel("Epoch")plt.ylabel("Learning rate")plt.legend()plt.show()
4.4 按指数学习率衰减
按指数衰减调整学习率的效果如下:
在PyTorch中实现时使用:
# gamma:指数的底
# 调整方式
# lr= lr∗gamma^epoch
optim.lr_scheduler.ExponentialLR(optimizer, gamma)
具体使用方式如下所示:
import torch
from torch import optim
import matplotlib.pyplot as pltdef test_ExponentialLR():# 0.参数初始化LR = 0.1 # 设置学习率初始化值为0.1iteration = 10max_epoch = 200# 1 初始化参数y_true = torch.tensor([0])x = torch.tensor([1.0])w = torch.tensor([1.0], requires_grad=True)# 2.优化器optimizer = optim.SGD([w], lr=LR, momentum=0.9)# 3.设置学习率下降策略gamma = 0.95scheduler_lr = optim.lr_scheduler.ExponentialLR(optimizer, gamma=gamma)# 4.获取学习率的值和当前的epochlr_list, epoch_list = list(), list()for epoch in range(max_epoch):lr_list.append(scheduler_lr.get_last_lr())epoch_list.append(epoch)for i in range(iteration): # 遍历每一个batch数据loss = (w*x-y_true)**2optimizer.zero_grad()# 反向传播loss.backward()optimizer.step()# 更新下一个epoch的学习率scheduler_lr.step()# 5.绘制学习率变化的曲线plt.plot(epoch_list, lr_list, label="Multi Step LR Scheduler")plt.xlabel("Epoch")plt.ylabel("Learning rate")plt.legend()plt.show()
4.5 小结
方法 | 等间隔学习率衰减 (Step Decay) | 指定间隔学习率衰减 (Exponential Decay) | 指数学习率衰减 (Exponential Moving Average Decay) |
---|---|---|---|
衰减方式 | 固定步长衰减 | 指定步长衰减 | 平滑指数衰减,历史平均考虑 |
实现难度 | 简单易实现 | 相对简单,容易调整 | 需要额外历史计算,较复杂 |
适用场景 | 大型数据集、较为简单的任务 | 对训练平稳性要求较高的任务 | 高精度训练,避免过快收敛 |
优点 | 直观,易于调试,适用于大批量数据 | 易于调试,稳定训练过程 | 平滑且考虑历史更新,收敛稳定性较强 |
缺点 | 学习率变化较大,可能跳过最优点 | 在某些情况下可能衰减过快,导致优化提前停滞 | 超参数调节较为复杂,可能需要更多的计算资源 |
5 正则化方法
5.1 什么是正则化
- 在设计机器学习算法时希望在新样本上的泛化能力强。许多机器学习算法都采用相关的策略来减小测试误差,这些策略被统称为正则化
- 神经网络强大的表示能力经常遇到过拟合,所以需要使用不同形式的正则化策略
- 目前在深度学习中使用较多的策略有范数惩罚,DropOut,特殊的网络层等,接下来我们对其进行详细的介绍
5.2 Dropout正则化
在训练深层神经网络时,由于模型参数较多,在数据量不足的情况下,很容易过拟合。Dropout(中文翻译成随机失活)是一个简单有效的正则化方法。
- 在训练过程中,Dropout的实现是让神经元以超参数p(丢弃概率)的概率停止工作或者激活被置为0,未被置为0的进行缩放,缩放比例为1/(1-p)。训练过程可以认为是对完整的神经网络的一些子集进行训练,每次基于输入数据只更新子网络的参数
- 在实际应用中,Dropout参数p的概率通常取值在0.2到0.5之间
- 对于较小的模型或较复杂的任务,丢弃率可以选择0.3或更小
- 对于非常深的网络,较大的丢弃率(如0.5或0.6)可能会有效防止过拟合
- 实际应用中,通常会在全连接层(激活函数后)之后添加Dropout层
- 在测试过程中,随机失活不起作用
- 在测试阶段,使用所有的神经元进行预测,以获得更稳定的结果
- 直接使用训练好的模型进行测试,由于所有的神经元都参与计算,输出的期望值会比训练阶段高。测试阶段的期望输出是 E[x_test] = x
- 测试/推理模式:model.eval()
- 缩放的必要性
- 在训练阶段,将参与计算的神经元的输出除以(1-p)
- 经过Dropout后的期望输出变为 E[x_dropout] = [(1-p) * x] / (1-p) = x,与测试阶段的期望输出一致
- 训练模型:model.train()
我们通过一段代码观察下dropout的效果:
import torch
import torch.nn as nndef test():# 初始化随机失活层dropout = nn.Dropout(p=0.4)# 初始化输入数据:表示某一层的weight信息inputs = torch.randint(0, 10, size=[1, 4]).float()layer = nn.Linear(4,5)y = layer(inputs)y = torch.relu(y)print("未失活FC层的输出结果:\n", y)y = dropout(y)print("失活后FC层的输出结果:\n", y)
输出结果:
未失活FC层的输出结果:tensor([[0.0000, 1.8033, 1.4608, 4.5189, 6.9116]], grad_fn=<ReluBackward0>)
失活后FC层的输出结果:tensor([[0.0000, 3.0055, 2.4346, 7.5315, 11.5193]], grad_fn=<MulBackward0>)
上述代码将Dropout层的丢弃概率p设置为0.4,此时经过Dropout层计算的张量中就出现了很多0, 未变为0的按照(1/(1-0.4))进行处理。
5.3 批量归一正则化(Batch Normalization)
在神经网络的训练过程中,流经网络的数据都是一个batch,每个batch之间的数据分布变化非常剧烈,这就使得网络参数频繁的进行大的调整以适应流经网络的不同分布的数据,给模型训练带来非常大的不稳定性,使得模型难以收敛。如果我们对每一个batch的数据进行标准化之后,数据分布就变得稳定,参数的梯度变化也变得稳定,有助于加快模型的收敛。
通过标准化每一层的输入,使其均值接近0,方差接近1,从而加速训练并提高泛化能力。
先对数据标准化,再对数据重构(缩放+平移),写成公式如下所示:
λ和β是可学习的参数,它相当于对标准化后的值做了一个线性变换,λ为系数,β为偏置;
eps 通常指为 1e-5,避免分母为 0;
E(x) 表示变量的均值;
Var(x) 表示变量的方差;
批量归一化的步骤如下:
-
计算均值和方差:对于每个神经元(即每一层的输入特征),计算该特征在一个小批量(batch)上的均值 μ B μ_B μB 和方差 σ B 2 \sigma_B^2 σB2,它们的计算公式如下:
μ B = 1 m ∑ i = 1 m x i μ_B=\frac{1}{m} \sum_{i=1}^{m} x_i μB=m1∑i=1mxi
σ B 2 = 1 m ∑ i = 1 m ( x i − μ B ) 2 σ_B^2=\frac{1}{m} \sum_{i=1}^{m} (x_i - \mu_B)^2 σB2=m1∑i=1m(xi−μB)2
其中 x i x_i xi 表示小批量中的第 i i i 个样本, m m m 是小批量的样本数量。
-
标准化:然后,对每个样本的输入进行标准化,得到归一化的输出:
x ^ i = x i − μ B σ B 2 + ϵ \hat{x}_i = \frac{x_i - \mu_B}{\sqrt{\sigma_B^2 + \epsilon}} x^i=σB2+ϵxi−μB
其中, ϵ ϵ ϵ 是一个小常数,用来避免除以零的情况。
-
缩放和平移:为了让网络能够恢复其学习能力,BN 层引入了两个可训练的参数 γ γ γ 和 β β β,分别用于缩放和平移:
y i = γ x ^ i + β y_i = \gamma \hat{x}_i + β yi=γx^i+β
其中, γ γ γ 和 β β β 是可学习的参数,通过 γ 和 β,BN 层不再是简单的将每一层输入强行变为标准正态分布,而是允许网络学习更适合于该层的输入分布;规范化操作会丢失原始输入的一些信息,而 γ γ γ 和 β β β 可以弥补这种信息损失。
批量归一化的作用:
-
**减少内部协方差偏移:**通过对每层的输入进行标准化,减少了输入数据分布的变化,从而加速了训练过程,并使得网络在训练过程中更加稳定。
-
加速训练:
- 在没有批量归一化的情况下,神经网络的训练通常会很慢,尤其是深度网络。因为在每层的训练过程中,输入数据的分布(特别是前几层)会不断变化,这会导致网络学习速度缓慢。
- 批量归一化通过确保每层的输入数据在训练时分布稳定,有效减少了这种变化,从而加速了训练过程。
-
**起到正则化作用:**批量归一化可以视作一种正则化方法,因为它引入了对训练样本的噪声(不同批次的统计信息不同,批次较小的均值和方差估计会更加不准确),使得模型不容易依赖特定的输入特征,从而起到一定的正则化效果,减少了对其他正则化技术(如Dropout)的需求。
-
**提升泛化能力:**由于其正则化效果,批量归一化能帮助网络在测试集上取得更好的性能。
批量归一化层在计算机视觉领域使用较多
Batch Normalization 的使用步骤:
- 在网络层后添加 BN 层:
- 通常,BN 层会添加在卷积层 (Conv2d) 或全连接层 (Linear) 之后,激活函数之前。
- 例如:Conv2d -> BN -> ReLU 或者 Linear -> BN -> ReLU。
- 训练时:model.train()
- BN 层会计算当前批次的均值 μ μ μ 和方差 σ 2 σ² σ2。
- 然后,利用这两个统计量对当前批次的数据进行规范化。
- 规范化后的数据会被缩放 γ γ γ 和平移 β β β。
- 同时,BN 层还会维护一个全局均值和全局方差的移动平均值,用于推理阶段。
- 推理时:model.eval()
- 推理时,不会再使用当前批次的均值和方差,而是使用训练阶段计算的全局均值和全局方差。
- 同样,规范化后的数据会被缩放 γ γ γ 和平移 β β β。
import torch
import torch.nn as nn"""
BatchNorm1d:主要应用于全连接层或处理一维数据的网络,例如文本处理。它接收形状为 (N, num_features) 的张量作为输入。
BatchNorm2d:主要应用于卷积神经网络,处理二维图像数据或特征图。它接收形状为 (N, C, H, W) 的张量作为输入。
BatchNorm3d:主要用于三维卷积神经网络 (3D CNN),处理三维数据,例如视频或医学图像。它接收形状为 (N, C, D, H, W) 的张量作为输入。
"""def tes01():# 创建测试样本, 假设是经过卷积层(Conv2d)处理后的特征图# (N, C, H, W): 一张图, 两个通道, 每个通道3行4列# 可以创建1个样本, 图像的BN是对每个通道的特征图(行列数据)进行标准化input_2d = torch.randn(size=(1, 2, 3, 4))print("input-->", input_2d)# num_features:输入特征数# eps:非常小的浮点数,防止除以0的错误# momentum:动量系数# affine:默认为True,γ和β被使用,让BN层更加灵活bn2d = nn.BatchNorm2d(num_features=2, eps=1e-05, momentum=0.1, affine=True) output = bn2d(input_2d)print("output-->", output)print(output.size())print(bn2d.weight)print(bn2d.bias) def tes02():# 创建测试样本# 2个样本, 1个特征# 不能创建1个样本, 无法统计均值和方差input_1d = torch.randn(size=(2, 2)) # 创建线性层对象linear1 = nn.Linear(in_features=2, out_features=3)# 创建BN层对象# num_features:输入特征数bn1d = nn.BatchNorm1d(num_features=3) # 20 output featuresoutput_1d = linear1(input_1d)# 进行批量归一化output = bn1d(output_1d) print("output-->", output)print(output.size()) # (32, 20)
输出结果:
test01:
input_2d--> tensor([[[[-0.2751, -1.2183, -0.5106, -0.1540],[-0.4585, -0.5989, -0.6063, 0.5986],[-0.4745, 0.1496, -1.1266, -1.2377]],[[ 0.2580, 1.2065, 1.4598, 0.8387],[-0.4586, 0.8938, -0.3328, 0.1192],[-0.3265, -0.6263, 0.0419, -1.2231]]]])
output--> tensor([[[[ 0.4164, -1.3889, -0.0343, 0.6484],[ 0.0655, -0.2032, -0.2175, 2.0889],[ 0.0349, 1.2294, -1.2134, -1.4262]],[[ 0.1340, 1.3582, 1.6853, 0.8835],[-0.7910, 0.9546, -0.6287, -0.0452],[-0.6205, -1.0075, -0.1449, -1.7779]]]],grad_fn=<NativeBatchNormBackward0>)
torch.Size([1, 2, 3, 4])
Parameter containing:
tensor([1., 1.], requires_grad=True)
Parameter containing:
tensor([0., 0.], requires_grad=True)test02:
output--> tensor([[-0.9998, 1.0000, 1.0000],[ 0.9998, -1.0000, -1.0000]], grad_fn=<NativeBatchNormBackward0>)
torch.Size([2, 3])
相关文章:

人工智能之深度学习_[5]-神经网络优化学习率衰减优化正则化方法
文章目录 神经网络入门二3 神经网络优化方法3.1 梯度下降算法回顾3.2 反向传播(BP算法)3.2.1 反向传播概念3.2.2 反向传播详解 3.3 梯度下降优化方法3.3.1 指数加权平均3.3.2 动量算法Momentum3.3.3 AdaGrad3.3.4 RMSProp3.3.5 Adam3.3.6 小结 4 学习率衰…...

Oracle之Merge into函数使用
Merge into函数为Oracle 9i添加的语法,用来合并update和insert语句。所以也经常用于update语句的查询优化: 一、语法格式: merge into A using B on (A.a B.a) --注意on后面带括号,且不能更新join的字段 when matched then upd…...

深度解析:哪种心磁图技术是心脏检查的精准之选?
在全球心血管疾病的阴影日益笼罩的今天,医学界正积极寻求一种无损、无创、无辐射的心脏健康监测方式。心磁图仪(MCG),这一前沿技术,凭借其独特的优势,悄然成为心脏电磁功能监测的新星。它不仅为心肌缺血、心…...

SpringBoot--基本使用(配置、整合SpringMVC、Druid、Mybatis、基础特性)
这里写目录标题 一.介绍1.为什么依赖不需要写版本?2.启动器(Starter)是何方神圣?3.SpringBootApplication注解的功效?4.启动源码5.如何学好SpringBoot 二.SpringBoot3配置文件2.1属性配置文件使用2.2 YAML配置文件使用2.3 YAML配置文件使用2.…...

单片机-STM32 IIC通信(OLED屏幕)(十一)
一、屏幕的分类 1、LED屏幕: 由无数个发光的LED灯珠按照一定的顺序排列而成,当需要显示内容的时候,点亮相关的LED灯即可,市场占有率很高,主要是用于户外,广告屏幕,成本低。 LED屏是一种用发光…...

观察者模式 - 观察者模式的应用场景
引言 观察者模式(Observer Pattern)是设计模式中行为型模式的一种,它定义了对象之间的一对多依赖关系,使得当一个对象的状态发生改变时,所有依赖于它的对象都会自动收到通知并更新。观察者模式广泛应用于事件处理系统…...

【C++】详细讲解继承(下)
本篇来继续说说继承。上篇可移步至【C】详细讲解继承(上) 1.继承与友元 友元关系不能继承 ,也就是说基类友元不能访问派⽣类私有和保护成员。 class Student;//前置声明class Same //基类 { public:friend void Fun(const Same& p, con…...

消息队列篇--原理篇--Pulsar(Namespace,BookKeeper,类似Kafka甚至更好的消息队列)
Apache Pulusar是一个分布式、多租户、高性能的发布/订阅(Pub/Sub)消息系统,最初由Yahoo开发并开源。它结合了Kafka和传统消息队列的优点,提供高吞吐量、低延迟、强一致性和可扩展的消息传递能力,适用于大规模分布式系…...

扬帆数据结构算法之舟,启航C++探索征途——LeetCode深度磨砺:顺序表技术精进实践
人无完人,持之以恒,方能见真我!!! 共同进步!! 文章目录 顺序表练习1.移除数组中指定的元素方法1(顺序表)方法2(双指针) 2.删除有序数组中的重复项…...

基于本地事务表+MQ实现分布式事务
基于本地事务表MQ实现分布式事务 引言1、原理2、本地消息表优缺点3、代码实现3.1、代码执行流程3.2、项目结构3.3、项目源码 引言 本地消息表的方案最初由ebay的工程师提出,核心思想是将分布式事务拆分成本地事务进行处理。本地消息表实现最终一致性。本文主要学习…...

数据结构:二叉树—面试题(一)
目录 1、相同的树 2、另一棵树的子树 3、翻转二叉树 4、平衡二叉树 5、对称二叉树 6、二叉树遍历 7、二叉树的分层遍历 1、相同的树 习题链接https://leetcode.cn/problems/same-tree/description/https://leetcode.cn/problems/same-tree/description/ 描述:…...

【Wordpress网站制作】切换语言的问题
前言 自学笔记,解决问题为主,欢迎补充。 本文重点:如何将页面语言从默认的【英语】修改成【中文】。 问题描述 安装完wordpress,在【Setting】→【General】的语言中,选项只有英语。无法切换成中文 方法1: 在 wp-c…...

【第二天】零基础入门刷题Python-算法篇-数据结构与算法的介绍-五种常见的排序算法(持续更新)
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、Python数据结构与算法的详细介绍1.Python中的常用的排序算法1.排序算法的介绍2.五种详细的排序算法代码 总结 前言 提示:这里可以添加本文要记…...

Neural networks 神经网络
发展时间线 基础概念 多层神经网络结构 神经网络中一个网络层的数学表达 TensorFlow实践 创建网络层 神经网络的创建、训练与推理 推理 推理可以理解为执行一次前向传播 前向传播 前向传播直观数学表达 前向传播直观数学表达的Python实现 前向传播向量化实现 相关数学知识…...

汽车免拆诊断案例 | 2007 款日产天籁车起步加速时偶尔抖动
故障现象 一辆2007款日产天籁车,搭载VQ23发动机(气缸编号如图1所示,点火顺序为1-2-3-4-5-6),累计行驶里程约为21万km。车主反映,该车起步加速时偶尔抖动,且行驶中加速无力。 图1 VQ23发动机…...

代码随想录day3
203:移除链表元素:注意虚拟头节点的使用 ListNode* removeElements(ListNode* head, int val) {ListNode* result new ListNode();result->next head;ListNode* current result;while(current ! nullptr && current->next ! nullptr){if(current-…...

Spring 面试题【每日20道】【其一】
1、Spring 当中什么是循环依赖(常问)? 中等 在Spring框架中,循环依赖(Circular Dependency)是指两个或多个bean互相之间直接或间接地依赖对方的注入。例如: A bean依赖于B bean。B bean又依赖…...

leetcode刷题记录(八十九)——35. 搜索插入位置
(一)问题描述 35. 搜索插入位置 - 力扣(LeetCode)35. 搜索插入位置 - 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位…...

Flutter 与 React 前端框架对比:深入分析与实战示例
Flutter 与 React 前端框架对比:深入分析与实战示例 在现代前端开发中,Flutter 和 React 是两个非常流行的框架。Flutter 是 Google 推出的跨平台开发框架,支持从一个代码库生成 iOS、Android、Web 和桌面应用;React 则是 Facebo…...

基于Docker的Spark分布式集群
目录 1. 说明 2. 服务器规划 3. 步骤 3.1 要点 3.2 配置文件 3.2 访问Spark Master 4. 使用测试 5. 参考 1. 说明 以docker容器方式实现apache spark计算集群,能灵活的增减配置与worker数目。 2. 服务器规划 服务器 (1master, 3workers) ip开放端口备注ce…...

Web 代理、爬行器和爬虫
目录 Web 在线网页代理服务器的使用方法Web 在线网页代理服务器使用流程详解注意事项 Web 请求和响应中的代理方式Web 开发中的请求方法借助代理进行文件下载的示例 Web 服务器请求代理方式代理、网关和隧道的概念参考文献说明 爬虫的工作原理及案例网络爬虫概述爬虫工作原理 W…...

MySQL 事件调度器
MySQL 事件调度器确实是一个更方便且内置的解决方案,可以在 MySQL 服务器端自动定期执行表优化操作,无需依赖外部工具或应用程序代码。这种方式也能减少数据库维护的复杂性,尤其适用于在数据库频繁更新或删除时进行自动化优化。 使用 MySQL …...

直线拟合例子 ,岭回归拟合直线
目录 直线拟合,算出离群点 岭回归拟合直线: 直线拟合,算出离群点 import cv2 import numpy as np# 输入的点 points np.array([[51, 149],[122, 374],[225, 376],[340, 382],[463, 391],[535, 298],[596, 400],[689, 406],[821, 407] ], dtypenp.float32)# 使用…...

Flutter android debug 编译报错问题。插件编译报错
下面相关内容 都以 Mac 电脑为例子。 一、问题 起因:(更新 Android studio 2024.2.2.13、 Flutter SDK 3.27.2) 最近 2025年 1 月 左右,我更新了 Android studio 和 Flutter SDK 再运行就会出现下面的问题。当然 下面的提示只是其…...

关于IPD流程的学习理解和使用
IPD(Integrated Product Development,集成产品开发)是一种系统化的产品开发流程和方法论,旨在通过跨职能团队的协作和并行工程,缩短产品开发周期,提高产品质量,降低开发成本。IPD 最初由美国 PR…...

C# 类(Class)
C# 类(Class) 概述 在C#编程语言中,类(Class)是面向对象编程(OOP)的核心概念之一。类是一种用户定义的数据类型,它包含了一组属性(数据)和方法(…...

Jenkins pipline怎么设置定时跑脚本
目录 示例:在Jenkins Pipeline中设置定时触发 使用pipeline指令设置定时触发 使用Declarative Pipeline设置定时触发 使用Scripted Pipeline设置定时触发 解释Cron表达式 保存和应用配置 小结 在Jenkins中,定时跑脚本(例如定时执行Pip…...

PostgreSQL模糊查询相关学习参考
PostgreSQL大数据量快速模糊检索实践_postgresql 模糊查询-CSDN博客文章浏览阅读1.5k次,点赞20次,收藏25次。注意: 本文内容于 2024-08-18 23:50:33 创建,可能不会在此平台上进行更新。。_postgresql 模糊查询https://blog.csdn.n…...

【电脑无法通过鼠标和键盘唤醒应该怎么办】
【电脑无法通过鼠标和键盘唤醒应该怎么办】 方法一(有时候不起作用):方法二(方法一无效时,使用方法二): 方法一(有时候不起作用): 方法二(方法一无效时,使用方法二):...

Java 大视界 -- Java 大数据中的数据脱敏技术与合规实践(60)
💖亲爱的朋友们,热烈欢迎来到 青云交的博客!能与诸位在此相逢,我倍感荣幸。在这飞速更迭的时代,我们都渴望一方心灵净土,而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识,也…...