1. Pretrain
1.1. 数据篇
1.1.1. 数据爬取
数据规模:10T 左右的训练数据。也可以少找一些,在训练的同时,不断去收集更多的新数据。
高质量数据,如 PDF 格式的论文数据等,最好是调用效果较好的 pdf 服务。 若自己训练一个 OCR 模型,要有足够高质量的 pdf-text 对齐数据。
准备数据还要懂得一个基础概念:数据的知识密度是有差异的。“唐诗三百首”的知识量要远远大于“中国新闻网的三百篇新闻”。 最近,一种新的数据趋势是“合成高知识密度数据”,把几千字的新闻概括成几百字喂给模型,四舍五入也等于训练速度提高了十倍。
1.1.2. 数据清洗
利用模型对 pretrain 数据的质量进行打分,成了数据清洗工作的标配,llama3
、qwen2
的技术报告都有提及。
基本上大家都认同:同等 size 下,BERT 结构的模型表征能力是强于 transformer-decoder
模型的,打分模型最好还是从 BERT 家族中选一个来训。
至于训练数据怎么准备,可以让 GPT4 标注一下,或者是利用“某个源的数据是高质量数据,某个源的数据是低质量数据”这种规则生产一些。 特别强调,任何打分器,都会给 code, markdown, latex 等格式数据打很低的分数。
训打分器这个工作的难点是不要执着于 100% 的准确率,能用就行,要花一个月时间来训打分器,那还不如没有打分器。 此外,要学会变通,有 32k 的语料不代表要训 32k 的打分器,训个 4k 就差不多了,不需要纠结“前 4k 低质量,后 32k 高质量”这种特殊情况。
打分器结果知识众多数据特征中的一个特征,不一定要完全依赖它来洗数据,可以和其他特征结合使用。 数据清晰的另一个大杀器:规则。
数据长度是否少于某个值,数据中某个 token 的比例超过某个阈值,数据的 zh 占比、en 占比、数字占比, 数据是否有“http”字段,数据是否包含了“新冠”、“疫情”等低质量关键词,数据是否包含某些反动词汇, 数据是否包含黄色字眼,等等。
要注意,用规则清晰或过滤数据时,不要把数据搞成分布有偏的数据。 比如,你觉着“包含网址的数据质量低,而网址的英文占比高”,所以你把英文占比高的数据都去掉了。
数据脱敏也是数据清洗环节必须要做的工作。 要尽可能把训练数据中涉及到的人名、电话号码、邮箱等剔除出去。 更广义的,把数据的“转载自......”删掉,黄色信息、反动信息,references 等剔除出去,都可以视作数据脱敏工作的一部分。
1.1.3. 数据去重
如何去重?要有一个大数据处理集群,hadoop、spark,只要是一个 map/reduce 框架就可以。 然后,实现一个简单的 minhash 代码。
数据去重工作的一个重要意识:要先确定需要多少训练数据,再确定去重的粒度。 例如,需要 10T 训练数据,就卡相似度在 80% 的阈值进行去重;需要 5T 的训练数据,就卡相似度在 90% 的阈值进行去重。以此类推。
目前没有任何工作能证明,一条数据在 pretrain 阶段训多少遍对模型是最友好的。 即使去重粒度小,导致一篇文章出现多次,也可以通过让两篇相似文档之间隔尽可能多的 token 来降低影响。
1.1.4. 数据配比
前面提到,在数据清洗的时候把 code 等格式化数据摘出来。 具体就是训练一个数据分类器,对每一个 document 进行类别判断,把数据分成新闻、百科、代码、markdown等类目即可。 分类器模型依然可以选择使用 BERT 家族。
大部分的技术报告里的配比基本上都是“知识+代码+逻辑”三大类目,知识数据分为多语言知识,逻辑数据可以认为是 math 数据和 cot 数据的混合体。
1.1.5. 数据顺序
1.1.6. 数据流水线
1.1.7. 数据实验
上述所有环节串起来后,先在小模型上做好实验,把 scaling_law
的这个理念先搞懂。
具体的实验内容,可以根据自己的时间、人力来三个阶段走:
- 粗糙一点:在小模型上起多个数据配比、数据顺序,训练 500B 左右的数据量,选择 loss 曲线最完美,或 loss 下降最低的那个模型;
- 专业一点:额外起多个 size 的小模型,跑出 loss 结果,结合 scaling_law 公式,推算大模型最适合的数据配比、学习率、训练 token 量等参数;
- 创新一点:像 llama 和 deepseek 技术报告里提到的,绘制出 loss 到 benchmark 的 scaling_law,提前预知模型训多少 token 能在某个 benchmark 达到什么样的能力。
1.2. 训练篇
1.2.1. Tokenzier
1.2.2. 模型结构
1.2.3. 模型参数
模型的 size
选模型 size 主要看两个因素,训练算力和推理算力:
- 训练算力:有多少机器、能做多久 pretrain,有多少训练数据,使用的训练框架一天能吃多少 token,需要提前确定。假设要在 2 个月后训完模型,目标训 2T 数据,就可以算出自己该训什么 size 的模型;
- 推理算力:敲定模型 size 的时候,需要知道自己的推理机器是什么,不要出现 1 张推理卡刚好装不下模型的尴尬现象。
模型的超参数 size
目前学界有一个共识:一个好模型是“身材匀称”的,也就是说标准的 llama 结构应该是横向和纵向成某种比例的,
所以增大模型 size 的时候,layer_num
和 hidden_size
要一起递增。
具体到该用什么值的参数,尽量能被 2 / 4 / 8 / 64 / 128 等数整除。 不是有什么理论证明,而是训练框架要求你这样。
- layer_num:
- num_head: 8 的整数倍,它与 tensor_parallel 有关。tensor_parallel 的极限一般是 8,因为大于 8 就引入了机间通讯,训练效率就低了。tensor_parallel 的效率很高,能开大就尽量开大一点;
- hidden_states: 128 的整数倍;
- vocab_size: 128 的整数倍.
另外一个比较重要的超参数是 seq_len 的选取:无论你的业务场景需不需要长文本,都不要一开始就使用 32K / 200K 这种夸张的数据 seq_len,算力根本承受不起
rope 的 NTK 外推方法已经是各大厂标配的方案:4K/8K + rope 小 base + 90% 数据量 --> 32K/64K + rope 大 base + 10% 数据量。
1.2.4. 训练框架
从零开始的 pretrain 必须选 megatron,continue-pretrain 可以考虑使用 deepspeed。 换句话说,T 级别的 token 训练量必须是 megatron,B 的级别 token 训练量无所谓。
megatron的优点
- 训练速度快:
tensor_parallel
和pipeline_parallel
的优化做得很好,rope
被开发成了 apex 算子; - 参数清晰而明确:
argument.py
里,你能看见上百个参数配置,哪些层使用 dropout 等细节,训练框架早就帮你考虑好了,可微操的空间很大。 - 启动训练的时候模型加载速度快,千亿级别模型一分钟加载完毕。
megatron的缺点
- 上手成本高,对 torch 的多机通讯需要从头开始学。基建工作较多。
- NVIDIA 的官方代码库,全是 bug。
DeepSpeed的优点
- 代码简单,有
model.trainer
这种框架; - 用户群体多,alignment 阶段的开源代码 90% 都是 DeepSpeed 实现的。
DeepSpeed的缺点
- 训练速度慢,相比于 megatron 可能有 10% ~30% 左右的算力损失。现在有很多工作已经实现了 deepspeed 的 pipeline_parallel 和 tensor_parallel,但相对的,用起来也就没那么简单了。
- 加载速度很慢。从 bash train.sh 到 看见 loss 出现,小模型得十几分钟,大模型得半个小时,如果你需要 debug,一个下午只够 debug 个三五次。
- 代码简单的代价是“微操很难”,尤其是使用 model.trainer 训练,对源码几乎无修改空间。
- megatron 有 bug 只是让你跑不起来,你修了就好了。deepspeed 的 bug 或者不合理的设置,并不影响你把代码跑起来,只有在你遇见解释不了的现象的时候才可能暴露。
无论是用哪个训练框架,都要记得把 attention 的默认方式换成 flash_attention。
1.2.5. 训练技巧
训练效率优化
能减小模型的通讯量就去减小,能避免机间通讯就去避免。
在显存够用的情况下,能不引入 tensor_parallel
,pipeline_parallel
,sequence_parallel
就不要去引入,同样地、能不开 offload 就不要开 offload,能不开重算就不开重算。
总结下来就是:
data_parallel
几乎是唯一不牺牲算力的并行方式;- 让数据在显存和内存之间切换,也很耗时。
训练 Loss 分析
记住几条:
- 要有
channel_loss
, 至少中文知识、英文知识、代码三类数据的 loss 要分开观察; - loss_spike 不可忽视,虽然目前还没有工作去证明说 loss_spike 会对模型造成不可逆的效果伤害,但不出现 loss_spike 总是好的啊。无论是 loss 突然激增还是突然激减,都要重点留意,大概率是数据问题(脏数据不仅 loss 高,也会 loss 低,全是乱码的数据 loss 很高,全是换行符的数据 loss 很低)。如果数据过脏,就回退到上个 checkpoint 进行训练;
- 即使数据没有问题,也是有可能出现 loss_spike 的,这时候要留意下数据的 grad 有没有异常,大概率和优化器有关,参考 llama 等的技术报告,好好调整 adamw 优化器的 这两个参数,基本上能解决大部分训练初期的 loss_spkie 问题。一旦熬过了训练初期,loss 平稳下降到了 2 ~3 左右,后续的训练也基本不会再有幺蛾子了。
训练流程
当训练数据和训练代码都准备就绪后,按照以下四个流程来设置学习率和超参数:
- 开局 warmup,学习率缓慢上升到最大;
- 中间 cos/cos_decay/constant/constant_decay, 学习率比较大,是否 decay 自己决定,多翻翻别人的技术报告,或者在小模型上做实验决定;
- 后期改变 rope base + seq_len, 开始让模型适应长文本;
收尾 anneal,用高精数据、IFT数据等来给强化模型的考试能力,做完退火就该上考场刷 benchmark 了。
一般 pretrain 只要启动了,大家就可以去做别的工作了,只要模型训练没停,一般都不需要再有人为干预。
- 烧卡了, loss 爆炸了,loss陡然下降了(数据大概率重复了),就回退到上一个 checkpoint 重启。
1.3. 评估篇
1.3.1. PPL
看测试集的 loss 来衡量模型效果。
准备一些百科、逻辑、code 等数据,每天观察模型在这些测试集合上的 loss 表现,偶尔浮动是正常的,但整体趋势肯定是持续下降,最后趋于稳定的。 只要训练集中没这些语料,和训练曲线应该是基本一致的。
另一方面,不管你的 tokenizer 压缩率多少,在通用知识测试集上的 loss 也是应该能降低到 2 以下的,否则就说明有点训练不充分。