開源 AI 食譜文件

在消費級 GPU 上使用 TRL 和直接偏好最佳化 (DPO) 微調 SmolVLM

Hugging Face's logo
加入 Hugging Face 社群

並獲得增強的文件體驗

開始使用

Open In Colab

在消費級 GPU 上使用 TRL 和直接偏好最佳化 (DPO) 微調 SmolVLM

作者: Sergio Paniego

在本指南中,我們將指導您如何使用**Transformer 強化學習 (TRL)** 庫,透過**直接偏好最佳化 (DPO)** 來微調一個**小巧的 🤏 視覺語言模型 (VLM)**,以展示即使在消費級 GPU 上,您也可以根據特定需求定製 VLM。

我們將使用**偏好資料集**來微調 SmolVLM,以幫助模型與期望的輸出保持一致。SmolVLM 是一款效能高、記憶體效率高的模型,是完成此項任務的理想選擇。如果您對語言或視覺語言模型的**偏好最佳化**還不熟悉,可以檢視這篇部落格進行深入瞭解。

我們將使用的資料集是 HuggingFaceH4/rlaif-v_formatted,其中包含成對的**`提示 + 影像`**,以及每對的**`選擇`**和**`拒絕`**答案。此次微調過程的目標是使模型始終偏好資料集中的**選擇答案**,從而減少幻覺。

本 Notebook 已在 **NVIDIA L4 GPU** 上測試透過。

updated_fine_tuning_smol_vlm_diagram_dpo.png

1. 安裝依賴

讓我們先安裝微調所需的基本庫吧!🚀

!pip install  -U -q transformers trl datasets bitsandbytes peft accelerate
# Tested with transformers==4.46.3, trl==0.12.2, datasets==3.2.0, bitsandbytes==0.45.0, peft==0.14.0, accelerate==1.2.0
!pip install -q flash-attn --no-build-isolation

使用您的 Hugging Face 賬戶進行身份驗證,以便直接從本 Notebook 儲存和分享您的模型 🗝️。

from huggingface_hub import notebook_login

notebook_login()

2. 載入資料集 📁

我們將使用 HuggingFaceH4/rlaif-v_formatted 資料集,其中提供了成對的**`提示 + 影像`**,以及每對的**`選擇`**和**`拒絕`**答案。這種結構化格式非常適合使用**直接偏好最佳化 (DPO)** 進行模型訓練。

該資料集已經為此任務預先格式化。如果您使用自定義資料集,則需要將其預處理成相同的格式。

在此示例中,我們將使用資料集的一個子集來演示該過程。然而,在實際場景中,您應使用完整的資料集以獲得更好的效能。

from datasets import load_dataset

dataset_id = "HuggingFaceH4/rlaif-v_formatted"
train_dataset, test_dataset = load_dataset(dataset_id, split=["train[:6%]", "test[:1%]"])

我們將確保所有影像都格式化為 RGB

from PIL import Image


def ensure_rgb(example):
    # Convert the image to RGB if it's not already
    image = example["images"][0]
    if isinstance(image, Image.Image):
        if image.mode != "RGB":
            image = image.convert("RGB")
        example["images"] = [image]
    return example


# Apply the transformation to the dataset
train_dataset = train_dataset.map(ensure_rgb, num_proc=32)
test_dataset = test_dataset.map(ensure_rgb, num_proc=32)

讓我們瀏覽一個數據集中的示例,以便更好地瞭解其結構和我們正在處理的資料型別。

train_dataset[20]
>>> train_dataset[20]["images"][0]

3. 使用 TRL 微調模型

3.1 載入量化模型以進行訓練 ⚙️

首先,讓我們使用 bitsandbytes 載入 SmolVLM-Instruct 模型的量化版本,並載入處理器。我們將使用 SmolVLM-Instruct

import torch
from transformers import Idefics3ForConditionalGeneration, AutoProcessor

model_id = "HuggingFaceTB/SmolVLM-Instruct"
from transformers import BitsAndBytesConfig

# BitsAndBytesConfig int-4 config
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True, bnb_4bit_use_double_quant=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.bfloat16
)

# Load model and tokenizer
model = Idefics3ForConditionalGeneration.from_pretrained(
    model_id,
    device_map="auto",
    torch_dtype=torch.bfloat16,
    quantization_config=bnb_config,
    _attn_implementation="flash_attention_2",
)
processor = AutoProcessor.from_pretrained(model_id)

3.2 設定 QLoRA 和 DPOConfig 🚀

在這一步中,我們將為我們的訓練設定配置 QLoRAQLoRA 是一種強大的微調技術,旨在減少記憶體佔用,使得即使在有限的硬體上也能高效地微調大型模型。

QLoRA 在傳統的 **LoRA** (Low-Rank Adaptation) 基礎上,引入了介面卡權重的量化。這一增強顯著降低了記憶體使用量並加快了訓練速度,使其成為資源受限環境的理想選擇。

>>> from peft import LoraConfig, get_peft_model

>>> # Configure LoRA
>>> peft_config = LoraConfig(
...     r=8,
...     lora_alpha=8,
...     lora_dropout=0.1,
...     target_modules=["down_proj", "o_proj", "k_proj", "q_proj", "gate_proj", "up_proj", "v_proj"],
...     use_dora=True,
...     init_lora_weights="gaussian",
... )

>>> # Apply PEFT model adaptation
>>> peft_model = get_peft_model(model, peft_config)

>>> # Print trainable parameters
>>> peft_model.print_trainable_parameters()
trainable params: 11,269,248 || all params: 2,257,542,128 || trainable%: 0.4992

接下來,我們將使用 `DPOConfig` 配置訓練選項。

from trl import DPOConfig

training_args = DPOConfig(
    output_dir="smolvlm-instruct-trl-dpo-rlaif-v",
    bf16=True,
    gradient_checkpointing=True,
    per_device_train_batch_size=1,
    per_device_eval_batch_size=1,
    gradient_accumulation_steps=32,
    num_train_epochs=5,
    dataset_num_proc=8,  # tokenization will use 8 processes
    dataloader_num_workers=8,  # data loading will use 8 workers
    logging_steps=10,
    report_to="tensorboard",
    push_to_hub=True,
    save_strategy="steps",
    save_steps=10,
    save_total_limit=1,
    eval_steps=10,  # Steps interval for evaluation
    eval_strategy="steps",
)

我們將使用 TRL 庫中的 DPOTrainer 類為**直接偏好最佳化 (DPO)** 定義訓練引數。

**DPO** 使用帶標籤的偏好資料來引導模型生成符合偏好的響應。TRL 的 DPOTrainer 會在訓練前**對資料集進行分詞**並將其儲存到磁碟。這個過程可能會消耗大量磁碟空間,具體取決於用於訓練的資料量。請做好相應規劃以避免儲存空間不足。

這一步可能需要一些時間,所以請放鬆並享受這個過程!😄

from trl import DPOTrainer

trainer = DPOTrainer(
    model=model,
    ref_model=None,
    args=training_args,
    train_dataset=train_dataset,
    eval_dataset=test_dataset,
    peft_config=peft_config,
    processing_class=processor,
)

開始訓練模型!🎉

trainer.train()

讓我們儲存結果 💾

trainer.save_model(training_args.output_dir)

4. 測試微調後的模型 🔍

在微調完我們的視覺語言模型 (VLM) 後,是時候評估它的效能了!在本節中,我們將使用 HuggingFaceH4/rlaif-v_formatted 資料集中的示例來測試模型。讓我們深入瞭解結果,評估模型與偏好響應的一致性如何!🚀

在開始之前,讓我們清理一下 GPU 記憶體,以確保流暢和最佳的效能。🧹

>>> import gc
>>> import time


>>> def clear_memory():
...     # Delete variables if they exist in the current global scope
...     if "inputs" in globals():
...         del globals()["inputs"]
...     if "model" in globals():
...         del globals()["model"]
...     if "processor" in globals():
...         del globals()["processor"]
...     if "trainer" in globals():
...         del globals()["trainer"]
...     if "peft_model" in globals():
...         del globals()["peft_model"]
...     if "bnb_config" in globals():
...         del globals()["bnb_config"]
...     time.sleep(2)

...     # Garbage collection and clearing CUDA memory
...     gc.collect()
...     time.sleep(2)
...     torch.cuda.empty_cache()
...     torch.cuda.synchronize()
...     time.sleep(2)
...     gc.collect()
...     time.sleep(2)

...     print(f"GPU allocated memory: {torch.cuda.memory_allocated() / 1024**3:.2f} GB")
...     print(f"GPU reserved memory: {torch.cuda.memory_reserved() / 1024**3:.2f} GB")


>>> clear_memory()
GPU allocated memory: 1.64 GB
GPU reserved memory: 2.01 GB

我們將使用與之前相同的流程重新載入基礎模型。

model = Idefics3ForConditionalGeneration.from_pretrained(
    model_id,
    device_map="auto",
    torch_dtype=torch.bfloat16,
    _attn_implementation="flash_attention_2",
)

processor = AutoProcessor.from_pretrained(model_id)

我們將訓練好的介面卡附加到預訓練模型上。該介面卡包含了訓練期間進行的微調調整,使基礎模型能夠在保持其核心引數不變的情況下利用新知識。透過整合介面卡,我們在不改變其原始結構的情況下增強了模型的能力。

adapter_path = "sergiopaniego/smolvlm-instruct-trl-dpo-rlaif-v"
model.load_adapter(adapter_path)

讓我們在一個未見過的樣本上評估模型。

test_dataset[20]
>>> test_dataset[20]["images"][0]

讓我們建立一個通用函式,可以用不同的樣本呼叫,以簡化測試過程。這個函式將使我們能夠高效地評估模型在多個示例上的效能,而無需為每個示例重寫程式碼。透過使用這個可重用的函式,我們可以快速評估模型在各種輸入下的表現。

def generate_text_from_sample(model, processor, sample, max_new_tokens=1024, device="cuda"):
    # Prepare the text input by applying the chat template
    text_input = processor.apply_chat_template(sample["prompt"], add_generation_prompt=True)

    image_inputs = []
    image = sample["images"][0]
    if image.mode != "RGB":
        image = image.convert("RGB")
    image_inputs.append([image])

    # Prepare the inputs for the model
    model_inputs = processor(
        text=text_input,
        images=image_inputs,
        return_tensors="pt",
    ).to(
        device
    )  # Move inputs to the specified device

    # Generate text with the model
    generated_ids = model.generate(**model_inputs, max_new_tokens=max_new_tokens)

    # Trim the generated ids to remove the input ids
    trimmed_generated_ids = [out_ids[len(in_ids) :] for in_ids, out_ids in zip(model_inputs.input_ids, generated_ids)]

    # Decode the output text
    output_text = processor.batch_decode(
        trimmed_generated_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False
    )

    return output_text[0]  # Return the first decoded output text

現在,我們準備好呼叫函式並評估模型了!🚀

output = generate_text_from_sample(model, processor, test_dataset[20])
output

該模型現在能夠根據提供的影像和提示生成響應。對於這樣的任務,將您的模型的效能與基準進行比較是很有用的,以瞭解它改進了多少,以及它與其他選項的對比情況。有關此比較的更多資訊和詳細資訊,請檢視這篇文章

💻 我開發了一個用於測試模型的示例應用程式,您可以在這裡找到它。

由於這裡我們只用資料集的一個子集進行了一個示例訓練,對於 Space,我使用了官方的 Hugging Face DPO 微調模型。您可以輕鬆地將其與另一個展示預訓練模型的 Space 進行比較,該 Space 可在這裡找到。

from IPython.display import IFrame

IFrame(src="https://sergiopaniego-smolvlm-trl-dpo-rlaif-v.hf.space", width=1000, height=800)

5. 繼續學習之旅 🧑‍🎓️

透過這些資源擴充套件您對視覺語言模型及相關工具的知識。

您也可以重新訪問 使用 Hugging Face 生態系統 (TRL) 微調視覺語言模型 (Qwen2-VL-7B) 中的“繼續學習之旅”部分。

這些資源將幫助您深化在多模態學習領域的知識和專業技能。

< > 在 GitHub 上更新

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