模型训练 warm-up, consine schedule, 以及 Automatic Mixed Precision

在模型训练过程中,模型预热(Warm-up)和余弦退火调度(Consine schedule)对于提高模型的训练效果和稳定性非常有帮助,它们可以优化学习率的变化,进而影响模型的学习过程。自动混合精度训练(Automatic Mixed Precision, AMP),这是一种优化训练过程的技术,尤其在使用 NVIDIA GPU 时。这种方法可以提高计算速度,同时减少所需的内存带宽,这是通过在训练神经网络时灵活使用单精度(float32)和半精度(float16)来实现的

预热(Warm-up)

预热期是训练开始的几个迭代或者几个周期,在这个阶段,学习率从一个相对较低的值逐渐增加到一个预定的较高值。预热的目的包括:

  • 防止模型早期发散:在神经网络训练初期,权重是随机初始化的,此时若直接使用较大的学习率,可能会导致权重更新过大,从而使得模型输出快速发散,尤其是在损失函数的梯度非常大的情况下。
  • 逐步适应训练:通过慢慢增加学习率,模型可以逐渐适应数据集的特点和复杂性,这有助于提高模型训练的稳定性和效率。
    提高训练效果:在一些情况下,预热可以帮助模型达到更好的最终性能,尤其是在使用大型模型和复杂数据集时。
    if warmup_epochs > 0:
        warmup_schedule = np.linspace(0, lr, warmup_epochs)

余弦退火调度(Consine schedule)

余弦退火是一种调整学习率的方法,它模拟了退火过程中的冷却曲线,即学习率随训练进程先减小后平稳。具体来说:

  • 避免局部最小:通过模拟“退火”过程,余弦退火允许学习率初期较高,以跳出可能的局部最小值,然后逐渐降低学习率以细化到全局最小或更好的局部最小。
  • 优化收敛:随着训练的进行,模型的参数逐渐接近最优,此时需要较小的学习率来进行微调,避免过大的参数更新导致“越过”最佳解。
  • 控制结束阶段的学习率:通过模拟余弦曲线,学习率在训练的最后阶段可以非常平稳,有助于模型在最优解附近稳定下来。

在实际使用中,可以使用pytorch框架内置函数:

import torch
import torch.optim as optim
import torch.nn as nn
from torch.optim.lr_scheduler import CosineAnnealingLR

optimizer = optim.Adam(model.parameters(), lr=0.01)  # 初始学习率
# 设置调度器
scheduler = CosineAnnealingLR(optimizer, T_max=10, eta_min=0.001)  # T_max 是周期,eta_min 是最小学习率

data_loader = [torch.randn(10, 10) for _ in range(100)] 
# 训练循环
for epoch in range(10):
    for inputs in data_loader:
        optimizer.zero_grad()
        outputs = model(inputs)
        loss = outputs.sum()  # 使用简单的求和作为损失函数示例
        loss.backward()
        optimizer.step()
    scheduler.step()  # 更新学习率

    print(f"Epoch {epoch+1}: Current learning rate = {scheduler.get_last_lr()[0]}")

也可以自定义consine scheduler

η t = η min ⁡ + 1 2 ( η max ⁡ − η min ⁡ ) ( 1 + cos ⁡ ( t π T ) ) \eta_{t}=\eta_{\min }+\frac{1}{2}\left(\eta_{\max }-\eta_{\min }\right)\left(1+\cos \left(\frac{t \pi}{T}\right)\right) ηt=ηmin+21(ηmaxηmin)(1+cos(Ttπ))

def consine_scheduler(base_value, final_value, epochs, niter_per_ep):
	# final_value should be smaller than base_value
	iters = np.arange(epochs * niter_per_ep)
    schedule = final_value + 0.5 * (base_value - final_value) * (1 + np.cos(np.pi * iters / len(iters)))
    assert len(schedule) == epochs * niter_per_ep
    return schedule

也可以把预热和consine_scheduler放在一起:

def cosine_scheduler(base_value, final_value, epochs, niter_per_ep, warmup_epochs=0, start_warmup_value=0):
    warmup_schedule = np.array([])
    warmup_iters = warmup_epochs * niter_per_ep
    if warmup_epochs > 0:
        warmup_schedule = np.linspace(start_warmup_value, base_value, warmup_iters)

    iters = np.arange(epochs * niter_per_ep - warmup_iters)
    schedule = final_value + 0.5 * (base_value - final_value) * (1 + np.cos(np.pi * iters / len(iters)))

    schedule = np.concatenate((warmup_schedule, schedule))
    assert len(schedule) == epochs * niter_per_ep
    return schedule

自动混合精度 (Automatic Mixed Precision)

混合精度训练结合了两种数据类型:

  • 单精度(Float32):传统上,深度学习模型使用单精度格式进行计算和存储参数。单精度提供了足够的数值范围和精度,适合保证大多数训练任务的稳定性和收敛性。
  • 半精度(Float16):半精度提供较低的数值精度,但在现代GPU(如NVIDIA的Volta及更高级的架构)上,使用半精度进行计算速度更快,且消耗更少的内存。

torch.cuda.amp.autocast 允许自动选择最适合操作的数据类型,它能在运行时动态地将数据类型从Float32转换为Float16,从而加速训练并减少内存使用,而不牺牲模型的性能或精度。这种转换对大多数操作是透明的。

with autocast(): # 默认 enabled=True
        outputs = model(inputs.cuda())
        loss = loss_fn(outputs, targets.cuda())

在 PyTorch 中,torch.cuda.amp.GradScaler 是配合自动混合精度 (autocast) 使用的工具,旨在处理半精度计算中可能出现的数值不稳定问题。由于半精度浮点数的动态范围有限,计算过程中的梯度可能出现太小而无法在半精度中精确表示的情况,这就是所谓的“梯度欠流”(underflow)和“梯度溢出”(overflow)。GradScaler 通过动态调整梯度的规模(即缩放因子)来帮助避免这些问题。具体来说,它会自动调整梯度的大小,以确保它们不会因为太小而在半精度表示中丢失,也不会因为太大而溢出。

import torch
from torch import nn
from torch.cuda.amp import autocast, GradScaler
import torch.optim as optim

# 定义模型和优化器
model = nn.Linear(10, 1).cuda()
optimizer = optim.SGD(model.parameters(), lr=0.01)

# 初始化GradScaler
scaler = GradScaler()

for data, target in data_loader:  # 假设data_loader已经定义
    optimizer.zero_grad()
    
    # 自动混合精度块
    with autocast():
        output = model(data)
        loss = loss_fn(output, target)
    
    # 放大梯度
    scaler.scale(loss).backward()
    
    # 优化器更新前,先尝试减小梯度规模
    scaler.step(optimizer)
    
    # 更新缩放器
    scaler.update()


  • 18
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值