优化器
优化器
PyTorch 中的优化器是用于管理并更新模型中可学习参数的值,使得模型输出更加接近真实标签。
Optimizer
Optimizer
是优化器的基类
class Optimizer(object):
def __init__(self, params, defaults):
self.defaults = defaults
self.state = defaultdict(dict)
self.param_groups = []
2
3
4
5
Optimizer 属性
defaults
:存储的是优化器的超参数,例子如下:
{'lr': 0.1, 'momentum': 0.9, 'dampening': 0, 'weight_decay': 0, 'nesterov': False}
state
:参数的缓存,例子如下:
defaultdict(<class 'dict'>, {tensor([[ 0.3864, -0.0131],
[-0.1911, -0.4511]], requires_grad=True): {'momentum_buffer': tensor([[0.0052, 0.0052],
[0.0052, 0.0052]])}})
2
3
param_groups
:管理的参数组,是一个 list,其中每个元素是一个字典,顺序是 params,lr,momentum,dampening,weight_decay,nesterov,例子如下:
[{'params': [tensor([[-0.1022, -1.6890],[-1.5116, -1.7846]], requires_grad=True)], 'lr': 1, 'momentum': 0, 'dampening': 0, 'weight_decay': 0, 'nesterov': False}]
Optimizer 方法
zero_grad()
:清空所管理参数的梯度。由于PyTorch
的特性是张量的梯度不自动清零,因此每次反向传播之后都需要清空梯度。代码如下:step()
:执行一步梯度更新add_param_group()
:添加参数组,主要代码如下:state_dict()
:获取优化器当前状态信息字典load_state_dict()
:加载状态信息字典,包括state 、momentum_buffer 和 param_groups
。主要用于模型的断点续训练。我们可以在每隔 50 个 epoch 就保存模型的state_dict
到硬盘,在意外终止训练时,可以继续加载上次保存的状态,继续训练。
zero_grad()
在 PyTorch 中,
zero_grad()
函数用于将模型中所有参数的梯度重置为零。这是因为在神经网络训练过程中,每次进行反向传播(backpropagation)时,梯度是累积计算的,而不是被替换。因此,在处理每个批次(batch)的数据之前,我们需要调用zero_grad()
来确保梯度的正确计算。
def zero_grad(self, set_to_none: bool = False):
for group in self.param_groups:
for p in group['params']:
if p.grad is not None: #梯度不为空
if set_to_none:
p.grad = None
else:
if p.grad.grad_fn is not None:
p.grad.detach_()
else:
p.grad.requires_grad_(False)
p.grad.zero_()# 梯度设置为0
2
3
4
5
6
7
8
9
10
11
12
zero_grad()
函数有两种常见的使用方式:model.zero_grad()
和optimizer.zero_grad()
。这两种方式在功能上是等效的,它们都会将模型中所有参数的梯度设置为零。当使用optimizer = optim.Optimizer(model.parameters())
时,这两种方式可以互换使用,其中Optimizer
可以是 SGD、Adam 等优化器。
使用示例
import torch
import torch.optim as optim
# 定义模型和优化器
model = ... # 这里应该是你的模型定义
optimizer = optim.SGD(model.parameters(), lr=0.01) # 使用SGD优化器,学习率为0.01
# 训练循环
for inputs, targets in dataloader:
# 前向传播
outputs = model(inputs)
# 计算损失
loss = criterion(outputs, targets)
# 梯度清零
optimizer.zero_grad() # 或者使用 model.zero_grad()
# 反向传播
loss.backward()
# 更新参数
optimizer.step()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
在上面的示例中,我们首先定义了一个模型和一个优化器。然后,在训练循环中,我们首先对模型进行前向传播以计算输出,接着计算损失,然后调用optimizer.zero_grad()
来清零梯度,进行反向传播以计算梯度,最后使用优化器更新模型的参数。
step()
step()
方法执行以下操作:
梯度获取:在调用
step()
之前,通常已经通过loss.backward()
计算了损失函数关于模型参数的梯度。这些梯度存储在模型参数的.grad
属性中。参数更新:
step()
方法会根据优化器(如 SGD、Adam 等)中定义的更新规则,以及当前的学习率和其他超参数,来更新模型的参数。对于每个参数,它会使用相应的梯度信息来调整参数值,以便在下一次迭代中更好地拟合训练数据。内部状态更新:一些优化器(如 Adam)还维护了参数的内部状态,如一阶矩估计和二阶矩估计。在调用
step()
时,这些内部状态也会根据当前的梯度和参数进行更新。
需要注意的是,在每次调用step()
之前,通常需要先调用optimizer.zero_grad()
来清除之前累积的梯度。这是因为 PyTorch 中的梯度是累积的,如果不进行清零操作,新的梯度将会与之前的梯度相加,导致错误的更新。
此外,step()
方法通常与训练循环中的批次(batch)处理配合使用。在每个批次处理完成后,都会调用step()
来更新模型参数,以便在下一个批次中继续优化。
张量 weight 的形状为optimizer.step()
更新梯度,也就是所有的张量都减去 1。
使用示例
weight = torch.randn((2, 2), requires_grad=True)
weight.grad = torch.ones((2, 2))
optimizer = optim.SGD([weight], lr=1)
print("weight before step:{}".format(weight.data))
optimizer.step()
print("weight after step:{}".format(weight.data))
2
3
4
5
6
7
输出为:
weight before step:tensor([[0.6614, 0.2669],
[0.0617, 0.6213]])
weight after step:tensor([[-0.3386, -0.7331],
[-0.9383, -0.3787]])
2
3
4
add_param_group()
在 PyTorch 的
torch.optim.Optimizer
类中,add_param_group()
方法用于向优化器中添加一个参数组。这允许你为模型的不同部分设置不同的学习率或其他优化参数。这在训练复杂的神经网络时非常有用,特别是当你希望某些层的学习率高于其他层时。
add_param_group()
方法接受一个字典作为参数,该字典描述了参数组的配置。下面是一些常见的键及其说明:
params (iterable)
: 一个可迭代的参数列表或字典,指定了需要优化的参数。通常,你可以直接传入model.parameters()
来获取模型的所有参数,或者通过某些条件筛选特定的参数。weight_decay (float, optional)
: 权重衰减系数,用于正则化。它通过将权重乘以一个小于 1 的因子来实现,通常设置为类似 0.001 的值。如果未指定,则默认为优化器级别的weight_decay
。lr (float, optional)
: 学习率。它控制参数更新的步长。如果未指定,则默认为优化器级别的学习率。momentum (float, optional)
: 动量系数,仅对使用动量的优化器(如 SGD)有效。它用于加速 SGD 在相关方向上前进,并抑制震荡。如果未指定,则默认为优化器级别的动量。dampening (float, optional)
: 阻尼系数,用于调节动量的影响,防止动量过大导致的不稳定。如果未指定,则默认为优化器级别的阻尼。nesterov (bool, optional)
: 是否使用 Nesterov 动量。当设置为True
时,使用 Nesterov 加速梯度(NAG)算法,它是对标准动量方法的一种改进。如果未指定,则默认为优化器级别的 Nesterov 设置。
使用示例
import torch
import torch.optim as optim
# 假设我们有一个简单的模型
model = torch.nn.Sequential(
torch.nn.Linear(10, 20),
torch.nn.ReLU(),
torch.nn.Linear(20, 1)
)
# 创建优化器,并添加默认的参数组
optimizer = optim.SGD(model.parameters(), lr=0.01)
# 为模型的第一层设置不同的学习率
param_group = {'params': model[0].parameters(), 'lr': 0.02}
optimizer.add_param_group(param_group)
# 现在,模型的第一层将使用0.02的学习率,而其余层将使用默认的0.01学习率
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
torch.optim
PyTorch 提供了一个优化器的库torch.optim
:
- torch.optim.SGD
- torch.optim.ASGD
- torch.optim.Adadelta
- torch.optim.Adagrad
- torch.optim.Adam
- torch.optim.AdamW
- torch.optim.Adamax
- torch.optim.RAdam
- torch.optim.NAdam
- torch.optim.SparseAdam
- torch.optim.LBFGS
- torch.optim.RMSprop
- torch.optim.Rprop
torch.optim.lr_scheduler
torch.optim.lr_scheduler
是 PyTorch 中用于调整学习率(learning rate)的模块。在训练神经网络时,学习率是一个非常重要的超参数,它决定了模型参数在每次更新时的步长大小。学习率调度器(scheduler)可以根据训练过程中的某些指标(如验证损失或训练轮次)动态地调整学习率,以优化训练过程。学习率调度应该在优化器更新后应用。
torch.optim.lr_scheduler
提供了多种学习率调度器,每种调度器都有其特定的参数。以下是一些常用调度器的参数说明:
StepLR: 每训练一定数量的轮次(epochs),学习率按指定的因子衰减。
optimizer
(Optimizer): 优化器。step_size
(int): 学习率衰减的周期,即每隔多少个 epochs 衰减一次。gamma
(float, optional): 学习率衰减的因子,默认为 0.1。last_epoch
(int, optional): 上一个 epoch 的索引,默认为 -1。
ExponentialLR: 学习率按指数衰减。
optimizer
(Optimizer): 优化器。gamma
(float): 衰减因子。last_epoch
(int, optional): 上一个 epoch 的索引,默认为 -1。
CosineAnnealingLR: 学习率按余弦退火策略调整。
optimizer
(Optimizer): 优化器。T_max
(int): 余弦退火周期的一半,即一个完整余弦周期包含的 epochs 数量。eta_min
(float, optional): 学习率的最小值,默认为 0。last_epoch
(int, optional): 上一个 epoch 的索引,默认为 -1。
ReduceLROnPlateau: 当某个指标(如验证损失)停止改进时,减少学习率。
optimizer
(Optimizer): 优化器。mode
(str): 指标改进的模式,可选 'min' 或 'max'。factor
(float): 学习率减少的因子。patience
(int): 等待多少个 epochs 无改进后才减少学习率。verbose
(bool): 是否打印信息,默认为 False。threshold
(float): 测量新的最佳值的阈值,仅用于 'min' 模式。threshold_mode
(str): 相对或绝对阈值模式。cooldown
(int): 学习率减少后的冷却周期,即多少个 epochs 不调整学习率。min_lr
(float or list): 学习率的最小值。eps
(float): 提高数值稳定性的小值。
CyclicLR: 学习率在给定范围内循环变化。
optimizer
(Optimizer): 优化器。base_lr
(float or list): 初始学习率。max_lr
(float or list): 最大学习率。step_size_up
(int): 学习率从base_lr
增加到max_lr
的周期长度。step_size_down
(int): 学习率从max_lr
减少到base_lr
的周期长度。mode
(str): 循环的模式,可以是 'triangular', 'triangular2' 或 'exp_range'。gamma
(float): 用于 'exp_range' 模式的乘数因子。scale_fn
(function): 自定义的缩放函数。scale_mode
(str): 缩放模式,可以是 'cycle' 或 'iterations'。cycle_momentum
(bool): 是否也循环动量。base_momentum
(float or list): 动量的初始值。max_momentum
(float or list): 动量的最大值。last_epoch
(int): 上一个 epoch 的索引。
LambdaLR: 根据一个 lambda 函数来调整学习率。
optimizer
(Optimizer): 优化器。lr_lambda
(function or list): 用于计算学习率的 lambda 函数或函数列表。last_epoch
(int): 上一个 epoch 的索引。
这只是 torch.optim.lr_scheduler
中提供的一些常见调度器及其参数。具体的用法和参数可能因不同的调度器而有所差异。
实例
import os
import torch
# 设置权重,服从正态分布 --> 2 x 2
weight = torch.randn((2, 2), requires_grad=True)
# 设置梯度为全1矩阵 --> 2 x 2
weight.grad = torch.ones((2, 2))
# 输出现有的weight和data
print("The data of weight before step:\n{}".format(weight.data))
print("The grad of weight before step:\n{}".format(weight.grad))
# 实例化优化器
optimizer = torch.optim.SGD([weight], lr=0.1, momentum=0.9)
# 进行一步操作
optimizer.step()
# 查看进行一步后的值,梯度
print("The data of weight after step:\n{}".format(weight.data))
print("The grad of weight after step:\n{}".format(weight.grad))
# 权重清零
optimizer.zero_grad()
# 检验权重是否为0
print("The grad of weight after optimizer.zero_grad():\n{}".format(weight.grad))
# 输出参数
print("optimizer.params_group is \n{}".format(optimizer.param_groups))
# 查看参数位置,optimizer和weight的位置一样,我觉得这里可以参考Python是基于值管理
print("weight in optimizer:{}\nweight in weight:{}\n".format(id(optimizer.param_groups[0]['params'][0]), id(weight)))
# 添加参数:weight2
weight2 = torch.randn((3, 3), requires_grad=True)
optimizer.add_param_group({"params": weight2, 'lr': 0.0001, 'nesterov': True})
# 查看现有的参数
print("optimizer.param_groups is\n{}".format(optimizer.param_groups))
# 查看当前状态信息
opt_state_dict = optimizer.state_dict()
print("state_dict before step:\n", opt_state_dict)
# 进行5次step操作
for _ in range(50):
optimizer.step()
# 输出现有状态信息
print("state_dict after step:\n", optimizer.state_dict())
# 保存参数信息
torch.save(optimizer.state_dict(),os.path.join(r"D:\pythonProject\Attention_Unet", "optimizer_state_dict.pkl"))
print("----------done-----------")
# 加载参数信息
state_dict = torch.load(r"D:\pythonProject\Attention_Unet\optimizer_state_dict.pkl") # 需要修改为你自己的路径
optimizer.load_state_dict(state_dict)
print("load state_dict successfully\n{}".format(state_dict))
# 输出最后属性信息
print("\n{}".format(optimizer.defaults))
print("\n{}".format(optimizer.state))
print("\n{}".format(optimizer.param_groups))
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
输出结果
# 进行更新前的数据,梯度
The data of weight before step:
tensor([[-0.3077, -0.1808],
[-0.7462, -1.5556]])
The grad of weight before step:
tensor([[1., 1.],
[1., 1.]])
# 进行更新后的数据,梯度
The data of weight after step:
tensor([[-0.4077, -0.2808],
[-0.8462, -1.6556]])
The grad of weight after step:
tensor([[1., 1.],
[1., 1.]])
# 进行梯度清零的梯度
The grad of weight after optimizer.zero_grad():
tensor([[0., 0.],
[0., 0.]])
# 输出信息
optimizer.params_group is
[{'params': [tensor([[-0.4077, -0.2808],
[-0.8462, -1.6556]], requires_grad=True)], 'lr': 0.1, 'momentum': 0.9, 'dampening': 0, 'weight_decay': 0, 'nesterov': False}]
# 证明了优化器的和weight的储存是在一个地方,Python基于值管理
weight in optimizer:1841923407424
weight in weight:1841923407424
# 输出参数
optimizer.param_groups is
[{'params': [tensor([[-0.4077, -0.2808],
[-0.8462, -1.6556]], requires_grad=True)], 'lr': 0.1, 'momentum': 0.9, 'dampening': 0, 'weight_decay': 0, 'nesterov': False}, {'params': [tensor([[ 0.4539, -2.1901, -0.6662],
[ 0.6630, -1.5178, -0.8708],
[-2.0222, 1.4573, 0.8657]], requires_grad=True)], 'lr': 0.0001, 'nesterov': True, 'momentum': 0.9, 'dampening': 0, 'weight_decay': 0}]
# 进行更新前的参数查看,用state_dict
state_dict before step:
{'state': {0: {'momentum_buffer': tensor([[1., 1.],
[1., 1.]])}}, 'param_groups': [{'lr': 0.1, 'momentum': 0.9, 'dampening': 0, 'weight_decay': 0, 'nesterov': False, 'params': [0]}, {'lr': 0.0001, 'nesterov': True, 'momentum': 0.9, 'dampening': 0, 'weight_decay': 0, 'params': [1]}]}
# 进行更新后的参数查看,用state_dict
state_dict after step:
{'state': {0: {'momentum_buffer': tensor([[0.0052, 0.0052],
[0.0052, 0.0052]])}}, 'param_groups': [{'lr': 0.1, 'momentum': 0.9, 'dampening': 0, 'weight_decay': 0, 'nesterov': False, 'params': [0]}, {'lr': 0.0001, 'nesterov': True, 'momentum': 0.9, 'dampening': 0, 'weight_decay': 0, 'params': [1]}]}
# 存储信息完毕
----------done-----------
# 加载参数信息成功
load state_dict successfully
# 加载参数信息
{'state': {0: {'momentum_buffer': tensor([[0.0052, 0.0052],
[0.0052, 0.0052]])}}, 'param_groups': [{'lr': 0.1, 'momentum': 0.9, 'dampening': 0, 'weight_decay': 0, 'nesterov': False, 'params': [0]}, {'lr': 0.0001, 'nesterov': True, 'momentum': 0.9, 'dampening': 0, 'weight_decay': 0, 'params': [1]}]}
# defaults的属性输出
{'lr': 0.1, 'momentum': 0.9, 'dampening': 0, 'weight_decay': 0, 'nesterov': False}
# state属性输出
defaultdict(<class 'dict'>, {tensor([[-1.3031, -1.1761],
[-1.7415, -2.5510]], requires_grad=True): {'momentum_buffer': tensor([[0.0052, 0.0052],
[0.0052, 0.0052]])}})
# param_groups属性输出
[{'lr': 0.1, 'momentum': 0.9, 'dampening': 0, 'weight_decay': 0, 'nesterov': False, 'params': [tensor([[-1.3031, -1.1761],
[-1.7415, -2.5510]], requires_grad=True)]}, {'lr': 0.0001, 'nesterov': True, 'momentum': 0.9, 'dampening': 0, 'weight_decay': 0, 'params': [tensor([[ 0.4539, -2.1901, -0.6662],
[ 0.6630, -1.5178, -0.8708],
[-2.0222, 1.4573, 0.8657]], requires_grad=True)]}]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65