主题
第 3 章 配置覆盖、类型检查与执行边界
3.1 一次运行为什么会有三份“配置”
train.py 开头先定义普通 Python 全局变量,随后执行 configurator.py,最后把选中的全局值收集成 config 字典。配置不是独立对象:脚本后面直接使用 batch_size、learning_rate 等名字。默认值、配置文件、命令行参数依次覆盖,顺序决定最终结果。configurator.py
例如:
sh
python train.py config/train_shakespeare_char.py --batch_size=12 --device=cpu --dtype=float32 --compile=False先载入默认 batch_size=12,配置文件改为 64,末尾参数又改回 12。如果把同一个参数放在配置文件之前,后读的配置文件会覆盖它。这里没有“命令行永远优先”的特殊层级,只有从左到右执行。
config_keys 在执行配置之前,从全局变量中挑选非下划线开头、类型属于整数、浮点数、布尔或字符串的键。之后建立的 config 是这些键的快照。配置文件新引入的变量未必被记录;DDP 稍后再修改的每进程累积次数也不会反写这个快照。日志中的原始设置和运行时派生值有意分开,但读 checkpoint 时要知道自己看的是哪一个。
3.2 命令行的类型检查到底检查了什么
包含等号的参数必须以 -- 开头,随后 key, val = arg.split('=')。literal_eval 尝试把字符串变成 Python 字面值;失败时才保留原字符串。最后检查 type(attempt) == type(globals()[key])。
| 输入 | 若原值类型如下 | 实际结果 |
|---|---|---|
--batch_size=3 | int | 通过,得到整数 3 |
--batch_size=3.0 | int | AssertionError |
--compile=False | bool | 通过 |
--compile=false | bool | 小写不是 Python 布尔字面值,成为字符串,类型不符 |
--unknown=3 | 键不存在 | ValueError |
--start=a=b | str | 拆成三段,解包失败 |
表中反例已由 examples/verify_mechanics.py 执行,不是按理想解析器推测。外层 shell 去掉引号以后,参数里依然包含第二个等号,因此简单加引号不能修复最后一行;提示中需要等号时可使用 FILE: 文件方式。
类型检查不是范围检查。batch_size=-1、temperature=0.0、top_k=0 都可能通过各自的类型关,却在后续分配或采样时失败。也不是安全校验:配置文件走的是 exec,可以导入模块、写文件或执行任意 Python。literal_eval 只约束单个命令行值,不能给配置文件提供沙箱。只运行可信、审查过的配置;来自网页或陌生仓库的“配置”应按代码处理。
3.3 把开关按它真正影响的东西分类
| 组别 | 源码字段 | 谁使用它 |
|---|---|---|
| 数据与批次 | dataset, batch_size, block_size, gradient_accumulation_steps | 文件路径、取窗、模型长度、累积循环 |
| 模型 | n_layer, n_head, n_embd, dropout, bias | 新建模型;恢复时部分被 checkpoint 覆盖 |
| 优化器 | learning_rate, weight_decay, beta1, beta2, grad_clip | AdamW、学习率和梯度裁剪 |
| 日程 | max_iters, decay_lr, warmup_iters, lr_decay_iters, min_lr | 停止条件和逐步学习率 |
| 评估与保存 | out_dir, eval_interval, eval_iters, eval_only, always_save_checkpoint, init_from | 初始化、验证与 checkpoint |
| 系统 | backend, device, dtype, compile | 分布式、设备、自动混合精度和编译 |
| 在线日志 | wandb_log, wandb_project, wandb_run_name | 仅启用时导入和连接 W&B |
dtype='float32' 在本书 CPU 测试中是明确配置。原脚本 CPU 路径使用 nullcontext(),不会因为选择了 bfloat16 就自动把模型变成 BF16。默认 dtype 是根据 CUDA 支持情况决定的;没有 CUDA 时默认落入 float16,随后 GradScaler 可能给出禁用警告。把设备、dtype 和编译开关一起明确写出,比靠默认值猜行为容易审计。
3.4 七份配置,各自省略了什么
字符训练配置把层数、头数、宽度设为 6、6、384,长度 256,批次 64,累积 1,dropout 0.2;学习率 beta2=0.99。它每 250 步评估,200 个批次估计损失,每 10 步打印,只在验证改善时保存。bias、优化器其他字段仍从主脚本继承,因此不能把配置文件单独当作完整实验说明。
GPT-2 训练配置设 gradient_accumulation_steps=40、B=12,T=1024,600000 的训练与衰减终点,每 1000 步评估,评估 200 批,每 10 步打印,权重衰减 0.1。它主动启用 W&B,读者运行前若不想外联必须追加 --wandb_log=False。第 10 章解释 40 如何在 8 卡下变成每进程 5 次。
四份评估配置只有模型型号不同:gpt2、gpt2-medium、gpt2-large、gpt2-xl;共同设批次 8、评估 500 批、eval_only=True、关闭 W&B。它们没有指定数据目录,继承默认 OWT。微调配置指定 Shakespeare BPE、gpt2-xl、批次 1、累积 32、最多 20、固定学习率
3.5 练习:写出最终值,而不是凭文件名判断
补充练习。 执行 python train.py --batch_size=4 config/train_shakespeare_char.py --n_embd=128 后,批次、宽度和头数各是多少?能开始模型前向吗?
答案。 批次 64、宽度 128、头数 6。配置文件覆盖了先前批次,最后参数只覆盖宽度。128 不能被 6 整除,注意力初始化断言失败。修复时应连同头数调整,例如 4;配置正确解析不代表结构有效。
3.6 默认值逐项参考
下面是固定 train.py 的全部 33 项默认配置,读者可用它补齐覆盖文件中没写出的值。它们是源码事实,不是面向任意硬件的推荐参数。相同字段在 sample.py、bench.py 有各自默认值,不能跨入口推断。
| 字段 | 固定源码默认值 | 含义及边界 |
|---|---|---|
out_dir | 'out' | checkpoint 目录;复用旧目录前先确认 init_from |
eval_interval | 2000 | 相邻评估的迭代间隔;影响开销、保存和 RNG |
log_interval | 1 | 打印间隔;过密日志会增加同步与 I/O |
eval_iters | 200 | 每个 split 的抽样批次数;精度与耗时折中 |
eval_only | False | 仅在 iter_num=0 的原版分支退出,见 9.3 |
always_save_checkpoint | True | 每次评估都可保存;best 字段语义见 9.3 |
init_from | 'scratch' | scratch、resume 或 gpt2*;控制权重及优化器来源 |
wandb_log | False | 启用会外联日志服务;本书始终关闭 |
wandb_project | 'owt' | 在线日志项目名;关闭日志时不使用 |
wandb_run_name | 'gpt2' | 在线日志运行名;不决定本地输出目录 |
dataset | 'openwebtext' | data 下的子目录名;编码必须与模型一致 |
gradient_accumulation_steps | 5 * 8 | DDP 分配前的总累积数 A;必须整除 W |
batch_size | 12 | 每进程每微步序列数;不是全局 batch |
block_size | 1024 | 每序列长度;限制窗口和位置表 |
n_layer | 12 | block 数;主干成本近似线性增长 |
n_head | 12 | 注意力头数;宽度须能整除它 |
n_embd | 768 | 残差宽度 C;主矩阵成本约按 C 平方增长 |
dropout | 0.0 | 训练态随机丢弃比例;评估态关闭 |
bias | False | Linear/LN 是否带偏置;GPT-2 导入强制 True |
learning_rate | 0.0006 | 最大或固定学习率;每步写入所有参数组 |
max_iters | 600000 | 原版是更新后检查 >;有多执行一步边界 |
weight_decay | 0.1 | 矩阵参数组的 AdamW 解耦衰减 |
beta1 | 0.9 | Adam 一阶矩平滑系数 |
beta2 | 0.95 | Adam 二阶矩平滑系数 |
grad_clip | 1.0 | 全局梯度范数阈值;0.0 关闭裁剪 |
decay_lr | True | 是否调用 warmup+cosine;False 用固定值 |
warmup_iters | 2000 | warmup 分支终点;首步不是零 |
lr_decay_iters | 600000 | 余弦终点;须大于 warmup |
min_lr | 6e-05 | 衰减结束后保持的学习率 |
backend | 'nccl' | 进程组后端;改 gloo 不自动支持 CPU DDP |
device | 'cuda' | 单进程设备;DDP 会改为本地 CUDA 卡 |
dtype | 支持 CUDA BF16 则 bfloat16,否则 float16 | 自动混合精度选择;CPU 不使用 autocast |
compile | True | 是否 torch.compile;本书关闭,不测编译收益 |
七份配置的在线日志字符串也只是标签:字符训练使用项目 shakespeare-char、运行名 mini-gpt、输出 out-shakespeare-char;GPT-2 训练使用 owt、gpt2-124M,输出仍继承 out;微调使用 shakespeare、由当前时间拼出的 ft-...、输出 out-shakespeare。时间戳只改变日志名,不设随机 seed。四份评估文件不定义输出路径和项目名,继续继承默认。逐项赋值、行号及原表达式见配套 research/author/coverage.json 的 config_assignments,可以核对是否遗漏继承项。
本章来源
- configurator.py 的执行与解析
- train.py 默认配置与 config_keys
- 七份原始配置
- 本书解析反例:
qa/author/mechanics.json。