PPO и прямое обучение с подкреплением: как научить LLM говорить так, как хочет человек
Введение: почему GPT-4 отвечает лучше, чем GPT-3
Если вы возьмёте GPT-3 (базовую версию) и попросите написать эссе — получите связный, но часто поверхностный текст. GPT-4 отвечает структурированнее, точнее, с учётом контекста и намерений пользователя.
Разница не в архитектуре. Обе — декодер-трансформеры. Разница в финальном этапе обучения.
GPT-3: Pretrain → Next-token prediction
GPT-4: Pretrain → Next-token prediction → RLHF (PPO) → Alignment
RLHF (Reinforcement Learning from Human Feedback) — это процесс, при котором модель дообучается так, чтобы её ответы соответствовали человеческим ожиданиям. А PPO (Proximal Policy Optimization) — алгоритм, который это обеспечивает.
В этой статье — полный разбор PPO для LLM: от теории к коду, от OpenAI Classic к open-source альтернативам.
Контекст: три этапа обучения LLM
Этап 1: Pre-training (самообучение на тексте)
Модель читает триллионы токенов и учится предсказывать следующее слово.
Вход: "Столица Франции — "
Выход: " Париж" (высокая вероятность)
Результат: модель знает язык, факты, логику. Но она не умеет вести диалог и отвечает в стиле "продолжения текста", а не "помощи пользователю".
Этап 2: SFT — Supervised Fine-Tuning (обучение диалогу)
Люди пишут примеры диалогов "вопрос → ответ", и модель учится формату.
{
"messages": [
{"role": "user", "content": "Напиши функцию сортировки"},
{"role": "assistant", "content": "Конечно! Вот быстрая сортировка..."}
]
}
Результат: модель умеет диалог. Но она отвечает всё подряд — включая вредные, предвзятые или неточные ответы.
Этап 3: RLHF — обучение с подкреплением (выравнивание)
Модель дообучается так, чтобы её ответы нравилась людям. Именно здесь — PPO.
Результат: модель отвечает полезнее, честнее, безопаснее.
Почему именно PPO?
Альтернативы RLHF
| Метод | Год | Сложность | Качество | Использование |
|---|---|---|---|---|
| PPO (RLHF) | 2022 | Высокая | ★★★★★ | GPT-3.5, Claude |
| DPO | 2023 | Низкая | ★★★★ | Llama 3, Mistral |
| KTO | 2024 | Средняя | ★★★★ | Некоторые модели |
| ORPO | 2024 | Низкая | ★★★ | Экспериментальные |
PPO — самый зрелый и эффективный метод alignment. Именно его использовала OpenAI для GPT-3.5 и Anthropic для Claude.
DPO (Direct Preference Optimization) — более простая альтернатива, которая набирает популярность. Но PPO всё ещё даёт лучшее качество при правильной настройке.
Теория PPO: от первого лица агента
Reinforcement Learning: базовая схема
┌──────────┐ действия ┌─────────┐
│ Agent │ ──────────────→ │ Environment │
│ (LLM) │ ←────────────── │ (Prompt) │
│ │ reward └─────────┘
└──────────┘
- State (состояние): prompt пользователя
- Action (действие): генерация следующего токена
- Reward (награда): оценка ответа (чем лучше — тем выше)
- Policy (политика): функция P, которая определяет, какой токен выбрать
Цель PPO: максимизировать суммарную награду за весь ответ.
Policy Gradient: простой, но проблемный подход
Самый простой RL-алгоритм — Policy Gradient. Увеличиваем вероятность "хороших" действий, уменьшаем вероятность "плохих".
Проблема: обновления слишком большие → модель "взрывается"
Результат: модель забывает всё, чему училась на этапах 1 и 2
PPO: стабильные обновления
PPO добавляет clip-механизм, который не позволяет обновлению быть слишком большим.
old_policy: "вероятность 'Париж' = 0.7"
new_policy: "вероятность 'Париж' = 0.9"
ratio = 0.9 / 0.7 = 1.29
clip(ratio, 0.8, 1.2) = 1.2 ← ограничиваем изменение на 20%
Ключевая формула PPO:
L^CLIP(θ) = E[min(ratio(t) · A(t), clip(ratio(t), 1-ε, 1+ε) · A(t))]
Где:
ratio(t)— отношение вероятностей старого и нового policyA(t)— advantage (насколько действие лучше среднего)ε— clip-параметр (обычно 0.1-0.3)
Проще говоря: PPO говорит модели "обучайся, но не слишком быстро, чтобы не забыть всё важное".
Архитектура RLHF с PPO: четыре модели
Это самая важная часть для понимания. В PPO-RLHF участвуют четыре модели:
┌─────────────────────────────────────────────────────────────┐
│ Training Pipeline │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Ref │ │ Actor │ │ Critic │ │
│ │ Model │ │ (LLM) │ │ (LLM) │ │
│ │ (frozen)│ │ (updates)│ │ (learns) │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ │ KL-пенальти │ Генерация │ Оценка value │
│ │ (стабильность)│ ответа │ │
│ ▼ ▼ │ │
│ ┌──────────┐ └───────────────────┼────┐ │
│ │ Reward │ ▼ │ │
│ │ Model │ ┌─────────────────┐ │
│ │ (scorer) │ │ Reward Model │ │
│ └──────────┘ │ (4: score) │ │
│ └─────────────────┘ │
└─────────────────────────────────────────────────────────────┘
1. Actor Model (Actor) — обучаемая LLM
Основная модель, которая генерирует ответы. Это SFT-модель, которая постепенно улучшается через PPO.
2. Critic Model (Критик) — оценивает ответы
Critic — это копия Actor, которая учится предсказывать value (ожидаемую суммарную награду) для данного состояния.
Critic предсказывает: "Этот частичный ответ в среднем даст награду 0.7"
3. Reference Model (Референс) — замороженная копия Actor
Копия Actor, которая не обучается. Используется для KL-пенальти — чтобы новый policy не отклонялся слишком сильно от оригинала.
KL(π_θ || π_ref) = Σ π_ref(x) · log(π_ref(x) / π_θ(x))
Если KL слишком большой → ответ слишком отличается от SFT → штраф.
4. Reward Model (Наградная модель) — ставит оценки
Отдельная модель, обученная предсказывать человеческие предпочтения.
Reward Model("Париж — столица Франции.") → 0.85
Reward Model("Не знаю, наверное Лондон.") → 0.30
Reward Model("Франция. Столица — Париж.") → 0.90
Пошаговый процесс PPO-RLHF
Шаг 1: Сбор данных предпочтений
Люди (или LLM) ранжируют ответы.
{
"prompt": "Столица Франции?",
"chosen": "Париж — столица Франции.",
"rejected": "Не знаю, наверное Лондон."
}
Шаг 2: Обучение Reward Model
from transformers import AutoModelForSequenceClassification
from trl import RewardTrainer
reward_model = AutoModelForSequenceClassification.from_pretrained(
"mistralai/Mistral-7B-Instruct-v0.3",
num_labels=1
)
trainer = RewardTrainer(
model=reward_model,
args=TrainingArguments(
per_device_train_batch_size=4,
num_epochs=3,
learning_rate=1e-5,
),
train_dataset=preference_dataset,
)
trainer.train()
Шаг 3: PPO-обучение
from trl import PPOTrainer, PPOConfig
config = PPOConfig(
model_name="mistralai/Mistral-7B-Instruct-v0.3",
learning_rate=1.41e-5,
batch_size=128,
mini_batch_size=32,
gradient_accumulation_steps=4,
ppo_epochs=4,
cliprange=0.2,
cliprange_value=0.2,
gamma=1.0, # discount factor
lam=0.95, # GAE lambda
kl_coef=0.001, # KL-пенальти
)
ppo_trainer = PPOTrainer(config=config)
for epoch in range(num_epochs):
for batch in dataloader:
# 1. Генерация ответа Actor'ом
generated = ppo_trainer.generate(
batch["prompt"],
max_new_tokens=512,
temperature=0.7
)
# 2. Оценка через Reward Model
reward = reward_model(generated)
# 3. Вычисление advantage (GAE)
advantage = compute_gae(
values=critic_output,
rewards=reward,
gamma=config.gamma,
lam=config.lam
)
# 4. PPO update
loss = ppo_trainer.step(
queries=batch["prompt"],
responses=generated,
rewards=reward,
advantage=advantage,
log_values=critic_output
)
# 5. KL-контроль
kl_penalty = compute_kl_penalty(
generated, reference_model, kl_coef=config.kl_coef
)
Шаг 4: Валидация и деплой
# Тестирование на holdout-промптах
test_prompts = [
"Напиши вежливый отказ клиенту",
"Объясни квантовую физику 5-летнему",
"Код для парсинга сайта на Python"
]
for prompt in test_prompts:
response = ppo_trainer.generate(prompt, max_new_tokens=512)
print(f"Prompt: {prompt}")
print(f"Response: {response}")
print("---")
GAE: Generalized Advantage Estimation
Что такое advantage?
advantage = reward - baseline
Если reward = 0.9, baseline = 0.5 → advantage = +0.4 (хорошее действие)
Если reward = 0.3, baseline = 0.5 → advantage = -0.2 (плохое действие)
GAE: баланс между смещением и дисперсией
GAE(δ, λ) = δ[t] + (γλ)·δ[t+1] + (γλ)²·δ[t+2] + ...
δ[t] = r[t] + γ·V(s[t+1]) - V(s[t]) — temporal difference error
γ — discount factor (обычно 1.0)
λ — smoothing parameter (обычно 0.95)
| λ | Смещение | Дисперсия | Когда использовать |
|---|---|---|---|
| 0.0 | Высокое | Низкое | Короткие ответы |
| 0.95 | Низкое | Низкое | Стандарт (рекомендуется) |
| 1.0 | Низкое | Высокое | Длинные ответы, стабильный training |
Практика: обучение с помощью TRL
Установка
pip install transformers trl accelerate peft torch
pip install datasets wandb
Полный пример PPO-RLHF
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM
from trl import PPOTrainer, PPOConfig, AutoModelForCausalLMWithValueHead
from datasets import Dataset
# 1. Загрузка моделей
tokenizer = AutoTokenizer.from_pretrained("mistralai/Mistral-7B-Instruct-v0.3")
tokenizer.pad_token = tokenizer.eos_token
# Actor + Critic (одна модель с value head)
actor_critic = AutoModelForCausalLMWithValueHead.from_pretrained(
"mistralai/Mistral-7B-Instruct-v0.3"
)
# Reference model (замороженная)
ref_model = AutoModelForCausalLMWithValueHead.from_pretrained(
"mistralai/Mistral-7B-Instruct-v0.3"
)
# 2. Подготовка данных
prompts = [
"Пользователь: Как отменить подписку на сервис?\nАссистент:",
"Пользователь: Объясни разницу между SQL и NoSQL\nАссистент:",
"Пользователь: Напиши поздравление с днём рождения\nАссистент:",
]
dataset = Dataset.from_dict({"prompt": prompts})
# 3. Конфигурация PPO
config = PPOConfig(
model_name="mistral-7b-ppo",
learning_rate=1.41e-5,
batch_size=8,
mini_batch_size=4,
gradient_accumulation_steps=2,
ppo_epochs=2,
cliprange=0.2,
cliprange_value=0.2,
gamma=1.0,
lam=0.95,
kl_coef=0.05,
vf_coef=0.1,
scale_reward="ref",
normalize_reward=True,
top_k=0,
top_p=1.0,
target=6.0,
log_with="wandb",
)
# 4. Инициализация PPO Trainer
ppo_trainer = PPOTrainer(
config=config,
model=actor_critic,
ref_model=ref_model,
tokenizer=tokenizer,
dataset=dataset,
)
# 5. Обучение
for epoch, batch in ppo_trainer.dataloader:
# Генерация
question_tensors = batch["prompt"]
response = ppo_trainer.generate(
question_tensors,
max_new_tokens=256,
temperature=0.7,
top_p=1.0,
)
# Вычисление rewards (в реальном сценарии — через Reward Model)
reward_scores = [0.8, 0.5, 0.9, 0.6, 0.7, 0.4, 0.85, 0.55]
# PPO step
stats = ppo_trainer.step(
question_tensors, response, reward_scores
)
ppo_trainer.log_stats(
stats, batch, reward_scores
)
Использование собственного Reward Model
from transformers import AutoModelForSequenceClassification
# Обученный Reward Model
reward_model = AutoModelForSequenceClassification.from_pretrained(
"./reward-model-mistral-7b",
num_labels=1,
)
def get_rewards(prompts, responses):
"""Вычисление rewards для batch"""
rewards = []
with torch.no_grad():
for prompt, response in zip(prompts, responses):
# Формируем текст для reward model
text = prompt + response
inputs = tokenizer(
text,
return_tensors="pt",
truncation=True,
max_length=512
)
# Получаем score
output = reward_model(**inputs)
rewards.append(output.logits[0].item())
return torch.tensor(rewards)
Open-source альтернативы PPO
1. TRL (Transformer Reinforcement Learning)
pip install trl
Самая популярная библиотека от Hugging Face. Поддерживает PPO, DPO, IPO, ORPO, KTO.
2. DeepSpeed + ORCA (Microsoft)
git clone https://github.com/microsoft/DeepSpeed
cd DeepSpeed/examples/Orca
Масштабируемый PPO на сотни GPU. Используется для обучения больших моделей.
3. ColossalChat
git clone https://github.com/hpcaitech/ColossalAI
cd ColossalAI/apps/chat
Полный стек RLHF: data collection → RM training → PPO → evaluation.
4. OpenRLHF
pip install openrlhf
Новая библиотека от Ant Group, оптимизированная для LLM. В 2-3 раза быстрее TRL.
Сравнение: PPO vs DPO
DPO (Direct Preference Optimization)
DPO упрощает RLHF до бинарной классификации. Вместо четырёх моделей — одна.
RLHF (PPO): 4 модели (Actor, Critic, Reference, Reward)
DPO: 1 модель (только Actor)
Когда использовать PPO, а когда DPO?
| Критерий | PPO | DPO |
|---|---|---|
| Качество | Лучше (стабильнее) | Почти такое же |
| Сложность | Высокая (4 модели) | Низкая (1 модель) |
| Стабильность | Требует тонкой настройки | Более стабильный |
| Ресурсы | 2-4 GPU (минимум) | 1-2 GPU |
| Контроль | Полный (можно кастомные rewards) | Ограниченный |
| Масштабируемость | Хорошая | Отличная |
Практическая рекомендация
Начинаете RLHF? → DPO (проще, быстрее)
Нужно максимальное качество? → PPO (но сложнее)
Мало ресурсов (1 GPU)? → DPO или QLoRA + DPO
Кастомные функции награды? → PPO (больше контроля)
Типичные проблемы и решения
Проблема 1: Reward hacking
Симптом: Модель учится "обманывать" reward model, генерируя ответы с высокой оценкой, но плохого качества.
Prompt: "Напиши эссе о климате"
Response: "Климат — это климат. Климат важен. Климат — это..."
Reward: 0.95 (reward model "ведётся" на повторения)
Решение:
- Используйте KL-пенальти (kl_coef = 0.05-0.1)
- Нормализуйте rewards
- Добавьте проверку на пересечения и повторения
- Используйте human-validated rewards
Проблема 2: Collapse policy
Симптом: Модель генерирует один и тот же ответ на все промпты.
На всё: "Спасибо за ваш вопрос! Давайте разберёмся вместе."
Решение:
- Увеличьте temperature при генерации (0.7 → 0.9)
- Уменьшите cliprange (0.2 → 0.1)
- Увеличьте KL-пенальти (0.001 → 0.01)
- Добавьте разнообразие в промпты
Проблема 3: Нестабильное обучение
Симптом: Loss скачет, reward растёт и падает.
Решение:
- Уменьшите learning rate (1.41e-5 → 7e-6)
- Увеличьте gradient_accumulation_steps
- Уменьшите batch_size
- Используйте gradient clipping (max_grad_norm=1.0)
Проблема 4: Out of Memory
Симптом: "CUDA out of memory" на 40 ГБ GPU.
Решение:
- Используйте QLoRA для Actor/Critic
- Включите gradient checkpointing
- Уменьшите max_new_tokens (512 → 256)
- Используйте DeepSpeed ZeRO-3
- Уменьшите batch_size до 1
Проблема 5: Reward model предвзят
Симптом: Reward model предпочитает длинные ответы, даже если они бессодержательные.
Решение:
- Обучайте RM на разнообразных данных
- Нормализуйте rewards по длине
- Добавьте penalty за длину
- Используйте human-validated evaluation
Параметры PPO: шпаргалка
Clip-параметры
| Параметр | Стандарт | Диапазон | Описание |
|---|---|---|---|
cliprange |
0.2 | 0.1-0.3 | Clip-радиус для policy |
cliprange_value |
0.2 | 0.1-0.3 | Clip-радиус для value head |
kl_coef |
0.05 | 0.001-0.5 | Вес KL-пенальти |
Оптимизация
| Параметр | Стандарт | Диапазон | Описание |
|---|---|---|---|
learning_rate |
1.41e-5 | 1e-6 - 1e-4 | Learning rate |
batch_size |
128 | 32-512 | Размер batch |
mini_batch_size |
32 | 8-128 | Размер mini-batch |
gradient_accumulation_steps |
4 | 1-16 | Накопление градиентов |
ppo_epochs |
4 | 1-10 | Эпохи PPO |
GAE
| Параметр | Стандарт | Диапазон | Описание |
|---|---|---|---|
gamma |
1.0 | 0.9-1.0 | Discount factor |
lam |
0.95 | 0.8-1.0 | GAE smoothing |
Value function
| Параметр | Стандарт | Диапазон | Описание |
|---|---|---|---|
vf_coef |
0.1 | 0.01-1.0 | Вес value loss |
scale_reward |
"ref" | "ref"/"vanilla" | Масштабирование rewards |
normalize_reward |
True | True/False | Нормализация rewards |
target |
6.0 | 4.0-10.0 | Target KL |
Чек-лист: запуск PPO-RLHF
- Обучить SFT-модель (или взять готовую)
- Собрать данные предпочтений (1000+ пар chosen/rejected)
- Обучить Reward Model
- Подготовить Reference Model (копия SFT)
- Подготовить Actor/Critic (одна модель с value head)
- Настроить PPO-параметры (начните с conservative)
- Запустить обучение, следить за:
- Loss падает стабильно
- KL divergence < 10 (желательно < 3)
- Reward растёт
- Генерации не collapsed
- Провести human evaluation
- Сравнить с SFT-моделью на holdout-промптах
- Деплой обученной модели
Итоги
PPO — это золотой стандарт alignment LLM. Именно он делает GPT-4 лучше GPT-3, Claude — лучше базовых моделей.
Ключевые выводы:
- PPO требует 4 модели: Actor, Critic, Reference, Reward
- GAE — эффективная оценка advantage для длинных ответов
- KL-пенальти защищает от collapse policy
- DPO — более простая альтернатива для старта
- TRL — лучший open-source фреймворк для PPO-RLHF
Когда стоит использовать PPO?
✅ Да, если:
- У вас есть ресурсы (2+ GPU, 80GB VRAM)
- Нужно максимальное качество alignment
- Есть данные предпочтений (1000+ пар)
- Нужен кастомный reward function
❌ Нет, если:
- У вас 1 GPU с 24GB VRAM → используйте DPO
- Нет данных предпочтений → соберите их сначала
- Нужен быстрый результат → начните с DPO
Что дальше?
- DPO — если PPO кажется слишком сложным
- RLAIF — если хотите заменить human feedback на AI feedback
- ORPO — если хотите объединить SFT и RLHF в один шаг
Ресурсы
- PPO Paper (Schulman et al., 2017)
- RLHF Paper (Ouyang et al., 2022)
- TRL Documentation
- DPO Paper (Rafailov et al., 2023)
- OpenRLHF
- ColossalChat
Если вам интересна тема alignment LLM, посмотрите наши статьи про DPO и reinforcement learning.