Перейти к содержимому
ПереводыQwen, InternLM и другие

verl, часть 2: GRPO — алгоритм, параметры и готовые скрипты

Полный перевод страницы GRPO из документации verl и README папки examples/grpo_trainer: идея группового сэмплирования, все ключевые параметры (rollout.n, мини-батчи, агрегация потерь, KL-потери k1–k3), Dr. GRPO, соглашения о скриптах — и построчный разбор скрипта для Qwen3-4B.

Обновлено: 31.05.2025.

В обучении с подкреплением классические алгоритмы вроде PPO опираются на модель-«критика», которая оценивает ценность действий и направляет обучение. Но обучение критика может быть ресурсоёмким.

GRPOGRPO (групповая относительная оптимизация политики)EN Group Relative Policy Optimization (GRPO)PPO без критика: на промпт генерируется группа из G ответов, преимущество — (r − среднее) / std по группе. DeepSeekMath, 2024. упрощает процесс, убирая отдельную модель критика. Вместо этого он работает так:

  • Групповое сэмплирование. Для данной задачи модель генерирует несколько возможных решений — «группу» выходов.
  • Назначение наград. Каждое решение оценивается и получает награду в зависимости от правильности или качества.
  • Вычисление базовой линии. Средняя награда группы служит базовой линией.
  • Обновление политики. Модель обновляет параметры, сравнивая награду каждого решения с базовой линией группы: решения лучше среднего поощряются, хуже среднего — подавляются.

Так снижаются вычислительные затраты — не нужно обучать отдельную модель оценки ценности, и обучение становится эффективнее. Подробнее — в исходной статье DeepSeekMath: Pushing the Limits of Mathematical Reasoning in Open Language Models.

  • Нет функции ценности (без критика): в отличие от PPO, GRPO не обучает отдельную сеть ценности (критика).
  • Групповое сэмплирование (групповые генерации): вместо одной генерации на вход GRPO генерирует текущей политикой несколько продолжений (ответов) на каждый промпт. Этот набор продолжений называется группой.
  • Относительные награды: внутри группы продолжения оцениваются (например, по правильности), и награды нормируются относительно группы.

Обратите внимание: все параметры со словами micro_batch_size задают максимальное число примеров или токенов на один прямой или обратный проход, чтобы не кончилась память GPU, и их значения не должны менять поведение алгоритма или сходимость.

Хотя многие параметры начинаются с префикса ppo_, они работают для разных RL-алгоритмов verl, поскольку цикл обучения GRPO похож на цикл PPO (без критика).

flowchart TD
P["data.train_batch_size промптов"] --> R["rollout.n ответов на каждый промпт<br/>всего train_batch_size × n траекторий"]
R --> M["делим на мини-батчи:<br/>ppo_mini_batch_size промптов × n ответов"]
M --> U["каждый мини-батч — одно обновление актора<br/>(ppo_epochs проходов по всем мини-батчам)"]
U --> MB["внутри мини-батча — микробатчи<br/>ppo_micro_batch_size_per_gpu (только ради памяти)"]
Как батчи вкладываются друг в друга (схема переводчика вместо картинки оригинала)
  • actor_rollout_ref.rollout.n: сколько раз сэмплировать ответ на каждый промпт. По умолчанию 1. Для GRPO поставьте значение больше 1 — для группового сэмплирования. (В оригинале здесь опечатка actor_rollout.ref.rollout.n; правильное имя — как в README ниже.)
  • data.train_batch_size: глобальный размер батча промптов, по которым генерируется набор траекторий. Число ответов (траекторий) — data.train_batch_size * actor_rollout_ref.rollout.n.
  • actor_rollout_ref.actor.ppo_mini_batch_size: набор траекторий делится на несколько мини-батчей размера ppo_mini_batch_size для обновлений актора. ppo_mini_batch_size — глобальный размер по всем воркерам.
  • actor_rollout_ref.actor.ppo_epochs: сколько эпох обновлений GRPO делать по одному набору траекторий актора.
  • actor_rollout_ref.actor.clip_ratio: диапазон клиппинга GRPO. По умолчанию 0,2.
  • algorithm.adv_estimator: по умолчанию gae. Поставьте grpo.
  • actor_rollout_ref.actor.loss_agg_mode: по умолчанию "token-mean". Варианты: "token-mean", "seq-mean-token-sum", "seq-mean-token-mean". В исходной статье GRPO потеря берётся на уровне примера (seq-mean-token-mean), что может быть нестабильно в сценариях с длинными цепочками рассуждений. Все примеры GRPO в verl используют для агрегации потерь значение по умолчанию "token-mean".

Вместо штрафа KL в награде GRPO регуляризует, добавляя KL-расхождение между обучаемой и референсной политикой прямо в функцию потерь:

  • actor_rollout_ref.actor.use_kl_loss: использовать KL-потерю в акторе. Если включено, KL в функции награды не применяется. По умолчанию False. Для GRPO поставьте True.
  • actor_rollout_ref.actor.kl_loss_coef: коэффициент KL-потери. По умолчанию 0,001.
  • actor_rollout_ref.actor.kl_loss_type: поддерживаются kl (k1), abs, mse (k2), low_var_kl (k3) и full. Знак «+» в конце (например, 'k1+' и 'k3+') включает straight-through — для несмещённой оценки градиента используется k2 независимо от того, как оценивается само значение KL (подробнее — в обсуждении PR #2953 в репозитории verl). Как считать KL-расхождение между актором и референсной политикой — см. подробный разбор в блоге Джона Шульмана.

Статья Understanding R1-Zero-Like Training: A Critical Perspective утверждает, что в GRPO есть смещение оптимизации, из-за которого ответы искусственно удлиняются — особенно неправильные. Эта неэффективность возникает из-за того, как GRPO вычисляет преимущества, нормируя награды по группе. Dr. GRPO вместо этого агрегирует токенные потери, нормируя на глобальную константу, чтобы убрать смещение по длине.

Чтобы включить Dr. GRPO, задайте (остальные параметры — как у GRPO):

  • actor_rollout_ref.actor.loss_agg_mode: "seq-mean-token-sum-norm" — отключает усреднение по измерению последовательностей;
  • actor_rollout_ref.actor.loss_scale_factor: (по желанию) постоянное целое число (например, максимальная длина ответа), чтобы нормировка была одинаковой на всём обучении. Если не задано, берётся длина ответа текущего батча;
  • actor_rollout_ref.actor.use_kl_loss: для Dr. GRPO поставьте False;
  • algorithm.norm_adv_by_std_in_grpo: False — отключает нормировку на стандартное отклонение.

Лог обучения и команды GRPO для Qwen2.5: ссылка.

Окно терминала
bash examples/grpo_trainer/run_qwen3_8b_fsdp.sh

Другие эталонные результаты — на странице baseline документации.

(Первые разделы README — «что такое GRPO» и «ключевые компоненты» — дословно повторяют начало страницы документации выше; не дублируем.)

  • actor_rollout_ref.rollout.n: число сэмплов на промпт (для GRPO обязательно ≥ 2).
  • data.train_batch_size: промптов на один глобальный шаг. Всего траекторий = train_batch_size * rollout.n.
  • actor_rollout_ref.actor.ppo_mini_batch_size: глобальный мини-батч для обновлений актора (должен делить train_batch_size * n).
  • actor_rollout_ref.actor.ppo_epochs: эпохи внутреннего цикла по собранным траекториям.
  • actor_rollout_ref.actor.clip_ratio: диапазон клиппинга PPO, по умолчанию 0.2.
  • actor_rollout_ref.actor.loss_agg_mode: token-mean (по умолчанию), seq-mean-token-sum или seq-mean-token-mean.
  • actor_rollout_ref.actor.use_kl_loss=True + actor_rollout_ref.actor.kl_loss_coef / kl_loss_type: регуляризация к референсной политике через KL-потерю в акторе.
  • algorithm.adv_estimator=grpo.

Чтобы включить Dr. GRPO (см. Understanding R1-Zero-Like Training), задайте поверх стандартных настроек GRPO:

actor_rollout_ref.actor.loss_agg_mode=seq-mean-token-sum-norm
actor_rollout_ref.actor.use_kl_loss=False
algorithm.norm_adv_by_std_in_grpo=False

Все скрипты в этой папке названы по соглашению:

run_<model>_<train-backend>[_<platform-or-variant>].sh

Где:

  • <model> — канонический размер семейства моделей (qwen3_8b для плотных текстовых моделей, qwen3_30b_a3b для MoE, qwen2_5_vl_7b / qwen3_vl_8b для моделей со зрением, qwen3_235b_a22b / deepseek_v3_671b / deepseek_v4_flash — демонстрации масштаба);
  • <train-backend> ∈ {fsdp, megatron, mindspeed};
  • <platform-or-variant> — только для вариантов под конкретное железо, например gb200, fp8, veomni, или для скриптов MindSpeed на NPU;
  • INFER_BACKEND выбирает бэкенд генерации внутри скриптов, которые поддерживают несколько вариантов (vllm, sglang или trtllm);
  • DEVICE выбирает путь GPU или NPU в скриптах, поддерживающих обе платформы.

Каждый скрипт выносит часто настраиваемые параметры в переменные окружения в начале файла, так что можно запускать так:

Окно терминала
MODEL_PATH=Qwen/Qwen3-14B \
NNODES=2 NGPUS_PER_NODE=8 \
INFER_BACKEND=sglang ROLLOUT_N=8 TRAIN_BATCH_SIZE=2048 \
bash examples/grpo_trainer/run_qwen3_8b_fsdp.sh
  • Во всех скриптах по умолчанию включены dynamic batch size (динамический размер батча) и sequence balancing (балансировка последовательностей).
  • Скрипты для текстовых LLM по умолчанию обучают на gsm8k + math; скрипты для моделей со зрением — на geo3k.
  • Скрипты-демонстрации масштаба (235B, 671B) обучают на dapo-math-17k / aime-2024.
Семейство моделейvllmsglangtrtllmБэкенд обученияПлатформы
Qwen3-8B (плотная)✓✓✓FSDP, Megatronnvidia, npu (FSDP + MindSpeed), вариант _gb200
Qwen2.5-VL-7B✓✓✓FSDP, Megatronnvidia
Qwen3-VL-8B✓FSDP, Megatronnvidia, npu (FSDP)
Qwen3-VL-30B-A3B✓FSDP, Megatronnvidia, npu (FSDP, VeOmni)
Qwen3-VL-235B-A22B✓Megatronnvidia
Qwen3-30B-A3B (MoE)✓✓✓FSDP, Megatronnvidia, npu (MindSpeed, VeOmni)
Qwen3-235B-A22B✓✓Megatronnvidia, npu
Qwen3-Next-80B-A3B✓FSDPnpu
Qwen3.5-27B (плотная)✓FSDP2nvidia, npu
Qwen3.5-35B (плотная)✓FSDP2, Megatronnvidia, npu
Qwen3.5-35B-A3B (MoE)✓VeOmninvidia
Qwen3.5-122B-A10B✓Megatronnvidia
DeepSeek-V3 671B✓Megatronnvidia
DeepSeek-V4-Flash✓Megatronnvidia, amd
GLM-4.1V-9B✓FSDPnvidia
MiniCPM-o-2.6✓FSDPnvidia
Moonlight-16B-A3B✓Megatronnvidia
Nemotron-Nano-v3-30B-A3B✓Megatronnvidia
Seed-OSS-36B✓FSDP2nvidia
GPT-OSS-20B✓✓FSDP, VeOmninvidia
Mistral-Nemo-12B (демо с моделью награды)✓FSDPnvidia

Варианты с LoRA лежат в examples/tuning/lora/, варианты для профилирования — в examples/profile/. Демонстрации масштаба и скрипты под конкретное железо (например, run_qwen3_8b_fsdp_gb200.sh, варианты FP8, VeOmni) сохраняют суффикс в конце имени, чтобы их было легко найти.

Разбор скрипта run_qwen3_4b_fsdp.sh (от переводчика)

Заголовок раздела «Разбор скрипта run_qwen3_4b_fsdp.sh (от переводчика)»

Скрипт для Qwen3-4B — самый близкий к вашей машине. Он состоит из четырёх частей.

1. Настраиваемые переменные (значения по умолчанию из скрипта):

ПеременнаяПо умолчаниюЧто это
MODEL_PATHQwen/Qwen3-4Bмодель
TRAIN_FILE, TEST_FILE~/data/gsm8k/{train,test}.parquetданные из шага 1 быстрого старта
NNODES, NGPUS_PER_NODE1, 8машин и GPU на машине — для вас 4
TRAIN_BATCH_SIZE512промптов за шаг
PPO_MINI_BATCH_SIZE256мини-батч → 2 обновления актора за шаг
MAX_PROMPT_LENGTH, MAX_RESPONSE_LENGTH1024, 1024длины в токенах
ACTOR_LR1e-6скорость обучения
KL_LOSS_COEF, ENTROPY_COEFF0,001; 0вес KL-потери и бонуса за энтропию
ROLLOUT_TP2тензорный параллелизм движка генерации: модель режется на 2 GPU
ROLLOUT_GPU_MEM_UTIL0,6доля памяти под генерацию
ROLLOUT_N5размер группы GRPO
SAVE_FREQ, TEST_FREQ, TOTAL_EPOCHS20, 5, 15как часто сохранять и проверять, сколько эпох

2. Выбор устройства. Если установлен torch_npu, скрипт считает, что это Ascend NPU, и меняет настройки (16 устройств, другие переменные окружения). На NVIDIA ничего не меняется.

3. Массивы параметров. Параметры сгруппированы в массивы Bash DATA, MODEL, ACTOR, ROLLOUT, REF, TRAINER. Важное в них:

  • algorithm.adv_estimator=grpo и algorithm.use_kl_in_reward=False — GRPO с KL в потере, а не в награде;
  • data.filter_overlong_prompts=True и data.truncation='error' — слишком длинные промпты выбрасываются заранее, а если что-то всё же не влезет — ошибка, а не тихая обрезка;
  • actor_rollout_ref.model.use_remove_padding=True и enable_gradient_checkpointing=True — экономия вычислений и памяти;
  • actor_rollout_ref.actor.use_kl_loss=True, kl_loss_type=low_var_kl — оценка k3;
  • actor_rollout_ref.actor.use_dynamic_bsz=True с ppo_max_token_len_per_gpu=3000 — микробатчи собираются не по числу примеров, а по числу токенов (до 3000 на GPU), поэтому ppo_micro_batch_size_per_gpu фактически не используется;
  • actor_rollout_ref.ref.fsdp_config.param_offload=True — референсная модель хранится в памяти процессора и подгружается только для подсчёта вероятностей;
  • actor_rollout_ref.rollout.free_cache_engine=True — после генерации vLLM освобождает KV-кэш, чтобы обучению хватило памяти;
  • trainer.logger='["console","wandb"]' — если у вас нет wandb, замените на console.

4. Запуск. Если не задано VERL_USE_UV=0, команда запускается через uv run ... --extra vllm --extra fsdp, и Ray получает тот же интерпретатор через py_executable. Всё, что вы допишете в командной строке после имени скрипта ("$@"), передаётся в конец — так можно переопределить любой параметр.