BotOf TechAI / IoT / Full-Stack / 植物养护知识分享
W0702 / 聚焦微调实战演练

训练可观测与动态调参

建立从代码、数据、超参到 Loss、梯度、显存和吞吐的实验谱系,用证据定位训练事故。

建议时长
7–9 小时
难度
工程进阶
课程位置
7 / 12
LEARNING OBJECTIVES

学完这一课,你应该能:

  • 能定义一条可复现训练运行的完整身份
  • 能同时监控优化、数据与系统指标
  • 能用时间线定位 OOM、Loss spike 和吞吐下降
  • 能设计安全的 checkpoint 与恢复策略

1. 一次实验的身份是什么

可复现实验不是一个 run name,而是:

run identity =
code SHA
+ dependency lock
+ dataset hash
+ model revision
+ config
+ random seed
+ hardware
+ precision

任意一项缺失,都可能得到不同结果。final-v2-good 这种命名不能回答使用了哪个数据和基座。

2. 四类指标必须同时存在

优化指标

  • train/eval loss
  • learning rate
  • global/per-layer gradient norm
  • parameter norm、update/weight ratio

数据指标

  • tokens/batch 与有效监督 token
  • 截断率、padding 比例
  • 数据读取等待
  • 当前 batch 的数据 shard 与样本 id

系统指标

  • step time、tokens/s
  • GPU utilization、SM occupancy(需要时)
  • allocated/reserved VRAM
  • CPU、磁盘、网络与 dataloader wait

质量指标

  • 任务 metric
  • 黄金集错误分类
  • 固定 prompt 的生成样例

Loss 下降只说明优化目标下降,不保证验证质量、事实性或业务行为改善。

3. 建立最小实验记录

import os, subprocess, torch

def git_sha():
    return subprocess.check_output(
        ["git", "rev-parse", "HEAD"], text=True
    ).strip()

run_config = {
    **training_args,
    "git_sha": git_sha(),
    "dataset_hash": dataset_hash,
    "model_revision": model_revision,
    "torch_version": torch.__version__,
    "cuda_version": torch.version.cuda,
    "gpu": torch.cuda.get_device_name(0),
}
tracker.init(project="ai-dev", config=run_config)

制品也要版本化:训练配置、数据卡、checkpoint、评测输出和失败样例。指标图没有对应权重,就不能重放结果。

4. 为什么 Loss spike 要看时间线

Loss 在 step 3200 突升,可能来自:

  • 学习率调度变化
  • 梯度累积边界错误
  • 特殊数据 batch
  • 混合精度 overflow
  • 从 checkpoint 恢复后优化器状态丢失
  • 多卡数据或梯度不同步

正确做法是把这些事件放在同一时间轴:

tracker.log({
    "train/loss": loss.item(),
    "train/grad_norm": grad_norm,
    "train/lr": scheduler.get_last_lr()[0],
    "data/supervised_tokens": valid_labels,
    "system/allocated_gb": torch.cuda.memory_allocated() / 2**30,
    "system/reserved_gb": torch.cuda.memory_reserved() / 2**30,
    "system/tokens_per_sec": tokens / step_seconds,
}, step=global_step)

同时记录 sample_ids 到本地审计日志。若异常能随同一 batch 重现,数据原因的证据就很强。

5. OOM 不能只看模型大小

峰值显存近似包含:

weights
+ gradients
+ optimizer states
+ saved activations
+ temporary kernels/buffers
+ allocator reserved/fragmentation

排查顺序:

  1. 记录 OOM 前序列长度与 batch。
  2. 区分 allocated 与 reserved。
  3. 减小 micro-batch,保持 gradient accumulation,验证是否激活主导。
  4. 开启 gradient checkpointing,比较显存与 step time。
  5. 检查评测/生成是否忘记 inference mode。
  6. 不要把 empty_cache() 当根治;它不释放仍被引用的 tensor。

6. 吞吐下降的分解

step_time =
data_wait
+ host_to_device
+ forward
+ backward
+ optimizer
+ communication
+ checkpoint/eval

GPU utilization 低不等于模型小。可能是 tokenizer 在主进程、数据存储慢、频繁打印、评测过密或多卡通信等待。

先测空 dataloader 的模型吞吐,再测完整流水线;再逐项增加 logging、evaluation、checkpoint。分层基线比盯一个 utilization 百分比更可靠。

7. Checkpoint 要能真正恢复

完整恢复通常需要:

  • model/adapter state
  • optimizer state
  • scheduler state
  • scaler state
  • global step/epoch
  • RNG state
  • dataloader sampler state

只加载权重再继续训练,不等于无缝恢复。学习率、Adam 动量和数据位置都可能重置。

做一次恢复演练:训练 100 步保存,从 checkpoint 再跑 20 步;与不中断的 120 步比较 Loss 与权重差异。确定性限制要写进报告。

8. 训练事故实验

主动制造:

  1. OOM:插入超长样本。
  2. Loss spike:把一批标签错位或学习率提高 20 倍。
  3. 吞吐下降:在每步同步保存大文件。

要求不看代码修改记录,只根据指标时间线提出根因候选,再用实验验证。交付:

  • 事故时间线
  • 根因证据
  • 临时处置
  • 永久修复
  • 防止复发的自动检查

9. 练习与答案提示

  1. allocated 低而 reserved 高说明什么?**答:**allocator 保留或碎片,不能直接等同活跃 tensor。
  2. eval loss 降、任务分数不升可能吗?**答:**可能,优化目标与任务指标不完全一致。
  3. 为什么记录 sample id?**答:**把异常与具体数据关联并重放。
  4. 只保存 LoRA adapter 够吗?**答:**发布可能够,训练恢复还需优化器、调度器等状态。
  5. 梯度 checkpointing 的代价?**答:**反向时重算前向,省显存但增加计算时间。

10. 延伸阅读