Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

不是Issue,一点个人训练minimind的记录 #26

Open
ipfgao opened this issue Sep 14, 2024 · 12 comments
Open

不是Issue,一点个人训练minimind的记录 #26

ipfgao opened this issue Sep 14, 2024 · 12 comments
Labels
good first issue Good for newcomers

Comments

@ipfgao
Copy link

ipfgao commented Sep 14, 2024

minimind训练记录

  • 注:以下记录是基于2024年9月14日上午八时更新后测试的情况。

0. 环境安装

  pip install -r requirements.txt

conda新建的环境下,直接按照上面的命令安装时出现了两个问题,一个是flash_attn==2.5.0下载时依赖torch,一直报错,另一个是torch==2.1.2+cu121没有找到该版本,只有torch==2.1.2

第一个问题,可能是连接github时的网络问题,变成了编译安装。
所以先单独安装了torch,再安装的flash_attn。此时依然报错,调整网络环境后,直接pip安装成功。

第二个问题,直接pip install torch==2.1.2

1. 数据集下载

参考Readme,在项目根目录下新建了dataset文件夹,并从如下地址下载数据集到./dataset/目录下:

MiniMind训练数据集 下载地址
【tokenizer训练集】 HuggingFace / 百度网盘
【Pretrain数据】 Seq-Monkey官方 / 百度网盘 / HuggingFace
【SFT数据】 匠数大模型SFT数据集
【DPO数据1】 活字数据集1
【DPO数据2】 活字数据集2

其中,【tokenizer训练集】【Pretrain数据】下载自百度网盘;【SFT数据】来自modelscope。

【DPO数据1】(huozi_rlhf_data.json)、【DPO数据2】(test-00000-of-00001-8ecd46436fadcf7f.parquet、train-00000-of-00001-789dc5dece0f1fc1.parquet)来自hugginface(上不了hugginface,可以试试hf-mirror),放在了dataset\dpo目录下。

【Pretrain数据】的百度网盘地址内容很全,包括如下文件:

├── mobvoi_seq_monkey_general_open_corpus.jsonl
├── mobvoi_seq_monkey_general_open_corpus.zip
├── pretrain_data_32000.bin
├── pretrain_data_6400.bin
├── sft_data_multi.csv
├── sft_data_single.csv
└── tokenizer_train.jsonl

其中的tokenizer_train.jsonl文件与【tokenizer训练集】是同一个文件。

mobvoi_seq_monkey_general_open_corpus.zip是mobvoi_seq_monkey_general_open_corpus.jsonl的压缩文件。

【tokenizer训练集】HuggingFace地址下的内容跟【Pretrain数据】百度网盘 的内容基本一致,跟【Pretrain数据】HuggingFace 地址是同一个。

2. 数据集处理

按Readme中的步骤进行操作:

python data_process.py

终端输出如下:

tokenizer词表大小: 6400
seq_monkey: [350000]
seq_monkey: [450000]
seq_monkey: [500000]
... ... ... ...//省略
seq_monkey: [12850000]
(1510396873,)

运行上述命令后,处理数据集,在dataset目录下生成了pretrain_data.binclean_seq_monkey.bin两个文件。
经md5校验,这两个文件与【Pretrain数据】中的pretrain_data_6400.bin完全一致。

md5sum dataset/*.bin
491ffc23467d2eb29b1177969d9d8bf0  clean_seq_monkey.bin
012b3d4b7bcffec47d1c5244d177bebc  pretrain_data_32000.bin
491ffc23467d2eb29b1177969d9d8bf0  pretrain_data_6400.bin
491ffc23467d2eb29b1177969d9d8bf0  pretrain_data.bin

可见大佬已经把数据处理好并上传到百度网盘和HuggingFace了。
结合Readme的说明,可以得知pretrain_data_32000.bin是基于mistral tokenizer作为分词器预训练的,而pretrain_data_6400.bin(也是通过python data_process.py自己生成的pretrain_data.bin)是基于自定义Tokenizer模型(见model/minimind_tokenizer)预训练的。

3. 模型训练

训练环境:

Intel i9-13900k + 128G DDR5-4800 + NIVIDA RTX 4090 × 2,Driver Version: 550.54.14,CUDA Version: 12.4。

训练过程

预训练

按Readme中的说明,单机多卡训练可以使用deepspeed,但项目的requirements中并没有deepspeed。需要手动安装:

pip install deepspeed

安装成功后,执行如下命令进行了单机多卡训练:

deepspeed --master_port 29500 --num_gpus=2 1-pretrain.py
  • 注:第一次执行时,出现显存不足的错误,经检查是训练服务器运行了ollama服务,占用了一个显卡的大量显存,关停ollama服务后正常执行。

训练的同时开启nvidia-smi实时监控显卡资源占用情况:

watch nvidia-smi

单显卡显存占用量13000MiB左右,双卡共占用约26000MiB,两颗GPU占用均在99-100%。
通过htop监测CPU和内存占用情况,其中CPU有两个线程占用在100%左右,另有两个核心占用在22%左右,内存占用在6-7GB左右。

Epoch0

第一个Epoch约用时1个小时左右。
loss值变化:
Epoch:[0/20]
0/23047, loss:8.867 ;
9400/23047,loss:2.853;
之后从2.8左右,缓慢下降至2.651。

Epoch1

第二个Epoch约用时1个小时左右。
loss值变化:
Epoch:[1/20]
(0/23047) loss:2.645;
(9500/23047) loss:2.557;
(19700/23047) loss:2.641
一直在2.5-2.7左右波动。

Epoch2

第三个Epoch没有训练完,强制结束了。
loss值变化:
在2.4-2.7左右波动。
Epoch:[2/20]
(0/23047) loss:2.533
(9500/23047) loss:2.483
结束前最后一个保存点:
Epoch:2/20 loss:2.572 lr:0.0000965 epoch_Time:29.0min:

单轮对话任务微调

采用sft_data_single.csv数据集进行任务微调。

torchrun --nproc_per_node 2 3-full_sft.py

刚开始遇到两处错误,一处是NameError: name '加完班回到家窝在沙发里' is not defined,经与大佬咨询,更用新代码跳过了个别这种所谓的“非法Python标识符的字符串”异常数据;另一处是TypeError: can only concatenate str (not "float") to str,通过修改model/dataset.pySFTDataset类的__getitem__方法,强制转化为字符串,跑通了训练。详细情况可以见项目Issues
预测微调训练的时间为98min,实际时间相当,与Readme中2个小时比较接近,但按Reame的说法,应该远远小于预训练才对,可能是用torchrun的性能发挥比不上deepspeed。

Epoch:[0/19](0/24681) loss:8.863 lr:0.00020000 epoch_Time:372.0min:
Epoch:[0/19](100/24681) loss:5.251 lr:0.00020000 epoch_Time:98.0min:

实际nvidia-smi监测显卡的情况,两个显卡显存占用均在17000MiB以上,GPU占用率87~93%,均未发挥到100%。

Epoch0

loss值变化:

Epoch:[0/19](0/24681) loss:8.863 lr:0.00020000 epoch_Time:372.0min:
Epoch:[0/19](100/24681) loss:5.251 lr:0.00020000 epoch_Time:98.0min:
Epoch:[0/19](200/24681) loss:4.716 lr:0.00020000 epoch_Time:96.0min:
Epoch:[0/19](300/24681) loss:4.324 lr:0.00020000 epoch_Time:94.0min:
Epoch:[0/19](400/24681) loss:4.061 lr:0.00020000 epoch_Time:93.0min:
Epoch:[0/19](500/24681) loss:3.599 lr:0.00020000 epoch_Time:93.0min:
Epoch:[0/19](600/24681) loss:3.665 lr:0.00020000 epoch_Time:92.0min:
Epoch:[0/19](700/24681) loss:3.460 lr:0.00020000 epoch_Time:92.0min:
Epoch:[0/19](800/24681) loss:3.517 lr:0.00020000 epoch_Time:91.0min:
Epoch:[0/19](900/24681) loss:3.360 lr:0.00020000 epoch_Time:91.0min:
Epoch:[0/19](1000/24681) loss:3.225 lr:0.00020000 epoch_Time:91.0min:
Epoch:[0/19](1100/24681) loss:2.948 lr:0.00020000 epoch_Time:90.0min:
Epoch:[0/19](1200/24681) loss:2.979 lr:0.00020000 epoch_Time:90.0min:
Epoch:[0/19](1300/24681) loss:3.064 lr:0.00020000 epoch_Time:90.0min:
Epoch:[0/19](1400/24681) loss:2.871 lr:0.00020000 epoch_Time:88.0min:
Epoch:[0/19](1500/24681) loss:2.802 lr:0.00020000 epoch_Time:88.0min:
Epoch:[0/19](1600/24681) loss:2.510 lr:0.00019999 epoch_Time:87.0min:
Epoch:[0/19](1700/24681) loss:2.733 lr:0.00019999 epoch_Time:87.0min:
Epoch:[0/19](1800/24681) loss:2.589 lr:0.00019999 epoch_Time:87.0min:
Epoch:[0/19](1900/24681) loss:2.744 lr:0.00019999 epoch_Time:86.0min:
Epoch:[0/19](2000/24681) loss:2.733 lr:0.00019999 epoch_Time:87.0min:
Epoch:[0/19](2100/24681) loss:2.690 lr:0.00019999 epoch_Time:86.0min:

初始迅速下降至2.6左右,然后继续缓慢下降,至第一个Epoch结束,loss下降至1.8~2.0。

Epoch1

第二个Epoch训练至第3000轮强制结束。
Epoch:1/19 loss:1.843 lr:0.00019871 epoch_Time:321.0min:
Epoch:1/19 loss:1.977 lr:0.00019870 epoch_Time:96.0min:
Epoch:1/19 loss:2.030 lr:0.00019869 epoch_Time:95.0min:
Epoch:1/19 loss:2.023 lr:0.00019868 epoch_Time:93.0min:
Epoch:1/19 loss:1.923 lr:0.00019867 epoch_Time:93.0min:
Epoch:1/19 loss:1.794 lr:0.00019866 epoch_Time:93.0min:
Epoch:1/19 loss:1.999 lr:0.00019864 epoch_Time:92.0min:
Epoch:1/19 loss:1.916 lr:0.00019863 epoch_Time:92.0min:
Epoch:1/19 loss:1.881 lr:0.00019862 epoch_Time:91.0min:
Epoch:1/19 loss:2.007 lr:0.00019861 epoch_Time:91.0min:
Epoch:1/19 loss:2.018 lr:0.00019860 epoch_Time:92.0min:
Epoch:1/19 loss:1.846 lr:0.00019859 epoch_Time:91.0min:
Epoch:1/19 loss:2.004 lr:0.00019858 epoch_Time:91.0min:
Epoch:1/19 loss:2.062 lr:0.00019857 epoch_Time:89.0min:
Epoch:1/19 loss:1.944 lr:0.00019856 epoch_Time:89.0min:
Epoch:1/19 loss:1.887 lr:0.00019855 epoch_Time:89.0min:
Epoch:1/19 loss:1.731 lr:0.00019854 epoch_Time:88.0min:
Epoch:1/19 loss:1.982 lr:0.00019852 epoch_Time:88.0min:
Epoch:1/19 loss:1.815 lr:0.00019851 epoch_Time:88.0min:
Epoch:1/19 loss:1.956 lr:0.00019850 epoch_Time:87.0min:
Epoch:1/19 loss:2.041 lr:0.00019849 epoch_Time:87.0min:
Epoch:1/19 loss:1.966 lr:0.00019848 epoch_Time:86.0min:
Epoch:1/19 loss:2.006 lr:0.00019847 epoch_Time:86.0min:
Epoch:1/19 loss:2.039 lr:0.00019846 epoch_Time:86.0min:
Epoch:1/19 loss:1.965 lr:0.00019845 epoch_Time:85.0min:
Epoch:1/19 loss:1.977 lr:0.00019843 epoch_Time:85.0min:
Epoch:1/19 loss:2.057 lr:0.00019842 epoch_Time:85.0min:
Epoch:1/19 loss:1.935 lr:0.00019841 epoch_Time:84.0min:
Epoch:1/19 loss:1.851 lr:0.00019840 epoch_Time:84.0min:
Epoch:1/19 loss:1.779 lr:0.00019839 epoch_Time:83.0min:
Epoch:1/19 loss:1.982 lr:0.00019838 epoch_Time:83.0min:

deepspeed任务微调

经测试,deepspeed单机多卡微调训练速度与torchrun相当,显卡资源占用率相当。

deepspeed --master_port 29500 --num_gpus=2 3-full_sft.py
[2024-09-14 17:07:19,658] [INFO] [real_accelerator.py:203:get_accelerator] Setting ds_accelerator to cuda (auto detect)
[2024-09-14 17:07:20,518] [WARNING] [runner.py:212:fetch_hostfile] Unable to find hostfile, will proceed with training with local resources only.
[2024-09-14 17:07:20,518] [INFO] [runner.py:585:main] cmd = /home/nlp/anaconda3/envs/minimind/bin/python -u -m deepspeed.launcher.launch --world_info=eyJsb2NhbGhvc3QiOiBbMCwgMV19 --master_addr=127.0.0.1 --master_port=29500 --enable_each_rank_log=None 3-full_sft.py
[2024-09-14 17:07:21,568] [INFO] [real_accelerator.py:203:get_accelerator] Setting ds_accelerator to cuda (auto detect)
[2024-09-14 17:07:22,342] [INFO] [launch.py:146:main] WORLD INFO DICT: {'localhost': [0, 1]}
[2024-09-14 17:07:22,342] [INFO] [launch.py:152:main] nnodes=1, num_local_procs=2, node_rank=0
[2024-09-14 17:07:22,342] [INFO] [launch.py:163:main] global_rank_mapping=defaultdict(<class 'list'>, {'localhost': [0, 1]})
[2024-09-14 17:07:22,342] [INFO] [launch.py:164:main] dist_world_size=2
[2024-09-14 17:07:22,342] [INFO] [launch.py:168:main] Setting CUDA_VISIBLE_DEVICES=0,1
[2024-09-14 17:07:22,350] [INFO] [launch.py:256:main] process 281567 spawned with command: ['/home/nlp/anaconda3/envs/minimind/bin/python', '-u', '3-full_sft.py', '--local_rank=0']
[2024-09-14 17:07:22,355] [INFO] [launch.py:256:main] process 281568 spawned with command: ['/home/nlp/anaconda3/envs/minimind/bin/python', '-u', '3-full_sft.py', '--local_rank=1']
LLM总参数量:26.878 百万
Epoch:[0/19](0/24681) loss:8.872 lr:0.00020000 epoch_Time:392.0min:
Epoch:[0/19](100/24681) loss:5.362 lr:0.00020000 epoch_Time:98.0min:
Epoch:[0/19](200/24681) loss:4.663 lr:0.00020000 epoch_Time:96.0min:
Epoch:[0/19](300/24681) loss:4.327 lr:0.00020000 epoch_Time:94.0min:
Epoch:[0/19](400/24681) loss:3.852 lr:0.00020000 epoch_Time:94.0min:
Epoch:[0/19](500/24681) loss:3.747 lr:0.00020000 epoch_Time:93.0min:
^C[2024-09-14 17:09:31,712] [INFO] [launch.py:319:sigkill_handler] Killing subprocess 281567

多轮对话任务微调

修改3-full_sft.py中第178行的csv文件路径,如下:

    df = pd.read_csv('./dataset/sft_data_multi.csv')

同时修改第93行,ckp的保存文件名,如下:

            ckp = f'{save_dir}/multi_sft_{lm_config.dim}{moe_path}.pth'

的在预训练的base模型上直接进行多轮对话任务微调。

torchrun --nproc_per_node 2 3-full_sft.py
[2024-09-14 17:16:59,496] torch.distributed.run: [WARNING] 
[2024-09-14 17:16:59,496] torch.distributed.run: [WARNING] *****************************************
[2024-09-14 17:16:59,496] torch.distributed.run: [WARNING] Setting OMP_NUM_THREADS environment variable for each process to be 1 in default, to avoid your system being overloaded, please further tune the variable for optimal performance in your application as needed. 
[2024-09-14 17:16:59,496] torch.distributed.run: [WARNING] *****************************************
LLM总参数量:26.878 百万
Epoch:[0/19](0/2611) loss:8.856 lr:0.00020000 epoch_Time:33.0min:
Epoch:[0/19](100/2611) loss:4.938 lr:0.00020000 epoch_Time:10.0min:
Epoch:[0/19](200/2611) loss:4.291 lr:0.00019999 epoch_Time:10.0min:
Epoch:[0/19](300/2611) loss:4.034 lr:0.00019998 epoch_Time:9.0min:
Epoch:[0/19](400/2611) loss:3.389 lr:0.00019997 epoch_Time:9.0min:
Epoch:[0/19](500/2611) loss:3.341 lr:0.00019995 epoch_Time:9.0min:
Epoch:[0/19](600/2611) loss:3.158 lr:0.00019993 epoch_Time:8.0min:
Epoch:[0/19](700/2611) loss:3.085 lr:0.00019991 epoch_Time:8.0min:
Epoch:[0/19](800/2611) loss:2.985 lr:0.00019988 epoch_Time:7.0min:
Epoch:[0/19](900/2611) loss:2.974 lr:0.00019985 epoch_Time:7.0min:
Epoch:[0/19](1000/2611) loss:2.932 lr:0.00019981 epoch_Time:7.0min:
Epoch:[0/19](1100/2611) loss:2.791 lr:0.00019977 epoch_Time:6.0min:
Epoch:[0/19](1200/2611) loss:2.727 lr:0.00019973 epoch_Time:6.0min:
Epoch:[0/19](1300/2611) loss:2.563 lr:0.00019968 epoch_Time:6.0min:
Epoch:[0/19](1400/2611) loss:2.598 lr:0.00019963 epoch_Time:5.0min:
Epoch:[0/19](1500/2611) loss:2.669 lr:0.00019957 epoch_Time:5.0min:
Epoch:[0/19](1600/2611) loss:2.543 lr:0.00019951 epoch_Time:4.0min:
Epoch:[0/19](1700/2611) loss:2.523 lr:0.00019945 epoch_Time:4.0min:
Epoch:[0/19](1800/2611) loss:2.494 lr:0.00019939 epoch_Time:4.0min:
Epoch:[0/19](1900/2611) loss:2.530 lr:0.00019932 epoch_Time:3.0min:
Epoch:[0/19](2000/2611) loss:2.430 lr:0.00019924 epoch_Time:3.0min:
Epoch:[0/19](2100/2611) loss:2.442 lr:0.00019916 epoch_Time:2.0min:
Epoch:[0/19](2200/2611) loss:2.381 lr:0.00019908 epoch_Time:2.0min:
Epoch:[0/19](2300/2611) loss:2.426 lr:0.00019900 epoch_Time:2.0min:
Epoch:[0/19](2400/2611) loss:2.255 lr:0.00019891 epoch_Time:1.0min:
Epoch:[0/19](2500/2611) loss:2.090 lr:0.00019882 epoch_Time:0.0min:
Epoch:[0/19](2600/2611) loss:2.290 lr:0.00019872 epoch_Time:0.0min:

训练一轮后强制结束

4.模型测试

因为测试脚本的2-eval.py里的ckp地址取的是./out/single_chat下的模型,所以需要在out下新建一个single_chat文件夹,然后把生成的full_sft_512.pth文件移动进去。
然后执行测试

python 2-eval.py 

结果有好有坏的,具体如下:

模型参数: 26.878464 百万 = 0.026878464 B (Billion)
[Q]: 你叫什么名字啊?
[A]: 我是一款大型语言模型,没有具体的名字,但是我可以为您提供一些流行的名字。比如,如果您想要一个流行的名字,我可以为您提供一些创意词汇,比如“快乐”、“阳光”、“快乐”、“温暖”等。您需要哪种名字的名字呢?

[Q]: 你叫什么名字?
[A]:  作为一个AI,我没有名字,但是我可以告诉你如何使用您的设备或应用程序来帮助您完成各种任务。

[Q]: 中国有哪些比较好的大学?
[A]: 中国有许多优秀的大学,比如北京大学,上海大学,上海大学,上海大学,上海大学,上海大学,上海大学,上海大学,上海大学,上海大学,上海大学,上海大学,上海大学,上海大学,上海大学,上海大学,上海大学,上海大学,上海大学,上海大学,上海大学,上海大学。

[Q]: 全世界最好的大学是什么?
[A]:  全世界最好的大学是中国大学,成立于1955年。这是一所位于中国的高等学府,成立于1955年,是中国最古老、最全面的大学之一。该校拥有众多优秀的学术成就,包括《中华人民共和国国政法》、《中华人民共和国国政法》、《中华人民共和国国政法》等。这所大学在中国拥有众多优秀的学术成就,是中国教育领域的顶尖标准。

[Q]: 你知道光速是多少吗?
[A]:  光速是恒定在真空中,因为质量会不断变化。根据牛顿第二定律,任何物体都在互相作用力,物体的加速度与物体的质量成正比,与物体的质量成反比。所以,如果你知道光速,你知道光速是多少,你就可以用公式表示为:
F = ma
其中,F表示物体的质量,m表示物体的加速度,a表示物体的加速度,a表示物体的加速度。

[Q]: 你知道长江吗?
[A]: 长江是中国的一条著名旅游景点,位于长江上游。它是中国最著名的旅游景点之一,也是中国著名的旅游胜地之一。

[Q]: 人类的血液主要由哪些成分组成?
[A]: 人类血液主要由蛋白质、氨基酸、脂质、维生素和矿物质组成。蛋白质是由碳水化合物组成的,脂质是碳水化合物,脂质是蛋白质,脂质是维生素。脂质是人体的遗传信息,脂质是人体细胞的主要组成部分,维生素和矿物质是人体细胞的主要组成部分,维生素是人体细胞的主要来源,矿物质是人体细胞的主要来源。

[Q]: 第一颗人造卫星是哪个国家发射的?
[A]: 这个问题很难回答,因为每个人的发射地点和国籍都不同。不过,根据国际标准,第一颗人造卫星是伊欧,而第二颗人造卫星是伊欧。

[Q]: 你知道杭州有什么美食吗?
[A]: 杭州有很多美食,比如杭州的传统菜肴、西湖的特色小吃、汉堡包、牛肉面等等。

[Q]: 你知道泰山在哪里吗?
[A]: 泰山位于中国北部,是中国的山水之都。

[Q]: 地球上最大的动物是什么?
[A]:  地球上最大的动物是蓝鲸。

[Q]: 地球自转一圈大约需要多少时间?
[A]: 地球的自转一圈大约需要23小时56分钟。

[Q]: 人类最早使用的金属是什么?
[A]:  人类最早使用的金属是铅笔,因为铅笔是由金属构成的。

[Q]: 水的化学分子式是什么?
[A]: 水的化学键是氢键。水分子是水分子中一种化学键,由两个氢原子和一个氧原子组成。水分子的化学键是氢键。

[Q]: 大气层中含量最多的气体是什么?
[A]: 在大气层中,最多的气体是氮气。它是地球上最轻的大气层,也是地球上所有大气层中的氮气和氧气的最大分子。

[Q]: 世界上最高的山峰是什么?
[A]: 世界上最高的山峰是珠穆朗玛峰,位于中国和尼泊尔边境上的喜马拉雅山脉上,海拔8848.86米。该山峰被誉为“地球之巅峰”,吸引着成千上万的登山者前来挑战。

[Q]: 你知道世界上最深的海沟是什么吗?
[A]: 是的,世界上最深的海沟是马里亚纳海沟。它位于西南亚的马里亚纳群岛,海拔高度约为4,817米。马里亚纳海沟是一个世界著名的地质奇观,位于西南亚马里亚纳群岛,是地球上最大的热带海洋,也是地球上最深的海沟。

[Q]: 最早发明印刷术的是哪个国家?
[A]: 印刷术是公元前3世纪的一种发明。

[Q]: 万有引力是谁提出的?
[A]: 万有引力是宇宙中最大的天体之一,它是宇宙中最大的天体之一。万有引力是由爱因斯坦提出的,并对周围的星系产生巨大的引力,使得星系的质量不断增加。万有引力是宇宙中最强大的天体,它对恒星的引力形成了引力,并为星系的形成提供了基础。

[Q]: 光合作用的主要原理是什么?
[A]: 光合作用的主要原理是:当太阳光照射到地球周围时,它们会通过光能转化为化学能,并产生电能。这个过程会产生氧气和氢气,这使得植物和一些有机物能够在光合作用中生长。光合作用是植物和一些有机物产生的,这些有机物通过光能转化为化学能。光合作用是植物生长和发展的必要条件,也是植物和有机物生长和繁殖的基础。

[Q]: 你知道大熊猫的主要食物是什么吗?
[A]: 大熊猫的主要食物是竹子和小型哺乳动物,它们主要是竹子和小型哺乳动物。

[Q]: 海水为什么是咸的?
[A]: 海水咸是因为它是海水中的海洋,其中的盐分会与水分子发生反应,导致水的分子间的相互作用,形成了海水中的盐分和水蒸气。

[Q]: 我们平时喝的牛奶主要含有什么营养成分?
[A]: 我们的饮料主要含有维生素C、维生素C和钙。其中,维生素C是一种重要的营养成分,有助于维持身体健康。维生素C是维生素C的一种,可以帮助调节血压和保护心脏健康。维生素C是一种抗氧化剂,有助于保护身体免受自由基对皮肤的伤害,同时还能保护肠道健康。

[Q]: 一星期有多少天?
[A]: 一天是星期一,也就是星期一。

许是训练轮次不够的问题吧。

5.后记

多轮对话未见测试脚本,此处未做测试。
DPO训练还没有做,待中秋节后吧。
不过整个语言模型的预训练和任务微调的路线算是跑通了。
路漫漫其修远兮,吾将上下而求索。
最后特别感谢jingyaogong大佬!!!
中秋节快乐!!

@jingyaogong
Copy link
Owner

这个效果的话,一个是26M规模有些小,再一个sft跑到5轮以后会好点,现在的瞎编现象比较严重。

谢谢你的分享,非常详细,比我写的清晰很多hhhh

中秋节快乐!!!✨

@chuanzhubin
Copy link
Contributor

记录的很好! 可以发个教程了。

@jingyaogong jingyaogong added the good first issue Good for newcomers label Sep 14, 2024
@mikeallen39
Copy link

赞!学习了!

@kururu623
Copy link

谢谢大佬,十分具有参考意义,新建环境也遇到了大佬们遇到的坑,flash-attn真的难装啊,https://github.com/bdashore3/flash-attention后面是通过这里下载安装才行,windows系统

@donjy
Copy link

donjy commented Sep 19, 2024

感谢大佬分享

@ipfgao
Copy link
Author

ipfgao commented Sep 20, 2024

项目文件说明

  • 注:依据2024年9月20日的更新编写,未完

images

Readme里的图片目录。

model

模型文件夹。

model/minimind_tokenizer

项目自定义的Tokenizer模型文件。

  • model/minimind_tokenizer/merges.txt
    merges文件存放的是训练tokenizer阶段所得到的合并词表结果,就是tokenizer.json中,model.merges下的内容。
  • model/minimind_tokenizer/tokenizer_config.json
    分词器的配置信息,定义了分词器的版本、额外添加的标记(tokens)、结构/代码和模型参数等信息,比如tokenizer_class指定使用的分词器类名以及model_max_length指定模型能够处理的最大序列长度 和 bos_token指定句首的标记等内容。
  • model/minimind_tokenizer/tokenizer.json
    最终的分词器模型文件,包含了分词器的版本号、分词器的截断、填充策略、特殊标记、文本归一化的函数、预分词的策略或方法、分词器模型的类型、词汇表(vocab)和合并规则(merges)等信息。
  • model/minimind_tokenizer/vocab.json
    词表文件,就是tokenizer.json中,model.vocab下的内容。

注:分词器训练代码可见train_tokenizer.py

model/dataset.py

数据集定义文件,该文件定义了两个继承自Dataset的数据集类,分别是 PretrainDataset 和 SFTDataset,它们分别用于预训练任务和微调任务的数据加载和处理。

model/LMConfig.py

模型配置文件,定义 LMConfig 类,继承自 PretrainedConfig。如果想修改模型参数,可以在这个文件里改。

主要包括如下内容:

  • dim: 模型维度,默认为 512
  • n_layers: Transformer 层数,默认为 8
  • n_heads: 注意力头数,默认为 16
  • n_kv_heads: KV 头数,默认为 8
  • vocab_size: 词汇表大小,应于分词器保持一致,默认为 6400
  • hidden_dim: 隐藏层维度,默认为 None
  • multiple_of: 隐藏层维度的倍数,默认为 64
  • norm_eps: 归一化层的 epsilon 值,默认为 1e-5
  • max_seq_len: 最大序列长度,默认为 512,如果需要长文本对话支持,可以加大该值的设置
  • dropout: Dropout 概率,默认为 0.0
  • flash_attn: 是否使用 Flash Attention,默认为 True

以下是 MOE(Mixture of Experts)的特定配置当 use_moe 为 False 时,以下配置无效

  • use_moe: 是否使用 MOE,默认为 False
  • num_experts_per_tok:每个 token 选择的专家数量,默认为 2
  • n_routed_experts=4, # 总的专家数量,默认为 4
  • n_shared_experts: bool = True, # 是否使用共享专家,默认为 True
  • scoring_func='softmax', # 评分函数,默认为 'softmax'
  • aux_loss_alpha=0.01, # 辅助损失的 alpha 参数,默认为 0.01
  • seq_aux=True, # 是否在序列级别上计算辅助损失,默认为 True
  • norm_topk_prob=True, # 是否标准化 top-k 概率,默认为 True

model/model.py

模型文件,定义了模型结构,包括多个子模块如 FeedForward、RMSNorm、MoEGate、MOEFeedForward、TransformerBlock 等,实现了前向传播计算、损失函数计算和通过逐步生成方式进行文本生成。

主要内容总结:

  1. RMSNorm:

    • 实现 RMSNorm(Root Mean Square Layer Normalization)归一化,一种归一化方法,用于提高模型的稳定性和训练效果。
  2. Attention:

    • 实现自注意力机制,包括 QKV 计算、缩放点积注意力、多头注意力等。
  3. FeedForward:

    • 前馈神经网络,用于对输入数据进行非线性变换。
  4. MoEGate:

    • 实现专家混合(MoE)的门控机制,用于在多个专家之间进行选择性信息传递。
  5. MOEFeedForward:

    • 实现专家混合(MoE)的前馈神经网络。
  6. TransformerBlock:

    • 实现 Transformer 的一个块,包含自注意力机制和前馈神经网络。
  7. Transformer:

    • 实现整个 Transformer 模型,由多个 Transformer Block 组成的多层结构。包含词嵌入、位置编码、最终的输出层以及训练和推理方法。

主要功能:

  1. 前向传播计算:
    • 通过 forward 方法,输入 tokens 或 input_ids 和 targets,进行前向传播计算,得到 logits 和 last_loss(如果提供 targets)。
  2. 损失函数计算:
    • 使用 F.cross_entropy 计算损失函数,忽略索引为 -1 的标签。
  3. 文本生成:
    • 通过 generate 方法,实现逐步生成方式进行文本生成,支持温度、top_k 等参数调整。
  4. 评估答案:
    • 通过 eval_answer 方法,对给定的输入索引进行推理,得到最终的 logits。

0-eval_pretrain.py

测试预训练模型的接龙效果。
模型加载逻辑:
如果 model_from 为 1,则从本地路径加载自定义的 Transformer 模型。
如果 model_from 为 2,则使用 transformers 库中的预训练模型。

1-pretrain.py

功能概述

预训练脚本,执行预训练。
可以使用自定义的数据集进行预训练,训练过程中会动态调整学习率,并且支持分布式训练以提高训练效率。

使用、配置及功能说明

以下是该脚本的使用、配置和功能说明:

单机多卡使用

  • torchrun --nproc_per_node 2 1-pretrain.py: 运行脚本时需要使用torchrun命令,指定使用的GPU数量为2。

参数配置

  • lm_config = LMConfig(): 加载预定义的语言模型配置,具体配置内容在model/LMConfig.py文件中。
  • out_dir = 'out': 设置输出目录,默认为out文件夹。
  • epochs = 20: 训练的轮数。
  • batch_size = 64: 每个批次的数据量。
  • learning_rate = 2e-4: 初始学习率。
  • device = 'cuda:0' if torch.cuda.is_available() else 'cpu': 选择GPU设备,如果没有GPU则使用CPU。
  • dtype = 'bfloat16': 数据类型,支持自动混合精度训练(AMP)。
  • save_dir = os.path.join(out_dir): 模型保存目录,默认为out文件夹。
  • tokens_per_iter = batch_size * max_seq_len: 每个迭代步的数据量。

数据加载

  • data_path_list = ['./dataset/pretrain_data.bin']: 训练数据的文件路径列表,默认为./dataset/pretrain_data.bin
  • num_workers = 8: 数据加载的线程数,可以根据系统CPU核心数调整。

训练循环

  • iter_per_epoch = len(train_loader): 计算每个轮次的迭代步数。
  • for epoch in range(epochs): train_epoch(epoch): 进行多轮训练,每轮调用train_epoch函数进行训练。

模型的保存频率

在训练过程中,模型的权重会每迭代1000步保存一次模型,以便后续检查点或恢复训练。

使用已有权重再训练的说明

如果你已经有一个预训练模型的权重文件(例如pretrain_model.pth),并且你想继续在该模型基础上进行微调,可以按照以下步骤操作:

  1. 加载已有权重
     ckp = f'{save_dir}/pretrain_{lm_config.dim}{moe_path}.pth'
     
     state_dict = torch.load(ckp, map_location=device)
     unwanted_prefix = '_orig_mod.'
     for k, v in list(state_dict.items()):
         if k.startswith(unwanted_prefix):
             state_dict[k[len(unwanted_prefix):]] = state_dict.pop(k)
     model.load_state_dict(state_dict, strict=False)

注意,这部分是脚本中是注释掉的。 如果你想用已有权重继续训练,需要在脚本中找到上述代码,解除注释,才能使用现有的模型进行训练。
2. 继续训练
解除注释后还是用torchrun --nproc_per_node 2 1-pretrain.py

2-eval.py

测试模型的对话效果。通过加载预训练后的模型,并让模型来回答内置在脚本中的一系列问题,以评估模型的对话效果。

        ckp = f'./out/full_sft_{lm_config.dim}{moe_path}.pth'

其中ckp是检查点的路径,用于加载预训练的模型权重。

3-full_sft.py

执行指令微调训练
这段代码实现了指令微调(Instruction Fine-tuning),主要用于训练一个语言模型。以下是代码的主要功能和配置参数说明:

配置参数

代码中可以配置的参数有:

  • 模型加载

    model_from = 1  # 从权重加载,2使用transformers

    选择是否从本地权重文件加载模型(model_from = 1)或使用huggingface的Transformers库中的预训练模型(model_from = 2)。

  • 配置本地权重文件路径

          ckp = f'./out/pretrain_{lm_config.dim}{moe_path}.pth'

    如果是用预训练权重训练,则可按默认配置,如果是想从已微调的模型继续微调,则需要指定ckp路径,比如ckp = './out/full_sft_{lm_config.dim}{moe_path}.pth'

  • 数据集和批处理

    epochs = 19  # 训练轮数
    batch_size = 40  # 每个batch的大小
    learning_rate = 1e-4  # 学习率
    gradient_accumulation_steps = 1  # 梯度累积步数

    控制数据集的使用和训练的批处理大小、学习率和梯度累积方式。

  • 设备配置

    device = 'cuda:0' if torch.cuda.is_available() else 'cpu'  # 选择GPU或CPU
    dtype = 'bfloat16' or 'float16'  # 数据类型

    指定训练设备和使用的数据类型(半精度浮点数)。

  • 分布式训练

    ddp = int(os.environ.get("RANK", -1)) != -1  # 是否启用分布式训练

    如果环境变量中有RANK,则启用分布式训练(DDP)。

然后按Readme的描述运行脚本即可。

4-lora_sft.py

执行lora微调训练

5-dpo_train.py

执行DPO训练

chat_openai_api.py

实现与OpenAI API类似的接口

CODE_OF_CONDUCT.md

贡献者公约

data_process.py

处理数据集,例如pretrain数据提前进行token-encoder、sft数据集抽离qa到csv文件

eval_ceval.py

评估模型在ceval数据集上的表现

export_model.py

可以导出模型到transformers格式,推送到huggingface。

fast_infenence.py

使用 Streamlit 框架构建的交互式聊天应用程序,主要内容和功能的概述如下:
定义的内容

  1. 模型和Tokenizer 加载:
    • 使用 AutoModelForCausalLMAutoTokenizer 从 Hugging Face 的模型库中加载预训练的语言模型和对应的 tokenizer。
    • 通过 st.cache_resource 缓存模型和 tokenizer,以提高加载效率。
  2. 生成配置:
    • 定义了温度(temperature)、top_k 等超参数用于控制生成的质量和多样性。
  3. 聊天消息管理:
    • clear_chat_messagesinit_chat_messages 函数分别用于清空和初始化聊天记录。
    • 使用 Streamlit 的会话状态(session state)来存储和管理对话历史。
  4. 主要功能函数:
    • main 函数是应用程序的主入口,负责处理用户输入、模型生成响应以及界面交互。

实现的功能

  1. 用户界面:
    • Streamlit 页面设置和标题显示。
    • 提供一个文本输入框供用户输入对话内容。
    • 使用聊天消息的 UI 组件展示历史对话记录。
  2. 对话处理:
    • 接收用户的输入,并将其作为新消息添加到会话状态中。
    • 将当前对话历史转换为模型可理解的格式(通过 tokenizerapply_chat_template)。
    • 使用预定义的超参数生成模型的响应。
    • 实时地将生成的回答部分展示在界面上,直到遇到 EOS 标记或达到最大序列长度。
  3. 交互功能:
    • 提供一个按钮允许用户清空所有对话记录。
    • 通过 Streamlit 的回调机制实现动态更新界面内容和响应状态。

LICENSE

项目使用Apache License许可证。

my_openai_api.py

使用Flask框架构建的API服务器,用于处理与聊天模型相关的请求,包括生成聊天响应和计算文本的嵌入向量。

README_en.md

项目说明文件(英语)。

README.md

项目说明文件。

requirements.txt

python环境依赖文件,列出了运行该项目所需的Python包及其版本。

train_tokenizer.py

用于分词器训练。

@AirGHub
Copy link

AirGHub commented Oct 1, 2024

步骤2 数据集处理
为什么我运行显示分块处理而不是跟你的训练记录一样(有点疑惑)
你的终端显示
tokenizer词表大小: 6400
seq_monkey: [350000]
seq_monkey: [450000]
seq_monkey: [500000]
我的终端显示
tokenizer词表大小: 6400
Processed chunk 1 with 50000 lines
Processed chunk 2 with 50000 lines
Processed chunk 3 with 50000 lines
Processed chunk 4 with 50000 lines
Processed chunk 5 with 50000 lines

@jingyaogong
Copy link
Owner

步骤2 数据集处理 为什么我运行显示分块处理而不是跟你的训练记录一样(有点疑惑) 你的终端显示 tokenizer词表大小: 6400 seq_monkey: [350000] seq_monkey:[350000] seq_monkey: [450000] seq_monkey:[450000] seq_monkey: [500000] seq_monkey:[500000] 我的终端显示 tokenizer词表大小: 6400 Processed chunk 1 with 50000 lines处理了 50000 行的块 1 Processed chunk 2 with 50000 lines处理了 50000 行的块 2 Processed chunk 3 with 50000 lines处理了 50000 行的块 3 Processed chunk 4 with 50000 lines处理了 50000 行的块 4 Processed chunk 5 with 50000 lines处理了 50000 行的区块 5

@AirGHub 1-pretrain updated on 27th.

@bzy-080408
Copy link

咨询一下你们跑时候的ram和vram用到多少了?
我用国产显卡,4x16G VRAM加上80G内存训练时候爆了,VRAM使用96%,内存100。
还有使用4x国产海光 K100 64G VRAM训练时候第一个epoch就花了差不多3h,有人试过在nvidia v100上训练吗?IMG_20241002_211321.jpg

IMG_20241002_211321.jpg

@cyou121
Copy link

cyou121 commented Oct 3, 2024

请问一张4090能预训练嘛

@jingyaogong
Copy link
Owner

请问一张4090能预训练嘛

可以,足够

@fyqqyf
Copy link

fyqqyf commented Oct 13, 2024

内存占用在6-7GB左右。
我在训练时,为啥内存占用很高,54/62G左右;配置为A6000*2,单机双卡训练;batch_size=16,num_workers=8
@ipfgao

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
good first issue Good for newcomers
Projects
None yet
Development

No branches or pull requests

10 participants