深度学习中常用的优化方法包括啦momentum(动量法),Adagrad(adaptive gradient自适应梯度法),RMSProp(root mean square propagation均方根传播算法),Adam(adaptive moment estimation自适应矩估计法)
指数加权平均算法
所谓指数加权平均算法是上述优化算法的基础,其作用是对历史数据和当前数据进行加权求和,具体公式如下
if t==0:
if t > 0
其中
- 为时间步t的加权平均值
- 为为时间步t-1的加权平均值
- 为时间步t的观测值
- 为平滑因子,
可以较为明显地看出,所谓指数加权平均算法的关键就在于的大小,其越大,当前时间步的值就会越偏向过去的值,换句话说,整体数值序列就会更加平滑
指数加权平均算法的可视化
import torch
import torch.nn as nn
import matplotlib.pyplot as plt
from numpy.array_api import linspace
torch.manual_seed(0)
def exponential_wma(data,beta=0.9):
list1 = []
for n,i in enumerate(data):
if n ==0:
list1.append(i)
else:
list1.append(beta * list1[n-1] + (1-beta) * i)
return list1
if __name__ == '__main__':
data = torch.randn(50)*10
x = torch.linspace(1,50,50)
fig = plt.figure()
axes1 = plt.subplot(1,2,1)
axes1.scatter(x,data)
axes1.plot(x,data)
axes1.set_xlabel('x')
axes1.set_ylabel('y')
axes1.set_title('original data')
print(data)
axes2 = plt.subplot(1,2,2)
axes2.scatter(x,data,label='original_data')
axes2.plot(x,exponential_wma(data), label='ewma_curve')
axes2.set_xlabel('x')
axes2.set_ylabel('y')
axes2.set_title('ewma')
print(exponential_wma(data))
axes2.legend()
plt.subplots_adjust(wspace=0.4)
fig.savefig('ewma.png')
plt.show()
# tensor([-11.2584, -11.5236, -2.5058, -4.3388, 8.4871, 6.9201, -3.1601,
# -21.1522, 3.2227, -12.6333, 3.4998, 3.0813, 1.1984, 12.3766,
# 11.1678, -2.4728, -13.5265, -16.9593, 5.6665, 7.9351, 5.9884,
# -15.5510, -3.4136, 18.5301, 7.5019, -5.8550, -1.7340, 1.8348,
# 13.8937, 15.8633, 9.4630, -8.4368, -6.1358, 0.3159, 10.5536,
# 1.7784, -2.3034, -3.9175, 5.4329, -3.9516, 2.0553, -4.5033,
# 15.2098, 34.1050, -15.3118, -12.3414, 18.1973, -5.5153, -13.2533,
# 1.8855])
# [tensor(-11.2584), tensor(-11.2849), tensor(-10.4070), tensor(-9.8002), tensor(-7.9715), tensor(-6.4823), tensor(-6.1501), tensor(-7.6503), tensor(-6.5630), tensor(-7.1700), tensor(-6.1030), tensor(-5.1846), tensor(-4.5463), tensor(-2.8540), tensor(-1.4518), tensor(-1.5539), tensor(-2.7512), tensor(-4.1720), tensor(-3.1881), tensor(-2.0758), tensor(-1.2694), tensor(-2.6976), tensor(-2.7692), tensor(-0.6392), tensor(0.1749), tensor(-0.4281), tensor(-0.5587), tensor(-0.3193), tensor(1.1020), tensor(2.5781), tensor(3.2666), tensor(2.0962), tensor(1.2730), tensor(1.1773), tensor(2.1150), tensor(2.0813), tensor(1.6428), tensor(1.0868), tensor(1.5214), tensor(0.9741), tensor(1.0822), tensor(0.5237), tensor(1.9923), tensor(5.2036), tensor(3.1520), tensor(1.6027), tensor(3.2621), tensor(2.3844), tensor(0.8206), tensor(0.9271)]
这里设置的平滑因子为0.9,通常情况下使用动量法的时候平滑因子也会设置为0.9
平滑因子越大,理论上曲线就会越平滑
Momentum动量法
Momentum动量法的原理就是在梯度下降的时候使用指数加权平均法计算下降的梯度
动量更新方法如下
其中
- 是动量项(累积的梯度),用于更新参数。
- 是动量系数(通常 ,例如 0.9 ),它决定了之前梯度对当前更新的影响程度。
- 是前一步的动量值。
- 是当前时间步 t 计算得到的梯度。
- 是当前模型参数。
参数更新方法如下
其中
- 是更新后的参数。
- 是当前的参数。
- 是学习率。
- 是当前的动量项(累积梯度)。
动量法的调用
动量法的调用一般集成在SGD优化器中,通过设置SGD优化器中的momentum参数来配置,momentum参数的值就是动量系数(平滑因子)
import torch
import torch.nn as nn
import matplotlib.pyplot as plt
import torch.optim as optim
model = nn.Linear(5, 1)
optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9)
print(model)
print(optimizer)
# Linear(in_features=5, out_features=1, bias=True)
# SGD (
# Parameter Group 0
# dampening: 0
# differentiable: False
# foreach: None
# fused: None
# lr: 0.001
# maximize: False
# momentum: 0.9
# nesterov: False
# weight_decay: 0
# )
AdaGrad自适应梯度
AdaGrad(自适应梯度法)的作用是随着训练的进行,对学习率进行逐步衰减
累计梯度平方和
其中
- 是参数梯度平方的累积和(是一个对角矩阵,表示每个参数的梯度平方和)。
- 是当前时间步 t 的梯度
- 是前面所有时间步的梯度平方和
参数更新方式
其中
- 是当前的参数
- 是全局的学习率
- 是梯度平方的累积和
- 是一个小常数,用于防止除以零,通常取值为
自适应梯度法的调用
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
class SimpleNet(nn.Module):
def __init__(self):
super(SimpleNet, self).__init__()
self.fc1 = nn.Linear(10, 100)
self.fc2 = nn.Linear(100, 5)
def forward(self, x):
x = F.relu(self.fc1(x))
x = self.fc2(x)
return x
if __name__ == '__main__':
my_net = SimpleNet()
optimizer = optim.Adagrad(my_net.parameters(), lr=0.01)
print(my_net)
print(optimizer)
# SimpleNet(
# (fc1): Linear(in_features=10, out_features=100, bias=True)
# (fc2): Linear(in_features=100, out_features=5, bias=True)
# )
# Adagrad (
# Parameter Group 0
# differentiable: False
# eps: 1e-10
# foreach: None
# fused: None
# initial_accumulator_value: 0
# lr: 0.01
# lr_decay: 0
# maximize: False
# weight_decay: 0
# )
RMSProp均方根传播法
RMSProp均方根传播法是在Adagrad的基础上做了优化,由于Adagrad中的累计平方和,会导致学习率快速下降导致模型收敛变慢
所以RMSProp对累计平方和进行了优化,转为了加权平均算法
梯度平方的指数加权平均算法
其中
- 是梯度平方的指数加权移动平均值(即该参数梯度的平滑历史平方值)
- 是衰减系数(通常取值接近 1,例如 0.9 或 0.99)
- 是当前时刻 t 的梯度
- 是前一时刻的梯度平方的指数加权移动平均
参数更新
其中
- 是当前的参数
- 是当前的学习率
- 是梯度平方的指数加权移动平均
- 是一个小常数(通常取 10^{-8} ),用于防止除以零的情况
同时RMSProp也支持动量法用于记录历史梯度,但是与SGD中国的动量法有所不同
动量项 的更新
其中
- 是当前动量项
- 是前一时刻的动量项
- 是动量系数,通常接近 1(例如 0.9 或 0.99),表示过去动量的影响
- 是学习率
- 是当前的梯度
- 是梯度平方的指数加权移动平均,用来动态调整每个参数的学习率
这里动量项的加入会使模型加快熟练
均方根传播法的调用
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
class SimpleNet(nn.Module):
def __init__(self):
super(SimpleNet, self).__init__()
self.fc1 = nn.Linear(10, 100)
self.fc2 = nn.Linear(100, 5)
def forward(self, x):
x = F.relu(self.fc1(x))
x = self.fc2(x)
return x
if __name__ == '__main__':
my_net = SimpleNet()
optimizer = optim.RMSprop(my_net.parameters(), lr=0.01, alpha=0.99,momentum=0.9)
print(my_net)
print(optimizer)
# SimpleNet(
# (fc1): Linear(in_features=10, out_features=100, bias=True)
# (fc2): Linear(in_features=100, out_features=5, bias=True)
# )
# RMSprop (
# Parameter Group 0
# alpha: 0.99
# capturable: False
# centered: False
# differentiable: False
# eps: 1e-08
# foreach: None
# lr: 0.01
# maximize: False
# momentum: 0.9
# weight_decay: 0
# )
Adam自适应矩估计法
Adam自适应矩估计法,与RMSProp不同,其完整融合了Momentum和AdaGrad方法
一阶矩估计(动量)
- 是梯度的一阶矩的指数加权移动平均值(即动量)
- 是控制动量的衰减系数,通常取 0.9
- 是当前时刻的梯度
二阶矩估计(梯度平方的指数加权移动平均)
- 是梯度平方的指数加权移动平均值(相当于 RMSProp 中的累积梯度平方)
- 是控制二阶矩的衰减系数,通常取 0.999
一阶矩和二阶矩的偏差校正
由于一阶矩和二阶矩在初始化的时候,不具备前一时刻的动量,所以由于(1-衰减系数)的存在,刚开始的梯度下降幅度偏小,所以这里使用了偏差校正去放大一开始的下降幅度
- 是一阶矩的偏差校正值
- 是二阶矩的偏差校正值
参数更新
- 是当前的参数
- 是学习率
- 是一个小常数,通常取 10^{-8} ,用于防止除以零的情况
- 是偏差校正后的一阶矩
- 是偏差校正后的二阶矩
参数设置说明
- 和 :分别控制一阶矩和二阶矩的衰减速率。通常推荐值为 = 0.9 和 = 0.999
- :用于防止除以零的问题,保证数值稳定性,通常设置为 10^{-8}
- 学习率 :这是 Adam 的全局学习率,通常设置为 0.001
- 是 的 t 次方,随着迭代步数 t 的增加, 趋近于 0,从而消除偏差
自适应矩估计法的调用
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
class SimpleNet(nn.Module):
def __init__(self):
super(SimpleNet, self).__init__()
self.fc1 = nn.Linear(10, 100)
self.fc2 = nn.Linear(100, 5)
def forward(self, x):
x = F.relu(self.fc1(x))
x = self.fc2(x)
return x
if __name__ == '__main__':
my_net = SimpleNet()
optimizer = optim.Adam(my_net.parameters(), lr=0.01, betas=(0.9, 0.999), eps=1e-8)
print(my_net)
print(optimizer)
# SimpleNet(
# (fc1): Linear(in_features=10, out_features=100, bias=True)
# (fc2): Linear(in_features=100, out_features=5, bias=True)
# )
# Adam (
# Parameter Group 0
# amsgrad: False
# betas: (0.9, 0.999)
# capturable: False
# differentiable: False
# eps: 1e-08
# foreach: None
# fused: None
# lr: 0.01
# maximize: False
# weight_decay: 0
# )
补充
在以上四个优化器中,都支持配置weight_decay参数,其为正则化系数,添加后可以对模型添加L2正则