veRL разбор: внутренности, инструменты, баг NCCL для RL post-training | AiManual
AiManual Logo Ai / Manual.
02 Июн 2026 Гайд

Глубокое погружение в veRL: что скрывается под капотом RL post-training, инструменты и фатальный баг NCCL

Детальный разбор фреймворка veRL: архитектура, гибридный parallelism, инструменты. Как обойти критический баг NCCL при пост-тренинге с RL. Опыт форка.

Когда вы в последний раз пытались обучить LLM с подкреплением на сотне GPU? Процесс, который должен быть рутинным, превращается в череду падений, зависаний и тихих NaN в логах. Я перепробовал почти всё: от асинхронных подходов до монолитных решений. Но один инструмент выделяется — veRL. Не из-за маркетинга, а из-за архитектуры. Но и он не идеален. Давайте залезем внутрь, посмотрим на его кишки, разберём инструментарий и наступим на грабли — критический баг NCCL, который сломал мне неделю.

Почему veRL вообще заслуживает внимания?

В экосистеме RL post-training — адская мешанина. Есть TRL от Hugging Face, OpenRLHF, DeepSpeed, но каждый раз ты упираешься в проблему: или инференс модели во время получения rollouts тормозит генерацию, или градиенты не сходятся из-за плохой синхронизации. veRL (volatile Efficient Reinforcement Learning) родился в недрах UCSD и сразу предложил другую парадигму: разделить actor, critic, reference и reward модели на отдельные группы процессов, а для генерации ответов использовать vLLM. Это не просто бенчмарк-хантер, а инженерно продуманная система.

В последнем релизе на начало 2026 года — veRL v0.5.3 — уже есть поддержка GRPO, PPO, RLOO, а также гибридный parallelism (TP, PP, DP, FSDP). Но самое важное — он использует Ray для оркестровки и vLLM для инференса. Звучит красиво. Но давайте откроем капот.

veRL не использует стандартный Hugging Face Trainer для генерации — он запускает отдельный vLLM-инстанс (или несколько) внутри того же Ray-кластера. Это решение ускоряет сбор траекторий в 2-5 раз по сравнению с model.generate().

Архитектура: три слоя, которые работают как часы (пока не ломаются)

Посмотрим на диаграмму потоков в veRL. Упрощённо:

  • Rollout-воркеры — получают текущую политику (актор), запускают vLLM с несколькими промптами, генерируют ответы. Каждый воркер держит локальную копию модели (через Ray object store или shared memory).
  • Training-воркеры — на них живут actor, critic, reference, reward модели. Они обновляются через FSDP или DeepSpeed ZeRO-3, а rollout-воркеры просто забирают свежий чекпоинт через Ray.
  • Reward-воркеры — отдельный пул для RM (reward model), чтобы не блокировать train.

Такая декомпозиция позволяет масштабироваться: можно добавить 64 GPU только для rollouts и не трогать training-кластер. Но есть тонкость — синхронизация градиентов между репликами актора. И тут вылезает NCCL-баг.

Критический баг NCCL: как я потерял неделю

После первого успешного запуска на 32 GPU A100 я решил масштабироваться до 128. И всё сломалось. Обучение зависало случайным образом через 10-30 шагов. Логи молчали, никаких OOM, никаких ошибок — просто стоп. Я начал подозревать всё: от сети до драйверов. Но проблема оказалась глубже.

Баг воспроизводился в all_reduce при размере тензора меньше 1 MB. При гибридном parallelism малые градиенты (например, от bias-слоёв или layer norms) вызывали deadlock в NCCL 2.21.5. На версии 2.22.3 проблема частично исправлена, но не для всех конфигураций.

Как я это выяснил? Собрал трассировку с NCCL_DEBUG=INFO и увидел:

NCCL WARN: Net : Call to recv from GPU 7 failed: remote GPU 3, socket 6144 error 2

Казалось бы — сетевая проблема. Но нет — причина была в том, что при малом размере сообщения NCCL использовал внутренний буфер, который мог переполняться при большом количестве одновременных all-reduce. И veRL запускает одновременно десятки таких операций из-за pipeline parallelism.

Решение, которое сработало у меня — форкнуть veRL и заменить в коммуникации all_reduce на reduce_scatter + all_gather для маленьких тензоров, а также принудительно задать NCCL_MAX_P2P_NCHANNELS=2. Но это временный костыль. Разработчики veRL уже знают о баге: в issue #345 обсуждают патч с опцией --ncccl_min_tensor_size_mb 1. Но стабильного фикса пока нет.

1 Диагностика бага

Достаточно запустить тест с torch.distributed.all_reduce на маленьком тензоре на вашем кластере. Если зависает — вы в зоне риска.

2 Временное решение

Добавьте в train.py перед запуском:

import os
os.environ['NCCL_MAX_P2P_NCHANNELS'] = '2'
os.environ['NCCL_MIN_TENSOR_SIZE'] = '1048576'  # 1 MB

Но это не панацея — при большом количестве GPU (256+) баг может вернуться.

Инструментарий veRL: что прячется в коробке

Кроме самого обучения, veRL поставляет несколько утилит, которые редко описывают в документации.

Утилита Назначение
verl.bench Бенчмарк производительности rollouts vs training
verl.profile Профилирование памяти и времени по слоям
verl.replay Загрузка ранее собранных траекторий для повторного обучения (offline)
verl.rollout Отдельный запуск генерации с текущей политикой (удобно для дата-аугментации)

Особого внимания заслуживает verl.profile — он показывает, сколько времени тратится на коммуникацию. На моём эксперименте с багом NCCL именно там я увидел аномалии: время all_reduce для bias-слоёв превышало 2 секунды при норме 20 мс.

Пошаговый план: запуск veRL на кластере с обходом бага

1 Подготовка окружения

Установите Ray 2.35.0 (или новее), vLLM 0.8.2, PyTorch 2.7.0 с CUDA 12.8. veRL ставится через pip или из сорцов:

git clone https://github.com/volcengine/verl.git
cd verl
pip install -e .

2 Конфигурация датасета и ревард-модели

Используйте JSON-файл с промптами и синтетическими ответами. Для простоты возьмите datasets/gsm8k. Ревард-модель — например, OpenAssistant/reward-model-deberta-v3-large.

3 Запуск с отключением опасных оптимизаций

python -m verl.trainer.main_ppo \
    --config_path config/ppo_llama3_8b.yaml \
    --n_gpus_per_node 8 \
    --num_nodes 4 \
    --rollout_batch_size 512 \
    --max_epochs 1 \
    --offload_actor True \
    --enable_gradient_compression False \
    --ncccl_min_tensor_size_mb 1

Флаг --enable_gradient_compression=False — важный: при включении баг NCCL проявляется с вероятностью 80%.

4 Мониторинг и профилирование

Зайдите в Ray Dashboard (порт 8265), следите за worker_pending — если время пересылки градиентов растёт, немедленно останавливайте и включайте большее значение --ncccl_min_tensor_size_mb.

💡
Для кластеров с InfiniBand критично выставить NCCL_IB_TIMEOUT=30 и NCCL_IB_RETRY_CNT=7. Без этого all_reduce на 256 GPU будет падать с таймаутом.

Типичные ошибки (и как НЕ надо делать)

Ошибка 1: Использовать --rollout_batch_size меньше 256. При маленьком batch размер тензоров градиентов падает ниже порога NCCL, и баг активируется. Лучше увеличить batch, даже ценой меньшего числа итераций.

Ошибка 2: Запускать veRL на GPU с разной памятью (например, смесь A100 80GB и A100 40GB). vLLM не умеет эффективно балансировать — часть GPU будет простаивать, а часть выпадать по OOM. Используйте однотипные ускорители.

Ошибка 3: Игнорировать техники Unsloth GRPO для длинного контекста. veRL поддерживает контекст до 128K, но без оптимизации flash-attention и paged attention vLLM умирает. Встройте --use_vllm_paged_attention.

Ошибка 4: Пытаться использовать verl.replay для офлайн-обучения без предварительной нормализации ревардов. Без этого градиенты уходят в космос. Сделайте reward_norm=RunningMeanStd.

Будущее veRL и NCCL-проблема

На момент июня 2026 года сообщество активно форкает veRL и чинит баги собственными силами. Один из самых интересных форков — с интеграцией RLM для динамического управления длиной контекста. Но NCCL-баг остаётся открытым issue под номером #401. Разработчики обещают фикс в v0.6.0 с использованием torch.distributed.all_to_all_single для малых тензоров. Ждём.

А пока советую всем, кто использует veRL в продакшне: заложите в цикл обучения дополнительный мониторинг градиентных коммуникаций. Если loss вдруг начал скакать, а GPU молчат — проверьте не только градиенты, но и размер all_reduce. И не забывайте про DroPE — возможно, вам вообще не нужен RL с полным референсом.

Подписаться на канал