今日目标:掌握学习率调度、优化器选型、混合精度训练、梯度累积等实战技巧,让模型训练又快又稳
预计阅读:10分钟 | 动手操作:40分钟


一、训练技巧全景图

┌──────────────────────────────────────────────────┐
│              深度学习训练加速工具箱                 │
├──────────────┬──────────────┬────────────────────┤
│ 学习率调度    │ 优化器选型    │ 精度与内存          │
│ Step/Cosine  │ SGD/Adam/    │ 混合精度/梯度累积   │
│ Warmup/      │ AdamW/       │ 梯度裁剪/           │
│ OneCycle     │ RMSprop      │ 梯度检查点          │
├──────────────┼──────────────┼────────────────────┤
│ 初始化       │ 分布式训练    │ 调试诊断            │
│ Kaiming/     │ DataParallel/│ loss曲线/NAN排查/   │
│ Xavier/      │ DDP/FSDP     │ 过拟合欠拟合        │
│ 正交初始化    │              │                     │
└──────────────┴──────────────┴────────────────────┘
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.optim.lr_scheduler import (
    StepLR, CosineAnnealingLR, CosineAnnealingWarmRestarts,
    OneCycleLR, ReduceLROnPlateau, LambdaLR
)
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
from torch.cuda.amp import autocast, GradScaler
import numpy as np

torch.manual_seed(42)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

二、核心知识点

2.1 学习率调度:训练的"油门"

"""
学习率是训练中最重要(也是最难调)的超参数

太大了 → 不收敛,loss震荡
太小了 → 收敛太慢,陷入局部最优
刚刚好 → 快速稳定收敛

学习率调度 = 训练过程中动态调整学习率
"""

# 准备数据
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2470, 0.2435, 0.2616)),
])
train_dataset = datasets.CIFAR10('./data', train=True, download=True, transform=transform)
train_loader = DataLoader(train_dataset, batch_size=128, shuffle=True, num_workers=0)


def create_model():
    """快速创建一个简单CNN用于演示"""
    return nn.Sequential(
        nn.Conv2d(3, 32, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2),
        nn.Conv2d(32, 64, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2),
        nn.Conv2d(64, 128, 3, padding=1), nn.ReLU(), nn.AdaptiveAvgPool2d(1),
        nn.Flatten(), nn.Linear(128, 10),
    )


def compare_lr_schedulers():
    """对比不同学习率调度策略"""
    schedulers = {
        'StepLR (gamma=0.5, step=30)': lambda opt: StepLR(opt, step_size=30, gamma=0.5),
        'CosineAnnealing': lambda opt: CosineAnnealingLR(opt, T_max=50, eta_min=1e-5),
        'CosineWarmRestarts': lambda opt: CosineAnnealingWarmRestarts(opt, T_0=20, T_mult=2),
        'OneCycleLR': lambda opt: OneCycleLR(opt, max_lr=0.01, steps_per_epoch=len(train_loader), epochs=50),
        'ReduceLROnPlateau': lambda opt: ReduceLROnPlateau(opt, mode='min', factor=0.5, patience=5),
        'No Scheduler': lambda opt: None,
    }
    
    # 模拟训练过程,记录学习率变化
    print("学习率变化对比 (前50个epoch):")
    print(f"{'Scheduler':<25s} {'初始LR':>10s} {'最终LR':>10s} {'变化曲线':>20s}")
    print('-' * 70)
    
    for name, scheduler_fn in schedulers.items():
        model = create_model()
        optimizer = optim.SGD(model.parameters(), lr=0.01)
        scheduler = scheduler_fn(optimizer)
        
        lr_history = []
        for epoch in range(50):
            # 模拟训练
            for batch_idx, (x, y) in enumerate(train_loader):
                if batch_idx > 0:  # 只模拟1个batch
                    break
                optimizer.zero_grad()
                loss = F.cross_entropy(model(x), y)
                loss.backward()
                optimizer.step()
            
            if scheduler is not None:
                if isinstance(scheduler, ReduceLROnPlateau):
                    scheduler.step(loss.item())
                else:
                    scheduler.step()
            
            lr_history.append(optimizer.param_groups[0]['lr'])
        
        curve = '↘' + '─' * int(lr_history[-1] / lr_history[0] * 10)
        print(f"{name:<25s} {lr_history[0]:>10.6f} {lr_history[-1]:>10.6f} {curve:<20s}")

compare_lr_schedulers()
学习率 Warmup
def warmup_lambda(epoch, warmup_epochs=5):
    """学习率warmup:前N个epoch从0线性增长到初始lr"""
    if epoch < warmup_epochs:
        return (epoch + 1) / warmup_epochs
    return 1.0

# 使用
# scheduler = LambdaLR(optimizer, lr_lambda=lambda e: warmup_lambda(e, 5))

"""
Warmup为什么重要:
1. 训练初期模型参数是随机的,梯度方向不稳定
2. 大学习率会让模型"跑偏",难以纠正
3. Warmup让模型先用小学习率找到正确方向,再加速

经验:warmup 5个epoch对大多数任务都有效
"""
学习率调度器选型指南
"""
学习率调度器选型指南:

┌──────────────────────┬────────────────────┬──────────────────────┐
│     调度器           │     适用场景        │     特点             │
├──────────────────────┼────────────────────┼──────────────────────┤
│ StepLR               │ 传统CNN任务         │ 简单粗暴,阶梯下降   │
│ CosineAnnealing      │ 大多数任务(推荐)  │ 平滑下降,当前主流   │
│ CosineWarmRestarts   │ 长时间训练          │ 周期性重启,跳出局部  │
│ OneCycleLR            │ 快速训练            │ 先升后降,收敛快     │
│ ReduceLROnPlateau    │ 自动调参            │ loss不降时自动降lr   │
│ LambdaLR             │ 自定义策略          │ 完全灵活             │
└──────────────────────┴────────────────────┴──────────────────────┘

推荐默认方案:
  一般任务: CosineAnnealingLR(optimizer, T_max=epochs)
  需要调参: ReduceLROnPlateau(optimizer, patience=5)
  追求极致: CosineAnnealing + Warmup
"""

2.2 优化器选型:SGD、Adam、AdamW

"""
优化器选型指南:

┌──────────┬──────────────┬──────────────┬──────────────────────┐
│ 优化器   │    特点      │   适用场景    │     端侧AI建议       │
├──────────┼──────────────┼──────────────┼──────────────────────┤
│ SGD+M    │ 需要调参,   │ 目标检测/    │ 检测模型首选,       │
│          │ 泛化性好     │ 图像分类     │ 泛化能力更强         │
│ Adam     │ 自适应,     │ NLP/快速原型 │ 不推荐,没有weight   │
│          │ 收敛快       │              │ decay的解耦          │
│ AdamW    │ 解耦weight   │ 通用(推荐) │ 推荐!分类任务首选   │
│          │ decay        │              │                      │
│ RMSprop  │ 处理非平稳   │ RNN/强化学习 │ 端侧AI不常用         │
└──────────┴──────────────┴──────────────┴──────────────────────┘

选型口诀:
  - 分类任务 → AdamW (lr=1e-3, weight_decay=1e-4)
  - 检测任务 → SGD + Momentum (lr=1e-2, momentum=0.9)
  - 不确定   → AdamW,大概率不会错
"""

def compare_optimizers():
    """对比不同优化器的收敛速度"""
    optimizers = {
        'SGD': lambda params: optim.SGD(params, lr=0.01),
        'SGD+Momentum': lambda params: optim.SGD(params, lr=0.01, momentum=0.9),
        'Adam': lambda params: optim.Adam(params, lr=0.001),
        'AdamW': lambda params: optim.AdamW(params, lr=0.001, weight_decay=1e-4),
    }
    
    print("\n优化器收敛对比 (5个epoch):")
    print(f"{'优化器':<15s} {'初始Loss':>10s} {'最终Loss':>10s} {'下降':>10s}")
    print('-' * 50)
    
    for name, opt_fn in optimizers.items():
        model = create_model()
        optimizer = opt_fn(model.parameters())
        
        losses = []
        for epoch in range(5):
            for x, y in train_loader:
                optimizer.zero_grad()
                loss = F.cross_entropy(model(x), y)
                loss.backward()
                optimizer.step()
                losses.append(loss.item())
                if len(losses) > 50:  # 只模拟少量steps
                    break
        
        print(f"{name:<15s} {losses[0]:>10.4f} {losses[-1]:>10.4f} {losses[0]-losses[-1]:>10.4f}")

compare_optimizers()

2.3 混合精度训练(AMP):白嫖显存和速度

"""
混合精度训练 = float32 + float16 混合使用

为什么:
  - float16计算速度是float32的2-4倍(取决于GPU架构)
  - float16显存占用减半
  - 大多数操作float16精度足够

怎么做:
  - 前向传播用float16(快)
  - 反向传播时敏感操作(softmax, loss)用float32(稳)
  - 用GradScaler防止梯度下溢

PyTorch AMP只需加3行代码!
"""

def train_with_amp(model, loader, epochs=5):
    """使用混合精度训练"""
    model = model.to(device)
    optimizer = optim.AdamW(model.parameters(), lr=0.001)
    criterion = nn.CrossEntropyLoss()
    scaler = GradScaler()  # 1. 创建GradScaler
    
    for epoch in range(epochs):
        model.train()
        for x, y in loader:
            x, y = x.to(device), y.to(device)
            optimizer.zero_grad()
            
            # 2. autocast上下文
            with autocast():
                output = model(x)
                loss = criterion(output, y)
            
            # 3. scaler处理梯度
            scaler.scale(loss).backward()
            scaler.step(optimizer)
            scaler.update()
        
        if epoch == 0:
            break
    
    print("AMP训练演示完成!")


def compare_amp_performance():
    """对比AMP和FP32的速度和显存"""
    model = create_model().to(device)
    dummy_input = torch.randn(64, 3, 32, 32).to(device)
    
    # 预热
    for _ in range(10):
        _ = model(dummy_input)
    
    # FP32
    import time
    torch.cuda.synchronize()
    start = time.time()
    for _ in range(100):
        _ = model(dummy_input)
    torch.cuda.synchronize()
    fp32_time = time.time() - start
    
    # AMP
    torch.cuda.synchronize()
    start = time.time()
    for _ in range(100):
        with autocast():
            _ = model(dummy_input)
    torch.cuda.synchronize()
    amp_time = time.time() - start
    
    print(f"\n混合精度性能对比:")
    print(f"  FP32: {fp32_time:.3f}s ({100/fp32_time:.0f} iters/s)")
    print(f"  AMP:  {amp_time:.3f}s ({100/amp_time:.0f} iters/s)")
    print(f"  加速: {fp32_time/amp_time:.1f}x")
    
    if torch.cuda.is_available():
        print(f"\n  显存占用 (估算):")
        mem = torch.cuda.memory_allocated() / 1024**2
        print(f"  FP32权重: {sum(p.numel()*4 for p in model.parameters())/1024**2:.1f} MB")
        print(f"  FP16权重: {sum(p.numel()*2 for p in model.parameters())/1024**2:.1f} MB (理论上)")
        print(f"  实际节省: 约40-50%显存")

if torch.cuda.is_available():
    compare_amp_performance()

2.4 梯度累积:小显存也能用大Batch

"""
梯度累积 = 多个小batch的梯度累加,再一次性更新

场景:你只有8GB显存,想用batch_size=256
方案:batch_size=32 + gradient_accumulation_steps=8

效果:loss曲线和真正的batch_size=256几乎一样!
"""

def train_with_gradient_accumulation(model, loader, accumulation_steps=4):
    """
    梯度累积训练
    
    accumulation_steps=4 意味着:
    每4个batch累加梯度,然后更新一次
    等价于 batch_size 扩大了4倍
    """
    model = model.to(device)
    optimizer = optim.AdamW(model.parameters(), lr=0.001)
    criterion = nn.CrossEntropyLoss()
    
    model.train()
    for batch_idx, (x, y) in enumerate(loader):
        if batch_idx >= 20:  # 只演示
            break
        
        x, y = x.to(device), y.to(device)
        
        # 前向传播
        output = model(x)
        loss = criterion(output, y) / accumulation_steps  # 注意:loss要除以累积步数!
        loss.backward()
        
        # 每 accumulation_steps 步更新一次
        if (batch_idx + 1) % accumulation_steps == 0:
            optimizer.step()
            optimizer.zero_grad()
    
    print("梯度累积演示完成!")


def gradient_accumulation_example():
    """
    梯度累积等效性证明
    
    正常batch_size=128:
      loss = sum(loss_i) / 128
      loss.backward()
      optimizer.step()
    
    梯度累积 batch_size=32, steps=4:
      for i in range(4):
          loss = sum(loss_i) / 32
          (loss / 4).backward()  # 除以4保持梯度尺度一致
      optimizer.step()
    
    两种方式等价的数学证明:
      ∂/∂θ (1/128 * ΣL_i) = 1/128 * Σ(∂L_i/∂θ)
      = 1/4 * [1/32 * Σ_1(∂L_i/∂θ)] + 1/4 * [1/32 * Σ_2(∂L_i/∂θ)] + ... ✓
    """
    print("梯度累积等效性: 已验证 ✓")
    print("关键:每次backward前loss要除以accumulation_steps!")

gradient_accumulation_example()

2.5 梯度裁剪:防止梯度爆炸

"""
梯度裁剪:当梯度的L2范数超过阈值时,按比例缩小

使用场景:
  - RNN/LSTM/Transformer训练(序列长,梯度容易爆炸)
  - 训练不稳定时
  - 使用大学习率时

Pytorch一行代码:
  torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
"""

def gradient_clipping_demo():
    """演示梯度裁剪的效果"""
    model = create_model()
    # 制造一个会产生大梯度的情况
    x = torch.randn(1, 3, 32, 32) * 100  # 异常大的输入
    
    # 不裁剪
    output = model(x)
    loss = output.sum()
    loss.backward()
    
    grad_norms_no_clip = []
    for p in model.parameters():
        if p.grad is not None:
            grad_norms_no_clip.append(p.grad.norm().item())
    
    # 裁剪
    model.zero_grad()
    output = model(x)
    loss = output.sum()
    loss.backward()
    torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
    
    grad_norms_clipped = []
    for p in model.parameters():
        if p.grad is not None:
            grad_norms_clipped.append(p.grad.norm().item())
    
    print("梯度裁剪效果:")
    print(f"  不裁剪 - 最大梯度范数: {max(grad_norms_no_clip):.4f}")
    print(f"  裁剪后 - 最大梯度范数: {max(grad_norms_clipped):.4f}")
    print(f"  裁剪比例: {max(grad_norms_clipped)/max(grad_norms_no_clip)*100:.1f}%")

gradient_clipping_demo()

2.6 权重初始化

def demonstrate_weight_init():
    """演示不同初始化方法"""
    x = torch.randn(100, 256)
    
    print("权重初始化方法对比:")
    
    # 1. 全零初始化 → 所有神经元学一样的东西 → 对称性问题
    w_zeros = nn.Linear(256, 128)
    nn.init.zeros_(w_zeros.weight)
    out_zeros = w_zeros(x)
    print(f"  全零: 输出均值={out_zeros.mean():.4f}, 标准差={out_zeros.std():.4f} → 没用!")
    
    # 2. 标准正态初始化 → 深层网络容易梯度消失/爆炸
    w_normal = nn.Linear(256, 128)
    nn.init.normal_(w_normal.weight, mean=0, std=0.01)
    out_normal = w_normal(x)
    print(f"  正态(0,0.01): 输出均值={out_normal.mean():.4f}, 标准差={out_normal.std():.4f}")
    
    # 3. Xavier初始化(适合tanh/sigmoid)
    w_xavier = nn.Linear(256, 128)
    nn.init.xavier_normal_(w_xavier.weight)
    out_xavier = w_xavier(x)
    print(f"  Xavier: 输出均值={out_xavier.mean():.4f}, 标准差={out_xavier.std():.4f} → 方差保持")
    
    # 4. Kaiming初始化(适合ReLU,最常用!)
    w_kaiming = nn.Linear(256, 128)
    nn.init.kaiming_normal_(w_kaiming.weight, mode='fan_in', nonlinearity='relu')
    out_kaiming = w_kaiming(x)
    print(f"  Kaiming: 输出均值={out_kaiming.mean():.4f}, 标准差={out_kaiming.std():.4f} → 推荐!")
    
    print("\n  PyTorch默认的Linear用的就是Kaiming初始化!")

demonstrate_weight_init()

三、动手实践:完整的训练技巧集成

3.1 实战:把所有技巧整合到一个训练脚本

class TrainingPipeline:
    """
    集成所有训练技巧的训练Pipeline
    
    包含:
    ✅ 学习率Warmup + CosineAnnealing
    ✅ AdamW优化器
    ✅ 混合精度训练 (AMP)
    ✅ 梯度累积(可选)
    ✅ 梯度裁剪
    ✅ 早停
    """
    def __init__(self, model, device, 
                 lr=0.001, weight_decay=1e-4,
                 warmup_epochs=5, total_epochs=100,
                 use_amp=True, accumulation_steps=1,
                 grad_clip=1.0, patience=10):
        
        self.model = model.to(device)
        self.device = device
        self.use_amp = use_amp
        self.accumulation_steps = accumulation_steps
        self.grad_clip = grad_clip
        self.patience = patience
        
        # 优化器
        self.optimizer = optim.AdamW(model.parameters(), lr=lr, weight_decay=weight_decay)
        
        # 学习率调度:Warmup + Cosine
        def lr_lambda(epoch):
            if epoch < warmup_epochs:
                return (epoch + 1) / warmup_epochs  # Warmup
            # Cosine annealing
            progress = (epoch - warmup_epochs) / (total_epochs - warmup_epochs)
            return 0.5 * (1 + np.cos(np.pi * progress))
        
        self.scheduler = LambdaLR(self.optimizer, lr_lambda)
        self.criterion = nn.CrossEntropyLoss()
        self.scaler = GradScaler() if use_amp else None
        
        # 训练状态
        self.best_acc = 0
        self.best_epoch = 0
        self.epochs_no_improve = 0
    
    def train_epoch(self, loader):
        self.model.train()
        total_loss, correct, total = 0, 0, 0
        
        for batch_idx, (x, y) in enumerate(loader):
            x, y = x.to(self.device), y.to(self.device)
            
            # 混合精度
            if self.use_amp:
                with autocast():
                    output = self.model(x)
                    loss = self.criterion(output, y) / self.accumulation_steps
                
                self.scaler.scale(loss).backward()
                
                if (batch_idx + 1) % self.accumulation_steps == 0:
                    # 梯度裁剪
                    self.scaler.unscale_(self.optimizer)
                    torch.nn.utils.clip_grad_norm_(self.model.parameters(), self.grad_clip)
                    
                    self.scaler.step(self.optimizer)
                    self.scaler.update()
                    self.optimizer.zero_grad()
            else:
                output = self.model(x)
                loss = self.criterion(output, y) / self.accumulation_steps
                loss.backward()
                
                if (batch_idx + 1) % self.accumulation_steps == 0:
                    torch.nn.utils.clip_grad_norm_(self.model.parameters(), self.grad_clip)
                    self.optimizer.step()
                    self.optimizer.zero_grad()
            
            with torch.no_grad():
                total_loss += loss.item() * self.accumulation_steps * x.size(0)
                pred = output.argmax(1)
                correct += pred.eq(y).sum().item()
                total += y.size(0)
        
        return total_loss / total, correct / total
    
    @torch.no_grad()
    def evaluate(self, loader):
        self.model.eval()
        total_loss, correct, total = 0, 0, 0
        
        for x, y in loader:
            x, y = x.to(self.device), y.to(self.device)
            
            if self.use_amp:
                with autocast():
                    output = self.model(x)
                    loss = self.criterion(output, y)
            else:
                output = self.model(x)
                loss = self.criterion(output, y)
            
            total_loss += loss.item() * x.size(0)
            correct += output.argmax(1).eq(y).sum().item()
            total += y.size(0)
        
        return total_loss / total, correct / total
    
    def fit(self, train_loader, val_loader, epochs):
        for epoch in range(epochs):
            train_loss, train_acc = self.train_epoch(train_loader)
            val_loss, val_acc = self.evaluate(val_loader)
            
            self.scheduler.step()
            
            # 保存最佳模型
            if val_acc > self.best_acc:
                self.best_acc = val_acc
                self.best_epoch = epoch + 1
                self.epochs_no_improve = 0
                torch.save(self.model.state_dict(), 'best_model.pth')
            else:
                self.epochs_no_improve += 1
            
            if epoch % 10 == 0:
                lr = self.optimizer.param_groups[0]['lr']
                print(f"Epoch {epoch+1:3d}: "
                      f"Train {train_acc:.2%}, Val {val_acc:.2%}, "
                      f"LR {lr:.6f}, Best {self.best_acc:.2%}")
            
            # 早停
            if self.epochs_no_improve >= self.patience:
                print(f"早停!第 {epoch+1} 个epoch停止")
                break
        
        print(f"\n训练完成!最佳val_acc={self.best_acc:.2%} (epoch {self.best_epoch})")
        self.model.load_state_dict(torch.load('best_model.pth'))
        return self.model


# 使用
# model = create_model()
# pipeline = TrainingPipeline(model, device, lr=0.001, total_epochs=50)
# pipeline.fit(train_loader, val_loader, epochs=50)

四、训练诊断:Loss不下降怎么办?

"""
训练出问题时的诊断流程:

1. loss不下降:
   → 检查学习率是否太大或太小 → 从1e-3开始探索
   → 检查数据加载是否正确 → 打印一个batch看看
   → 检查模型是否在训练模式 → model.train()
   → 检查梯度是否为零 → 打印param.grad

2. loss变成NaN:
   → 学习率太大 → 降10倍
   → 数据中有NaN → 检查数据预处理
   → 梯度爆炸 → 加梯度裁剪
   → 除零错误 → 检查自定义loss中的除法

3. 训练集准确率很高,验证集很低:
   → 过拟合 → 加正则化/数据增强/减少模型容量

4. 训练集和验证集准确率都很低:
   → 欠拟合 → 加模型容量/降学习率/增加训练时间

5. loss震荡:
   → 学习率太大 → 降10倍
   → batch_size太小 → 增大batch_size或加梯度累积

诊断口诀:
  不收敛 → 降学习率
  NaN    → 降学习率 + 梯度裁剪
  过拟合 → 加正则化
  欠拟合 → 加模型/训练时间
"""

五、常见坑点

坑1:AMP中loss.backward()之前不要手动除

# ❌ 错误:自己除了loss,scaler又scale了一次
loss = criterion(output, y) / accumulation_steps
scaler.scale(loss).backward()  # 双重缩放!

# ✅ 正确:accumulation时用scaler
loss = criterion(output, y)
scaler.scale(loss / accumulation_steps).backward()

坑2:ReduceLROnPlateau需要手动传loss

# scheduler = ReduceLROnPlateau(optimizer, patience=5)
# scheduler.step(val_loss)  # 必须传入验证loss,不是训练loss!

坑3:AdamW的weight_decay和L2正则化不同

# AdamW解耦了weight_decay和L2正则化
# SGD: θ = θ - lr * (∇L + 2*λ*θ)
# AdamW: θ = θ - lr * ∇L - lr * λ * θ
# 效果:AdamW的weight_decay等价于L2,但Adam的不等价
# 所以:用AdamW,别用Adam + weight_decay

坑4:梯度累积时BatchNorm的处理

# BN在每次forward时更新running_mean/running_var
# 梯度累积时,多个小batch的BN统计量不同
# 影响:accumulation_steps太大时,BN统计量不准确

# 解决方案:
# 1. accumulation_steps不要太大(≤4一般没问题)
# 2. 用SyncBatchNorm(多卡时)
# 3. 用GroupNorm代替BatchNorm(不依赖batch统计量)

六、今日作业

  1. 学习率实验:对比StepLR和CosineAnnealing,记录训练曲线差异
  2. AMP实验:对比FP32和AMP的训练速度和显存占用
  3. 梯度累积:用batch_size=32 + accumulation_steps=4模拟batch_size=128,验证loss曲线一致性
  4. 打卡:评论区发你的实验结果,格式:“Day 25/100 打卡:训练技巧全部掌握!”

今日小结

今天你学会了:
✅ 6种学习率调度器及选型指南
✅ SGD/Adam/AdamW优化器选型
✅ 混合精度训练(AMP)—— 3行代码加速2-4x
✅ 梯度累积 —— 小显存也能用大Batch
✅ 梯度裁剪 —— 防止梯度爆炸
✅ 4种权重初始化方法
✅ 集成所有技巧的完整TrainingPipeline
✅ 训练诊断流程(loss不下降/NaN/过拟合/欠拟合)
✅ 4个经典坑点

明日预告

Day 26:数据增强技术 — 让模型更鲁棒
几何变换、颜色变换、CutOut、MixUp、CutMix、AutoAugment


🔥 关注我,每天解锁一个端侧AI技能!
微信公众号:xxx | 小红书:xxx | CSDN:xxx
评论区打卡,一起坚持100天!


附:小红书图文版

封面标题建议:模型训练加速大全 | 这些技巧让训练飞起来 🚀

P1 — 封面
标题:训练技巧全攻略
副标题:学习率 / 优化器 / AMP / 梯度累积
关键词:训练技巧 / 加速 / 深度学习

P2 — 学习率调度
6种调度器选型指南
推荐:CosineAnnealing + Warmup
StepLR简单粗暴,Cosine平滑下降
OneCycle收敛最快

P3 — 优化器选型
分类→AdamW (lr=1e-3)
检测→SGD+Momentum (lr=1e-2)
不确定→AdamW,大概率不会错
AdamW > Adam(解耦weight_decay)

P4 — 混合精度AMP
只用3行代码,加速2-4倍
显存节省40-50%
autocast + GradScaler
端侧AI训练省显存的利器

P5 — 梯度累积+裁剪
梯度累积:小显存用大Batch
batch_size=32×4步=128效果
梯度裁剪:防止梯度爆炸
max_norm=1.0是常用值

P6 — 今日作业
学习率对比实验 + AMP实验
评论区打卡 Day 25/100

标签:#训练技巧 #学习率 #混合精度 #深度学习


CSDN发布提示:CSDN版本建议在学习率调度部分放6张学习率变化曲线图,在AMP部分放速度对比柱状图,在Pipeline部分放流程图。

更多推荐