在本教程中,我们将更详细地介绍 timm 库中的 TanhLRScheduler 及其所有支持的超参数。

Tanh

from timm.scheduler.tanh_lr import TanhLRScheduler
from nbdev.showdoc import show_doc

class TanhLRScheduler[源代码]

TanhLRScheduler(optimizer:Optimizer, t_initial:int, lb:float=-7.0, ub:float=3.0, lr_min:float=0.0, cycle_mul:float=1.0, cycle_decay:float=1.0, cycle_limit:int=1, warmup_t=0, warmup_lr_init=0, warmup_prefix=False, t_in_epochs=True, noise_range_t=None, noise_pct=0.67, noise_std=1.0, noise_seed=42, initialize=True) :: 调度器

带重启的双曲正切衰减。这在论文 https://arxiv.org/abs/1806.01593 中有所描述。

如上所示,TanhLRScheduler 接受一个 optimizer 以及一些我们将在下面详细介绍的超参数。我们将首先了解如何使用 timm 训练脚本来训练使用 TanhLRScheduler 的模型,然后看看如何将此调度器用作自定义训练脚本的独立调度器。

使用 cosine 调度器配合 timm 训练脚本

要使用 TanhLRScheduler 训练模型,我们只需通过传入 --sched tanh 参数以及必要的超参数来更新训练脚本参数。在下一节中,我们还将介绍每个超参数如何更新 TanhLRScheduler

使用 TanhLRScheduler 的训练命令如下所示

python train.py ../imagenette2-320/ --sched tanh

参数

现在让我们看看相关的超参数以及它们如何更新退火调度。

optimizer

这是将用于训练过程的 optimizer

from timm import create_model 
from timm.optim import create_optimizer
from types import SimpleNamespace
model = create_model('resnet34')

args = SimpleNamespace()
args.weight_decay = 0
args.lr = 1e-4
args.opt = 'adam' 
args.momentum = 0.9

optimizer = create_optimizer(args, model)

使用 create_optimizer 创建的这个 optimizer 对象将作为 optimizer 参数传递。

t_initial

调度超参数的 epoch 数。

lb

论文中用 L 表示的下界。

ub

论文中用 U 表示的上界。

注意:LU 表示函数 tanh(x) 在区间 [L, U] 上的下界和上界。

t_mul

默认为 1.0。与 SGDR 类似,它通过增加退火时间来更新调度。

from matplotlib import pyplot as plt

def get_lr_per_epoch(scheduler, num_epoch):
    lr_per_epoch = []
    for epoch in range(num_epoch):
        lr_per_epoch.append(scheduler.get_epoch_values(epoch))
    return lr_per_epoch

t_mul=1.

num_epoch = 50

scheduler = TanhLRScheduler(optimizer, t_initial=num_epoch, t_mul=1.)
lr_per_epoch = get_lr_per_epoch(scheduler, num_epoch*3)
plt.plot([i for i in range(num_epoch*3)], lr_per_epoch, label="With `t_mul=1.`");

t_mul=2.

scheduler = TanhLRScheduler(optimizer, t_initial=num_epoch, t_mul=2.)
lr_per_epoch = get_lr_per_epoch(scheduler, num_epoch*3)
plt.plot([i for i in range(num_epoch*3)], lr_per_epoch, label="With `t_mul=1.`");

如示例所示,从 epoch 50 开始的第二个调度需要两倍的时间才能将学习率降低到最小值。

lr_min

默认为 1e-5。调度过程中使用的最小学习率。学习率永远不会低于此值。

decay_rate

decay_rate > 0 且 < 1 时,每次重启时,学习率将按新学习率衰减,新学习率等于 lr * decay_rate。因此,如果 decay_rate=0.5,则新学习率将是初始 lr 的一半。

decay_rate=1. 或无衰减

num_epoch = 50
scheduler = TanhLRScheduler(optimizer, t_initial=num_epoch, decay_rate=1.)
lr_per_epoch = get_lr_per_epoch(scheduler, num_epoch*2)

plt.plot([i for i in range(num_epoch*2)], lr_per_epoch);

decay_rate=0.5

num_epoch = 50
scheduler = TanhLRScheduler(optimizer, t_initial=num_epoch, decay_rate=0.5)
lr_per_epoch = get_lr_per_epoch(scheduler, num_epoch*2)

plt.plot([i for i in range(num_epoch*2)], lr_per_epoch);

warmup_t

定义预热 epoch 的数量。

warmup_lr_init

预热期间的初始学习率。

num_epoch = 50
scheduler = TanhLRScheduler(optimizer, t_initial=num_epoch, warmup_t=5, warmup_lr_init=1e-5)
lr_per_epoch = get_lr_per_epoch(scheduler, num_epoch)
plt.plot([i for i in range(num_epoch)], lr_per_epoch, label="With warmup");

num_epoch = 50
scheduler = TanhLRScheduler(optimizer, t_initial=num_epoch)
lr_per_epoch = get_lr_per_epoch(scheduler, num_epoch)
plt.plot([i for i in range(num_epoch)], lr_per_epoch, label="Without warmup", alpha=0.8);

plt.legend();

如我们所见,通过设置 warmup_twarmup_lr_initcosine 调度器首先以 warmup_lr_init 的值开始,然后逐渐增加到优化器中设置的 initial_lr (即 1e-4)。从 warmup_lr_initinitial_lr 需要 warmup_t 个 epoch。

warmup_prefix

默认为 False。如果设置为 True,则每个新的 epoch 号等于 epoch = epoch - warmup_t

num_epoch = 50
scheduler = TanhLRScheduler(optimizer, t_initial=num_epoch, warmup_t=5, warmup_lr_init=1e-5)
lr_per_epoch = get_lr_per_epoch(scheduler, num_epoch)
plt.plot([i for i in range(num_epoch)], lr_per_epoch, label="Without warmup_prefix");

num_epoch = 50
scheduler = TanhLRScheduler(optimizer, t_initial=num_epoch, warmup_t=5, warmup_lr_init=1e-5, warmup_prefix=True)
lr_per_epoch = get_lr_per_epoch(scheduler, num_epoch)
plt.plot([i for i in range(num_epoch)], lr_per_epoch, label="With warmup_prefix");

plt.legend();

在上面的示例中,我们可以看到 warmup_prefix 如何更新学习率退火调度。

cycle_limit

TanhLRScheduler 中最大重启次数。

cycle_limit=1

num_epoch = 50
scheduler = TanhLRScheduler(optimizer, t_initial=num_epoch, cycle_limit=1)
lr_per_epoch = get_lr_per_epoch(scheduler, num_epoch*2)

plt.plot([i for i in range(num_epoch*2)], lr_per_epoch);

cycle_limit=2

num_epoch = 50
scheduler = TanhLRScheduler(optimizer, t_initial=num_epoch, cycle_limit=2)
lr_per_epoch = get_lr_per_epoch(scheduler, num_epoch*2)

plt.plot([i for i in range(num_epoch*2)], lr_per_epoch);

t_in_epochs

如果设置为 False,则针对 epoch t 返回的学习率为 None

num_epoch = 50
scheduler = TanhLRScheduler(optimizer, t_initial=num_epoch, t_in_epochs=False)
lr_per_epoch = get_lr_per_epoch(scheduler, num_epoch)

lr_per_epoch[:5]
[None, None, None, None, None]

noise_range_t

为学习率调度器添加噪声。

noise_pct

要添加的噪声量。默认为 0.67。

noise_std

噪声标准差。默认为 1.0。

noise_seed

要使用的噪声种子。默认为 42。

initialize

如果设置为 True,则将属性 initial_lr 设置给每个参数组。默认为 True