Accelerate 文件

在 DeepSpeed 中使用多個模型

Hugging Face's logo
加入 Hugging Face 社群

並獲得增強的文件體驗

開始使用

在 DeepSpeed 中使用多個模型

本指南假設您已閱讀並理解 DeepSpeed 使用指南

將 Accelerate 和 DeepSpeed 結合使用多個模型在以下場景中非常有用:

  • 知識蒸餾
  • RLHF 等訓練後技術(更多示例請參見 TRL 庫)
  • 一次性訓練多個模型

目前,Accelerate 提供了一個**非常實驗性**的 API 來幫助您使用多個模型。

本教程將重點介紹兩種常見用例:

  1. 知識蒸餾:訓練一個較小的學生模型來模仿一個更大、效能更好的教師模型。如果學生模型能放入單個 GPU,我們可以使用 ZeRO-2 進行訓練,並使用 ZeRO-3 對教師模型進行分片以進行推理。這比對兩個模型都使用 ZeRO-3 要快得多。
  2. 一次性訓練多個*不相關*的模型。

知識蒸餾

知識蒸餾是使用多個模型但只訓練其中一個模型的好例子。

通常情況下,您會對兩個模型使用單個 utils.DeepSpeedPlugin。然而,在這種情況下,有兩個獨立的配置。Accelerate 允許您建立和使用多個外掛,**當且僅當**它們位於一個 `dict` 中,以便您可以在需要時引用並啟用相應的外掛。

from accelerate.utils import DeepSpeedPlugin

zero2_plugin = DeepSpeedPlugin(hf_ds_config="zero2_config.json")
zero3_plugin = DeepSpeedPlugin(hf_ds_config="zero3_config.json")

deepspeed_plugins = {"student": zero2_plugin, "teacher": zero3_plugin}

`zero2_config.json` 應配置為完整訓練(因此,如果您不使用自己的 `scheduler` 和 `optimizer`,請指定它們),而 `zero3_config.json` 只應為推理模型配置,如下例所示。

{
    "bf16": {
        "enabled": "auto"
    },
    "zero_optimization": {
        "stage": 3,
        "overlap_comm": true,
        "reduce_bucket_size": "auto",
        "stage3_prefetch_bucket_size": "auto",
        "stage3_param_persistence_threshold": "auto",
        "stage3_max_live_parameters": "auto",
        "stage3_max_reuse_distance": "auto",
    },
    "train_micro_batch_size_per_gpu": 1
}

下面展示了一個 `zero2_config.json` 配置示例。

{
    "bf16": {
        "enabled": "auto"
    },
    "optimizer": {
        "type": "AdamW",
        "params": {
            "lr": "auto",
            "weight_decay": "auto",
            "torch_adam": true,
            "adam_w_mode": true
        }
    },
    "scheduler": {
        "type": "WarmupLR",
        "params": {
            "warmup_min_lr": "auto",
            "warmup_max_lr": "auto",
            "warmup_num_steps": "auto"
        }
    },
    "zero_optimization": {
        "stage": 2,
        "offload_optimizer": {
            "device": "cpu",
            "pin_memory": true
        },
    },
    "gradient_accumulation_steps": 1,
    "gradient_clipping": "auto",
    "train_batch_size": "auto",
    "train_micro_batch_size_per_gpu": "auto",
}

即使某個特定模型不被訓練,如果未指定 `train_micro_batch_size_per_gpu`,DeepSpeed 也會引發錯誤。

接下來,建立一個單獨的 Accelerator 並傳入這兩個配置。

from accelerate import Accelerator

accelerator = Accelerator(deepspeed_plugins=deepspeed_plugins)

現在讓我們看看如何使用它們。

學生模型

預設情況下,Accelerate 將 `dict` 中的第一項設定為預設或啟用的外掛(即 `"student"` 外掛)。您可以使用 utils.deepspeed.get_active_deepspeed_plugin() 函式來驗證這一點,以檢視哪個外掛已啟用。

active_plugin = get_active_deepspeed_plugin(accelerator.state)
assert active_plugin is deepspeed_plugins["student"]

`AcceleratorState` 也會將活動的 DeepSpeed 外掛儲存在 `state.deepspeed_plugin` 中。

assert active_plugin is accelerator.deepspeed_plugin

由於 `student` 是當前活動的外掛,讓我們繼續準備模型、最佳化器和排程器。

student_model, optimizer, scheduler = ...
student_model, optimizer, scheduler, train_dataloader = accelerator.prepare(student_model, optimizer, scheduler, train_dataloader)

現在是時候處理教師模型了。

教師模型

首先,您需要在 Accelerator 中指定應使用 `zero3_config.json` 配置。

accelerator.state.select_deepspeed_plugin("teacher")

這將停用 `"student"` 外掛並啟用 `"teacher"` 外掛。Transformers 內部的有狀態 DeepSpeed 配置會被更新,這會改變在使用 `deepspeed.initialize()` 時呼叫哪個外掛配置。這使您可以使用 Transformers 提供的自動 `deepspeed.zero.Init` 上下文管理器整合。

teacher_model = AutoModel.from_pretrained(...)
teacher_model = accelerator.prepare(teacher_model)

否則,您應該使用 `deepspeed.zero.Init` 手動初始化模型。

with deepspeed.zero.Init(accelerator.deepspeed_plugin.config):
    model = MyModel(...)

訓練

從這裡開始,只要 `teacher_model` 從不被訓練,您的訓練迴圈可以是任何您喜歡的形式。

teacher_model.eval()
student_model.train()
for batch in train_dataloader:
    with torch.no_grad():
        output_teacher = teacher_model(**batch)
    output_student = student_model(**batch)
    # Combine the losses or modify it in some way
    loss = output_teacher.loss + output_student.loss
    accelerator.backward(loss)
    optimizer.step()
    scheduler.step()
    optimizer.zero_grad()

訓練多個不相關的模型

訓練多個模型是一個更復雜的場景。在當前狀態下,我們假設每個模型在訓練期間都與另一個模型**完全不相關**。

這種情況仍然需要建立兩個 utils.DeepSpeedPlugin。但是,您還需要第二個 Accelerator,因為不同的 `deepspeed` 引擎在不同時間被呼叫。一個 Accelerator 一次只能攜帶一個例項。

然而,由於 state.AcceleratorState 是一個有狀態的物件,它已經知道可用的兩個 utils.DeepSpeedPlugin。您只需例項化第二個 Accelerator,無需額外引數。

first_accelerator = Accelerator(deepspeed_plugins=deepspeed_plugins)
second_accelerator = Accelerator()

您可以呼叫任一 `first_accelerator.state.select_deepspeed_plugin()` 來啟用或停用特定的外掛,然後呼叫 `prepare`。

# can be `accelerator_0`, `accelerator_1`, or by calling `AcceleratorState().select_deepspeed_plugin(...)`
first_accelerator.state.select_deepspeed_plugin("first_model")
first_model = AutoModel.from_pretrained(...)
# For this example, `get_training_items` is a nonexistent function that gets the setup we need for training
first_optimizer, first_scheduler, train_dl, eval_dl = get_training_items(model1)
first_model, first_optimizer, first_scheduler, train_dl, eval_dl = accelerator.prepare(
    first_model, first_optimizer, first_scheduler, train_dl, eval_dl
)

second_accelerator.state.select_deepspeed_plugin("second_model")
second_model = AutoModel.from_pretrained(...)
# For this example, `get_training_items` is a nonexistent function that gets the setup we need for training
second_optimizer, second_scheduler, _, _ = get_training_items(model2)
second_model, second_optimizer, second_scheduler = accelerator.prepare(
    second_model, second_optimizer, second_scheduler
)

現在您可以開始訓練了。

for batch in dl:
    outputs1 = first_model(**batch)
    first_accelerator.backward(outputs1.loss)
    first_optimizer.step()
    first_scheduler.step()
    first_optimizer.zero_grad()
    
    outputs2 = model2(**batch)
    second_accelerator.backward(outputs2.loss)
    second_optimizer.step()
    second_scheduler.step()
    second_optimizer.zero_grad()

資源

要檢視更多示例,請查閱 [Accelerate] 中當前的相關測試

< > 在 GitHub 上更新

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