Перейти к содержимому
Ступень 66.4 Рассуждения и RL — путь DeepSeek-R1

DAPO, GSPO, CISPO — развитие GRPO

Что сообщество и китайские лаборатории исправили в GRPO за 2025 год: коллапс энтропии, смещение по длине, вырожденные группы, шумные веса importance sampling, нестабильность MoE — DAPO, Dr. GRPO, GSPO, CISPO и подход Kimi k1.5.

УРОКstage-6/6-4-reasoning/03-grpo-variants.mdx1 ч 50 минУрок 3 из 5
После урока вы сможете
  • Назвать пять болезней «наивного» GRPO и для каждой — лекарство и лабораторию, которая его предложила
  • Объяснить четыре приёма DAPO и посчитать Clip-Higher и штраф за сверхдлинный ответ на числах
  • Сравнить, как GRPO, DAPO, Dr. GRPO, GSPO и CISPO распределяют градиент по токенам, и проверить это кодом
  • Объяснить, почему падает энтропия политики и чем опасен коллапс энтропии

После выхода R1 десятки команд попробовали повторить R1-Zero. Самый показательный результат — у ByteDance Seed: «наивный» GRPO на Qwen2.5-32B-Base дал на AIME 2024 всего 30 баллов, а DeepSeek на той же базе сообщали о 47. Рецепт, который «просто работает» на 671B-модели с тщательно подобранной инфраструктурой, на других моделях начал ломаться: падала энтропия, раздувалась длина, обучение внезапно разваливалось.

За 2025 год накопился набор исправлений. Почти все они — китайские: DAPO (ByteDance и Цинхуа), GSPO (Qwen), CISPO (MiniMax), подход Kimi k1.5 (Moonshot). Сегодня это не экзотика, а переключатели в TRL и verl (библиотеки RL-дообучения от Hugging Face и ByteDance, они уже встречались в модуле 6.3 и уроке 6.4.1): loss_type="dapo", importance_sampling_level="sequence", loss_type="cispo". Чтобы осознанно их выбирать, нужно понимать, какую болезнь лечит каждый.

Болезнь «наивного» GRPOСимптом в логахЛекарство
коллапс энтропииколлапс энтропииEN entropy collapseЭнтропия политики быстро падает при RL: ответы в группе становятся почти одинаковыми, исследование прекращается, рост награды упирается в потолок.энтропия падает, ответы в группе одинаковые, награда выходит на платоClip-Higher (DAPO), CISPO, контроль энтропии
Вырожденные («пустые») группырастёт доля групп с наградой 0 или 1, градиент шумныйдинамический сэмплинг (DAPO)
смещение по длинесмещение по длине (в GRPO)EN response-level length biasИз-за множителя 1/|o_i| короткие верные ответы поощряются сильнее, а длинные неверные наказываются слабее. Исправления — token-level loss (DAPO) и Dr. GRPO.длина растёт, особенно у неверных ответовtoken-level loss (DAPO), Dr. GRPO
Шум усечённых ответовхорошие, но обрезанные по лимиту ответы получают штрафмягкий штраф за длину (DAPO), штраф Kimi
Шумные токенные веса importance sampling (IS, урок 6.3.2), коллапс MoEвнезапный необратимый развал обученияGSPO

Энтропияэнтропия (информационная)EN (Shannon) entropyH(p) = −Σ p log p — среднее «удивление», мера неопределённости распределения. Максимальна у равномерного распределения. политики на токене — H=−∑aπ(a)log⁡π(a)H = -\sum_a \pi(a) \log \pi(a): насколько модель «сомневается», какой токен выбрать (урок 1.3.3). Если энтропия близка к нулю, модель на каждом шаге почти детерминирована — и все GG ответов группы становятся одинаковыми. А одинаковые ответы — это одинаковые награды и нулевые преимущества. RL перестаёт исследовать и упирается в потолок.

Почему энтропия падает сама? RL подкрепляет то, что получило высокую награду. Но сэмплируются чаще всего вероятные токены, значит, и подкрепляются чаще всего они. Подъём и без того частого токена делает распределение острее. Работа Cui и соавторов («The Entropy Mechanism of RL for Reasoning LMs», май 2025) формализовала это: изменение энтропии за шаг определяется ковариацией между логарифмом вероятности действия и его преимуществом. Напомним: ковариация двух величин положительна, если они обычно вместе выше или вместе ниже своих средних, и отрицательна, если одна растёт, когда другая падает (урок 2.3.2). Положительная ковариация здесь значит: высокое преимущество чаще достаётся и без того вероятным токенам. Если поощряются частые действия — энтропия падает, если редкие — растёт. Там же показана эмпирическая зависимость R=−a eH+bR = -a\, e^{H} + b (aa и bb — положительные константы, подобранные под конкретную модель): когда энтропия исчерпана (H→0H \to 0, значит eH→1e^{H} \to 1), награда упирается в потолок b−ab - a.

Проверим на числах (код в разделе «Практика»): распределение [0.7,0.2,0.05,0.05][0.7, 0.2, 0.05, 0.05] с энтропией 0.871. Один шаг градиента политики, который поощряет частый токен, опускает энтропию до 0.655. Тот же шаг, но поощряющий редкий токен, поднимает её до 0.903.

Отсюда все приёмы борьбы: не мешать редким токенам расти (Clip-Higher, CISPO), не давать частым разгоняться (Clip-Cov и KL-Cov из той же работы — они ограничивают обновления токенов с наибольшей ковариацией), добавлять бонус за энтропию (в TRL — entropy_coef). В отчёте Qwen3 отдельно сказано, что на этапе reasoning RL энтропию держали растущей или стабильной — и это было критично для стабильности.

DAPO (Decoupled Clip and Dynamic sAmpling Policy Optimization) — работа ByteDance Seed и института AIR Университета Цинхуа, март 2025. Открыто всё: алгоритм, код на verl и датасет DAPO-Math-17K, где ответы всех задач приведены к целым числам, чтобы их было легко проверять. Награда простая: +1 за верный ответ, −1 за неверный. KL-штраф убран совсем: модель для длинных рассуждений и должна далеко уйти от исходной.

Клиппинг PPO (урок 6.0.3) ограничивает ρ=πθ/πold\rho = \pi_\theta / \pi_{\text{old}} отрезком [0.8,1.2][0.8, 1.2]. Посмотрим, что это значит для вероятностей:

πold\pi_{\text{old}}Максимум πθ\pi_\theta при ε=0.2\varepsilon = 0.2При εhigh=0.28\varepsilon_{\text{high}} = 0.28
0.91.08 — ограничения фактически нет1.15
0.10.120.128
0.010.0120.0128

Частый токен может «подрасти» до единицы за один шаг, а редкий — только на 20% от своей крохотной вероятности. DAPO заметили, что почти все клиппированные токены имели вероятность меньше 0.2. Лекарство — асимметричный клиппингасимметричный клиппинг (Clip-Higher)EN Clip-Higher (decoupled clipping range)Верхняя граница клиппинга больше нижней (в DAPO — 1 + 0.28 и 1 − 0.2), чтобы редкие «исследовательские» токены могли расти.: развязать границы, 1−εlow1 - \varepsilon_{\text{low}} и 1+εhigh1 + \varepsilon_{\text{high}}, и поднять верхнюю до 0.28. Нижнюю оставляют 0.2: если её расширить, редкие токены можно «задавить» до нуля, и пространство сэмплирования схлопнется.

Вспомните вырожденные группы из урока 6.4.1. По мере обучения всё больше задач модель решает на 100%, их группы дают нулевой градиент, и эффективный размер батча тает. динамический сэмплингдинамический сэмплингEN dynamic samplingВыбросить группы с точностью 0 или 1 и досэмплировать новые, пока батч не заполнится «живыми» группами.: генерировать с запасом, выбрасывать группы с точностью 0 или 1 и досэмплировать, пока батч не заполнится «живыми» группами. Генерации становится больше, но, по наблюдениям авторов, до той же точности обучение доходит быстрее.

В GRPO сначала усредняют по токенам ответа, потом по ответам. Каждый ответ весит одинаково, поэтому каждый токен длинного ответа весит меньше. Для длинных рассуждений это плохо дважды: хорошие длинные решения учат модель слабее, а мусор в длинных ответах (повторы, бессвязный текст) почти не наказывается. loss на уровне токеновloss на уровне токеновEN token-level policy gradient lossСумма по всем токенам батча, делённая на их общее число: каждый токен весит одинаково, независимо от длины ответа. DAPO:

JDAPO=E[1∑i=1G∣oi∣∑i=1G∑t=1∣oi∣min⁡(ρi,tAi, clip(ρi,t,1−εlow,1+εhigh) Ai)]\mathcal{J}_{\text{DAPO}} = \mathbb{E}\left[ \frac{1}{\sum_{i=1}^{G} |o_i|} \sum_{i=1}^{G} \sum_{t=1}^{|o_i|} \min\big(\rho_{i,t} A_i,\ \mathrm{clip}(\rho_{i,t}, 1 - \varepsilon_{\text{low}}, 1 + \varepsilon_{\text{high}})\, A_i\big) \right]

Что изменилось: одна общая сумма по всем токенам группы, делённая на их общее число. Паттерн, который повышает или понижает награду, поощряется или подавляется одинаково, в каком бы длинном ответе он ни встретился.

У генерации есть лимит длины. Ответ, обрезанный на полуслове, не содержит итогового ответа и получает −1 — хотя рассуждение могло быть верным. Это шум в награде. Сначала авторы попробовали самое простое — не учиться на обрезанных ответах вовсе, обнулив их loss маской (overlong filtering). Уже это заметно стабилизировало обучение (см. таблицу ниже). Следующий шаг — «буфер» LcacheL_{\text{cache}} перед жёстким лимитом Lmax⁡L_{\max}, пример формирование наградыформирование награды (reward shaping)EN reward shapingДобавки к основной награде, которые направляют обучение: штраф за длину, за формат, за смешение языков.:

Rlength(y)={0,∣y∣≤Lmax⁡−Lcache(Lmax⁡−Lcache)−∣y∣Lcache,Lmax⁡−Lcache<∣y∣≤Lmax⁡−1,∣y∣>Lmax⁡R_{\text{length}}(y) = \begin{cases} 0, & |y| \le L_{\max} - L_{\text{cache}} \\[2pt] \dfrac{(L_{\max} - L_{\text{cache}}) - |y|}{L_{\text{cache}}}, & L_{\max} - L_{\text{cache}} < |y| \le L_{\max} \\[6pt] -1, & |y| > L_{\max} \end{cases}

В статье Lmax⁡=20 480L_{\max} = 20\,480, Lcache=4096L_{\text{cache}} = 4096: до 16 384 токенов штрафа нет, дальше он линейно растёт. Ответ длиной 18 432 токена получает (16 384−18 432)/4096=−0.5(16\,384 - 18\,432) / 4096 = -0.5 к награде за точность.

Таблица 1 из статьи DAPO, AIME 2024 (avg@32 — каждую задачу решают 32 раза и усредняют долю верных ответов, так оценка меньше зависит от удачи одного сэмпла), Qwen2.5-32B-Base:

КонфигурацияAIME 2024
DeepSeek-R1-Zero-Qwen-32B (ориентир)47
Наивный GRPO30
+ фильтрация обрезанных ответов36
+ Clip-Higher38
+ мягкий штраф за длину41
+ token-level loss42
+ динамический сэмплинг = DAPO50

Итог — 50 баллов при вдвое меньшем числе шагов, чем у ориентира. Авторы отмечают, что token-level loss даёт немного баллов, зато делает рост длины «здоровым» и обучение — стабильнее.

Работа Sea AI Lab «Understanding R1-Zero-Like Training» (март 2025) нашла в формуле GRPO два смещения и предложила вариант Dr. GRPO («GRPO Done Right»):

  1. Смещение по длине — из-за 1/∣oi∣1/\vert o_i \vert. При A>0A > 0 короткие ответы получают больший градиент на токен, при A<0A < 0 длинные неверные ответы наказываются слабее. Результат — модель «учится» ошибаться многословно, и длина растёт даже когда награда уже не растёт.
  2. Смещение по трудности — из-за деления на std группы. Почти решённые и почти безнадёжные вопросы (маленький std) получают больший вес (вы видели это в уроке 6.4.1).

Исправление — делить сумму по токенам на константу G⋅Lmax⁡G \cdot L_{\max}, а не на длину ответа, и не делить преимущество на std: Ai=ri−mean(r)A_i = r_i - \mathrm{mean}(r). По данным авторов, точность сохраняется, а длина неверных ответов заметно падает. В TRL это loss_type="dr_grpo" и scale_rewards="none" (или False).

GSPO: отношение вероятностей для всего ответа

Заголовок раздела «GSPO: отношение вероятностей для всего ответа»

GSPO (Group Sequence Policy Optimization) — работа команды Qwen, июль 2025. Её аргумент глубже настроек: токенные веса importance sampling в GRPO некорректны в принципе.

Importance sampling исправляет расхождение распределений, усредняя по многим сэмплам. А в GRPO вес ρi,t\rho_{i,t} считается для каждой позиции по одному сэмплу из распределения следующего токена. Такой вес ничего не корректирует — он просто добавляет шум, который накапливается с длиной ответа и усиливается клиппингом. Авторы наблюдали необратимый коллапс: откат к чекпоинту и подбор гиперпараметров не помогали. Принцип GSPO: единица оптимизации должна совпадать с единицей награды. Награда выдаётся за весь ответ — значит, и отношение вероятностей нужно для всего ответа:

si(θ)=(πθ(yi∣x)πθold(yi∣x))1∣yi∣=exp⁡(1∣yi∣∑t=1∣yi∣log⁡πθ(yi,t∣x,yi,<t)πθold(yi,t∣x,yi,<t))s_i(\theta) = \left(\frac{\pi_\theta(y_i \mid x)}{\pi_{\theta_{\text{old}}}(y_i \mid x)}\right)^{\frac{1}{|y_i|}} = \exp\left(\frac{1}{|y_i|} \sum_{t=1}^{|y_i|} \log \frac{\pi_\theta(y_{i,t} \mid x, y_{i,<t})}{\pi_{\theta_{\text{old}}}(y_{i,t} \mid x, y_{i,<t})}\right) JGSPO=E[1G∑i=1Gmin⁡(siAi, clip(si,1−ε,1+ε) Ai)]\mathcal{J}_{\text{GSPO}} = \mathbb{E}\left[\frac{1}{G}\sum_{i=1}^{G} \min\big(s_i A_i,\ \mathrm{clip}(s_i, 1 - \varepsilon, 1 + \varepsilon)\, A_i\big)\right]

Как читать: sis_i — среднее геометрическое токенных отношений, то есть насколько в среднем на токен изменилась вероятность всего ответа. Среднее геометрическое nn чисел — корень nn-й степени из их произведения: для 2 и 8 это 2⋅8=4\sqrt{2 \cdot 8} = 4. Вероятность ответа — произведение вероятностей токенов, поэтому отношение для всего ответа — произведение токенных ρi,t\rho_{i,t}, а корень степени ∣yi∣\vert y_i \vert превращает его в «типичное» отношение на токен. Вторая запись формулы — то же самое через логарифмы: среднее логарифмов, а затем экспонента. Корень степени ∣yi∣\vert y_i \vert нужен, чтобы ответы разной длины жили в одной шкале. Клиппинг тоже на уровне ответа: «слишком off-policy» ответы выбрасываются целиком. Поэтому и границы другие: в экспериментах статьи — 3⋅10−43 \cdot 10^{-4} и 4⋅10−44 \cdot 10^{-4}, а у GRPO для сравнения 0.2 и 0.27. Парадокс: GSPO клиппирует около 15% токенов против 0.13% у GRPO — на два порядка больше — и всё равно учится эффективнее. Оставшиеся градиенты просто качественнее.

MoE. Для моделей-смесей экспертов (модуль 5.3) аргумент становится решающим. В 48-слойной Qwen3-30B-A3B-Base после каждого шага градиента примерно 10% экспертов, активных для того же ответа, меняются. Токенные отношения ρi,t\rho_{i,t} при этом прыгают уже не из-за обучения, а из-за смены маршрута. Для GRPO приходилось использовать Routing Replay — запоминать маршруты старой политики и принудительно повторять их при подсчёте ρ\rho. Это стоит памяти и связи между GPU и ограничивает модель. GSPO в Routing Replay не нуждается: вероятность целого ответа гораздо устойчивее к смене отдельных экспертов. Авторы пишут, что GSPO внёс вклад в улучшения последних моделей Qwen3.

MiniMax-M1 (июнь 2025) — reasoning-модель на гибридном линейном внимании (урок 5.5.3). Обучая её, команда заметила: токены рефлексии — «However», «Recheck», «Wait», «Aha» — у базовой модели редки и маловероятны. После первого обновления их ρ\rho резко растёт, клиппинг их вырезает, и в следующих off-policy-шагах (у MiniMax было 16 шагов на батч) они градиента не получают. Off-policy — противоположность on-policy из уроков 6.0.2 и 6.3.1: шаги делают на ответах, которые сгенерировала уже немного устаревшая версия политики. Но именно эти «развилки» рассуждения и нужны. Clip-Higher DAPO в их постановке помогал слабо.

CISPO (Clipped IS-weight Policy Optimization) возвращается к REINFORCE с поправкой importance sampling и клиппирует сам вес, а не обновление:

JCISPO=E[1∑i∣oi∣∑i=1G∑t=1∣oi∣sg(ρ^i,t) Ailog⁡πθ(oi,t∣q,oi,<t)],ρ^i,t=clip(ρi,t, 1−εlow, 1+εhigh)\begin{gathered} \mathcal{J}_{\text{CISPO}} = \mathbb{E}\left[\frac{1}{\sum_i |o_i|}\sum_{i=1}^{G}\sum_{t=1}^{|o_i|} \mathrm{sg}\big(\hat\rho_{i,t}\big)\, A_i \log \pi_\theta(o_{i,t} \mid q, o_{i,<t})\right], \\ \hat\rho_{i,t} = \mathrm{clip}\big(\rho_{i,t},\ 1 - \varepsilon_{\text{low}},\ 1 + \varepsilon_{\text{high}}\big) \end{gathered}

sg\mathrm{sg} — stop-gradient: вес — просто число, градиент через него не течёт (в PyTorch это .detach(), см. код ниже). Разница с PPO: там клиппированный токен даёт нулевой градиент, здесь — градиент с ограниченным весом. Нижнюю границу MiniMax фактически не использовали, настраивали только верхнюю. Плюс token-level loss и динамический сэмплинг из DAPO, KL-штрафа нет. В контрольном эксперименте на Qwen2.5-32B-Base CISPO догнал DAPO за вдвое меньшее число шагов. Весь RL модели M1 занял три недели на 512 H800 — 534 700 долл. аренды.

Kimi k1.5 вышла 20 января 2025 года — в один день с R1 — и пришла к похожему выводу другим путём. Moonshot решает задачу «максимум награды минус KL к текущей политике итерации» — ту самую, у которой в уроке 6.3.1 мы нашли точное решение π∗∝πθier/τ\pi^* \propto \pi_{\theta_i} e^{r/\tau}. На практике градиент сводится к градиенту политики с baseline = средняя награда kk ответов на задачу плюс квадратичный штраф за отход от πθi\pi_{\theta_i}. То есть та же групповая идея, что в GRPO, полученная из другой теории (online mirror descent — «зеркальный спуск»; её детали нам не понадобятся, важен итоговый градиент). Модели-критика тоже нет, и аргумент интересный: критик наказал бы шаг рассуждения, ведущий в тупик, а для длинных рассуждений полезно пробовать ошибочные пути, если модель потом из них выбирается.

Второй вклад — борьба с «передумывание»«передумывание» (overthinking)EN overthinkingМодель тратит тысячи токенов там, где хватило бы сотни. Лечат штрафом за длину, long2short, бюджетом мышления.. Штраф за длину внутри группы kk ответов:

λi=0.5−leni−min⁡lenmax⁡len−min⁡len,len_rewardi={λi,ответ верныйmin⁡(0,λi),ответ неверный\begin{gathered} \lambda_i = 0.5 - \frac{\mathrm{len}_i - \min \mathrm{len}}{\max \mathrm{len} - \min \mathrm{len}}, \\ \text{len\_reward}_i = \begin{cases} \lambda_i, & \text{ответ верный} \\ \min(0, \lambda_i), & \text{ответ неверный} \end{cases} \end{gathered}

Пример: в группе ответы длиной 1000, 3000 и 5000 токенов, λ=[0.5, 0, −0.5]\lambda = [0.5,\ 0,\ -0.5]. Самый короткий верный получает +0.5, самый длинный неверный — −0.5, а короткий неверный — 0 (за краткость неверного ответа не хвалят). Штраф включают не с начала, а после «разогрева»: иначе он замедляет обучение.

Ещё два приёма k1.5 — partial rollouts (длинные ответы генерируются по частям через несколько итераций, контекст RL довели до 128K токенов) и long2short: перенос навыков длинного рассуждения в короткие ответы через усреднение весов моделей, отбор самого короткого верного ответа из 8, DPO «короткое верное против длинного» и отдельную RL-фазу со штрафом за длину. Результаты k1.5 в режиме длинного рассуждения: AIME 77.5, MATH-500 96.2.

МетодКто и когдаЧто меняетЧто лечит
Kimi k1.5Moonshot, январь 2025зеркальный спуск, штраф за длину, long2shortoverthinking, длинный контекст в RL
Dr. GRPOSea AI Lab, март 2025без 1/∣oi∣1/\vert o_i \vert и без stdсмещения по длине и трудности
DAPODAPOEN Decoupled Clip and Dynamic sAmpling Policy Optimization (DAPO)ByteDance Seed и Tsinghua AIR, 2025: Clip-Higher, динамический сэмплинг, token-level loss, мягкий штраф за сверхдлинные ответы.ByteDance Seed + Tsinghua AIR, март 2025Clip-Higher, динамический сэмплинг, token-level loss, штраф за длину, без KLколлапс энтропии, вырожденные группы, шум обрезки
CISPOCISPOEN Clipped IS-weight Policy Optimization (CISPO)MiniMax-M1, 2025: клиппуется вес importance sampling (без градиента через него), а не само обновление — градиент получают все токены.MiniMax, июнь 2025клиппинг веса IS со stop-gradientпотеря градиента редкими токенами
GSPOGSPOEN Group Sequence Policy Optimization (GSPO)Qwen, 2025: отношение вероятностей и клиппинг на уровне всего ответа, s_i = (π_θ(y)/π_old(y))^(1/|y|). Стабилизирует RL для MoE.Qwen, июль 2025отношение и клиппинг на уровне ответашум токенных весов, нестабильность MoE

Направление 2026 года — дальше в ту же сторону: мягкие «шлюзы» вместо жёсткого клиппинга (например, SAPO от Qwen, ноябрь 2025, есть в TRL как loss_type="sapo"), асинхронный RL и аккуратная работа с расхождением генерации и обучения. Универсального победителя нет: выбор зависит от модели (плотная или MoE), длины ответов и того, сколько шагов делают на одном батче.

Сравним методы на одной игрушечной группе прямо по градиентам. Три эксперимента: как loss распределяет вес по токенам ответов разной длины; что происходит с редким токеном, вероятность которого после шага удвоилась; как поощрение частого и редкого токена меняет энтропию.

import torch
# ── 1. Как разные loss распределяют градиент по токенам (π_θ = π_old, ρ = 1) ──
r = torch.tensor([1.0, 1.0, 0.0, 0.0]) # группа: два верных, два неверных ответа
lengths = torch.tensor([4, 12, 4, 12]) # короткие и длинные ответы
L_MAX, T = 16, 12 # лимит длины (для Dr. GRPO) и ширина батча = самый длинный ответ
mask = (torch.arange(T)[None, :] < lengths[:, None]).float() # mask[i, t] = 1, если позиция t есть в ответе i, иначе паддинг
def token_weights(kind):
logp = torch.zeros(4, T, requires_grad=True) # значения не важны: смотрим только градиент
if kind == "Dr. GRPO":
adv = r - r.mean() # без деления на std
else:
adv = (r - r.mean()) / r.std(unbiased=False)
# exp(logp − logp.detach()) по значению равно 1 (ρ = 1, политика не менялась), но его производная по logp тоже 1,
# поэтому logp.grad покажет, с каким весом каждый токен входит в градиент
obj = torch.exp(logp - logp.detach()) * adv[:, None] * mask # ρ·A на каждом токене
if kind == "GRPO":
J = (obj.sum(1) / mask.sum(1)).mean() # 1/|o_i| внутри ответа, затем среднее по группе
elif kind == "DAPO":
J = obj.sum() / mask.sum() # все токены группы равноправны
else:
J = obj.sum() / (len(r) * L_MAX) # Dr. GRPO: делим на константу G·L_max
J.backward()
return logp.grad[:, 0] # вес первого токена каждого ответа
print("вес токена (dJ/d log π): верн.4 верн.12 неверн.4 неверн.12")
for kind in ["GRPO", "DAPO", "Dr. GRPO"]:
print(f"{kind:9s}", " ".join(f"{w:+.4f}" for w in token_weights(kind).tolist()))
# ── 2. Редкий «развилочный» токен: π_old = 0.01, после шага π = 0.02 (ρ = 2), A = +1 ──
def grad_rare_token(kind, n_tokens=10):
logp_old = torch.log(torch.full((n_tokens,), 0.5)) # обычные токены ответа: π_old = 0.5
logp_old[0] = torch.log(torch.tensor(0.01)) # первый — редкий «развилочный»: π_old = 0.01
logp = logp_old.clone()
logp[0] = torch.log(torch.tensor(0.02)) # после шага он стал вдвое вероятнее, остальные — нет
logp.requires_grad_(True)
A = 1.0
ratio = torch.exp(logp - logp_old)
if kind == "GRPO, ε=0.2":
J = torch.min(ratio * A, ratio.clamp(0.8, 1.2) * A).mean()
elif kind == "DAPO, ε_high=0.28":
J = torch.min(ratio * A, ratio.clamp(0.8, 1.28) * A).mean()
elif kind == "CISPO, вес ≤ 5":
w = ratio.clamp(max=5.0).detach() # вес IS обрезан и без градиента
J = (w * A * logp).mean()
else: # GSPO: одно отношение на весь ответ
s = torch.exp((logp - logp_old).mean()) # s = (π/π_old)^(1/|y|)
J = torch.min(s * A, s.clamp(1 - 3e-4, 1 + 4e-4) * A)
J.backward()
return logp.grad[0].item(), logp.grad[1].item()
print()
for kind in ["GRPO, ε=0.2", "DAPO, ε_high=0.28", "CISPO, вес ≤ 5", "GSPO"]:
g_rare, g_other = grad_rare_token(kind)
print(f"{kind:18s} градиент редкого токена {g_rare:+.3f}, обычного {g_other:+.3f}")
# ── 3. Почему энтропия падает: подкрепляем частый токен или редкий ──
def entropy(p):
return -(p * p.log()).sum().item()
for name, adv in [("поощряем частый", [1.0, -1.0, -1.0, -1.0]), ("поощряем редкий", [-1.0, -1.0, 1.0, -1.0])]:
z = torch.log(torch.tensor([0.7, 0.2, 0.05, 0.05])).requires_grad_(True) # логиты: softmax(log p) = p
J = (torch.softmax(z, 0) * torch.tensor(adv)).sum() # ожидаемое преимущество
J.backward()
with torch.no_grad():
p_new = torch.softmax(z + 1.0 * z.grad, 0) # один шаг градиентного ПОДЪЁМА с lr = 1 (J максимизируем)
print(f"{name}: H = {entropy(torch.softmax(z, 0).detach()):.3f} → {entropy(p_new):.3f}")

Вывод:

вес токена (dJ/d log π): верн.4 верн.12 неверн.4 неверн.12
GRPO +0.0625 +0.0208 -0.0625 -0.0208
DAPO +0.0312 +0.0312 -0.0312 -0.0312
Dr. GRPO +0.0078 +0.0078 -0.0078 -0.0078
GRPO, ε=0.2 градиент редкого токена +0.000, обычного +0.100
DAPO, ε_high=0.28 градиент редкого токена +0.000, обычного +0.100
CISPO, вес ≤ 5 градиент редкого токена +0.200, обычного +0.100
GSPO градиент редкого токена +0.000, обычного +0.000
поощряем частый: H = 0.871 → 0.655
поощряем редкий: H = 0.871 → 0.903

Как читать (здесь JJ — цель, которую максимизируют, поэтому плюс означает «поднять вероятность»):

  • Эксперимент 1. В GRPO токен короткого ответа весит в 3 раза больше токена длинного — и для верных, и для неверных. Проверим вручную: награды [1,1,0,0][1, 1, 0, 0], среднее 0.5, std 0.5, значит A=±1A = \pm 1. В GRPO вес токена — A/(G⋅∣oi∣)A / (G \cdot \vert o_i \vert): 1/(4⋅4)=0.06251 / (4 \cdot 4) = 0.0625 и 1/(4⋅12)≈0.02081 / (4 \cdot 12) \approx 0.0208. В DAPO — AA, делённое на общее число токенов 4+12+4+12=324 + 12 + 4 + 12 = 32: 1/32≈0.03121/32 \approx 0.0312. В Dr. GRPO A=±0.5A = \pm 0.5 (без деления на std) и делитель G⋅Lmax⁡=64G \cdot L_{\max} = 64: 0.5/64≈0.00780.5 / 64 \approx 0.0078. DAPO и Dr. GRPO дают всем токенам одинаковый вес. У Dr. GRPO веса меньше по модулю (делим на константу G⋅Lmax⁡G \cdot L_{\max} и не нормируем на std) — это компенсируют learning rate.
  • Эксперимент 2. Редкий токен, вероятность которого удвоилась (ρ=2\rho = 2), в GRPO и даже в DAPO вылетает за границу клиппинга и не получает градиента — хотя у него положительное преимущество. CISPO даёт ему градиент с весом 2 (верхнюю границу веса 5 мы взяли из рекомендации TRL для loss_type="cispo"). GSPO решает иначе: среднее геометрическое s=21/10≈1.07s = 2^{1/10} \approx 1.07 выходит за узкую границу, и ответ клиппируется целиком — слишком off-policy ответы GSPO просто не использует.
  • Эксперимент 3. Поощрение частого токена снижает энтропию (0.871 → 0.655), редкого — повышает. Механизм коллапса энтропии на четырёх числах.

Попробуйте сами: поставьте в эксперименте 2 n_tokens=1000 — у GSPO s=21/1000≈1.0007s = 2^{1/1000} \approx 1.0007, всё ещё больше 1+4⋅10−41 + 4 \cdot 10^{-4}. А при n_tokens=2000 ответ уже пройдёт через клиппинг. Длинный ответ, в котором изменился один токен, для GSPO почти on-policy.

Проверь себя6 вопросов
1 Токен имел вероятность πold=0.02\pi_{\text{old}} = 0.02. До какой максимальной вероятности его можно поднять за шаг с Clip-Higher εhigh=0.28\varepsilon_{\text{high}} = 0.28? Ответ — с точностью до 0.0001.
2 Что делает динамический сэмплинг DAPO?
3 Какие утверждения о GSPO верны?Можно выбрать несколько ответов
4 В чём главное отличие CISPO от клиппинга PPO/GRPO?
5 DAPO: Lmax⁡=20 480L_{\max} = 20\,480, Lcache=4096L_{\text{cache}} = 4096. Какую добавку к награде получит ответ длиной 17 408 токенов?
6 Что помогает против коллапса энтропии?Можно выбрать несколько ответов
  • «Наивный» GRPO болеет пятью болезнями: коллапс энтропии, вырожденные группы, смещение по длине, шум обрезанных ответов, шумные токенные веса importance sampling (особенно у MoE).
  • DAPO = Clip-Higher + динамический сэмплинг + token-level loss + мягкий штраф за длину, без KL: с 30 до 50 баллов AIME на Qwen2.5-32B. Dr. GRPO убирает деление на длину и на std.
  • GSPO считает отношение вероятностей и клиппинг на уровне всего ответа — так устойчивее, особенно для MoE.
  • CISPO клиппирует вес, а не обновление: редкие «развилочные» токены не теряют градиент.
  • Kimi k1.5 пришла к групповому baseline через зеркальный спуск и добавила штраф за длину и long2short.

Дальше: как передать рассуждения маленькой модели без собственного RL — дистилляция R1, strong-to-weak у Qwen3, on-policy дистилляция — и как управлять тем, сколько модель «думает».