基于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

一些注意事项

  1. 首次启动deepspeed时,CPU adam部分的代码需要使用g++进行编译,部分极端情况下g++可能会出现编译错误,可能需要修改g++的配置,可以参考https://github.com/NVIDIA/apex/issues/1491

  2. 注意cuda的版本和torch cuda的版本,这两个版本必须一致,才可以进行上述的编译,可以直接通过apt-get install安装所需的cuda版本,以及用export PATH设置环境变量以访问新版CUDA

posted @ 2024-02-02 19:00  AlphaInf  阅读(4982)  评论(2编辑  收藏  举报