普通人从0开始训练LLM大语言模型
- 首先声明。本项目基于开源项目:MiniMind,基于个人的理解和踩坑。做了个人的理解和注释。完成从小白开始,从0训练一个LLM大语言模型。项目源地址:https://github.com/jingyaogong/minimind
- MiniMind极其轻量,力求做到最普通的个人GPU也可快速推理甚至训练。
- MiniMind发布了大模型极简结构,数据集清洗和预处理、监督预训练(Pretrain)、有监督指令微调(SFT)、低秩自适应(LoRA) 微调,无奖励强化学习直接偏好对齐(DPO)的全阶段代码,也包含拓展共享混合专家(MoE) 的稀疏模型。
- 这不仅是一个开源模型的实现,也是入门大语言模型(LLM)的教程。希望此项目能为研究者提供一个抛砖引玉的入门示例,帮助大家快速上手并对LLM领域产生更多的探索与创新。
前言
大语言模型(LLM)领域,如 GPT、LLaMA、GLM 等,虽然它们效果惊艳, 但动辄10 Bilion庞大的模型参数个人设备显存远不够训练,甚至推理困难。 几乎所有人都不会只满足于用Lora等方案fine-tuing大模型学会一些新的指令, 这约等于在教牛顿玩21世纪的智能手机,然而,这远远脱离了学习物理本身的奥妙。 此外,卖课付费订阅的营销号漏洞百出的一知半解讲解AI的教程遍地, 让理解LLM的优质内容雪上加霜,严重阻碍了学习者。
因此,本项目的目标是把上手LLM的门槛无限降低, 直接从0开始训练一个极其轻量的语言模型。
Tip
(截至2025-2-8)MiniMind系列已完成了1个型号微小模型的预训练,最小仅需26M(0.02B),即可具备流畅的对话能力!
模型 (大小) | tokenizer长度 | 推理占用 | release | 主观评分(/100) |
---|---|---|---|---|
minimind-v1-small (26M) | 6400 | 0.5 GB | 2025.02.08 | 50’ |
该分析在具有Torch 2.1.2、CUDA 12.6 和 NVIDIA GeForce RTX 4060 Laptop GPU上进行。没错,是在本人的笔记本上进行的。
项目包含:
- 公开MiniMind模型代码(包含Dense和MoE模型)、Pretrain、SFT指令微调、LoRA微调、DPO偏好优化的全过程代码、数据集和来源。
- 兼容
transformers
、accelerate
、trl
、peft
等流行框架。 - 训练支持单机单卡、单机多卡(DDP、DeepSpeed)训练,使用wandb可视化训练流程。支持在任意位置停止,及在任意位置继续训练。
- 在Ceval数据集上进行模型测试的代码。
- 实现Openai-Api基本的chat接口,便于集成到第三方ChatUI使用(FastGPT、Open-WebUI等)。
希望此开源项目可以帮助LLM初学者快速入门!
基础环境
仅是我个人的软硬件环境配置,自行推理或训练,请自行酌情更改:
CPU: Intel(R) Core(TM) i7-13620H
内存:32 GB
显卡:NVIDIA GeForce RTX 4060 Laptop GPU
环境:python 3.10 + Torch 2.1.2 + DDP单机多卡训练
系统:win11专业工作站版本 + Anaconda3-2024.02-1-Windows-x86_64
初始化环境安装
本次推理和训练,均采用Anaconda3部署环境,环境要求:
# 初始化虚拟环境
conda create -n minimind-v1 python=3.10
# 激活环境
conda activate minimind-v1
# 配置conda环境为国内源,可自行更改
# 设置清华源
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/
# 部署torch
pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126
# 测试torch是否可用cuda
# python解释器下进行
import torch
print(torch.cuda.is_available())
cuda、python和操作系统是有严格的关联关系。torch如果不可用,请自行去https://pytorch.org/get-started/locally/ 下载对应的版本。
启动训练
1、克隆项目代码
git clone https://gitee.com/zhu-wanming/minimind-v1-small.git
2、环境安装
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
# 注意torch请根据自身设备情况,安装对应版本
3、下载数据集
-
git代码里不包含训练所需的数据集(太大了)。相关数据集我会在下面提供下载方式和对应说明,请自行挂魔法下载。数据集放到
./dataset
目录下MiniMind训练数据集 下载地址 【tokenizer训练集】 HuggingFace 【Pretrain数据】 Seq-Monkey官方 / HuggingFace 【SFT数据】 匠数大模型SFT数据集 【DPO数据】 Huggingface
对应下载的数据集信息如下:
【tokenizer训练集】 tokenizer_train.jsonl 大概980M
【Pretrain数据】 pretrain_data.csv 大概4.33G
【SFT数据】 sft_data_zh.jsonl 大概16G
【DPO数据】 ./dpo/* 所有文件大概72M
数据集下载完成后,我们可以开始训练任务了!
注意:关于训练模型的大小和精度,请在./model/LMConfig.py 中调整model的参数配置
这里仅需调整dim和n_layers和use_moe参数,分别是(512+8)或(768+16),对应于minimind-v1-small和minimind-v1
4、处理数据集
抽取sft_data_zh.jsonl sft数据集抽离qa到csv文件(例如pretrain数据提前进行token-encoder、sft数据集抽离qa到csv文件)
# 处理数据集
python data_process.py
# 抽取数据时间大概5分钟左右。在./dataset 目录下生成 sft_data_single.csv 文件。
5、预训练
对Pretrain数据 pretrain_data.csv 执行预训练,得到 pretrain_*.pth 作为预训练的输出权重
python 1-pretrain.py
# 因为本地显卡是笔记本型号,显存占用高且训练时间长,预训练时间大概9小时左右。训练完成会在./out 目录下生成pretrain_*.pth权重模型。
6、指令微调
执行指令微调,得到 full_sft_*.pth 作为指令微调的输出权重
python 3-full_sft.py
# 因为本地显卡是笔记本型号,显存占用高且微调时间长,微调时间大概9小时左右。微调完成会在./out 目录下生成full_sft_*.pth作为指令微调的输出权重。
7、执行lora微调(非必须)
python 4-lora_sft.py
# 本次训练旨在快速训练一个LLM大语言小模型,此步骤可不做。后续完全熟悉后可自行调整处理。
8、执行DPO人类偏好强化学习对齐(非必须)
python 5-dpo_train.py
# 本次训练旨在快速训练一个LLM大语言小模型,此步骤可不做。后续完全熟悉后可自行调整处理。
以上除了非必须步骤都完成后,专属于个人的LLM大语言小模型就已经训练完成了。接下来可以开始运行测试。
测试LLM训练模型
1、测试预训练模型的接龙效果
python 0-eval_pretrain.py
# 本次训练的LLM模型较小,执行推理测试时候,显存占用较低且速度极快。
2、连续对话测试
python 2-eval.py
3、启动网页聊天界面
# 需要python>=3.10,安装 pip install streamlit==1.27.2
streamlit run fast_inference.py
推理与导出
1、导出模型
python ./export_model.py
# 可以导出模型到transformers格式,推送到huggingface
2、API推理
python my_openai_api.py
# 完成了openai_api的聊天接口,方便将自己的模型接入第三方UI 例如fastgpt、OpenWebUI等
-
模型可用自己的训练模型,也可从Huggingface下载已经训练好了的模型权重文件
minimind (root dir) ├─minimind | ├── config.json | ├── generation_config.json | ├── LMConfig.py | ├── model.py | ├── pytorch_model.bin | ├── special_tokens_map.json | ├── tokenizer_config.json | ├── tokenizer.json
-
启动聊天服务端
python my_openai_api.py
-
测试服务接口
python chat_openai_api.py
-
API接口示例,兼容openai api格式
curl http://ip:port/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "model-identifier", "messages": [ { "role": "user", "content": "世界上最高的山是什么?" } ], "temperature": 0.7, "max_tokens": -1, "stream": true }'
评测
C-Eval评测代码见:./eval_ceval.py
小模型的测评通常为了避免回复格式的难以固定的特点, 而直接判断A
,B
,C
,D
四个字母对应token预测概率,取最大的作为回答答案,与标准答案计算正确率。 minimind模型本身没有使用较大的数据集训练,也没有针对回答选择题的指令做微调,测评结果可以当个参考。
例如minimind-small的结果细项:
Type | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Data | probability_and_statistics | law | middle_school_biology | high_school_chemistry | high_school_physics | legal_professional | high_school_chinese | high_school_history | tax_accountant | modern_chinese_history | middle_school_physics | middle_school_history | basic_medicine | operating_system | logic | electrical_engineer | civil_servant | chinese_language_and_literature | college_programming | accountant | plant_protection | middle_school_chemistry | metrology_engineer | veterinary_medicine | marxism | advanced_mathematics | high_school_mathematics | business_administration | mao_zedong_thought | ideological_and_moral_cultivation | college_economics | professional_tour_guide | environmental_impact_assessment_engineer | computer_architecture | urban_and_rural_planner | college_physics | middle_school_mathematics | high_school_politics | physician | college_chemistry | high_school_biology | high_school_geography | middle_school_politics | clinical_medicine | computer_network | sports_science | art_studies | teacher_qualification | discrete_mathematics | education_science | fire_engineer | middle_school_geography |
Type | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
T/A | 3/18 | 5/24 | 4/21 | 7/19 | 5/19 | 2/23 | 4/19 | 6/20 | 10/49 | 4/23 | 4/19 | 4/22 | 1/19 | 3/19 | 4/22 | 7/37 | 11/47 | 5/23 | 10/37 | 9/49 | 7/22 | 4/20 | 3/24 | 6/23 | 5/19 | 5/19 | 4/18 | 8/33 | 8/24 | 5/19 | 17/55 | 10/29 | 7/31 | 6/21 | 11/46 | 5/19 | 3/19 | 4/19 | 13/49 | 3/24 | 5/19 | 4/19 | 6/21 | 6/22 | 2/19 | 2/19 | 14/33 | 12/44 | 6/16 | 7/29 | 9/31 | 1/12 |
Accuracy | 16.67% | 20.83% | 19.05% | 36.84% | 26.32% | 8.70% | 21.05% | 30.00% | 20.41% | 17.39% | 21.05% | 18.18% | 5.26% | 15.79% | 18.18% | 18.92% | 23.40% | 21.74% | 27.03% | 18.37% | 31.82% | 20.00% | 12.50% | 26.09% | 26.32% | 26.32% | 22.22% | 24.24% | 33.33% | 26.32% | 30.91% | 34.48% | 22.58% | 28.57% | 23.91% | 26.32% | 15.79% | 21.05% | 26.53% | 12.50% | 26.32% | 21.05% | 28.57% | 27.27% | 10.53% | 10.53% | 42.42% | 27.27% | 37.50% | 24.14% | 29.03% | 8.33% |
总题数: 1346
总正确数: 316
总正确率: 23.48%
结果汇总:
category | correct | question_count | accuracy |
---|---|---|---|
minimind-v1-small | 344 | 1346 | 25.56% |
minimind-v1 | 351 | 1346 | 26.08% |
验证
Model Name | params | len_vocab | batch_size | pretrain_time | sft_single_time | sft_multi_time |
---|---|---|---|---|---|---|
minimind-v1-small | 26M | 6400 | 64 | ≈2 hour (1 epoch) | ≈2 hour (1 epoch) | ≈0.5 hour (1 epoch) |
minimind-v1-moe | 4×26M | 6400 | 40 | ≈6 hour (1 epoch) | ≈5 hour (1 epoch) | ≈1 hour (1 epoch) |
minimind-v1 | 108M | 6400 | 16 | ≈6 hour (1 epoch) | ≈4 hour (1 epoch) | ≈1 hour (1 epoch) |
-
预训练(Text-to-Text):
- LLM首先要学习的并非直接与人交流,而是让肚子中充满知识的墨水,至于墨水理论上喝的越饱越好,产生大量的对世界的认知积累。
- 预训练就是让Model先埋头苦学大量基本的知识,例如从维基百科、新闻、常识、书籍等。
- 它无监督的从大量的文本数据中压缩知识到自己模型的权重,目的是:学会词语接龙。例如我们输入“秦始皇是”四个字,它在大量学习后能预测出下一句话大概率是“中国的第一位皇帝”。
pretrain的学习率设置为1e-4到1e-5的动态学习率,预训练epoch数设为5。
torchrun --nproc_per_node 2 1-pretrain.py
-
单轮次对话有监督微调(Single dialog Fine-tuning):
- 经过预训练,半成品LLM此时已经掌握了几乎所有的语言知识和百科常识。此时它还不会与人聊天,相反它只会无脑地进行输入词语的接龙,生成下一个词。
- 此时需要对半成品LLM做限制在聊天模板中进行微调,例如当它遇到这样的模板“<聊天开始>秦始皇是<聊天终止> ”后不再无脑接龙,而是意识到这是一段完整的对话结束。
- 我们称这个过程为指令微调,就如同让学富五车的「牛顿」先生适应21世纪的聊天习惯,学习屏幕左侧是对方消息,右侧是本人消息这个规律。
- 在训练时,MiniMind的指令和回答长度被截断在512,是为了节省显存空间。就像我们学习时,会先从短的文章开始,当学会阅读200字作文后,800字长文章就不需要再单独学习。
在推理时通过调整RoPE线性差值,实现长度外推到1024或2048及以上很方便。学习率设置为1e-5到1e-6的动态学习率,微调epoch数为6。
# 3-full_sft.py中设置数据集为sft_data_single.csv torchrun --nproc_per_node 2 3-full_sft.py
-
多轮对话微调(Multi dialog Fine-tuning):
- 在2的基础上,LLM已经学会一个问题->一个回答的聊天模板。此时仅需在具备历史问答的更长聊天模板上进一步微调即可。
- 我们仅需使用数据集的history_chat 字段,即历史对话,以及history_chat_response字段,即历史对话的回答。
- 构建【问题->回答,问题->回答,问题->】的新聊天模板,然后使用这个数据集进行微调。
- 学习完成的模型不仅仅只能回答当前问题,还能根据历史对话进行连贯的对话。
- 这一步 并非必须 ,因为小模型长上文对话能力很弱,强行对齐多轮问答模板会损失一定程度的单轮SFT效果。
学习率设置为1e-5到1e-6的动态学习率,微调epoch数为5。
# 3-full_sft.py中设置数据集为sft_data.csv torchrun --nproc_per_node 2 3-full_sft.py
-
人类反馈强化学习(RLHF)之-直接偏好优化(Direct Preference Optimization, DPO):
- 在前面的训练中,GPT已经具备了基本的对话能力,但是这样的能力完全基于单词接龙,缺少正例反例的激励。
- GPT尚且未知什么回答是好的,什么是差的。我们希望它能够更符合人的偏好,给出更让人满意的回答。
- 这个过程就像是让GPT参加工作培训,从优秀员工的作为例子,消极员工作为反例,学习如何更好地服务客户。
- RLHF系列中,与PPO(Proximal Policy Optimization)这种需要奖励模型、价值模型的RL算法不同;
- DPO通过推导PPO奖励模型的显式解,把在线奖励模型换成离线数据,ref输出可以提前保存。
- DPO性能几乎不变,只用跑 actor 和 ref 2 个模型,大大节省显存开销和增加训练稳定性。
- 同样的,LLM的RL步骤也 并非必须,有利也有弊。
活字三元组(q,chose,reject)数据集,学习率le-5,半精度fp16,共1个epoch,耗时1h。
python 5-dpo_train.py
📋关于LLM的参数配置,有一篇很有意思的论文MobileLLM做了详细的研究和实验。 scaling law在小模型中有自己独特的规律。 引起Transformer参数成规模变化的参数几乎只取决于d_model
和n_layers
。
-
d_model
↑+n_layers
↓->矮胖子 -
d_model
↓+n_layers
↑->瘦高个2020年提出Scaling Law的论文认为,训练数据量、参数量以及训练迭代次数才是决定性能的关键因素,而模型架构的影响几乎可以忽视。 然而似乎这个定律对小模型并不完全适用。 MobileLLM提出架构的深度比宽度更重要,「深而窄」的「瘦长」模型可以学习到比「宽而浅」模型更多的抽象概念。 例如当模型参数固定在125M或者350M时,30~42层的「狭长」模型明显比12层左右的「矮胖」模型有更优越的性能, 在常识推理、问答、阅读理解等8个基准测试上都有类似的趋势。 这其实是非常有趣的发现,因为以往为100M左右量级的小模型设计架构时,几乎没人尝试过叠加超过12层。 这与MiniMind在训练过程中,模型参数量在
d_model
和n_layers
之间进行调整实验观察到的效果是一致的。 然而「深而窄」的「窄」也是有维度极限的,当d_model<512时,词嵌入维度坍塌的劣势非常明显, 增加的layers并不能弥补词嵌入在固定q_head带来d_head不足的劣势。 当d_model>1536时,layers的增加似乎比d_model的优先级更高,更能带来具有“性价比”的参数->效果增益。 因此MiniMind设定small模型的d_model=512,n_layers=8来获取的「极小体积<->更好效果」的平衡。 设定d_model=768,n_layers=16来获取效果的更大收益,更加符合小模型scaling-law的变化曲线。
作为参考,GPT3的参数设定见下表: