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

Дистилляция рассуждений и режимы мышления

Как передать рассуждения маленькой модели без собственного RL — R1-Distill, strong-to-weak у Qwen3, on-policy дистилляция с обратной KL — и как управлять тем, сколько модель думает: /think и /no_think, бюджет мышления, test-time compute.

УРОКstage-6/6-4-reasoning/04-distillation-thinking.mdx1 ч 40 минУрок 4 из 5
После урока вы сможете
  • Объяснить, почему маленькой модели выгоднее учиться рассуждать у большой, чем через собственный RL, и привести числа из R1 и Qwen3
  • Сравнить off-policy дистилляцию (SFT, прямая KL) и on-policy дистилляцию (обратная KL) и проверить разницу кодом
  • Описать гибридный режим мышления и бюджет мышления на примере Qwen3 и DeepSeek-V4
  • Объяснить идею test-time compute и когда она выгоднее увеличения модели

R1 — это 671 миллиард параметров. На ноутбуке её не запустить, а дообучать её своим RL могут единицы. Нужны маленькие модели, которые умеют рассуждать. Очевидный путь — повторить рецепт R1-Zero на маленькой базе. Но прошлые уроки показали, что RL лишь усиливает то, что в базе уже было: у TinyZero 0.5B-модель рассуждать не научилась вовсе, а Qwen-32B после 10 тысяч шагов большого RL набрала на AIME 47%.

Оказалось, есть путь дешевле и лучше: научить маленькую модель у большой — дистилляциядистилляцияEN (knowledge) distillation. В 2025–2026 годах она прошла путь от «просто SFT на ответах R1» до on-policy дистилляции, которой DeepSeek-V4, по обзорам её техотчёта, заменила целый этап RL. Вторая половина урока — о том, как управлять «длиной мысли»: режимы /think и /no_think, бюджет мышления и общий принцип test-time compute.

Рецепт из статьи R1 предельно прост. Те же ~800 тысяч примеров, на которых дообучали саму R1 (≈600K рассуждений, отобранных отбором с отклонениемотбор с отклонением (rejection sampling)EN rejection samplingСгенерировать много ответов и оставить только прошедшие проверку. В пост-трейнинге — способ собрать SFT-данные из RL-модели., и ≈200K прочих задач), используют как SFT-данные для шести открытых моделей. Формат — тот, что вы строили в уроке 6.1.2: промпт, затем ответ ассистента с рассуждением внутри <think>…</think> и итоговым ответом; loss — только на ответе. Никакого RL — авторы сознательно оставили его сообществу.

МодельБазаAIME 2024 pass@1MATH-500 pass@1
R1-Distill-Qwen-1.5BQwen2.5-Math-1.5B28.983.9
R1-Distill-Qwen-7BQwen2.5-Math-7B55.592.8
R1-Distill-Qwen-14BQwen2.5-14B69.793.9
R1-Distill-Qwen-32BQwen2.5-32B72.694.3
R1-Distill-Llama-8BLlama-3.1-8B50.489.1
R1-Distill-Llama-70BLlama-3.3-70B-Instruct70.094.5
для сравнения: GPT-4o-0513—9.374.6

Модель на 1.5 миллиарда параметров решает олимпиадную математику втрое лучше GPT-4o — только за счёт того, что училась на чужих рассуждениях. А эксперимент §4.1 ставит точку в споре на одной базе: Qwen-32B после большого RL — 47.0%, после дистилляции из R1 — 72.6%. Вывод авторов: маленьким моделям выгоднее учиться у большой, а двигать границу возможностей по-прежнему должны сильные базы и большой RL. Дистилляция копирует, а не изобретает.

LeapLab из Цинхуа, чья работа о pass@k обсуждалась в прошлых уроках, добавляет важное наблюдение: дистилляция и RLVR устроены принципиально по-разному. RL затачивает решения, которые база и так находила; дистилляция способна принести новые паттерны рассуждений — ведь они приходят от более сильной модели.

У SFT на ответах учителя две слабости.

1. Смещение экспозиции (смещение экспозициисмещение экспозиции (exposure bias)EN exposure biasПри обучении модель видит только «правильные» префиксы, а при генерации — свои собственные, с ошибками. Отсюда накопление ошибок.). На обучении ученик видит только «идеальные» префиксы учителя. На генерации он продолжает свои префиксы — с собственными ошибками, которых в данных не было. Ошибка на десятом токене выводит его в незнакомую область, и дальше ошибки копятся. Чем длиннее рассуждение, тем больнее.

2. Слабый ученик и прямая KL. SFT на сэмплах учителя — это минимизация прямой KL:

KL(πT∥πS)=∑yπT(y)log⁡πT(y)πS(y)\mathrm{KL}(\pi_T \Vert \pi_S) = \sum_y \pi_T(y) \log \frac{\pi_T(y)}{\pi_S(y)}

Штраф огромен, если ученик дал маленькую вероятность тому, что учитель делает часто. На числах: учитель даёт ответу вероятность 0.5, ученик — 0.001; слагаемое 0.5⋅ln⁡(0.5/0.001)=0.5⋅ln⁡500≈3.10.5 \cdot \ln(0.5 / 0.001) = 0.5 \cdot \ln 500 \approx 3.1. Обратная ситуация (учитель 0.001, ученик 0.5) почти бесплатна: 0.001⋅ln⁡(0.001/0.5)≈−0.0060.001 \cdot \ln(0.001 / 0.5) \approx -0.006. Поэтому ученик старается покрыть всё, что умеет учитель. Если ёмкости (параметров ученика) не хватает, он размазывает вероятность между способами решения — и генерирует то, чего учитель не делал никогда: начало одного решения, конец другого.

Альтернатива — обратная KLобратная KL-дивергенция (reverse KL)EN reverse KL divergenceKL(q ‖ p), где q — модель, p — цель. «Ищет моду» — модель концентрируется на одном пике цели. Прямая KL(p ‖ q) «покрывает» все пики.:

KL(πS∥πT)=∑yπS(y)log⁡πS(y)πT(y)\mathrm{KL}(\pi_S \Vert \pi_T) = \sum_y \pi_S(y) \log \frac{\pi_S(y)}{\pi_T(y)}

Теперь среднее берётся по ответам ученика. Штраф огромен, если ученик кладёт вероятность туда, где учитель уверен, что ответа нет: с теми же числами (ученик 0.5, учитель 0.001) слагаемое снова ≈3.1\approx 3.1. Ученик выбирает один способ и делает его хорошо. Это и называют «покрытие мод» против «поиска моды». Мода распределения — его «пик», самое вероятное значение; у распределения может быть несколько пиков, и тогда говорят о нескольких модах. Учитель, который знает два способа решения, — распределение с двумя модами.

Проверим на игрушке. Учитель знает два способа решения (две моды в точках −3 и +3, между ними почти пусто), ученик проще — одна «гауссиана», то есть колокол нормального распределениянормальное (гауссовское) распределениеEN normal (Gaussian) distribution«Колокол» N(μ, σ²). Правило 68–95–99.7 — доля значений в пределах 1, 2 и 3 σ от среднего. с центром μ\mu и шириной σ\sigma (урок 1.3.1).

import torch
x = torch.linspace(-10, 10, 801) # сетка «вариантов ответа»
def gauss(mu, sigma):
p = torch.exp(-0.5 * ((x - mu) / sigma) ** 2)
return p / p.sum() # нормируем на сетке → распределение
# Учитель знает два хороших способа решения: моды в −3 и +3, между ними почти пусто
teacher = 0.6 * gauss(torch.tensor(-3.0), 1.0) + 0.4 * gauss(torch.tensor(3.0), 1.0)
gap = (x.abs() <= 1) # «провал» между модами
def fit(kind, steps=3000):
mu = torch.tensor(-1.0, requires_grad=True) # ученик проще учителя: одна гауссиана
log_sigma = torch.tensor(0.0, requires_grad=True) # учим log σ: σ = exp(log_sigma) всегда > 0
opt = torch.optim.Adam([mu, log_sigma], lr=0.02)
for _ in range(steps):
s = gauss(mu, log_sigma.exp()) + 1e-12 # + крошечное число, чтобы не было log(0) = −∞
if kind.startswith("прямая"):
loss = (teacher * (teacher.log() - s.log())).sum() # KL(учитель ‖ ученик): SFT на сэмплах учителя
else:
loss = (s * (s.log() - teacher.log())).sum() # KL(ученик ‖ учитель): on-policy
opt.zero_grad()
loss.backward()
opt.step()
s = gauss(mu, log_sigma.exp()).detach()
print(f"{kind:34s} μ = {mu.item():+.2f}, σ = {log_sigma.exp().item():.2f}, масса в провале: {s[gap].sum():.3f}")
print(f"{'учитель':34s} масса в провале: {teacher[gap].sum():.3f}")
for kind in ["прямая KL (SFT на ответах учителя)", "обратная KL (on-policy)"]:
fit(kind)
учитель масса в провале: 0.023
прямая KL (SFT на ответах учителя) μ = -0.61, σ = 3.13, масса в провале: 0.249
обратная KL (on-policy) μ = -2.99, σ = 1.02, масса в провале: 0.026

Прямая KL поставила ученика между способами (μ≈−0.6\mu \approx -0.6, это ровно среднее учителя 0.6⋅(−3)+0.4⋅30.6 \cdot (-3) + 0.4 \cdot 3) и в 10 раз раздула массу в провале: четверть ответов ученика — то, чего учитель не делает никогда. Обратная KL выбрала один способ (μ≈−3\mu \approx -3, σ≈1\sigma \approx 1) и повторила его почти точно. Цена — второй способ потерян: обратная KL жертвует разнообразием ради качества.

on-policy дистилляцияon-policy дистилляция (OPD)EN on-policy distillation (OPD)Ученик сам генерирует ответы, учитель оценивает каждый токен; минимизируется обратная KL(π_ученика ‖ π_учителя). Qwen3, DeepSeek-V4. соединяет обратную KL и идею RL «учиться на своих ответах»:

  1. Ученик генерирует ответ на промпт — как роллаут в GRPO.
  2. Учитель прогоняет этот ответ одним прямым проходом (без генерации — это дёшево) и выдаёт свои вероятности на каждой позиции.
  3. Loss — обратная KL на каждом токене: ∑t[log⁡πS(yt∣y<t)−log⁡πT(yt∣y<t)]\sum_t \big[\log \pi_S(y_t \mid y_{<t}) - \log \pi_T(y_t \mid y_{<t})\big] по сэмплированным токенам или точная KL по всему словарю на каждой позиции.
  4. Повторить.
flowchart LR
P["Промпт"] --> S["Ученик генерирует ответ"]
S --> T["Учитель: один прямой проход по ответу ученика"]
T --> L["Обратная KL на каждом токене"]
L -->|"градиент"| S
subgraph RL["Сравните с GRPO"]
  S2["Ученик генерирует G ответов"] --> R["Одна награда на ответ"]
end
On-policy дистилляция: та же петля, что в RL, но сигнал — на каждом токене

Почему это работает лучше и SFT, и RL:

  • Против SFT: ученик учится на своих префиксах — учитель поправляет его ровно там, где он реально ошибается. Обучение идёт на том же распределении префиксов, что и генерация, поэтому exposure bias снимается.
  • Против RL: вместо одного бита «верно/неверно» на тысячи токенов — оценка каждого токена. Сигнал на порядки плотнее. На языке RL это обычный градиент политики (урок 6.0.3), только у каждого токена своё преимущество log⁡πT−log⁡πS\log \pi_T - \log \pi_S (со stop-gradient): токен, который учитель считает вероятнее, чем ученик, поднимается. В отчёте DeepSeek-V4 прямо сказано, что так on-policy дистилляцию обычно встраивают в готовые RL-фреймворки, — и что такая потокенная оценка даёт большую дисперсию градиента, поэтому сами они считают KL по всему словарю (см. ниже).
  • Цена: нужны логиты учителя, то есть его веса (дистилляция «белого ящика»). По API, где видны только тексты, так не сделать. И у учителя с учеником должен быть общий токенизатор, иначе их вероятности относятся к разным токенам и несравнимы. Поэтому дистиллируют обычно внутри семейства: Qwen3-8B → Qwen3-0.6B.

Идея не нова. В 2023 году почти одновременно вышли две работы этой линии: MiniLLM группы CoAI Университета Цинхуа и Microsoft Research (обратная KL вместо прямой для генеративных LLM) и GKD из Google DeepMind (обучение на ответах, сгенерированных самим учеником; в TRL есть экспериментальный GKDTrainer). Но в массовую практику on-policy дистилляция вошла в 2025 году вместе с reasoning-моделями.

Для маленьких моделей семейства (0.6B, 1.7B, 4B, 8B, 14B и MoE 30B-A3B) Qwen не повторяет четырёхэтапный конвейер флагмана, а применяет дистилляция «от сильной к слабой»дистилляция «от сильной к слабой»EN strong-to-weak distillationСхема Qwen3 для маленьких моделей. Сначала обучение на ответах большой модели (off-policy), затем подгонка к её логитам на собственных генерациях ученика (on-policy). в две фазы:

  1. Off-policy: SFT на ответах учителей в обоих режимах — с рассуждением и без.
  2. On-policy: ученик сам генерирует ответы в режиме /think или /no_think, а его логиты подтягивают к логитам учителя (Qwen3-32B или Qwen3-235B-A22B), минимизируя KL.

Таблица 21 отчёта сравнивает два продолжения для Qwen3-8B после off-policy фазы на математике и коде:

МетодAIME’24 pass@1AIME’24 pass@64GPU-часыGPU-часыEN GPU hoursЧисло ускорителей × часы работы. 2048 GPU за сутки — 49 152 GPU-часа.
Off-policy дистилляция (старт)55.090.0—
+ RL67.690.017 920
+ on-policy дистилляция74.493.31 800

Дистилляция лучше по качеству и в 10 раз дешевле. Обратите внимание на pass@64: RL оставил его на месте (90.0 → 90.0) — знакомый эффект «затачивания» — а дистилляция подняла (93.3): учитель расширил то, что ученик умеет в принципе.

В отчёте DeepSeek-V4 сказано прямо: смешанный RL-этап, завершавший пост-трейнинг V3.2, полностью заменён on-policy дистилляцией. Схема:

  1. Для каждого домена (математика, код, агенты, следование инструкциям…) отдельно обучают специалиста: SFT, затем RL с GRPO.
  2. Одна общая модель-ученик учится у более чем десяти учителей сразу: минимизирует взвешенную сумму обратных KL к каждому из них на своих траекториях. Для математического промпта главным оказывается математический учитель, для кода — кодовый.
  3. KL считают по всему словарю на каждой позиции, а не по одному сэмплированному токену: так градиент стабильнее.

Похожую многоучительскую схему (MOPD) описывает Xiaomi для MiMo-V2-Flash. Тренд понятен: RL с наградами выращивает сильных узких специалистов, а дистилляция дёшево и стабильно собирает их в одну модель.

Рассуждение — это токены, а токены — это деньги и задержка. Спросить «столица Франции?» и получить 2000 токенов размышлений — плохой продукт. Отсюда гибридный режим мышлениягибридный режим мышленияEN hybrid thinking modeОдна модель умеет отвечать и с длинным рассуждением, и сразу. В Qwen3 режим переключается флагами /think и /no_think.: одна модель, два режима.

Qwen3 (апрель 2025) получил гибридный режим на третьем этапе пост-трейнинга — «слиянии режимов». Модель, уже обученную рассуждать (холодный старт + reasoning RL), дообучают на смеси данных двух типов: с рассуждением и без. Режим выбирают флагами в сообщении пользователя или системном промпте: /think или /no_think; в многоходовом диалоге модель следует последнему флагу. По умолчанию модель рассуждает.

Важная деталь шаблона: в режиме без рассуждения модель всё равно выдаёт блок <think> — пустой. Так формат ответа всегда одинаковый, а разработчик может «выключить мысли», просто подставив пустой блок в шаблон. В библиотеке transformers это параметр enable_thinking=False в tokenizer.apply_chat_template.

Честный эпилог. Уже в июле 2025 года Qwen выпустила обновление Qwen3-2507 с отдельными Instruct- и Thinking-моделями: команда решила, что раздельное обучение даёт лучшее качество в каждом режиме, чем два режима в одних весах. Другие лаборатории выбрали иначе: DeepSeek-V3.1 (август 2025) — гибридная, а DeepSeek-V4 предлагает три режима рассуждения — без мышления, «Think High» и «Think Max». Каждый режим в V4 выращен отдельным специалистом с собственным штрафом за длину и окном контекста на RL, а затем все они слиты дистилляцией; для максимального режима в начало системного промпта добавляется специальная инструкция «думать максимально тщательно».

У Qwen3 есть бюджет мышлениябюджет мышленияEN thinking budgetЛимит токенов на рассуждение; при его исчерпании рассуждение обрывают и просят модель ответить. Ручка «точность ↔ задержка».. Механизм прост: когда рассуждение доходит до заданного числа токенов, генерацию останавливают, дописывают короткую служебную фразу в духе «время вышло, отвечаю по уже сделанному» и закрывающий тег </think>. Дальше модель пишет ответ по тому, что успела надумать. Авторы отмечают, что этому специально не учили: умение отвечать по незаконченному рассуждению появилось само после слияния режимов. А качество на математике и коде плавно растёт с бюджетом — получается ручка «точность ↔ задержка» в руках пользователя.

Похожие ручки есть и у других: MiniMax-M1 выпущена в версиях с бюджетом 40K и 80K токенов, Kimi k1.5 переносит навыки длинных рассуждений в короткие (long2short, урок 6.4.3), а работа s1 (2025) показала обратный приём — продлевать рассуждение, дописывая «Wait», когда модель пытается закончить слишком рано.

Общий принцип за всем этим — вычисления во время инференсавычисления во время инференса (test-time compute)EN test-time computeБольше вычислений на ответ вместо большей модели: длинное рассуждение, много сэмплов с голосованием, поиск с проверщиком.: тратить больше вычислений не на обучение, а на каждый ответ. Три способа:

СпособКакПример
Последовательныйдлиннее рассуждение, перепроверкаR1, режимы Think High и Think Max, бюджет мышления
ПараллельныйNN ответов + голосованиесамосогласованность (голосование большинством)EN self-consistency (majority voting)Сгенерировать N ответов и выбрать самый частый итоговый. В отчётах — maj@k или cons@k. или выбор лучшего проверщикомcons@64 в отчётах R1 (голосование по 64 ответам, урок 6.4.2)
Поискдерево шагов с оценкой каждого шага (PRMпроцессная модель вознаграждения (PRM)EN process reward model (PRM)Оценивает каждый шаг рассуждения, а не только итоговый ответ. В DeepSeek-R1 описана как «неудачная попытка».)статьи 2024 года, в R1 не прижилось

Ключевой вопрос — когда это выгоднее большей модели. Snell и соавторы (UC Berkeley и Google DeepMind, 2024) сравнили при равных FLOPs маленькую модель с умно распределённым test-time compute и модель в 14 раз больше. На лёгких и средних задачах — и даже на части трудных, где маленькая модель хотя бы иногда права, — дополнительный инференс выигрывал. На самых трудных задачах выигрыша почти не было: там нужна более сильная модель. Отсюда и практичный вывод: бюджет мышления стоит подбирать под трудность задачи, а не ставить максимальным всегда.

Вторая сторона медали — «передумывание»«передумывание» (overthinking)EN overthinkingМодель тратит тысячи токенов там, где хватило бы сотни. Лечат штрафом за длину, long2short, бюджетом мышления.: тысячи токенов там, где хватило бы сотни. С ним борются штрафом за длину на RL (Kimi k1.5, DeepSeek-V4), long2short и тем же бюджетом.

Сравнение прямой и обратной KL вы уже запустили выше. Осталось увидеть, как бюджет мышления выглядит на уровне текста. Вместо настоящей модели — готовое «рассуждение» из токенов-слов.

STOP = "\n(Время на размышление вышло — отвечаю по уже сделанным выкладкам.)\n</think>\n\n"
def apply_thinking_budget(thinking_tokens, budget):
"""Если рассуждение длиннее бюджета — обрываем его и закрываем блок <think> служебной фразой."""
if len(thinking_tokens) <= budget:
return "<think>\n" + " ".join(thinking_tokens) + "\n</think>\n\n", False
return "<think>\n" + " ".join(thinking_tokens[:budget]) + STOP, True
thinking = "17 * 24 = 17 * 20 + 17 * 4 = 340 + 68 = 408 . Проверю : 24 * 17 = 240 + 168 = 408 .".split()
print("токенов в рассуждении:", len(thinking))
for budget in [40, 12]:
text, cut = apply_thinking_budget(thinking, budget)
print(f"--- бюджет {budget}, обрезано: {cut}")
print(text.rstrip())
токенов в рассуждении: 30
--- бюджет 40, обрезано: False
<think>
17 * 24 = 17 * 20 + 17 * 4 = 340 + 68 = 408 . Проверю : 24 * 17 = 240 + 168 = 408 .
</think>
--- бюджет 12, обрезано: True
<think>
17 * 24 = 17 * 20 + 17 * 4 =
(Время на размышление вышло — отвечаю по уже сделанным выкладкам.)
</think>

С настоящей моделью шаги те же: (1) сгенерировать рассуждение с max_new_tokens = budget; (2) если в выводе нет </think>, дописать служебную фразу и закрывающий тег; (3) продолжить генерацию уже ответа. Qwen3 использует для этого короткую английскую фразу из своего отчёта — найдите её в §4.3 и сравните поведение модели с ней и с нашей русской.

Проверь себя6 вопросов
1 Как технически получены модели DeepSeek-R1-Distill?
2 Во сколько раз on-policy дистилляция Qwen3-8B дешевле RL по GPU-часам (17 920 против 1 800)? Округлите до целого.
3 Какие утверждения об on-policy дистилляции верны?Можно выбрать несколько ответов
4 Ученик слишком мал, чтобы освоить оба способа решения, которые знает учитель. Что сделает SFT на ответах учителя (прямая KL)?
5 Как выглядит ответ Qwen3 в режиме /no_think?
6 Что показали Snell и соавторы (2024) про test-time compute?
  • R1-Distill — простой SFT на ~800K трассах R1; 1.5B-модель решает AIME лучше GPT-4o. Маленьким моделям выгоднее учиться у большой, чем выращивать рассуждения своим RL.
  • SFT на ответах учителя = прямая KL: покрывает моды и страдает от exposure bias. On-policy дистилляция — ученик генерирует, учитель оценивает каждый токен, минимизируется обратная KL.
  • Qwen3: on-policy дистилляция лучше RL (74.4 против 67.6 на AIME’24) и в 10 раз дешевле; DeepSeek-V4 заменила ею весь финальный смешанный этап RL.
  • Гибридное мышление — /think и /no_think с пустым блоком мыслей; бюджет мышления обрывает рассуждение и просит ответить; V4 предлагает три уровня усилия.
  • Test-time compute выгоден, когда модель хотя бы иногда права; бюджет стоит подбирать под трудность.

Дальше: практикум — GRPO своими руками: мини-R1-Zero на CPU и Qwen через TRL или verl на GPU.