中文大模型预训练实战:1.4B参数从零开始
这篇文章详细记录了从零起步完整复现1.4B参数中文大语言模型的全流程。基座模型采用QWEN系列,训练数据总量约8B token,算力配置为两张A100 80G显卡,累计训练时长约100小时。虽然规模远不及数百亿甚至千亿参数的大模型,但最终效果基本达到预期。整个复现过程中踩了不少坑,也沉淀了一些可直接复用的实战经验,整理出来供有类似需求的团队参考。
项目代码仓库地址:https://github.com/jiahe7ay/MINI_LLM ——名字实在没想好,索性就叫miniLLM了。
动机
决定做这件事主要基于三个考量。
第一,Allen实验室发布的OLMO技术报告让人印象深刻。他们不仅细致公开了训练细节,还把从数据处理、模型训练到评估的完整代码全部开源。这种透明度值得学习,也希望能把自己踩过的坑和积累的方法分享出来。
第二,说实话,一直想亲自预训练一个大模型,但又总是犹豫——既担心能力不足,也怕算力不够。直到在GitHub上发现两个项目:baby-llama2-chinese 和 Phi2-mini-Chinese。深入研究后发现,个人复现大模型并没有想象中那么遥不可及。于是决定参考这两个项目的成功经验,换用自选的模型架构和训练数据集,尝试跑通一套自己的“大模型”。过程虽然折腾,但相比只看论文,这种动手实践对理解预训练机制带来的收获要大得多。
第三,一个明显的行业趋势是:越来越多的厂商开始关注小参数模型,比如Qwen-0.5B、Phi-2B。这说明小参数模型在大模型生态中逐渐找到了自己的位置。相比参数量庞大的模型,小模型计算需求低、训练速度快、资源占用少,更多个人和团队有能力参与进来。而且这类模型在不少具体任务上表现也不差,实际应用价值正在被验证。从长远角度看,小参数模型这条路线值得持续投入。
细节
模型基座的选择
项目选用QWEN作为基座,通过调整注意力头数和层数将参数量扩展到1.4B。具体配置改动在项目代码的config文件中均可查见,这里不赘述。选QWEN的主要原因是它在中文开源大模型中成熟度和稳定性都较高。另外也注意到,不少个人复现项目会自训练tokenizer,但这一步确实耗时费力。权衡后决定直接复用现成的开源tokenizer。对比了几个主流tokenizer的压缩率和训练规模后,QWEN的tokenizer表现突出,于是连整个模型架构也一并采用QWEN。毕竟核心目标是复现实践,而非从零创造全新模型。
训练数据的选择
本次训练主要使用了以下数据:
- wikipedia-cn-20230720-filtered:基于2023年7月20日的中文维基dump,只保留了质量较高的约25万条条目。
- 中文BaiduBaike数据
- 天工150B中文预训练数据集:受限于算力,仅下载了前20个文件。
- Bell数据集:分别使用了其中的2M、0.5M和1M子集作为SFT训练数据。
训练集构造
数据预处理遵循QWEN的通用办法:每篇文章末尾添加专用的结束符 <|im_end|>,清晰界定单篇边界,确保模型训练时能准确区分不同样本。对于超出输入序列长度限制的文章,直接截断到规定长度,截断部分作为下一个独立样本。这样既保证了输入合规性,又避免浪费原始数据。
环境
如果能装上flash-attn,训练速度大概可提升20%。但实话讲,这组件现在越来越难装了。具体依赖版本如下:
datasets
transformers==4.36.0
torch==2.2.0
accelerate==0.27.2
einops==0.7.0
flash-attn==2.5.5
tiktoken
einops
训练参数
预训练参数配置如下:
per_device_train_batch_size=24,
per_device_eval_batch_size=4,
gradient_accumulation_steps=10,
num_train_epochs=1,
weight_decay=0.1,
ddp_find_unused_parameters=False,
warmup_steps=0,
learning_rate=1e-4,
evaluation_strategy='steps',
eval_steps=100,
sa ve_steps=50,
sa ve_strategy='steps',
sa ve_total_limit=4,
report_to='tensorboard',
optim="adamw_torch",
lr_scheduler_type='cosine',
bf16=True,
logging_steps=20,
log_level='info',
logging_first_step=True,
SFT阶段的训练参数:
per_device_train_batch_size=32,
gradient_accumulation_steps=2,
num_train_epochs=3,
weight_decay=0.1,
warmup_steps=0,
learning_rate=6e-5,
ddp_find_unused_parameters=False,
evaluation_strategy='steps',
eval_steps=500,
sa ve_steps=500,
sa ve_total_limit=3,
report_to='tensorboard',
optim="adamw_torch",
remove_unused_columns=False,
lr_scheduler_type='cosine',
bf16=True,
logging_steps=10,
log_level='info',
logging_first_step=True,
两个关键注意点:
- 多卡训练时建议将
ddp_find_unused_parameters设为 False,能明显提升训练速度。 - 尽量调大单卡的batch size。尝试过用较小的单卡bs搭配较大的梯度累积步数凑总bs,但收敛速度明显不如直接放大单卡bs。推测与学习率调度机制有关。
训练加速
使用accelerate库配合deepspeed实现多卡加速,启动命令是 accelerate launch --multi_gpu --config_file accelerate_multi_gpu.yaml xx.py。deepspeed配置文件如下:
compute_environment: LOCAL_MACHINE
debug: false
deepspeed_config:
gradient_accumulation_steps: 10
gradient_clipping: 1.0
offload_optimizer_device: cpu
offload_param_device: cpu
zero3_init_flag: false
zero3_sa ve_16bit_model: false
zero_stage: 2
distributed_type: DEEPSPEED
downcast_bf16: 'no'
machine_rank: 0
main_training_function: main
mixed_precision: bf16
num_machines: 1
num_processes: 2
rdzv_backend: static
same_network: true
tpu_env: []
tpu_use_cluster: false
tpu_use_sudo: false
use_cpu: false
启动训练
(此部分原文无具体解释性内容,保留标题。)
训练结果
预训练loss
先用维基百科加百度百科的数据进行第一轮预训练,保存好checkpoint权重,再基于该权重继续训练。
SFT训练loss
(此部分原文无具体描述。)
从上面这几张Loss图可以清楚看出,大模型训练确实遵循scaling law——曲线走势基本一致。这次亲手实践后,对scaling law的理解又加深了一层。
模型效果
1. 简单的问好
(此部分原文内容为空。)
2. 一些通识类的问题
比如关于乔布斯的问题,基本能答对80%。但存在典型幻觉:模型说乔布斯是在攻读博士期间开始创业的,这明显不符合事实。苹果公司相关问题回答基本正确。不过介绍广州时出现了重复回答,同一个句子中“广州塔”出现了两次。
3. 模型有分辨问题并进行准确回答的能力
测试了四个问题,全部回答正确。
4. 尝试使用它来写一下代码
先让它写一个排序算法,它用文字给出了思路。乍一看是冒泡排序,但仔细看第2步描述——“继续比较下一个值和最后一个值”——这个“最后一个值”明显多余,差一点才能完全正确。然后让它用Python写排序算法,结果直接变成了调包侠。不过注释部分仍存在问题:第一条注释说“将排序后的结果储存在新的列表中”,但代码里并未这样做;第二条注释中的列表名也写错了。
5. 尝试写一下文案
这部分表现倒是有模有样,基本符合预期。
总结
这次实践从准备到完成大约花了一个月时间。整个项目代码量不大,但过程确实充满折腾。作为一个独立项目,各种报错和问题层出不穷,只能逐一排查、解决。其中的艰辛,只有真正动手做过的人才能体会。
不过,正是这些挑战让做模型的乐趣变得格外真实。每解决一个bug、每看到模型性能往上跳一点,那种成就感跟通关一个好游戏差不多。这种乐趣不仅来自技术本身的挑战,更来自对知识探索和实际应用的满足感。
后续还会继续尝试训练更多的模型和数据,希望能为中文大模型的开源社区多贡献一点力量。
最后,感谢所有能看到这里的朋友。
QA
Q:为什么没有使用强化学习?
A:其实试过DPO来做强化学习,但效果不太理想,知识遗忘得比较厉害。不确定是训练数据的问题还是方法本身的问题,后续会继续尝试用强化学习来优化模型。
Q:项目的后续计划是什么?
A:有可能会继续增加数据规模接着训练,或者转向尝试训练一个MoE(混合专家)模型。
Q:为什么没有用C-EVAL做评测?
A:坦白说,目前这个模型能力还比较弱,等后续能力提升之后再去做更有挑战性的评测。