PEFT 文件

DeepSpeed

Hugging Face's logo
加入 Hugging Face 社群

並獲得增強的文件體驗

開始使用

DeepSpeed

DeepSpeed 是一個為大型模型分散式訓練設計的庫,專注於速度和規模,可處理數十億引數。其核心是零冗餘最佳化器 (ZeRO),它能在資料並行程序之間分片最佳化器狀態 (ZeRO-1)、梯度 (ZeRO-2) 和引數 (ZeRO-3)。這極大地減少了記憶體使用,使您能夠將訓練擴充套件到數十億引數的模型。為了進一步提高記憶體效率,ZeRO-Offload 透過在最佳化過程中利用 CPU 資源來減少 GPU 計算和記憶體消耗。

這兩個功能在 🤗 Accelerate 中都得到支援,您可以將它們與 🤗 PEFT 一起使用。

與 bitsandbytes 量化 + LoRA 的相容性

下表總結了 PEFT 的 LoRA、bitsandbytes 庫和 DeepSpeed Zero 階段在微調方面的相容性。DeepSpeed Zero-1 和 2 在推理時沒有效果,因為階段 1 分片最佳化器狀態,階段 2 分片最佳化器狀態和梯度。

DeepSpeed 階段 是否相容?
Zero-1 🟢
Zero-2 🟢
Zero-3 🟢

關於 DeepSpeed Stage 3 + QLoRA,請參考下文 “在多 GPU 上使用 PEFT QLoRA 和 DeepSpeed ZeRO3 微調大模型” 部分。

為了驗證這些觀察結果,我們使用了 QLoRA + PEFT 和此處可用的 accelerate 配置,運行了Transformers 強化學習 (TRL) 庫的 SFT (監督式微調) 官方示例指令碼。我們在 2x NVIDIA T4 GPU 上進行了這些實驗。

在多裝置和多節點上使用 PEFT 和 DeepSpeed ZeRO3 微調大模型

本節指南將幫助您學習如何使用我們的 DeepSpeed 訓練指令碼進行 SFT。您將配置該指令碼,在單臺機器的 8x H100 80GB GPU 上使用 LoRA 和 ZeRO-3 對 Llama-70B 模型進行 SFT(監督式微調)。透過更改 accelerate 配置,您可以將其擴充套件到多臺機器上。

配置

首先執行以下命令,使用 🤗 Accelerate 建立一個 DeepSpeed 配置檔案--config_file 標誌允許您將配置檔案儲存到特定位置,否則它將作為 default_config.yaml 檔案儲存在 🤗 Accelerate 快取中。

配置檔案用於在啟動訓練指令碼時設定預設選項。

accelerate config --config_file deepspeed_config.yaml

系統會詢問您一些關於設定的問題,並配置以下引數。在本例中,您將使用 ZeRO-3,因此請確保選擇這些選項。

`zero_stage`: [0] Disabled, [1] optimizer state partitioning, [2] optimizer+gradient state partitioning and [3] optimizer+gradient+parameter partitioning
`gradient_accumulation_steps`: Number of training steps to accumulate gradients before averaging and applying them. Pass the same value as you would pass via cmd argument else you will encounter mismatch error.
`gradient_clipping`: Enable gradient clipping with value. Don't set this as you will be passing it via cmd arguments.
`offload_optimizer_device`: [none] Disable optimizer offloading, [cpu] offload optimizer to CPU, [nvme] offload optimizer to NVMe SSD. Only applicable with ZeRO >= Stage-2. Set this as `none` as don't want to enable offloading.
`offload_param_device`: [none] Disable parameter offloading, [cpu] offload parameters to CPU, [nvme] offload parameters to NVMe SSD. Only applicable with ZeRO Stage-3. Set this as `none` as don't want to enable offloading.
`zero3_init_flag`: Decides whether to enable `deepspeed.zero.Init` for constructing massive models. Only applicable with ZeRO Stage-3. Set this to `True`.
`zero3_save_16bit_model`: Decides whether to save 16-bit model weights when using ZeRO Stage-3. Set this to `True`.
`mixed_precision`: `no` for FP32 training, `fp16` for FP16 mixed-precision training and `bf16` for BF16 mixed-precision training. Set this to `True`.

完成此操作後,相應的配置應如下所示,您可以在 config 資料夾中的 deepspeed_config.yaml 找到它。

compute_environment: LOCAL_MACHINE                                                                                                                                           
debug: false
deepspeed_config:
  deepspeed_multinode_launcher: standard
  gradient_accumulation_steps: 4
  offload_optimizer_device: none
  offload_param_device: none
  zero3_init_flag: true
  zero3_save_16bit_model: true
  zero_stage: 3
distributed_type: DEEPSPEED
downcast_bf16: 'no'
machine_rank: 0
main_training_function: main
mixed_precision: bf16
num_machines: 1
num_processes: 8
rdzv_backend: static
same_network: true
tpu_env: []
tpu_use_cluster: false
tpu_use_sudo: false
use_cpu: false

啟動命令

啟動命令可在 run_peft_deepspeed.sh 找到,如下所示:

accelerate launch --config_file "configs/deepspeed_config.yaml"  train.py \
--seed 100 \
--model_name_or_path "meta-llama/Llama-2-70b-hf" \
--dataset_name "smangrul/ultrachat-10k-chatml" \
--chat_template_format "chatml" \
--add_special_tokens False \
--append_concat_token False \
--splits "train,test" \
--max_seq_len 2048 \
--num_train_epochs 1 \
--logging_steps 5 \
--log_level "info" \
--logging_strategy "steps" \
--eval_strategy "epoch" \
--save_strategy "epoch" \
--push_to_hub \
--hub_private_repo True \
--hub_strategy "every_save" \
--bf16 True \
--packing True \
--learning_rate 1e-4 \
--lr_scheduler_type "cosine" \
--weight_decay 1e-4 \
--warmup_ratio 0.0 \
--max_grad_norm 1.0 \
--output_dir "llama-sft-lora-deepspeed" \
--per_device_train_batch_size 8 \
--per_device_eval_batch_size 8 \
--gradient_accumulation_steps 4 \
--gradient_checkpointing True \
--use_reentrant False \
--dataset_text_field "content" \
--use_flash_attn True \
--use_peft_lora True \
--lora_r 8 \
--lora_alpha 16 \
--lora_dropout 0.1 \
--lora_target_modules "all-linear" \
--use_4bit_quantization False

請注意,我們使用的 LoRA 引數為 rank=8,alpha=16,並針對所有線性層。我們傳遞了 deepspeed 配置檔案,並在 ultrachat 資料集的一個子集上微調 70B Llama 模型。

重要部分

讓我們深入瞭解一下這個指令碼,以便您能看到發生了什麼,並理解其工作原理。

首先要知道的是,該指令碼使用 DeepSpeed 進行分散式訓練,因為已經傳遞了 DeepSpeed 配置。SFTTrainer 類處理了使用傳入的 peft 配置建立 PEFT 模型的所有繁重工作。之後,當您呼叫 trainer.train() 時,SFTTrainer 內部使用 🤗 Accelerate 來準備模型、最佳化器和訓練器,並利用 DeepSpeed 配置建立 DeepSpeed 引擎,然後進行訓練。主要程式碼片段如下:

# trainer
trainer = SFTTrainer(
    model=model,
    processing_class=tokenizer,
    args=training_args,
    train_dataset=train_dataset,
    eval_dataset=eval_dataset,
    peft_config=peft_config,
)
trainer.accelerator.print(f"{trainer.model}")

# train
checkpoint = None
if training_args.resume_from_checkpoint is not None:
    checkpoint = training_args.resume_from_checkpoint
trainer.train(resume_from_checkpoint=checkpoint)

# saving final model
trainer.save_model()

記憶體使用

在上述示例中,每個 GPU 消耗的記憶體為 64 GB (80%),如下方截圖所示。

訓練執行的 GPU 記憶體使用情況

更多資源

您也可以參考這篇部落格文章 使用 🤗 PEFT 和 DeepSpeed 微調 Falcon 180B,瞭解如何在 2 臺機器的 16 個 A100 GPU 上微調 180B Falcon 模型。

在多 GPU 上使用 PEFT QLoRA 和 DeepSpeed ZeRO3 微調大模型

在本節中,我們將探討如何使用 QLoRA 和 DeepSpeed Stage-3 在 2x40GB GPU 上微調 70B llama 模型。為此,我們首先需要 bitsandbytes>=0.43.3, accelerate>=1.0.1, transformers>4.44.2, trl>0.11.4peft>0.13.0。使用 Accelerate 配置時,我們需要將 zero3_init_flag 設定為 true。以下是配置檔案,可在 deepspeed_config_z3_qlora.yaml 找到。

compute_environment: LOCAL_MACHINE                                                                                                                                           
debug: false
deepspeed_config:
  deepspeed_multinode_launcher: standard
  offload_optimizer_device: none
  offload_param_device: none
  zero3_init_flag: true
  zero3_save_16bit_model: true
  zero_stage: 3
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

啟動命令如下,可在 run_peft_qlora_deepspeed_stage3.sh 獲取。

accelerate launch --config_file "configs/deepspeed_config_z3_qlora.yaml"  train.py \
--seed 100 \
--model_name_or_path "meta-llama/Llama-2-70b-hf" \
--dataset_name "smangrul/ultrachat-10k-chatml" \
--chat_template_format "chatml" \
--add_special_tokens False \
--append_concat_token False \
--splits "train,test" \
--max_seq_len 2048 \
--num_train_epochs 1 \
--logging_steps 5 \
--log_level "info" \
--logging_strategy "steps" \
--eval_strategy "epoch" \
--save_strategy "epoch" \
--push_to_hub \
--hub_private_repo True \
--hub_strategy "every_save" \
--bf16 True \
--packing True \
--learning_rate 1e-4 \
--lr_scheduler_type "cosine" \
--weight_decay 1e-4 \
--warmup_ratio 0.0 \
--max_grad_norm 1.0 \
--output_dir "llama-sft-qlora-dsz3" \
--per_device_train_batch_size 2 \
--per_device_eval_batch_size 2 \
--gradient_accumulation_steps 2 \
--gradient_checkpointing True \
--use_reentrant True \
--dataset_text_field "content" \
--use_flash_attn True \
--use_peft_lora True \
--lora_r 8 \
--lora_alpha 16 \
--lora_dropout 0.1 \
--lora_target_modules "all-linear" \
--use_4bit_quantization True \
--use_nested_quant True \
--bnb_4bit_compute_dtype "bfloat16" \
--bnb_4bit_quant_storage_dtype "bfloat16"

注意傳遞的新引數 bnb_4bit_quant_storage_dtype,它表示用於打包 4 位引數的資料型別。例如,當它設定為 bfloat16 時,32/4 = 8 個 4 位引數在量化後被打包在一起。

在訓練程式碼方面,重要的程式碼更改是:

...

bnb_config = BitsAndBytesConfig(
    load_in_4bit=args.use_4bit_quantization,
    bnb_4bit_quant_type=args.bnb_4bit_quant_type,
    bnb_4bit_compute_dtype=compute_dtype,
    bnb_4bit_use_double_quant=args.use_nested_quant,
+   bnb_4bit_quant_storage=quant_storage_dtype,
)

...

model = AutoModelForCausalLM.from_pretrained(
    args.model_name_or_path,
    quantization_config=bnb_config,
    trust_remote_code=True,
    attn_implementation="flash_attention_2" if args.use_flash_attn else "eager",
+   torch_dtype=quant_storage_dtype or torch.float32,
)

請注意,AutoModelForCausalLMtorch_dtypebnb_4bit_quant_storage 資料型別相同。就是這樣。其他所有事情都由 Trainer 和 TRL 處理。

記憶體使用

在上述示例中,每個 GPU 消耗的記憶體為 **36.6 GB**。因此,過去需要 8x80GB GPU 的 DeepSpeed Stage 3+LoRA,以及需要幾塊 80GB GPU 的 DDP+QLoRA,現在只需要 2x40GB GPU。這使得大型模型的微調更加普及。

在單個 GPU 上使用 PEFT、DeepSpeed ZeRO3 和 CPU Offloading 微調大模型

本節指南將幫助您學習如何使用我們的 DeepSpeed 訓練指令碼。您將配置該指令碼,使用 ZeRO-3 和 CPU Offload 訓練一個用於條件生成的大型模型。

💡 為了幫助您入門,請檢視我們用於因果語言建模條件生成的示例訓練指令碼。您可以根據自己的應用調整這些指令碼,或者如果您的任務與指令碼中的任務相似,也可以直接使用它們。

配置

首先執行以下命令,使用 🤗 Accelerate 建立一個 DeepSpeed 配置檔案--config_file 標誌允許您將配置檔案儲存到特定位置,否則它將作為 default_config.yaml 檔案儲存在 🤗 Accelerate 快取中。

配置檔案用於在啟動訓練指令碼時設定預設選項。

accelerate config --config_file ds_zero3_cpu.yaml

系統會詢問您一些關於設定的問題,並配置以下引數。在本例中,您將使用 ZeRO-3 和 CPU-Offload,因此請確保選擇這些選項。

`zero_stage`: [0] Disabled, [1] optimizer state partitioning, [2] optimizer+gradient state partitioning and [3] optimizer+gradient+parameter partitioning
`gradient_accumulation_steps`: Number of training steps to accumulate gradients before averaging and applying them.
`gradient_clipping`: Enable gradient clipping with value.
`offload_optimizer_device`: [none] Disable optimizer offloading, [cpu] offload optimizer to CPU, [nvme] offload optimizer to NVMe SSD. Only applicable with ZeRO >= Stage-2.
`offload_param_device`: [none] Disable parameter offloading, [cpu] offload parameters to CPU, [nvme] offload parameters to NVMe SSD. Only applicable with ZeRO Stage-3.
`zero3_init_flag`: Decides whether to enable `deepspeed.zero.Init` for constructing massive models. Only applicable with ZeRO Stage-3.
`zero3_save_16bit_model`: Decides whether to save 16-bit model weights when using ZeRO Stage-3.
`mixed_precision`: `no` for FP32 training, `fp16` for FP16 mixed-precision training and `bf16` for BF16 mixed-precision training. 

一個示例配置檔案可能如下所示。最重要的一點是,zero_stage 設定為 3,並且 offload_optimizer_deviceoffload_param_device 設定為 cpu

compute_environment: LOCAL_MACHINE
deepspeed_config:
  gradient_accumulation_steps: 1
  gradient_clipping: 1.0
  offload_optimizer_device: cpu
  offload_param_device: cpu
  zero3_init_flag: true
  zero3_save_16bit_model: true
  zero_stage: 3
distributed_type: DEEPSPEED
downcast_bf16: 'no'
dynamo_backend: 'NO'
fsdp_config: {}
machine_rank: 0
main_training_function: main
megatron_lm_config: {}
mixed_precision: 'no'
num_machines: 1
num_processes: 1
rdzv_backend: static
same_network: true
use_cpu: false

重要部分

讓我們深入瞭解一下這個指令碼,以便您能看到發生了什麼,並理解其工作原理。

main函式中,指令碼建立了一個 Accelerator 類來初始化分散式訓練所需的所有必要條件。

💡 您可以在 main 函式中隨時更改模型和資料集。如果您的資料集格式與指令碼中的不同,您可能還需要編寫自己的預處理函式。

該指令碼還為您正在使用的 🤗 PEFT 方法建立了一個配置,在本例中是 LoRA。LoraConfig 指定了任務型別和重要引數,例如低秩矩陣的維度、矩陣縮放因子以及 LoRA 層的丟棄機率。如果您想使用不同的 🤗 PEFT 方法,請確保將 LoraConfig 替換為適當的

 def main():
+    accelerator = Accelerator()
     model_name_or_path = "facebook/bart-large"
     dataset_name = "twitter_complaints"
+    peft_config = LoraConfig(
         task_type=TaskType.SEQ_2_SEQ_LM, inference_mode=False, r=8, lora_alpha=32, lora_dropout=0.1
     )

在整個指令碼中,您會看到 main_process_firstwait_for_everyone 函式,它們有助於控制和同步程序的執行時機。

get_peft_model() 函式接收一個基礎模型和您之前準備的 peft_config,以建立一個 PeftModel

  model = AutoModelForSeq2SeqLM.from_pretrained(model_name_or_path)
+ model = get_peft_model(model, peft_config)

將所有相關的訓練物件傳遞給 🤗 Accelerate 的 prepare 方法,它會確保一切準備就緒以進行訓練。

model, train_dataloader, eval_dataloader, test_dataloader, optimizer, lr_scheduler = accelerator.prepare(
    model, train_dataloader, eval_dataloader, test_dataloader, optimizer, lr_scheduler
)

接下來的程式碼片段檢查 `Accelerator` 中是否使用了 DeepSpeed 外掛,如果外掛存在,則檢查是否正在使用 ZeRO-3。這個條件標誌用於在推理過程中呼叫 `generate` 函式時,當模型引數被分片時同步 GPU。

is_ds_zero_3 = False
if getattr(accelerator.state, "deepspeed_plugin", None):
    is_ds_zero_3 = accelerator.state.deepspeed_plugin.zero_stage == 3

在訓練迴圈中,通常的 `loss.backward()` 被 🤗 Accelerate 的 backward 替換,它會根據您的配置使用正確的 `backward()` 方法。

  for epoch in range(num_epochs):
      with TorchTracemalloc() as tracemalloc:
          model.train()
          total_loss = 0
          for step, batch in enumerate(tqdm(train_dataloader)):
              outputs = model(**batch)
              loss = outputs.loss
              total_loss += loss.detach().float()
+             accelerator.backward(loss)
              optimizer.step()
              lr_scheduler.step()
              optimizer.zero_grad()

就是這樣!指令碼的其餘部分處理訓練迴圈、評估,甚至為您將其推送到 Hub。

訓練

執行以下命令來啟動訓練指令碼。之前,您已將配置檔案儲存為 `ds_zero3_cpu.yaml`,因此您需要將該路徑傳遞給啟動器,並使用 `--config_file` 引數,如下所示:

accelerate launch --config_file ds_zero3_cpu.yaml examples/peft_lora_seq2seq_accelerate_ds_zero3_offload.py

您將看到一些跟蹤訓練期間記憶體使用情況的輸出日誌,一旦訓練完成,指令碼將返回準確率並將預測結果與標籤進行比較。

GPU Memory before entering the train : 1916
GPU Memory consumed at the end of the train (end-begin): 66
GPU Peak Memory consumed during the train (max-begin): 7488
GPU Total Peak Memory consumed during the train (max): 9404
CPU Memory before entering the train : 19411
CPU Memory consumed at the end of the train (end-begin): 0
CPU Peak Memory consumed during the train (max-begin): 0
CPU Total Peak Memory consumed during the train (max): 19411
epoch=4: train_ppl=tensor(1.0705, device='cuda:0') train_epoch_loss=tensor(0.0681, device='cuda:0')
100%|████████████████████████████████████████████████████████████████████████████████████████████| 7/7 [00:27<00:00,  3.92s/it]
GPU Memory before entering the eval : 1982
GPU Memory consumed at the end of the eval (end-begin): -66
GPU Peak Memory consumed during the eval (max-begin): 672
GPU Total Peak Memory consumed during the eval (max): 2654
CPU Memory before entering the eval : 19411
CPU Memory consumed at the end of the eval (end-begin): 0
CPU Peak Memory consumed during the eval (max-begin): 0
CPU Total Peak Memory consumed during the eval (max): 19411
accuracy=100.0
eval_preds[:10]=['no complaint', 'no complaint', 'complaint', 'complaint', 'no complaint', 'no complaint', 'no complaint', 'complaint', 'complaint', 'no complaint']
dataset['train'][label_column][:10]=['no complaint', 'no complaint', 'complaint', 'complaint', 'no complaint', 'no complaint', 'no complaint', 'complaint', 'complaint', 'no complaint']

注意事項

  1. 目前不支援在使用 PEFT 和 DeepSpeed 時進行合併,這會引發錯誤。
  2. 當使用 CPU offloading 時,使用 PEFT 縮小最佳化器狀態和梯度到介面卡權重大小所帶來的主要收益將體現在 CPU RAM 上,而不會節省 GPU 記憶體。
  3. 與停用 CPU offloading 相比,DeepSpeed Stage 3 和 qlora 在與 CPU offloading 一起使用時會導致更多的 GPU 記憶體使用。

💡 當您的程式碼需要合併(和取消合併)權重時,請嘗試事先使用 DeepSpeed Zero-3 手動收集引數。

import deepspeed

is_ds_zero_3 = ... # check if Zero-3

with deepspeed.zero.GatheredParameters(list(model.parameters()), enabled= is_ds_zero_3):
    model.merge_adapter()
    # do whatever is needed, then unmerge in the same context if unmerging is required
    ...
    model.unmerge_adapter()
< > 在 GitHub 上更新

© . This site is unofficial and not affiliated with Hugging Face, Inc.