基于Deepspeed实现LLaMA-13B或70B模型的微调
写在前面
事实证明,在部分情况下,依然有开启deepspeed的必要性
这是上一篇文章,讲述使用双卡/8卡3090微调llama2-70B/13B模型 - AlphaInf - 博客园 (cnblogs.com)
但这一篇文章,存在下面的几个问题:
- 如果训练的数据的seq_len过长,那么很有可能出现OOM,无法训练长的数据
- 如果需要调的参数极多,所需显存大幅超过了实际拥有的显存,那么也将无法微调
于是,就需要有deepspeed登场
deepspeed开启ZeRO3的情况下,可以将内存“虚拟化”为显存,在训练时,不将整个模型加载到显存中,而是将当前正在训练的部分加载进去(通常是逐层Transformer模块的形式)。
当设备的内存达到2TB时,则可实现70B级别模型的全量微调,且只需要3090显卡(甚至只需要一张!),且在单节点情况下,增加显卡可以使训练速度呈线性增长。
因此,会比传统的训练方法慢很多,且对通信带宽(特别是PCI总线)有着极高的要求。
在配置设备时,我们强烈建议采用PCIE 4.0的设备,而不是PCIE 3.0的设备(虽然8卡准系统的价格从1w变为了4w,但这是值得的)
笔者新配的机器,为一台10卡3090,采用了PCIE 4.0的准系统+2TB DDR4内存,使其具备了低成本进行70B模型推理的能力。
(低成本是相对的,这台机也要19w,够买一辆比亚迪汉了)
经过实测,在开启ZeRO3的情况下,一张A100-80G能达到4张3090的速度,但是价格却是3090的20倍(
微调和deepspeed环境的安装和激活
可以参考使用双卡/8卡3090微调llama2-70B/13B模型 - AlphaInf - 博客园 (cnblogs.com) 来安装基础的环境
在这个环境的基础上,通过pip install deepspeed安装deepspeed库。
本人采用了venv环境,安装虚拟环境,名字叫venv_deepspeed,以避免与其他环境产生冲突。
激活的命令是
source venv-deepspeed/bin/activate
此时,我们就可以进入虚拟环境中
这个虚拟环境和一般的python环境不一样,可以安装两套独立,互不影响的环境。
此外,你还需要配置ds_config(这个文件放在LLaMA-Factory的根目录下),这是一个ZeRO3的配置文件(根目录下也已经配好了)
[2024-06-16添加]注意,该ZeRO3的配置中,包含有offload_optimizer/param的配置,这里面必须要进行设置,新版llama-factory中并无该方面配置,将导致ZeRO3并不能节约内存
{
"bf16": {
"enabled": true
},
"optimizer": {
"type": "AdamW",
"params": {
"lr": "auto",
"betas": "auto",
"eps": "auto",
"weight_decay": "auto"
}
},
"scheduler": {
"type": "WarmupLR",
"params": {
"warmup_min_lr": "auto",
"warmup_max_lr": "auto",
"warmup_num_steps": "auto"
}
},
"zero_optimization": {
"stage": 3,
"offload_optimizer": {
"device": "cpu",
"pin_memory": true
},
"offload_param": {
"device": "cpu",
"pin_memory": true
},
"overlap_comm": true,
"contiguous_gradients": true,
"sub_group_size": 1e9,
"reduce_bucket_size": "auto",
"stage3_prefetch_bucket_size": "auto",
"stage3_param_persistence_threshold": "auto",
"stage3_max_live_parameters": 1e9,
"stage3_max_reuse_distance": 1e9,
"stage3_gather_16bit_weights_on_model_save": true
},
"gradient_accumulation_steps": "auto",
"gradient_clipping": "auto",
"steps_per_print": 2000,
"train_batch_size": "auto",
"train_micro_batch_size_per_gpu": "auto",
"wall_clock_breakdown": false
}
启动deepspeed
我们在LLaMA-Factory的目录下,运行该命令即可启动
deepspeed --num_gpus 2 src/train_bash.py \
--deepspeed ds_config.json \
--stage sft \
--do_train \
--model_name_or_path /hy-tmp/tigerbot-13b-chat-v5-4k \
--dataset self_cognition \
--template tigerbot \
--finetuning_type lora \
--lora_target q_proj,v_proj \
--cutoff_len 4096 \
--output_dir /hy-tmp/tigerbot-13b-rename \
--overwrite_cache \
--per_device_train_batch_size 2 \
--gradient_accumulation_steps 1 \
--lr_scheduler_type cosine \
--logging_steps 1 \
--save_steps 1000 \
--learning_rate 5e-5 \
--num_train_epochs 3.0 \
--plot_loss \
--bf16 \
--overwrite_output_dir
下面简单讲解下一些参数的含义
- --deepspeed 控制ds_config的控制信息
- --model_name_or_path 原始模型的路径
- --dataset 数据集选择,可以参考LLaMA-Factory中如何修改该数据集
- --template 模板的设置,可以参考前一篇非deepspeed微调的文章来设置。
- --finetuning_type 微调方式,可以选择lora/freeze/full的方式进行微调。
- --lora_target 设置要经过lora微调的是哪一些部分的权重
- --cutlff_len 最长的训练数据,最多可以包含多少个token(注意,这里是输入+输出的长度总和)
- --output_dir 输出训练好的权重的路径
下面给一个采用freeze法,在8卡3090上训练LLaMA-70B模型的命令
deepspeed --num_gpus 8 src/train_bash.py \
--deepspeed ds_config.json \
--stage sft \
--do_train \
--model_name_or_path /hy-tmp/tigerbot-70b-chat-v4-4k \
--dataset 法条结构化 \
--template tigerbot \
--finetuning_type freeze \
--num_layer_trainable 28 \
--cutoff_len 4096 \
--output_dir /hy-tmp/extract-law-structure-70b \
--overwrite_cache \
--per_device_train_batch_size 2 \
--gradient_accumulation_steps 1 \
--lr_scheduler_type cosine \
--logging_steps 1 \
--save_steps 1000 \
--learning_rate 1e-5 \
--num_train_epochs 1.0 \
--plot_loss \
--bf16 \
--overwrite_output_dir
和上面不同的地方在于
-
2卡变8卡,不过实际上2卡也可以训练,毕竟ZeRO3是一层一层训练的。
-
lora变为了freeze,且增加了num_layer_trainable的参数,这里相当于是,训练最后的28层参数(不包括norm),其他参数不参与训练。
在28层训练的情况下,消耗的内存总量约为600GB
-
训练轮数(num_train_epochs)被降低到了1,经过实测,训练参数一多,就得降低训练轮数,不然很容易过拟合
-
学习率也被降低到了1e-5
一些注意事项
-
首次启动deepspeed时,CPU adam部分的代码需要使用g++进行编译,部分极端情况下g++可能会出现编译错误,可能需要修改g++的配置,可以参考https://github.com/NVIDIA/apex/issues/1491
-
注意cuda的版本和torch cuda的版本,这两个版本必须一致,才可以进行上述的编译,可以直接通过apt-get install安装所需的cuda版本,以及用export PATH设置环境变量以访问新版CUDA