作者:Lil2J@知乎(已授权)
链接:https://zhuanlan.zhihu.com/p/684946331
简介
这篇文章主要记录了我个人对1.4b中文大模型的实践复现过程。我选择了QWEN作为基座模型,并训练了一个参数量达到1.4b的预训练模型,其中涉及的训练token数量约为8b。在此过程中,我使用了两张a100 80g显卡,并耗费了大约100个小时的训练时间。尽管这个规模无法与其他大型模型相媲美,但我也取得了一定的效果。因此,我想将这次实践的过程和技术细节分享给大家,希望能够为感兴趣的朋友们提供一些参考和帮助。
这次项目:
https://github.com/jiahe7ay/MINI_LLM
因为实在不知道取什么名字,就取了个miniLLM了。
动机
我做这次实践纪录的动机主要如下:
1.在阅读了Allen实验室发布的关于OLMO模型的技术报告后,我深受启发。他们不仅详细介绍了模型的训练细节,还大方地开源了整套大模型构建的代码,涵盖了数据处理、训练、评估等各个环节。这种开源精神让我深感敬佩,我也想为开源尽自己的一份薄力。
2.其实自己尝试预训练一个大模型,这是我一直以来都非常想尝试做的事情,由于对自己的能力和所拥有的资源持有疑虑,我一直未能付诸实践。然而,机缘巧合之下,我在github上看到了这两个项目:
1.baby-llama2-chinese
2.Phi2-mini-Chinese
在深入研究了上述两个项目后,我深受启发,意识到个人尝试复现一个大模型是完全可行的。因此,我决定基于这两个项目的成功经验,结合我自身的需求,更换它们所使用的模型和训练数据集。通过这一尝试,我希望能够训练出属于我自己的“大模型”。通过这样的实践,我能够更深入地理解预训练模型的工作原理,并为我未来的研究和工作积累宝贵的经验。
3.随着技术的不断发展和资源的日益丰富,越来越多的大厂开始关注参数量相对较小的大模型,如qwen-0.5b和phi-2b等。这一趋势让我意识到,在未来,参数量相对较小的大模型可能会在大模型领域中占据一席之地。
相较于庞大的模型,参数量相对较小的大模型具有更低的计算需求、更快的训练速度和更少的资源消耗。这使得更多的个人和团队能够参与到大模型的训练和应用中,推动了技术的普及和发展。同时,这类模型也在某些特定任务上展现出了不俗的性能,证明了其在实际应用中的价值。
总的来说,我个人非常看好参数量相对较小的大模型的发展前景。
细节
模型基座的选择
在我的项目中,我选择了QWEN作为基座模型,并将其配置扩展至1.4b参数规模。具体的修改细节,您可以在我的项目中的模型config文件中找到。我主要调整了模型的注意力头数和层数,以适应我现有的算力资源。当然,每位研究者都可以根据自己的需求和资源情况来进行相应的调整。
选择QWEN模型的原因在于,我认为它是一个成熟且稳定的中文开源大模型。此外,我注意到其他大模型复现项目通常都会自行训练tokenizer,但考虑到我个人并不希望在这一步骤上花费过多时间和精力,我决定从现有的开源项目中选取一个tokenizer。经过对比不同项目的tokenizer的压缩率和训练规模,我发现QWEN的tokenizer表现优异,因此我最终选择使用它,并决定整个模型也采用QWEN。毕竟,我的目标主要是进行复现实践,而非创造出一个新的模型。
训练数据的选择
1.wikipedia-cn-20230720-filtered:本数据集基于中文维基2023年7月20日的dump存档。作为一项以数据为中心的工作,本数据集仅保留了质量较254,547条高的词条内容。
2.中文BaiduBaiKe的数据
3.天工150b中文预训练数据集:因为算力资源限制,我只下载了前20个文件
4.bell数据集:使用了这数据集中的2M,0.5M和1M作为sft的训练数据集
训练集构造
在数据预处理阶段,我遵循了QWEN的通用做法,即在每个文章的末尾加上一个特定的结束符号“<|im_end|>”。这个符号的作用在于清晰地界定单篇文章的边界,确保模型在训练时能够准确地识别出文章的结束位置,从而与下一个文章进行区分。
此外,考虑到模型训练对输入序列长度的限制,如果文章长度超过了这一限制,我会进行截断处理。具体来说,我会将超长的文章截断至规定长度,并将截断的部分作为下一个样本。这样做不仅保证了模型输入的合规性,同时也充分利用了原始数据,避免了信息的浪费。
这种数据预处理方式既遵循了行业内的常见做法,又考虑到了模型训练的实际需求,有助于提升模型的训练效果和泛化能力。
环境
如果安装了flash-attn的话,训练速度大概能提升20%,但是我发现这个flash-attn越来越不好装了。
具体的requirements如下:
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,save_steps=50,save_strategy='steps',save_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,save_steps=500,save_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,
值得注意一点是:
1.多卡的话最好设置ddp_find_unused_parameters=False,这样也能提升训练的速度
2.尽可能地把单个bs调大,因为我试过把单个bs没有调那么大,通过使用累积梯度步数来增大总的bs,但是收敛的没有把单个bs调大快,我在想这个和学习率调整有关。
训练加速
使用了accelerate库来使用deepspeed来加速,只需要运行 accelerate launch --multi_gpu --config_file accelerate_multi_gpu.yaml xx.py即可
具体的配置如下:
compute_environment: LOCAL_MACHINE
debug: false
deepspeed_config:gradient_accumulation_steps: 10gradient_clipping: 1.0offload_optimizer_device: cpuoffload_param_device: cpuzero3_init_flag: falsezero3_save_16bit_model: falsezero_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.尝试写一下文案
这一部分倒是写的有模有样的。
总结
这次实践从准备到完成大约花费了我一个月的时间。虽然整个项目的代码量并不算大,但其中的过程确实充满了挑战和艰辛。作为一个独立完成的项目,我遇到了各种各样的问题和报错,需要逐一排查和解决。这其中的艰辛,只有真正做过的人才能深刻体会到。
然而,正是这些挑战和困难,让我更加深刻地体会到了做模型的乐趣。每一次解决问题,每一次看到模型性能的提升,都像是在玩游戏一样,带给我独特的成就感和快感。这种乐趣不仅来自于技术的挑战,更来自于对知识的探索和应用的满足感。
未来,我还会继续尝试训练更多的模型和数据,希望能够为中文大模型的开源社区贡献自己的一份力量。
最后,我要感谢所有能够看到这里的朋友们。谢谢你们!
QA
Q:为什么没有使用强化学习
A:其实我是有试过DPO去做强化学习的,但是效果不甚理想,知识遗忘的会比较厉害,不知道是我训练数据的问题还是方法的问题,但是后面会继续尝试强化学习去优化模型。
Q:项目的后续是?
A:有可能会继续增加数据规模继续训练,或者转向尝试训练一个Moe模型
Q:为什么没有尝试C-EVAL的评测
A:我现在觉得这个模型还是太弱鸡了,等后续能力强一点再去尝试一下。
备注:昵称-学校/公司-方向/会议(eg.ACL),进入技术/投稿群
id:DLNLPer,记得备注呦