复现 GPT-2 权重时千万别掉进位置编码初始化的坑里
最近在死磕 OpenAI GPT-2 权重复现,结果在验证阶段遇到了一个极其诡异的问题:所有的超参数、训练曲线以及数据预处理流程全部对齐,但跑出来的指标始终比原版差一截,且无法在训练后期通过增加步数来弥补。这种感觉就像是方向盘偏移了 1 度,虽然起步没区别,但跑几公里后就彻底跑偏了。
为了定位问题,我没有在训练日志里死磕,而是决定采取最原始的“暴力拆解法”——把 checkpoint 的权重逐层 dump 出来,直接与原版权重进行数值对比。结果让我大跌眼镜,问题竟然出在最基础的位置编码(Positional Embedding)初始化方式上。
在我的实现版本中,为了追求所谓的“学术规范”,我手写了一个 nn.Parameter 并采用了经典的 Xavier 初始化。而 OpenAI 官方的实现其实非常简单粗暴,直接使用了 nn.Embedding 的默认初始化(即在 $[-1, 1]$ 之间进行均匀分布采样)。从理论上看,这两种初始化方式在均值和方差上差异极小,但在 Transformer 这种深层网络中,微小的初始偏差会被自注意力机制(Self-Attention)层层放大。
排查过程其实非常枯燥且反直觉。起初我以为是学习率衰减的问题,但对比后发现不对劲。我尝试画了每一层的输出分布图,惊讶地发现我的实现版本在 Attention Logits 上的方差比官方版本高出了 15% 左右。这个数字虽然不大,但它直接影响了 Softmax 后的概率分布,导致模型在处理长序列时的注意力权重分布过于尖锐,从而影响了收敛方向。
当我意识到是初始化范围的问题后,迅速将 nn.Parameter 替换回 nn.Embedding 的默认逻辑,并重新训练了 30k 步。结果非常令人欣慰,验证集的 Loss 曲线直接与官方曲线重合,之前那些怎么调都对不齐的指标瞬间对齐了。
这次经历给我最大的启示是:在做权重迁移或模型复现时,绝对不能过度信任自己的“标准实现”。很多时候,所谓的标准实现(如 Xavier 或 Kaiming 初始化)反而成了干扰项,因为原作者在写代码时可能随手用了一个默认值,而这个默认值恰恰是模型能够收敛的关键。
如果你现在也在复现 GPT-2 或者类似的 Transformer 架构,我建议你不要在训练脚本里浪费太多时间,而是直接采取以下策略:第一,直接 dump 官方权重做初始化校验,把每一层的 Tensor 形状和数值分布对一遍;第二,重点检查 Embedding 层和 Layer Norm 层的初始化范围,这两个地方最容易隐藏这种“软 Bug”。
最后分享一个实操经验:不要等到训练结束才去验证指标。建议在训练完第一个 epoch 后,就随机抽取几个样本,让模型的采样输出与官方结果做对比。如果输出的概率分布出现明显偏移,大概率就是初始化或预处理环节出了问题,越早发现越省时间。
用正态分布初始化位置编码简直是自寻死路,指标直接崩掉,太离谱了