这是一个基于PyTorch从零构建Transformer语言模型并研究缩放定律的项目,通过训练探究非嵌入参数量、训练数据量与模型性能的关联,并运用DeepMind的缩放定律预测同架构不同规模模型的最终loss (但该预测仅基于非嵌入参数与训练token数量,未涵盖其他超参数设置,详见最后常见问题中的调参说明)。
- 🚀 从零实现的Transformer语言模型
- 📊 基于Chinchilla缩放定律的性能预测
- 🔬 完整的训练监控和可视化
- 📈 FLOPs-Loss缩放关系分析
- 🎯 支持多种模型规模配置
├── model.py # decoder-only模型实现
├── train.py # 训练脚本
├── scaling.py # 缩放定律相关函数
├── fit_scaling.py # 缩放定律拟合
├── predict loss.py # 损失预测
├── test1.py # IsoFLOPs曲线分析
├── data.bin # 训练数据(需要通过Assignment1中的`get train data.py`得到)
├── tokenizer.json # 分词器配置
├── isoflops_curves.json # IsoFLOPs实验数据
├── 配置.text # 模型配置参数
└── ckpt/ # 检查点和结果
├── epoch_*.pt # 训练检查点
├── *.png # 曲线图
└── scaling_records_70M.npy # 使用第一个配置得到的缩放数据记录
data.bin通过get train data.py得到, 需要根据训练模型的规模的大小确定训练总token数量,这里建议每一个参数至少分配到20个token训练。
实现了完整的Transformer语言模型,包括:
- 多头自注意力机制
- 前馈神经网络
- 层归一化和残差连接
- 位置编码
模型配置示例:
config = {
"vocab_size": 32000,
"context_length": 2048,
"d_model": 768,
"num_layers": 12,
"num_heads": 12,
"d_ff": 3072,
"attn_pdrop": 0.1,
"residual_pdrop": 0.1,
}- 自定义AdamW优化器实现
- 余弦学习率调度
- 梯度裁剪
- 内存监控
- 自动检查点保存
- 训练/验证损失可视化
基于Chinchilla论文实现的缩放定律:
L(N,D) = E + A/N^α + B/D^β
其中:
- N: 模型参数量
- D: 训练数据量
- L: 损失值
- E, A, B, α, β: 拟合参数(论文的实现数据为E=1.69, A=406.4, B=410.7, alpha=0.34, beta=0.28)
pip install torch numpy matplotlib scipy transformers psutil# 使用默认配置训练70M参数模型
python train.py --epochs 8 --batch_size 16 --d_model 384 --num_layers 24
# 训练130M参数模型
python train.py --d_model 512 --num_layers 36 --num_heads 8
# 训练300M参数模型
python train.py --d_model 768 --num_layers 36 --num_heads 12# 拟合缩放定律并生成可视化
python fit_scaling.py
# 预测特定配置的损失(通过模型的非嵌入参数、训练总token数量)
python "predict loss.py"项目预设了三种模型规模配置:
| 模型规模 | 参数量 | d_model | num_layers | num_heads |
|---|---|---|---|---|
| 小型 | ~70M | 384 | 24 | 6 |
| 中型 | ~130M | 512 | 36 | 8 |
| 大型 | ~300M | 768 | 36 | 12 |
训练完成(实验采用第一种默认配置)后,可以得到:
-
训练曲线
- 训练和验证困惑度变化
ckpt/train_ppl.png、ckpt/val_ppl.png
- 训练和验证困惑度变化
-
缩放定律拟合
- 根据已有的实验数据分析每一组FLOPs理想最优的(N, D),拟合得到理想最优的loss-FLOPs曲线
optimal_curve_70M.png - 在固定的有效模型参数(非嵌入参数),以及实验数据得到loss-FLOPs的拟合曲线
fix_N_70M.png
- 根据已有的实验数据分析每一组FLOPs理想最优的(N, D),拟合得到理想最优的loss-FLOPs曲线
-
缩放数据记录
- 包含step、tokens、FLOPs、损失的详细记录
ckpt/scaling_records_70M.npy
- 包含step、tokens、FLOPs、损失的详细记录
-
IsoFLOPs分析
- 计算预算与最优数据集大小的关系
Scaling Law.png
- 计算预算与最优数据集大小的关系
FLOPs ≈ 6 × N × D
其中N为非嵌入参数量,D为训练token数量。
基于DeepMind的研究,模型损失与参数量、数据量的关系为:
L(N,D) = E + A × N^(-α) + B × D^(-β)
- 内存管理: 项目包含内存监控功能,建议在GPU环境下运行大模型
- 数据格式: 训练数据使用int32格式的二进制文件
- 检查点: 每个epoch自动保存检查点,支持训练中断恢复
- 可视化: 所有训练曲线和分析结果自动保存为PNG格式
- 支持自定义tokenizer
- 可配置的dropout和学习率策略
- 灵活的数据集划分
- 详细的训练日志和性能监控
Q1:对于缩放定律所有大小的模型都适用吗?
A1:缩放定律的适用性取决于模型是否处于可缩放区间。当模型参数规模过小(例如低于约50M参数,具体阈值依赖于模型架构与任务复杂度)时,模型通常处于容量受限状态,此时即使持续增加训练数据或计算量,训练损失的下降幅度也较为有限,难以呈现稳定的幂律关系。 OpenAI或者DeepMind提出的缩放定律均建立在以下前提之上:模型规模或训练资源的增加能够带来显著且连续的性能提升。因此,只有当模型进入可观测的可缩放区间时,loss与模型规模(或FLOPs、数据量)之间的幂律关系才具有统计意义。 基于上述考虑,在本实验设置中选取约70M参数规模的模型作为最小实验模型,以尽量避免容量受限效应的干扰,确保模型行为处于可缩放区间,从而提高缩放定律拟合结果的稳定性与可信度。
在固定的模型架构、训练目标前提下,只要不同规模模型均处于可缩放区间,并在相同数据分布上训练至充分收敛,其训练损失或验证损失通常可以用统一的缩放定律进行拟合。
Q2:如何确定最优学习率?
A2:关于最优学习率的确定,
因此,
$μP$ 使得最优学习率在模型规模扩展过程中近似保持不变,从而显著降低了跨模型规模调参的成本。
Q3:如何确定深宽比、高宽比以及Attention计算的隐藏维度空间?
A3: 关于前馈网络宽度与模型嵌入维度之比(
在具体结构设计中,上述比例仍需保持在合理范围内,以避免对模型表达能力与训练稳定性产生不利影响:
- 嵌入维度(Embedding Size)过小会形成信息瓶颈,限制输入token语义特征的表达能力;嵌入维度过大则可能导致特征表示过于分散,降低注意力机制在有限计算预算下的建模效率;
- 注意力层数过少会限制模型对长程上下文信息的逐层整合能力,使高层语义表征不充分;注意力层数过多则可能增加优化难度,降低训练稳定性,并在数据规模受限时引入过拟合风险;
- 前馈网络维度过小会限制非线性变换的表达能力,从而削弱模型对复杂语义模式的建模;前馈网络维度过大则可能显著增加参数冗余,出现收益递减现象,使模型更易出现收敛缓慢或过拟合等问题。
因此,在固定总参数量且模型处于可缩放区间的前提下,已有研究表明模型性能对深宽比、高宽比以及注意力头维度的变化通常呈现二阶敏感性,其影响程度显著小于模型规模与训练计算量等一阶因素。
Q4:如何确定多头注意力机制的数量?
A4:关于多头注意力机制中注意力头数量的确定,其核心作用在于提升模型对不同子空间关系的建模能力。 在多头注意力机制中,不同注意力头采用彼此独立的Q、K、V投影参数,对同一输入表示进行并行建模,从而使模型能够在不同表示子空间中关注不同类型的依赖关系。 在固定embedding维度的前提下,注意力头数量的增加并不会显著改变模型的总参数规模,而是将表示空间划分为更多低维子空间。然而,注意力头数量并非越多越好:
- 头数过少时,模型对不同关系模式的建模能力受限;
- 头数过多时,则会导致单个注意力头的维度过小,削弱其表示能力,并可能降低注意力计算的有效性。 在固定embedding维度的前提下,注意力头数量通常选择在8-16的范围内,以保证单个注意力头具有足够的表示维度(通常不少于32-128),从而在多样化关系建模能力与计算稳定性之间取得平衡。
多头注意力并不是增加容量,而是重新组织的容量。
Q5:为什么在缩放注意力中注意力头数基本变化不大?
A5:在缩放注意力机制的实验中,注意力头数通常保持相对固定,其原因在于多头注意力主要影响表示结构而非模型容量。 在固定embedding维度(d_model)的前提下,增加注意力头数量并不会显著提升模型的表达能力,而是将原有表示空间划分为更多低维子空间。
当注意力头数量过多时,单个注意力头的维度(d_model // num_head)随之减小,削弱了其建模能力,同时不同注意力头之间容易产生高度冗余的注意力模式。
此外,过多的注意力头还会带来额外的计算与数值稳定性开销,其性能收益往往呈现明显的递减趋势。
因此,在缩放实验中通常保持注意力头数量不变,以减少结构性因素对模型性能的干扰,使实验结果更专注于规模变量本身。
Q6:如何确定处理批次的数量?
A6:关于训练批次大小的确定,首先受限于GPU显存容量或则CPU内存,应在显存可承受范围内选择尽可能大的batch size。 在此基础上,可通过梯度累积构建更大的有效批次规模,以提升训练过程的数值稳定性。 然而,批次规模并非越大越好。尽管较大的batch size能降低梯度方差、使训练过程更加平稳,但在超过一定范围后,其对模型最终性能的提升会出现明显的收益递减,甚至可能影响泛化能力。因此,在实际训练中,通常在稳定性与泛化性能之间选择一个折中范围,并与学习率设置协同调整。
📌 常见经验范围(LLM)
- 小模型或单机实验:有效批次规模通常在几十至数千的范围内;
- 中大型LLM:有效批次规模通常在8k~64k之间。
当有效批次规模超过上述范围后,模型训练稳定性虽可能继续提升,但对最终性能的改善往往呈现明显的收益递减。
Q7:如何选择epoch大小?
A7:关于训练轮数的选择,其本质在于控制模型在固定数据集上对样本的重复暴露次数。 在训练初期,随着epoch的增加,模型能够更充分地利用数据分布信息,性能通常持续提升。已有研究表明,在随机梯度优化过程中即使重复使用同一数据样本,其在不同训练阶段所产生的梯度更新效果并不完全等价,因此适度增加训练轮数仍可带来性能增益。 然而,当epoch数量超过一定范围后,模型对训练数据的拟合程度不断加深,梯度多样性逐渐下降,性能提升趋于饱和,甚至可能出现过拟合或泛化能力下降的问题。因此,在实际训练中,通常通过验证集性能或损失收敛情况,选择位于性能提升与收益递减之间的合适epoch数。
💡不同规模和容量的模型,其最优训练epoch通常不同,因此epoch往往需要在训练过程中通过验证集性能动态确定。 在训练过程中,当模型在
验证集上的loss下降趋势逐渐趋于平缓,或在继续训练时出现上升,而验证指标不再提升甚至下降时,说明模型已开始过拟合,此时对应的epoch可视为该模型在当前设置下的最优训练轮数。