本专栏用于记录关于深度学习的笔记,不光方便自己复习与查阅,同时也希望能给您解决一些关于深度学习的相关问题,并提供一些微不足道的人工神经网络模型设计思路。
专栏地址:「深度学习一遍过」必修篇
目录
1.1.3 Nesterov accelerated gradient 法
反向传播
1 优化器的高级使用
为每个参数单独设置选项:
optim.SGD([
{'params': model.base.parameters()},
{'params': model.classifier.parameters(), 'lr': 1e-3}
], lr=1e-2, momentum=0.9)
梯度下降算法中,学习率太大,函数无法收敛,甚至发散,如下图。学习率足够小,理论上是可以达到局部最优值的(非凸函数不能保证达到全局最优),但学习率太小却使得学习过程过于缓慢,合适的学习率应该是能在保证收敛的前提下,能尽快收敛。对于深度网络中,参数众多,参数值初始位置随机,同样大小的学习率,对于某些参数可能合适,对另外一些参数可能偏小(学习过程缓慢),对另外一些参数可能太大(无法收敛,甚至发散),而学习率一般而言对所有参数都是固定的,所以无法同时满足所有参数的要求。通过引入 可以让那些因学习率太大而来回摆动的参数,梯度能前后抵消,从而阻止发散。
1.1 基于更新方向
1.1.1 随机梯度下降 SGD 优化算法
torch.optim.SGD(params, lr=0.1, momentum=0, dampening=0, weight_decay=0, nesterov=False)
- () – 待优化参数的 或者是定义了参数组的
- () – 学习率
- (, 可选) – 动量因子(默认:)
- (, 可选) – 权重衰减( 惩罚)(默认:)
- (, 可选) – 动量的抑制因子(默认:)
- (, 可选) – 使用 动量(默认:)
在某多分类任务中,设置其损失函数、优化器、学习率:
criterion = nn.CrossEntropyLoss()
optimizer_ft = optim.SGD(modelclc.parameters(), lr=0.1, momentum=0.9)
exp_lr_scheduler = lr_scheduler.StepLR(optimizer_ft, step_size=100, gamma=0.1)
我们可视化后发现,训练集和测试集整体的正确率是呈现上升趋势,训练集的 呈现下降趋势也没问题,但测试集的 呈现突然地暴涨而无法收敛,于是我们断定选用 优化算法,尤其是填入的这几个参数无法阻止模型梯度发散,所以决定换个优化算法试试,具体见下文。
1.1.2 momentum 动量法
加速SGD,特别是处理高曲率、小但一致的梯度;积累了之前梯度指数级衰减的移动平均,并且继续沿该方向移动。
如果梯度方向不变,就越发更新的快,反之减弱,当前保证梯度收敛。
1.1.3 Nesterov accelerated gradient 法
在标准动量方法中添加了一个校正因子
要求梯度下降更快,更加智能,直接先按照前一次梯度方向更新一步将它作为当前的梯度
1.2 基于选择更为合理的学习率
1.2.1 Adam 优化算法
对梯度的一阶和二阶都进行了估计与偏差修正,使用梯度的一阶矩估计和二阶矩估计来动态调整每个参数的学习率。
- 对学习率没有那么敏感,学习步长有一个确定 的范围,参数更新比较稳。
- 学习率在训练的后期仍然可能不稳定导致无法 收敛到足够好的值,泛化能力较差。
torch.optim.Adam(params, lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0)
- () – 待优化参数的iterable或者是定义了参数组的
- (, 可选) – 学习率(默认:)
- ( [, ], 可选) – 用于计算梯度以及梯度平方的运行平均值的系数(默认:,)
- (, 可选) – 为了增加数值计算的稳定性而加到分母里的项(默认:)
- (, 可选) – 权重衰减( 惩罚)(默认: )
在某多分类任务中,设置其损失函数、优化器、学习率:
criterion = nn.CrossEntropyLoss()
optimizer_ft = optim.Adam(modelclc.parameters(), lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0)
exp_lr_scheduler = lr_scheduler.StepLR(optimizer_ft, step_size=100, gamma=0.1)
还是同样的结果:验证集的 又没有收敛,于是我果断提前按下了 “暂停” 键。此情此景下,我分析:改了个优化器不行,于是我决定进行调参这一痛苦操作…………
1.2.2 RMSprop 优化算法
torch.optim.RMSprop(params, lr=0.01, alpha=0.99, eps=1e-08, weight_decay=0, momentum=0, centered=False)
- () – 待优化参数的 或者是定义了参数组的
- (, 可选) – 学习率(默认:)
- (, 可选) – 动量因子(默认:)
- (, 可选) – 平滑常数(默认:)
- (, 可选) – 为了增加数值计算的稳定性而加到分母里的项(默认:)
- (, 可选) – 如果为 ,计算中心化的 ,并且用它的方差预测值对梯度进行归一化
- (, 可选) – 权重衰减( 惩罚)(默认: )
1.2.3 Adadelta优化算法
Adadelta 与 Adagrad 不同,只累加了一个窗口的梯度,使用动量平均计算。
- 优点:保留了 Adagrad 调节不同维度学习率的优势
- 缺点:训练后期反复在局部最小值附近抖动
torch.optim.Adadelta(params, lr=1.0, rho=0.9, eps=1e-06, weight_decay=0)
- () – 待优化参数的 或者是定义了参数组的
- (, 可选) – 用于计算平方梯度的运行平均值的系数(默认:)
- ( , 可选) – 为了增加数值计算的稳定性而加到分母里的项(默认:)
- (, 可选) – 在 被应用到参数更新之前对它缩放的系数(默认:)
- (, 可选) – 权重衰减( 惩罚)(默认: )
1.2.4 Adagrad优化算法
自适应地为各个维度的参数分配不同的学习率
- 较小的时候,能够放大梯度,较大的时候,能够约束梯度(激励+惩罚)。
- 梯度累积导致学习率单调递减,后期学习率非常小。
- 需要设置一个合适的全局初始学习率 。
torch.optim.Adagrad(params, lr=0.01, lr_decay=0, weight_decay=0)
- () – 待优化参数的 或者是定义了参数组的
- (, 可选) – 学习率(默认: )
- (, 可选) – 学习率衰减(默认: )
- (, 可选) – 权重衰减( 惩罚)(默认: )
1.2.5 Adamax优化算法
torch.optim.Adamax(params, lr=0.002, betas=(0.9, 0.999), eps=1e-08, weight_decay=0)
- () – 待优化参数的 或者是定义了参数组的
- (, 可选) – 学习率(默认:)
- ( [, ], 可选) – 用于计算梯度以及梯度平方的运行平均值的系数
- (, 可选) – 为了增加数值计算的稳定性而加到分母里的项(默认:)
- (, 可选) – 权重衰减( 惩罚)(默认: )
1.2.6 AMSgrad 方法
Adam 类的方法之所以会不能收敛到好的结果,是因为在优化算法中广泛使用的指数衰减方法会使得梯度的记忆时间太短。
使用过去平方梯度的最大值来更新参数, 而不是指数平均 。
2 学习率迭代策略
2.1 StepLR调整算法
等间隔调整学习率,调整倍数为 倍,调整间隔为 , 指 。
torch.optim.lr_scheduler.StepLR(optimizer, step_size, gamma=0.1, last_epoch=-1)
scheduler = StepLR(optimizer, step_size=30, gamma=0.1)
for epoch in range(100):
train(...)
validate(...)
scheduler.step()
2.2 MultiStepLR 调整算法
调节的 是自己定义。
torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones, gamma=0.1, last_epoch=-1)
lr = 0.05 if epoch < 30
lr = 0.005 if 30 <= epoch < 80
lr = 0.0005 if epoch >= 80
scheduler = MultiStepLR(optimizer, milestones=[30,80], gamma=0.1)
for epoch in range(100):
train(...)
validate(...)
scheduler.step()
2.3 ExponentialLR
指数形式增长
torch.optim.lr_scheduler.ExponentialLR(optimizer, gamma, last_epoch=-1)
2.4 LambdaLR
能够根据自己的定义调节
torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda, last_epoch=-1)
lambda1 = lambda epoch: epoch // 30
lambda2 = lambda epoch: 0.95 ** epoch
scheduler = LambdaLR(optimizer, lr_lambda=[lambda1, lambda2])
for epoch in range(100):
train(...)
validate(...)
scheduler.step()
3 分类优化目标定义
机器学习用有限训练集上的期望损失作为优化目标(代理损失函数),损失代表预测值 与真实值 的不一致程度,损失函数越小,一般模型的性能越好。
观察训练集和测试集的误差就能知道模型的收敛情况,估计模型的性能。
- 分类,预测概率分布
- 回归,预测具体数值
- 稳定平滑
- 适配不平衡样本
- 优化分类界面等
import torch.optim as optim
criterion = nn.CrossEntropyLoss() #交叉熵损失
3.1 NLLLoss优化目标
负的损失,用于训练一个 类分类器。
torch.nn.NLLLoss(weight=None, size_average=True)
- : (,) , 是类别的个数
- : (), 中每个值的大小满足
m = nn.LogSoftmax()
loss = nn.NLLLoss()
# input is of size nBatch x nClasses = 3 x 5
input = autograd.Variable(torch.randn(3, 5), requires_grad=True)
# each element in target has to have 0 <= value < nclasses
target = autograd.Variable(torch.LongTensor([1, 0, 4]))
output = loss(m(input), target)
output.backward()
3.2 CrossEntropyLoss优化目标
torch.nn.CrossEntropyLoss(weight=None, size_average=True)
- : (,) ,是类别的个数
- : (), 是 的大小,
将 和 集成到一个类中
3.3 BCELoss优化目标
计算 与 之间的二进制交叉熵
torch.nn.BCELoss(weight=None, size_average=True)
- : (,) , 是类别的个数
- Target: (,) , 中每个值的大小满足
3.4 KL散度优化目标:
散度常用来描述两个分布的距离,并在输出分布的空间上执行直接回归是有用的。
torch.nn.KLDivLoss(weight=None, size_average=True)
3.5 MSELoss与L1优化目标
torch.nn.MSELoss(size_average=True)
torch.nn.L1Loss(size_average=True)
torch.nn.SmoothL1Loss(size_average=True)
实例补充:损失函数
- 计算实际输出和目标之间的差距
- 为我们更新输出提供一定的依据(反向传播)
L1Loss
import torch
from torch.nn import L1Loss
inputs = torch.tensor([1, 2, 3], dtype=torch.float32)
targets = torch.tensor([1, 2, 5], dtype=torch.float32)
inputs = torch.reshape(inputs, (1,1,1,3))
targets = torch.reshape(targets, (1,1,1,3))
loss = L1Loss()
result = loss(inputs, targets)
print(result)
MSELoss
import torch
from torch import nn
inputs = torch.tensor([1, 2, 3], dtype=torch.float32)
targets = torch.tensor([1, 2, 5], dtype=torch.float32)
inputs = torch.reshape(inputs, (1,1,1,3))
targets = torch.reshape(targets, (1,1,1,3))
loss = nn.MSELoss()
result = loss(inputs, targets)
print(result)
交叉熵损失
import torch
from torch import nn
x = torch.tensor([0.1, 0.2, 0.3])
y = torch.tensor([1])
x = torch.reshape(x, (1, 3))
loss = nn.CrossEntropyLoss()
result = loss(x, y)
print(result)
反向传播
import torchvision
import torch
from torch import nn
import torch.nn.functional as F
from torch.utils.data import DataLoader
test_data = torchvision.datasets.CIFAR10('./dataset', train=False, download=True, transform=torchvision.transforms.ToTensor())
test_loader = DataLoader(dataset=test_data, batch_size=1)
class customize_model(nn.Module):
def __init__(self):
super(customize_model,self).__init__()
self.conv1 = nn.Conv2d(3, 12, 3, 2)
self.bn1 = nn.BatchNorm2d(12)
self.conv2 = nn.Conv2d(12, 24, 3, 2)
self.bn2 = nn.BatchNorm2d(24)
self.fc1 = nn.Linear(1176 , 10)
self.flatten = nn.Flatten()
def forward(self , x):
x = F.relu(self.bn1(self.conv1(x)))
x = F.relu(self.bn2(self.conv2(x)))
x = self.flatten(x)
x = self.fc1(x)
return x
model = customize_model()
optim = torch.optim.SGD(model.parameters(), lr=0.01)
loss = nn.CrossEntropyLoss()
for epoch in range(5):
running_loss = 0.0
for data in test_loader:
imgs, targets = data
outputs = model(imgs)
result_loss = loss(outputs, targets)
optim.zero_grad()
result_loss.backward()
optim.step()
running_loss = running_loss + result_loss
print(running_loss)