PPO устарел. И вот почему это больно
Представь: ты тренируешь языковую модель решать математические задачи. Нужно RL-обучение. Берёшь PPO — стандарт де-факто. И тут начинается ад. Два гигантских нейрона: политика и критик. Каждый — сотни миллиардов параметров. Память заканчивается через 20 минут. График загрузки GPU напоминает кардиограмму инфарктника.
Команда DeepSeekMath посмотрела на эту конструкцию и спросила: "А зачем нам вообще отдельный критик?" Так родился GRPO — Group Relative Policy Optimization. Алгоритм, который выкидывает половину проблемы и работает в разы эффективнее.
Главная боль PPO: Критик — это отдельная модель, которую нужно обучать параллельно с политикой. Она оценивает "ценность" состояний, но занимает столько же памяти, сколько основная модель. Для Llama 70B это примерно 140GB GPU RAM только для двух моделей. Безумие.
Суть GRPO: сравнивай ответы, а не оценивай абсолютно
Вот ключевой инсайт GRPO. Вместо того чтобы обучать критика предсказывать "насколько хорош этот ответ", алгоритм говорит: "Давайте просто сравним несколько ответов между собой".
Работает это так:
- Для одного промпта генерируешь N разных ответов (обычно 4-8)
- Получаешь реварды для каждого ответа (например, от другой модели или по метрике)
- Сортируешь ответы по ревардам
- Используешь относительные ранги вместо абсолютных значений
Математика без магии: как выглядит loss-функция
Вот где начинается настоящая красота. Формула GRPO настолько проста, что сначала не верится, что она работает:
import torch
import torch.nn.functional as F
def grpo_loss(log_probs, rewards):
"""
log_probs: тензор размерности [batch_size, n_responses]
rewards: тензор размерности [batch_size, n_responses]
"""
# Нормализуем реварды в пределах группы
normalized_rewards = (rewards - rewards.mean(dim=-1, keepdim=True)) \
/ (rewards.std(dim=-1, keepdim=True) + 1e-8)
# Превращаем в вероятности через softmax
weights = F.softmax(normalized_rewards, dim=-1)
# Взвешенная сумма логарифмических вероятностей
loss = -(weights * log_probs).sum(dim=-1).mean()
return loss
Видишь? Никакого отдельного критика. Никаких сложных value-функций. Просто сравниваем ответы внутри группы и усиливаем те, что оказались лучше.
1 Почему это работает для reasoning-задач
Математические задачи — идеальный кейс для GRPO. Почему? Потому что здесь есть чёткие критерии: ответ либо правильный, либо нет. Или почти правильный. Не нужно тонких нюансов "насколько хорош этот поэтический образ".
Когда модель решает уравнение, можно:
- Проверить конечный ответ
- Оценить промежуточные шаги
- Даже посчитать "похожесть" на правильное решение
Это даёт чёткие реварды. А раз реварды чёткие — можно сравнивать ответы между собой. И не нужен критик, который пытается предсказать абсолютное значение.
PPO vs GRPO: битва на конкретных цифрах
| Параметр | PPO | GRPO | Выигрыш |
|---|---|---|---|
| Моделей для обучения | 2 (политика + критик) | 1 (только политика) | 50% памяти |
| Градиентных шагов | 2 (отдельно для политики и критика) | 1 | 2x скорость |
| Стабильность | Низкая (две модели могут расходиться) | Высокая (нет расхождения) | Меньше debugging |
| Качество на GSM8K | ~75% | ~82% (в DeepSeekMath) | +7% абсолютных |
Цифры не врут. GRPO не просто экономит ресурсы — он даёт лучшие результаты. В DeepSeekMath-R1 модель достигла 84.8% на GSM8K. И это без танцев с бубнами вокруг критика.
Как НЕ надо реализовывать GRPO: частые ошибки
Смотри, вот типичная ошибка новичков:
# ❌ НЕПРАВИЛЬНО: забываем нормализацию
# Это сломается, если реварды в разных группах имеют разный масштаб
def broken_grpo_loss(log_probs, rewards):
weights = F.softmax(rewards, dim=-1) # Опасность!
loss = -(weights * log_probs).sum(dim=-1).mean()
return loss
Почему это плохо? Представь: в одной группе реварды [0.9, 0.8, 0.7], а в другой [90, 80, 70]. Softmax превратит вторую группу в почти one-hot вектор. Обучение станет нестабильным.
Важно: Всегда нормализуй реварды внутри группы (вычитай среднее, дели на стандартное отклонение). Иначе алгоритм будет сравнивать несравнимое.
2 Размер группы — золотая середина
Ещё одна ошибка — неправильный выбор N (количество ответов на промпт).
- N=2: Слишком мало. Сравнение почти бесполезно
- N=4-8: Идеально. Есть что сравнивать, но не слишком дорого
- N=16+: Бессмысленно тратишь compute. Убывающая отдача
DeepSeekMath использует N=4 для большинства экспериментов. Этого достаточно, чтобы получить надёжный сигнал, но не сжечь бюджет на генерацию.
GRPO в конвейере: от сырой модели до reasoning-монстра
Вот как выглядит полный пайплайн обучения с GRPO:
# Упрощенный псевдокод полного конвейера
def train_grpo_pipeline(model, dataset, num_epochs=3):
optimizer = torch.optim.AdamW(model.parameters(), lr=5e-6)
for epoch in range(num_epochs):
for batch in dataset:
# 1. Генерация нескольких ответов на каждый промпт
all_responses = []
all_log_probs = []
for _ in range(4): # N=4
responses, log_probs = model.generate(batch["prompt"])
all_responses.append(responses)
all_log_probs.append(log_probs)
# 2. Оценка ревардов (например, другой моделью)
rewards = evaluate_responses(all_responses, batch["reference"])
# 3. Вычисление GRPO loss
loss = grpo_loss(torch.stack(all_log_probs, dim=1), rewards)
# 4. Обратное распространение
optimizer.zero_grad()
loss.backward()
torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
optimizer.step()
return model
Заметь: в этом пайплайне нет шага "обновить критика". Нет шага "синхронизировать политику и критика". Нет градиентов для value-функции. Всё упрощается до неприличия.
Когда GRPO не работает (и нужно возвращаться к PPO)
GRPO — не серебряная пуля. Есть сценарии, где PPO всё ещё лучше:
- Субъективные задачи: Поэзия, креативное письмо, юмор. Здесь нет чётких "правильных" ответов, только предпочтения. Критик, обученный на человеческих оценках, может улавливать тонкие нюансы.
- Очень маленькие группы: Если можешь генерировать только 1-2 ответа на промпт (ограничения по времени/ресурсам), относительное сравнение теряет смысл.
- Задачи с долгосрочными последствиями: В диалоговых системах, где нужно учитывать контекст нескольких ходов назад, value-функция критика помогает планировать.
Но для reasoning, математики, кодинга — GRPO бьёт PPO по всем фронтам. Особенно если посмотреть на статью про KEF vs OpenAI o3, где как раз сравнивают подходы к прокачке reasoning.
GRPO vs DPO: родственники или конкуренты?
На первый взгляд, GRPO похож на DPO (Direct Preference Optimization). Оба избавляются от отдельного критика. Но есть ключевые различия:
| Аспект | DPO | GRPO |
|---|---|---|
| Данные | Парные предпочтения (A лучше B) | Множественные ответы с ревардами |
| Цель | Выравнивание с человеческими ценностями | Максимизация объективных метрик |
| Сложность | Нужны размеченные данные предпочтений | Нужна функция оценки (ревард) |
По сути, DPO — для alignment ("будь полезным, честным, безвредным"), а GRPO — для capability ("решай математику лучше"). Они решают разные задачи, но оба показывают: можно обойтись без громоздкого RLHF с отдельным критиком.
Практический совет: с чего начать внедрение GRPO
Хочешь попробовать GRPO на своей модели? Вот минимальный рабочий план:
1 Подготовка данных
Собери датасет с чёткими правильными ответами. Для математики — GSM8K, MATH. Для кодинга — HumanEval, MBPP. Важно: у тебя должна быть функция, которая для любого ответа выдаёт числовой ревард.
Если не знаешь, где брать данные — посмотри статью про источники данных для fine-tuning.
2. Выбери функцию реварда
Варианты:
- Exact match: 1 если ответ совпадает с эталоном, 0 если нет
- Вероятность от другой модели: GPT-4 оценивает, насколько ответ правильный
- Собственный классификатор: Небольшая модель, обученная отличать правильные решения от неправильных
3. Начни с маленькой модели
Не лезь сразу на Llama 70B. Возьми Qwen 1.5B или Llama 3.2 3B. Обучи на 1000 примеров. Посмотри, сходится ли loss. Если статья про "несущие" нейроны в Llama 3.2 3B показала что-то — примени эти знания.
4. Мониторь разнообразие ответов
Самая опасная вещь в GRPO — коллапс разнообразия. Если модель начнёт генерировать почти одинаковые ответы, сравнение теряет смысл. Добавь энтропийную регуляризацию:
def grpo_loss_with_entropy(log_probs, rewards, beta=0.01):
normalized_rewards = (rewards - rewards.mean(dim=-1, keepdim=True)) \
/ (rewards.std(dim=-1, keepdim=True) + 1e-8)
weights = F.softmax(normalized_rewards, dim=-1)
policy_loss = -(weights * log_probs).sum(dim=-1).mean()
# Энтропийная регуляризация
entropy = -(F.softmax(log_probs, dim=-1) * F.log_softmax(log_probs, dim=-1)).sum(dim=-1).mean()
total_loss = policy_loss - beta * entropy
return total_loss
Что дальше? Эволюция GRPO и будущее RL для LLM
GRPO — не конечная точка. Уже видны направления развития:
- GRPO + RAG: Использовать RAG-системы для получения ревардов. Модель генерирует ответ, RAG ищет похожие правильные решения, оценивает схожесть.
- Мультимодальный GRPO: Не только текст, но и код, диаграммы, математические обозначения. Особенно актуально с ростом мультимодальных моделей.
- GRPO для небольших моделей: Если Grokkit прав и маленькие модели могут заменить большие — GRPO станет стандартом для их обучения.
Самый интересный вопрос: а что, если применить GRPO не только к финальному ответу, но и к промежуточным шагам reasoning? За каждый логический шаг — отдельный ревард. Получится что-то вроде hierarchical GRPO. В DeepSeekMath-R1 уже есть намёки на это.
Прогноз: Через год 80% reasoning-моделей будут использовать GRPO или его производные. PPO останется только для alignment-задач, где нужны тонкие человеческие предпочтения. А для всего, что можно измерить объективно — будет царствовать групповое относительное сравнение.
Попробуй. Начни с маленького эксперимента. Удивишься, насколько проще стало обучать модели решать задачи. Без критика, без синхронизации, без половины головной боли. Просто генерируй, сравнивай, усиливаем лучшее. Вот и весь секрет.