Direct Preference Optimization
DPO - Tối ưu hóa sở thích trực tiếp
RLHF cần 3 pha phức tạp (SFT → Reward Model → PPO) và thường mất hàng nghìn GPU-hour để stabilize. Liệu có cách nào đạt được cùng kết quả alignment chỉ bằng 1 pha supervised learning?
Hãy tưởng tượng bạn đang dạy một đầu bếp mới biết nấu món nào hợp khẩu vị khách hàng. Có hai cách:
Cách RLHF: bạn thuê một giám khảo ẩm thực (reward model), cho họ ăn hàng ngàn món và học cách chấm điểm; rồi đầu bếp nấu → giám khảo chấm → đầu bếp học theo điểm → lặp lại hàng vạn lần. Vòng lặp này tốn thời gian, và nếu giám khảo bị "hack" (ví dụ chỉ thích món có nước sốt nhiều), đầu bếp sẽ tưới sốt vào mọi thứ.
Cách DPO: bạn đưa đầu bếp xem từng cặp món đã có xếp hạng ("món A được khách ưa hơn món B") và dạy thẳng: "hãy viết công thức sao cho xác suất ra món A cao hơn xác suất ra món B". Không cần giám khảo trung gian, không cần vòng lặp RL bất ổn định. Đơn giản, trực tiếp, và điều bất ngờ là: về mặt toán học, nó tương đương với cách RLHF khi tối ưu hoàn hảo.
Cái hay của DPO là nó hé lộ một sự thật sâu sắc về alignment: toàn bộ vòng lặp RL phức tạp thực ra chỉ là cách gián tiếp để làm một việc đơn giản — đẩy xác suất y_chosen lên và y_rejected xuống, có kiểm soát bởi mô hình tham chiếu. Khi hiểu được cấu trúc ẩn này, chúng ta có thể làm thẳng thay vì vòng vo.
Hình minh họa
RLHF vs DPO — So sánh pipeline
Chuyển đổi giữa hai phương pháp để thấy sự khác biệt về số bước, số mô hình trung gian và độ ổn định.
RLHF
- 3 pha, 3 mô hình (SFT, RM, Policy)
- Vòng lặp PPO — bất ổn, nhiều HP
- Có khả năng exploration online
- Rủi ro reward hacking cao
DPO
- 2 pha, 1 mô hình + 1 π_ref đóng băng
- Ổn định như supervised learning
- Bị giới hạn bởi dataset preference
- Triển khai nhanh, ít hyperparameter
Cho một cặp (prompt, y_chosen, y_rejected) — xem DPO tính loss
Chọn cặp bất kỳ và trượt β để quan sát cách DPO đo margin giữa reward ngầm của y_chosen và y_rejected.
Prompt
Giải thích thuật toán Dijkstra bằng ngôn ngữ đơn giản.
y_chosen (được ưa hơn)
Dijkstra giống như lan nước: bắt đầu ở đỉnh nguồn, nước chảy ra mọi cạnh, điểm nào ướt trước thì khoảng cách ngắn nhất đã được tìm thấy.
log π_ref(y_w|x) = -8.20
log π_θ(y_w|x) = -6.80
y_rejected (bị loại)
Dijkstra là một giải thuật đồ thị tính khoảng cách. Bạn tự tra tài liệu thêm.
log π_ref(y_l|x) = -6.50
log π_θ(y_l|x) = -9.10
Ghi chú: Cặp điển hình: phản hồi dài, có ẩn dụ, được con người ưa hơn.
r(x,y_w) = log π_θ/π_ref
1.400
r(x,y_l)
-2.600
margin = β·(r_w − r_l)
0.400
loss = −log σ(margin)
0.513
Xác suất mô hình "hiểu đúng" cặp preference (σ của margin): 59.9%. Tổng loss trung bình trên 4 cặp: 0.440.
Giả sử π_ref ưu tiên câu trả lời bịa đặt (refLogp rejected cao hơn chosen). Sau khi DPO huấn luyện tới, dấu hiệu nào cho thấy mô hình đã 'đảo ngược' được ưu tiên đó?
Bạn đang train DPO và quan sát: với 2.000 step đầu, loss giảm đẹp; sau đó log π_θ(y_rejected) tiếp tục giảm mạnh xuống −40, −80, ... trong khi log π_θ(y_chosen) gần như không đổi. Điều gì đang xảy ra?
Giải thích
DPO (Direct Preference Optimization) là thuật toán alignment được Rafailov et al. công bố năm 2023. Nó biến bài toán RLHF thành một bài toán supervised learning đơn giản, đạt cùng mục tiêu alignment nhưng không cần reward model riêng, không cần PPO và không cần vòng lặp RL bất ổn định.
Xuất phát từ bài toán RLHF:
Lời giải closed-form nổi tiếng của bài này (với R cố định) là:
Đảo ngược quan hệ trên, ta thu được reward ngầm ẩn viết qua policy:
Ghép với mô hình Bradley–Terry cho preference (xác suất y_w được ưa hơn y_l bằng sigmoid của hiệu reward), thành phần log Z(x) triệt tiêu — và ta thu được hàm loss DPO cốt lõi:
Ba quan sát quan trọng về loss DPO:
- β đóng vai trò KL coefficient. β nhỏ cho phép π_θ lệch mạnh khỏi π_ref; β lớn giữ chặt π_θ gần π_ref. Giá trị điển hình là 0.1–0.5.
- π_ref thường là bản sao đông cứng của checkpoint SFT. Không được cập nhật — nó là "điểm neo" để loss có ý nghĩa KL.
- Gradient của loss có dạng trực quan: phần gradient đẩy y_chosen lên và kéo y_rejected xuống được nhân với một hệ số phụ thuộc vào "mức độ mô hình đã hiểu sai cặp này". Cặp dễ → gradient nhỏ; cặp mô hình đang sai → gradient lớn.
# Tham khảo: https://huggingface.co/docs/trl/dpo_trainer
import torch
from datasets import load_dataset
from transformers import AutoModelForCausalLM, AutoTokenizer
from trl import DPOTrainer, DPOConfig
# 1. Tải mô hình SFT và bản sao làm reference
model_id = "Qwen/Qwen2-0.5B-SFT" # mô hình đã qua SFT
model = AutoModelForCausalLM.from_pretrained(
model_id, torch_dtype=torch.bfloat16, device_map="auto"
)
ref_model = AutoModelForCausalLM.from_pretrained(
model_id, torch_dtype=torch.bfloat16, device_map="auto"
)
tokenizer = AutoTokenizer.from_pretrained(model_id)
# 2. Dataset preference: mỗi mẫu có 3 trường {prompt, chosen, rejected}
dataset = load_dataset("trl-lib/ultrafeedback_binarized", split="train")
# 3. Cấu hình DPOConfig
training_args = DPOConfig(
output_dir="./qwen-dpo",
num_train_epochs=1,
per_device_train_batch_size=2,
gradient_accumulation_steps=8,
learning_rate=5e-7,
lr_scheduler_type="cosine",
warmup_ratio=0.1,
beta=0.1, # β của DPO — điển hình 0.1
loss_type="sigmoid", # "sigmoid" = DPO gốc; "ipo" = IPO; "kto_pair" = KTO pair
max_length=1024,
max_prompt_length=512,
bf16=True,
logging_steps=10,
save_strategy="epoch",
report_to="tensorboard",
)
# 4. Trainer — không cần reward model, không cần PPO
trainer = DPOTrainer(
model=model,
ref_model=ref_model,
args=training_args,
train_dataset=dataset,
tokenizer=tokenizer,
)
trainer.train()
trainer.save_model("./qwen-dpo/final")
# Log ra: loss/dpo, rewards/chosen, rewards/rejected, rewards/margin, rewards/accuracies
Trong thực tế, DPO là lựa chọn mặc định cho hầu hết team alignment có dữ liệu preference sẵn. Llama 3, Zephyr, Tulu 2, Mistral Instruct — tất cả đều dùng DPO hoặc biến thể của nó. RLHF với PPO chỉ còn giữ vai trò trong các bài toán thực sự cần exploration (ví dụ reasoning dài, code). Với mọi use case alignment tiêu chuẩn, DPO là lựa chọn đầu tiên nên thử.
- DPO biến pipeline RLHF 3 pha thành 2 pha (SFT → DPO) — không cần reward model riêng, không cần PPO.
- Dữ liệu: bộ ba (prompt, y_chosen, y_rejected) từ xếp hạng con người hoặc AI.
- Loss: log-sigmoid của margin giữa reward ngầm y_chosen và y_rejected, với reward ngầm là β·log(π_θ/π_ref).
- β đóng vai trò KL coefficient: nhỏ → cập nhật mạnh, lớn → thận trọng; điển hình 0.1–0.5.
- Thất bại tiêu biểu: đẩy log π(y_rejected) xuống −∞ khi train lâu — khắc phục bằng IPO, early stopping, lr thấp.
- Nên chọn DPO khi có preference data sẵn; chọn RLHF/GRPO khi bài toán cần exploration thực sự (reasoning, code).
Kiểm tra hiểu biết
DPO khác RLHF chủ yếu ở điểm nào về mặt pipeline huấn luyện?