1.损失函数
1.1 线性回归损失函数
1.1.1 MAE损失
MAE(Mean Absolute Error,平均绝对误差)通常也被称为 L1-Loss,通过对预测值和真实值之间的绝对差取平均值来衡量他们之间的差异。。
公式:
其中:
-
n 是样本的总数。
-
y_i 是第 i 个样本的真实值。
-
\hat{y}_i 是第 i 个样本的预测值。
-
\left| y_i - \hat{y}_i \right| 是真实值和预测值之间的绝对误差。
特点:
-
鲁棒性:与均方误差(MSE)相比,MAE对异常值(outliers)更为鲁棒,因为它不会像MSE那样对较大误差平方敏感。
-
物理意义直观:MAE以与原始数据相同的单位度量误差,使其易于解释。
应用场景:
MAE通常用于需要对误差进行线性度量的情况,尤其是当数据中可能存在异常值时,MAE可以避免对异常值的过度惩罚。
import torch
import torch.nn as nny_true = torch.tensor([1.0, 2.0, 3.0])
y_pred = torch.tensor([4.0, 5.0, 6.0])mae = nn.L1Loss()loss = mae(y_true,y_pred)print(loss)
1.1.2 MSE损失
均方差损失,也叫L2Loss
MSE(Mean Squared Error,均方误差)通过对预测值和真实值之间的误差平方取平均值,来衡量预测值与真实值之间的差异。
公式:
其中:
-
n 是样本的总数。
-
y_i 是第 i 个样本的真实值。
-
\hat{y}_i 是第 i 个样本的预测值。
-
\left( y_i - \hat{y}_i \right)^2 是真实值和预测值之间的误差平方。
特点:
-
平方惩罚:因为误差平方,MSE 对较大误差施加更大惩罚,所以 MSE 对异常值更为敏感。
-
凸性:MSE 是一个凸函数,这意味着它具有一个唯一的全局最小值,有助于优化问题的求解。
应用场景:
MSE被广泛应用在神经网络中。
y_true = torch.tensor([1.0, 4.0, 5.0])
y_pred = torch.tensor([6.0, 3.0, 2.0])mae = nn.MSELoss()loss = mae(y_true,y_pred)print(loss)
1.1.3 SmoothL1Loss
SmoothL1Loss可以做到在损失较小时表现为 L2 损失,而在损失较大时表现为 L1 损失。
公式:
其中,x 表示预测值和真实值之间的误差,即x = y_i - \hat{y}_i。
所有样本的平均损失为:
特点:
-
平滑过渡:当误差较小时,损失函数表现为 L2 Loss(平方惩罚);当误差较大时,损失函数逐渐向 L1 Loss过渡。这种平滑过渡既能对大误差有所控制,又不会对异常值过度敏感。
-
稳健性:对于异常值更加稳健,同时在小误差范围内提供了较好的优化效果。
应用场景:
常用于需要对大误差进行一定控制但又不希望完全忽略小误差的回归任务。特别适用于目标检测任务中的边界框回归,如 Faster R-CNN 等算法中。
predictions = torch.tensor([1.0, 2.0, 3.0, 4.0])
targets = torch.tensor([3.0, 2.5, 3.5, 4.5])# 方式一:
smooth_loss = nn.SmoothL1Loss()
loss = smooth_loss(predictions,targets)
print(loss)# 方式二:
loss1 = nn.functional.smooth_l1_loss(predictions,targets)
print(loss1)
1.2 CrossEntropyLoss
交叉熵损失函数,使用在输出层使用softmax激活函数进行多分类时,一般都采用交叉熵损失函数。
对于多分类问题,CrossEntropyLoss 公式如下:
其中:
-
C 是类别的总数。
-
y 是真实标签的one-hot编码向量,表示真实类别。
-
\hat{y} 是模型的输出(经过 softmax 后的概率分布)。
-
y_i 是真实类别的第 i 个元素(0 或 1)。
-
\hat{y}_i 是预测的类别概率分布中对应类别 i 的概率。
特点:
Softmax 就是将网络输出的 logits 通过 softmax 函数,就映射成为(0,1)的值,而这些值的累和为1(满足概率的性质),那么我们将它理解成概率,选取概率最大(也就是值对应最大的)节点,作为我们的预测目标类别。
# 假设有三个类别,模型输出是未经softmax的logits
logits = torch.tensor([[1.5, 2.0, 0.5], [0.5, 1.0, 1.5]])
labels = torch.tensor([1,2])criterion = nn.CrossEntropyLoss()loss = criterion(logits,labels)
print(loss)
1.3 BCELoss
二分类交叉熵损失函数,使用在输出层使用sigmoid激活函数进行二分类时。
公式:
log的底数一般默认为e,y是真实类别目标,根据公式可知L是一个分段函数 :
以上损失函数是一个样本的损失值,总样本的损失值是求损失均值即可。
# y 是模型的输出,已经被sigmoid处理过,确保其值域在(0,1)
y = torch.tensor([[0.7], [0.2], [0.9], [0.7]],dtype=torch.float32)
# targets 是真实的标签,0或1
t = torch.tensor([[1], [0], [1], [0]],dtype=torch.float32)# 计算损失方式一:
bceloss = nn.BCELoss()
loss = bceloss(y,t)
print(loss)#计算损失方式二:
loss2 = nn.functional.binary_cross_entropy(y,t)
print(loss2)
1.4. 总结
(1)当输出层使用softmax多分类时,使用交叉熵损失函数;
(2)当输出层使用sigmoid二分类时,使用二分类交叉熵损失函数, 比如在逻辑回归中使用;
(3)当功能为线性回归时,使用smooth L1损失函数或均方差损失-L2 loss;
2.BP算法
多层神经网络的学习能力比单层网络强得多。想要训练多层网络,需要更强大的学习算法。误差反向传播算法(Back Propagation)是其中最杰出的代表,它是目前最成功的神经网络学习算法。BP 算法不仅可用于多层前馈神经网络,还可以用于其他类型的神经网络。通常说 BP 网络时,一般是指用 BP 算法训练的多层前馈神经网络。
误差反向传播算法(BP)的基本步骤:
(1)前向传播:正向计算得到预测值。
(2)计算损失:通过损失函数计算预测值和真实值的差距。
(3)梯度计算:反向传播的核心是计算损失函数 L 对每个权重和偏置的梯度。
(4)更新参数:一旦得到每层梯度,就可以使用梯度下降算法来更新每层的权重和偏置,使得损失逐渐减小。
(5)迭代训练:将前向传播、梯度计算、参数更新的步骤重复多次,直到损失函数收敛或达到预定的停止条件。
2.1 前向传播
前向传播(Forward Propagation)把输入数据经过各层神经元的运算并逐层向前传输,一直到输出层为止。
作用:
-
计算神经网络的输出结果,用于预测或计算损失。
-
在反向传播中使用,通过计算损失函数相对于每个参数的梯度来优化网络。
2.2 反向传播
反向传播(Back Propagation,简称BP)通过计算损失函数相对于每个参数的梯度来调整权重,使模型在训练数据上的表现逐渐优化。反向传播结合了链式求导法则和梯度下降算法,是神经网络模型训练过程中更新参数的关键步骤。
2.2.1 原理
利用链式求导法则对每一层进行求导,直到求出输入层x的导数,然后利用导数值进行梯度更新
2.2.2. 链式法则
链式求导法则(Chain Rule)是微积分中的一个重要法则,用于求复合函数的导数。在深度学习中,链式法则是反向传播算法的基础,这样就可以通过分层的计算求得损失函数相对于每个参数的梯度。以下面的复合函数为例:
其中 x 是输入数据,w 是权重,b 是偏置。
函数分解:
可以将该复合函数分解为:
链式求导:
x = torch.tensor(1.0)
w = torch.tensor(0.0,requires_grad =True)
b = torch.tensor(0.0,requires_grad =True)# 函数
y = (torch.exp(-(w*x+b)+1))**-1# 自动微分
y.backward()print(w.grad)
2.2.3 重要性
反向传播算法极大地提高了多层神经网络训练的效率,使得训练深度模型成为可能。通过链式法则逐层计算梯度,反向传播可以有效地处理复杂的网络结构,确保每一层的参数都能得到合理的调整。
2.2.4 案例
2.2.4.1 数据准备
整体网络结构及神经元数据和权重参数如下图所示:
2.2.4.2 神经元计算
2.2.4.3 损失计算
2.2.4.5 梯度计算
2.2.4.5 参数更新
假设学习率是0.5
方式1:
import math# 前向传播
i1 = 0.05
i2 = 0.10
b1 = 0.35w1 = 0.15
w2 = 0.20w3 = 0.25
w4 = 0.30w5 = 0.40
w6 = 0.45w7 = 0.50
w8 = 0.55# 神经元h1
def h1():# 线性l1_1 = i1 * w1 + i2 * w2 + b1# 激活函数return 1 / (1 + math.exp(-l1_1))# 神经元h2
def h2():# 线性l1_2 = i1 * w3 + i2 * w4 + b1# 激活函数return 1 / (1 + math.exp(-l1_2))b2 = 0.60# 神经元o1
def o1(h1_val, h2_val):# 线性l2_1 = h1_val * w5 + h2_val * w6 + b2# 激活函数return 1 / (1 + math.exp(-l2_1))# 神经元o2
def o2(h1_val, h2_val):# 线性l2_2 = h1_val * w7 + h2_val * w8 + b2# 激活函数return 1 / (1 + math.exp(-l2_2))# 计算前向传播的结果
h1_val = h1()
h2_val = h2()
o1_val = o1(h1_val, h2_val)
o2_val = o2(h1_val, h2_val)print('h1:', h1_val)
print('h2:', h2_val)
print('o1:', o1_val)
print('o2:', o2_val)# 计算MSE损失
def mse(o1_val, o2_val):return 0.5 * ((o1_val - 0.01) ** 2 + (o2_val - 0.99) ** 2)print('MSE Loss:', mse(o1_val, o2_val))# 求w5的梯度 = mse()对o1求导 * o1对l2_1求导 * l2_1对w5求导
# dw5 = (o1 - 0.01) * [o1 * (1 - o1)] * h1
dw5 = (o1_val - 0.01) * (o1_val * (1.0 - o1_val)) * h1_val
print('dw5:', dw5)# 求w7的梯度 = mse()对o2求导 * o2对l2_2求导 * l2_2对w7求导
# dw7 = (o2 - 0.99) * [o2 * (1 - o2)] * h1
dw7 = (o2_val - 0.99) * (o2_val * (1.0 - o2_val)) * h1_val
print('dw7:', dw7)# 求w1的梯度 = mse()对o1求导 * o1对l2_1求导 * l2_1对h1求导 * h1对l1_1求导 *l1_1对w1求导 + mse()对o2求导 * o2对l2_2求导 * l2_2对h1求导 * h1对l1_1求导 *l1_1对w1求导
# dw1 = (o1() - 0.01) * [o1() * (1 - o1())] * w5 * [h1() * (1 - h1())] * i1 + (o2 - 0.99) * [o2 * (1 - o2)] *w7 * [h1() * (1 - h1())] * i1
dw1 = (o1_val - 0.01) * (o1_val * (1.0 - o1_val)) * w5 * h1_val*(1.0-h1_val) * 0.05 + (o2_val - 0.99) * (o2_val * (1.0 - o2_val)) * w7 *h1_val*(1.0-h1_val) * 0.05
print('dw1:', dw1)
方式2:
# 前向传播
i1_i2 = torch.tensor([0.05,0.10])model1 = nn.Linear(2,2)
model1.weight.data = torch.tensor([[0.15,0.20],[0.25,0.30]])
model1.bias.data = torch.tensor([0.35])
l11_l12 = model1(i1_i2)
h1_h2 =torch.sigmoid(l11_l12)model2 = nn.Linear(2,2)
model2.weight.data = torch.tensor([[0.40,0.45],[0.50,0.55]])
model2.bias.data = torch.tensor([0.60])
l21_l22 = model2(h1_h2)
o1_o2 = torch.sigmoid(l21_l22)# 反向传播
true_o1_o2 = torch.tensor([0.01,0.99])
mse = nn.MSELoss()
loss = mse(o1_o2,true_o1_o2)
print(loss)loss.backward()
print(model1.weight.grad)
print(model2.weight.grad)
方式3:
import torch
import torch.nn as nn
import torch.optim as optimclass Net(nn.Module):def __init__(self):super(Net,self).__init__()self.linear1 = nn.Linear(2,2)self.linear2 = nn.Linear(2,2)self.linear1.weight.data=torch.tensor([[0.15,0.20],[0.25,0.30]])self.linear2.weight.data=torch.tensor([[0.40,0.45],[0.50,0.55]])self.linear1.bias.data = torch.tensor([0.35], dtype=torch.float32)self.linear2.bias.data = torch.tensor([0.60], dtype=torch.float32)def forward(self,x):x = self.linear1(x)x = torch.sigmoid(x)x = self.linear2(x)x = torch.sigmoid(x)return xinput = torch.tensor([[0.05, 0.10]])
target = torch.tensor([[0.01, 0.99]])model = Net()
output = model(input)print(output)
mse = torch.sum((output-target)**2)/2
print(mse)optimizer = optim.SGD(model.parameters(), lr=0.5)
# 梯度清零
optimizer.zero_grad()mse .backward()print(model.linear1.weight.grad)
print(model.linear2.weight.grad)#更新梯度
optimizer.step()# 打印更新后的网络参数
print(model.state_dict())
2.3 BP之梯度下降
梯度下降算法的目标是找到使损失函数 L(\theta) 最小的参数 \theta,其核心是沿着损失函数梯度的负方向更新参数,以逐步逼近局部或全局最优解,从而使模型更好地拟合训练数据。
2.3.1 传统下降方式
2.3.1.1 批量梯度下降
Batch Gradient Descent 简称BGD
特点: 每次更新参数时,使用整个训练集来计算梯度。
优点:
-
收敛稳定,能准确地沿着损失函数的真实梯度方向下降。
-
适用于小型数据集。
缺点:
-
对于大型数据集,计算量巨大,更新速度慢。
-
需要大量内存来存储整个数据集。
公式:
其中,m 是训练集样本总数,x^{(i)}, y^{(i)} 是第 i 个样本及其标签。
2.3.1.2 随机梯度下降
Stochastic Gradient Descent 简称SGD
特点:每次更新参数时,仅使用一个样本来计算梯度。
优点:
-
更新频率高,计算快,适合大规模数据集。
-
能够跳出局部最小值,有助于找到全局最优解。
缺点:
-
收敛不稳定,容易震荡,因为每个样本的梯度可能都不完全代表整体方向。
-
需要较小的学习率来缓解震荡。
公式:
其中,x^{(i)}, y^{(i)} 是当前随机抽取的样本及其标签。
2.3.1.3 小批量梯度下降
Mini-batch Gradient Descent 简称MGBD
特点:每次更新参数时,使用一小部分训练集(小批量)来计算梯度。
优点:
-
在计算效率和收敛稳定性之间取得平衡。
-
能够利用向量化加速计算,适合现代硬件(如GPU)。
缺点:
-
选择适当的批量大小比较困难;批量太小则接近SGD,批量太大则接近批量梯度下降。
-
通常会根据硬件算力设置为32\64\128\256等2的次方。
公式:
2.3.2 存在的问题
-
收敛速度慢:BGD和MBGD使用固定学习率,太大会导致震荡,太小又收敛缓慢。
-
局部最小值和鞍点问题:SGD在遇到局部最小值或鞍点时容易停滞,导致模型难以达到全局最优。
-
训练不稳定:SGD中的噪声容易导致训练过程中不稳定,使得训练陷入震荡或不收敛。
2.3.3 优化梯度下降方式
2.3.3.1 指数加权平均
指数移动加权平均(Exponential Moving Average简称EMA) 则是参考各数值,并且各数值的权重都不同,距离越远的数字对平均数计算的贡献就越小(权重较小),距离越近则对平均数的计算贡献就越大(权重越大)。
算数平均 | 将所有数加起来除以数的个数,每个数的权重是相同的 |
加权平均 | 给每个数赋予不同的权重求得平均数 |
移动平均 | 计算最近邻的 N 个数来获得平均数 |
公式:
其中:
-
St 表示指数加权平均值(EMA);
-
Yt 表示 t 时刻的值;
-
\beta 是平滑系数,取值范围为 0\leq \beta < 1。\beta 越接近 1,表示对历史数据依赖性越高;越接近 0 则越依赖当前数据。该值越大平均数越平缓
import numpy as np
import matplotlib.pyplot as plt
import torchtemperature = np.array([10,10.5,11,11.2,11.3,12,12.1,12.6,12.4,13,13.8,13.91,14.0,13.6,14.9,15.21,15.64,16,16.89,17.9])def origial():# 每天的温度变化图days = np.arange(1,len(temperature)+1,1)plt.plot(days,temperature,color ='r')plt.scatter(days,temperature)plt.show()# Exponentail Moving Average
def ema(beta):'''bata:指数加权平均的平滑系数'''# ema_list存储处理后的数据ema_list=[]for index,value in enumerate(temperature):if index ==0:ema_list.append(value)else:# beta*(上一转换元素)+(1-beta)*(未转换的此元素)new_value = beta*ema_list[-1]+(1-beta)*valueema_list.append(new_value)days = np.arange(1,len(temperature)+1,1)plt.scatter(days,temperature)plt.plot(days,ema_list,color='r')plt.show()origial()
# bate值越大,图像越平缓
ema(0.5)
ema(0.9)
2.3.3.2 Momentum
特点:
动量(Momentum)是对梯度下降的优化方法,可以更好地应对梯度变化和梯度消失问题,从而提高训练模型的效率和稳定性。
-
惯性效应: 该方法加入前面梯度的累积,这种惯性使得算法沿着当前的方向继续更新。如遇到鞍点,也不会因梯度逼近零而停滞。
-
减少震荡: 该方法平滑了梯度更新,减少在鞍点附近的震荡,帮助优化过程稳定向前推进。
-
加速收敛: 该方法在优化过程中持续沿着某个方向前进,能够更快地穿越鞍点区域,避免在鞍点附近长时间停留。
梯度计算公式:
Dt = β * St-1 + (1- β) * Dt
-
St-1 表示历史梯度移动加权平均值
-
wt 表示当前时刻的梯度值
-
β 为权重系数
原理:
Monmentum 优化方法是如何一定程度上克服 “平缓”、”鞍点”、”峡谷” 的问题呢?
当处于鞍点位置时,由于当前的梯度为 0,参数无法更新。但是 Momentum 动量梯度下降算法已经在先前积累了一些梯度值,很有可能使得跨过鞍点。
由于 mini-batch 普通的梯度下降算法,每次选取少数的样本梯度确定前进方向,可能会出现震荡,使得训练时间变长。Momentum 使用移动加权平均,平滑了梯度的变化,使得前进方向更加平缓,有利于加快训练过程。一定程度上有利于降低 “峡谷” 问题(就是会使得参数更新出现剧烈震荡)的影响。
API :
optimizer = optim.SGD(model.parameters(), lr, momentum)
momentum (动量系数)通常设置为 0 到0.5 之间的一个值,也可根据具体的应用场景调整。
总结:
-
动量项更新:利用当前梯度和历史动量来计算新的动量项。
-
权重参数更新:利用更新后的动量项来调整权重参数。
-
梯度计算:在每个时间步计算当前的梯度,用于更新动量项和权重参数。
-
Momentum 算法是对梯度值的平滑调整,但是并没有对梯度下降中的学习率进行优化。
import torch
import torch.nn as nn
import torch.optim as optimclass 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 outputdef backward():model = Net()optimizer = optim.SGD(model.parameters(),lr=1,momentum=0.5)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()
2.3.3.3 AdaGrad
自适应梯度算法 AdaGrad(Adaptive Gradient Algorithm)为每个参数引入独立的学习率,它根据历史梯度的平方和来调整这些学习率,这样就使得参数具有较大的历史梯度的学习率减小,而参数具有较小的历史梯度的学习率保持较大,从而实现更有效的学习。AdaGrad避免了统一学习率的不足,更多用于处理稀疏数据和梯度变化较大的问题。
AdaGrad流程:
(1)初始化学习率 α、初始化参数 θ、小常数 σ = 1e-6
(2)初始化梯度累积变量 s = 0
(3)从训练集中采样 m 个样本的小批量,计算梯度 g
(4)累积平方梯度 s = s + g ⊙ g,⊙ 表示各个分量相乘
(5)学习率 α 的计算公式如下:
(6)参数更新公式如下:
其中:
-
\alpha 是全局的初始学习率。
-
\sigma 是一个非常小的常数,用于避免除零操作(通常取 10^{-8})。
-
\frac{\alpha}{\sqrt{s }+\sigma} 是自适应调整后的学习率。
API:
optimizer = optim.Adagrad(model.parameters(), lr)
import torch
import torch.nn as nn
import torch.optim as optimclass 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 outputdef backward():model = Net()optimizer = optim.Adagrad(model.parameters(),lr=1)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()
2.3.3.4 RMSProp
均方根传播 RMSProp(Root Mean Square Propagation)在时间步中,不是简单地累积所有梯度平方和,而是使用指数加权平均来逐步衰减过时的梯度信息。这种方法专门用于解决AdaGrad在训练过程中学习率过度衰减的问题。
RMSProp过程
(1)初始化学习率 α、初始化参数 θ、小常数 σ = 1e-8( 用于防止除零操作(通常取 10^{-8} ))。
(2)初始化参数 θ
(3)初始化梯度累计变量 s=0
(4)从训练集中采样 m 个样本的小批量,计算梯度 g
(5)使用指数移动平均累积历史梯度,公式如下:
(6)学习率 α 的计算公式如下:
(7)参数更新公式如下:
API:
optimizer = optim.RMSprop(model.parameters(), lr, momentum)
import torch
import torch.nn as nn
import torch.optim as optimclass 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 outputdef backward():model = Net()optimizer = optim.RMSprop(model.parameters(),lr=0.01,momentum=0.5)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()
2.3.3.5 Adam
自适应矩估计 Adam(Adaptive Moment Estimation)算法将动量法和RMSProp的优点结合在一起:
-
动量法:通过一阶动量(即梯度的指数加权平均)来加速收敛,尤其是在有噪声或梯度稀疏的情况下。
-
RMSProp:通过二阶动量(即梯度平方的指数加权平均)来调整学习率,使得每个参数的学习率适应其梯度的变化。
-
Momentum 使用指数加权平均计算当前的梯度值、AdaGrad、RMSProp 使用自适应的学习率,Adam 结合了 Momentum、RMSProp 的优点,使用:移动加权平均的梯度和移动加权平均的学习率。使得能够自适应学习率的同时,也能够使用 Momentum 的优点。
API:
optimizer = optim.Adam(model.parameters(), lr)
import torch
import torch.nn as nn
import torch.optim as optimclass 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 outputdef backward():model = Net()optimizer = optim.Adam(model.parameters(),lr=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()
2.3.4 总结
梯度下降算法通过不断更新参数来最小化损失函数,是反向传播算法中计算权重调整的基础。在实际应用中,根据数据的规模和计算资源的情况,选择合适的梯度下降方式(批量、随机、小批量)及其变种(如动量法、Adam等)可以显著提高模型训练的效率和效果。
Adam是目前最为流行的优化算法之一,因其稳定性和高效性,广泛应用于各种深度学习模型的训练中。