torch.optim¶
已经支持最常用的方法,并且界面是通用的 足够了,因此更复杂的也可以轻松集成到 前途。
如何使用优化器¶
要使用,您必须构造一个 optimizer 对象,该对象将包含
当前状态,并将根据计算的梯度更新参数。
构建¶
要构造一个,你必须给它一个包含
parameters (all should be s) 或 named parameters
((str, )) 的元组进行优化。然后
您可以指定特定于优化器的选项,例如 Learning Rate(学习率)、权重衰减等。
Parameter
Parameter
例:
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
optimizer = optim.Adam([var1, var2], lr=0.0001)
命名参数示例:
optimizer = optim.SGD(model.named_parameters(), lr=0.01, momentum=0.9)
optimizer = optim.Adam([('layer0', var1), ('layer1', var2)], lr=0.0001)
每个参数选项¶
还支持指定每个参数的选项。要执行此操作,请改为
传递 s 的 iterable,传入 s 的
iterable。它们中的每一个都将定义一个单独的参数组,并且应该包含
一个键,其中包含属于它的参数列表。其他键
应与优化器接受的关键字参数匹配,并将使用
作为此组的优化选项。
Variable
params
例如,当想要指定每层学习率时,这非常有用:
optim.SGD([
{'params': model.base.parameters(), 'lr': 1e-2},
{'params': model.classifier.parameters()}
], lr=1e-3, momentum=0.9)
optim.SGD([
{'params': model.base.named_parameters(), 'lr': 1e-2},
{'params': model.classifier.named_parameters()}
], lr=1e-3, momentum=0.9)
这意味着 的参数将使用 的学习率 ,而 的 参数将坚持默认的学习率 。
最后,动量 将用于所有参数。model.base
1e-2
model.classifier
1e-3
0.9
注意
你仍然可以将选项作为关键字参数传递。它们将用作 defaults,位于未覆盖它们的组中。这在以下情况下非常有用 只想改变一个选项,同时保持所有其他选项一致 参数组之间。
另请考虑以下与参数的不同惩罚相关的示例。
请记住,这会返回一个 iterable ,
包含所有可学习的参数,包括 biases 和其他
可能更喜欢不同惩罚的参数。要解决此问题,可以指定
每个参数组的单独惩罚权重:
bias_params = [p for name, p in self.named_parameters() if 'bias' in name]
others = [p for name, p in self.named_parameters() if 'bias' not in name]
optim.SGD([
{'params': others},
{'params': bias_params, 'weight_decay': 0}
], weight_decay=1e-2, lr=1e-2)
以这种方式,偏差项与非偏差项隔离开来,并且 of 是专门为偏差项设置的,以避免对
这个组。weight_decay
0
采取优化步骤¶
所有优化器都实现了一个方法,该方法更新
参数。它可以通过两种方式使用:
optimizer.step()
¶
这是大多数优化器支持的简化版本。该函数可以是
在使用 例如 .backward()
例:
for input, target in dataset:
optimizer.zero_grad()
output = model(input)
loss = loss_fn(output, target)
loss.backward()
optimizer.step()
optimizer.step(closure)
¶
一些优化算法(如 Conjugate Gradient 和 LBFGS)需要 多次重新评估函数,因此您必须传入一个闭包,该闭包将 允许他们重新计算您的模型。闭包应清除梯度, 计算损失,然后返回。
例:
for input, target in dataset:
def closure():
optimizer.zero_grad()
output = model(input)
loss = loss_fn(output, target)
loss.backward()
return loss
optimizer.step(closure)
基类¶
- 类 torch.optim 中。优化器(params, defaults)[源][源]¶
所有优化器的基类。
警告
需要将参数指定为具有确定性 排序,在运行之间保持一致。不这样做的对象示例 满足这些属性的是 dictionaries 值的 set 和 iterators。
加载优化器状态。 |
|
执行单个优化步骤以更新参数。 |
|
注册一个 optimizer step pre hook,它将在 optimizer step 之前调用。 |
|
注册一个 optimizer step post 钩子,该钩子将在 optimizer step 之后调用。 |
|
算法¶
实现 Adadelta 算法。 |
|
实现 Adafactor 算法。 |
|
实现 Adagrad 算法。 |
|
实现 Adam 算法。 |
|
实现 AdamW 算法。 |
|
SparseAdam 实现了适用于稀疏梯度的 Adam 算法的掩码版本。 |
|
实现 Adamax 算法(基于无穷范数的 Adam 变体)。 |
|
实现平均随机梯度下降。 |
|
实现 L-BFGS 算法。 |
|
实现 NAdam 算法。 |
|
实现 RAdam 算法。 |
|
实现 RMSprop 算法。 |
|
实现弹性反向传播算法。 |
|
实现随机梯度下降(可选使用动量)。 |
我们的许多算法都有针对性能进行优化的各种实现, 可读性和/或通用性,因此我们尝试默认为通常最快的 implementation (如果尚未实现特定 由用户指定。
我们有 3 大类实现:for-loop、foreach(多张量)和 融合。最直接的实现是对参数进行 for 循环,其中 大块计算。For 循环通常比我们的 foreach 慢 实现,将参数组合成一个多张量并运行大块 的 County,从而节省了许多顺序的内核调用。我们的一些 优化器具有更快的融合实现,它融合了 计算到一个内核中。我们可以将 foreach 实现视为 fusing 水平融合实现,并在其上垂直融合。
通常,这 3 种实现的性能顺序> foreach > for 循环融合在一起。 因此,在适用时,我们默认在 for 循环中使用 foreach。Applicable 是指 foreach implementation is available,则用户尚未指定任何特定于 implementation 的 kwargs (例如,fused、foreach、differentiable),并且所有张量都是原生的。请注意,当 应该比 foreach 更快,实现更新,我们想给出 他们在到处拨动开关之前有更多的烘烤时间。我们总结了稳定性状态 对于下面第二个表中的每种实现,欢迎您尝试一下!
下表显示了每种算法的可用和默认实现:
算法 |
违约 |
有 foreach? |
已融合? |
---|---|---|---|
foreach |
是的 |
不 |
|
for 循环 |
不 |
不 |
|
foreach |
是的 |
是(仅限 CPU) |
|
foreach |
是的 |
是的 |
|
foreach |
是的 |
是的 |
|
for 循环 |
不 |
不 |
|
foreach |
是的 |
不 |
|
foreach |
是的 |
不 |
|
for 循环 |
不 |
不 |
|
foreach |
是的 |
不 |
|
foreach |
是的 |
不 |
|
foreach |
是的 |
不 |
|
foreach |
是的 |
不 |
|
foreach |
是的 |
是的 |
下表显示了 fused implementations 的稳定性状态:
算法 |
中央处理器 |
CUDA 的 |
议员 |
---|---|---|---|
支持 |
支持 |
支持 |
|
支持 |
支持 |
支持 |
|
试用版 |
支持 |
支持 |
|
试用版 |
稳定 |
试用版 |
|
试用版 |
稳定 |
试用版 |
|
支持 |
支持 |
支持 |
|
支持 |
支持 |
支持 |
|
支持 |
支持 |
支持 |
|
支持 |
支持 |
支持 |
|
支持 |
支持 |
支持 |
|
支持 |
支持 |
支持 |
|
支持 |
支持 |
支持 |
|
支持 |
支持 |
支持 |
|
试用版 |
试用版 |
试用版 |
如何调整学习率¶
提供了几种调整学习的方法
rate 基于 epoch 的数量。
允许根据某些验证测量结果动态降低学习率。
学习率调度应在 optimizer 更新后应用;例如,您 应该这样编写你的代码:
例:
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
scheduler = ExponentialLR(optimizer, gamma=0.9)
for epoch in range(20):
for input, target in dataset:
optimizer.zero_grad()
output = model(input)
loss = loss_fn(output, target)
loss.backward()
optimizer.step()
scheduler.step()
大多数学习率调度器都可以背靠背调用(也称为 链接调度器)。结果是,每个调度程序都在 other 的 Adobe,则根据前一个 Bean 获得的学习率。
例:
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
scheduler1 = ExponentialLR(optimizer, gamma=0.9)
scheduler2 = MultiStepLR(optimizer, milestones=[30,80], gamma=0.1)
for epoch in range(20):
for input, target in dataset:
optimizer.zero_grad()
output = model(input)
loss = loss_fn(output, target)
loss.backward()
optimizer.step()
scheduler1.step()
scheduler2.step()
在文档的许多地方,我们将使用以下模板来引用 scheduler 算法。
>>> scheduler = ...
>>> for epoch in range(100):
>>> train(...)
>>> validate(...)
>>> scheduler.step()
警告
在 PyTorch 1.1.0 之前,学习率调度器应该在之前被调用
优化器的更新;1.1.0 以突破性的方式改变了这种行为。如果您使用
优化器更新之前的学习率调度器(调用 )
(调用 ),这将跳过学习率计划的第一个值。
如果升级到 PyTorch 1.1.0 后无法重现结果,请检查
如果您在错误的时间打电话。scheduler.step()
optimizer.step()
scheduler.step()
调整优化期间的学习率。 |
|
设置初始学习率。 |
|
将每个参数组的学习率乘以指定函数中给定的因子。 |
|
每 step_size 个 epoch 每 gamma 衰减每个参数组的学习率。 |
|
一旦纪元数达到其中一个里程碑,则按 gamma 衰减每个参数组的学习率。 |
|
将每个参数组的学习率乘以一个小的常数因子。 |
|
通过线性改变小的乘法因子来衰减每个参数组的学习率。 |
|
每个 epoch 按 gamma 衰减每个参数组的学习率。 |
|
在给定total_iters中使用多项式函数衰减每个参数组的学习率。 |
|
使用余弦退火计划设置每个参数组的学习率。 |
|
链接学习率计划程序列表。 |
|
包含预期在优化过程中按顺序调用的计划程序列表。 |
|
当指标停止改进时降低学习率。 |
|
根据循环学习率策略 (CLR) 设置每个参数组的学习率。 |
|
根据 1cycle 学习率策略设置每个参数组的学习率。 |
|
使用余弦退火计划设置每个参数组的学习率。 |
如何使用命名参数加载优化器 state dict¶
该函数存储
loaded state dict (如果存在)。但是,加载优化器状态的过程不受影响,
因为参数的顺序对于保持兼容性很重要(在顺序不同的情况下)。
要使用 loaded state dict 中加载的参数名称,需要根据所需的行为实现自定义。
param_names
register_load_state_dict_pre_hook
这可能很有用,例如,当模型架构发生变化时,但权重和优化器状态需要 保持不变。以下示例演示如何实现此自定义。
例:
class OneLayerModel(nn.Module):
def __init__(self):
super().__init__()
self.fc = nn.Linear(3, 4)
def forward(self, x):
return self.fc(x)
model = OneLayerModel()
optimizer = optim.SGD(model.named_parameters(), lr=0.01, momentum=0.9)
# training..
torch.save(optimizer.state_dict(), PATH)
假设它实现了一个专家 (MoE),我们想要复制它并继续训练
对于两个 EA,它们的初始化方式与 Layer 相同。对于以下内容,我们通过将模型权重和优化器状态加载到 both 和 of 中来创建两个相同的层并恢复训练(并相应地调整它们):model
fc
model2
fc
model
fc1
fc2
model2
class TwoLayerModel(nn.Module):
def __init__(self):
super().__init__()
self.fc1 = nn.Linear(3, 4)
self.fc2 = nn.Linear(3, 4)
def forward(self, x):
return (self.fc1(x) + self.fc2(x)) / 2
model2 = TwoLayerModel()
# adapt and load model weights..
optimizer2 = optim.SGD(model2.named_parameters(), lr=0.01, momentum=0.9)
使用前一个优化器的状态 dict 加载 state dict for ,以便 和 都将使用优化器状态的副本进行初始化
(要从 开始恢复每一层的训练),我们可以使用以下钩子:optimizer2
fc1
fc2
fc
fc
def adapt_state_dict_ids(optimizer, state_dict):
adapted_state_dict = deepcopy(optimizer.state_dict())
# Copy setup parameters (lr, weight_decay, etc.), in case they differ in the loaded state dict.
for k, v in state_dict['param_groups'][0].items():
if k not in ['params', 'param_names']:
adapted_state_dict['param_groups'][0][k] = v
lookup_dict = {
'fc1.weight': 'fc.weight',
'fc1.bias': 'fc.bias',
'fc2.weight': 'fc.weight',
'fc2.bias': 'fc.bias'
}
clone_deepcopy = lambda d: {k: (v.clone() if isinstance(v, torch.Tensor) else deepcopy(v)) for k, v in d.items()}
for param_id, param_name in zip(
optimizer.state_dict()['param_groups'][0]['params'],
optimizer.state_dict()['param_groups'][0]['param_names']):
name_in_loaded = lookup_dict[param_name]
index_in_loaded_list = state_dict['param_groups'][0]['param_names'].index(name_in_loaded)
id_in_loaded = state_dict['param_groups'][0]['params'][index_in_loaded_list]
# Copy the state of the corresponding parameter
if id_in_loaded in state_dict['state']:
adapted_state_dict['state'][param_id] = clone_deepcopy(state_dict['state'][id_in_loaded])
return adapted_state_dict
optimizer2.register_load_state_dict_pre_hook(adapt_state_dict_ids)
optimizer2.load_state_dict(torch.load(PATH)) # The previous optimizer saved state_dict
这可确保使用具有正确 层状态的 adapt state_dict
在模型加载期间。
请注意,此代码是专门为此示例设计的(例如,假设单个参数组),
而其他情况可能需要不同的调整。model2
以下示例说明在模型结构更改时如何处理 loaded 中缺少的参数。
这会添加一个新图层,该图层在原始图层中不存在。
为了恢复训练,使用自定义钩子来调整优化器的 ,
确保现有参数正确映射,而缺少的参数(如旁路层)保持不变
(如本例中初始化的)。
这种方法可以在模型更改的情况下平稳加载和恢复优化器状态。
新的旁路层将从头开始训练:state dict
Model_bypass
bypass
Model1
adapt_state_dict_missing_param
state_dict
class Model1(nn.Module):
def __init__(self):
super().__init__()
self.fc = nn.Linear(5, 5)
def forward(self, x):
return self.fc(x) + x
model = Model1()
optimizer = optim.SGD(model.named_parameters(), lr=0.01, momentum=0.9)
# training..
torch.save(optimizer.state_dict(), PATH)
class Model_bypass(nn.Module):
def __init__(self):
super().__init__()
self.fc = nn.Linear(5, 5)
self.bypass = nn.Linear(5, 5, bias=False)
torch.nn.init.eye_(self.bypass.weight)
def forward(self, x):
return self.fc(x) + self.bypass(x)
model2 = Model_bypass()
optimizer2 = optim.SGD(model2.named_parameters(), lr=0.01, momentum=0.9)
def adapt_state_dict_missing_param(optimizer, state_dict):
adapted_state_dict = deepcopy(optimizer.state_dict())
# Copy setup parameters (lr, weight_decay, etc.), in case they differ in the loaded state dict.
for k, v in state_dict['param_groups'][0].items():
if k not in ['params', 'param_names']:
adapted_state_dict['param_groups'][0][k] = v
lookup_dict = {
'fc.weight': 'fc.weight',
'fc.bias': 'fc.bias',
'bypass.weight': None,
}
clone_deepcopy = lambda d: {k: (v.clone() if isinstance(v, torch.Tensor) else deepcopy(v)) for k, v in d.items()}
for param_id, param_name in zip(
optimizer.state_dict()['param_groups'][0]['params'],
optimizer.state_dict()['param_groups'][0]['param_names']):
name_in_loaded = lookup_dict[param_name]
if name_in_loaded in state_dict['param_groups'][0]['param_names']:
index_in_loaded_list = state_dict['param_groups'][0]['param_names'].index(name_in_loaded)
id_in_loaded = state_dict['param_groups'][0]['params'][index_in_loaded_list]
# Copy the state of the corresponding parameter
if id_in_loaded in state_dict['state']:
adapted_state_dict['state'][param_id] = clone_deepcopy(state_dict['state'][id_in_loaded])
return adapted_state_dict
optimizer2.register_load_state_dict_pre_hook(adapt_state_dict_ids)
optimizer2.load_state_dict(torch.load(PATH)) # The previous optimizer saved state_dict
作为第三个示例,不是根据参数的顺序加载状态(默认方法), 这个钩子可以根据参数的名称进行加载:
def names_matching(optimizer, state_dict):
assert len(state_dict['param_groups']) == len(optimizer.state_dict()['param_groups'])
adapted_state_dict = deepcopy(optimizer.state_dict())
for g_ind in range(len(state_dict['param_groups'])):
assert len(state_dict['param_groups'][g_ind]['params']) == len(
optimizer.state_dict()['param_groups'][g_ind]['params'])
for k, v in state_dict['param_groups'][g_ind].items():
if k not in ['params', 'param_names']:
adapted_state_dict['param_groups'][g_ind][k] = v
for param_id, param_name in zip(
optimizer.state_dict()['param_groups'][g_ind]['params'],
optimizer.state_dict()['param_groups'][g_ind]['param_names']):
index_in_loaded_list = state_dict['param_groups'][g_ind]['param_names'].index(param_name)
id_in_loaded = state_dict['param_groups'][g_ind]['params'][index_in_loaded_list]
# Copy the state of the corresponding parameter
if id_in_loaded in state_dict['state']:
adapted_state_dict['state'][param_id] = deepcopy(state_dict['state'][id_in_loaded])
return adapted_state_dict
权重平均(SWA 和 EMA)¶
实现随机权重平均 (SWA) 和指数移动平均线 (EMA),
实现 SWA 学习率调度器,
并且是用于更新 SWA/EMA 批处理的实用函数
训练结束时的标准化统计。
SWA 已在 平均权重导致更广泛的最优值和更好的泛化 中提出。
EMA 是一种广为人知的技术,它通过减少所需的权重更新次数来减少训练时间。它是 Polyak 平均法的变体,但在迭代中使用指数权重而不是相等的权重。
构建平均模型¶
AveragedModel 类用于计算 SWA 或 EMA 模型的权重。
您可以通过运行以下命令来创建 SWA 平均模型:
>>> averaged_model = AveragedModel(model)
EMA 模型是通过指定参数来构建的,如下所示:multi_avg_fn
>>> decay = 0.999
>>> averaged_model = AveragedModel(model, multi_avg_fn=get_ema_multi_avg_fn(decay))
Decay 是一个介于 0 和 1 之间的参数,用于控制平均参数的衰减速度。如果未提供给 ,则默认值为 0.999。Decay 值应接近 1.0,因为较小的值可能会导致优化收敛问题。
其中 alpha 是 EMA 衰减。
此处的模型可以是任意对象。 将跟踪 参数的运行平均值。更新这些
averages,你应该在 optimizer.step() 之后使用这个函数:
model
averaged_model
model
update_parameters()
>>> averaged_model.update_parameters(model)
对于 SWA 和 EMA,此调用通常在 optimizer 之后立即完成。对于 SWA,通常会在训练开始时跳过某些步骤。step()
自定义平均策略¶
默认情况下,计算
提供的参数,但您也可以将自定义平均函数与 OR 参数一起使用:
avg_fn
multi_avg_fn
avg_fn
允许定义一个对每个参数元组(平均参数、模型参数)进行操作的函数,并应返回新的平均参数。multi_avg_fn
允许同时定义对参数列表元组(平均参数列表、模型参数列表)的更有效操作,例如使用函数。此函数必须就地更新平均参数。torch._foreach*
在以下示例中,使用参数计算指数移动平均线:ema_model
avg_fn
>>> ema_avg = lambda averaged_model_parameter, model_parameter, num_averaged:\
>>> 0.9 * averaged_model_parameter + 0.1 * model_parameter
>>> ema_model = torch.optim.swa_utils.AveragedModel(model, avg_fn=ema_avg)
在以下示例中,使用 more efficient 参数计算指数移动平均线:ema_model
multi_avg_fn
>>> ema_model = AveragedModel(model, multi_avg_fn=get_ema_multi_avg_fn(0.9))
SWA 学习率计划¶
通常,在 SWA 中,学习率设置为较高的常量值。 是一个
学习率调度器,将学习率退火到一个固定值,然后保持它
不断。例如,下面的代码创建一个调度程序,该调度程序对
每个参数组内 5 个 epoch 的学习率从其初始值到 0.05:SWALR
>>> swa_scheduler = torch.optim.swa_utils.SWALR(optimizer, \
>>> anneal_strategy="linear", anneal_epochs=5, swa_lr=0.05)
您还可以通过设置 来将余弦退火使用到固定值而不是线性退火。anneal_strategy="cos"
处理批量归一化¶
update_bn()
是一个实用程序函数,允许计算 SWA 模型的 batchnorm 统计信息
在训练结束时,在给定的 DataLoader 上:loader
>>> torch.optim.swa_utils.update_bn(loader, swa_model)
update_bn()
将 the 应用于 DataLoader 中的每个元素并计算激活
模型中每个批量归一化层的统计信息。swa_model
警告
update_bn()
假设 DataLoader 中的每个批次都是一个张量或一个
张量,其中第一个元素是应应用网络的张量。
如果您的数据加载器具有不同的结构,您可以通过对数据集的每个元素执行前向传递来更新 的批量规范化统计信息。loader
swa_model
swa_model
swa_model
综上所述:SWA¶
在下面的示例中,是累积权重平均值的 SWA 模型。
我们训练模型总共 300 个 epoch,然后切换到 SWA 学习率计划
并开始收集 epoch 160 时参数的 SWA 平均值:swa_model
>>> loader, optimizer, model, loss_fn = ...
>>> swa_model = torch.optim.swa_utils.AveragedModel(model)
>>> scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=300)
>>> swa_start = 160
>>> swa_scheduler = SWALR(optimizer, swa_lr=0.05)
>>>
>>> for epoch in range(300):
>>> for input, target in loader:
>>> optimizer.zero_grad()
>>> loss_fn(model(input), target).backward()
>>> optimizer.step()
>>> if epoch > swa_start:
>>> swa_model.update_parameters(model)
>>> swa_scheduler.step()
>>> else:
>>> scheduler.step()
>>>
>>> # Update bn statistics for the swa_model at the end
>>> torch.optim.swa_utils.update_bn(loader, swa_model)
>>> # Use swa_model to make predictions on test data
>>> preds = swa_model(test_input)
综上所述:EMA¶
在下面的示例中,是 EMA 模型,该模型以 0.999 的衰减率累积权重的指数衰减平均值。
我们训练模型总共 300 个 epoch,并立即开始收集 EMA 平均值。ema_model
>>> loader, optimizer, model, loss_fn = ...
>>> ema_model = torch.optim.swa_utils.AveragedModel(model, \
>>> multi_avg_fn=torch.optim.swa_utils.get_ema_multi_avg_fn(0.999))
>>>
>>> for epoch in range(300):
>>> for input, target in loader:
>>> optimizer.zero_grad()
>>> loss_fn(model(input), target).backward()
>>> optimizer.step()
>>> ema_model.update_parameters(model)
>>>
>>> # Update bn statistics for the ema_model at the end
>>> torch.optim.swa_utils.update_bn(loader, ema_model)
>>> # Use ema_model to make predictions on test data
>>> preds = ema_model(test_input)
实施随机权重平均 (SWA) 和指数移动平均线 (EMA) 的平均模型。 |
|
将每个参数组中的学习率退火为固定值。 |
- torch.optim.swa_utils。update_bn(loader, model, device=None)[源][源]¶
更新 BatchNorm running_mean,running_var模型中的缓冲区。
它在加载器中执行一次数据传递以估计激活 模型中 BatchNorm 层的统计信息。
- 参数
loader (torch.utils.data.DataLoader) – 用于计算 激活统计信息 on。每个数据批次应为 tensor 或第一个元素为 Tensor 的列表/元组 包含数据。
model (torch.nn.Module) – 我们寻求更新 BatchNorm 的模型 统计学。
device (torch.device,可选) – 如果设置,则数据将在传递到 之前传输到。
device
model
例
>>> loader, model = ... >>> torch.optim.swa_utils.update_bn(loader, model)
注意
update_bn 实用程序假定每个数据批次都是一个张量或张量列表或元组;在后一种情况下,它 假定应该在第一个 元素。
loader
model.forward()