透過ORPO技術微調 llama3大模型(Fine-tune Llama 3 with ORPO)

aehyok發表於2024-04-23
1f45bd1e8577af66a05f5e3fadb0b291f45bd1e8577af66a05f5e3fadb0b29

透過ORPO對llama進行微調

前言

ORPO是一種新穎的微調技術,它將傳統的監督微調和偏好對齊階段整合到一個過程中。這減少了訓練所需的計算資源和時間。此外,經驗結果表明,ORPO在各種模型大小和基準測試中都超過了其他對齊方法。 在本文中,我們將使用ORPO和TRL庫來微調新的Llama 3 8B模型。程式碼可以在Google Colab(https://colab.research.google.com/drive/1eHNWg9gnaXErdAa8_mcvjMupbSS6rDvi?usp=sharing)和GitHub上的LLM(https://github.com/mlabonne/llm-course)課程中找到。

⚖️ ORPO

指令調整和偏好對齊是將大型語言模型(LLMs)適應特定任務的關鍵技術。傳統上,這涉及到一個多階段的過程:

  • 對指令進行監督式微調(SFT)以使模型適應目標領域
  • 像人類反饋的強化學習(RLHF)或直接優選最佳化(DPO)這樣的偏好對齊方法,以增加生成優選響應而非被拒絕響應的可能性。
微信圖片_20240423001958微信圖片_20240423001958

然而,研究人員發現這種方法的一個侷限性。就是監督微調(SFT)可以有效地讓模型適應特定領域,這也就是為什麼需要偏好對齊階段RLHF,擴大受歡迎輸出和不受歡迎輸出之間機率的差距。

imageimage

SFT過程中,不受歡迎機率增加實證 from ORPO論文

2024年Hong和Lee提出的ORPO透過將SFT和RLHF統一為一個完整訓練過程,為這個問題提供了一個優雅的解決方案。ORPO修改了標準language model的訓練目標,將負對數似然損失與odds ratio(OR)項結合起來。這種OR損失對不受歡迎的輸出施加了輕微的懲罰,同時加大獎勵受歡迎的輸出,允許模型同時學習目標任務並與人類偏好對齊。

91e1091deacae95fb17f1b6995b94c291e1091deacae95fb17f1b6995b94c2

ORPO已經在主要的微調庫中得到實現,比如TRL、Axolotl和LLaMA-Factory。在下一節中,我們將看到如何使用TRL進行操作。

💻 開始透過ORPO進行微調

Llama3是Meta開發的最新一代大型語言模型(LLM)。這些模型是在15萬億token的廣泛資料集上訓練的(相比之下,Llama2的訓練資料集為2萬億token)。釋出了兩種模型尺寸:一個700億引數的模型和一個更小的80億引數的模型。700億引數的模型已經展示了令人印象深刻的效能,在MMLU基準測試中得分為82,在HumanEval基準測試中得分為81.7。

Llama3模型還增加了上下文長度,最多可達8192個token(Llama2為4096個token),並且可能透過RoPE擴充套件到32k。此外,這些模型使用了一個帶有128K-token詞彙表的新分詞器,減少了編碼文字所需token數量的15%。這個詞彙表也解釋了從70億到80億引數的增長。

imageimage

ORPO需要一個偏好資料集,包括一個提示、一個被選擇的答案和一個被拒絕的答案。在這個例子中,我們將使用mlabonne/orpo-dpo- mix-40k,這是一個由以下高質量DPO資料集組合而成的資料集:

  • argilla/distilabel-capybara-dpo-7k-binarized : 高分選擇的答案 >=5(2,882個樣本) https://huggingface.co/datasets/argilla/distilabel-capybara-dpo-7k-binarized
  • argilla/distilabel-intel-orca-dpo-pairs : 高分選擇的答案 >=9,不在GSM8K中(2,299個樣本) https://huggingface.co/datasets/argilla/distilabel-intel-orca-dpo-pairs
  • argilla/ultrafeedback-binarized-preferences-cleaned : 高分選擇的答案 >=5(22,799個樣本) https://huggingface.co/datasets/argilla/ultrafeedback-binarized-preferences-cleaned
  • argilla/distilabel-math-preference-dpo : 高分選擇的答案 >=9(2,181個樣本) https://huggingface.co/datasets/argilla/distilabel-math-preference-dpo
  • unalignment/toxic-dpo-v0.2 (541個樣本) https://huggingface.co/datasets/unalignment/toxic-dpo-v0.2
  • M4-ai/prm_dpo_pairs_cleaned (7,958個樣本) https://huggingface.co/datasets/M4-ai/prm_dpo_pairs_cleaned
  • jondurbin/truthy-dpo-v0.1 (1,016個樣本) https://huggingface.co/datasets/jondurbin/truthy-dpo-v0.1 感謝argilla、unalignment、M4-ai和jondurbin提供了源資料集。

開始安裝所需的庫:

  pip install -U transformers datasets accelerate peft trl bitsandbytes wandb  

一旦安裝完成,我們可以匯入必要的庫,並登入到W&B(可選):

    import gc  
import os

import torch
import wandb
from datasets import load_dataset
from google.colab import userdata
from peft import LoraConfig, PeftModel, prepare_model_for_kbit_training
from transformers import (
AutoModelForCausalLM,
AutoTokenizer,
BitsAndBytesConfig,
TrainingArguments,
pipeline,
)
from trl import ORPOConfig, ORPOTrainer, setup_chat_format

wb_token = userdata.get('wandb')
wandb.login(key=wb_token)

如果你有一塊較新的GPU,你還應該能夠使用Flash Attention庫來替換預設的熱切關注實現,以一個更有效的方式來實現。

    if torch.cuda.get_device_capability()[0] >= 8:  
!pip install -qqq flash-attn
attn_implementation = "flash_attention_2"
torch_dtype = torch.bfloat16
else:
attn_implementation = "eager"
torch_dtype = torch.float16

接下來,我們將使用bitsandbytes以4位精度載入Llama 3 8B模型。然後,我們使用PEFT為QLoRA設定LoRA配置。我還使用了方便的setup_chat_format()函式來修改模型和為ChatML支援的分詞器。它會自動應用這個聊天模板,新增特殊的令牌,並調整模型的嵌入層的大小以匹配新的詞彙表大小。 請注意,你需要提交請求才能訪問meta-llama/Meta-Llama-3-8B,並且要登入到你的Hugging Face賬戶。或者,你可以載入未封閉的模型副本,如NousResearch/Meta--Llama-3-8B。

    # Model  
base_model = "meta-llama/Meta-Llama-3-8B"
new_model = "OrpoLlama-3-8B"

# QLoRA config
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch_dtype,
bnb_4bit_use_double_quant=True,
)

# LoRA config
peft_config = LoraConfig(
r=16,
lora_alpha=32,
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM",
target_modules=['up_proj', 'down_proj', 'gate_proj', 'k_proj', 'q_proj', 'v_proj', 'o_proj']
)

# Load tokenizer
tokenizer = AutoTokenizer.from_pretrained(base_model)

# Load model
model = AutoModelForCausalLM.from_pretrained(
base_model,
quantization_config=bnb_config,
device_map="auto",
attn_implementation=attn_implementation
)
model, tokenizer = setup_chat_format(model, tokenizer)
model = prepare_model_for_kbit_training(model)

現在模型已經準備好進行訓練,我們可以處理資料集。我們載入mlabonne/orpo-dpo-mix-40k,並使用apply_chat_template()函式將“chosen”和“rejected”列轉換為ChatML格式。請注意,我只使用了1,000個樣本,而不是整個資料集,因為執行起來會花費太長時間。

    dataset_name = "mlabonne/orpo-dpo-mix-40k"  
dataset = load_dataset(dataset_name, split="all")
dataset = dataset.shuffle(seed=42).select(range(10))

def format_chat_template(row):
row["chosen"] = tokenizer.apply_chat_template(row["chosen"], tokenize=False)
row["rejected"] = tokenizer.apply_chat_template(row["rejected"], tokenize=False)
return row

dataset = dataset.map(
format_chat_template,
num_proc= os.cpu_count(),
)
dataset = dataset.train_test_split(test_size=0.01)

首先,我們需要設定一些超引數: 學習率:與傳統的SFT或者DPO相比,ORPO使用的學習率非常低。這個值8e-6來自原始論文,大致對應於SFT的學習率1e-5和DPO的學習率5e-6。我建議在真正的微調中將其增加到大約1e-6。 beta:它是論文中的引數,其預設值為0.1。來自原始論文的一個附錄顯示瞭如何透過消融研究選擇它。 其他引數,如最大長度和批次大小,都設定為儘可能多地使用VRAM(在此配置中約為20 GB)。理想情況下,我們將對模型進行3-5個週期的訓練,但這裡我們將堅持1個週期。 最後,我們可以使用ORPOTrainer來訓練模型,它充當一個包裝器。

    orpo_args = ORPOConfig(  
learning_rate=8e-6,
beta=0.1,
lr_scheduler_type="linear",
max_length=1024,
max_prompt_length=512,
per_device_train_batch_size=2,
per_device_eval_batch_size=2,
gradient_accumulation_steps=4,
optim="paged_adamw_8bit",
num_train_epochs=1,
evaluation_strategy="steps",
eval_steps=0.2,
logging_steps=1,
warmup_steps=10,
report_to="wandb",
output_dir="./results/",
)

trainer = ORPOTrainer(
model=model,
args=orpo_args,
train_dataset=dataset["train"],
eval_dataset=dataset["test"],
peft_config=peft_config,
tokenizer=tokenizer,
)
trainer.train()
trainer.save_model(new_model)

在L4 GPU上對這1000個樣本進行模型訓練大約需要2個小時。讓我們檢視W&B的圖:

imageimage

當loss降低時,受歡迎輸出和不受歡迎輸出之間的差異並不明顯:平均邊界和準確度分別僅略高於0和0.5。

在原始論文中,作者們在 Anthropic/hh-rlhf 資料集(161k個樣本)上訓練模型進行了10個epochs,這比我們現在執行的時間要長得多。他們還對Llama3進行了實驗,並且友好地與我分享了他們的日誌(感謝Jiwoo Hong)。

在本教程的結尾,讓我們將QLoRA介面卡與基礎模型合併,並將其推送到Hugging Face Hub。

    # Flush memory  
del trainer, model
gc.collect()
torch.cuda.empty_cache()

# Reload tokenizer and model
tokenizer = AutoTokenizer.from_pretrained(base_model)
model = AutoModelForCausalLM.from_pretrained(
base_model,
low_cpu_mem_usage=True,
return_dict=True,
torch_dtype=torch.float16,
device_map="auto",
)
model, tokenizer = setup_chat_format(model, tokenizer)

# Merge adapter with base model
model = PeftModel.from_pretrained(model, new_model)
model = model.merge_and_unload()

model.push_to_hub(new_model, use_temp_dir=False)
tokenizer.push_to_hub(new_model, use_temp_dir=False)

恭喜,我們完成了Llama3:mlabonne/OrpoLlama-3-8B的快速微調。你可以使用這個Hugging Face Space(這裡有一個notebook,讓你自己來實踐)來使用它。儘管模型訓練不足,正如W&B曲線所強調的那樣,我還是使用LLM AutoEval在Nous的基準測試套件上進行了一些評估。

imageimage

我們的ORPO微調實際上相當不錯,並且提高了基礎模型在每個基準測試上的效能。這是令人鼓舞的,並且很可能意味著在整個40k樣本上進行微調將帶來很好的結果。

對於開源社群來說,這是一個激動人心的時刻,越來越多的高質量開放權重模型被髮布。閉源和開放權重模型之間的差距正在逐漸縮小,而微調是獲取您用例最佳效能的重要工具。

imageimage

結論

在這篇教程中,我們介紹了ORPO演算法,並解釋了它如何將SFT(監督式微調)和RLHF統一為單一的過程。然後,我們使用TRL(Transformer Reinforcement Learning)對一個定製的偏好資料集上的Llama3-8B進行微調。最終模型展示了令人鼓舞的結果,並突顯了ORPO作為新的微調正規化的潛力。

我希望這很有幫助,並推薦你執行Colab筆記本來微調你自己的Llama3模型。在將來的文章中,我們將看到如何建立高質量的資料集——這是一個經常被忽視的點。

最後文章參考自:https://huggingface.co/blog/mlabonne/orpo-llama-3

相關文章