Fine-tuning LLM через RLHF (Reinforcement Learning from Human Feedback)
RLHF — техника выравнивания LLM с человеческими предпочтениями, применённая в InstructGPT, Claude, Gemini. Три стадии: SFT (Supervised Fine-Tuning), Reward Model обучение, RL оптимизация через PPO. Делает модель не только умной, но и полезной, безвредной, честной.
Зачем RLHF, а не просто SFT
SFT на инструкциях даёт модель, которая умеет следовать формату. Но не умеет расставлять приоритеты между качеством ответов. RLHF добавляет сигнал предпочтения: ответ A лучше ответа B по критериям полезности/безопасности/стиля. Этот сигнал нельзя выразить через cross-entropy loss.
Без RLHF: модель оптимизирует правдоподобие следующего токена. С RLHF: оптимизирует reward от human proxy (reward model), удержанный KL-дивергенцией от SFT baseline.
Стадия 1: SFT
Fine-tuning базовой LLM на (prompt, quality_response) парах. Датасет: 10K–100K примеров качественных демонстраций.
from trl import SFTTrainer
from transformers import AutoModelForCausalLM, TrainingArguments
model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-3-8B")
trainer = SFTTrainer(
model=model,
train_dataset=dataset,
dataset_text_field="text",
max_seq_length=2048,
args=TrainingArguments(
output_dir="./sft-output",
per_device_train_batch_size=4,
gradient_accumulation_steps=8,
learning_rate=2e-5,
num_train_epochs=3,
bf16=True
)
)
trainer.train()
LoRA для SFT: PEFT/LoRA снижает требования к памяти с ~160 GB (70B full fine-tune) до ~40 GB (QLoRA 4-bit). r=64, alpha=128, target_modules=["q_proj","v_proj","k_proj","o_proj","gate_proj","up_proj","down_proj"].
Стадия 2: Reward Model
Reward Model (RM) принимает (prompt, response) → скаляр reward. Обучается на парных сравнениях.
Датасет сравнений: Аннотаторы оценивают пары ответов (y_w, y_l) — chosen/rejected. Источники: Anthropic HH-RLHF, OpenAI comparisons dataset, Alpaca Farm. Для домена — внутренние аннотаторы.
Архитектура RM: LLM с добавленной regression head (linear layer на [EOS] токене):
from trl import RewardTrainer, RewardConfig
# базовая модель = SFT модель
reward_model = AutoModelForSequenceClassification.from_pretrained(
"sft-output",
num_labels=1 # скаляр reward
)
reward_trainer = RewardTrainer(
model=reward_model,
train_dataset=comparison_dataset, # chosen/rejected pairs
args=RewardConfig(
output_dir="./reward-model",
per_device_train_batch_size=4,
gradient_accumulation_steps=4,
learning_rate=1e-5,
max_length=512
)
)
Bradley-Terry loss:
L = -log(sigmoid(r(y_w) - r(y_l))). Оптимизирует: reward выбранного ответа > reward отклонённого.
Метрики качества RM: Accuracy на held-out comparison dataset. Целевые значения: >70% (базовый), >75% (хороший), >80% (отличный). Выше 85% — риск overfitting на аннотаторский bias.
Стадия 3: PPO Fine-tuning
Proximal Policy Optimization оптимизирует LLM (policy) для максимизации reward при KL-ограничении:
Objective = E[r_θ(prompt, response)] - β * KL(π_θ || π_SFT)
β — коэффициент KL penalty. При β=0 — pure RL, модель может коллапсировать в reward hacking. При β слишком высоком — не отходит от SFT.
from trl import PPOTrainer, PPOConfig, AutoModelForCausalLMWithValueHead
ppo_config = PPOConfig(
model_name="sft-output",
learning_rate=1.41e-5,
batch_size=128,
mini_batch_size=16,
gradient_accumulation_steps=1,
ppo_epochs=4,
kl_penalty="kl",
init_kl_coef=0.2, # начальный β
target_kl=6.0, # адаптивная KL цель
adap_kl_ctrl=True # автоматическая коррекция β
)
ppo_trainer = PPOTrainer(
config=ppo_config,
model=AutoModelForCausalLMWithValueHead.from_pretrained("sft-output"),
ref_model=ref_model, # замороженный SFT reference
tokenizer=tokenizer,
reward_model=reward_model,
dataset=prompt_dataset
)
for batch in ppo_trainer.dataloader:
queries, responses = ppo_trainer.generate(batch['input_ids'], ...)
rewards = reward_model(queries, responses)
stats = ppo_trainer.step(queries, responses, rewards)
Value head: PPO требует оценку V(s) состояния. Добавляется linear layer поверх LLM — обучается совместно с policy.
Альтернативы PPO
DPO (Direct Preference Optimization): Убирает RM и PPO — напрямую оптимизирует предпочтения через reparameterization. Проще, стабильнее, но менее гибкий.
from trl import DPOTrainer, DPOConfig
dpo_trainer = DPOTrainer(
model=sft_model,
ref_model=ref_model,
beta=0.1, # temperature
train_dataset=comparison_dataset,
args=DPOConfig(output_dir="dpo-output", ...)
)
ORPO (Odds Ratio Preference Optimization): Объединяет SFT + preference alignment в один pass. Без reference модели.
SimPO: Sequence-level preference, reference-free.
Constitutional AI (CAI) — вариант Anthropic
Вместо human aннотаторов для RM: LLM-generated critique & revision. Набор принципов (constitution) → модель сама оценивает ответы → synthetic preference dataset → RM обучение. Снижает зависимость от дорогой human annotation.
Инфраструктура
Требования к GPU:
- SFT LLaMA-3 8B: 2× A100 80GB (QLoRA) или 8× A100 (full fine-tune)
- RM training: аналогично SFT
- PPO: самый resource-intensive — нужно держать в памяти policy + reference + RM одновременно. 8× A100 80GB для 8B модели, 32× для 70B
DeepSpeed ZeRO-3: Шардирование параметров/градиентов/оптимайзера между GPU. Обязательно для PPO на 70B+.
vLLM для генерации в PPO: Ускоряет sampling (rollout generation) в 10–20× vs HuggingFace generate. Критично — генерация занимает 80% времени PPO.
Мониторинг RLHF
W&B или MLflow для трекинга:
-
ppo/mean_scores— средний reward за эпоху (должен расти) -
ppo/kl_divergence— должен оставаться в [target_kl ± 30%] -
ppo/policy_loss— стабильность policy - Qualitative: регулярная ручная оценка сэмплов
Reward hacking детекция: перегенерация held-out промптов каждые N шагов, ручная оценка на наличие деградации (repetition, sycophancy, gibberish).
Сроки: 12–20 недель
Сбор comparison датасета — самая дорогая часть (6–10 недель при внутренней аннотации). Технический пайплайн SFT+RM+PPO: 4–6 недель. Итеративные улучшения Constitution + RM: бесконечный процесс.







