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

verl, часть 3: GRPO на четырёх RTX 4080 — план памяти, команды, настройка

Как запустить GRPO из verl на машине курса (4× RTX 4080 по 32 ГБ, PCIe без NVLink): расчёт памяти для Qwen3-0.6B/1.7B/4B, адаптация официального скрипта, перевод ключевых советов из Performance Tuning Guide и FAQ verl, что смотреть в логах и как чинить типичные ошибки.

Машина курса: 4× RTX 4080 по 32 ГБ, связь между картами только через PCIe 4.0 (без NVLink), 128 ГБ RAM.

Три следствия для verl:

  1. Память. В verl на одних и тех же картах живут актор (обучаемая модель с градиентами и состоянием оптимизатора), референсная модель и движок генерации vLLM с KV-кэшем. Всё это должно уместиться в 4 × 32 ГБ.
  2. Связь. Обучение на FSDP постоянно гоняет веса и градиенты между картами (all-gather, reduce-scatter — урок 5.4.3), а перед каждой генерацией веса перекладываются в vLLM. По PCIe это медленнее, чем на серверах, — значит, выгоднее меньше дробить модель.
  3. Модели. Реалистичный выбор для полного GRPO — Qwen3-0.6B, 1.7B, 4B. 8B и больше — только с LoRA и выгрузкой в RAM (см. в конце).

Грубая оценка для полного дообучения в смешанной точности с Adam — около 16 байт на параметр (веса, градиенты, мастер-копия в fp32 и два момента Adam; урок 5.4.1). FSDP делит это между четырьмя картами. Плюс vLLM забирает долю памяти под свои веса и KV-кэш, плюс активации.

МодельАктор (16 Б × N)На одну карту после FSDP / 4vLLM при gpu_memory_utilization=0.4Вердикт
Qwen3-0.6B≈10 ГБ≈2,5 ГБ12,8 ГБсвободно — для отладки
Qwen3-1.7B≈27 ГБ≈7 ГБ12,8 ГБкомфортно
Qwen3-4B≈64 ГБ≈16 ГБ12,8 ГБвпритык: нужен чекпойнтинг активаций, при нехватке — выгрузка оптимизатора

Референсная модель в официальном скрипте выгружена в RAM (ref.fsdp_config.param_offload=True), поэтому в таблице её нет.

  1. Данные (один раз, из корня репозитория verl):

    Окно терминала
    UV_RUN="uv run --frozen --all-packages --extra vllm --extra fsdp"
    $UV_RUN python3 examples/data_preprocess/gsm8k.py --local_save_dir ~/data/gsm8k
  2. Дымовой тест на Qwen3-0.6B — проверить, что всё запускается, за несколько минут. Все переменные — из «шапки» официального скрипта run_qwen3_4b_fsdp.sh (разбор в части 2); хвостовые аргументы скрипт передаёт в verl как есть:

    Окно терминала
    MODEL_PATH=Qwen/Qwen3-0.6B NGPUS_PER_NODE=4 \
    TRAIN_BATCH_SIZE=32 PPO_MINI_BATCH_SIZE=16 ROLLOUT_N=4 \
    MAX_RESPONSE_LENGTH=512 ROLLOUT_TP=1 ROLLOUT_GPU_MEM_UTIL=0.4 \
    PROJECT_NAME=course_grpo EXPERIMENT_NAME=smoke_qwen3_0_6b \
    bash examples/grpo_trainer/run_qwen3_4b_fsdp.sh \
    trainer.logger=console trainer.total_training_steps=3 trainer.val_before_train=False

    Ожидание: три строки step:... в логе без ошибок. trainer.total_training_steps ограничивает обучение заданным числом шагов (по умолчанию null — считать по эпохам).

  3. Настоящий запуск на Qwen3-1.7B:

    Окно терминала
    MODEL_PATH=Qwen/Qwen3-1.7B NGPUS_PER_NODE=4 \
    TRAIN_BATCH_SIZE=128 PPO_MINI_BATCH_SIZE=64 ROLLOUT_N=5 \
    MAX_PROMPT_LENGTH=512 MAX_RESPONSE_LENGTH=1024 \
    ROLLOUT_TP=1 ROLLOUT_GPU_MEM_UTIL=0.4 \
    TOTAL_EPOCHS=2 TEST_FREQ=10 SAVE_FREQ=50 \
    PROJECT_NAME=course_grpo EXPERIMENT_NAME=qwen3_1_7b_gsm8k \
    bash examples/grpo_trainer/run_qwen3_4b_fsdp.sh trainer.logger=console 2>&1 | tee grpo_1_7b.log

    Делимость: 128 промптов × 5 = 640 траекторий за шаг; мини-батч 64 × 5 = 320 траекторий → 2 обновления актора; на каждой из 4 карт — по 80 траекторий мини-батча. Эпоха GSM8K — 7473 / 128 ≈ 58 шагов.

  4. Qwen3-4B — те же команды с MODEL_PATH=Qwen/Qwen3-4B. Если на фазе обучения не хватает памяти, добавьте в конец actor_rollout_ref.actor.fsdp_config.optimizer_offload=True (состояние Adam уедет в RAM: медленнее, но влезет).

В официальном скрипте ROLLOUT_TP=2: vLLM режет модель на две карты. На 4B-модели и наших картах в этом нет нужды — модель целиком помещается в одну. Перевод из Performance Tuning Guide:

Используйте меньший tensor_parallel_size. Когда ресурсы GPU позволяют, меньший размер тензорного параллелизма порождает больше реплик vLLM. Параллелизм данных (DP) может дать большую пропускную способность, чем тензорный (TP), но и больше расходует KV-кэш. Тщательно взвешивайте компромисс между числом реплик и расходом памяти. Наш эксперимент в разделе 8.4 статьи HybridFlow оценивает этот компромисс.

Для PCIe-машины довод ещё сильнее: при TP каждый токен генерации требует обмена между картами, а при ROLLOUT_TP=1 четыре реплики vLLM генерируют независимо. Это тот же выбор «TP или DP», что в уроке 7.1.2.

Настройка производительности (перевод фрагментов Performance Tuning Guide)

Заголовок раздела «Настройка производительности (перевод фрагментов Performance Tuning Guide)»

Главный принцип выбора размеров батчей в verl:

  • Алгоритмические параметры (размер батча обучения, размер мини-батча PPO) — глобальные (с точки зрения единого контроллера) и нормируются в каждом воркере.
  • Параметры производительности (размер микробатча, максимальная длина в токенах для динамического батча) — локальные: они задают распределение данных на одну GPU.

В скриптах используйте *micro_batch_size_per_gpu, а не *micro_batch_size.

Советы:

  1. Включите чекпойнтинг градиентов (enable_gradient_checkpointing=True у модели актора и критика) — часто позволяет брать бо́льшие микробатчи.
  2. Увеличивайте *micro_batch_size_per_gpu, пока он не сравняется с нормированным mini_batch_size.
  3. Параметры только прямого прохода (например, actor_rollout_ref.ref.log_prob_micro_batch_size_per_gpu, actor_rollout_ref.rollout.log_prob_micro_batch_size_per_gpu) могут быть больше (например, вдвое), чем параметры обучения.
  4. Микробатчи критика и модели награды могут быть больше, чем у актора: у актора гораздо больше словарь в последнем слое.
  5. Включите выгрузку активаций (enable_activation_offload=True) — вместе с чекпойнтингом градиентов позволяет брать бо́льшие микробатчи; сейчас работает только в бэкенде FSDP.

Динамический размер батча позволяет обрабатывать за один прямой проход примерно одинаковое число токенов (при разном числе примеров). Это заметно повышает эффективность обучения и снижает расход памяти. Включается use_dynamic_bsz=True; тогда вместо *micro_batch_size_per_gpu настраиваются:

  • actor_rollout_ref.actor.ppo_max_token_len_per_gpu — максимум токенов в прямом и обратном проходе при обновлении политики;
  • actor_rollout_ref.ref.log_prob_max_token_len_per_gpu и actor_rollout_ref.rollout.log_prob_max_token_len_per_gpu — максимум токенов при вычислении логарифмов вероятностей.

Советы:

  1. Увеличьте ppo_max_token_len_per_gpu — минимум до 2 × (max_prompt_length + max_response_length) — и пробуйте больше для пропускной способности.
  2. Ограничения для параметров только прямого прохода могут быть больше, чем для обучения.
  3. Для критика и модели награды берите ограничения хотя бы вдвое больше, чем у актора.

(От переводчика: в скрипте run_qwen3_4b_fsdp.sh стоит ppo_max_token_len_per_gpu=3000, а 2 × (1024 + 1024) = 4096 — то есть ниже совета документации. На 32 ГБ это разумная осторожность; если память позволяет, поднимите до 4096 хвостовым аргументом actor_rollout_ref.actor.ppo_max_token_len_per_gpu=4096.)

Для обучения на FSDP задайте actor_rollout_ref.model.use_remove_padding=True, чтобы убрать токены-заполнители перед прямым проходом (если есть критик — и critic.model.use_remove_padding=True). Выигрыш зависит от того, сколько паддинга в батчах. (В официальном скрипте уже включено.)

Вычисление энтропии по логитам даёт пик памяти (словарь Qwen — ~152 тыс. токенов). Чтобы его снизить, включите поблочное вычисление: actor_rollout_ref.ref.entropy_from_logits_with_chunking=True. Обычный чекпойнтинг градиентов на вычисление энтропии при обучении не распространяется; для него есть actor_rollout_ref.actor.entropy_checkpointing=True.

МетрикаЧто значитХорошо, если
critic/score/meanсредняя награда по траекториям шагарастёт от шага к шагу (с шумом)
метрики с префиксом val-core/ (в старых версиях — val/test_score/...)качество на тестовой выборке; mean@N — среднее по N ответамрастёт вместе с наградой
response_length/mean, response_length/clip_ratioдлина ответов и доля обрезанныхобрезанных мало
actor/entropyэнтропия политикине падает к нулю слишком быстро (коллапс энтропииколлапс энтропииEN entropy collapseЭнтропия политики быстро падает при RL: ответы в группе становятся почти одинаковыми, исследование прекращается, рост награды упирается в потолок., урок 6.4.3)
actor/pg_clipfrac, actor/ppo_klдоля клиппинга и сдвиг политики за шагмалы (единицы процентов)
timing_s/gen, timing_s/update_actorвремя фаз в секундах (в старых логах, как в быстром старте, — timing/...)генерация обычно дольше обучения — это нормально

Во втором терминале держите nvtop или watch -n 1 nvidia-smi: видно, как память перераспределяется между фазами генерации и обучения.

СимптомЧто делать
CUDA out of memory на фазе обученияуменьшить ppo_max_token_len_per_gpu / микробатчи; ROLLOUT_GPU_MEM_UTIL ниже; optimizer_offload=True
OOM на фазе генерацииуменьшить ROLLOUT_N, MAX_RESPONSE_LENGTH или ROLLOUT_GPU_MEM_UTIL
«Unable to register worker with raylet»Перевод из FAQ: причина — системные ограничения (например, SLURM ограничивает, как делятся процессоры узла): ray.init() пытается запустить столько процессов, сколько ядер, и ограничения мешают. Задайте ray_kwargs.ray_init.num_cpus — число, допустимое в вашей системе
«CUDA error: an illegal memory access» при генерацииИз FAQ: смотрите документацию vLLM по устранению неполадок для вашей версии
Зависание на инициализации NCCLна картах GeForce без P2P попробуйте NCCL_P2P_DISABLE=1 (совет из урока 7.1.2)
Ошибка компиляции Triton (compile_module_from_src)Из FAQ: отключите JIT-компиляцию слитых ядер параметром use_torch_compile (см. страницу конфигурации)
  • Qwen3-8B с LoRA. Варианты с LoRA лежат в examples/tuning/lora/ (см. README из части 2): полное GRPO на 8B в 4 × 32 ГБ не поместится, а LoRA снимает основную нагрузку — состояние оптимизатора.
  • Свой датасет и своя награда. Шаблон — examples/data_preprocess/gsm8k.py и verl/utils/reward_score/gsm8k.py; практикум — урок 6.4.5.
  • Варианты GRPO (DAPO, Dr. GRPO, GSPO) включаются параметрами — см. часть 2 и урок 6.4.3.
EnglishРусский
rolloutгенерация траекторий
actor / reference / criticактор / референс / критик
offloadвыгрузка (в RAM)
dynamic batch sizeдинамический размер батча
remove paddingудаление паддинга
reshardingперекладка (перешардирование) весов