<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="atom.xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://kobkrit.com/en/blog</id>
    <title>Kobkrit Viriyayudhakorn Blog</title>
    <updated>2026-07-20T21:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://kobkrit.com/en/blog"/>
    <subtitle>Kobkrit Viriyayudhakorn Blog</subtitle>
    <icon>https://kobkrit.com/en/img/favicon.ico</icon>
    <entry>
        <title type="html"><![CDATA[[LLM 1/10] Continue Pretraining: Teaching New Knowledge to a Thai LLM]]></title>
        <id>https://kobkrit.com/en/blog/llm-01-continue-pretraining</id>
        <link href="https://kobkrit.com/en/blog/llm-01-continue-pretraining"/>
        <updated>2026-07-20T21:00:00.000Z</updated>
        <summary type="html"><![CDATA[Continue Pretraining (CPT) from the equations down to code that actually runs on free Colab, with catastrophic forgetting measured in real numbers]]></summary>
        <content type="html"><![CDATA[<p>A large language model that handles Thai reasonably well will still, more often than not, know nothing
about your organization's specialized knowledge — not Thai government regulations, not the jargon of your
industry, not your internal documents. This article covers the most direct fix: <strong>Continue Pretraining (CPT)</strong>,
from the equations all the way to code that runs end to end on free Colab in about 15 minutes.</p>
<a class="badge_rUYD" href="https://colab.research.google.com/github/kobkrit/thai-llm-tutorials/blob/main/notebooks/01_continue_pretraining.ipynb" target="_blank" rel="noopener noreferrer" aria-label="Open the notebook 01_continue_pretraining.ipynb in Google Colab (opens in a new tab)"><svg class="mark_NB8U" viewBox="0 0 24 24" width="20" height="20" aria-hidden="true" focusable="false"><mask id="llmcourse-colab-cut"><rect x="0" y="0" width="24" height="24" fill="#fff"></rect><circle cx="16.2" cy="12" r="6.1" fill="#000"></circle></mask><circle cx="8.4" cy="12" r="4.6" fill="none" stroke="#F9AB00" stroke-width="3.1" mask="url(#llmcourse-colab-cut)"></circle><circle cx="16.2" cy="12" r="4.6" fill="none" stroke="#E8710A" stroke-width="3.1"></circle></svg><span class="text_QXpz">Open in Colab</span><code class="notebook_ntO0">01_continue_pretraining.ipynb</code></a>
<nav class="nav_RfLT" aria-label="Thai LLM tutorial series navigation"><p class="heading_XRWm">Thai LLM series<span class="progress_f8e8">Part 1 of 10</span></p><ol class="list_U31a"><li class="item_Y10l"><span class="chip_DDpP chipCurrent_BGpo" aria-current="step"><span class="number_u3BE" aria-hidden="true">1</span><span class="title_BPvL">Continue Pretraining</span><span class="srOnly_owtF">(you are here)</span></span></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-02-sft-lora"><span class="number_u3BE" aria-hidden="true">2</span><span class="title_BPvL">SFT and LoRA</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo"><span class="number_u3BE" aria-hidden="true">3</span><span class="title_BPvL">RLHF and PPO</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-04-dpo"><span class="number_u3BE" aria-hidden="true">4</span><span class="title_BPvL">DPO: Direct Preference Optimization</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-05-grpo"><span class="number_u3BE" aria-hidden="true">5</span><span class="title_BPvL">GRPO</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-06-context-distillation"><span class="number_u3BE" aria-hidden="true">6</span><span class="title_BPvL">Context Distillation</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-07-model-distillation"><span class="number_u3BE" aria-hidden="true">7</span><span class="title_BPvL">Model Distillation</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-08-guardrails"><span class="number_u3BE" aria-hidden="true">8</span><span class="title_BPvL">Guardrails</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-09-benchmarking"><span class="number_u3BE" aria-hidden="true">9</span><span class="title_BPvL">Benchmarking</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-10-deployment"><span class="number_u3BE" aria-hidden="true">10</span><span class="title_BPvL">Deployment</span></a></li></ol></nav>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-the-problem">1. The Problem<a href="https://kobkrit.com/en/blog/llm-01-continue-pretraining#1-the-problem" class="hash-link" aria-label="Direct link to 1. The Problem" title="Direct link to 1. The Problem" translate="no">​</a></h2>
<p>Picture asking Qwen3-0.6B something like <em>"Under the Office of the Prime Minister's regulations, when is
procurement by the specific-method route permitted?"</em> The model will answer confidently, and it will be <strong>wrong</strong>,
because it has never seen enough Thai government documents.</p>
<p>Plenty of people try to fix this with fine-tuning on a few thousand question-answer pairs, and find it doesn't work.
The reason is that <strong>SFT teaches the <em>shape</em> of an answer, not the <em>knowledge</em> behind it</strong>. If the knowledge isn't in
the weights, teaching the model to answer in the right tone of voice just makes it confident while it lies.</p>
<p>New knowledge reaches a model through three routes, and picking the wrong one is why most LLM projects fail:</p>
<table><thead><tr><th>Approach</th><th>Best for</th><th>Cost at inference time</th></tr></thead><tbody><tr><td><strong>RAG</strong></td><td>Knowledge that changes often and needs source citations</td><td>A retrieval on every call + a long prompt</td></tr><tr><td><strong>Continue Pretraining</strong></td><td>Large volumes of specialized knowledge that stay fairly stable</td><td>None (it's already in the weights)</td></tr><tr><td><strong>SFT</strong></td><td>Format, tone, answer structure</td><td>None</td></tr></tbody></table>
<p>This article is about the second route.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-what-were-going-to-do">2. What We're Going to Do<a href="https://kobkrit.com/en/blog/llm-01-continue-pretraining#2-what-were-going-to-do" class="hash-link" aria-label="Direct link to 2. What We're Going to Do" title="Direct link to 2. What We're Going to Do" translate="no">​</a></h2>
<p>We take a <strong>base</strong> model (one that hasn't been through instruction tuning) and keep training it with the
<strong>exact same objective used during pretraining</strong> — next-token prediction — on raw Thai text from the domain we care about.
No labels, no question-answer pairs, just plain text.</p>
<p>But the heart of this article isn't "train it and it gets better." It's what you give up in exchange:</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>The core idea of this chapter</div><div class="admonitionContent_BuS1"><p>CPT <strong>buys</strong> domain accuracy by <strong>paying</strong> with general capability you lose along the way.
It is a trade, not a free lunch, and the "exchange rate" is governed by a single number called the <strong>replay ratio</strong>.</p></div></div>
<p>The phenomenon of a model forgetting what it used to be able to do is called <strong>catastrophic forgetting</strong>.
We won't just gesture at it — we'll <strong>measure it as a number</strong> and then find a point we can live with.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-the-equations">3. The Equations<a href="https://kobkrit.com/en/blog/llm-01-continue-pretraining#3-the-equations" class="hash-link" aria-label="Direct link to 3. The Equations" title="Direct link to 3. The Equations" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="31-the-cpt-objective">3.1 The CPT objective<a href="https://kobkrit.com/en/blog/llm-01-continue-pretraining#31-the-cpt-objective" class="hash-link" aria-label="Direct link to 3.1 The CPT objective" title="Direct link to 3.1 The CPT objective" translate="no">​</a></h3>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi mathvariant="script">L</mi><mtext>CPT</mtext></msub><mo stretchy="false">(</mo><mi>θ</mi><mo stretchy="false">)</mo><mo>=</mo><mo>−</mo><msub><mi mathvariant="double-struck">E</mi><mrow><mi>x</mi><mo>∼</mo><msub><mi mathvariant="script">D</mi><mtext>domain</mtext></msub></mrow></msub><mrow><mo fence="true">[</mo><munderover><mo>∑</mo><mrow><mi>t</mi><mo>=</mo><mn>1</mn></mrow><mrow><mi mathvariant="normal">∣</mi><mi>x</mi><mi mathvariant="normal">∣</mi></mrow></munderover><mi>log</mi><mo>⁡</mo><msub><mi>p</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>x</mi><mi>t</mi></msub><mo>∣</mo><msub><mi>x</mi><mrow><mo>&lt;</mo><mi>t</mi></mrow></msub><mo stretchy="false">)</mo><mo fence="true">]</mo></mrow></mrow><annotation encoding="application/x-tex">\mathcal{L}_{\text{CPT}}(\theta) = -\mathbb{E}_{x\sim\mathcal{D}_{\text{domain}}}\left[\sum_{t=1}^{|x|}\log p_\theta(x_t \mid x_{&lt;t})\right]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathcal">L</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">CPT</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.0278em">θ</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:3.6em;vertical-align:-1.55em"></span><span class="mord">−</span><span class="mord"><span class="mord mathbb">E</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">x</span><span class="mrel mtight">∼</span><span class="mord mtight"><span class="mord mathcal mtight" style="margin-right:0.0278em">D</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em"><span style="top:-2.3488em;margin-left:-0.0278em;margin-right:0.0714em"><span class="pstrut" style="height:2.5em"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">domain</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1512em"><span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2559em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="minner"><span class="mopen"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.05em"><span style="top:-4.05em"><span class="pstrut" style="height:5.6em"></span><span style="width:0.667em;height:3.6em"><svg xmlns="http://www.w3.org/2000/svg" width="0.667em" height="3.6em" viewBox="0 0 667 3600"><path d="M403 1759 V84 H666 V0 H319 V1759 v0 v1759 v84 h347 v-84
H403z M403 1759 V0 H319 V1759 v0 v1759 v84 h84z"></path></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.55em"><span></span></span></span></span></span></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.961em"><span style="top:-1.8829em;margin-left:0em"><span class="pstrut" style="height:3.05em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">t</span><span class="mrel mtight">=</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.05em"><span class="pstrut" style="height:3.05em"></span><span><span class="mop op-symbol large-op">∑</span></span></span><span style="top:-4.386em;margin-left:0em"><span class="pstrut" style="height:3.05em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">∣</span><span class="mord mathnormal mtight">x</span><span class="mord mtight">∣</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.2671em"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mop">lo<span style="margin-right:0.0139em">g</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">∣</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mrel mtight">&lt;</span><span class="mord mathnormal mtight">t</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1774em"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mclose"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.05em"><span style="top:-4.05em"><span class="pstrut" style="height:5.6em"></span><span style="width:0.667em;height:3.6em"><svg xmlns="http://www.w3.org/2000/svg" width="0.667em" height="3.6em" viewBox="0 0 667 3600"><path d="M347 1759 V0 H0 V84 H263 V1759 v0 v1759 H0 v84 H347z
M347 1759 V0 H263 V1759 v0 v1759 h84z"></path></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.55em"><span></span></span></span></span></span></span></span></span></span></span></span>
<ul>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>x</mi><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">x_t</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> = the token at position <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi></mrow><annotation encoding="application/x-tex">t</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6151em"></span><span class="mord mathnormal">t</span></span></span></span></li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>x</mi><mrow><mo>&lt;</mo><mi>t</mi></mrow></msub></mrow><annotation encoding="application/x-tex">x_{&lt;t}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6079em;vertical-align:-0.1774em"></span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mrel mtight">&lt;</span><span class="mord mathnormal mtight">t</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1774em"><span></span></span></span></span></span></span></span></span></span> = every token before it</li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>p</mi><mi>θ</mi></msub></mrow><annotation encoding="application/x-tex">p_\theta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> = the probability the model predicts</li>
</ul>
<p>This equation is <strong>identical to the one used during pretraining</strong>. The only thing that changes is the data.
That's why CPT needs no labels — the text is its own answer key.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="32-perplexity-our-unit-of-measurement">3.2 Perplexity: our unit of measurement<a href="https://kobkrit.com/en/blog/llm-01-continue-pretraining#32-perplexity-our-unit-of-measurement" class="hash-link" aria-label="Direct link to 3.2 Perplexity: our unit of measurement" title="Direct link to 3.2 Perplexity: our unit of measurement" translate="no">​</a></h3>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mtext>PPL</mtext><mo stretchy="false">(</mo><mi mathvariant="script">D</mi><mo stretchy="false">)</mo><mo>=</mo><mi>exp</mi><mo>⁡</mo><mtext> ⁣</mtext><mrow><mo fence="true">(</mo><mfrac><mn>1</mn><mi>N</mi></mfrac><munder><mo>∑</mo><mi>i</mi></munder><msub><mi mathvariant="script">L</mi><mtext>CPT</mtext></msub><mo stretchy="false">(</mo><msup><mi>x</mi><mrow><mo stretchy="false">(</mo><mi>i</mi><mo stretchy="false">)</mo></mrow></msup><mo stretchy="false">)</mo><mo fence="true">)</mo></mrow></mrow><annotation encoding="application/x-tex">\text{PPL}(\mathcal{D}) = \exp\!\left(\frac{1}{N}\sum_{i}\mathcal{L}_{\text{CPT}}(x^{(i)})\right)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord text"><span class="mord">PPL</span></span><span class="mopen">(</span><span class="mord mathcal" style="margin-right:0.0278em">D</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:3.0277em;vertical-align:-1.2777em"></span><span class="mop">exp</span><span class="mspace" style="margin-right:-0.1667em"></span><span class="mspace" style="margin-right:0.1667em"></span><span class="minner"><span class="mopen delimcenter" style="top:0em"><span class="delimsizing size4">(</span></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3214em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.109em">N</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.05em"><span style="top:-1.8723em;margin-left:0em"><span class="pstrut" style="height:3.05em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span style="top:-3.05em"><span class="pstrut" style="height:3.05em"></span><span><span class="mop op-symbol large-op">∑</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.2777em"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathcal">L</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">CPT</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.938em"><span style="top:-3.113em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mopen mtight">(</span><span class="mord mathnormal mtight">i</span><span class="mclose mtight">)</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mclose delimcenter" style="top:0em"><span class="delimsizing size4">)</span></span></span></span></span></span></span>
<p>In plain language: <strong>"on average, how many options is the model torn between?"</strong>
PPL = 20 means it's wavering among roughly 20 candidates; PPL = 5 means it's far more certain. Lower is better.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="33-the-most-important-equation-in-this-chapter--replay-mixing">3.3 The most important equation in this chapter — replay mixing<a href="https://kobkrit.com/en/blog/llm-01-continue-pretraining#33-the-most-important-equation-in-this-chapter--replay-mixing" class="hash-link" aria-label="Direct link to 3.3 The most important equation in this chapter — replay mixing" title="Direct link to 3.3 The most important equation in this chapter — replay mixing" translate="no">​</a></h3>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi mathvariant="script">D</mi><mtext>mix</mtext></msub><mo>=</mo><mi>λ</mi><mtext> </mtext><msub><mi mathvariant="script">D</mi><mtext>domain</mtext></msub><mo>+</mo><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><mi>λ</mi><mo stretchy="false">)</mo><mtext> </mtext><msub><mi mathvariant="script">D</mi><mtext>general</mtext></msub></mrow><annotation encoding="application/x-tex">\mathcal{D}_{\text{mix}} = \lambda\,\mathcal{D}_{\text{domain}} + (1-\lambda)\,\mathcal{D}_{\text{general}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathcal" style="margin-right:0.0278em">D</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3175em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">mix</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em"></span><span class="mord mathnormal">λ</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathcal" style="margin-right:0.0278em">D</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">domain</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mopen">(</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.0361em;vertical-align:-0.2861em"></span><span class="mord mathnormal">λ</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathcal" style="margin-right:0.0278em">D</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">general</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span></span></span></span></span>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>λ</mi></mrow><annotation encoding="application/x-tex">\lambda</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal">λ</span></span></span></span> is the share of domain data in each batch.</p>
<ul>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>λ</mi><mo>=</mo><mn>1.0</mn></mrow><annotation encoding="application/x-tex">\lambda = 1.0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal">λ</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">1.0</span></span></span></span> → pure domain data → fastest domain gains <strong>and the fastest forgetting</strong></li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>λ</mi><mo>=</mo><mn>0.5</mn></mrow><annotation encoding="application/x-tex">\lambda = 0.5</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal">λ</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0.5</span></span></span></span> → an even mix → slower, but far less forgetting</li>
</ul>
<p>Don't hardcode this value. <strong>Sweep it</strong>, then pick the point you can accept.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-seeing-the-equations">4. Seeing the Equations<a href="https://kobkrit.com/en/blog/llm-01-continue-pretraining#4-seeing-the-equations" class="hash-link" aria-label="Direct link to 4. Seeing the Equations" title="Direct link to 4. Seeing the Equations" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-perplexity-actually-tells-us">What perplexity actually tells us<a href="https://kobkrit.com/en/blog/llm-01-continue-pretraining#what-perplexity-actually-tells-us" class="hash-link" aria-label="Direct link to What perplexity actually tells us" title="Direct link to What perplexity actually tells us" translate="no">​</a></h3>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-01-continue-pretraining/perplexity-meaning.light.svg" alt="The relationship between cross-entropy loss and perplexity, and what a 5-point PPL reduction means from different starting points" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-01-continue-pretraining/perplexity-meaning.dark.svg" alt="The relationship between cross-entropy loss and perplexity, and what a 5-point PPL reduction means from different starting points" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 1.1</span>PPL is the exp of the loss — and a drop of 5 points means very different things depending on where you started</p><div class="captionFooter_w00v"></div></figcaption></figure>
<p>The right-hand plot shows what people usually get wrong: if someone tells you they "cut perplexity by 5 points"
without saying where they started, the sentence is nearly meaningless. 80 → 75 is a 6% improvement; 10 → 5 is 50%.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-trade-off-the-replay-ratio-controls">The trade-off the replay ratio controls<a href="https://kobkrit.com/en/blog/llm-01-continue-pretraining#the-trade-off-the-replay-ratio-controls" class="hash-link" aria-label="Direct link to The trade-off the replay ratio controls" title="Direct link to The trade-off the replay ratio controls" translate="no">​</a></h3>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-01-continue-pretraining/replay-ratio-tradeoff.light.svg" alt="A plot showing that as lambda increases, domain perplexity falls while general perplexity rises, with the Pareto frontier marked" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-01-continue-pretraining/replay-ratio-tradeoff.dark.svg" alt="A plot showing that as lambda increases, domain perplexity falls while general perplexity rises, with the Pareto frontier marked" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 1.2</span>The shape of the trade-off that falls out of the replay mixing equation (an illustration of the mechanism, not measured results — the real numbers are in section 8)</p><div class="captionFooter_w00v"></div></figcaption></figure>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-setting-up-the-environment">5. Setting Up the Environment<a href="https://kobkrit.com/en/blog/llm-01-continue-pretraining#5-setting-up-the-environment" class="hash-link" aria-label="Direct link to 5. Setting Up the Environment" title="Direct link to 5. Setting Up the Environment" translate="no">​</a></h2>
<p>Open Colab and pick <strong>Runtime → Change runtime type → T4 GPU</strong> (the free tier is enough).</p>
<div class="theme-admonition theme-admonition-danger admonition_xJq3 alert alert--danger"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"></path></svg></span>The gotcha that breaks most LLM notebooks on free Colab</div><div class="admonitionContent_BuS1"><p>The Colab T4 is Turing architecture (SM 7.5), which <strong>does not support bfloat16</strong> and <strong>does not support FlashAttention-2</strong>.</p><p>But Qwen3-0.6B's <code>config.json</code> declares <code>torch_dtype: bfloat16</code>.
So if you write <code>torch_dtype="auto"</code> the way most tutorials do, <strong>your code will either crash or run bizarrely slowly</strong>.</p><p>Throughout this series we always state the dtype explicitly:</p><div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">torch_dtype</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">float16      </span><span class="token comment" style="color:#999988;font-style:italic"># not bfloat16</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">attn_implementation</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"sdpa"</span><span class="token plain">     </span><span class="token comment" style="color:#999988;font-style:italic"># not flash_attention_2</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">fp16</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token plain">                      </span><span class="token comment" style="color:#999988;font-style:italic"># in TrainingArguments (not bf16=True)</span><br></span></code></pre></div></div></div></div>
<p>The first cell of every notebook in this series prints this line so you can see it with your own eyes:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">cap </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_device_capability</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"compute capability:"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cap</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                    </span><span class="token comment" style="color:#999988;font-style:italic"># T4 = (7, 5)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"native bf16:"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cap</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&gt;=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                   </span><span class="token comment" style="color:#999988;font-style:italic"># T4 -&gt; False</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"torch says   :"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">is_bf16_supported</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># T4 -&gt; True (counts emulation!)</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span><code>is_bf16_supported()</code> lies on a T4</div><div class="admonitionContent_BuS1"><p>Recent torch returns <code>True</code> on a T4 because it counts <strong>emulation</strong> as support — which is far slower than fp16.
Gate on <strong>compute capability ≥ 8.0</strong> (Ampere and up) instead. This was a real bug, caught only by running the notebook on Colab.</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-much-vram-you-have-and-where-it-goes">How much VRAM you have, and where it goes<a href="https://kobkrit.com/en/blog/llm-01-continue-pretraining#how-much-vram-you-have-and-where-it-goes" class="hash-link" aria-label="Direct link to How much VRAM you have, and where it goes" title="Direct link to How much VRAM you have, and where it goes" translate="no">​</a></h3>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-01-continue-pretraining/optimizer-memory.light.svg" alt="A bar chart breaking VRAM usage into weights, gradients, fp32 master, Adam states and activations, comparing adamw fp32 against 8-bit" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-01-continue-pretraining/optimizer-memory.dark.svg" alt="A bar chart breaking VRAM usage into weights, gradients, fp32 master, Adam states and activations, comparing adamw fp32 against 8-bit" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 1.3</span>Where VRAM goes during full-model training, computed from the real values in Qwen3-0.6B's config (596M parameters)</p><div class="captionFooter_w00v"></div></figcaption></figure>
<p>Notice that <strong>the optimizer state takes up more room than the model itself</strong> — Adam keeps one copy each of <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>m</mi></mrow><annotation encoding="application/x-tex">m</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">m</span></span></span></span> and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>v</mi></mrow><annotation encoding="application/x-tex">v</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.0359em">v</span></span></span></span>
for every parameter. Switching to <code>adamw_bnb_8bit</code> saves 3.3 GB, which means a lot more headroom for batch size or sequence length.</p>
<p>Play with the VRAM budget yourself — change the numbers and see where it OOMs:</p>
<div class="root_EEmQ"><div class="controls_hr8V"><div class="control_Br1p"><label class="controlLabel_J5tp" for="llmcourse-mbc-model"><span>Model</span></label><select id="llmcourse-mbc-model" class="select_AyHE"><option value="Qwen3-0.6B" selected="">Qwen3-0.6B</option><option value="Qwen3-1.7B">Qwen3-1.7B</option><option value="Qwen3-4B">Qwen3-4B</option><option value="Qwen3-8B">Qwen3-8B</option></select><span class="controlHint_ilRY">596.0M parameters, derived from config.json</span></div><div class="control_Br1p"><label class="controlLabel_J5tp" for="llmcourse-mbc-params"><span>Parameters (millions)</span></label><input id="llmcourse-mbc-params" class="numberInput_P4fE" type="number" min="1" max="1000000" step="1" value="596"></div><fieldset class="control_Br1p" style="border:0;padding:0;margin:0"><legend class="segmentedLegend_oU13">Weight dtype</legend><div class="segmented_Klsm"><span class="segment_AC25"><input type="radio" id="_R_pauldeh_-fp32" name="llmcourse-mbc-dtype-_R_pauldeh_" value="fp32"><label class="segmentLabel_wkEZ" for="_R_pauldeh_-fp32">fp32 (4B)</label></span><span class="segment_AC25"><input type="radio" id="_R_pauldeh_-fp16" name="llmcourse-mbc-dtype-_R_pauldeh_" checked="" value="fp16"><label class="segmentLabel_wkEZ" for="_R_pauldeh_-fp16">fp16 (2B)</label></span><span class="segment_AC25"><input type="radio" id="_R_pauldeh_-int8" name="llmcourse-mbc-dtype-_R_pauldeh_" value="int8"><label class="segmentLabel_wkEZ" for="_R_pauldeh_-int8">int8 (1B)</label></span><span class="segment_AC25"><input type="radio" id="_R_pauldeh_-nf4" name="llmcourse-mbc-dtype-_R_pauldeh_" value="nf4"><label class="segmentLabel_wkEZ" for="_R_pauldeh_-nf4">nf4 (0.5B)</label></span></div></fieldset><fieldset class="control_Br1p" style="border:0;padding:0;margin:0"><legend class="segmentedLegend_oU13">Run mode</legend><div class="segmented_Klsm"><span class="segment_AC25"><input type="radio" id="_R_11auldeh_-train" name="llmcourse-mbc-mode-_R_11auldeh_" checked="" value="train"><label class="segmentLabel_wkEZ" for="_R_11auldeh_-train">Training</label></span><span class="segment_AC25"><input type="radio" id="_R_11auldeh_-inference" name="llmcourse-mbc-mode-_R_11auldeh_" value="inference"><label class="segmentLabel_wkEZ" for="_R_11auldeh_-inference">Serving</label></span></div></fieldset><div class="control_Br1p"><label class="controlLabel_J5tp" for="_R_19auldeh_"><span>LoRA rank</span><span class="controlValue_cYgn">r = 16</span></label><input id="_R_19auldeh_" class="range_qGHz" type="range" min="0" max="7" step="1" aria-label="LoRA rank" aria-valuetext="r = 16" value="3"></div><div class="control_Br1p"><label class="controlLabel_J5tp" for="_R_1hauldeh_"><span>Batch size</span><span class="controlValue_cYgn">1</span></label><input id="_R_1hauldeh_" class="range_qGHz" type="range" min="0" max="6" step="1" aria-label="Batch size" aria-valuetext="1" value="0"></div><div class="control_Br1p"><label class="controlLabel_J5tp" for="_R_1pauldeh_"><span>Sequence length</span><span class="controlValue_cYgn">1024 tok</span></label><input id="_R_1pauldeh_" class="range_qGHz" type="range" min="0" max="7" step="1" aria-label="Sequence length in tokens" aria-valuetext="1024 tokens" value="2"></div><div class="control_Br1p"><label class="controlLabel_J5tp" for="_R_21auldeh_"><span>Concurrent requests</span><span class="controlValue_cYgn">1</span></label><input id="_R_21auldeh_" class="range_qGHz" type="range" min="0" max="8" step="1" disabled="" aria-label="Concurrent requests held in the KV cache" aria-valuetext="1" value="0"></div><div class="control_Br1p"><label class="checkboxRow_XXA4" for="llmcourse-mbc-ckpt"><input id="llmcourse-mbc-ckpt" type="checkbox" checked=""><span>Gradient checkpointing</span></label><span class="controlHint_ilRY">Trades about 30% more compute for a large drop in activation memory.</span></div></div><div class="svgWrap_mSxx"><svg class="svg_pLEH chart_YWLW" viewBox="0 0 720 118" role="img" aria-label="Stacked VRAM usage totalling 1.43 GiB against a 16 GiB ceiling. Verdict: fits."><rect x="0" y="26" width="720" height="44" rx="6" class="barTrack_ylwk"></rect><rect x="0" y="26" width="47.0428466796875" height="44" class="barSegment_eSn9 seriesWeights_xyK5"><title>weights: 1.13 GiB</title></rect><rect x="47.0428466796875" y="26" width="1" height="44" class="barSegment_eSn9 seriesGradients_Yy9k"><title>gradients: 19.25 MiB</title></rect><rect x="47.826131184895836" y="26" width="4.69970703125" height="44" class="barSegment_eSn9 seriesOptimizer_Sr99"><title>optimizer: 115.50 MiB</title></rect><rect x="52.52583821614583" y="26" width="6.917317708333332" height="44" class="barSegment_eSn9 seriesActivations_mq5k"><title>activations: 170.00 MiB</title></rect><line x1="666.6666666666666" y1="14" x2="666.6666666666666" y2="82" class="ceilingLine_Gd0g"></line><text x="666.6666666666666" y="10" text-anchor="end" class="ceilingLabel_huNs">16 GB — Colab T4</text><g><line x1="0" y1="70" x2="0" y2="75" class="tick_YNak"></line><text x="0" y="88" text-anchor="middle" class="tickLabel_B3jM">0</text></g><g><line x1="166.66666666666666" y1="70" x2="166.66666666666666" y2="75" class="tick_YNak"></line><text x="166.66666666666666" y="88" text-anchor="middle" class="tickLabel_B3jM">4</text></g><g><line x1="333.3333333333333" y1="70" x2="333.3333333333333" y2="75" class="tick_YNak"></line><text x="333.3333333333333" y="88" text-anchor="middle" class="tickLabel_B3jM">8</text></g><g><line x1="500" y1="70" x2="500" y2="75" class="tick_YNak"></line><text x="500" y="88" text-anchor="middle" class="tickLabel_B3jM">12</text></g><g><line x1="666.6666666666666" y1="70" x2="666.6666666666666" y2="75" class="tick_YNak"></line><text x="666.6666666666666" y="88" text-anchor="middle" class="tickLabel_B3jM">16</text></g><text x="720" y="116" text-anchor="end" class="axisLabel_Yazw">GiB</text></svg></div><ul class="legend_BTbY"><li class="legendItem_ApeG"><span class="swatch_vsP4 seriesWeights_xyK5" aria-hidden="true"></span><span class="legendLabel_rxKN">Weights</span><span class="legendValue_wTen">1.13 GiB</span></li><li class="legendItem_ApeG"><span class="swatch_vsP4 seriesGradients_Yy9k" aria-hidden="true"></span><span class="legendLabel_rxKN">Gradients</span><span class="legendValue_wTen">19.25 MiB</span></li><li class="legendItem_ApeG"><span class="swatch_vsP4 seriesOptimizer_Sr99" aria-hidden="true"></span><span class="legendLabel_rxKN">Optimizer state</span><span class="legendValue_wTen">115.50 MiB</span></li><li class="legendItem_ApeG"><span class="swatch_vsP4 seriesActivations_mq5k" aria-hidden="true"></span><span class="legendLabel_rxKN">Activations</span><span class="legendValue_wTen">170.00 MiB</span></li><li class="legendItem_ApeG"><span class="swatch_vsP4 seriesKv_dhmF" aria-hidden="true"></span><span class="legendLabel_rxKN">KV cache</span><span class="legendValue_wTen">—</span></li></ul><div class="readouts__tjv"><div class="readout_D9ns"><span class="readoutLabel_EsIV">Total VRAM</span><span class="readoutValue_VS6z">1.43 GiB</span><span class="readoutSub_DoT9">14.57 GiB to spare</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">Trainable params</span><span class="readoutValue_VS6z">10.1M</span><span class="readoutSub_DoT9">1.69%</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">KV cache per token</span><span class="readoutValue_VS6z">112 KiB</span><span class="readoutSub_DoT9">2 x 28 x 8 x 128</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">Full context KV</span><span class="readoutValue_VS6z">4.38 GiB</span><span class="readoutSub_DoT9">41.0K tok</span></div></div><p class="callout_aEDz calloutSuccess_oTZ4" role="status"><strong class="calloutTitle_nx3s">It fits.</strong>This run needs 1.43 GiB and leaves 14.57 GiB of headroom on a free Colab T4.</p></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-preparing-the-data">6. Preparing the Data<a href="https://kobkrit.com/en/blog/llm-01-continue-pretraining#6-preparing-the-data" class="hash-link" aria-label="Direct link to 6. Preparing the Data" title="Direct link to 6. Preparing the Data" translate="no">​</a></h2>
<p>We use <strong><code>pythainlp/thaigov-v2-corpus-22032023</code></strong> — a corpus of Thai government news and official documents
(public domain). It stands in for "specialized knowledge the model has never seen enough of."</p>
<p>We also use a second set of general Thai text as <strong>replay data</strong> to hold back the forgetting.</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> datasets </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> load_dataset</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">domain </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> load_dataset</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"pythainlp/thaigov-v2-corpus-22032023"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> split</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"train"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">domain </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> domain</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">shuffle</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">seed</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">42</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">select</span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin">range</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">8000</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="packing-dont-let-padding-eat-your-budget">Packing: don't let padding eat your budget<a href="https://kobkrit.com/en/blog/llm-01-continue-pretraining#packing-dont-let-padding-eat-your-budget" class="hash-link" aria-label="Direct link to Packing: don't let padding eat your budget" title="Direct link to Packing: don't let padding eat your budget" translate="no">​</a></h3>
<p>If you pad every document out to the same length, you burn an enormous amount of compute on <code>&lt;pad&gt;</code>.
The right approach is to <strong>concatenate every document and slice the stream into equal 512-token blocks</strong>.</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">pack</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">examples</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> block_size</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">512</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ids </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> text </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> examples</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"context"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        ids</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">extend</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">tokenizer</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">text </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> tokenizer</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">eos_token</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">input_ids</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    n </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin">len</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ids</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">//</span><span class="token plain"> block_size</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> block_size</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"input_ids"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">ids</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">i</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">i</span><span class="token operator" style="color:#393A34">+</span><span class="token plain">block_size</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> i </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token builtin">range</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> n</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> block_size</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>Why Thai costs more than English</div><div class="admonitionContent_BuS1"><p>Most models' tokenizers were trained predominantly on English data.
Thai text therefore gets chopped into tokens far more finely — the same sentence can cost 2–3× more tokens.
That means <strong>higher API bills, a context window that fills up sooner, and slower training</strong>. The notebook measures this number for you.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-the-main-code">7. The Main Code<a href="https://kobkrit.com/en/blog/llm-01-continue-pretraining#7-the-main-code" class="hash-link" aria-label="Direct link to 7. The Main Code" title="Direct link to 7. The Main Code" translate="no">​</a></h2>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> torch</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> transformers </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> AutoModelForCausalLM</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> AutoTokenizer</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> TrainingArguments</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> Trainer</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">model </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> AutoModelForCausalLM</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">from_pretrained</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"Qwen/Qwen3-0.6B-Base"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">           </span><span class="token comment" style="color:#999988;font-style:italic"># base, not instruct — CPT must start from a base model</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    torch_dtype</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">float16</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># the T4 has no bf16</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    attn_implementation</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"sdpa"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">       </span><span class="token comment" style="color:#999988;font-style:italic"># the T4 has no FlashAttention-2</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">model </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> model</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                 </span><span class="token comment" style="color:#999988;font-style:italic"># must cast to fp32 before training — see the box below</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">args </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> TrainingArguments</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    output_dir</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"cpt-out"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    per_device_train_batch_size</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    gradient_accumulation_steps</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">8</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># effective batch = 16</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    num_train_epochs</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    learning_rate</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">2e-5</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">               </span><span class="token comment" style="color:#999988;font-style:italic"># 10x lower than SFT — see the warning below</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    lr_scheduler_type</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"cosine"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    warmup_steps</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">50</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    optim</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"adamw_bnb_8bit"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">           </span><span class="token comment" style="color:#999988;font-style:italic"># saves 3.3 GB</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    gradient_checkpointing</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    max_grad_norm</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1.0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">                </span><span class="token comment" style="color:#999988;font-style:italic"># keeps fp16 from blowing up</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    fp16</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">                        </span><span class="token comment" style="color:#999988;font-style:italic"># not bf16</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    logging_steps</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-danger admonition_xJq3 alert alert--danger"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"></path></svg></span>The fp16 trap that kills a full fine-tune immediately</div><div class="admonitionContent_BuS1"><p><code>fp16=True</code> <strong>does not mean the weights are fp16</strong>. It means <em>mixed precision</em>: matrix
multiplies happen in fp16, but the <strong>master weights must stay fp32</strong>, because the optimizer
adds very small quantities (lr = 2e-5) that fp16 cannot represent.</p><p>Load the model in fp16 and train all of it with <code>fp16=True</code> directly, and you get:</p><div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ValueError: Attempting to unscale FP16 gradients.</span><br></span></code></pre></div></div><p><code>max_grad_norm=1.0</code> forces gradients to be unscaled before clipping, and those gradients are
fp16. The fix is <code>model.float()</code> before training, casting back to fp16 for evaluation.
(For LoRA in parts 2 and 4, you cast only the adapter parameters instead.)</p></div></div>
<div class="theme-admonition theme-admonition-danger admonition_xJq3 alert alert--danger"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"></path></svg></span>The learning rate is where this goes wrong most often</div><div class="admonitionContent_BuS1"><p>If you take <code>learning_rate=2e-4</code> (the value people typically use with LoRA) and apply it to full-model CPT,
<strong>you will erase the model's capabilities within a few hundred steps</strong>.
CPT wants an LR roughly 10–50× lower than SFT, because we're moving <em>every single</em> weight.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="8-results">8. Results<a href="https://kobkrit.com/en/blog/llm-01-continue-pretraining#8-results" class="hash-link" aria-label="Direct link to 8. Results" title="Direct link to 8. Results" translate="no">​</a></h2>
<p>The notebook measures three things before and after training and writes them to <code>results.json</code>:</p>
<ol>
<li class=""><strong>Domain held-out PPL</strong> — should drop clearly (this is what we're paying for)</li>
<li class=""><strong>General held-out PPL</strong> — should rise somewhat (this is the price)</li>
<li class=""><strong>TH-KNOW accuracy</strong> from the KobEval-TH benchmark, with a <strong>Wilson 95% CI</strong></li>
</ol>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>Why there must always be a confidence interval</div><div class="admonitionContent_BuS1"><p>On a 100-question test set, the 95% confidence interval spans roughly ±10 points.
Which means "78% versus 74%" is usually <strong>indistinguishable from chance</strong>.
An accuracy number without a CI isn't an experimental result, it's a rumor — we'll dig into this in chapter 9.</p></div></div>
<div class="root_IS5b"><div class="picker_cO8e"><span class="pickerLabel_sE2x" id="llmcourse-bac-picker">Prompt</span><div class="pickerButtons_j7L1" role="tablist" aria-labelledby="llmcourse-bac-picker"><button type="button" role="tab" id="llmcourse-bac-tab-0" aria-selected="true" aria-controls="llmcourse-bac-panel-0" tabindex="0" class="pickerButton_gFO3 pickerButtonActive_xIUp">1</button><button type="button" role="tab" id="llmcourse-bac-tab-1" aria-selected="false" aria-controls="llmcourse-bac-panel-1" tabindex="-1" class="pickerButton_gFO3">2</button></div></div><blockquote class="prompt_O4Wp" lang="th"><span class="promptLabel_h2F6">Prompt</span>อธิบายว่าทำไมท้องฟ้าถึงเป็นสีฟ้า แบบสั้น ๆ</blockquote><div class="grid_h_9T" id="llmcourse-bac-panel-0" role="tabpanel" aria-labelledby="llmcourse-bac-tab-0" style="grid-template-columns:repeat(auto-fit, minmax(min(100%, 260px), 1fr))"><article class="card_S27b"><header class="cardHeader_w7wJ"><h4 class="cardTitle_NUQN">base</h4><div class="badges_pXcS"><span class="badge_wUaQ badgeBad_WFwi" title="Share of non-whitespace characters that are Thai script">Thai 18%</span><span class="badge_wUaQ">41 tokens</span></div></header><div class="output_VSGg" lang="th">The sky appears blue because of Rayleigh scattering. ท้องฟ้า is blue เพราะ light scatter ครับ. Shorter wavelengths scatter more than longer ones.</div></article><article class="card_S27b"><header class="cardHeader_w7wJ"><h4 class="cardTitle_NUQN">sft</h4><div class="badges_pXcS"><span class="badge_wUaQ badgeGood_MHH_" title="Share of non-whitespace characters that are Thai script">Thai 99%</span><span class="badge_wUaQ">78 tokens</span></div></header><div class="output_VSGg" lang="th">ท้องฟ้าเป็นสีฟ้าเพราะแสงอาทิตย์กระทบกับโมเลกุลของอากาศแล้วเกิดการกระเจิงแบบเรย์ลี ซึ่งแสงสีน้ำเงินที่มีความยาวคลื่นสั้นกว่าจะกระเจิงได้มากกว่าแสงสีแดง เราจึงมองเห็นท้องฟ้าเป็นสีฟ้าครับ</div></article></div><p class="status_mfC7">Showing the built-in sample.</p></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="9-comparison">9. Comparison<a href="https://kobkrit.com/en/blog/llm-01-continue-pretraining#9-comparison" class="hash-link" aria-label="Direct link to 9. Comparison" title="Direct link to 9. Comparison" translate="no">​</a></h2>
<p>The notebook trains three variants on the same data so you can see the trade-off in numbers:</p>
<table><thead><tr><th>Model</th><th>Domain PPL ↓</th><th>General PPL ↓</th><th>TH-DOMAIN</th><th>Train time</th></tr></thead><tbody><tr><td>Base (untrained)</td><td>4.83</td><td>5.88</td><td>27.3%</td><td>—</td></tr><tr><td>CPT, λ = 1.0 (domain only)</td><td><strong>4.07</strong> (−0.76)</td><td>6.72 (<strong>+0.85</strong>)</td><td>—</td><td>8.0 min</td></tr><tr><td>CPT, λ = 0.5 (with replay)</td><td>4.29 (−0.53)</td><td><strong>4.98</strong> (−0.90)</td><td><strong>36.4%</strong></td><td>8.0 min</td></tr></tbody></table>
<small>Measured on a Colab T4 (sm_75, 14.56 GB) — peak VRAM 10.50 GB, Qwen3-0.6B-Base,
100 optimizer steps per run. Every number comes from the <code>results.json</code> the notebook writes.</small>
<p>Reading this table correctly is the point of the chapter:</p>
<ul>
<li class=""><strong>λ = 1.0 wins on domain PPL (4.07, the lowest) but general PPL gets worse</strong>, 5.88 → 6.72.
That is <strong>catastrophic forgetting, measured</strong> rather than asserted.</li>
<li class=""><strong>λ = 0.5 gives up a little domain accuracy (4.29) and general perplexity <em>improves</em></strong> to 4.98.
Replay does not merely prevent forgetting here; it leaves the model better at Thai overall.</li>
</ul>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>TH-DOMAIN went up, but it is not yet conclusive</div><div class="admonitionContent_BuS1"><p>27.3% → 36.4% looks encouraging, but the Wilson 95% intervals are 13.2–48.2 and 19.7–57.0 —
they overlap across nearly their whole range. At n=22 this is a <em>hint</em>, not a finding.</p><p>The solid evidence is the perplexity, which is computed over tens of thousands of tokens rather
than 22 questions. Making TH-DOMAIN conclusive would need hundreds of items — the subject of part 9.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="10-summary">10. Summary<a href="https://kobkrit.com/en/blog/llm-01-continue-pretraining#10-summary" class="hash-link" aria-label="Direct link to 10. Summary" title="Direct link to 10. Summary" translate="no">​</a></h2>
<ul>
<li class=""><strong>CPT puts knowledge into the weights</strong> using the same objective as pretraining, with no labels required</li>
<li class=""><strong>It is always a trade</strong> — domain accuracy is bought with general capability you lose</li>
<li class=""><strong>The replay ratio <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>λ</mi></mrow><annotation encoding="application/x-tex">\lambda</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal">λ</span></span></span></span> is the dial that sets the exchange rate</strong> — sweep it, don't guess</li>
<li class=""><strong>A low learning rate</strong> is the line between doing CPT and destroying the model</li>
<li class=""><strong>Every number needs a confidence interval</strong></li>
</ul>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>Limitations of this experiment</div><div class="admonitionContent_BuS1"><p>We train on roughly 8,000 documents, while real CPT at the scale of OpenThaiGPT uses data measured in the <strong>tens of billions of tokens</strong>.
That's a gap of about 6 orders of magnitude.</p><p>This experiment genuinely demonstrates the <strong>mechanism</strong> and the <strong>trade-off</strong>.
But it <strong>does not produce a model that is better for real use</strong>. Don't cite these results as evidence that you built a better Thai model.
What you get is an understanding of what each dial does — and that understanding transfers to work at real scale.</p></div></div>
<p><strong>Next chapter:</strong> <a class="" href="https://kobkrit.com/en/blog/llm-02-sft-lora">SFT and LoRA</a> — once the model has the knowledge, how do we teach it to <em>answer</em>,
and why does training just 1.7% of the parameters get you almost as far as training all of them?</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="references">References<a href="https://kobkrit.com/en/blog/llm-01-continue-pretraining#references" class="hash-link" aria-label="Direct link to References" title="Direct link to References" translate="no">​</a></h2>
<ol>
<li class="">Gururangan et al. (2020). <a href="https://arxiv.org/abs/2004.10964" target="_blank" rel="noopener noreferrer" class="">Don't Stop Pretraining: Adapt Language Models to Domains and Tasks</a> — the domain-adaptive pretraining recipe this chapter follows</li>
<li class="">Ibrahim et al. (2024). <a href="https://arxiv.org/abs/2403.08763" target="_blank" rel="noopener noreferrer" class="">Simple and Scalable Strategies to Continually Pre-train Large Language Models</a> — the replay and LR strategies that keep CPT from destroying the model</li>
<li class="">Gupta et al. (2023). <a href="https://arxiv.org/abs/2308.04014" target="_blank" rel="noopener noreferrer" class="">Continual Pre-Training of Large Language Models: How to (re)warm your model?</a> — why LR warmup matters so much when continuing pretraining</li>
<li class="">Luo et al. (2023). <a href="https://arxiv.org/abs/2308.08747" target="_blank" rel="noopener noreferrer" class="">An Empirical Study of Catastrophic Forgetting in Large Language Models During Continual Fine-tuning</a> — a systematic measurement of catastrophic forgetting</li>
<li class="">Kaplan et al. (2020). <a href="https://arxiv.org/abs/2001.08361" target="_blank" rel="noopener noreferrer" class="">Scaling Laws for Neural Language Models</a> — scaling laws -- the basis for calling 8,000 documents far too little</li>
<li class="">Hoffmann et al. (2022). <a href="https://arxiv.org/abs/2203.15556" target="_blank" rel="noopener noreferrer" class="">Training Compute-Optimal Large Language Models</a> — Chinchilla: the compute-optimal data-to-parameter ratio</li>
<li class="">Yuenyong et al. (2025). <a href="https://arxiv.org/abs/2504.01789" target="_blank" rel="noopener noreferrer" class="">OpenThaiGPT 1.6 and R1: Thai-Centric Open Source and Reasoning Large Language Models</a> — Thai CPT at real scale, for contrast with this chapter's toy run</li>
<li class="">Lowphansirikul et al. (2021). <a href="https://arxiv.org/abs/2101.09635" target="_blank" rel="noopener noreferrer" class="">WangchanBERTa: Pretraining transformer-based Thai Language Models</a> — the pioneering Thai LM and its corpus preparation</li>
</ol>
<hr>
<p><em>The writing, code and notebooks in this series are licensed under <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank" rel="noopener noreferrer" class="">CC BY-NC-SA 4.0</a> — reuse and adapt them freely with attribution, for non-commercial purposes, and share your adaptations under the same terms. Third-party models and datasets referenced here keep their own licences.</em></p>
<nav class="nav_RfLT" aria-label="Thai LLM tutorial series navigation"><p class="heading_XRWm">Thai LLM series<span class="progress_f8e8">Part 1 of 10</span></p><ol class="list_U31a"><li class="item_Y10l"><span class="chip_DDpP chipCurrent_BGpo" aria-current="step"><span class="number_u3BE" aria-hidden="true">1</span><span class="title_BPvL">Continue Pretraining</span><span class="srOnly_owtF">(you are here)</span></span></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-02-sft-lora"><span class="number_u3BE" aria-hidden="true">2</span><span class="title_BPvL">SFT and LoRA</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo"><span class="number_u3BE" aria-hidden="true">3</span><span class="title_BPvL">RLHF and PPO</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-04-dpo"><span class="number_u3BE" aria-hidden="true">4</span><span class="title_BPvL">DPO: Direct Preference Optimization</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-05-grpo"><span class="number_u3BE" aria-hidden="true">5</span><span class="title_BPvL">GRPO</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-06-context-distillation"><span class="number_u3BE" aria-hidden="true">6</span><span class="title_BPvL">Context Distillation</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-07-model-distillation"><span class="number_u3BE" aria-hidden="true">7</span><span class="title_BPvL">Model Distillation</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-08-guardrails"><span class="number_u3BE" aria-hidden="true">8</span><span class="title_BPvL">Guardrails</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-09-benchmarking"><span class="number_u3BE" aria-hidden="true">9</span><span class="title_BPvL">Benchmarking</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-10-deployment"><span class="number_u3BE" aria-hidden="true">10</span><span class="title_BPvL">Deployment</span></a></li></ol></nav>]]></content>
        <author>
            <name>Kobkrit Viriyayudhakorn</name>
            <uri>https://github.com/kobkrit</uri>
        </author>
        <category label="ai" term="ai"/>
        <category label="llm" term="llm"/>
        <category label="thai" term="thai"/>
        <category label="tutorial" term="tutorial"/>
        <category label="fine-tuning" term="fine-tuning"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[LLM 2/10] SFT + LoRA: Teaching a Model to Be an Assistant by Training 1.69% of Its Parameters]]></title>
        <id>https://kobkrit.com/en/blog/llm-02-sft-lora</id>
        <link href="https://kobkrit.com/en/blog/llm-02-sft-lora"/>
        <updated>2026-07-20T20:00:00.000Z</updated>
        <summary type="html"><![CDATA[Supervised fine-tuning with LoRA, from the completion-mask equation down to code that actually finishes on free Colab — why a trained 40 MB 'correction' can stand in for training the whole model, and why the 'under 1%' figure blogs love to quote doesn't hold for small models]]></summary>
        <content type="html"><![CDATA[<p>Last chapter we used Continue Pretraining to put knowledge into the model's weights.
But a model that <em>knows</em> is not a model that <em>answers</em> — a base model has exactly one job: continuing text.
This chapter teaches <strong>SFT (Supervised Fine-Tuning)</strong> with <strong>LoRA</strong>: a technique that trains only about 1.7% of the parameters
yet changes the behavior of the entire model — finished in ~15 minutes on free Colab.
What you get back is an adapter file of roughly 40 MB that becomes the backbone of the rest of the series.</p>
<a class="badge_rUYD" href="https://colab.research.google.com/github/kobkrit/thai-llm-tutorials/blob/main/notebooks/02_sft_lora.ipynb" target="_blank" rel="noopener noreferrer" aria-label="Open the notebook 02_sft_lora.ipynb in Google Colab (opens in a new tab)"><svg class="mark_NB8U" viewBox="0 0 24 24" width="20" height="20" aria-hidden="true" focusable="false"><mask id="llmcourse-colab-cut"><rect x="0" y="0" width="24" height="24" fill="#fff"></rect><circle cx="16.2" cy="12" r="6.1" fill="#000"></circle></mask><circle cx="8.4" cy="12" r="4.6" fill="none" stroke="#F9AB00" stroke-width="3.1" mask="url(#llmcourse-colab-cut)"></circle><circle cx="16.2" cy="12" r="4.6" fill="none" stroke="#E8710A" stroke-width="3.1"></circle></svg><span class="text_QXpz">Open in Colab</span><code class="notebook_ntO0">02_sft_lora.ipynb</code></a>
<nav class="nav_RfLT" aria-label="Thai LLM tutorial series navigation"><p class="heading_XRWm">Thai LLM series<span class="progress_f8e8">Part 2 of 10</span></p><ol class="list_U31a"><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-01-continue-pretraining"><span class="number_u3BE" aria-hidden="true">1</span><span class="title_BPvL">Continue Pretraining</span></a></li><li class="item_Y10l"><span class="chip_DDpP chipCurrent_BGpo" aria-current="step"><span class="number_u3BE" aria-hidden="true">2</span><span class="title_BPvL">SFT and LoRA</span><span class="srOnly_owtF">(you are here)</span></span></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo"><span class="number_u3BE" aria-hidden="true">3</span><span class="title_BPvL">RLHF and PPO</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-04-dpo"><span class="number_u3BE" aria-hidden="true">4</span><span class="title_BPvL">DPO: Direct Preference Optimization</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-05-grpo"><span class="number_u3BE" aria-hidden="true">5</span><span class="title_BPvL">GRPO</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-06-context-distillation"><span class="number_u3BE" aria-hidden="true">6</span><span class="title_BPvL">Context Distillation</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-07-model-distillation"><span class="number_u3BE" aria-hidden="true">7</span><span class="title_BPvL">Model Distillation</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-08-guardrails"><span class="number_u3BE" aria-hidden="true">8</span><span class="title_BPvL">Guardrails</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-09-benchmarking"><span class="number_u3BE" aria-hidden="true">9</span><span class="title_BPvL">Benchmarking</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-10-deployment"><span class="number_u3BE" aria-hidden="true">10</span><span class="title_BPvL">Deployment</span></a></li></ol></nav>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-the-problem">1. The Problem<a href="https://kobkrit.com/en/blog/llm-02-sft-lora#1-the-problem" class="hash-link" aria-label="Direct link to 1. The Problem" title="Direct link to 1. The Problem" translate="no">​</a></h2>
<p>Take a true base model like Qwen3-0.6B-Base from the last chapter and type in (in Thai)
<em>"Could you recommend some Thai dishes, please?"</em> What comes back is usually not an answer
but a <strong>continuation</strong> — it may compose three more questions, keep going as a travel article,
or switch into English partway through, because the only thing it was ever trained on is
"on the internet, what usually follows text like this?"</p>
<p>The ability to <em>answer</em> — take an instruction, respond to the point, then <strong>stop</strong> — does not come with pretraining.
It comes from <strong>SFT</strong>: continued training on (instruction, good answer) pairs, thousands to millions of them.
Every instruct model you have ever used has been through this stage.</p>
<p>But the moment you try it yourself, you hit two problems stacked on top of each other:</p>
<p><strong>Layer one — the cost of full fine-tuning.</strong> Train every parameter and you get an entire new model (~1.2 GB per task for a 0.6B model).
An organization with ten tasks — document summaries, letter drafting, customer replies, complaint triage — has to store ten copies.
And moving <em>every single</em> weight with a high learning rate is a recipe for erasing the knowledge you just added in chapter 1 (remember the learning-rate box?).</p>
<p><strong>Layer two — Thai.</strong> Even the post-trained Qwen3-0.6B shows the symptom we will see all series long:
ask in Thai, and the answer <strong>drifts into English mid-sentence</strong>
(this is where the series' <code>th_ratio</code> metric comes from), because the SFT data it saw was overwhelmingly English.</p>
<p>This chapter fixes both layers at once: SFT on Thai instruction data, done through LoRA instead of full fine-tuning.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-what-were-going-to-do">2. What We're Going to Do<a href="https://kobkrit.com/en/blog/llm-02-sft-lora#2-what-were-going-to-do" class="hash-link" aria-label="Direct link to 2. What We're Going to Do" title="Direct link to 2. What We're Going to Do" translate="no">​</a></h2>
<p>We take Qwen3-0.6B and train it on 4,000 Thai instruction-answer pairs,
using the exact same loss as chapter 1 plus two new pieces:</p>
<ol>
<li class=""><strong>Completion mask</strong> — compute the loss only on the <em>answer</em> tokens, never the question (section 3.1 explains the comically broken failure you get if you skip this)</li>
<li class=""><strong>LoRA (Low-Rank Adaptation)</strong> — freeze all the original weights and instead train two small matrices laid over each layer</li>
</ol>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>The core idea of this chapter</div><div class="admonitionContent_BuS1"><p>You are <strong>not training the model's weights</strong> — you are training a <strong>low-rank "correction"</strong> laid on top of the original weights.</p><p>This is why the adapter is only ~40 MB, not 1.2 GB,
why you can keep twenty adapters and swap them on a single base (twenty tasks = 0.8 GB, not 24 GB),
and why the reference model in chapter 4 (DPO) will cost <strong>zero additional bytes</strong> of VRAM —
switch the adapter off and you get the starting model back exactly.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-the-equations">3. The Equations<a href="https://kobkrit.com/en/blog/llm-02-sft-lora#3-the-equations" class="hash-link" aria-label="Direct link to 3. The Equations" title="Direct link to 3. The Equations" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="31-the-sft-loss-and-the-completion-mask">3.1 The SFT loss and the completion mask<a href="https://kobkrit.com/en/blog/llm-02-sft-lora#31-the-sft-loss-and-the-completion-mask" class="hash-link" aria-label="Direct link to 3.1 The SFT loss and the completion mask" title="Direct link to 3.1 The SFT loss and the completion mask" translate="no">​</a></h3>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi mathvariant="script">L</mi><mtext>SFT</mtext></msub><mo stretchy="false">(</mo><mi>θ</mi><mo stretchy="false">)</mo><mo>=</mo><mo>−</mo><msub><mi mathvariant="double-struck">E</mi><mrow><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>y</mi><mo stretchy="false">)</mo></mrow></msub><mrow><mo fence="true">[</mo><munderover><mo>∑</mo><mrow><mi>t</mi><mo>=</mo><mn>1</mn></mrow><mrow><mi mathvariant="normal">∣</mi><mi>y</mi><mi mathvariant="normal">∣</mi></mrow></munderover><msub><mi>m</mi><mi>t</mi></msub><mi>log</mi><mo>⁡</mo><msub><mi>p</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>y</mi><mi>t</mi></msub><mo>∣</mo><mi>x</mi><mo separator="true">,</mo><msub><mi>y</mi><mrow><mo>&lt;</mo><mi>t</mi></mrow></msub><mo stretchy="false">)</mo><mo fence="true">]</mo></mrow></mrow><annotation encoding="application/x-tex">\mathcal{L}_{\text{SFT}}(\theta) = -\mathbb{E}_{(x,y)}\left[\sum_{t=1}^{|y|} m_t \log p_\theta(y_t \mid x, y_{&lt;t})\right]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathcal">L</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">SFT</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.0278em">θ</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:3.6em;vertical-align:-1.55em"></span><span class="mord">−</span><span class="mord"><span class="mord mathbb">E</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em"><span style="top:-2.5198em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mopen mtight">(</span><span class="mord mathnormal mtight">x</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.0359em">y</span><span class="mclose mtight">)</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.3552em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="minner"><span class="mopen"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.05em"><span style="top:-4.05em"><span class="pstrut" style="height:5.6em"></span><span style="width:0.667em;height:3.6em"><svg xmlns="http://www.w3.org/2000/svg" width="0.667em" height="3.6em" viewBox="0 0 667 3600"><path d="M403 1759 V84 H666 V0 H319 V1759 v0 v1759 v84 h347 v-84
H403z M403 1759 V0 H319 V1759 v0 v1759 v84 h84z"></path></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.55em"><span></span></span></span></span></span></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.961em"><span style="top:-1.8829em;margin-left:0em"><span class="pstrut" style="height:3.05em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">t</span><span class="mrel mtight">=</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.05em"><span class="pstrut" style="height:3.05em"></span><span><span class="mop op-symbol large-op">∑</span></span></span><span style="top:-4.386em;margin-left:0em"><span class="pstrut" style="height:3.05em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">∣</span><span class="mord mathnormal mtight" style="margin-right:0.0359em">y</span><span class="mord mtight">∣</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.2671em"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal">m</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mop">lo<span style="margin-right:0.0139em">g</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">∣</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mrel mtight">&lt;</span><span class="mord mathnormal mtight">t</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1774em"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mclose"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.05em"><span style="top:-4.05em"><span class="pstrut" style="height:5.6em"></span><span style="width:0.667em;height:3.6em"><svg xmlns="http://www.w3.org/2000/svg" width="0.667em" height="3.6em" viewBox="0 0 667 3600"><path d="M347 1759 V0 H0 V84 H263 V1759 v0 v1759 H0 v84 H347z
M347 1759 V0 H263 V1759 v0 v1759 h84z"></path></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.55em"><span></span></span></span></span></span></span></span></span></span></span></span>
<ul>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>y</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">(x, y)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="mclose">)</span></span></span></span> = one training pair — <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow><annotation encoding="application/x-tex">x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">x</span></span></span></span> is the instruction part (chat template included) and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi></mrow><annotation encoding="application/x-tex">y</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0359em">y</span></span></span></span> is the token sequence of the example the model actually sees during training</li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>y</mi><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">y_t</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> = the token at position <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi></mrow><annotation encoding="application/x-tex">t</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6151em"></span><span class="mord mathnormal">t</span></span></span></span>, and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>y</mi><mrow><mo>&lt;</mo><mi>t</mi></mrow></msub></mrow><annotation encoding="application/x-tex">y_{&lt;t}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mrel mtight">&lt;</span><span class="mord mathnormal mtight">t</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1774em"><span></span></span></span></span></span></span></span></span></span> = every token before it</li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>p</mi><mi>θ</mi></msub></mrow><annotation encoding="application/x-tex">p_\theta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> = the probability predicted by the model with parameters <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>θ</mi></mrow><annotation encoding="application/x-tex">\theta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal" style="margin-right:0.0278em">θ</span></span></span></span></li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>m</mi><mi>t</mi></msub><mo>∈</mo><mo stretchy="false">{</mo><mn>0</mn><mo separator="true">,</mo><mn>1</mn><mo stretchy="false">}</mo></mrow><annotation encoding="application/x-tex">m_t \in \{0,1\}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6891em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal">m</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mopen">{</span><span class="mord">0</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord">1</span><span class="mclose">}</span></span></span></span> = the <strong>completion mask</strong> — 1 only on <em>answer</em>-side tokens, and <strong>0 on prompt tokens</strong></li>
</ul>
<p>Remove <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>m</mi><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">m_t</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal">m</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> (i.e. set <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>m</mi><mi>t</mi></msub><mo>≡</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">m_t \equiv 1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6138em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal">m</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">≡</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">1</span></span></span></span> everywhere) and this equation instantly becomes the CPT objective from chapter 1.
<strong>SFT is CPT on text staged as a conversation, plus one mask</strong> — nothing more.</p>
<p>But that one mask is half the battle, because <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>m</mi><mi>t</mi></msub><mo>≡</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">m_t \equiv 1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6138em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal">m</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">≡</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">1</span></span></span></span> is <strong>the easiest default to fall into</strong>
(many pipelines, <code>SFTTrainer</code> included, will silently train this way if the collator isn't wired correctly).
And in typical Thai instruction data, prompt-side tokens make up around 60% of each example —
meaning most of your gradient is busy <strong>teaching the model to write the user's questions</strong>, not to answer them.
The side effect this causes will make its appearance in section 9.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="32-lora-training-the-correction-not-the-weights">3.2 LoRA: training the correction, not the weights<a href="https://kobkrit.com/en/blog/llm-02-sft-lora#32-lora-training-the-correction-not-the-weights" class="hash-link" aria-label="Direct link to 3.2 LoRA: training the correction, not the weights" title="Direct link to 3.2 LoRA: training the correction, not the weights" translate="no">​</a></h3>
<p>Instead of updating the weight matrix <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>W</mi><mn>0</mn></msub></mrow><annotation encoding="application/x-tex">W_0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1389em">W</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em"><span style="top:-2.55em;margin-left:-0.1389em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> directly, LoRA pins <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>W</mi><mn>0</mn></msub></mrow><annotation encoding="application/x-tex">W_0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1389em">W</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em"><span style="top:-2.55em;margin-left:-0.1389em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> in place and learns a difference that is the product of two small matrices:</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msup><mi>W</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo>=</mo><msub><mi>W</mi><mn>0</mn></msub><mo>+</mo><mi mathvariant="normal">Δ</mi><mi>W</mi><mo>=</mo><msub><mi>W</mi><mn>0</mn></msub><mo>+</mo><mfrac><mi>α</mi><mi>r</mi></mfrac><mtext> </mtext><mi>B</mi><mi>A</mi></mrow><annotation encoding="application/x-tex">W' = W_0 + \Delta W = W_0 + \frac{\alpha}{r}\,B A</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8019em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1389em">W</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8019em"><span style="top:-3.113em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1389em">W</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em"><span style="top:-2.55em;margin-left:-0.1389em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord">Δ</span><span class="mord mathnormal" style="margin-right:0.1389em">W</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1389em">W</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em"><span style="top:-2.55em;margin-left:-0.1389em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.7936em;vertical-align:-0.686em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.1076em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em">r</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0037em">α</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0502em">B</span><span class="mord mathnormal">A</span></span></span></span></span>
<ul>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>W</mi><mn>0</mn></msub><mo>∈</mo><msup><mi mathvariant="double-struck">R</mi><mrow><mi>d</mi><mo>×</mo><mi>k</mi></mrow></msup></mrow><annotation encoding="application/x-tex">W_0 \in \mathbb{R}^{d\times k}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1389em">W</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em"><span style="top:-2.55em;margin-left:-0.1389em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.8491em"></span><span class="mord"><span class="mord mathbb">R</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8491em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">d</span><span class="mbin mtight">×</span><span class="mord mathnormal mtight" style="margin-right:0.0315em">k</span></span></span></span></span></span></span></span></span></span></span></span> = the layer's original weights, <strong>frozen — they receive no gradient at all</strong></li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>B</mi><mo>∈</mo><msup><mi mathvariant="double-struck">R</mi><mrow><mi>d</mi><mo>×</mo><mi>r</mi></mrow></msup></mrow><annotation encoding="application/x-tex">B \in \mathbb{R}^{d\times r}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7224em;vertical-align:-0.0391em"></span><span class="mord mathnormal" style="margin-right:0.0502em">B</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.8491em"></span><span class="mord"><span class="mord mathbb">R</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8491em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">d</span><span class="mbin mtight">×</span><span class="mord mathnormal mtight" style="margin-right:0.0278em">r</span></span></span></span></span></span></span></span></span></span></span></span> and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi><mo>∈</mo><msup><mi mathvariant="double-struck">R</mi><mrow><mi>r</mi><mo>×</mo><mi>k</mi></mrow></msup></mrow><annotation encoding="application/x-tex">A \in \mathbb{R}^{r\times k}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7224em;vertical-align:-0.0391em"></span><span class="mord mathnormal">A</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.8491em"></span><span class="mord"><span class="mord mathbb">R</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8491em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">r</span><span class="mbin mtight">×</span><span class="mord mathnormal mtight" style="margin-right:0.0315em">k</span></span></span></span></span></span></span></span></span></span></span></span> = the two adapter matrices we actually train</li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi><mo>≪</mo><mi>min</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>d</mi><mo separator="true">,</mo><mi>k</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">r \ll \min(d, k)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5782em;vertical-align:-0.0391em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">≪</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mop">min</span><span class="mopen">(</span><span class="mord mathnormal">d</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0315em">k</span><span class="mclose">)</span></span></span></span> = the <strong>rank</strong> of the correction — LoRA's main dial (this chapter uses <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi><mo>=</mo><mn>16</mn></mrow><annotation encoding="application/x-tex">r = 16</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">16</span></span></span></span>)</li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>α</mi></mrow><annotation encoding="application/x-tex">\alpha</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.0037em">α</span></span></span></span> = a scale multiplier — the product <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>B</mi><mi>A</mi></mrow><annotation encoding="application/x-tex">BA</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal" style="margin-right:0.0502em">B</span><span class="mord mathnormal">A</span></span></span></span> is always multiplied by <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>α</mi><mi mathvariant="normal">/</mi><mi>r</mi></mrow><annotation encoding="application/x-tex">\alpha/r</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0037em">α</span><span class="mord">/</span><span class="mord mathnormal" style="margin-right:0.0278em">r</span></span></span></span> (here <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>α</mi><mo>=</mo><mn>32</mn></mrow><annotation encoding="application/x-tex">\alpha = 32</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.0037em">α</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">32</span></span></span></span>, so <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>α</mi><mi mathvariant="normal">/</mi><mi>r</mi><mo>=</mo><mn>2</mn></mrow><annotation encoding="application/x-tex">\alpha/r = 2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0037em">α</span><span class="mord">/</span><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">2</span></span></span></span>)</li>
</ul>
<p>Two details in this definition matter more than they look:</p>
<p><strong><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>B</mi></mrow><annotation encoding="application/x-tex">B</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal" style="margin-right:0.0502em">B</span></span></span></span> is initialized to all zeros</strong>, so <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Δ</mi><mi>W</mi><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">\Delta W = 0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord">Δ</span><span class="mord mathnormal" style="margin-right:0.1389em">W</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0</span></span></span></span> at the first step —
training <strong>starts from exactly the base model</strong>, with no period where random weights disturb it.
(<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi></mrow><annotation encoding="application/x-tex">A</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal">A</span></span></span></span> is random Gaussian — set both to zero and both gradients stay zero forever, since each is multiplied by the other's zeros.)</p>
<p><strong>The divisor <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi></mrow><annotation encoding="application/x-tex">r</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span></span></span></span> in <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>α</mi><mi mathvariant="normal">/</mi><mi>r</mi></mrow><annotation encoding="application/x-tex">\alpha/r</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0037em">α</span><span class="mord">/</span><span class="mord mathnormal" style="margin-right:0.0278em">r</span></span></span></span> makes the update magnitude independent of the rank</strong> —
double <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi></mrow><annotation encoding="application/x-tex">r</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span></span></span></span> and the sum <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>B</mi><mi>A</mi></mrow><annotation encoding="application/x-tex">BA</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal" style="margin-right:0.0502em">B</span><span class="mord mathnormal">A</span></span></span></span> has twice as many terms, but it gets divided right back.
So you can sweep <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi></mrow><annotation encoding="application/x-tex">r</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span></span></span></span> without re-tuning the learning rate every time.</p>
<p>And when training finishes, you have two choices: <strong>merge</strong> (<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>W</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo>=</mo><msub><mi>W</mi><mn>0</mn></msub><mo>+</mo><mfrac><mi>α</mi><mi>r</mi></mfrac><mi>B</mi><mi>A</mi></mrow><annotation encoding="application/x-tex">W' = W_0 + \frac{\alpha}{r}BA</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7519em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1389em">W</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7519em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1389em">W</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em"><span style="top:-2.55em;margin-left:-0.1389em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.0404em;vertical-align:-0.345em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6954em"><span style="top:-2.655em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">r</span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.394em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0037em">α</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.345em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mord mathnormal" style="margin-right:0.0502em">B</span><span class="mord mathnormal">A</span></span></span></span>, giving a single model with no added latency)
or <strong>keep it separate</strong> — the second is the road this series takes, because a detachable adapter is what chapter 4 uses to build a reference model for free.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="33-the-fraction-of-parameters-trained--a-number-to-check-not-to-recite">3.3 The fraction of parameters trained — a number to check, not to recite<a href="https://kobkrit.com/en/blog/llm-02-sft-lora#33-the-fraction-of-parameters-trained--a-number-to-check-not-to-recite" class="hash-link" aria-label="Direct link to 3.3 The fraction of parameters trained — a number to check, not to recite" title="Direct link to 3.3 The fraction of parameters trained — a number to check, not to recite" translate="no">​</a></h3>
<p>For a single <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi><mo>×</mo><mi>k</mi></mrow><annotation encoding="application/x-tex">d \times k</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7778em;vertical-align:-0.0833em"></span><span class="mord mathnormal">d</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal" style="margin-right:0.0315em">k</span></span></span></span> matrix, the adapter has <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi><mi>d</mi><mo>+</mo><mi>r</mi><mi>k</mi></mrow><annotation encoding="application/x-tex">rd + rk</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7778em;vertical-align:-0.0833em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="mord mathnormal">d</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="mord mathnormal" style="margin-right:0.0315em">k</span></span></span></span> parameters, a fraction of</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mfrac><mrow><mi>r</mi><mo stretchy="false">(</mo><mi>d</mi><mo>+</mo><mi>k</mi><mo stretchy="false">)</mo></mrow><mrow><mi>d</mi><mi>k</mi></mrow></mfrac></mrow><annotation encoding="application/x-tex">\frac{r(d+k)}{dk}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:2.113em;vertical-align:-0.686em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.427em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord mathnormal">d</span><span class="mord mathnormal" style="margin-right:0.0315em">k</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="mopen">(</span><span class="mord mathnormal">d</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord mathnormal" style="margin-right:0.0315em">k</span><span class="mclose">)</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span>
<ul>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi><mo separator="true">,</mo><mi>k</mi></mrow><annotation encoding="application/x-tex">d, k</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal">d</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0315em">k</span></span></span></span> = the dimensions of the original weight matrix</li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi></mrow><annotation encoding="application/x-tex">r</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span></span></span></span> = the adapter's rank</li>
</ul>
<p>Plug in the real values for Qwen3-0.6B (hidden 1024, intermediate 3072, 28 layers,
adapters on all 7 matrices: q, k, v, o, gate, up, down) and you get <strong>10,092,544 trainable parameters
on a base of 596,049,920 = 1.69%</strong>.</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>1.69% is not "under 1%" — a lesson in checking numbers</div><div class="admonitionContent_BuS1"><p>Nearly every LoRA article says it "trains under 1% of the parameters." That number <strong>is true at 7B scale and up</strong>,
but not for small models, because adapter parameters grow as <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi><mo stretchy="false">(</mo><mi>d</mi><mo>+</mo><mi>k</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">r(d+k)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="mopen">(</span><span class="mord mathnormal">d</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0315em">k</span><span class="mclose">)</span></span></span></span> — <em>linear</em> in the hidden size —
while base parameters grow as <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi><mi>k</mi></mrow><annotation encoding="application/x-tex">dk</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal">d</span><span class="mord mathnormal" style="margin-right:0.0315em">k</span></span></span></span> — <em>quadratic</em>. The smaller the model, the larger a fraction the adapter becomes.</p><p>Notice too that 1.69% is <em>lower</em> than the per-matrix fraction (~2.1–2.3%) — because the denominator includes
roughly 156 million embedding parameters we attach no adapter to. All of this can be checked with plain arithmetic,
and in section 7 the notebook has <code>peft</code> print the real value for you to see with your own eyes — <strong>trust the print, not a blog (this one included)</strong>.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-seeing-the-equations">4. Seeing the Equations<a href="https://kobkrit.com/en/blog/llm-02-sft-lora#4-seeing-the-equations" class="hash-link" aria-label="Direct link to 4. Seeing the Equations" title="Direct link to 4. Seeing the Equations" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="one-mask-redirects-the-entire-gradient">One mask redirects the entire gradient<a href="https://kobkrit.com/en/blog/llm-02-sft-lora#one-mask-redirects-the-entire-gradient" class="hash-link" aria-label="Direct link to One mask redirects the entire gradient" title="Direct link to One mask redirects the entire gradient" translate="no">​</a></h3>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-02-sft-lora/mask-matters.light.svg" alt="A horizontal bar chart comparing the distribution of loss terms between unmasked training, where 60% of the loss lands on prompt-side tokens, and completion-mask training, where all of the loss lands on answer-side tokens" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-02-sft-lora/mask-matters.dark.svg" alt="A horizontal bar chart comparing the distribution of loss terms between unmasked training, where 60% of the loss lands on prompt-side tokens, and completion-mask training, where all of the loss lands on answer-side tokens" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 2.1</span>Pure token accounting for a typical Thai question-answer pair (180 prompt + 120 answer tokens): without the mask, 60% of the loss terms are practice at writing the user's questions</p><div class="captionFooter_w00v"></div></figcaption></figure>
<p>There is nothing deeper in this figure than counting tokens — but the counting is exactly what people skip:
a Thai prompt (system message and chat template included) is often longer than its answer,
so training unmasked spends most of your compute teaching something you never wanted.
The notebook also prints the actual ratio for the sampled dataset — the 60/40 in the figure stands in for a typical example; it is not a sacred constant.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-rank-16-is-enough--the-low-rank-hypothesis">Why rank 16 is "enough" — the low-rank hypothesis<a href="https://kobkrit.com/en/blog/llm-02-sft-lora#why-rank-16-is-enough--the-low-rank-hypothesis" class="hash-link" aria-label="Direct link to Why rank 16 is &quot;enough&quot; — the low-rank hypothesis" title="Direct link to Why rank 16 is &quot;enough&quot; — the low-rank hypothesis" translate="no">​</a></h3>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-02-sft-lora/lora-decomposition.light.svg" alt="A log-log plot of all 1024 singular values showing a sharp cliff after position 16, with the first 16 directions holding roughly 93% of the matrix's energy" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-02-sft-lora/lora-decomposition.dark.svg" alt="A log-log plot of all 1024 singular values showing a sharp cliff after position 16, with the first 16 directions holding roughly 93% of the matrix's energy" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 2.2</span>The singular value spectrum of a synthetic ΔW (1024×1024) with a rank-16 signal buried under full-rank noise — an illustration of the low-rank hypothesis, not a measurement from real fine-tuning</p><div class="captionFooter_w00v"></div></figcaption></figure>
<p>LoRA's hypothesis (Hu et al., 2021) is that fine-tuning moves the weights along <strong>only a few important directions</strong>
relative to the full dimensionality of the matrix. This figure builds a fake <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Δ</mi><mi>W</mi></mrow><annotation encoding="application/x-tex">\Delta W</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord">Δ</span><span class="mord mathnormal" style="margin-right:0.1389em">W</span></span></span></span> with exactly that structure
(a rank-16 signal + noise) and lets SVD dig it back out — if real updates look like this,
a rank-16 adapter captures nearly all of the content. What the figure does <strong>not</strong> prove is that real updates always look like this.
That is an empirical finding of the research, and it is why LoRA "usually" comes close to full fine-tuning — not "always."</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="check-the-number-dont-recite-it">Check the number, don't recite it<a href="https://kobkrit.com/en/blog/llm-02-sft-lora#check-the-number-dont-recite-it" class="hash-link" aria-label="Direct link to Check the number, don't recite it" title="Direct link to Check the number, don't recite it" translate="no">​</a></h3>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-02-sft-lora/trainable-ratio.light.svg" alt="A log-log plot of trainable parameter fraction against LoRA rank from 1 to 256 forming a straight line, with the r=16 point at 1.69% and a red dashed line at the 1% level" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-02-sft-lora/trainable-ratio.dark.svg" alt="A log-log plot of trainable parameter fraction against LoRA rank from 1 to 256 forming a straight line, with the r=16 point at 1.69% and a red dashed line at the 1% level" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 2.3</span>Trainable-parameter fraction per rank, computed exactly from Qwen3-0.6B's real dimensions — the r=16 point sits at 1.69%, above the oft-quoted 'under 1%' line</p><div class="captionFooter_w00v"></div></figcaption></figure>
<p>A straight line on log-log axes confirms what the equation says: the fraction grows exactly <em>linearly</em> with <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi></mrow><annotation encoding="application/x-tex">r</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span></span></span></span>.
And on this model, "under 1%" is only true when <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi><mo>≤</mo><mn>9</mn></mrow><annotation encoding="application/x-tex">r \le 9</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7719em;vertical-align:-0.136em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">9</span></span></span></span> —
which is not a value anyone actually uses for language tasks. The point we use (<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi><mo>=</mo><mn>16</mn></mrow><annotation encoding="application/x-tex">r=16</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">16</span></span></span></span>) is 1.69%, or about 40 MB stored as fp32.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-setting-up-the-environment">5. Setting Up the Environment<a href="https://kobkrit.com/en/blog/llm-02-sft-lora#5-setting-up-the-environment" class="hash-link" aria-label="Direct link to 5. Setting Up the Environment" title="Direct link to 5. Setting Up the Environment" translate="no">​</a></h2>
<p>Open Colab and pick <strong>Runtime → Change runtime type → T4 GPU</strong> (the free tier is enough).</p>
<div class="theme-admonition theme-admonition-danger admonition_xJq3 alert alert--danger"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"></path></svg></span>The series-wide warning worth re-reading every chapter</div><div class="admonitionContent_BuS1"><p>The Colab T4 is Turing architecture (SM 7.5), which <strong>does not support bfloat16</strong> and <strong>does not support FlashAttention-2</strong>.</p><p>But Qwen3-0.6B's <code>config.json</code> declares <code>torch_dtype: bfloat16</code>.
So <code>torch_dtype="auto"</code> is <strong>a trap</strong> — your code will crash or run bizarrely slowly without telling you why.</p><div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">torch_dtype</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">float16      </span><span class="token comment" style="color:#999988;font-style:italic"># not bfloat16</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">attn_implementation</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"sdpa"</span><span class="token plain">     </span><span class="token comment" style="color:#999988;font-style:italic"># not flash_attention_2</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">fp16</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token plain">                      </span><span class="token comment" style="color:#999988;font-style:italic"># in SFTConfig (not bf16=True)</span><br></span></code></pre></div></div></div></div>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">cap </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_device_capability</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"compute capability:"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cap</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                    </span><span class="token comment" style="color:#999988;font-style:italic"># T4 = (7, 5)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"native bf16:"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cap</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&gt;=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                   </span><span class="token comment" style="color:#999988;font-style:italic"># T4 -&gt; False</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"torch says   :"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">is_bf16_supported</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># T4 -&gt; True (counts emulation!)</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span><code>is_bf16_supported()</code> lies on a T4</div><div class="admonitionContent_BuS1"><p>Recent torch returns <code>True</code> on a T4 because it counts <strong>emulation</strong> as support — which is far slower than fp16.
Gate on <strong>compute capability ≥ 8.0</strong> (Ampere and up) instead. This was a real bug, caught only by running the notebook on Colab.</p></div></div>
<div class="theme-admonition theme-admonition-danger admonition_xJq3 alert alert--danger"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"></path></svg></span>fp16 + LoRA: cast only the adapter to fp32</div><div class="admonitionContent_BuS1"><p>In chapter 1 we had to <code>model.float()</code> the entire model before training, because we were training every parameter.
Here the base is frozen — no gradients — so it can happily stay in fp16 and save half the VRAM.
What must be fp32 is <strong>only the trainable parameters</strong>, i.e. the roughly 10 million adapter parameters:</p><div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> p </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> model</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">parameters</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> p</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">requires_grad</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        p</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">data </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> p</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">data</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># cast the adapter only — not the whole model</span><br></span></code></pre></div></div><p>Forget this and you get <code>ValueError: Attempting to unscale FP16 gradients.</code> —
the exact same error as chapter 1, except this time the fix is far cheaper: cast 10 million parameters, not 596 million.</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-vram-budget-lora-transforms">The VRAM budget LoRA transforms<a href="https://kobkrit.com/en/blog/llm-02-sft-lora#the-vram-budget-lora-transforms" class="hash-link" aria-label="Direct link to The VRAM budget LoRA transforms" title="Direct link to The VRAM budget LoRA transforms" translate="no">​</a></h3>
<p>Remember how in chapter 1 Adam's optimizer state took more room than the model itself (about 4.4 GB for <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>m</mi></mrow><annotation encoding="application/x-tex">m</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">m</span></span></span></span> and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>v</mi></mrow><annotation encoding="application/x-tex">v</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.0359em">v</span></span></span></span>)?
Training only the adapter, Adam keeps state for just 10.1 million parameters — <strong>about 0.08 GB</strong> —
to the point that <code>adamw_bnb_8bit</code> is no longer needed. The big remaining budget items are the base weights (fp16, ~1.2 GB) and activations.</p>
<p>Try switching between full fine-tuning and various LoRA ranks in the calculator and watch where the budget moves:</p>
<div class="root_EEmQ"><div class="controls_hr8V"><div class="control_Br1p"><label class="controlLabel_J5tp" for="llmcourse-mbc-model"><span>Model</span></label><select id="llmcourse-mbc-model" class="select_AyHE"><option value="Qwen3-0.6B" selected="">Qwen3-0.6B</option><option value="Qwen3-1.7B">Qwen3-1.7B</option><option value="Qwen3-4B">Qwen3-4B</option><option value="Qwen3-8B">Qwen3-8B</option></select><span class="controlHint_ilRY">596.0M parameters, derived from config.json</span></div><div class="control_Br1p"><label class="controlLabel_J5tp" for="llmcourse-mbc-params"><span>Parameters (millions)</span></label><input id="llmcourse-mbc-params" class="numberInput_P4fE" type="number" min="1" max="1000000" step="1" value="596"></div><fieldset class="control_Br1p" style="border:0;padding:0;margin:0"><legend class="segmentedLegend_oU13">Weight dtype</legend><div class="segmented_Klsm"><span class="segment_AC25"><input type="radio" id="_R_pdeldeh_-fp32" name="llmcourse-mbc-dtype-_R_pdeldeh_" value="fp32"><label class="segmentLabel_wkEZ" for="_R_pdeldeh_-fp32">fp32 (4B)</label></span><span class="segment_AC25"><input type="radio" id="_R_pdeldeh_-fp16" name="llmcourse-mbc-dtype-_R_pdeldeh_" checked="" value="fp16"><label class="segmentLabel_wkEZ" for="_R_pdeldeh_-fp16">fp16 (2B)</label></span><span class="segment_AC25"><input type="radio" id="_R_pdeldeh_-int8" name="llmcourse-mbc-dtype-_R_pdeldeh_" value="int8"><label class="segmentLabel_wkEZ" for="_R_pdeldeh_-int8">int8 (1B)</label></span><span class="segment_AC25"><input type="radio" id="_R_pdeldeh_-nf4" name="llmcourse-mbc-dtype-_R_pdeldeh_" value="nf4"><label class="segmentLabel_wkEZ" for="_R_pdeldeh_-nf4">nf4 (0.5B)</label></span></div></fieldset><fieldset class="control_Br1p" style="border:0;padding:0;margin:0"><legend class="segmentedLegend_oU13">Run mode</legend><div class="segmented_Klsm"><span class="segment_AC25"><input type="radio" id="_R_11deldeh_-train" name="llmcourse-mbc-mode-_R_11deldeh_" checked="" value="train"><label class="segmentLabel_wkEZ" for="_R_11deldeh_-train">Training</label></span><span class="segment_AC25"><input type="radio" id="_R_11deldeh_-inference" name="llmcourse-mbc-mode-_R_11deldeh_" value="inference"><label class="segmentLabel_wkEZ" for="_R_11deldeh_-inference">Serving</label></span></div></fieldset><div class="control_Br1p"><label class="controlLabel_J5tp" for="_R_19deldeh_"><span>LoRA rank</span><span class="controlValue_cYgn">r = 16</span></label><input id="_R_19deldeh_" class="range_qGHz" type="range" min="0" max="7" step="1" aria-label="LoRA rank" aria-valuetext="r = 16" value="3"></div><div class="control_Br1p"><label class="controlLabel_J5tp" for="_R_1hdeldeh_"><span>Batch size</span><span class="controlValue_cYgn">1</span></label><input id="_R_1hdeldeh_" class="range_qGHz" type="range" min="0" max="6" step="1" aria-label="Batch size" aria-valuetext="1" value="0"></div><div class="control_Br1p"><label class="controlLabel_J5tp" for="_R_1pdeldeh_"><span>Sequence length</span><span class="controlValue_cYgn">1024 tok</span></label><input id="_R_1pdeldeh_" class="range_qGHz" type="range" min="0" max="7" step="1" aria-label="Sequence length in tokens" aria-valuetext="1024 tokens" value="2"></div><div class="control_Br1p"><label class="controlLabel_J5tp" for="_R_21deldeh_"><span>Concurrent requests</span><span class="controlValue_cYgn">1</span></label><input id="_R_21deldeh_" class="range_qGHz" type="range" min="0" max="8" step="1" disabled="" aria-label="Concurrent requests held in the KV cache" aria-valuetext="1" value="0"></div><div class="control_Br1p"><label class="checkboxRow_XXA4" for="llmcourse-mbc-ckpt"><input id="llmcourse-mbc-ckpt" type="checkbox" checked=""><span>Gradient checkpointing</span></label><span class="controlHint_ilRY">Trades about 30% more compute for a large drop in activation memory.</span></div></div><div class="svgWrap_mSxx"><svg class="svg_pLEH chart_YWLW" viewBox="0 0 720 118" role="img" aria-label="Stacked VRAM usage totalling 1.43 GiB against a 16 GiB ceiling. Verdict: fits."><rect x="0" y="26" width="720" height="44" rx="6" class="barTrack_ylwk"></rect><rect x="0" y="26" width="47.0428466796875" height="44" class="barSegment_eSn9 seriesWeights_xyK5"><title>weights: 1.13 GiB</title></rect><rect x="47.0428466796875" y="26" width="1" height="44" class="barSegment_eSn9 seriesGradients_Yy9k"><title>gradients: 19.25 MiB</title></rect><rect x="47.826131184895836" y="26" width="4.69970703125" height="44" class="barSegment_eSn9 seriesOptimizer_Sr99"><title>optimizer: 115.50 MiB</title></rect><rect x="52.52583821614583" y="26" width="6.917317708333332" height="44" class="barSegment_eSn9 seriesActivations_mq5k"><title>activations: 170.00 MiB</title></rect><line x1="666.6666666666666" y1="14" x2="666.6666666666666" y2="82" class="ceilingLine_Gd0g"></line><text x="666.6666666666666" y="10" text-anchor="end" class="ceilingLabel_huNs">16 GB — Colab T4</text><g><line x1="0" y1="70" x2="0" y2="75" class="tick_YNak"></line><text x="0" y="88" text-anchor="middle" class="tickLabel_B3jM">0</text></g><g><line x1="166.66666666666666" y1="70" x2="166.66666666666666" y2="75" class="tick_YNak"></line><text x="166.66666666666666" y="88" text-anchor="middle" class="tickLabel_B3jM">4</text></g><g><line x1="333.3333333333333" y1="70" x2="333.3333333333333" y2="75" class="tick_YNak"></line><text x="333.3333333333333" y="88" text-anchor="middle" class="tickLabel_B3jM">8</text></g><g><line x1="500" y1="70" x2="500" y2="75" class="tick_YNak"></line><text x="500" y="88" text-anchor="middle" class="tickLabel_B3jM">12</text></g><g><line x1="666.6666666666666" y1="70" x2="666.6666666666666" y2="75" class="tick_YNak"></line><text x="666.6666666666666" y="88" text-anchor="middle" class="tickLabel_B3jM">16</text></g><text x="720" y="116" text-anchor="end" class="axisLabel_Yazw">GiB</text></svg></div><ul class="legend_BTbY"><li class="legendItem_ApeG"><span class="swatch_vsP4 seriesWeights_xyK5" aria-hidden="true"></span><span class="legendLabel_rxKN">Weights</span><span class="legendValue_wTen">1.13 GiB</span></li><li class="legendItem_ApeG"><span class="swatch_vsP4 seriesGradients_Yy9k" aria-hidden="true"></span><span class="legendLabel_rxKN">Gradients</span><span class="legendValue_wTen">19.25 MiB</span></li><li class="legendItem_ApeG"><span class="swatch_vsP4 seriesOptimizer_Sr99" aria-hidden="true"></span><span class="legendLabel_rxKN">Optimizer state</span><span class="legendValue_wTen">115.50 MiB</span></li><li class="legendItem_ApeG"><span class="swatch_vsP4 seriesActivations_mq5k" aria-hidden="true"></span><span class="legendLabel_rxKN">Activations</span><span class="legendValue_wTen">170.00 MiB</span></li><li class="legendItem_ApeG"><span class="swatch_vsP4 seriesKv_dhmF" aria-hidden="true"></span><span class="legendLabel_rxKN">KV cache</span><span class="legendValue_wTen">—</span></li></ul><div class="readouts__tjv"><div class="readout_D9ns"><span class="readoutLabel_EsIV">Total VRAM</span><span class="readoutValue_VS6z">1.43 GiB</span><span class="readoutSub_DoT9">14.57 GiB to spare</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">Trainable params</span><span class="readoutValue_VS6z">10.1M</span><span class="readoutSub_DoT9">1.69%</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">KV cache per token</span><span class="readoutValue_VS6z">112 KiB</span><span class="readoutSub_DoT9">2 x 28 x 8 x 128</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">Full context KV</span><span class="readoutValue_VS6z">4.38 GiB</span><span class="readoutSub_DoT9">41.0K tok</span></div></div><p class="callout_aEDz calloutSuccess_oTZ4" role="status"><strong class="calloutTitle_nx3s">It fits.</strong>This run needs 1.43 GiB and leaves 14.57 GiB of headroom on a free Colab T4.</p></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-preparing-the-data">6. Preparing the Data<a href="https://kobkrit.com/en/blog/llm-02-sft-lora#6-preparing-the-data" class="hash-link" aria-label="Direct link to 6. Preparing the Data" title="Direct link to 6. Preparing the Data" translate="no">​</a></h2>
<p>We use <strong><code>airesearch/wangchanx-seed-free-synthetic-instruct-thai-120k</code></strong> —
120,000 synthetic Thai instruction-answer pairs from the WangchanX team, a Thai open-source NLP research effort (we sample 4,000).</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> datasets </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> load_dataset</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ds </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> load_dataset</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"airesearch/wangchanx-seed-free-synthetic-instruct-thai-120k"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> split</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"train"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ds </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> ds</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">shuffle</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">seed</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">42</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">select</span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin">range</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">4000</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">to_text</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ex</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    messages </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"role"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"user"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"content"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ex</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"instruction"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># always check column names against the dataset card</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"role"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"assistant"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"content"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ex</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"output"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"text"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> tok</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">apply_chat_template</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">messages</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> tokenize</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">False</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                                            enable_thinking</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">False</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">train_ds </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> ds</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">map</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">to_text</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> remove_columns</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">ds</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">column_names</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>Two lines worth reading slowly:</p>
<ul>
<li class=""><code>apply_chat_template</code> assembles the messages into the format Qwen3 was trained on (<code>&lt;|im_start|&gt;user</code> … <code>&lt;|im_end|&gt;</code> …).
We call it <strong>once</strong>, here and nowhere else — the reason lives in trap 2 of section 9.</li>
<li class=""><code>enable_thinking=False</code> turns off Qwen3's thinking mode, keeping this chapter's examples simple and the mask straightforward.</li>
</ul>
<p>Then perform the ritual that should become a habit: <strong>always decode the first example and look at it with your own eyes.</strong></p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">train_ds</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"text"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">:</span><span class="token number" style="color:#36acaa">400</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># you must see &lt;|im_start|&gt;user ... &lt;|im_end|&gt; ... &lt;|im_start|&gt;assistant ... exactly once each per turn</span><br></span></code></pre></div></div>
<p>The notebook also prints token-length statistics for the sampled set: the median on the prompt side versus the answer side,
and <strong>the fraction of examples longer than 768 tokens</strong> — that last number will come back to collect its debt in trap 3.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-the-main-code">7. The Main Code<a href="https://kobkrit.com/en/blog/llm-02-sft-lora#7-the-main-code" class="hash-link" aria-label="Direct link to 7. The Main Code" title="Direct link to 7. The Main Code" translate="no">​</a></h2>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> torch</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> transformers </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> AutoModelForCausalLM</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> AutoTokenizer</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> peft </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> LoraConfig</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> get_peft_model</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> trl </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> SFTTrainer</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> SFTConfig</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> DataCollatorForCompletionOnlyLM</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">tok </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> AutoTokenizer</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">from_pretrained</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Qwen/Qwen3-0.6B"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">model </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> AutoModelForCausalLM</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">from_pretrained</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"Qwen/Qwen3-0.6B"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">                </span><span class="token comment" style="color:#999988;font-style:italic"># the post-trained model — this chapter fixes behavior, not knowledge</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    torch_dtype</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">float16</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># the T4 has no bf16</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    attn_implementation</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"sdpa"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">       </span><span class="token comment" style="color:#999988;font-style:italic"># the T4 has no FlashAttention-2</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">lora </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> LoraConfig</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    r</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">16</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    lora_alpha</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">32</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">                    </span><span class="token comment" style="color:#999988;font-style:italic"># α/r = 2 — see equation 3.2</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    lora_dropout</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.05</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    target_modules</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"q_proj"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"k_proj"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"v_proj"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"o_proj"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token string" style="color:#e3116c">"gate_proj"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"up_proj"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"down_proj"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    task_type</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"CAUSAL_LM"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">model </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> get_peft_model</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">model</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> lora</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">model</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">print_trainable_parameters</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># trust this line, not the number in any blog</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> p </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> model</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">parameters</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">          </span><span class="token comment" style="color:#999988;font-style:italic"># the fp16 box from section 5</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> p</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">requires_grad</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        p</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">data </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> p</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">data</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>If the printed percentage isn't exactly 1.69, don't panic yet</div><div class="admonitionContent_BuS1"><p><code>peft</code> computes its percentage by dividing by the parameter count <em>including the adapter</em>, while our 1.69% divides by the pure base count.
Slightly different definitions, slightly different numbers — and that is precisely the point of section 3.3:
with numbers like this you <strong>must know where the numerator and denominator come from</strong>, not memorize a free-floating figure and cite it onward.</p></div></div>
<p>Next comes the piece that turns equation 3.1 into code — <strong>the collator that plays the role of <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>m</mi><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">m_t</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal">m</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span></strong>:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">collator </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> DataCollatorForCompletionOnlyLM</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    response_template</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"&lt;|im_start|&gt;assistant\n"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># everything before this = prompt</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    tokenizer</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">tok</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>This collator sets the label of every token before <code>&lt;|im_start|&gt;assistant</code> to <code>-100</code>,
the value PyTorch's loss function skips — the same value chapter 4 uses to mask out the prompt side
in its <code>seq_logp</code> function. Same mask, showing up in a different chapter.</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">cfg </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> SFTConfig</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    output_dir</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"sft-out"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    dataset_text_field</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"text"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    max_seq_length</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">768</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">               </span><span class="token comment" style="color:#999988;font-style:italic"># the notebook prints how many examples get truncated — trap 3</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    per_device_train_batch_size</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    gradient_accumulation_steps</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># effective batch = 16</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    num_train_epochs</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    learning_rate</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">2e-4</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">               </span><span class="token comment" style="color:#999988;font-style:italic"># fine for LoRA — catastrophic for full FT (chapter 1)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    lr_scheduler_type</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"cosine"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    warmup_ratio</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.03</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    packing</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">False</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">                    </span><span class="token comment" style="color:#999988;font-style:italic"># the completion mask can't be used with packing directly</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    fp16</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">                        </span><span class="token comment" style="color:#999988;font-style:italic"># T4: not bf16</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    logging_steps</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">trainer </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> SFTTrainer</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    model</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">model</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    args</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">cfg</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    train_dataset</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">train_ds</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    data_collator</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">collator</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    processing_class</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">tok</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">trainer</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">train</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                       </span><span class="token comment" style="color:#999988;font-style:italic"># ~15 minutes on a T4 (measured: 14.8)</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-danger admonition_xJq3 alert alert--danger"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"></path></svg></span>Why 2e-4 is safe here but was a disaster in chapter 1</div><div class="admonitionContent_BuS1"><p>This same learning rate, used to train <em>every</em> parameter, erases the model's capabilities within a few hundred steps.
With LoRA it is safe because (1) the 596 million original weights are frozen — the knowledge in the base cannot be overwritten,
and (2) <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>B</mi></mrow><annotation encoding="application/x-tex">B</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal" style="margin-right:0.0502em">B</span></span></span></span> starts at zero — the model at step one is exactly the base, then gradually walks away from it.
The worst thing LoRA can do is a bad adapter, which you can throw away at any time.</p></div></div>
<p>Training done, save only the correction:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">model</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">save_pretrained</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"qwen3-0.6b-th-sft-lora"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># ~40 MB — not 1.2 GB</span><br></span></code></pre></div></div>
<p>This is the very adapter chapter 4 loads under the name <code>kobkrit/qwen3-0.6b-th-sft-lora</code>,
serving as both the starting policy and (with the adapter switched off) the reference model — a cross-chapter gift priced at 40 MB.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="8-results">8. Results<a href="https://kobkrit.com/en/blog/llm-02-sft-lora#8-results" class="hash-link" aria-label="Direct link to 8. Results" title="Direct link to 8. Results" translate="no">​</a></h2>
<p>The notebook measures 3 things before and after training and writes them to <code>results.json</code>:</p>
<ol>
<li class=""><strong>TH-INSTR</strong> — the fraction of answers passing an instruction-following rubric (answers the question, stops on its own, stays in role)
from the KobEval-TH benchmark, with a <strong>Wilson 95% CI</strong></li>
<li class=""><strong><code>th_ratio</code></strong> — the fraction of Thai characters in the answers, the series' standing metric.
The stock Qwen3-0.6B is notorious for drifting into English on Thai prompts; SFT on 4,000 all-Thai examples
<em>should</em> move this number visibly — and if it doesn't, that is your signal to go hunt through the mask and the template before anything else.</li>
<li class=""><strong>The fraction of answers ending with eos within the token budget</strong> — the detector for the "won't stop" symptom from traps 1 and 3.</li>
</ol>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>One more time, on confidence intervals</div><div class="admonitionContent_BuS1"><p>A benchmark of a hundred-odd questions gives a CI roughly ±10 points wide, so a number without a CI is still not an experimental result.
The table in section 9 leaves <code>?</code> wherever a real number from the notebook belongs — we don't guess results in advance in this blog.</p></div></div>
<div class="root_IS5b"><div class="picker_cO8e"><span class="pickerLabel_sE2x" id="llmcourse-bac-picker">Prompt</span><div class="pickerButtons_j7L1" role="tablist" aria-labelledby="llmcourse-bac-picker"><button type="button" role="tab" id="llmcourse-bac-tab-0" aria-selected="true" aria-controls="llmcourse-bac-panel-0" tabindex="0" class="pickerButton_gFO3 pickerButtonActive_xIUp">1</button><button type="button" role="tab" id="llmcourse-bac-tab-1" aria-selected="false" aria-controls="llmcourse-bac-panel-1" tabindex="-1" class="pickerButton_gFO3">2</button></div></div><blockquote class="prompt_O4Wp" lang="th"><span class="promptLabel_h2F6">Prompt</span>อธิบายว่าทำไมท้องฟ้าถึงเป็นสีฟ้า แบบสั้น ๆ</blockquote><div class="grid_h_9T" id="llmcourse-bac-panel-0" role="tabpanel" aria-labelledby="llmcourse-bac-tab-0" style="grid-template-columns:repeat(auto-fit, minmax(min(100%, 260px), 1fr))"><article class="card_S27b"><header class="cardHeader_w7wJ"><h4 class="cardTitle_NUQN">base</h4><div class="badges_pXcS"><span class="badge_wUaQ badgeBad_WFwi" title="Share of non-whitespace characters that are Thai script">Thai 18%</span><span class="badge_wUaQ">41 tokens</span></div></header><div class="output_VSGg" lang="th">The sky appears blue because of Rayleigh scattering. ท้องฟ้า is blue เพราะ light scatter ครับ. Shorter wavelengths scatter more than longer ones.</div></article><article class="card_S27b"><header class="cardHeader_w7wJ"><h4 class="cardTitle_NUQN">sft</h4><div class="badges_pXcS"><span class="badge_wUaQ badgeGood_MHH_" title="Share of non-whitespace characters that are Thai script">Thai 99%</span><span class="badge_wUaQ">78 tokens</span></div></header><div class="output_VSGg" lang="th">ท้องฟ้าเป็นสีฟ้าเพราะแสงอาทิตย์กระทบกับโมเลกุลของอากาศแล้วเกิดการกระเจิงแบบเรย์ลี ซึ่งแสงสีน้ำเงินที่มีความยาวคลื่นสั้นกว่าจะกระเจิงได้มากกว่าแสงสีแดง เราจึงมองเห็นท้องฟ้าเป็นสีฟ้าครับ</div></article></div><p class="status_mfC7">Showing the built-in sample.</p></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="9-comparison">9. Comparison<a href="https://kobkrit.com/en/blog/llm-02-sft-lora#9-comparison" class="hash-link" aria-label="Direct link to 9. Comparison" title="Direct link to 9. Comparison" translate="no">​</a></h2>
<p>The notebook trains 3 variants on the same data, measured against the starting model:</p>
<table><thead><tr><th>Model</th><th>TH-INSTR (95% CI)</th><th><code>th_ratio</code></th><th>PPL</th><th>Trained params</th><th>Train time</th></tr></thead><tbody><tr><td>Qwen3-0.6B (base)</td><td>73.3% (55.6–85.8)</td><td>0.93</td><td>21.3</td><td>—</td><td>—</td></tr><tr><td><strong>LoRA r=16 + completion mask</strong></td><td><strong>83.3% (66.4–92.7)</strong></td><td><strong>0.97</strong></td><td><strong>17.7</strong></td><td>10.1M (1.69%)</td><td>13.2 min</td></tr></tbody></table>
<small>Measured on a Colab T4 — Qwen3-0.6B, 4,000 wangchanx examples, peak VRAM 7.42 GB,
250 optimizer steps. Every number comes from the <code>results.json</code> the notebook writes.</small>
<p>SFT improves on every axis: TH-INSTR <strong>+10 points</strong> (73.3% → 83.3%), <code>th_ratio</code> up
(more answers stay in Thai instead of drifting to English), and perplexity down from
21.3 to 17.7 — training only <strong>1.69% of the parameters</strong> for a ~40 MB adapter.</p>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>The intervals still overlap</div><div class="admonitionContent_BuS1"><p>73.3% vs 83.3% looks clear, but the Wilson intervals are 55.6–85.8 and 66.4–92.7 — they
<strong>still overlap</strong>. At n=30 this is not yet statistically significant. The stronger evidence
is <code>th_ratio</code> and PPL, computed over thousands of tokens. Making TH-INSTR conclusive would
need hundreds of items (part 9).</p></div></div>
<p><strong>On full fine-tuning and the no-mask row:</strong> the notebook does train a no-mask LoRA to
show the <em>behaviour</em> where the model finishes its answer and then invents the user's next
question (samples in §8), but it does not score full metrics for it, and it does not run a
100%-parameter full fine-tune — that exceeds the free-Colab budget. We report only what was
measured and do not fill in numbers for runs we did not do.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="10-summary">10. Summary<a href="https://kobkrit.com/en/blog/llm-02-sft-lora#10-summary" class="hash-link" aria-label="Direct link to 10. Summary" title="Direct link to 10. Summary" translate="no">​</a></h2>
<ul>
<li class=""><strong>SFT is CPT on conversations + a completion mask</strong> — the same loss as chapter 1; what changes is that the data is staged, and the mask chooses which tokens count</li>
<li class=""><strong>The mask is half the battle</strong> — <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>m</mi><mi>t</mi></msub><mo>≡</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">m_t \equiv 1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6138em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal">m</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">≡</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">1</span></span></span></span> sends 60% of the gradient into practicing questions, and yields a model that answers, then writes the next question itself</li>
<li class=""><strong>LoRA trains a "correction," not the weights</strong>: <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>W</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo>=</mo><msub><mi>W</mi><mn>0</mn></msub><mo>+</mo><mfrac><mi>α</mi><mi>r</mi></mfrac><mi>B</mi><mi>A</mi></mrow><annotation encoding="application/x-tex">W' = W_0 + \frac{\alpha}{r}BA</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7519em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1389em">W</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7519em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1389em">W</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em"><span style="top:-2.55em;margin-left:-0.1389em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.0404em;vertical-align:-0.345em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6954em"><span style="top:-2.655em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">r</span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.394em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0037em">α</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.345em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mord mathnormal" style="margin-right:0.0502em">B</span><span class="mord mathnormal">A</span></span></span></span> with <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>B</mi></mrow><annotation encoding="application/x-tex">B</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal" style="margin-right:0.0502em">B</span></span></span></span> starting at zero, so training departs from exactly the base — and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>α</mi><mi mathvariant="normal">/</mi><mi>r</mi></mrow><annotation encoding="application/x-tex">\alpha/r</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0037em">α</span><span class="mord">/</span><span class="mord mathnormal" style="margin-right:0.0278em">r</span></span></span></span> lets you change rank without re-tuning the lr</li>
<li class=""><strong>1.69% is not "under 1%"</strong> — adapters grow linearly with hidden size while the base grows quadratically, so the smaller the model, the bigger the adapter's share; always check with <code>print_trainable_parameters()</code></li>
<li class=""><strong>~40 MB of adapter per task</strong>: one base plus many adapters — and it is the reason chapter 4's reference model comes free</li>
<li class=""><strong>lr 2e-4 is safe because the base is frozen</strong> — the same value destroys the model when training everything</li>
<li class=""><strong>pad ≠ eos, template once, never truncate mid-answer</strong> — three traps whose symptoms surface at inference but whose causes live in the data</li>
</ul>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>Limitations of this experiment</div><div class="admonitionContent_BuS1"><p><strong>SFT teaches "format and style" far more than "knowledge"</strong> — 4,000 examples will not add new facts to the model.
Whatever it didn't know before, it still won't know after; it will simply be wrong in a prettier format and a more confident tone,
which is <em>more dangerous than before</em> — putting knowledge in was chapter 1's job (CPT), not this chapter's.</p><p>And as with every chapter: 4,000 examples are a demonstration of the <strong>mechanism</strong>. Production-grade SFT uses tens of thousands to millions of pairs
run through several more layers of quality curation. What you take from this chapter is an understanding of which dial does what and how things break,
which transfers to real scale. But don't cite these results as evidence you got a better Thai model.</p></div></div>
<p><strong>Next chapter:</strong> <a class="" href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo">RLHF and PPO</a> — the model can answer now, but "answers well" cannot be written as a loss function.
We will have humans <em>compare</em> answers, train a reward model from those preferences, then use reinforcement learning to push the model toward it —
with this chapter's 40 MB adapter as the starting point.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="references">References<a href="https://kobkrit.com/en/blog/llm-02-sft-lora#references" class="hash-link" aria-label="Direct link to References" title="Direct link to References" translate="no">​</a></h2>
<ol>
<li class="">Hu et al. (2021). <a href="https://arxiv.org/abs/2106.09685" target="_blank" rel="noopener noreferrer" class="">LoRA: Low-Rank Adaptation of Large Language Models</a> — the source of the W' = W₀ + (α/r)BA equation in section 3</li>
<li class="">Aghajanyan et al. (2020). <a href="https://arxiv.org/abs/2012.13255" target="_blank" rel="noopener noreferrer" class="">Intrinsic Dimensionality Explains the Effectiveness of Language Model Fine-Tuning</a> — evidence that fine-tuning has low intrinsic dimension -- why LoRA works at all</li>
<li class="">Dettmers et al. (2023). <a href="https://arxiv.org/abs/2305.14314" target="_blank" rel="noopener noreferrer" class="">QLoRA: Efficient Finetuning of Quantized LLMs</a> — extends LoRA with 4-bit so larger models fit one GPU</li>
<li class="">Biderman et al. (2024). <a href="https://arxiv.org/abs/2405.09673" target="_blank" rel="noopener noreferrer" class="">LoRA Learns Less and Forgets Less</a> — LoRA trades less learning for less forgetting -- read alongside section 9</li>
<li class="">Ouyang et al. (2022). <a href="https://arxiv.org/abs/2203.02155" target="_blank" rel="noopener noreferrer" class="">Training language models to follow instructions with human feedback</a> — InstructGPT: the origin of the whole SFT -&gt; RM -&gt; PPO pipeline</li>
<li class="">Wang et al. (2022). <a href="https://arxiv.org/abs/2212.10560" target="_blank" rel="noopener noreferrer" class="">Self-Instruct: Aligning Language Models with Self-Generated Instructions</a> — generating instruction data from the model itself</li>
<li class="">Zhou et al. (2023). <a href="https://arxiv.org/abs/2305.11206" target="_blank" rel="noopener noreferrer" class="">LIMA: Less Is More for Alignment</a> — a few thousand high-quality examples suffice -- why we use only 4,000</li>
</ol>
<hr>
<p><em>The writing, code and notebooks in this series are licensed under <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank" rel="noopener noreferrer" class="">CC BY-NC-SA 4.0</a> — reuse and adapt them freely with attribution, for non-commercial purposes, and share your adaptations under the same terms. Third-party models and datasets referenced here keep their own licences.</em></p>
<nav class="nav_RfLT" aria-label="Thai LLM tutorial series navigation"><p class="heading_XRWm">Thai LLM series<span class="progress_f8e8">Part 2 of 10</span></p><ol class="list_U31a"><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-01-continue-pretraining"><span class="number_u3BE" aria-hidden="true">1</span><span class="title_BPvL">Continue Pretraining</span></a></li><li class="item_Y10l"><span class="chip_DDpP chipCurrent_BGpo" aria-current="step"><span class="number_u3BE" aria-hidden="true">2</span><span class="title_BPvL">SFT and LoRA</span><span class="srOnly_owtF">(you are here)</span></span></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo"><span class="number_u3BE" aria-hidden="true">3</span><span class="title_BPvL">RLHF and PPO</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-04-dpo"><span class="number_u3BE" aria-hidden="true">4</span><span class="title_BPvL">DPO: Direct Preference Optimization</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-05-grpo"><span class="number_u3BE" aria-hidden="true">5</span><span class="title_BPvL">GRPO</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-06-context-distillation"><span class="number_u3BE" aria-hidden="true">6</span><span class="title_BPvL">Context Distillation</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-07-model-distillation"><span class="number_u3BE" aria-hidden="true">7</span><span class="title_BPvL">Model Distillation</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-08-guardrails"><span class="number_u3BE" aria-hidden="true">8</span><span class="title_BPvL">Guardrails</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-09-benchmarking"><span class="number_u3BE" aria-hidden="true">9</span><span class="title_BPvL">Benchmarking</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-10-deployment"><span class="number_u3BE" aria-hidden="true">10</span><span class="title_BPvL">Deployment</span></a></li></ol></nav>]]></content>
        <author>
            <name>Kobkrit Viriyayudhakorn</name>
            <uri>https://github.com/kobkrit</uri>
        </author>
        <category label="ai" term="ai"/>
        <category label="llm" term="llm"/>
        <category label="thai" term="thai"/>
        <category label="tutorial" term="tutorial"/>
        <category label="fine-tuning" term="fine-tuning"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[LLM 3/10] RLHF and PPO: Training a Model on a Reward You Cannot Differentiate]]></title>
        <id>https://kobkrit.com/en/blog/llm-03-rlhf-ppo</id>
        <link href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo"/>
        <updated>2026-07-20T19:00:00.000Z</updated>
        <summary type="html"><![CDATA[Training a real Thai reward model from preference pairs, then writing PPO from scratch in about 120 lines on free Colab — and finishing by taking off the KL leash to catch reward hacking red-handed]]></summary>
        <content type="html"><![CDATA[<p>In chapter 2 we taught the model by "imitating the answer key" one token at a time. But the qualities that make an AI assistant genuinely usable —
correct, polite, not making things up, not sliding into English — have no answer key to imitate, and cannot be written directly as a loss function.
This chapter is the most classical answer to that problem: <strong>RLHF (Reinforcement Learning from Human Feedback) with PPO</strong>.
We will train a real reward model from Thai preference pairs, then write the PPO loop ourselves <strong>from scratch</strong> in about 120 lines,
and close with my favorite experiment in the series: taking off the KL leash and watching the model cheat the reward live.
This is deliberately the heaviest chapter of the series, because chapter 4 (DPO) and chapter 5 (GRPO)
both start from this chapter's equations and each choose a different piece to delete.</p>
<a class="badge_rUYD" href="https://colab.research.google.com/github/kobkrit/thai-llm-tutorials/blob/main/notebooks/03_rlhf_ppo.ipynb" target="_blank" rel="noopener noreferrer" aria-label="Open the notebook 03_rlhf_ppo.ipynb in Google Colab (opens in a new tab)"><svg class="mark_NB8U" viewBox="0 0 24 24" width="20" height="20" aria-hidden="true" focusable="false"><mask id="llmcourse-colab-cut"><rect x="0" y="0" width="24" height="24" fill="#fff"></rect><circle cx="16.2" cy="12" r="6.1" fill="#000"></circle></mask><circle cx="8.4" cy="12" r="4.6" fill="none" stroke="#F9AB00" stroke-width="3.1" mask="url(#llmcourse-colab-cut)"></circle><circle cx="16.2" cy="12" r="4.6" fill="none" stroke="#E8710A" stroke-width="3.1"></circle></svg><span class="text_QXpz">Open in Colab</span><code class="notebook_ntO0">03_rlhf_ppo.ipynb</code></a>
<nav class="nav_RfLT" aria-label="Thai LLM tutorial series navigation"><p class="heading_XRWm">Thai LLM series<span class="progress_f8e8">Part 3 of 10</span></p><ol class="list_U31a"><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-01-continue-pretraining"><span class="number_u3BE" aria-hidden="true">1</span><span class="title_BPvL">Continue Pretraining</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-02-sft-lora"><span class="number_u3BE" aria-hidden="true">2</span><span class="title_BPvL">SFT and LoRA</span></a></li><li class="item_Y10l"><span class="chip_DDpP chipCurrent_BGpo" aria-current="step"><span class="number_u3BE" aria-hidden="true">3</span><span class="title_BPvL">RLHF and PPO</span><span class="srOnly_owtF">(you are here)</span></span></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-04-dpo"><span class="number_u3BE" aria-hidden="true">4</span><span class="title_BPvL">DPO: Direct Preference Optimization</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-05-grpo"><span class="number_u3BE" aria-hidden="true">5</span><span class="title_BPvL">GRPO</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-06-context-distillation"><span class="number_u3BE" aria-hidden="true">6</span><span class="title_BPvL">Context Distillation</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-07-model-distillation"><span class="number_u3BE" aria-hidden="true">7</span><span class="title_BPvL">Model Distillation</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-08-guardrails"><span class="number_u3BE" aria-hidden="true">8</span><span class="title_BPvL">Guardrails</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-09-benchmarking"><span class="number_u3BE" aria-hidden="true">9</span><span class="title_BPvL">Benchmarking</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-10-deployment"><span class="number_u3BE" aria-hidden="true">10</span><span class="title_BPvL">Deployment</span></a></li></ol></nav>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-the-problem">1. The Problem<a href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo#1-the-problem" class="hash-link" aria-label="Direct link to 1. The Problem" title="Direct link to 1. The Problem" translate="no">​</a></h2>
<p>SFT in chapter 2 carries one hidden assumption: <strong>there must be an answer key to imitate</strong>.
But think about what we actually want — say, "solve the math problem correctly, and explain it in readable Thai."
That sentence has no single answer key: good answers come in a hundred shapes, and "readable" cannot be written as an equation.</p>
<p>Try to optimize these things directly and you always run into two walls:</p>
<p><strong>Wall one — quality cannot be written as a loss.</strong>
"Better" cannot be defined as a function, but humans are very good at <em>comparing</em>:
show them two answers and ask which one they prefer, and they can do it immediately and fairly consistently.
So the data you can actually collect comes in threes: a prompt <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow><annotation encoding="application/x-tex">x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">x</span></span></span></span>, the chosen answer <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>y</mi><mi>w</mi></msub></mrow><annotation encoding="application/x-tex">y_w</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0269em">w</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span>, the rejected answer <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>y</mi><mi>l</mi></msub></mrow><annotation encoding="application/x-tex">y_l</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0197em">l</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span>.</p>
<p><strong>Wall two — even with a score, you cannot backprop.</strong>
Suppose a magical function <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>y</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">r(x,y)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="mclose">)</span></span></span></span> scored every answer. You still couldn't train supervised,
because the answer <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi></mrow><annotation encoding="application/x-tex">y</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0359em">y</span></span></span></span> comes from <strong>sampling</strong> tokens one at a time. The score arrives <em>after</em> the sampling is done,
and derivatives cannot travel back through sampling — the path from <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi></mrow><annotation encoding="application/x-tex">r</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span></span></span></span> back to the weights <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>θ</mi></mrow><annotation encoding="application/x-tex">\theta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal" style="margin-right:0.0278em">θ</span></span></span></span> breaks exactly there.</p>
<table><thead><tr><th>The road you'd like to take</th><th>The wall it hits</th></tr></thead><tbody><tr><td>Write a loss for "a good answer" directly</td><td>"Good" has no equation — there are only comparisons</td></tr><tr><td>Have humans score, then backprop</td><td>The score sits behind token sampling — gradients can't walk through sampling</td></tr><tr><td>Have humans score live during training</td><td>Humans can't keep pace with even a sliver of the rollouts</td></tr></tbody></table>
<p>Hence the chapter's title: we are about to optimize <strong>a reward we cannot differentiate</strong>.
The tool that can do that is called reinforcement learning.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-what-were-going-to-do">2. What We're Going to Do<a href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo#2-what-were-going-to-do" class="hash-link" aria-label="Direct link to 2. What We're Going to Do" title="Direct link to 2. What We're Going to Do" translate="no">​</a></h2>
<p>RLHF gets past both walls with a two-step walk:</p>
<ul>
<li class=""><strong>Stage A — Reward Model:</strong> train a model <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>r</mi><mi>ϕ</mi></msub><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>y</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">r_\phi(x,y)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0361em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">ϕ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="mclose">)</span></span></span></span> to imitate human comparisons from preference pairs (defeats wall one, and stands in for humans who can't score fast enough)</li>
<li class=""><strong>Stage B — PPO:</strong> use policy-gradient RL to push the policy toward <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>r</mi><mi>ϕ</mi></msub></mrow><annotation encoding="application/x-tex">r_\phi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7167em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">ϕ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span></span></span></span>'s scores without differentiating through the sampling (defeats wall two), with a <strong>KL leash</strong> holding it back from fleeing the starting model</li>
</ul>
<p>The price you pay is complexity: during training there are <strong>four models</strong> resident in VRAM at once —
the policy <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>π</mi><mi>θ</mi></msub></mrow><annotation encoding="application/x-tex">\pi_\theta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> (the one being trained), the reference <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>π</mi><mtext>ref</mtext></msub></mrow><annotation encoding="application/x-tex">\pi_{\text{ref}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">ref</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> (the frozen starting model),
the reward model <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>r</mi><mi>ϕ</mi></msub></mrow><annotation encoding="application/x-tex">r_\phi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7167em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">ϕ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span></span></span></span>, and a value network <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>V</mi><mi>ψ</mi></msub></mrow><annotation encoding="application/x-tex">V_\psi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9694em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.2222em">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.2222em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">ψ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span></span></span></span> that hasn't introduced itself yet (wait for section 3.4).</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>The core idea of this chapter</div><div class="admonitionContent_BuS1"><p>RLHF is the optimization of a reward you <strong>cannot differentiate</strong>, through an imperfect proxy (the reward model).
And optimizing a proxy hard, with nothing to restrain you, always fails by Goodhart's law:
when a measure becomes a target, it ceases to be a good measure.</p><p>The KL term in equation 3.2 is therefore <strong>not a regularizer</strong> sprinkled in for good luck —
it is <strong>the only thing</strong> standing between you and reward hacking.
Section 8 will prove that sentence by taking it out before your eyes.</p></div></div>
<p>And one more sentence to hold on to for the whole series: this chapter's objective is <strong>the master equation of the second half of the series</strong>.
Chapter 4 (DPO) solves it in closed form until the reward model and the RL loop cancel out.
Chapter 5 (GRPO) changes how the advantage is estimated until the value network disappears.
Understand this one chapter, and the next two become "deleting parts" — readable at sight.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-the-equations">3. The Equations<a href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo#3-the-equations" class="hash-link" aria-label="Direct link to 3. The Equations" title="Direct link to 3. The Equations" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="31-the-reward-model-bradleyterry">3.1 The reward model: Bradley–Terry<a href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo#31-the-reward-model-bradleyterry" class="hash-link" aria-label="Direct link to 3.1 The reward model: Bradley–Terry" title="Direct link to 3.1 The reward model: Bradley–Terry" translate="no">​</a></h3>
<p>Stage A trains <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>r</mi><mi>ϕ</mi></msub></mrow><annotation encoding="application/x-tex">r_\phi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7167em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">ϕ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span></span></span></span> with one short loss:</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi mathvariant="script">L</mi><mtext>RM</mtext></msub><mo stretchy="false">(</mo><mi>ϕ</mi><mo stretchy="false">)</mo><mo>=</mo><mo>−</mo><msub><mi mathvariant="double-struck">E</mi><mrow><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><msub><mi>y</mi><mi>w</mi></msub><mo separator="true">,</mo><msub><mi>y</mi><mi>l</mi></msub><mo stretchy="false">)</mo><mo>∼</mo><mi mathvariant="script">D</mi></mrow></msub><mo fence="false" stretchy="true" minsize="1.8em" maxsize="1.8em">[</mo><mi>log</mi><mo>⁡</mo><mi>σ</mi><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">(</mo><msub><mi>r</mi><mi>ϕ</mi></msub><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><msub><mi>y</mi><mi>w</mi></msub><mo stretchy="false">)</mo><mo>−</mo><msub><mi>r</mi><mi>ϕ</mi></msub><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><msub><mi>y</mi><mi>l</mi></msub><mo stretchy="false">)</mo><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">)</mo><mo fence="false" stretchy="true" minsize="1.8em" maxsize="1.8em">]</mo></mrow><annotation encoding="application/x-tex">\mathcal{L}_{\text{RM}}(\phi) = -\mathbb{E}_{(x,y_w,y_l)\sim\mathcal{D}}\Big[\log\sigma\big(r_\phi(x,y_w) - r_\phi(x,y_l)\big)\Big]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathcal">L</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">RM</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">ϕ</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1.8em;vertical-align:-0.65em"></span><span class="mord">−</span><span class="mord"><span class="mord mathbb">E</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em"><span style="top:-2.5198em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mopen mtight">(</span><span class="mord mathnormal mtight">x</span><span class="mpunct mtight">,</span><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1645em"><span style="top:-2.357em;margin-left:-0.0359em;margin-right:0.0714em"><span class="pstrut" style="height:2.5em"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0269em">w</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.143em"><span></span></span></span></span></span></span><span class="mpunct mtight">,</span><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em"><span style="top:-2.3488em;margin-left:-0.0359em;margin-right:0.0714em"><span class="pstrut" style="height:2.5em"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0197em">l</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1512em"><span></span></span></span></span></span></span><span class="mclose mtight">)</span><span class="mrel mtight">∼</span><span class="mord mathcal mtight" style="margin-right:0.0278em">D</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.3552em"><span></span></span></span></span></span></span><span class="mord"><span class="delimsizing size2">[</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mop">lo<span style="margin-right:0.0139em">g</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0359em">σ</span><span class="mord"><span class="delimsizing size1">(</span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">ϕ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0269em">w</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.8em;vertical-align:-0.65em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">ϕ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0197em">l</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mord"><span class="delimsizing size1">)</span></span><span class="mord"><span class="delimsizing size2">]</span></span></span></span></span></span>
<ul>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>r</mi><mi>ϕ</mi></msub><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>y</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">r_\phi(x,y)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0361em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">ϕ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="mclose">)</span></span></span></span> = a single scalar score per text — in practice a language model whose head is swapped for a single linear layer (<code>num_labels=1</code>)</li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>σ</mi></mrow><annotation encoding="application/x-tex">\sigma</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.0359em">σ</span></span></span></span> = the sigmoid, turning the score difference into the probability a human picks <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>y</mi><mi>w</mi></msub></mrow><annotation encoding="application/x-tex">y_w</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0269em">w</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> (the Bradley–Terry model)</li>
<li class="">the wider the gap <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>r</mi><mi>ϕ</mi></msub><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><msub><mi>y</mi><mi>w</mi></msub><mo stretchy="false">)</mo><mo>−</mo><msub><mi>r</mi><mi>ϕ</mi></msub><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><msub><mi>y</mi><mi>l</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">r_\phi(x,y_w) - r_\phi(x,y_l)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0361em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">ϕ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0269em">w</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.0361em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">ϕ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0197em">l</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span>, the lower the loss</li>
</ul>
<p>The point people overlook and pay for later: this loss sees only the <strong>difference</strong> of the scores.
Replace <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>r</mi><mi>ϕ</mi></msub></mrow><annotation encoding="application/x-tex">r_\phi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7167em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">ϕ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span></span></span></span> with <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>r</mi><mi>ϕ</mi></msub><mo>+</mo><mi>c</mi></mrow><annotation encoding="application/x-tex">r_\phi + c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8694em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">ϕ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">c</span></span></span></span> for any constant <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow><annotation encoding="application/x-tex">c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">c</span></span></span></span> — the loss doesn't change at all.
Which means the absolute scale of a reward model <strong>is meaningless and is not pinned down by training</strong>.
Two runs can yield mean scores of 3.7 and −12.4 that rank identically.
This is why you must <strong>always standardize rewards before feeding them into PPO</strong> (subtract the mean, divide by the std) — hold onto this; it returns in sections 7 and 9.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="32-the-master-equation-the-rlhf-objective">3.2 The master equation: the RLHF objective<a href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo#32-the-master-equation-the-rlhf-objective" class="hash-link" aria-label="Direct link to 3.2 The master equation: the RLHF objective" title="Direct link to 3.2 The master equation: the RLHF objective" translate="no">​</a></h3>
<p>If you memorize a single equation from this whole series, <strong>memorize this one:</strong></p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><munder><mrow><mi>max</mi><mo>⁡</mo></mrow><mi>θ</mi></munder><mtext>&nbsp;</mtext><msub><mi mathvariant="double-struck">E</mi><mrow><mi>x</mi><mo>∼</mo><mi mathvariant="script">D</mi><mo separator="true">,</mo><mtext> </mtext><mi>y</mi><mo>∼</mo><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><mo>⋅</mo><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo></mrow></msub><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">[</mo><msub><mi>r</mi><mi>ϕ</mi></msub><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>y</mi><mo stretchy="false">)</mo><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">]</mo><mtext>  </mtext><mo>−</mo><mtext>  </mtext><mi>β</mi><mtext> </mtext><msub><mi mathvariant="double-struck">D</mi><mtext>KL</mtext></msub><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">(</mo><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><mo>⋅</mo><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo><mtext> </mtext><mi mathvariant="normal">∥</mi><mtext> </mtext><msub><mi>π</mi><mtext>ref</mtext></msub><mo stretchy="false">(</mo><mo>⋅</mo><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">)</mo></mrow><annotation encoding="application/x-tex">\max_\theta\ \mathbb{E}_{x\sim\mathcal{D},\,y\sim\pi_\theta(\cdot|x)}\big[r_\phi(x,y)\big] \;-\; \beta\,\mathbb{D}_{\text{KL}}\big(\pi_\theta(\cdot|x)\,\|\,\pi_{\text{ref}}(\cdot|x)\big)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.6021em;vertical-align:-0.7521em"></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.4306em"><span style="top:-2.3479em;margin-left:0em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span><span class="mop">max</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.7521em"><span></span></span></span></span></span><span class="mspace">&nbsp;</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathbb">E</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em"><span style="top:-2.5198em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">x</span><span class="mrel mtight">∼</span><span class="mord mathcal mtight" style="margin-right:0.0278em">D</span><span class="mpunct mtight">,</span><span class="mspace mtight" style="margin-right:0.1952em"></span><span class="mord mathnormal mtight" style="margin-right:0.0359em">y</span><span class="mrel mtight">∼</span><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em"><span style="top:-2.3488em;margin-left:-0.0359em;margin-right:0.0714em"><span class="pstrut" style="height:2.5em"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1512em"><span></span></span></span></span></span></span><span class="mopen mtight">(</span><span class="mord mtight">⋅</span><span class="mord mtight">∣</span><span class="mord mathnormal mtight">x</span><span class="mclose mtight">)</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.3552em"><span></span></span></span></span></span></span><span class="mord"><span class="delimsizing size1">[</span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">ϕ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="mclose">)</span><span class="mord"><span class="delimsizing size1">]</span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.2em;vertical-align:-0.35em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathbb">D</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">KL</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord"><span class="delimsizing size1">(</span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord">⋅</span><span class="mord">∣</span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord">∥</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">ref</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord">⋅</span><span class="mord">∣</span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mord"><span class="delimsizing size1">)</span></span></span></span></span></span>
<p>In plain language: <strong>"collect as much reward as you can, but every step you walk away from the starting model costs a fine."</strong></p>
<ul>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>π</mi><mi>θ</mi></msub></mrow><annotation encoding="application/x-tex">\pi_\theta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> = the policy, the model being trained — note that <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi></mrow><annotation encoding="application/x-tex">y</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0359em">y</span></span></span></span> is <strong>sampled from <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>π</mi><mi>θ</mi></msub></mrow><annotation encoding="application/x-tex">\pi_\theta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> itself</strong>; this is the structural difference from SFT, which learns from static data sitting in a file</li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>π</mi><mtext>ref</mtext></msub></mrow><annotation encoding="application/x-tex">\pi_{\text{ref}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">ref</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> = the reference, the starting model (the post-SFT model from chapter 2), frozen throughout training</li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi></mrow><annotation encoding="application/x-tex">\beta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span></span></span></span> = the price per nat of wandering off — the tightness of the leash</li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="double-struck">D</mi><mtext>KL</mtext></msub></mrow><annotation encoding="application/x-tex">\mathbb{D}_{\text{KL}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8389em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathbb">D</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">KL</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> = the distributional distance between the policy and the reference</li>
</ul>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>Why this is the master equation of the second half of the series</div><div class="admonitionContent_BuS1"><p>Chapter 4 (DPO) will prove this equation has a closed-form solution, then flip it inside out until <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>r</mi><mi>ϕ</mi></msub></mrow><annotation encoding="application/x-tex">r_\phi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7167em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">ϕ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span></span></span></span> and the RL loop both vanish.
Chapter 5 (GRPO) will keep the RL skeleton but change how the advantage is computed until <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>V</mi><mi>ψ</mi></msub></mrow><annotation encoding="application/x-tex">V_\psi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9694em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.2222em">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.2222em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">ψ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span></span></span></span> vanishes.
Neither chapter proposes a new objective — they solve <strong>this same equation</strong> with different tools.</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="33-the-ppo-clipped-surrogate-stage-bs-engine">3.3 The PPO clipped surrogate: Stage B's engine<a href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo#33-the-ppo-clipped-surrogate-stage-bs-engine" class="hash-link" aria-label="Direct link to 3.3 The PPO clipped surrogate: Stage B's engine" title="Direct link to 3.3 The PPO clipped surrogate: Stage B's engine" translate="no">​</a></h3>
<p>Raw policy gradient (REINFORCE) can use a batch of rollouts for exactly one update before throwing it away — very expensive, because generation is the bottleneck.
PPO wants to squeeze the same rollouts for several epochs, so it needs a correction factor (the importance sampling ratio):</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>ρ</mi><mi>t</mi></msub><mo>=</mo><mfrac><mrow><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>a</mi><mi>t</mi></msub><mo>∣</mo><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo></mrow><mrow><msub><mi>π</mi><msub><mi>θ</mi><mtext>old</mtext></msub></msub><mo stretchy="false">(</mo><msub><mi>a</mi><mi>t</mi></msub><mo>∣</mo><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo></mrow></mfrac></mrow><annotation encoding="application/x-tex">\rho_t = \frac{\pi_\theta(a_t \mid s_t)}{\pi_{\theta_{\text{old}}}(a_t \mid s_t)}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord"><span class="mord mathnormal">ρ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.3689em;vertical-align:-0.9419em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.427em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em"><span style="top:-2.3488em;margin-left:-0.0278em;margin-right:0.0714em"><span class="pstrut" style="height:2.5em"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">old</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1512em"><span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2559em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">∣</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mord"><span class="mord mathnormal">s</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">∣</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mord"><span class="mord mathnormal">s</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.9419em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span>
<ul>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>s</mi><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">s_t</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal">s</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> = the state at position <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi></mrow><annotation encoding="application/x-tex">t</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6151em"></span><span class="mord mathnormal">t</span></span></span></span>: the prompt plus every token sampled so far</li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>a</mi><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">a_t</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> = the "action": the next token, already sampled during the rollout</li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>π</mi><msub><mi>θ</mi><mtext>old</mtext></msub></msub></mrow><annotation encoding="application/x-tex">\pi_{\theta_{\text{old}}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6864em;vertical-align:-0.2559em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em"><span style="top:-2.3488em;margin-left:-0.0278em;margin-right:0.0714em"><span class="pstrut" style="height:2.5em"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">old</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1512em"><span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2559em"><span></span></span></span></span></span></span></span></span></span> = a snapshot of the policy <strong>at rollout time</strong> — computed once and frozen</li>
</ul>
<p>Then <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>ρ</mi><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">\rho_t</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord"><span class="mord mathnormal">ρ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> gets pinched with a clip:</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msup><mi mathvariant="script">L</mi><mtext>CLIP</mtext></msup><mo stretchy="false">(</mo><mi>θ</mi><mo stretchy="false">)</mo><mo>=</mo><msub><mi mathvariant="double-struck">E</mi><mi>t</mi></msub><mo fence="false" stretchy="true" minsize="1.8em" maxsize="1.8em">[</mo><mi>min</mi><mo>⁡</mo><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">(</mo><msub><mi>ρ</mi><mi>t</mi></msub><mtext> </mtext><msub><mover accent="true"><mi>A</mi><mo>^</mo></mover><mi>t</mi></msub><mo separator="true">,</mo><mtext>&nbsp;clip</mtext><mo stretchy="false">(</mo><msub><mi>ρ</mi><mi>t</mi></msub><mo separator="true">,</mo><mtext> </mtext><mn>1</mn><mo>−</mo><mi>ϵ</mi><mo separator="true">,</mo><mtext> </mtext><mn>1</mn><mo>+</mo><mi>ϵ</mi><mo stretchy="false">)</mo><mtext> </mtext><msub><mover accent="true"><mi>A</mi><mo>^</mo></mover><mi>t</mi></msub><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">)</mo><mo fence="false" stretchy="true" minsize="1.8em" maxsize="1.8em">]</mo></mrow><annotation encoding="application/x-tex">\mathcal{L}^{\text{CLIP}}(\theta) = \mathbb{E}_t\Big[\min\big(\rho_t\,\hat A_t,\ \text{clip}(\rho_t,\,1-\epsilon,\,1+\epsilon)\,\hat A_t\big)\Big]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.1413em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathcal">L</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8913em"><span style="top:-3.113em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">CLIP</span></span></span></span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.0278em">θ</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1.8em;vertical-align:-0.65em"></span><span class="mord"><span class="mord mathbb">E</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord"><span class="delimsizing size2">[</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mop">min</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="delimsizing size1">(</span></span><span class="mord"><span class="mord mathnormal">ρ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9468em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal">A</span></span><span style="top:-3.2523em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1111em"><span class="mord">^</span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace">&nbsp;</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord text"><span class="mord">clip</span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">ρ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.8389em;vertical-align:-0.1944em"></span><span class="mord mathnormal">ϵ</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.8em;vertical-align:-0.65em"></span><span class="mord mathnormal">ϵ</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9468em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal">A</span></span><span style="top:-3.2523em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1111em"><span class="mord">^</span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord"><span class="delimsizing size1">)</span></span><span class="mord"><span class="delimsizing size2">]</span></span></span></span></span></span>
<ul>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mover accent="true"><mi>A</mi><mo>^</mo></mover><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">\hat A_t</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0968em;vertical-align:-0.15em"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9468em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal">A</span></span><span style="top:-3.2523em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1111em"><span class="mord">^</span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> = the advantage: "how much better than expected was this token" (defined in the next subsection)</li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ϵ</mi></mrow><annotation encoding="application/x-tex">\epsilon</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">ϵ</span></span></span></span> = the width of the trust region (standard value 0.2)</li>
</ul>
<p>The heart is that <strong>min + clip work together with deliberate pessimism</strong>:
if <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mover accent="true"><mi>A</mi><mo>^</mo></mover><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">\hat A_t</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0968em;vertical-align:-0.15em"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9468em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal">A</span></span><span style="top:-3.2523em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1111em"><span class="mord">^</span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> is positive (a good token), the payoff from pushing <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>ρ</mi><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">\rho_t</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord"><span class="mord mathnormal">ρ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> is <strong>capped</strong> at <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mo>+</mo><mi>ϵ</mi></mrow><annotation encoding="application/x-tex">1+\epsilon</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em"></span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">ϵ</span></span></span></span> — pushing beyond that earns nothing, gradient zero.
But if <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mover accent="true"><mi>A</mi><mo>^</mo></mover><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">\hat A_t</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0968em;vertical-align:-0.15em"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9468em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal">A</span></span><span style="top:-3.2523em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1111em"><span class="mord">^</span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> is negative (a bad token), the min always picks the <strong>worse</strong> branch — the penalty has no floor.
One-sentence summary: <strong>gains capped, losses uncapped.</strong> The policy therefore moves in small steps that stay close to where it already is.</p>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>Don't get confused: there are two "old models," and they are not the same model</div><div class="admonitionContent_BuS1"><p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>π</mi><mtext>ref</mtext></msub></mrow><annotation encoding="application/x-tex">\pi_{\text{ref}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">ref</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> in equation 3.2 is frozen <strong>for the entire run</strong> and serves as the KL leash.
<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>π</mi><msub><mi>θ</mi><mtext>old</mtext></msub></msub></mrow><annotation encoding="application/x-tex">\pi_{\theta_{\text{old}}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6864em;vertical-align:-0.2559em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em"><span style="top:-2.3488em;margin-left:-0.0278em;margin-right:0.0714em"><span class="pstrut" style="height:2.5em"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">old</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1512em"><span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2559em"><span></span></span></span></span></span></span></span></span></span> in equation 3.3 is a snapshot <strong>at the latest rollout</strong>, refreshed every round, and serves as the trust region.
The number-one bug among people writing their own PPO is storing these two in the same variable.</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="34-gae-computing-the-advantage-without-drowning-in-noise">3.4 GAE: computing the advantage without drowning in noise<a href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo#34-gae-computing-the-advantage-without-drowning-in-noise" class="hash-link" aria-label="Direct link to 3.4 GAE: computing the advantage without drowning in noise" title="Direct link to 3.4 GAE: computing the advantage without drowning in noise" translate="no">​</a></h3>
<p>The advantage is built from the TD error of the value network <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>V</mi><mi>ψ</mi></msub></mrow><annotation encoding="application/x-tex">V_\psi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9694em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.2222em">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.2222em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">ψ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span></span></span></span>:</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>δ</mi><mi>t</mi></msub><mo>=</mo><msub><mi>r</mi><mi>t</mi></msub><mo>+</mo><mi>γ</mi><msub><mi>V</mi><mi>ψ</mi></msub><mo stretchy="false">(</mo><msub><mi>s</mi><mrow><mi>t</mi><mo>+</mo><mn>1</mn></mrow></msub><mo stretchy="false">)</mo><mo>−</mo><msub><mi>V</mi><mi>ψ</mi></msub><mo stretchy="false">(</mo><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\delta_t = r_t + \gamma V_\psi(s_{t+1}) - V_\psi(s_t)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0379em">δ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:-0.0379em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.7333em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.0361em;vertical-align:-0.2861em"></span><span class="mord mathnormal" style="margin-right:0.0556em">γ</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.2222em">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.2222em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">ψ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">s</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">t</span><span class="mbin mtight">+</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2083em"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.0361em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.2222em">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.2222em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">ψ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">s</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span></span>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mover accent="true"><mi>A</mi><mo>^</mo></mover><mi>t</mi></msub><mo>=</mo><munderover><mo>∑</mo><mrow><mi>l</mi><mo>=</mo><mn>0</mn></mrow><mi mathvariant="normal">∞</mi></munderover><mo stretchy="false">(</mo><mi>γ</mi><mi>λ</mi><msup><mo stretchy="false">)</mo><mi>l</mi></msup><mtext> </mtext><msub><mi>δ</mi><mrow><mi>t</mi><mo>+</mo><mi>l</mi></mrow></msub></mrow><annotation encoding="application/x-tex">\hat A_t = \sum_{l=0}^{\infty} (\gamma\lambda)^l\,\delta_{t+l}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0968em;vertical-align:-0.15em"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9468em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal">A</span></span><span style="top:-3.2523em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1111em"><span class="mord">^</span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.9535em;vertical-align:-1.3021em"></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.6514em"><span style="top:-1.8479em;margin-left:0em"><span class="pstrut" style="height:3.05em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0197em">l</span><span class="mrel mtight">=</span><span class="mord mtight">0</span></span></span></span><span style="top:-3.05em"><span class="pstrut" style="height:3.05em"></span><span><span class="mop op-symbol large-op">∑</span></span></span><span style="top:-4.3em;margin-left:0em"><span class="pstrut" style="height:3.05em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">∞</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.3021em"><span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.0556em">γ</span><span class="mord mathnormal">λ</span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8991em"><span style="top:-3.113em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0197em">l</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0379em">δ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0379em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">t</span><span class="mbin mtight">+</span><span class="mord mathnormal mtight" style="margin-right:0.0197em">l</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2083em"><span></span></span></span></span></span></span></span></span></span></span>
<ul>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>V</mi><mi>ψ</mi></msub><mo stretchy="false">(</mo><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">V_\psi(s_t)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0361em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.2222em">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.2222em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">ψ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">s</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span> = the value network's prediction of "from here to the end, how much more reward will be collected" — this is <strong>model number four</strong></li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>r</mi><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">r_t</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> = the per-token reward (in our task: a KL penalty at every position, plus the task score at the final token)</li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>γ</mi></mrow><annotation encoding="application/x-tex">\gamma</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0556em">γ</span></span></span></span> = the discount factor (LLM work usually uses 1.0)</li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>λ</mi></mrow><annotation encoding="application/x-tex">\lambda</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal">λ</span></span></span></span> = the bias–variance dial: <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>λ</mi><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">\lambda = 0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal">λ</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0</span></span></span></span> trusts <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>V</mi><mi>ψ</mi></msub></mrow><annotation encoding="application/x-tex">V_\psi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9694em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.2222em">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.2222em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">ψ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span></span></span></span> wholeheartedly (high bias if <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>V</mi><mi>ψ</mi></msub></mrow><annotation encoding="application/x-tex">V_\psi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9694em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.2222em">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.2222em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">ψ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span></span></span></span> mispredicts), <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>λ</mi><mo>=</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">\lambda = 1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal">λ</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">1</span></span></span></span> doesn't trust it at all and waits for the real outcome to the very end (high variance, carrying the noise of the whole trajectory); the popular value is 0.95</li>
</ul>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>Remember this V_ψ well — it is the one GRPO will kill</div><div class="admonitionContent_BuS1"><p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>V</mi><mi>ψ</mi></msub></mrow><annotation encoding="application/x-tex">V_\psi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9694em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.2222em">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.2222em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">ψ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span></span></span></span> is a model roughly the size of the policy that must be trained <em>alongside it</em> with a loss of its own.
If <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>V</mi><mi>ψ</mi></msub></mrow><annotation encoding="application/x-tex">V_\psi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9694em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.2222em">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.2222em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">ψ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span></span></span></span> predicts garbage, the advantage is garbage, and the policy learns from a garbage signal — PPO's classic failure point.
Chapter 5 will answer the question "what if we replaced <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>V</mi><mi>ψ</mi></msub></mrow><annotation encoding="application/x-tex">V_\psi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9694em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.2222em">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.2222em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">ψ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span></span></span></span> with the mean of a group of answers sampled from the same prompt?"
That is the entire GRPO algorithm — deleting the fourth model with a single average.</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="35-the-full-ppo-loss-three-terms-two-models">3.5 The full PPO loss: three terms, two models<a href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo#35-the-full-ppo-loss-three-terms-two-models" class="hash-link" aria-label="Direct link to 3.5 The full PPO loss: three terms, two models" title="Direct link to 3.5 The full PPO loss: three terms, two models" translate="no">​</a></h3>
<p>Assemble every piece into the one loss the optimizer actually sees (written as a minimization):</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi mathvariant="script">L</mi><mtext>PPO</mtext></msub><mo>=</mo><mo>−</mo><msup><mi mathvariant="script">L</mi><mtext>CLIP</mtext></msup><mtext>  </mtext><mo>+</mo><mtext>  </mtext><msub><mi>c</mi><mn>1</mn></msub><mtext> </mtext><msub><mi mathvariant="double-struck">E</mi><mi>t</mi></msub><mo fence="false" stretchy="true" minsize="1.8em" maxsize="1.8em">[</mo><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">(</mo><msub><mi>V</mi><mi>ψ</mi></msub><mo stretchy="false">(</mo><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo><mo>−</mo><msub><mover accent="true"><mi>R</mi><mo>^</mo></mover><mi>t</mi></msub><msup><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">)</mo><mn>2</mn></msup><mo fence="false" stretchy="true" minsize="1.8em" maxsize="1.8em">]</mo><mtext>  </mtext><mo>−</mo><mtext>  </mtext><msub><mi>c</mi><mn>2</mn></msub><mtext> </mtext><msub><mi mathvariant="double-struck">E</mi><mi>t</mi></msub><mo fence="false" stretchy="true" minsize="1.8em" maxsize="1.8em">[</mo><mi mathvariant="script">H</mi><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">[</mo><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><mo>⋅</mo><mo>∣</mo><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">]</mo><mo fence="false" stretchy="true" minsize="1.8em" maxsize="1.8em">]</mo></mrow><annotation encoding="application/x-tex">\mathcal{L}_{\text{PPO}} = -\mathcal{L}^{\text{CLIP}} \;+\; c_1\,\mathbb{E}_t\Big[\big(V_\psi(s_t) - \hat R_t\big)^2\Big] \;-\; c_2\,\mathbb{E}_t\Big[\mathcal{H}\big[\pi_\theta(\cdot \mid s_t)\big]\Big]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathcal">L</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">PPO</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.9747em;vertical-align:-0.0833em"></span><span class="mord">−</span><span class="mord"><span class="mord mathcal">L</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8913em"><span style="top:-3.113em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">CLIP</span></span></span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.8em;vertical-align:-0.65em"></span><span class="mord"><span class="mord mathnormal">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathbb">E</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord"><span class="delimsizing size2">[</span></span><span class="mord"><span class="delimsizing size1">(</span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.2222em">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.2222em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">ψ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">s</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.8em;vertical-align:-0.65em"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9468em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal" style="margin-right:0.0077em">R</span></span><span style="top:-3.2523em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1667em"><span class="mord">^</span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:-0.0077em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord"><span class="mord"><span class="delimsizing size1">)</span></span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:1.054em"><span style="top:-3.3029em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mord"><span class="delimsizing size2">]</span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.8em;vertical-align:-0.65em"></span><span class="mord"><span class="mord mathnormal">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathbb">E</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord"><span class="delimsizing size2">[</span></span><span class="mord mathcal" style="margin-right:0.0097em">H</span><span class="mord"><span class="delimsizing size1">[</span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord">⋅</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">∣</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1.8em;vertical-align:-0.65em"></span><span class="mord"><span class="mord mathnormal">s</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mord"><span class="delimsizing size1">]</span></span><span class="mord"><span class="delimsizing size2">]</span></span></span></span></span></span>
<ul>
<li class="">first term = the clipped surrogate from 3.3 (negated because we want to maximize it)</li>
<li class="">second term = the value loss teaching <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>V</mi><mi>ψ</mi></msub></mrow><annotation encoding="application/x-tex">V_\psi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9694em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.2222em">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.2222em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">ψ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span></span></span></span> to predict close to the actual return <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mover accent="true"><mi>R</mi><mo>^</mo></mover><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">\hat R_t</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0968em;vertical-align:-0.15em"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9468em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal" style="margin-right:0.0077em">R</span></span><span style="top:-3.2523em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1667em"><span class="mord">^</span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:-0.0077em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span>; <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>c</mi><mn>1</mn></msub></mrow><annotation encoding="application/x-tex">c_1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> is usually 0.5</li>
<li class="">third term = the entropy bonus <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="script">H</mi></mrow><annotation encoding="application/x-tex">\mathcal{H}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathcal" style="margin-right:0.0097em">H</span></span></span></span>, keeping the distribution from collapsing too fast; <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>c</mi><mn>2</mn></msub></mrow><annotation encoding="application/x-tex">c_2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> is usually 0.01</li>
<li class="">as for the KL leash from equation 3.2, standard practice folds it into the per-token reward: <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>r</mi><mi>t</mi></msub><mo>←</mo><msub><mi>r</mi><mi>t</mi></msub><mo>−</mo><mi>β</mi><mtext> </mtext><mo stretchy="false">(</mo><mi>log</mi><mo>⁡</mo><msub><mi>π</mi><mi>θ</mi></msub><mo>−</mo><mi>log</mi><mo>⁡</mo><msub><mi>π</mi><mtext>ref</mtext></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">r_t \leftarrow r_t - \beta\,(\log\pi_\theta - \log\pi_{\text{ref}})</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">←</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.7333em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mopen">(</span><span class="mop">lo<span style="margin-right:0.0139em">g</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mop">lo<span style="margin-right:0.0139em">g</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">ref</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span>, which is the approach we take in section 7</li>
</ul>
<p>Count all the toys that need tuning: 4 models, plus <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ϵ</mi><mo separator="true">,</mo><mi>β</mi><mo separator="true">,</mo><mi>γ</mi><mo separator="true">,</mo><mi>λ</mi><mo separator="true">,</mo><msub><mi>c</mi><mn>1</mn></msub><mo separator="true">,</mo><msub><mi>c</mi><mn>2</mn></msub></mrow><annotation encoding="application/x-tex">\epsilon, \beta, \gamma, \lambda, c_1, c_2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal">ϵ</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0556em">γ</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal">λ</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span>, and two separate learning rates.
This is why PPO is famous for "run it twice with different seeds, get two completely different stories" —
and it is the reason for chapter 4's entire existence.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-seeing-the-equations">4. Seeing the Equations<a href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo#4-seeing-the-equations" class="hash-link" aria-label="Direct link to 4. Seeing the Equations" title="Direct link to 4. Seeing the Equations" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="bradleyterry-the-gradient-piles-onto-the-pairs-still-ranked-wrong">Bradley–Terry: the gradient piles onto the pairs still ranked wrong<a href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo#bradleyterry-the-gradient-piles-onto-the-pairs-still-ranked-wrong" class="hash-link" aria-label="Direct link to Bradley–Terry: the gradient piles onto the pairs still ranked wrong" title="Direct link to Bradley–Terry: the gradient piles onto the pairs still ranked wrong" translate="no">​</a></h3>
<p>Pick the <strong>Bradley-Terry</strong> mode in the tool below and drag the margin:
pairs the reward model already ranks confidently right (large positive margin) have almost no gradient left —
Stage A training automatically spends its budget on the pairs it <strong>still ranks wrong</strong>.</p>
<div class="root_Y8YJ"><div class="controls_hr8V"><fieldset class="control_Br1p" style="border:0;padding:0;margin:0"><legend class="segmentedLegend_oU13">Loss family</legend><div class="segmented_Klsm"><span class="segment_AC25"><input type="radio" id="_R_9culdeh_-bt" name="llmcourse-ple-family-_R_9culdeh_" checked="" value="bt"><label class="segmentLabel_wkEZ" for="_R_9culdeh_-bt">Bradley-Terry</label></span><span class="segment_AC25"><input type="radio" id="_R_9culdeh_-dpo" name="llmcourse-ple-family-_R_9culdeh_" value="dpo"><label class="segmentLabel_wkEZ" for="_R_9culdeh_-dpo">DPO</label></span><span class="segment_AC25"><input type="radio" id="_R_9culdeh_-ipo" name="llmcourse-ple-family-_R_9culdeh_" value="ipo"><label class="segmentLabel_wkEZ" for="_R_9culdeh_-ipo">IPO</label></span><span class="segment_AC25"><input type="radio" id="_R_9culdeh_-hinge" name="llmcourse-ple-family-_R_9culdeh_" value="hinge"><label class="segmentLabel_wkEZ" for="_R_9culdeh_-hinge">Hinge</label></span></div></fieldset><div class="control_Br1p"><label class="controlLabel_J5tp" for="_R_hculdeh_"><span>Reward margin Δ</span><span class="controlValue_cYgn">2.00</span></label><input id="_R_hculdeh_" class="range_qGHz" type="range" min="-6" max="6" step="0.05" aria-label="Reward margin delta between the chosen and rejected response" aria-valuetext="2.00" aria-describedby="_R_hculdeh_-hint" value="2"><span class="controlHint_ilRY" id="_R_hculdeh_-hint">Positive means the model already prefers the chosen response.</span></div><div class="control_Br1p"><label class="controlLabel_J5tp" for="_R_pculdeh_"><span>β</span><span class="controlValue_cYgn">0.10</span></label><input id="_R_pculdeh_" class="range_qGHz" type="range" min="0.01" max="1" step="0.01" disabled="" aria-label="Beta, the KL penalty strength" aria-valuetext="0.10" aria-describedby="_R_pculdeh_-hint" value="0.1"><span class="controlHint_ilRY" id="_R_pculdeh_-hint">Bradley-Terry has no temperature term.</span></div></div><div class="svgWrap_mSxx"><svg class="svg_pLEH plot_ViPE" viewBox="0 0 720 320" role="img" aria-label="Loss curve and gradient weight against the reward margin. At the current margin 2.00 the loss is 0.127 and the gradient weight is 0.119."><line x1="58" y1="148" x2="662" y2="148" class="zeroLine_dlwX"></line><line x1="58" y1="278" x2="662" y2="278" class="axis_LG2_"></line><g><line x1="58" y1="278" x2="58" y2="283" class="axis_LG2_"></line><text x="58" y="296" text-anchor="middle" class="tickLabel_B3jM">-6</text></g><g><line x1="158.66666666666666" y1="278" x2="158.66666666666666" y2="283" class="axis_LG2_"></line><text x="158.66666666666666" y="296" text-anchor="middle" class="tickLabel_B3jM">-4</text></g><g><line x1="259.3333333333333" y1="278" x2="259.3333333333333" y2="283" class="axis_LG2_"></line><text x="259.3333333333333" y="296" text-anchor="middle" class="tickLabel_B3jM">-2</text></g><g><line x1="360" y1="278" x2="360" y2="283" class="axis_LG2_"></line><text x="360" y="296" text-anchor="middle" class="tickLabel_B3jM">0</text></g><g><line x1="460.66666666666663" y1="278" x2="460.66666666666663" y2="283" class="axis_LG2_"></line><text x="460.66666666666663" y="296" text-anchor="middle" class="tickLabel_B3jM">2</text></g><g><line x1="561.3333333333334" y1="278" x2="561.3333333333334" y2="283" class="axis_LG2_"></line><text x="561.3333333333334" y="296" text-anchor="middle" class="tickLabel_B3jM">4</text></g><g><line x1="662" y1="278" x2="662" y2="283" class="axis_LG2_"></line><text x="662" y="296" text-anchor="middle" class="tickLabel_B3jM">6</text></g><rect x="360" y="18" width="302" height="260" class="correctRegion_qefa"></rect><path d="M58.00,18.00 L60.52,18.02 L63.03,18.03 L65.55,18.05 L68.07,18.07 L70.58,18.09 L73.10,18.11 L75.62,18.13 L78.13,18.16 L80.65,18.18 L83.17,18.21 L85.68,18.24 L88.20,18.26 L90.72,18.29 L93.23,18.33 L95.75,18.36 L98.27,18.39 L100.78,18.43 L103.30,18.47 L105.82,18.51 L108.33,18.55 L110.85,18.59 L113.37,18.64 L115.88,18.69 L118.40,18.74 L120.92,18.80 L123.43,18.85 L125.95,18.91 L128.47,18.97 L130.98,19.04 L133.50,19.11 L136.02,19.18 L138.53,19.26 L141.05,19.34 L143.57,19.42 L146.08,19.51 L148.60,19.60 L151.12,19.70 L153.63,19.80 L156.15,19.91 L158.67,20.02 L161.18,20.14 L163.70,20.26 L166.22,20.39 L168.73,20.53 L171.25,20.67 L173.77,20.82 L176.28,20.98 L178.80,21.14 L181.32,21.32 L183.83,21.50 L186.35,21.69 L188.87,21.89 L191.38,22.10 L193.90,22.31 L196.42,22.54 L198.93,22.78 L201.45,23.03 L203.97,23.30 L206.48,23.57 L209.00,23.86 L211.52,24.16 L214.03,24.47 L216.55,24.80 L219.07,25.15 L221.58,25.51 L224.10,25.88 L226.62,26.28 L229.13,26.69 L231.65,27.12 L234.17,27.56 L236.68,28.03 L239.20,28.52 L241.72,29.02 L244.23,29.55 L246.75,30.10 L249.27,30.68 L251.78,31.27 L254.30,31.90 L256.82,32.54 L259.33,33.21 L261.85,33.91 L264.37,34.63 L266.88,35.39 L269.40,36.16 L271.92,36.97 L274.43,37.81 L276.95,38.67 L279.47,39.57 L281.98,40.50 L284.50,41.45 L287.02,42.44 L289.53,43.46 L292.05,44.51 L294.57,45.59 L297.08,46.70 L299.60,47.84 L302.12,49.02 L304.63,50.22 L307.15,51.46 L309.67,52.73 L312.18,54.02 L314.70,55.35 L317.22,56.70 L319.73,58.08 L322.25,59.49 L324.77,60.92 L327.28,62.38 L329.80,63.86 L332.32,65.36 L334.83,66.88 L337.35,68.42 L339.87,69.98 L342.38,71.55 L344.90,73.14 L347.42,74.74 L349.93,76.34 L352.45,77.96 L354.97,79.58 L357.48,81.21 L360.00,82.84 L362.52,84.47 L365.03,86.09 L367.55,87.72 L370.07,89.33 L372.58,90.94 L375.10,92.54 L377.62,94.13 L380.13,95.70 L382.65,97.26 L385.17,98.80 L387.68,100.32 L390.20,101.82 L392.72,103.30 L395.23,104.76 L397.75,106.19 L400.27,107.60 L402.78,108.98 L405.30,110.33 L407.82,111.66 L410.33,112.95 L412.85,114.22 L415.37,115.45 L417.88,116.66 L420.40,117.83 L422.92,118.98 L425.43,120.09 L427.95,121.17 L430.47,122.22 L432.98,123.24 L435.50,124.23 L438.02,125.18 L440.53,126.11 L443.05,127.00 L445.57,127.87 L448.08,128.71 L450.60,129.51 L453.12,130.29 L455.63,131.04 L458.15,131.77 L460.67,132.47 L463.18,133.14 L465.70,133.78 L468.22,134.40 L470.73,135.00 L473.25,135.57 L475.77,136.12 L478.28,136.65 L480.80,137.16 L483.32,137.65 L485.83,138.11 L488.35,138.56 L490.87,138.99 L493.38,139.40 L495.90,139.79 L498.42,140.17 L500.93,140.53 L503.45,140.87 L505.97,141.20 L508.48,141.52 L511.00,141.82 L513.52,142.11 L516.03,142.38 L518.55,142.64 L521.07,142.90 L523.58,143.14 L526.10,143.36 L528.62,143.58 L531.13,143.79 L533.65,143.99 L536.17,144.18 L538.68,144.36 L541.20,144.53 L543.72,144.70 L546.23,144.86 L548.75,145.01 L551.27,145.15 L553.78,145.28 L556.30,145.41 L558.82,145.54 L561.33,145.66 L563.85,145.77 L566.37,145.88 L568.88,145.98 L571.40,146.07 L573.92,146.17 L576.43,146.26 L578.95,146.34 L581.47,146.42 L583.98,146.50 L586.50,146.57 L589.02,146.64 L591.53,146.70 L594.05,146.77 L596.57,146.83 L599.08,146.88 L601.60,146.94 L604.12,146.99 L606.63,147.04 L609.15,147.08 L611.67,147.13 L614.18,147.17 L616.70,147.21 L619.22,147.25 L621.73,147.29 L624.25,147.32 L626.77,147.35 L629.28,147.38 L631.80,147.41 L634.32,147.44 L636.83,147.47 L639.35,147.50 L641.87,147.52 L644.38,147.54 L646.90,147.57 L649.42,147.59 L651.93,147.61 L654.45,147.63 L656.97,147.64 L659.48,147.66 L662.00,147.68" class="gradCurve_Wct8"></path><path d="M58.00,18.00 L60.52,20.16 L63.03,22.32 L65.55,24.48 L68.07,26.64 L70.58,28.80 L73.10,30.96 L75.62,33.12 L78.13,35.27 L80.65,37.43 L83.17,39.59 L85.68,41.75 L88.20,43.90 L90.72,46.06 L93.23,48.21 L95.75,50.37 L98.27,52.52 L100.78,54.67 L103.30,56.83 L105.82,58.98 L108.33,61.13 L110.85,63.28 L113.37,65.43 L115.88,67.58 L118.40,69.73 L120.92,71.88 L123.43,74.03 L125.95,76.17 L128.47,78.32 L130.98,80.46 L133.50,82.60 L136.02,84.74 L138.53,86.88 L141.05,89.02 L143.57,91.16 L146.08,93.30 L148.60,95.43 L151.12,97.56 L153.63,99.69 L156.15,101.82 L158.67,103.95 L161.18,106.08 L163.70,108.20 L166.22,110.32 L168.73,112.44 L171.25,114.56 L173.77,116.67 L176.28,118.79 L178.80,120.90 L181.32,123.00 L183.83,125.11 L186.35,127.21 L188.87,129.31 L191.38,131.40 L193.90,133.49 L196.42,135.58 L198.93,137.66 L201.45,139.74 L203.97,141.81 L206.48,143.88 L209.00,145.95 L211.52,148.01 L214.03,150.07 L216.55,152.12 L219.07,154.16 L221.58,156.20 L224.10,158.23 L226.62,160.26 L229.13,162.28 L231.65,164.29 L234.17,166.29 L236.68,168.29 L239.20,170.28 L241.72,172.26 L244.23,174.24 L246.75,176.20 L249.27,178.15 L251.78,180.10 L254.30,182.03 L256.82,183.96 L259.33,185.87 L261.85,187.77 L264.37,189.66 L266.88,191.54 L269.40,193.41 L271.92,195.26 L274.43,197.10 L276.95,198.92 L279.47,200.73 L281.98,202.52 L284.50,204.30 L287.02,206.07 L289.53,207.81 L292.05,209.54 L294.57,211.25 L297.08,212.94 L299.60,214.62 L302.12,216.27 L304.63,217.91 L307.15,219.52 L309.67,221.12 L312.18,222.69 L314.70,224.24 L317.22,225.77 L319.73,227.27 L322.25,228.76 L324.77,230.21 L327.28,231.65 L329.80,233.06 L332.32,234.45 L334.83,235.81 L337.35,237.14 L339.87,238.45 L342.38,239.74 L344.90,240.99 L347.42,242.22 L349.93,243.43 L352.45,244.61 L354.97,245.76 L357.48,246.88 L360.00,247.98 L362.52,249.05 L365.03,250.09 L367.55,251.10 L370.07,252.09 L372.58,253.05 L375.10,253.99 L377.62,254.90 L380.13,255.78 L382.65,256.63 L385.17,257.47 L387.68,258.27 L390.20,259.05 L392.72,259.81 L395.23,260.54 L397.75,261.24 L400.27,261.93 L402.78,262.59 L405.30,263.22 L407.82,263.84 L410.33,264.43 L412.85,265.00 L415.37,265.55 L417.88,266.08 L420.40,266.60 L422.92,267.09 L425.43,267.56 L427.95,268.02 L430.47,268.45 L432.98,268.87 L435.50,269.28 L438.02,269.66 L440.53,270.03 L443.05,270.39 L445.57,270.73 L448.08,271.06 L450.60,271.37 L453.12,271.67 L455.63,271.96 L458.15,272.24 L460.67,272.50 L463.18,272.75 L465.70,273.00 L468.22,273.23 L470.73,273.45 L473.25,273.66 L475.77,273.86 L478.28,274.05 L480.80,274.24 L483.32,274.41 L485.83,274.58 L488.35,274.74 L490.87,274.90 L493.38,275.04 L495.90,275.18 L498.42,275.32 L500.93,275.44 L503.45,275.56 L505.97,275.68 L508.48,275.79 L511.00,275.90 L513.52,276.00 L516.03,276.09 L518.55,276.18 L521.07,276.27 L523.58,276.35 L526.10,276.43 L528.62,276.51 L531.13,276.58 L533.65,276.65 L536.17,276.71 L538.68,276.77 L541.20,276.83 L543.72,276.89 L546.23,276.94 L548.75,276.99 L551.27,277.04 L553.78,277.09 L556.30,277.13 L558.82,277.17 L561.33,277.21 L563.85,277.25 L566.37,277.29 L568.88,277.32 L571.40,277.36 L573.92,277.39 L576.43,277.42 L578.95,277.44 L581.47,277.47 L583.98,277.50 L586.50,277.52 L589.02,277.54 L591.53,277.57 L594.05,277.59 L596.57,277.61 L599.08,277.63 L601.60,277.64 L604.12,277.66 L606.63,277.68 L609.15,277.69 L611.67,277.71 L614.18,277.72 L616.70,277.74 L619.22,277.75 L621.73,277.76 L624.25,277.77 L626.77,277.78 L629.28,277.79 L631.80,277.80 L634.32,277.81 L636.83,277.82 L639.35,277.83 L641.87,277.84 L644.38,277.85 L646.90,277.86 L649.42,277.86 L651.93,277.87 L654.45,277.88 L656.97,277.88 L659.48,277.89 L662.00,277.89" class="lossCurve_FAHe"></path><line x1="460.66666666666663" y1="18" x2="460.66666666666663" y2="278" class="marker_YiWp"></line><circle cx="460.66666666666663" cy="272.50205471837484" r="5" class="lossDot_ngLI"></circle><circle cx="460.66666666666663" cy="132.46520845180916" r="5" class="gradDot_pEwz"></circle><text x="360" y="314" text-anchor="middle" class="axisLabel_Yazw">reward margin Δ</text><text x="12" y="148" text-anchor="middle" transform="rotate(-90 12 148)" class="axisLabel_Yazw lossAxisLabel_ITiy">loss</text><text x="708" y="148" text-anchor="middle" transform="rotate(90 708 148)" class="axisLabel_Yazw gradAxisLabel_OHlC">gradient weight</text></svg></div><p class="hintLine_kKNP">Drag anywhere on the plot, or use the Δ slider with the arrow keys.</p><div class="readouts__tjv"><div class="readout_D9ns"><span class="readoutLabel_EsIV">Δ</span><span class="readoutValue_VS6z">2.00</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">Loss</span><span class="readoutValue_VS6z">0.1269</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">Gradient weight</span><span class="readoutValue_VS6z">0.1192</span><span class="readoutSub_DoT9">11.9% of maximum</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">σ(−βΔ)</span><span class="readoutValue_VS6z">0.1192</span><span class="readoutSub_DoT9">11.92%</span></div></div><p class="callout_aEDz calloutWarning_QSZU" role="status"><strong class="calloutTitle_nx3s">This pair teaches almost nothing.</strong>The model already ranks this pair correctly, so σ(−βΔ) is only 11.92% and the gradient is 11.9% of what a hard pair would give. This is why preference datasets full of obvious wins barely move the model: the easy pairs are silently ignored, and the few genuinely confusing pairs do all the work.</p></div>
<p>If this plot reminds you of DPO's gradient in chapter 4 — that is no coincidence.
DPO lifts this Bradley–Terry model wholesale, merely changing what plays the role of the reward.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-trust-region-made-visible-min--clip">The trust region made visible: min + clip<a href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo#the-trust-region-made-visible-min--clip" class="hash-link" aria-label="Direct link to The trust region made visible: min + clip" title="Direct link to The trust region made visible: min + clip" translate="no">​</a></h3>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-03-rlhf-ppo/clip-surrogate.light.svg" alt="A two-panel plot of the clipped surrogate objective against the probability ratio for positive and negative advantages, with the clipped regions shaded" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-03-rlhf-ppo/clip-surrogate.dark.svg" alt="A two-panel plot of the clipped surrogate objective against the probability ratio for positive and negative advantages, with the clipped regions shaded" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 3.1</span>Equation 3.3 drawn directly at ε = 0.2 — on the positive-Â side, the payoff is capped at 1+ε (the flat region = zero gradient), while on the negative-Â side the penalty has no floor, because min always picks the worse branch</p><div class="captionFooter_w00v"></div></figcaption></figure>
<p>Study the asymmetry closely — it is PPO's entire personality:
moving toward something good earns at most 20% per step, but hand too much probability to a bad token and you are hauled back at full force, every time.
The "flat region" in the figure is the trust region that lets PPO reuse old rollouts for several epochs without exploding.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="gaes-biasvariance-dial">GAE's bias–variance dial<a href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo#gaes-biasvariance-dial" class="hash-link" aria-label="Direct link to GAE's bias–variance dial" title="Direct link to GAE's bias–variance dial" translate="no">​</a></h3>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-03-rlhf-ppo/gae-lambda.light.svg" alt="Two panels: a synthetic rollout with a terminal reward, and GAE advantage curves at lambda 0, 0.5, 0.95 and 1.0" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-03-rlhf-ppo/gae-lambda.dark.svg" alt="Two panels: a synthetic rollout with a terminal reward, and GAE advantage curves at lambda 0, 0.5, 0.95 and 1.0" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 3.2</span>GAE on a synthetic 20-step rollout (γ = 1): per-step rewards are small noise, the real score arrives at the end, and V_ψ is deliberately set to under-predict by about 0.4 — at λ = 0 the signal never reaches the early tokens; at λ = 1 every token gets full credit along with full noise (an illustration of the mechanism, not real training data)</p><div class="captionFooter_w00v"></div></figcaption></figure>
<p>Read the lines from the bottom up: at <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>λ</mi><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">\lambda = 0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal">λ</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0</span></span></span></span> (green) the advantage hugs zero nearly everywhere —
the end-of-episode score <strong>never reaches</strong> the early tokens, because everything is filtered through a <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>V</mi><mi>ψ</mi></msub></mrow><annotation encoding="application/x-tex">V_\psi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9694em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.2222em">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.2222em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">ψ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span></span></span></span> that mispredicts.
At <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>λ</mi><mo>=</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">\lambda = 1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal">λ</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">1</span></span></span></span> (red), every token gets full credit from the ending but carries the accumulated noise of the whole trajectory with it.
<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>λ</mi><mo>=</mo><mn>0.95</mn></mrow><annotation encoding="application/x-tex">\lambda = 0.95</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal">λ</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0.95</span></span></span></span> (blue) is the middle ground the whole field has settled on — the signal travels far, but the noise is damped.</p>
<p>Before moving on, build your intuition for the word advantage — "how much better than expected" — by hand:
this tool uses the <strong>group mean</strong> as its baseline instead of <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>V</mi><mi>ψ</mi></msub></mrow><annotation encoding="application/x-tex">V_\psi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9694em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.2222em">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.2222em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">ψ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span></span></span></span> (a full-blown spoiler of chapter 5).
Press the <strong>All correct</strong> preset and watch what happens when every answer earns the same reward:</p>
<div class="root_H3ot"><div class="presetRow_LrTq"><span class="presetLabel_EB8R">Try a group:</span><button type="button" class="button_ioxi">Mixed group</button><button type="button" class="button_ioxi">All correct</button><button type="button" class="button_ioxi">All wrong</button><button type="button" class="button_ioxi">One lucky sample</button><button type="button" class="button_ioxi">Graded rewards</button></div><div class="layout_fs8s"><div class="editor_p1Ql"><p class="editorHeading_hHgi">Rewards r_i (G = 8)</p><ul class="rewardList_b99T"><li class="rewardItem_fDZp"><label class="rewardLabel_NOYj" for="llmcourse-ags-r0">r<sub>1</sub></label><input id="llmcourse-ags-r0" class="rewardInput_nfJ6" type="number" step="0.05" aria-label="Reward for sample 1" value="1"></li><li class="rewardItem_fDZp"><label class="rewardLabel_NOYj" for="llmcourse-ags-r1">r<sub>2</sub></label><input id="llmcourse-ags-r1" class="rewardInput_nfJ6" type="number" step="0.05" aria-label="Reward for sample 2" value="0"></li><li class="rewardItem_fDZp"><label class="rewardLabel_NOYj" for="llmcourse-ags-r2">r<sub>3</sub></label><input id="llmcourse-ags-r2" class="rewardInput_nfJ6" type="number" step="0.05" aria-label="Reward for sample 3" value="1"></li><li class="rewardItem_fDZp"><label class="rewardLabel_NOYj" for="llmcourse-ags-r3">r<sub>4</sub></label><input id="llmcourse-ags-r3" class="rewardInput_nfJ6" type="number" step="0.05" aria-label="Reward for sample 4" value="1"></li><li class="rewardItem_fDZp"><label class="rewardLabel_NOYj" for="llmcourse-ags-r4">r<sub>5</sub></label><input id="llmcourse-ags-r4" class="rewardInput_nfJ6" type="number" step="0.05" aria-label="Reward for sample 5" value="0"></li><li class="rewardItem_fDZp"><label class="rewardLabel_NOYj" for="llmcourse-ags-r5">r<sub>6</sub></label><input id="llmcourse-ags-r5" class="rewardInput_nfJ6" type="number" step="0.05" aria-label="Reward for sample 6" value="0"></li><li class="rewardItem_fDZp"><label class="rewardLabel_NOYj" for="llmcourse-ags-r6">r<sub>7</sub></label><input id="llmcourse-ags-r6" class="rewardInput_nfJ6" type="number" step="0.05" aria-label="Reward for sample 7" value="1"></li><li class="rewardItem_fDZp"><label class="rewardLabel_NOYj" for="llmcourse-ags-r7">r<sub>8</sub></label><input id="llmcourse-ags-r7" class="rewardInput_nfJ6" type="number" step="0.05" aria-label="Reward for sample 8" value="0"></li></ul><div class="editorButtons_PH4K"><button type="button" class="button_ioxi">Remove</button><button type="button" class="button_ioxi">Add sample</button></div><div class="control_Br1p"><label class="checkboxRow_XXA4" for="_R_4af6ldeh_"><input id="_R_4af6ldeh_" type="checkbox" aria-describedby="_R_4af6ldeh_-hint" checked=""><span>Divide by std (standard GRPO)</span></label><span class="controlHint_ilRY" id="_R_4af6ldeh_-hint">Unchecked is the Dr.GRPO variant: it keeps the centring but drops the std, removing the bias toward low-variance groups.</span></div></div><div class="svgWrap_mSxx"><svg class="svg_pLEH" viewBox="0 0 720 274" role="img" aria-label="Group-relative advantages for 8 samples. Mean reward 0.500, standard deviation 0.500."><g><text x="64" y="22" text-anchor="end" dominant-baseline="middle" class="rowLabel_hE3S">r1 = 1.00</text><rect x="389" y="10" width="307" height="18" rx="2" class="barPositive_Lzkj"></rect><text x="702" y="22" text-anchor="start" dominant-baseline="middle" class="valueLabel_T0Ct">1.00</text></g><g><text x="64" y="52" text-anchor="end" dominant-baseline="middle" class="rowLabel_hE3S">r2 = 0.00</text><rect x="82" y="40" width="307" height="18" rx="2" class="barNegative_Uoik"></rect><text x="76" y="52" text-anchor="end" dominant-baseline="middle" class="valueLabel_T0Ct">-1.00</text></g><g><text x="64" y="82" text-anchor="end" dominant-baseline="middle" class="rowLabel_hE3S">r3 = 1.00</text><rect x="389" y="70" width="307" height="18" rx="2" class="barPositive_Lzkj"></rect><text x="702" y="82" text-anchor="start" dominant-baseline="middle" class="valueLabel_T0Ct">1.00</text></g><g><text x="64" y="112" text-anchor="end" dominant-baseline="middle" class="rowLabel_hE3S">r4 = 1.00</text><rect x="389" y="100" width="307" height="18" rx="2" class="barPositive_Lzkj"></rect><text x="702" y="112" text-anchor="start" dominant-baseline="middle" class="valueLabel_T0Ct">1.00</text></g><g><text x="64" y="142" text-anchor="end" dominant-baseline="middle" class="rowLabel_hE3S">r5 = 0.00</text><rect x="82" y="130" width="307" height="18" rx="2" class="barNegative_Uoik"></rect><text x="76" y="142" text-anchor="end" dominant-baseline="middle" class="valueLabel_T0Ct">-1.00</text></g><g><text x="64" y="172" text-anchor="end" dominant-baseline="middle" class="rowLabel_hE3S">r6 = 0.00</text><rect x="82" y="160" width="307" height="18" rx="2" class="barNegative_Uoik"></rect><text x="76" y="172" text-anchor="end" dominant-baseline="middle" class="valueLabel_T0Ct">-1.00</text></g><g><text x="64" y="202" text-anchor="end" dominant-baseline="middle" class="rowLabel_hE3S">r7 = 1.00</text><rect x="389" y="190" width="307" height="18" rx="2" class="barPositive_Lzkj"></rect><text x="702" y="202" text-anchor="start" dominant-baseline="middle" class="valueLabel_T0Ct">1.00</text></g><g><text x="64" y="232" text-anchor="end" dominant-baseline="middle" class="rowLabel_hE3S">r8 = 0.00</text><rect x="82" y="220" width="307" height="18" rx="2" class="barNegative_Uoik"></rect><text x="76" y="232" text-anchor="end" dominant-baseline="middle" class="valueLabel_T0Ct">-1.00</text></g><line x1="389" y1="0" x2="389" y2="246" class="axisLine_LyoP"></line><text x="389" y="266" text-anchor="middle" class="axisLabel_Yazw">Â = 0 (no update)</text></svg></div></div><div class="readouts__tjv"><div class="readout_D9ns"><span class="readoutLabel_EsIV">mean(r)</span><span class="readoutValue_VS6z">0.5000</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">std(r)</span><span class="readoutValue_VS6z">0.5000</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">max |Â|</span><span class="readoutValue_VS6z">1.000</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">Formula</span><span class="readoutValue_VS6z">(r − μ) / σ</span><span class="readoutSub_DoT9">GRPO</span></div></div><p class="callout_aEDz" role="status"><strong class="calloutTitle_nx3s">Watch the std term.</strong>Dividing by std(r) = 0.500 rescales this whole group. A group that happened to be near-unanimous gets a large multiplier and dominates the update, even though it carries less information than a group that genuinely disagreed. Untick the box to see the same rewards without the rescaling.</p></div>
<p>Advantage zero across the whole group = no signal to learn from — hold onto that feeling for section 9.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-kl-leash-equation-32-drawn-as-a-picture">The KL leash: equation 3.2 drawn as a picture<a href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo#the-kl-leash-equation-32-drawn-as-a-picture" class="hash-link" aria-label="Direct link to The KL leash: equation 3.2 drawn as a picture" title="Direct link to The KL leash: equation 3.2 drawn as a picture" translate="no">​</a></h3>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-03-rlhf-ppo/kl-leash.light.svg" alt="A plot of proxy reward against KL showing a beta 0.05 path that stops at an equilibrium point and a beta 0 path that runs into the reward-hacking region" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-03-rlhf-ppo/kl-leash.dark.svg" alt="A plot of proxy reward against KL showing a beta 0.05 path that stops at an equilibrium point and a beta 0 path that runs into the reward-hacking region" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 3.3</span>Two training trajectories in the (KL, reward) plane — β = 0.05 climbs and then stops at the point where the marginal gain equals the penalty, while β = 0 has no stopping point and runs right into reward-hacking territory (an illustration of the failure mode's mechanism — the actual measured curves are in section 8)</p><div class="captionFooter_w00v"></div></figcaption></figure>
<p>The green line's stopping point is not a guess — it is the mathematics of equation 3.2:
optimization stops exactly where <strong>the reward gained per nat equals β</strong>; walking further loses money.
When <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">\beta = 0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0</span></span></span></span> that stopping condition doesn't exist — every nat of wandering that buys even a sliver of reward is "profit,"
so the model keeps running out of natural language for as long as the reward number still twitches upward.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-setting-up-the-environment">5. Setting Up the Environment<a href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo#5-setting-up-the-environment" class="hash-link" aria-label="Direct link to 5. Setting Up the Environment" title="Direct link to 5. Setting Up the Environment" translate="no">​</a></h2>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>The real scale of what we are miniaturizing — read before running</div><div class="admonitionContent_BuS1"><p>Production RLHF holds 4 models of which the largest is usually 7B or more, uses real human preference pairs
numbering in the <strong>tens of thousands to millions</strong>, and splits generation (a rollout fleet) onto machines separate from training.
This notebook uses Qwen3-0.6B in every slot, 100 preference pairs, and 64 math problems.
What it demonstrates is <strong>the algorithm with every part present</strong> — not real RLHF.
The results will prove the mechanism, not that the model got better for real use.</p></div></div>
<p>Open Colab and pick <strong>Runtime → Change runtime type → T4 GPU</strong> (the free tier is enough).</p>
<div class="theme-admonition theme-admonition-danger admonition_xJq3 alert alert--danger"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"></path></svg></span>The series-wide warning worth re-reading every chapter</div><div class="admonitionContent_BuS1"><p>The Colab T4 is Turing architecture (SM 7.5), which <strong>does not support bfloat16</strong> and <strong>does not support FlashAttention-2</strong>.</p><p>But Qwen3-0.6B's <code>config.json</code> declares <code>torch_dtype: bfloat16</code>.
So <code>torch_dtype="auto"</code> is <strong>a trap</strong> — your code will crash or run bizarrely slowly without telling you why.</p><div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">torch_dtype</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">float16      </span><span class="token comment" style="color:#999988;font-style:italic"># not bfloat16</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">attn_implementation</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"sdpa"</span><span class="token plain">     </span><span class="token comment" style="color:#999988;font-style:italic"># not flash_attention_2</span><br></span></code></pre></div></div><p>And this chapter in particular carries one extra fp16 bomb, named ratio overflow — wait for section 7.</p></div></div>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">cap </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_device_capability</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"compute capability:"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cap</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                    </span><span class="token comment" style="color:#999988;font-style:italic"># T4 = (7, 5)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"native bf16:"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cap</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&gt;=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                   </span><span class="token comment" style="color:#999988;font-style:italic"># T4 -&gt; False</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"torch says   :"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">is_bf16_supported</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># T4 -&gt; True (counts emulation!)</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span><code>is_bf16_supported()</code> lies on a T4</div><div class="admonitionContent_BuS1"><p>Recent torch returns <code>True</code> on a T4 because it counts <strong>emulation</strong> as support — which is far slower than fp16.
Gate on <strong>compute capability ≥ 8.0</strong> (Ampere and up) instead. This was a real bug, caught only by running the notebook on Colab.</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-rlhf-is-expensive-in-one-picture">Why RLHF is expensive, in one picture<a href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo#why-rlhf-is-expensive-in-one-picture" class="hash-link" aria-label="Direct link to Why RLHF is expensive, in one picture" title="Direct link to Why RLHF is expensive, in one picture" translate="no">​</a></h3>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-03-rlhf-ppo/four-models.light.svg" alt="A horizontal bar chart comparing the VRAM of PPO with four fully loaded models against a LoRA setup where the policy and reference share base weights" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-03-rlhf-ppo/four-models.dark.svg" alt="A horizontal bar chart comparing the VRAM of PPO with four fully loaded models against a LoRA setup where the policy and reference share base weights" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 3.4</span>The four models that must sit in VRAM at once during one PPO step, computed from Qwen3-0.6B's real parameter count (fp16, weights only) — LoRA lets the policy and reference share a single base, saving one entire model</p><div class="captionFooter_w00v"></div></figcaption></figure>
<p>The numbers in the figure are just the <strong>weights</strong> — before activations, the KV cache during generation, gradients, and optimizer state.
And at 0.6B everything still looks tiny, but the ×4 multiplier goes nowhere as you scale: at 7B it is 56 GB before you've done anything at all.</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>The payoff from chapter 2 (round two)</div><div class="admonitionContent_BuS1"><p>Our policy is the base + the LoRA adapter from chapter 2, and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>π</mi><mtext>ref</mtext></msub></mrow><annotation encoding="application/x-tex">\pi_{\text{ref}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">ref</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> is that same base <strong>with the adapter off</strong> —
called inside <code>policy.disable_adapter()</code>. The reference model costs <strong>zero additional bytes</strong> of VRAM.
Chapter 4 will use this same move again for DPO; it is the architectural reason this series chose LoRA from the start.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-preparing-the-data">6. Preparing the Data<a href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo#6-preparing-the-data" class="hash-link" aria-label="Direct link to 6. Preparing the Data" title="Direct link to 6. Preparing the Data" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="stage-a--preference-pairs-for-the-reward-model">Stage A — preference pairs for the reward model<a href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo#stage-a--preference-pairs-for-the-reward-model" class="hash-link" aria-label="Direct link to Stage A — preference pairs for the reward model" title="Direct link to Stage A — preference pairs for the reward model" translate="no">​</a></h3>
<p>We use <strong><code>iapp/dpo_thai_tutorial</code></strong> (100 pairs, Apache-2.0) —
a Thai preference dataset I built myself for this series and released for free reuse.
Each row has <code>prompt</code>, <code>chosen</code>, <code>rejected</code>, hand-curated with an emphasis on politeness and natural-sounding Thai.</p>
<p>Split <strong>80/20</strong>: train on 80 pairs, hold 20 pairs out — never touched during training.
Stage A's passing bar is <strong>pairwise ranking accuracy on the 20 held-out pairs, with a Wilson 95% CI that does not straddle 0.5</strong>
— "significantly better than a coin flip" already proves the mechanism, because 20 pairs genuinely cannot make the CI any tighter.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="stage-b--problems-a-rule-can-grade">Stage B — problems a rule can grade<a href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo#stage-b--problems-a-rule-can-grade" class="hash-link" aria-label="Direct link to Stage B — problems a rule can grade" title="Direct link to Stage B — problems a rule can grade" translate="no">​</a></h3>
<p>For the PPO loop we use 64 math problems from <strong><code>VISAI-AI/gsm8k-thai</code></strong> (a Thai translation of GSM8K)
and score them with a <strong>verifiable rule</strong> instead of the Stage A reward model:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> re</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">rule_reward</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">response</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">str</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> gold</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token triple-quoted-string string" style="color:#e3116c">"""+1.0 if the last integer in the answer is correct, +0.2 if the answer is actually in Thai"""</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    nums </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> re</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">findall</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">r"-?\d+"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> response</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">replace</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">","</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    correct </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1.0</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> nums </span><span class="token keyword" style="color:#00009f">and</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">nums</span><span class="token punctuation" style="color:#393A34">[</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> gold </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.0</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    thai </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">sum</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"\u0e01"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;=</span><span class="token plain"> ch </span><span class="token operator" style="color:#393A34">&lt;=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"\u0e5b"</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> ch </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> response</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># the Thai Unicode block, U+0E01..U+0E5B</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    thai_bonus </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.2</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> thai </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> </span><span class="token builtin">max</span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin">len</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">response</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&gt;=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.5</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.0</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> correct </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> thai_bonus</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>Why Stage B doesn't use the reward model from Stage A</div><div class="admonitionContent_BuS1"><p>In a real system, Stage B consumes Stage A's output directly — that is the definition of RLHF.
But an RM trained on 100 pairs is <strong>far too weak to withstand PPO's pressure</strong>: it would get hacked within a few updates,
and then we couldn't tell whether our PPO loop was wrong or the RM was merely weak — the experiment would prove nothing at all.</p><p>So the rule reward serves as a <strong>verifiable stand-in for the RM</strong>: when the reward climbs, we know for certain the loop is working.
But it remains "imperfect" just like every RM — it measures only the final number and the fraction of Thai characters,
not the readability of anything around them, and that is exactly the loophole the <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">\beta = 0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0</span></span></span></span> experiment in section 8 will poke at.
(This idea of rule-verifiable rewards returns as the full-time protagonist in chapter 5.)</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-the-main-code">7. The Main Code<a href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo#7-the-main-code" class="hash-link" aria-label="Direct link to 7. The Main Code" title="Direct link to 7. The Main Code" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="71-stage-a--training-a-real-reward-model-6-minutes">7.1 Stage A — training a real reward model (~6 minutes)<a href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo#71-stage-a--training-a-real-reward-model-6-minutes" class="hash-link" aria-label="Direct link to 7.1 Stage A — training a real reward model (~6 minutes)" title="Direct link to 7.1 Stage A — training a real reward model (~6 minutes)" translate="no">​</a></h3>
<p>Turn a language model into a scoring machine: the LM head is replaced with a single linear layer returning one scalar.</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">nn</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">functional </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> F</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> transformers </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> AutoModelForSequenceClassification</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> AutoTokenizer</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> peft </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> LoraConfig</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> get_peft_model</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">tok </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> AutoTokenizer</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">from_pretrained</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Qwen/Qwen3-0.6B"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">rm </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> AutoModelForSequenceClassification</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">from_pretrained</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"Qwen/Qwen3-0.6B"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    num_labels</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">                          </span><span class="token comment" style="color:#999988;font-style:italic"># scalar head: one score per text</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    torch_dtype</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">float16</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">             </span><span class="token comment" style="color:#999988;font-style:italic"># the T4 has no bf16</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    attn_implementation</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"sdpa"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">rm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">config</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">pad_token_id </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> tok</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">pad_token_id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">rm </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> get_peft_model</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">rm</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> LoraConfig</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    task_type</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"SEQ_CLS"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> r</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">8</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> lora_alpha</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">16</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    target_modules</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"q_proj"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"k_proj"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"v_proj"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"o_proj"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    modules_to_save</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"score"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">             </span><span class="token comment" style="color:#999988;font-style:italic"># the score head starts random — must be trained in full</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> p </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> rm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">parameters</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> p</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">requires_grad</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        p</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">data </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> p</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">data</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">            </span><span class="token comment" style="color:#999988;font-style:italic"># the fp16 lesson from chapter 1: train in fp32</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">rm_loss</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">chosen</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> rejected</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    s_w </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> rm</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">**</span><span class="token plain">chosen</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">logits</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">squeeze</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">     </span><span class="token comment" style="color:#999988;font-style:italic"># r_φ(x, y_w)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    s_l </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> rm</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">**</span><span class="token plain">rejected</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">logits</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">squeeze</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># r_φ(x, y_l)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token plain">F</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">logsigmoid</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">s_w </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> s_l</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">mean</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># equation 3.1, literally</span><br></span></code></pre></div></div>
<p>Train 3 epochs on 80 pairs, then measure pairwise accuracy on the 20 held-out pairs with a Wilson CI.
If the CI clears 0.5 — you have just trained the first reward model of your life, on 80 rows of data.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="72-stage-b--ppo-written-from-scratch-120-lines-10-minutes">7.2 Stage B — PPO written from scratch, ~120 lines (~10 minutes)<a href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo#72-stage-b--ppo-written-from-scratch-120-lines-10-minutes" class="hash-link" aria-label="Direct link to 7.2 Stage B — PPO written from scratch, ~120 lines (~10 minutes)" title="Direct link to 7.2 Stage B — PPO written from scratch, ~120 lines (~10 minutes)" translate="no">​</a></h3>
<p>We will <strong>not use TRL's <code>PPOTrainer</code></strong>, and this is a deliberate decision, not stubbornness:
TRL has moved <code>PPOTrainer</code> into <code>trl.experimental</code> and announced plans to remove it in 0.29.0 —
code taught against that library will stop running within a few months.
A self-written PPO loop of about 120 lines will run for as long as PyTorch exists.
And more important still: write it yourself and you will <strong>know</strong> what every line does, just like the 25-line DPO loss in chapter 4.</p>
<p>The pieces on the board: policy = base + LoRA adapter (from chapter 2, <code>is_trainable=True</code>),
reference = the same base with the adapter off, and a value head — a two-layer MLP plugged into the final hidden state:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">value_head </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">nn</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Sequential</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">nn</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Linear</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1024</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1024</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">nn</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Tanh</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">nn</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Linear</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1024</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                           </span><span class="token comment" style="color:#999988;font-style:italic"># ~1M parameters — tiny next to the other three</span><br></span></code></pre></div></div>
<p>The rollout: sample answers 8 prompts at a time (<code>max_new_tokens=200</code>, <code>do_sample=True</code>),
score with <code>rule_reward</code>, <strong>standardize the scores within the batch</strong>, and <strong>store the rollout-time log-probs, detached, immediately</strong>.
Then enter this update loop — the 40 lines that are the heart of the whole chapter (the full loop is in the notebook):</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">compute_gae</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">rewards</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> values</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> gamma</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1.0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> lam</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.95</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token triple-quoted-string string" style="color:#e3116c">"""rewards: [T] for one response, values: [T+1] (last slot = 0 after the end)"""</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    adv</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> acc </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">zeros_like</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">rewards</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.0</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> t </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token builtin">reversed</span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin">range</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">rewards</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">shape</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        delta </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> rewards</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">t</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> gamma </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> values</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">t </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> values</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">t</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># equation 3.4</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        acc </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> delta </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> gamma </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> lam </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> acc                          </span><span class="token comment" style="color:#999988;font-style:italic"># Â_t = δ_t + γλ Â_{t+1}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        adv</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">t</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> acc</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> adv</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">ppo_update</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">rollout</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> eps</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> beta</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.05</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    out </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> policy</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">rollout</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ids</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> output_hidden_states</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">         </span><span class="token comment" style="color:#999988;font-style:italic"># one forward, two outputs</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    logp </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> gather_logprobs</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">out</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">logits</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> rollout</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ids</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">              </span><span class="token comment" style="color:#999988;font-style:italic"># [B, T], carries gradient</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">with</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">no_grad</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> policy</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">disable_adapter</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        logp_ref </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> gather_logprobs</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">policy</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">rollout</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ids</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">logits</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> rollout</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ids</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># per-token reward = KL penalty at every position + task score at the last token (equation 3.2)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    rew </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token plain">beta </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">logp</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">detach</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> logp_ref</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    rew</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">:</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+=</span><span class="token plain"> rollout</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">scores_std                 </span><span class="token comment" style="color:#999988;font-style:italic"># the standardized rule score</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    hidden </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> out</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">hidden_states</span><span class="token punctuation" style="color:#393A34">[</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">detach</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># detach = cut the gradient into the trunk</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    values </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> value_head</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">hidden</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">squeeze</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    adv </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">stack</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">compute_gae</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">r</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> F</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">pad</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">v</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> r</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> v </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token builtin">zip</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">rew</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> values</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    adv </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">adv </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> adv</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">mean</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">adv</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">std</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1e-8</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># standardize the advantage once more</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    log_ratio </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">logp </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> rollout</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">logp_old</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">clamp</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># prevents fp16 overflow!</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ratio </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> log_ratio</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">exp</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                                </span><span class="token comment" style="color:#999988;font-style:italic"># ρ_t (equation 3.3)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    surr </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">min</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ratio </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> adv</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">detach</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                     ratio</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">clamp</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> eps</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> eps</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> adv</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">detach</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    returns </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">adv </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> values</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">detach</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                </span><span class="token comment" style="color:#999988;font-style:italic"># the value head's target</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    m </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> rollout</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">resp_mask                            </span><span class="token comment" style="color:#999988;font-style:italic"># count response tokens only</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    loss </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">-</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">surr </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> m</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">sum</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.5</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">values </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> returns</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">pow</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> m</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">sum</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> m</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">sum</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> loss                                      </span><span class="token comment" style="color:#999988;font-style:italic"># (the entropy term is in the notebook)</span><br></span></code></pre></div></div>
<p>Settings: <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ϵ</mi><mo>=</mo><mn>0.2</mn></mrow><annotation encoding="application/x-tex">\epsilon = 0.2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">ϵ</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0.2</span></span></span></span>, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi><mo>=</mo><mn>0.05</mn></mrow><annotation encoding="application/x-tex">\beta = 0.05</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0.05</span></span></span></span>, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>γ</mi><mo>=</mo><mn>1.0</mn></mrow><annotation encoding="application/x-tex">\gamma = 1.0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0556em">γ</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">1.0</span></span></span></span>, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>λ</mi><mo>=</mo><mn>0.95</mn></mrow><annotation encoding="application/x-tex">\lambda = 0.95</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal">λ</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0.95</span></span></span></span>,
<strong>4 epochs per rollout</strong> on 64 prompts, adapter LR <code>1e-5</code>, value head LR <code>1e-4</code>.
Roughly 10 minutes per run on a T4 (the notebook runs twice: <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi><mo>=</mo><mn>0.05</mn></mrow><annotation encoding="application/x-tex">\beta = 0.05</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0.05</span></span></span></span> and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">\beta = 0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0</span></span></span></span>).</p>
<div class="theme-admonition theme-admonition-danger admonition_xJq3 alert alert--danger"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"></path></svg></span>Three lines that void the entire experiment if you miss them</div><div class="admonitionContent_BuS1"><p><strong>1. <code>.clamp(-10, 10)</code> before <code>.exp()</code></strong> — in fp16, <code>exp(12)</code> = 162,754, past the fp16 ceiling (65,504).
The result is <code>inf</code>, which turns into <code>NaN</code> and spreads through the whole batch within a single step.</p><p><strong>2. <code>logp_old</code> must be computed once at rollout time and stored detached</strong> — never recomputed inside the epoch loop.
Recompute it and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>ρ</mi><mi>t</mi></msub><mo>=</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">\rho_t = 1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord"><span class="mord mathnormal">ρ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">1</span></span></span></span> always, the clip never fires, and your PPO silently degrades into REINFORCE with no error whatsoever.</p><p><strong>3. Standardize scores before use</strong> — equation 3.1 already told you the reward's scale is undefined.
Yes, our rule reward lives between 0 and 1.2, but this habit has to stick for when you use a real RM whose scale can be anything it likes.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="8-results">8. Results<a href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo#8-results" class="hash-link" aria-label="Direct link to 8. Results" title="Direct link to 8. Results" translate="no">​</a></h2>
<p>The notebook tracks <strong>three curves simultaneously</strong> at every update and writes them to <code>results.json</code>:</p>
<ol>
<li class=""><strong>Mean reward per rollout</strong> — should climb (this is what we're buying)</li>
<li class=""><strong>Per-token KL against <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>π</mi><mtext>ref</mtext></msub></mrow><annotation encoding="application/x-tex">\pi_{\text{ref}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">ref</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span></strong> — should grow and then <strong>saturate</strong> under the ceiling that <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi></mrow><annotation encoding="application/x-tex">\beta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span></span></span></span> sets (this is the price we pay)</li>
<li class=""><strong>Mean answer length</strong> — the disease detector: length spiking or cratering abnormally is the first sign of a policy going strange</li>
</ol>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>Never read the reward curve without the KL curve beside it — ever</div><div class="admonitionContent_BuS1"><p>A climbing reward means nothing at all if you don't know what the model paid in exchange.
Reward up + KL saturating = learning under the leash.
Reward up + KL climbing without limit = fleeing language itself, heading for a loophole in the judge.
The same curve in a different context means precisely the opposite thing — this is the measured version of figure 3.3.</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="catching-reward-hacking-red-handed">Catching reward hacking red-handed<a href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo#catching-reward-hacking-red-handed" class="hash-link" aria-label="Direct link to Catching reward hacking red-handed" title="Direct link to Catching reward hacking red-handed" translate="no">​</a></h3>
<p>The notebook's second run sets <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">\beta = 0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0</span></span></span></span> and touches absolutely nothing else — no leash.
The pattern <strong>expected</strong>: reward climbs as fast or faster, but KL soars with no ceiling,
and the answers start to degenerate — repetitive, oddly short, or collapsing into a fixed formula with a number stuffed at the end of the sentence.
Because <code>rule_reward</code> sees only the final number and the fraction of Thai characters, everything it cannot see is free for the model to throw away.</p>
<p>Samples of degenerate answers from the <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">\beta = 0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0</span></span></span></span> run are printed by the notebook's final cell:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[This space is filled only from the notebook's real run — I will not invent degenerate</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> examples myself, because this whole series stands on the rule that no number or output</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> is ever made up. Run the notebook and the "hacking exhibits" cell will show 2-3 real</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> answers along with their KL.]</span><br></span></code></pre></div></div>
<div class="root_IS5b"><div class="picker_cO8e"><span class="pickerLabel_sE2x" id="llmcourse-bac-picker">Prompt</span><div class="pickerButtons_j7L1" role="tablist" aria-labelledby="llmcourse-bac-picker"><button type="button" role="tab" id="llmcourse-bac-tab-0" aria-selected="true" aria-controls="llmcourse-bac-panel-0" tabindex="0" class="pickerButton_gFO3 pickerButtonActive_xIUp">1</button><button type="button" role="tab" id="llmcourse-bac-tab-1" aria-selected="false" aria-controls="llmcourse-bac-panel-1" tabindex="-1" class="pickerButton_gFO3">2</button></div></div><blockquote class="prompt_O4Wp" lang="th"><span class="promptLabel_h2F6">Prompt</span>อธิบายว่าทำไมท้องฟ้าถึงเป็นสีฟ้า แบบสั้น ๆ</blockquote><div class="grid_h_9T" id="llmcourse-bac-panel-0" role="tabpanel" aria-labelledby="llmcourse-bac-tab-0" style="grid-template-columns:repeat(auto-fit, minmax(min(100%, 260px), 1fr))"><article class="card_S27b"><header class="cardHeader_w7wJ"><h4 class="cardTitle_NUQN">base</h4><div class="badges_pXcS"><span class="badge_wUaQ badgeBad_WFwi" title="Share of non-whitespace characters that are Thai script">Thai 18%</span><span class="badge_wUaQ">41 tokens</span></div></header><div class="output_VSGg" lang="th">The sky appears blue because of Rayleigh scattering. ท้องฟ้า is blue เพราะ light scatter ครับ. Shorter wavelengths scatter more than longer ones.</div></article><article class="card_S27b"><header class="cardHeader_w7wJ"><h4 class="cardTitle_NUQN">sft</h4><div class="badges_pXcS"><span class="badge_wUaQ badgeGood_MHH_" title="Share of non-whitespace characters that are Thai script">Thai 99%</span><span class="badge_wUaQ">78 tokens</span></div></header><div class="output_VSGg" lang="th">ท้องฟ้าเป็นสีฟ้าเพราะแสงอาทิตย์กระทบกับโมเลกุลของอากาศแล้วเกิดการกระเจิงแบบเรย์ลี ซึ่งแสงสีน้ำเงินที่มีความยาวคลื่นสั้นกว่าจะกระเจิงได้มากกว่าแสงสีแดง เราจึงมองเห็นท้องฟ้าเป็นสีฟ้าครับ</div></article></div><p class="status_mfC7">Showing the built-in sample.</p></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="9-comparison">9. Comparison<a href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo#9-comparison" class="hash-link" aria-label="Direct link to 9. Comparison" title="Direct link to 9. Comparison" translate="no">​</a></h2>
<p>The notebook runs the same battery over three systems on held-out Thai math problems (TH-MATH):</p>
<table><thead><tr><th>Model</th><th>TH-MATH acc (95% CI)</th><th>Final mean KL</th><th>Mean answer length</th><th>Training time</th></tr></thead><tbody><tr><td>SFT from chapter 2 (starting point)</td><td>baseline</td><td>0</td><td>baseline</td><td>—</td></tr><tr><td>PPO, β = 0.05</td><td>?</td><td>? (should saturate)</td><td>?</td><td>~10 min</td></tr><tr><td>PPO, β = 0 (ablation)</td><td>?</td><td>? (should soar)</td><td>?</td><td>~10 min</td></tr></tbody></table>
<p>The pattern you <strong>should expect</strong>: the <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi><mo>=</mo><mn>0.05</mn></mrow><annotation encoding="application/x-tex">\beta = 0.05</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0.05</span></span></span></span> row nudges accuracy up slightly or holds even, with KL steady,
while the <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">\beta = 0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0</span></span></span></span> row earns <strong>the highest training-time rule reward</strong> but should not do better on held-out accuracy —
and the language breaks. Cheated points do not transfer to new problems.</p>
<p>If you see something else, read it this way:</p>
<ul>
<li class=""><strong>All three rows nearly identical</strong> → the advantage was near zero throughout; check whether rewards within each batch have any spread (if the model gets every problem wrong, the post-standardization advantage is pure noise — the same feeling as the All wrong preset in section 4's tool)</li>
<li class=""><strong>β = 0 but KL doesn't soar</strong> → 4 epochs times 64 prompts is too short for the hack to ripen — extend the run and look again; don't conclude "no hacking" just yet</li>
<li class=""><strong>β = 0.05 but KL soars anyway</strong> → almost certainly the scores weren't standardized, letting the reward's scale swamp β — or the LR is too high</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="traps-to-watch-for">Traps to watch for<a href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo#traps-to-watch-for" class="hash-link" aria-label="Direct link to Traps to watch for" title="Direct link to Traps to watch for" translate="no">​</a></h3>
<p><strong>1. Not standardizing the reward</strong>
The reward's scale is undefined per equation 3.1 — two RMs that rank identically can differ in scale by a factor of ten.
That scale multiplies straight into the advantage and the gradient: a run that used to be stable explodes the moment you swap RMs.</p>
<p><strong>2. Recomputing <code>logp_old</code> inside the update loop</strong>
The quietest bug of the chapter: no error, the loss looks normal, but <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>ρ</mi><mi>t</mi></msub><mo>=</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">\rho_t = 1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord"><span class="mord mathnormal">ρ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">1</span></span></span></span> forever.
The clip never fires, the trust region doesn't exist — you are running REINFORCE while believing you are running PPO.</p>
<p><strong>3. Sharing the trunk between policy and value without a stop-gradient</strong>
The value loss has a large scale and flows back into the backbone, overwriting language ability.
This notebook guards against it with <code>.detach()</code> on the hidden state (visible in the section 7.2 code) — a one-line price.</p>
<p><strong>4. fp16 ratio overflow</strong>
<code>exp(log_ratio)</code> can blow past the fp16 ceiling starting around log-ratio 11.1.
Always <code>.clamp(-10, 10)</code> before <code>.exp()</code> — and if you see log-ratios reaching that level often,
it is a sign the policy is fleeing the old rollout too fast (lower the LR or the number of epochs per rollout).</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="10-summary">10. Summary<a href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo#10-summary" class="hash-link" aria-label="Direct link to 10. Summary" title="Direct link to 10. Summary" translate="no">​</a></h2>
<ul>
<li class=""><strong>RLHF = a two-step detour</strong> to optimize what cannot be differentiated: train a judge (<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>r</mi><mi>ϕ</mi></msub></mrow><annotation encoding="application/x-tex">r_\phi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7167em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">ϕ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span></span></span></span>), then use RL to run toward the judge's score</li>
<li class=""><strong>Bradley–Terry sees only differences</strong> — a reward's absolute scale is undefined, so always standardize before use</li>
<li class=""><strong>The master equation <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>max</mi><mo>⁡</mo><mtext>&nbsp;</mtext><mi mathvariant="double-struck">E</mi><mo stretchy="false">[</mo><mi>r</mi><mo stretchy="false">]</mo><mo>−</mo><mi>β</mi><mtext> </mtext><msub><mi mathvariant="double-struck">D</mi><mtext>KL</mtext></msub></mrow><annotation encoding="application/x-tex">\max\ \mathbb{E}[r] - \beta\,\mathbb{D}_{\text{KL}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mop">max</span><span class="mspace">&nbsp;</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathbb">E</span><span class="mopen">[</span><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="mclose">]</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathbb">D</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">KL</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> is the one equation to memorize</strong> — chapters 4 and 5 are this same equation solved by other means</li>
<li class=""><strong>KL is not a regularizer</strong> — it is the system's only stopping condition; the moment you remove it, Goodhart goes to work</li>
<li class=""><strong>min + clip = pessimism by design</strong>: gains capped, losses uncapped — the trust region that makes old rollouts reusable</li>
<li class=""><strong>GAE is the bias–variance dial</strong>, and the <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>V</mi><mi>ψ</mi></msub></mrow><annotation encoding="application/x-tex">V_\psi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9694em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.2222em">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.2222em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">ψ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span></span></span></span> it leans on is the fourth model — the one GRPO deletes in chapter 5</li>
<li class=""><strong>PPO is expensive by structure, not by badly written code</strong>: 4 models + around ten hyperparameters is the sticker price</li>
</ul>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>Limitations of this experiment</div><div class="admonitionContent_BuS1"><p><strong>64 prompts and a rule-based reward are a demonstration of the algorithm, not RLHF.</strong>
Our rule reward is only a stand-in for the Stage A reward model —
real RLHF uses RMs at the 7B+ level trained on tens of thousands to millions of human preference pairs,
and needs a separate rollout fleet, because generation consumes several times more compute than the updates.</p><p>What this experiment genuinely proves is two things: <strong>the hand-written PPO loop works correctly</strong> (reward climbs under the KL leash),
and <strong>the mechanism of reward hacking is real</strong> (remove β and it is measured, not merely narrated).
Don't cite these results as an aligned Thai model — what you gain is an understanding of how the entire machine turns,
which is exactly what the next two chapters require.</p></div></div>
<p><strong>Next chapter:</strong> <a class="" href="https://kobkrit.com/en/blog/llm-04-dpo">DPO</a> — DPO deletes both the reward model and the RL loop <strong>with pure algebra</strong>, starting from master equation 3.2, the one you just memorized.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="references">References<a href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo#references" class="hash-link" aria-label="Direct link to References" title="Direct link to References" translate="no">​</a></h2>
<ol>
<li class="">Schulman et al. (2017). <a href="https://arxiv.org/abs/1707.06347" target="_blank" rel="noopener noreferrer" class="">Proximal Policy Optimization Algorithms</a> — the original PPO paper: the clipped surrogate in section 3</li>
<li class="">Schulman et al. (2015). <a href="https://arxiv.org/abs/1506.02438" target="_blank" rel="noopener noreferrer" class="">High-Dimensional Continuous Control Using Generalized Advantage Estimation</a> — GAE: the advantage estimator PPO uses</li>
<li class="">Christiano et al. (2017). <a href="https://arxiv.org/abs/1706.03741" target="_blank" rel="noopener noreferrer" class="">Deep reinforcement learning from human preferences</a> — the paper that started RL from human preferences</li>
<li class="">Stiennon et al. (2020). <a href="https://arxiv.org/abs/2009.01325" target="_blank" rel="noopener noreferrer" class="">Learning to summarize from human feedback</a> — the first convincingly working RLHF, on summarization</li>
<li class="">Ouyang et al. (2022). <a href="https://arxiv.org/abs/2203.02155" target="_blank" rel="noopener noreferrer" class="">Training language models to follow instructions with human feedback</a> — InstructGPT: the origin of the whole SFT -&gt; RM -&gt; PPO pipeline</li>
<li class="">Bai et al. (2022). <a href="https://arxiv.org/abs/2204.05862" target="_blank" rel="noopener noreferrer" class="">Training a Helpful and Harmless Assistant with Reinforcement Learning from Human Feedback</a> — the helpful/harmless dataset and its lessons about KL</li>
<li class="">Zheng et al. (2023). <a href="https://arxiv.org/abs/2307.04964" target="_blank" rel="noopener noreferrer" class="">Secrets of RLHF in Large Language Models Part I: PPO</a> — the PPO implementation details other papers omit</li>
<li class="">Bradley &amp; Terry (1952). <a href="https://doi.org/10.2307/2334029" target="_blank" rel="noopener noreferrer" class="">Rank Analysis of Incomplete Block Designs: I. The Method of Paired Comparisons</a> — the Bradley-Terry model every reward model rests on</li>
</ol>
<hr>
<p><em>The writing, code and notebooks in this series are licensed under <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank" rel="noopener noreferrer" class="">CC BY-NC-SA 4.0</a> — reuse and adapt them freely with attribution, for non-commercial purposes, and share your adaptations under the same terms. Third-party models and datasets referenced here keep their own licences.</em></p>
<nav class="nav_RfLT" aria-label="Thai LLM tutorial series navigation"><p class="heading_XRWm">Thai LLM series<span class="progress_f8e8">Part 3 of 10</span></p><ol class="list_U31a"><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-01-continue-pretraining"><span class="number_u3BE" aria-hidden="true">1</span><span class="title_BPvL">Continue Pretraining</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-02-sft-lora"><span class="number_u3BE" aria-hidden="true">2</span><span class="title_BPvL">SFT and LoRA</span></a></li><li class="item_Y10l"><span class="chip_DDpP chipCurrent_BGpo" aria-current="step"><span class="number_u3BE" aria-hidden="true">3</span><span class="title_BPvL">RLHF and PPO</span><span class="srOnly_owtF">(you are here)</span></span></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-04-dpo"><span class="number_u3BE" aria-hidden="true">4</span><span class="title_BPvL">DPO: Direct Preference Optimization</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-05-grpo"><span class="number_u3BE" aria-hidden="true">5</span><span class="title_BPvL">GRPO</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-06-context-distillation"><span class="number_u3BE" aria-hidden="true">6</span><span class="title_BPvL">Context Distillation</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-07-model-distillation"><span class="number_u3BE" aria-hidden="true">7</span><span class="title_BPvL">Model Distillation</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-08-guardrails"><span class="number_u3BE" aria-hidden="true">8</span><span class="title_BPvL">Guardrails</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-09-benchmarking"><span class="number_u3BE" aria-hidden="true">9</span><span class="title_BPvL">Benchmarking</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-10-deployment"><span class="number_u3BE" aria-hidden="true">10</span><span class="title_BPvL">Deployment</span></a></li></ol></nav>]]></content>
        <author>
            <name>Kobkrit Viriyayudhakorn</name>
            <uri>https://github.com/kobkrit</uri>
        </author>
        <category label="ai" term="ai"/>
        <category label="llm" term="llm"/>
        <category label="thai" term="thai"/>
        <category label="tutorial" term="tutorial"/>
        <category label="fine-tuning" term="fine-tuning"/>
        <category label="alignment" term="alignment"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[LLM 4/10] DPO: When a Language Model Becomes Its Own Reward Model]]></title>
        <id>https://kobkrit.com/en/blog/llm-04-dpo</id>
        <link href="https://kobkrit.com/en/blog/llm-04-dpo"/>
        <updated>2026-07-20T18:00:00.000Z</updated>
        <summary type="html"><![CDATA[Deriving DPO from the RLHF objective line by line until the reward model and the RL loop visibly disappear, then writing the DPO loss by hand in 25 lines and proving it matches TRL]]></summary>
        <content type="html"><![CDATA[<p>In the last chapter we did RLHF with PPO, and you saw how many moving parts it has —
a separate reward model to train, four models resident in VRAM at once,
a dozen-plus PPO hyperparameters to tune, and if the reward model is off, the policy finds a shortcut that games the score.
In this chapter we'll accomplish the same thing with an ordinary supervised training loop — no reward model, no RL.
And crucially, <strong>this is not an approximation</strong>. We'll prove algebraically that those two pieces genuinely <strong>cancel out</strong>.</p>
<a class="badge_rUYD" href="https://colab.research.google.com/github/kobkrit/thai-llm-tutorials/blob/main/notebooks/04_dpo.ipynb" target="_blank" rel="noopener noreferrer" aria-label="Open the notebook 04_dpo.ipynb in Google Colab (opens in a new tab)"><svg class="mark_NB8U" viewBox="0 0 24 24" width="20" height="20" aria-hidden="true" focusable="false"><mask id="llmcourse-colab-cut"><rect x="0" y="0" width="24" height="24" fill="#fff"></rect><circle cx="16.2" cy="12" r="6.1" fill="#000"></circle></mask><circle cx="8.4" cy="12" r="4.6" fill="none" stroke="#F9AB00" stroke-width="3.1" mask="url(#llmcourse-colab-cut)"></circle><circle cx="16.2" cy="12" r="4.6" fill="none" stroke="#E8710A" stroke-width="3.1"></circle></svg><span class="text_QXpz">Open in Colab</span><code class="notebook_ntO0">04_dpo.ipynb</code></a>
<nav class="nav_RfLT" aria-label="Thai LLM tutorial series navigation"><p class="heading_XRWm">Thai LLM series<span class="progress_f8e8">Part 4 of 10</span></p><ol class="list_U31a"><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-01-continue-pretraining"><span class="number_u3BE" aria-hidden="true">1</span><span class="title_BPvL">Continue Pretraining</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-02-sft-lora"><span class="number_u3BE" aria-hidden="true">2</span><span class="title_BPvL">SFT and LoRA</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo"><span class="number_u3BE" aria-hidden="true">3</span><span class="title_BPvL">RLHF and PPO</span></a></li><li class="item_Y10l"><span class="chip_DDpP chipCurrent_BGpo" aria-current="step"><span class="number_u3BE" aria-hidden="true">4</span><span class="title_BPvL">DPO: Direct Preference Optimization</span><span class="srOnly_owtF">(you are here)</span></span></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-05-grpo"><span class="number_u3BE" aria-hidden="true">5</span><span class="title_BPvL">GRPO</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-06-context-distillation"><span class="number_u3BE" aria-hidden="true">6</span><span class="title_BPvL">Context Distillation</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-07-model-distillation"><span class="number_u3BE" aria-hidden="true">7</span><span class="title_BPvL">Model Distillation</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-08-guardrails"><span class="number_u3BE" aria-hidden="true">8</span><span class="title_BPvL">Guardrails</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-09-benchmarking"><span class="number_u3BE" aria-hidden="true">9</span><span class="title_BPvL">Benchmarking</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-10-deployment"><span class="number_u3BE" aria-hidden="true">10</span><span class="title_BPvL">Deployment</span></a></li></ol></nav>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-the-problem">1. The Problem<a href="https://kobkrit.com/en/blog/llm-04-dpo#1-the-problem" class="hash-link" aria-label="Direct link to 1. The Problem" title="Direct link to 1. The Problem" translate="no">​</a></h2>
<p>Say you want your AI assistant to "always answer in Thai" — sounds simple.
Now try writing that as a loss function. You can't.</p>
<p>This is the central problem of alignment: answer quality <strong>cannot be written as an equation</strong>.
"More polite," "more natural," "doesn't drift into English" — there is no single correct answer key.
There is only <strong>comparison</strong>: show a human two answers and ask which one they prefer.
So the data comes in threes: a prompt <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow><annotation encoding="application/x-tex">x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">x</span></span></span></span>, a preferred answer <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>y</mi><mi>w</mi></msub></mrow><annotation encoding="application/x-tex">y_w</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0269em">w</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> (chosen), and a dispreferred one <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>y</mi><mi>l</mi></msub></mrow><annotation encoding="application/x-tex">y_l</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0197em">l</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> (rejected).</p>
<p>PPO-style RLHF solves this by taking a two-step detour:</p>
<ol>
<li class="">Train a <strong>reward model</strong> <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>r</mi><mi>ϕ</mi></msub><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>y</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">r_\phi(x,y)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0361em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">ϕ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="mclose">)</span></span></span></span> to imitate human preferences</li>
<li class="">Use <strong>RL</strong> to push the policy toward high scores from that reward model</li>
</ol>
<p>The detour has a price:</p>
<table><thead><tr><th>Problem with RLHF/PPO</th><th>What it costs you in practice</th></tr></thead><tbody><tr><td>An extra model to train</td><td>More steps, more ways to fail, more time</td></tr><tr><td>Four models loaded at once</td><td>policy + ref + reward + value — VRAM explodes</td></tr><tr><td>Reward hacking</td><td>The model finds a loophole that scores well without humans liking it any more</td></tr><tr><td>PPO is hyperparameter-sensitive</td><td>Two runs with different seeds can tell completely different stories</td></tr></tbody></table>
<p>So this chapter asks one short question: <strong>can we skip steps 1 and 2 entirely?</strong></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-what-were-going-to-do">2. What We're Going to Do<a href="https://kobkrit.com/en/blog/llm-04-dpo#2-what-were-going-to-do" class="hash-link" aria-label="Direct link to 2. What We're Going to Do" title="Direct link to 2. What We're Going to Do" translate="no">​</a></h2>
<p>Yes, we can — and the reason is beautiful.</p>
<p>The starting point is the observation that the KL-constrained RLHF objective <strong>has a closed-form solution</strong>.
We already know exactly what the optimal policy looks like, without running a single step of RL.
Knowing that, we <strong>invert the equation</strong> — instead of asking "what policy does this reward produce?"
we ask "what reward does this policy imply?"</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>The core idea of this chapter</div><div class="admonitionContent_BuS1"><p>Once you invert the equation, <strong>the language model already is a reward model</strong>, implicitly.
The reward model and the RL loop aren't "approximated away" — they <strong>cancel out algebraically</strong>.
What's left is an ordinary supervised loss function you can train with a single <code>Trainer</code>.</p></div></div>
<p>This is <strong>DPO (Direct Preference Optimization)</strong>, proposed by Rafailov et al. (2023).
The "Direct" comes from optimizing on preference data directly, with no intermediary.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-the-equations">3. The Equations<a href="https://kobkrit.com/en/blog/llm-04-dpo#3-the-equations" class="hash-link" aria-label="Direct link to 3. The Equations" title="Direct link to 3. The Equations" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="31-setting-up-the-rlhf-objective">3.1 Setting up: the RLHF objective<a href="https://kobkrit.com/en/blog/llm-04-dpo#31-setting-up-the-rlhf-objective" class="hash-link" aria-label="Direct link to 3.1 Setting up: the RLHF objective" title="Direct link to 3.1 Setting up: the RLHF objective" translate="no">​</a></h3>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><munder><mrow><mi>max</mi><mo>⁡</mo></mrow><mi>π</mi></munder><mtext>&nbsp;</mtext><msub><mi mathvariant="double-struck">E</mi><mrow><mi>x</mi><mo>∼</mo><mi mathvariant="script">D</mi><mo separator="true">,</mo><mtext> </mtext><mi>y</mi><mo>∼</mo><mi>π</mi><mo stretchy="false">(</mo><mo>⋅</mo><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo></mrow></msub><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">[</mo><mi>r</mi><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>y</mi><mo stretchy="false">)</mo><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">]</mo><mo>−</mo><mi>β</mi><mtext> </mtext><msub><mi mathvariant="double-struck">D</mi><mtext>KL</mtext></msub><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">[</mo><mi>π</mi><mo stretchy="false">(</mo><mi>y</mi><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo><mtext> </mtext><mi mathvariant="normal">∥</mi><mtext> </mtext><msub><mi>π</mi><mtext>ref</mtext></msub><mo stretchy="false">(</mo><mi>y</mi><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">]</mo></mrow><annotation encoding="application/x-tex">\max_{\pi}\ \mathbb{E}_{x\sim\mathcal{D},\,y\sim\pi(\cdot|x)}\big[r(x,y)\big] - \beta\,\mathbb{D}_{\text{KL}}\big[\pi(y|x)\,\|\,\pi_{\text{ref}}(y|x)\big]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.55em;vertical-align:-0.7em"></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.4306em"><span style="top:-2.4em;margin-left:0em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">π</span></span></span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span><span class="mop">max</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.7em"><span></span></span></span></span></span><span class="mspace">&nbsp;</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathbb">E</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em"><span style="top:-2.5198em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">x</span><span class="mrel mtight">∼</span><span class="mord mathcal mtight" style="margin-right:0.0278em">D</span><span class="mpunct mtight">,</span><span class="mspace mtight" style="margin-right:0.1952em"></span><span class="mord mathnormal mtight" style="margin-right:0.0359em">y</span><span class="mrel mtight">∼</span><span class="mord mathnormal mtight" style="margin-right:0.0359em">π</span><span class="mopen mtight">(</span><span class="mord mtight">⋅</span><span class="mord mtight">∣</span><span class="mord mathnormal mtight">x</span><span class="mclose mtight">)</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.3552em"><span></span></span></span></span></span></span><span class="mord"><span class="delimsizing size1">[</span></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="mclose">)</span><span class="mord"><span class="delimsizing size1">]</span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.2em;vertical-align:-0.35em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathbb">D</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">KL</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord"><span class="delimsizing size1">[</span></span><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="mord">∣</span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord">∥</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">ref</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="mord">∣</span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mord"><span class="delimsizing size1">]</span></span></span></span></span></span>
<p>In plain language: <strong>"maximize the reward, but don't wander too far from where you started."</strong></p>
<ul>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>π</mi></mrow><annotation encoding="application/x-tex">\pi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.0359em">π</span></span></span></span> = the policy, i.e. the model we're training</li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>π</mi><mtext>ref</mtext></msub></mrow><annotation encoding="application/x-tex">\pi_{\text{ref}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">ref</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> = the reference policy, i.e. the starting model (here, the post-SFT model from chapter 2)</li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>y</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">r(x,y)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="mclose">)</span></span></span></span> = the reward for answer <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi></mrow><annotation encoding="application/x-tex">y</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0359em">y</span></span></span></span> given prompt <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow><annotation encoding="application/x-tex">x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">x</span></span></span></span></li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi></mrow><annotation encoding="application/x-tex">\beta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span></span></span></span> = how tight the leash is; higher values pull harder back toward <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>π</mi><mtext>ref</mtext></msub></mrow><annotation encoding="application/x-tex">\pi_{\text{ref}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">ref</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span></li>
</ul>
<p>The KL term is not decoration. Without it the model runs off to wherever reward is highest and the language falls apart.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="32-step-1--the-closed-form-solution">3.2 Step 1 — the closed-form solution<a href="https://kobkrit.com/en/blog/llm-04-dpo#32-step-1--the-closed-form-solution" class="hash-link" aria-label="Direct link to 3.2 Step 1 — the closed-form solution" title="Direct link to 3.2 Step 1 — the closed-form solution" translate="no">​</a></h3>
<p>That problem can be solved by hand (it amounts to finding the distribution that minimizes KL against a target distribution), giving</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msup><mi>π</mi><mo>∗</mo></msup><mo stretchy="false">(</mo><mi>y</mi><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo><mo>=</mo><mfrac><mn>1</mn><mrow><mi>Z</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></mrow></mfrac><msub><mi>π</mi><mtext>ref</mtext></msub><mo stretchy="false">(</mo><mi>y</mi><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo><mi>exp</mi><mo>⁡</mo><mrow><mo fence="true">(</mo><mfrac><mn>1</mn><mi>β</mi></mfrac><mi>r</mi><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>y</mi><mo stretchy="false">)</mo><mo fence="true">)</mo></mrow></mrow><annotation encoding="application/x-tex">\pi^*(y|x) = \frac{1}{Z(x)}\pi_{\text{ref}}(y|x)\exp\left(\frac{1}{\beta}r(x,y)\right)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7387em"><span style="top:-3.113em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="mord">∣</span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.4em;vertical-align:-0.95em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3214em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0715em">Z</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.936em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">ref</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="mord">∣</span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mop">exp</span><span class="mspace" style="margin-right:0.1667em"></span><span class="minner"><span class="mopen delimcenter" style="top:0em"><span class="delimsizing size3">(</span></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3214em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0528em">β</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.8804em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="mclose">)</span><span class="mclose delimcenter" style="top:0em"><span class="delimsizing size3">)</span></span></span></span></span></span></span>
<ul>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>Z</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo>=</mo><msub><mo>∑</mo><mi>y</mi></msub><msub><mi>π</mi><mtext>ref</mtext></msub><mo stretchy="false">(</mo><mi>y</mi><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo><mi>exp</mi><mo>⁡</mo><mtext> ⁣</mtext><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">(</mo><mi>r</mi><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>y</mi><mo stretchy="false">)</mo><mi mathvariant="normal">/</mi><mi>β</mi><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">)</mo></mrow><annotation encoding="application/x-tex">Z(x) = \sum_{y}\pi_{\text{ref}}(y|x)\exp\!\big(r(x,y)/\beta\big)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0715em">Z</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1.2858em;vertical-align:-0.4358em"></span><span class="mop"><span class="mop op-symbol small-op" style="position:relative;top:0em">∑</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.0017em"><span style="top:-2.4003em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.4358em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">ref</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="mord">∣</span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mop">exp</span><span class="mspace" style="margin-right:-0.1667em"></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="delimsizing size1">(</span></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="mclose">)</span><span class="mord">/</span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mord"><span class="delimsizing size1">)</span></span></span></span></span> is the <strong>partition function</strong>, the denominator that makes everything sum to 1</li>
<li class="">Note that <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>Z</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">Z(x)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0715em">Z</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span></span></span></span> <strong>depends only on <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow><annotation encoding="application/x-tex">x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">x</span></span></span></span></strong>, not on <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi></mrow><annotation encoding="application/x-tex">y</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0359em">y</span></span></span></span> — hold on to that sentence, it's about to become the hero of the story</li>
</ul>
<p>The intuition: the optimal policy is <strong>the original model, reweighted by <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>exp</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>r</mi><mi mathvariant="normal">/</mi><mi>β</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\exp(r/\beta)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mop">exp</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="mord">/</span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mclose">)</span></span></span></span></strong>.
High-reward answers get their probability amplified, low-reward answers get pushed down, but everything starts from the original shape of <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>π</mi><mtext>ref</mtext></msub></mrow><annotation encoding="application/x-tex">\pi_{\text{ref}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">ref</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span>.</p>
<p>In practice we can't compute <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>Z</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">Z(x)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0715em">Z</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span></span></span></span>, because it requires summing over every possible answer in the universe.
That's why people reach for RL — and it's exactly why DPO doesn't have to.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="33-step-2--invert-the-equation-to-get-the-reward">3.3 Step 2 — invert the equation to get the reward<a href="https://kobkrit.com/en/blog/llm-04-dpo#33-step-2--invert-the-equation-to-get-the-reward" class="hash-link" aria-label="Direct link to 3.3 Step 2 — invert the equation to get the reward" title="Direct link to 3.3 Step 2 — invert the equation to get the reward" translate="no">​</a></h3>
<p>Take the log of both sides and rearrange:</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>r</mi><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>y</mi><mo stretchy="false">)</mo><mo>=</mo><mi>β</mi><mi>log</mi><mo>⁡</mo><mfrac><mrow><msup><mi>π</mi><mo>∗</mo></msup><mo stretchy="false">(</mo><mi>y</mi><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo></mrow><mrow><msub><mi>π</mi><mtext>ref</mtext></msub><mo stretchy="false">(</mo><mi>y</mi><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo></mrow></mfrac><mo>+</mo><mi>β</mi><mi>log</mi><mo>⁡</mo><mi>Z</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">r(x,y) = \beta\log\frac{\pi^*(y|x)}{\pi_{\text{ref}}(y|x)} + \beta\log Z(x)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.363em;vertical-align:-0.936em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mop">lo<span style="margin-right:0.0139em">g</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.427em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">ref</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="mord">∣</span><span class="mord mathnormal">x</span><span class="mclose">)</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6887em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="mord">∣</span><span class="mord mathnormal">x</span><span class="mclose">)</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.936em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mop">lo<span style="margin-right:0.0139em">g</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0715em">Z</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span></span></span></span></span>
<p>This line is the crux: <strong>any reward function can be rewritten in terms of the optimal policy and the starting policy</strong>.
Which means that if we have two models, we can compute their implicit reward immediately, without ever training a reward model.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="34-step-3--substitute-into-bradley-terry-and-zx-vanishes">3.4 Step 3 — substitute into Bradley-Terry and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>Z</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">Z(x)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0715em">Z</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span></span></span></span> vanishes<a href="https://kobkrit.com/en/blog/llm-04-dpo#34-step-3--substitute-into-bradley-terry-and-zx-vanishes" class="hash-link" aria-label="Direct link to 34-step-3--substitute-into-bradley-terry-and-zx-vanishes" title="Direct link to 34-step-3--substitute-into-bradley-terry-and-zx-vanishes" translate="no">​</a></h3>
<p>The standard model of preference is <strong>Bradley-Terry</strong>: the probability a human picks <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>y</mi><mi>w</mi></msub></mrow><annotation encoding="application/x-tex">y_w</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0269em">w</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> over <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>y</mi><mi>l</mi></msub></mrow><annotation encoding="application/x-tex">y_l</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0197em">l</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> is</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>p</mi><mo stretchy="false">(</mo><msub><mi>y</mi><mi>w</mi></msub><mo>≻</mo><msub><mi>y</mi><mi>l</mi></msub><mo>∣</mo><mi>x</mi><mo stretchy="false">)</mo><mo>=</mo><mi>σ</mi><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">(</mo><mi>r</mi><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><msub><mi>y</mi><mi>w</mi></msub><mo stretchy="false">)</mo><mo>−</mo><mi>r</mi><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><msub><mi>y</mi><mi>l</mi></msub><mo stretchy="false">)</mo><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">)</mo></mrow><annotation encoding="application/x-tex">p(y_w \succ y_l \mid x) = \sigma\big(r(x,y_w) - r(x,y_l)\big)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal">p</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0269em">w</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">≻</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0197em">l</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">∣</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1.2em;vertical-align:-0.35em"></span><span class="mord mathnormal" style="margin-right:0.0359em">σ</span><span class="mord"><span class="delimsizing size1">(</span></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0269em">w</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.2em;vertical-align:-0.35em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0197em">l</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mord"><span class="delimsizing size1">)</span></span></span></span></span></span>
<p>where <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>σ</mi></mrow><annotation encoding="application/x-tex">\sigma</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.0359em">σ</span></span></span></span> is the sigmoid. Notice that reward appears in this equation only as a <strong>difference</strong>.
Substitute equation 3.3 — <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi><mi>log</mi><mo>⁡</mo><mi>Z</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\beta\log Z(x)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mop">lo<span style="margin-right:0.0139em">g</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0715em">Z</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span></span></span></span> appears identically on both sides because it's the same <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow><annotation encoding="application/x-tex">x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">x</span></span></span></span> — so it <strong>cancels out</strong>.</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi mathvariant="script">L</mi><mtext>DPO</mtext></msub><mo stretchy="false">(</mo><mi>θ</mi><mo stretchy="false">)</mo><mo>=</mo><mo>−</mo><msub><mi mathvariant="double-struck">E</mi><mrow><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><msub><mi>y</mi><mi>w</mi></msub><mo separator="true">,</mo><msub><mi>y</mi><mi>l</mi></msub><mo stretchy="false">)</mo></mrow></msub><mrow><mo fence="true">[</mo><mi>log</mi><mo>⁡</mo><mi>σ</mi><mrow><mo fence="true">(</mo><mi>β</mi><mi>log</mi><mo>⁡</mo><mfrac><mrow><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>y</mi><mi>w</mi></msub><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo></mrow><mrow><msub><mi>π</mi><mtext>ref</mtext></msub><mo stretchy="false">(</mo><msub><mi>y</mi><mi>w</mi></msub><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo></mrow></mfrac><mo>−</mo><mi>β</mi><mi>log</mi><mo>⁡</mo><mfrac><mrow><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>y</mi><mi>l</mi></msub><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo></mrow><mrow><msub><mi>π</mi><mtext>ref</mtext></msub><mo stretchy="false">(</mo><msub><mi>y</mi><mi>l</mi></msub><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo></mrow></mfrac><mo fence="true">)</mo></mrow><mo fence="true">]</mo></mrow></mrow><annotation encoding="application/x-tex">\mathcal{L}_{\text{DPO}}(\theta) = -\mathbb{E}_{(x,y_w,y_l)}\left[\log\sigma\left(\beta\log\frac{\pi_\theta(y_w|x)}{\pi_{\text{ref}}(y_w|x)} - \beta\log\frac{\pi_\theta(y_l|x)}{\pi_{\text{ref}}(y_l|x)}\right)\right]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathcal">L</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">DPO</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.0278em">θ</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.4em;vertical-align:-0.95em"></span><span class="mord">−</span><span class="mord"><span class="mord mathbb">E</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em"><span style="top:-2.5198em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mopen mtight">(</span><span class="mord mathnormal mtight">x</span><span class="mpunct mtight">,</span><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1645em"><span style="top:-2.357em;margin-left:-0.0359em;margin-right:0.0714em"><span class="pstrut" style="height:2.5em"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0269em">w</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.143em"><span></span></span></span></span></span></span><span class="mpunct mtight">,</span><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em"><span style="top:-2.3488em;margin-left:-0.0359em;margin-right:0.0714em"><span class="pstrut" style="height:2.5em"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0197em">l</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1512em"><span></span></span></span></span></span></span><span class="mclose mtight">)</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.3552em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="minner"><span class="mopen delimcenter" style="top:0em"><span class="delimsizing size3">[</span></span><span class="mop">lo<span style="margin-right:0.0139em">g</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0359em">σ</span><span class="mspace" style="margin-right:0.1667em"></span><span class="minner"><span class="mopen delimcenter" style="top:0em"><span class="delimsizing size3">(</span></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mop">lo<span style="margin-right:0.0139em">g</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.427em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">ref</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0269em">w</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord">∣</span><span class="mord mathnormal">x</span><span class="mclose">)</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0269em">w</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord">∣</span><span class="mord mathnormal">x</span><span class="mclose">)</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.936em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mop">lo<span style="margin-right:0.0139em">g</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.427em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">ref</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0197em">l</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord">∣</span><span class="mord mathnormal">x</span><span class="mclose">)</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0197em">l</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord">∣</span><span class="mord mathnormal">x</span><span class="mclose">)</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.936em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mclose delimcenter" style="top:0em"><span class="delimsizing size3">)</span></span></span><span class="mclose delimcenter" style="top:0em"><span class="delimsizing size3">]</span></span></span></span></span></span></span>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>This is the sentence the whole article exists to say</div><div class="admonitionContent_BuS1"><p>The uncomputable thing (<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>Z</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">Z(x)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0715em">Z</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span></span></span></span>) disappears, because Bradley-Terry only cares about the difference of rewards.
What remains is the log-probability of two models on text we already have, obtainable with an ordinary forward pass.
<strong>No sampling of answers, no rollouts, no value function</strong> — DPO is supervised learning, all the way down.</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="35-the-gradient--where-the-intuition-lives">3.5 The gradient — where the intuition lives<a href="https://kobkrit.com/en/blog/llm-04-dpo#35-the-gradient--where-the-intuition-lives" class="hash-link" aria-label="Direct link to 3.5 The gradient — where the intuition lives" title="Direct link to 3.5 The gradient — where the intuition lives" translate="no">​</a></h3>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi mathvariant="normal">∇</mi><mi>θ</mi></msub><msub><mi mathvariant="script">L</mi><mtext>DPO</mtext></msub><mo>=</mo><mo>−</mo><mi>β</mi><mtext> </mtext><mi mathvariant="double-struck">E</mi><mrow><mo fence="true">[</mo><mi>σ</mi><mo stretchy="false">(</mo><msub><mover accent="true"><mi>r</mi><mo>^</mo></mover><mi>l</mi></msub><mo>−</mo><msub><mover accent="true"><mi>r</mi><mo>^</mo></mover><mi>w</mi></msub><mo stretchy="false">)</mo><mrow><mo fence="true">(</mo><msub><mi mathvariant="normal">∇</mi><mi>θ</mi></msub><mi>log</mi><mo>⁡</mo><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>y</mi><mi>w</mi></msub><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo><mo>−</mo><msub><mi mathvariant="normal">∇</mi><mi>θ</mi></msub><mi>log</mi><mo>⁡</mo><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>y</mi><mi>l</mi></msub><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo><mo fence="true">)</mo></mrow><mo fence="true">]</mo></mrow></mrow><annotation encoding="application/x-tex">\nabla_\theta\mathcal{L}_{\text{DPO}} = -\beta\,\mathbb{E}\left[\sigma(\hat r_l - \hat r_w)\left(\nabla_\theta\log\pi_\theta(y_w|x) - \nabla_\theta\log\pi_\theta(y_l|x)\right)\right]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em"></span><span class="mord"><span class="mord">∇</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathcal">L</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">DPO</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord">−</span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathbb">E</span><span class="mspace" style="margin-right:0.1667em"></span><span class="minner"><span class="mopen delimcenter" style="top:0em">[</span><span class="mord mathnormal" style="margin-right:0.0359em">σ</span><span class="mopen">(</span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1944em"><span class="mord">^</span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0197em">l</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1944em"><span class="mord">^</span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0269em">w</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.1667em"></span><span class="minner"><span class="mopen delimcenter" style="top:0em">(</span><span class="mord"><span class="mord">∇</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mop">lo<span style="margin-right:0.0139em">g</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0269em">w</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord">∣</span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord"><span class="mord">∇</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mop">lo<span style="margin-right:0.0139em">g</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0197em">l</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord">∣</span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mclose delimcenter" style="top:0em">)</span></span><span class="mclose delimcenter" style="top:0em">]</span></span></span></span></span></span>
<p>where <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>r</mi><mo>^</mo></mover><mo>=</mo><mi>β</mi><mi>log</mi><mo>⁡</mo><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">(</mo><msub><mi>π</mi><mi>θ</mi></msub><mi mathvariant="normal">/</mi><msub><mi>π</mi><mtext>ref</mtext></msub><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">)</mo></mrow><annotation encoding="application/x-tex">\hat r = \beta\log\big(\pi_\theta/\pi_{\text{ref}}\big)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1944em"><span class="mord">^</span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1.2em;vertical-align:-0.35em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mop">lo<span style="margin-right:0.0139em">g</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="delimsizing size1">(</span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord">/</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">ref</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord"><span class="delimsizing size1">)</span></span></span></span></span> is called the <strong>implicit reward</strong>.</p>
<p>Read it piece by piece:</p>
<ul>
<li class="">The right-hand parenthesis = the <strong>direction</strong>: push the log-prob of <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>y</mi><mi>w</mi></msub></mrow><annotation encoding="application/x-tex">y_w</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0269em">w</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> up and the log-prob of <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>y</mi><mi>l</mi></msub></mrow><annotation encoding="application/x-tex">y_l</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0197em">l</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> down, simultaneously</li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>σ</mi><mo stretchy="false">(</mo><msub><mover accent="true"><mi>r</mi><mo>^</mo></mover><mi>l</mi></msub><mo>−</mo><msub><mover accent="true"><mi>r</mi><mo>^</mo></mover><mi>w</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\sigma(\hat r_l - \hat r_w)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0359em">σ</span><span class="mopen">(</span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1944em"><span class="mord">^</span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0197em">l</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1944em"><span class="mord">^</span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0269em">w</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span> = the <strong>weight</strong>: "how badly is the model ranking this pair?"</li>
</ul>
<p>That weight is the most important teaching point here. If the model already ranks the pair correctly
(<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mover accent="true"><mi>r</mi><mo>^</mo></mover><mi>w</mi></msub></mrow><annotation encoding="application/x-tex">\hat r_w</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1944em"><span class="mord">^</span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0269em">w</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> clearly greater than <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mover accent="true"><mi>r</mi><mo>^</mo></mover><mi>l</mi></msub></mrow><annotation encoding="application/x-tex">\hat r_l</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1944em"><span class="mord">^</span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0197em">l</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span>), then <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>σ</mi><mo stretchy="false">(</mo><msub><mover accent="true"><mi>r</mi><mo>^</mo></mover><mi>l</mi></msub><mo>−</mo><msub><mover accent="true"><mi>r</mi><mo>^</mo></mover><mi>w</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\sigma(\hat r_l - \hat r_w)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0359em">σ</span><span class="mopen">(</span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1944em"><span class="mord">^</span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0197em">l</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1944em"><span class="mord">^</span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0269em">w</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span> approaches zero and the pair contributes almost no gradient.
<strong>DPO therefore focuses on its own mistakes automatically</strong>, with nobody curating the data for it.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-seeing-the-equations">4. Seeing the Equations<a href="https://kobkrit.com/en/blog/llm-04-dpo#4-seeing-the-equations" class="hash-link" aria-label="Direct link to 4. Seeing the Equations" title="Direct link to 4. Seeing the Equations" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="bradley-terry-from-a-reward-difference-to-a-probability">Bradley-Terry: from a reward difference to a probability<a href="https://kobkrit.com/en/blog/llm-04-dpo#bradley-terry-from-a-reward-difference-to-a-probability" class="hash-link" aria-label="Direct link to Bradley-Terry: from a reward difference to a probability" title="Direct link to Bradley-Terry: from a reward difference to a probability" translate="no">​</a></h3>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-04-dpo/bradley-terry.light.svg" alt="A sigmoid curve over the reward difference between chosen and rejected, with the regions where the model agrees and disagrees with the human shaded" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-04-dpo/bradley-terry.dark.svg" alt="A sigmoid curve over the reward difference between chosen and rejected, with the regions where the model agrees and disagrees with the human shaded" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 4.1</span>Bradley-Terry turns a reward difference into the probability a human picks chosen — the model never needs absolute reward values, only the difference</p><div class="captionFooter_w00v"></div></figcaption></figure>
<p>Where the difference is zero the probability is exactly 0.5 — "the model has no opinion."
And because the curve only cares about the difference, adding a constant to both rewards changes nothing at all.
<strong>That is the geometric reason <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>Z</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">Z(x)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0715em">Z</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span></span></span></span> can cancel.</strong></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="loss-and-the-gradient-weight">Loss and the gradient weight<a href="https://kobkrit.com/en/blog/llm-04-dpo#loss-and-the-gradient-weight" class="hash-link" aria-label="Direct link to Loss and the gradient weight" title="Direct link to Loss and the gradient weight" translate="no">​</a></h3>
<p>Let <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Δ</mi><mo>=</mo><mi>log</mi><mo>⁡</mo><mfrac><mrow><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>y</mi><mi>w</mi></msub><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo></mrow><mrow><msub><mi>π</mi><mtext>ref</mtext></msub><mo stretchy="false">(</mo><msub><mi>y</mi><mi>w</mi></msub><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo></mrow></mfrac><mo>−</mo><mi>log</mi><mo>⁡</mo><mfrac><mrow><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>y</mi><mi>l</mi></msub><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo></mrow><mrow><msub><mi>π</mi><mtext>ref</mtext></msub><mo stretchy="false">(</mo><msub><mi>y</mi><mi>l</mi></msub><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo></mrow></mfrac></mrow><annotation encoding="application/x-tex">\Delta = \log\frac{\pi_\theta(y_w|x)}{\pi_{\text{ref}}(y_w|x)} - \log\frac{\pi_\theta(y_l|x)}{\pi_{\text{ref}}(y_l|x)}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord">Δ</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1.53em;vertical-align:-0.52em"></span><span class="mop">lo<span style="margin-right:0.0139em">g</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.01em"><span style="top:-2.655em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em"><span style="top:-2.3488em;margin-left:-0.0359em;margin-right:0.0714em"><span class="pstrut" style="height:2.5em"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">ref</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1512em"><span></span></span></span></span></span></span><span class="mopen mtight">(</span><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1645em"><span style="top:-2.357em;margin-left:-0.0359em;margin-right:0.0714em"><span class="pstrut" style="height:2.5em"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0269em">w</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.143em"><span></span></span></span></span></span></span><span class="mord mtight">∣</span><span class="mord mathnormal mtight">x</span><span class="mclose mtight">)</span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.485em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em"><span style="top:-2.3488em;margin-left:-0.0359em;margin-right:0.0714em"><span class="pstrut" style="height:2.5em"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1512em"><span></span></span></span></span></span></span><span class="mopen mtight">(</span><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1645em"><span style="top:-2.357em;margin-left:-0.0359em;margin-right:0.0714em"><span class="pstrut" style="height:2.5em"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0269em">w</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.143em"><span></span></span></span></span></span></span><span class="mord mtight">∣</span><span class="mord mathnormal mtight">x</span><span class="mclose mtight">)</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.52em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.53em;vertical-align:-0.52em"></span><span class="mop">lo<span style="margin-right:0.0139em">g</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.01em"><span style="top:-2.655em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em"><span style="top:-2.3488em;margin-left:-0.0359em;margin-right:0.0714em"><span class="pstrut" style="height:2.5em"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">ref</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1512em"><span></span></span></span></span></span></span><span class="mopen mtight">(</span><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em"><span style="top:-2.3488em;margin-left:-0.0359em;margin-right:0.0714em"><span class="pstrut" style="height:2.5em"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0197em">l</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1512em"><span></span></span></span></span></span></span><span class="mord mtight">∣</span><span class="mord mathnormal mtight">x</span><span class="mclose mtight">)</span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.485em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em"><span style="top:-2.3488em;margin-left:-0.0359em;margin-right:0.0714em"><span class="pstrut" style="height:2.5em"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1512em"><span></span></span></span></span></span></span><span class="mopen mtight">(</span><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em"><span style="top:-2.3488em;margin-left:-0.0359em;margin-right:0.0714em"><span class="pstrut" style="height:2.5em"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0197em">l</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1512em"><span></span></span></span></span></span></span><span class="mord mtight">∣</span><span class="mord mathnormal mtight">x</span><span class="mclose mtight">)</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.52em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span>.
Then the loss is <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>−</mo><mi>log</mi><mo>⁡</mo><mi>σ</mi><mo stretchy="false">(</mo><mi>β</mi><mi mathvariant="normal">Δ</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">-\log\sigma(\beta\Delta)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord">−</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mop">lo<span style="margin-right:0.0139em">g</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0359em">σ</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mord">Δ</span><span class="mclose">)</span></span></span></span> and the gradient weight is <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>σ</mi><mo stretchy="false">(</mo><mo>−</mo><mi>β</mi><mi mathvariant="normal">Δ</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\sigma(-\beta\Delta)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0359em">σ</span><span class="mopen">(</span><span class="mord">−</span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mord">Δ</span><span class="mclose">)</span></span></span></span>.</p>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-04-dpo/dpo-loss-and-gradient.light.svg" alt="Two panels showing DPO loss decreasing as margin grows, and the gradient weight converging to zero once the model ranks a pair correctly, compared across three beta values" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-04-dpo/dpo-loss-and-gradient.dark.svg" alt="Two panels showing DPO loss decreasing as margin grows, and the gradient weight converging to zero once the model ranks a pair correctly, compared across three beta values" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 4.2</span>Left: DPO loss against margin — Right: the weight a pair receives in the gradient, for β = 0.1, 0.3, 1.0</p><div class="captionFooter_w00v"></div></figcaption></figure>
<p>The right panel is the one to study. Once the margin is strongly positive, the weight converges to zero — that pair has "graduated."
And the higher <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi></mrow><annotation encoding="application/x-tex">\beta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span></span></span></span> is, the steeper the curve: the model both learns fast and "stops learning" fast.
At <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi><mo>=</mo><mn>1.0</mn></mrow><annotation encoding="application/x-tex">\beta = 1.0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">1.0</span></span></span></span>, pairs with a margin past 4 have essentially no gradient left.
At <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi><mo>=</mo><mn>0.1</mn></mrow><annotation encoding="application/x-tex">\beta = 0.1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0.1</span></span></span></span> the curve is far flatter, so the model keeps collecting gradient from every pair — slower, but steadier.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="beta-controls-how-far-the-model-can-drift-from-where-it-started"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi></mrow><annotation encoding="application/x-tex">\beta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span></span></span></span> controls how far the model can drift from where it started<a href="https://kobkrit.com/en/blog/llm-04-dpo#beta-controls-how-far-the-model-can-drift-from-where-it-started" class="hash-link" aria-label="Direct link to beta-controls-how-far-the-model-can-drift-from-where-it-started" title="Direct link to beta-controls-how-far-the-model-can-drift-from-where-it-started" translate="no">​</a></h3>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-04-dpo/beta-controls-drift.light.svg" alt="Bar chart of the probabilities of 5 candidate answers at different beta values, against a dashed line for the starting policy" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-04-dpo/beta-controls-drift.dark.svg" alt="Bar chart of the probabilities of 5 candidate answers at different beta values, against a dashed line for the starting policy" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 4.3</span>The equation π* ∝ π_ref · exp(r/β) on a toy example with 5 answers — a small β collapses everything onto the highest-reward answer, a large β restores the shape of π_ref</p><div class="captionFooter_w00v"></div></figcaption></figure>
<p>Read this one right to left: at <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi><mo>=</mo><mn>0.1</mn></mrow><annotation encoding="application/x-tex">\beta = 0.1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0.1</span></span></span></span> nearly all the probability mass collapses onto <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>y</mi><mn>5</mn></msub></mrow><annotation encoding="application/x-tex">y_5</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">5</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span>, the highest-reward answer.
That is <strong>mode collapse</strong> — a great score with all diversity wiped out.
At <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi><mo>=</mo><mn>10</mn></mrow><annotation encoding="application/x-tex">\beta = 10</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">10</span></span></span></span> the bars nearly overlap the dashed line, meaning almost nothing was learned.
<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi></mrow><annotation encoding="application/x-tex">\beta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span></span></span></span> isn't a hyperparameter you "tune for lowest loss." It is <strong>a choice of where to sit on the trade-off</strong> between following preferences and keeping your original identity.</p>
<p>Drag the slider yourself and watch how loss and gradient change shape:</p>
<div class="root_Y8YJ"><div class="controls_hr8V"><fieldset class="control_Br1p" style="border:0;padding:0;margin:0"><legend class="segmentedLegend_oU13">Loss family</legend><div class="segmented_Klsm"><span class="segment_AC25"><input type="radio" id="_R_9e6ldeh_-bt" name="llmcourse-ple-family-_R_9e6ldeh_" value="bt"><label class="segmentLabel_wkEZ" for="_R_9e6ldeh_-bt">Bradley-Terry</label></span><span class="segment_AC25"><input type="radio" id="_R_9e6ldeh_-dpo" name="llmcourse-ple-family-_R_9e6ldeh_" checked="" value="dpo"><label class="segmentLabel_wkEZ" for="_R_9e6ldeh_-dpo">DPO</label></span><span class="segment_AC25"><input type="radio" id="_R_9e6ldeh_-ipo" name="llmcourse-ple-family-_R_9e6ldeh_" value="ipo"><label class="segmentLabel_wkEZ" for="_R_9e6ldeh_-ipo">IPO</label></span><span class="segment_AC25"><input type="radio" id="_R_9e6ldeh_-hinge" name="llmcourse-ple-family-_R_9e6ldeh_" value="hinge"><label class="segmentLabel_wkEZ" for="_R_9e6ldeh_-hinge">Hinge</label></span></div></fieldset><div class="control_Br1p"><label class="controlLabel_J5tp" for="_R_he6ldeh_"><span>Reward margin Δ</span><span class="controlValue_cYgn">2.00</span></label><input id="_R_he6ldeh_" class="range_qGHz" type="range" min="-6" max="6" step="0.05" aria-label="Reward margin delta between the chosen and rejected response" aria-valuetext="2.00" aria-describedby="_R_he6ldeh_-hint" value="2"><span class="controlHint_ilRY" id="_R_he6ldeh_-hint">Positive means the model already prefers the chosen response.</span></div><div class="control_Br1p"><label class="controlLabel_J5tp" for="_R_pe6ldeh_"><span>β</span><span class="controlValue_cYgn">0.10</span></label><input id="_R_pe6ldeh_" class="range_qGHz" type="range" min="0.01" max="1" step="0.01" aria-label="Beta, the KL penalty strength" aria-valuetext="0.10" value="0.1"></div></div><div class="svgWrap_mSxx"><svg class="svg_pLEH plot_ViPE" viewBox="0 0 720 320" role="img" aria-label="Loss curve and gradient weight against the reward margin. At the current margin 2.00 the loss is 0.598 and the gradient weight is 0.045."><line x1="58" y1="148" x2="662" y2="148" class="zeroLine_dlwX"></line><line x1="58" y1="278" x2="662" y2="278" class="axis_LG2_"></line><g><line x1="58" y1="278" x2="58" y2="283" class="axis_LG2_"></line><text x="58" y="296" text-anchor="middle" class="tickLabel_B3jM">-6</text></g><g><line x1="158.66666666666666" y1="278" x2="158.66666666666666" y2="283" class="axis_LG2_"></line><text x="158.66666666666666" y="296" text-anchor="middle" class="tickLabel_B3jM">-4</text></g><g><line x1="259.3333333333333" y1="278" x2="259.3333333333333" y2="283" class="axis_LG2_"></line><text x="259.3333333333333" y="296" text-anchor="middle" class="tickLabel_B3jM">-2</text></g><g><line x1="360" y1="278" x2="360" y2="283" class="axis_LG2_"></line><text x="360" y="296" text-anchor="middle" class="tickLabel_B3jM">0</text></g><g><line x1="460.66666666666663" y1="278" x2="460.66666666666663" y2="283" class="axis_LG2_"></line><text x="460.66666666666663" y="296" text-anchor="middle" class="tickLabel_B3jM">2</text></g><g><line x1="561.3333333333334" y1="278" x2="561.3333333333334" y2="283" class="axis_LG2_"></line><text x="561.3333333333334" y="296" text-anchor="middle" class="tickLabel_B3jM">4</text></g><g><line x1="662" y1="278" x2="662" y2="283" class="axis_LG2_"></line><text x="662" y="296" text-anchor="middle" class="tickLabel_B3jM">6</text></g><rect x="360" y="18" width="302" height="260" class="correctRegion_qefa"></rect><path d="M58.00,18.00 L60.52,18.23 L63.03,18.46 L65.55,18.69 L68.07,18.92 L70.58,19.16 L73.10,19.39 L75.62,19.62 L78.13,19.85 L80.65,20.09 L83.17,20.32 L85.68,20.55 L88.20,20.79 L90.72,21.02 L93.23,21.26 L95.75,21.49 L98.27,21.73 L100.78,21.96 L103.30,22.20 L105.82,22.43 L108.33,22.67 L110.85,22.91 L113.37,23.14 L115.88,23.38 L118.40,23.62 L120.92,23.86 L123.43,24.10 L125.95,24.33 L128.47,24.57 L130.98,24.81 L133.50,25.05 L136.02,25.29 L138.53,25.53 L141.05,25.77 L143.57,26.01 L146.08,26.25 L148.60,26.49 L151.12,26.73 L153.63,26.97 L156.15,27.22 L158.67,27.46 L161.18,27.70 L163.70,27.94 L166.22,28.18 L168.73,28.43 L171.25,28.67 L173.77,28.91 L176.28,29.16 L178.80,29.40 L181.32,29.64 L183.83,29.89 L186.35,30.13 L188.87,30.38 L191.38,30.62 L193.90,30.87 L196.42,31.11 L198.93,31.36 L201.45,31.60 L203.97,31.85 L206.48,32.09 L209.00,32.34 L211.52,32.58 L214.03,32.83 L216.55,33.08 L219.07,33.32 L221.58,33.57 L224.10,33.82 L226.62,34.07 L229.13,34.31 L231.65,34.56 L234.17,34.81 L236.68,35.06 L239.20,35.30 L241.72,35.55 L244.23,35.80 L246.75,36.05 L249.27,36.30 L251.78,36.55 L254.30,36.80 L256.82,37.04 L259.33,37.29 L261.85,37.54 L264.37,37.79 L266.88,38.04 L269.40,38.29 L271.92,38.54 L274.43,38.79 L276.95,39.04 L279.47,39.29 L281.98,39.54 L284.50,39.79 L287.02,40.04 L289.53,40.29 L292.05,40.54 L294.57,40.79 L297.08,41.04 L299.60,41.29 L302.12,41.54 L304.63,41.80 L307.15,42.05 L309.67,42.30 L312.18,42.55 L314.70,42.80 L317.22,43.05 L319.73,43.30 L322.25,43.55 L324.77,43.81 L327.28,44.06 L329.80,44.31 L332.32,44.56 L334.83,44.81 L337.35,45.06 L339.87,45.31 L342.38,45.57 L344.90,45.82 L347.42,46.07 L349.93,46.32 L352.45,46.57 L354.97,46.82 L357.48,47.08 L360.00,47.33 L362.52,47.58 L365.03,47.83 L367.55,48.08 L370.07,48.33 L372.58,48.59 L375.10,48.84 L377.62,49.09 L380.13,49.34 L382.65,49.59 L385.17,49.84 L387.68,50.10 L390.20,50.35 L392.72,50.60 L395.23,50.85 L397.75,51.10 L400.27,51.35 L402.78,51.60 L405.30,51.85 L407.82,52.11 L410.33,52.36 L412.85,52.61 L415.37,52.86 L417.88,53.11 L420.40,53.36 L422.92,53.61 L425.43,53.86 L427.95,54.11 L430.47,54.36 L432.98,54.61 L435.50,54.86 L438.02,55.11 L440.53,55.36 L443.05,55.61 L445.57,55.86 L448.08,56.11 L450.60,56.36 L453.12,56.61 L455.63,56.86 L458.15,57.11 L460.67,57.36 L463.18,57.61 L465.70,57.86 L468.22,58.11 L470.73,58.36 L473.25,58.61 L475.77,58.85 L478.28,59.10 L480.80,59.35 L483.32,59.60 L485.83,59.85 L488.35,60.09 L490.87,60.34 L493.38,60.59 L495.90,60.84 L498.42,61.08 L500.93,61.33 L503.45,61.58 L505.97,61.82 L508.48,62.07 L511.00,62.32 L513.52,62.56 L516.03,62.81 L518.55,63.05 L521.07,63.30 L523.58,63.54 L526.10,63.79 L528.62,64.03 L531.13,64.28 L533.65,64.52 L536.17,64.77 L538.68,65.01 L541.20,65.26 L543.72,65.50 L546.23,65.74 L548.75,65.99 L551.27,66.23 L553.78,66.47 L556.30,66.71 L558.82,66.96 L561.33,67.20 L563.85,67.44 L566.37,67.68 L568.88,67.92 L571.40,68.16 L573.92,68.40 L576.43,68.64 L578.95,68.88 L581.47,69.12 L583.98,69.36 L586.50,69.60 L589.02,69.84 L591.53,70.08 L594.05,70.32 L596.57,70.56 L599.08,70.80 L601.60,71.04 L604.12,71.27 L606.63,71.51 L609.15,71.75 L611.67,71.98 L614.18,72.22 L616.70,72.46 L619.22,72.69 L621.73,72.93 L624.25,73.16 L626.77,73.40 L629.28,73.63 L631.80,73.87 L634.32,74.10 L636.83,74.33 L639.35,74.57 L641.87,74.80 L644.38,75.03 L646.90,75.27 L649.42,75.50 L651.93,75.73 L654.45,75.96 L656.97,76.19 L659.48,76.42 L662.00,76.65" class="gradCurve_Wct8"></path><path d="M58.00,18.00 L60.52,18.81 L63.03,19.62 L65.55,20.42 L68.07,21.22 L70.58,22.03 L73.10,22.83 L75.62,23.63 L78.13,24.43 L80.65,25.22 L83.17,26.02 L85.68,26.81 L88.20,27.60 L90.72,28.40 L93.23,29.18 L95.75,29.97 L98.27,30.76 L100.78,31.54 L103.30,32.33 L105.82,33.11 L108.33,33.89 L110.85,34.67 L113.37,35.45 L115.88,36.22 L118.40,37.00 L120.92,37.77 L123.43,38.54 L125.95,39.31 L128.47,40.08 L130.98,40.85 L133.50,41.62 L136.02,42.38 L138.53,43.14 L141.05,43.91 L143.57,44.67 L146.08,45.42 L148.60,46.18 L151.12,46.94 L153.63,47.69 L156.15,48.44 L158.67,49.19 L161.18,49.94 L163.70,50.69 L166.22,51.44 L168.73,52.18 L171.25,52.93 L173.77,53.67 L176.28,54.41 L178.80,55.15 L181.32,55.88 L183.83,56.62 L186.35,57.35 L188.87,58.09 L191.38,58.82 L193.90,59.55 L196.42,60.28 L198.93,61.00 L201.45,61.73 L203.97,62.45 L206.48,63.17 L209.00,63.89 L211.52,64.61 L214.03,65.33 L216.55,66.05 L219.07,66.76 L221.58,67.47 L224.10,68.19 L226.62,68.89 L229.13,69.60 L231.65,70.31 L234.17,71.02 L236.68,71.72 L239.20,72.42 L241.72,73.12 L244.23,73.82 L246.75,74.52 L249.27,75.21 L251.78,75.91 L254.30,76.60 L256.82,77.29 L259.33,77.98 L261.85,78.67 L264.37,79.36 L266.88,80.04 L269.40,80.73 L271.92,81.41 L274.43,82.09 L276.95,82.77 L279.47,83.44 L281.98,84.12 L284.50,84.79 L287.02,85.47 L289.53,86.14 L292.05,86.81 L294.57,87.48 L297.08,88.14 L299.60,88.81 L302.12,89.47 L304.63,90.13 L307.15,90.79 L309.67,91.45 L312.18,92.11 L314.70,92.76 L317.22,93.42 L319.73,94.07 L322.25,94.72 L324.77,95.37 L327.28,96.02 L329.80,96.66 L332.32,97.31 L334.83,97.95 L337.35,98.59 L339.87,99.23 L342.38,99.87 L344.90,100.51 L347.42,101.14 L349.93,101.78 L352.45,102.41 L354.97,103.04 L357.48,103.67 L360.00,104.29 L362.52,104.92 L365.03,105.54 L367.55,106.17 L370.07,106.79 L372.58,107.41 L375.10,108.02 L377.62,108.64 L380.13,109.26 L382.65,109.87 L385.17,110.48 L387.68,111.09 L390.20,111.70 L392.72,112.31 L395.23,112.91 L397.75,113.52 L400.27,114.12 L402.78,114.72 L405.30,115.32 L407.82,115.91 L410.33,116.51 L412.85,117.11 L415.37,117.70 L417.88,118.29 L420.40,118.88 L422.92,119.47 L425.43,120.05 L427.95,120.64 L430.47,121.22 L432.98,121.80 L435.50,122.38 L438.02,122.96 L440.53,123.54 L443.05,124.12 L445.57,124.69 L448.08,125.26 L450.60,125.83 L453.12,126.40 L455.63,126.97 L458.15,127.54 L460.67,128.10 L463.18,128.67 L465.70,129.23 L468.22,129.79 L470.73,130.35 L473.25,130.90 L475.77,131.46 L478.28,132.01 L480.80,132.57 L483.32,133.12 L485.83,133.67 L488.35,134.21 L490.87,134.76 L493.38,135.31 L495.90,135.85 L498.42,136.39 L500.93,136.93 L503.45,137.47 L505.97,138.01 L508.48,138.54 L511.00,139.08 L513.52,139.61 L516.03,140.14 L518.55,140.67 L521.07,141.20 L523.58,141.72 L526.10,142.25 L528.62,142.77 L531.13,143.29 L533.65,143.81 L536.17,144.33 L538.68,144.85 L541.20,145.36 L543.72,145.88 L546.23,146.39 L548.75,146.90 L551.27,147.41 L553.78,147.92 L556.30,148.43 L558.82,148.93 L561.33,149.44 L563.85,149.94 L566.37,150.44 L568.88,150.94 L571.40,151.44 L573.92,151.93 L576.43,152.43 L578.95,152.92 L581.47,153.41 L583.98,153.90 L586.50,154.39 L589.02,154.88 L591.53,155.36 L594.05,155.85 L596.57,156.33 L599.08,156.81 L601.60,157.29 L604.12,157.77 L606.63,158.24 L609.15,158.72 L611.67,159.19 L614.18,159.67 L616.70,160.14 L619.22,160.61 L621.73,161.07 L624.25,161.54 L626.77,162.01 L629.28,162.47 L631.80,162.93 L634.32,163.39 L636.83,163.85 L639.35,164.31 L641.87,164.77 L644.38,165.22 L646.90,165.67 L649.42,166.13 L651.93,166.58 L654.45,167.02 L656.97,167.47 L659.48,167.92 L662.00,168.36" class="lossCurve_FAHe"></path><line x1="460.66666666666663" y1="18" x2="460.66666666666663" y2="278" class="marker_YiWp"></line><circle cx="460.66666666666663" cy="128.1032171348292" r="5" class="lossDot_ngLI"></circle><circle cx="460.66666666666663" cy="57.36109539227202" r="5" class="gradDot_pEwz"></circle><text x="360" y="314" text-anchor="middle" class="axisLabel_Yazw">reward margin Δ</text><text x="12" y="148" text-anchor="middle" transform="rotate(-90 12 148)" class="axisLabel_Yazw lossAxisLabel_ITiy">loss</text><text x="708" y="148" text-anchor="middle" transform="rotate(90 708 148)" class="axisLabel_Yazw gradAxisLabel_OHlC">gradient weight</text></svg></div><p class="hintLine_kKNP">Drag anywhere on the plot, or use the Δ slider with the arrow keys.</p><div class="readouts__tjv"><div class="readout_D9ns"><span class="readoutLabel_EsIV">Δ</span><span class="readoutValue_VS6z">2.00</span><span class="readoutSub_DoT9">βΔ = 0.200</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">Loss</span><span class="readoutValue_VS6z">0.5981</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">Gradient weight</span><span class="readoutValue_VS6z">0.0450</span><span class="readoutSub_DoT9">69.7% of maximum</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">σ(−βΔ)</span><span class="readoutValue_VS6z">0.4502</span><span class="readoutSub_DoT9">45.02%</span></div></div><p class="callout_aEDz" role="status"><strong class="calloutTitle_nx3s">This pair carries real signal.</strong>σ(−βΔ) is 45.02%: the model is still wrong or unsure about this pair, so it dominates the batch gradient. Drag Δ to the right and watch that weight collapse.</p></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-setting-up-the-environment">5. Setting Up the Environment<a href="https://kobkrit.com/en/blog/llm-04-dpo#5-setting-up-the-environment" class="hash-link" aria-label="Direct link to 5. Setting Up the Environment" title="Direct link to 5. Setting Up the Environment" translate="no">​</a></h2>
<p>Open Colab and pick <strong>Runtime → Change runtime type → T4 GPU</strong> (the free tier suffices).</p>
<div class="theme-admonition theme-admonition-danger admonition_xJq3 alert alert--danger"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"></path></svg></span>The series-wide warning worth re-reading every chapter</div><div class="admonitionContent_BuS1"><p>The Colab T4 is Turing architecture (SM 7.5), which <strong>does not support bfloat16</strong> and <strong>does not support FlashAttention-2</strong>.</p><p>But Qwen3-0.6B's <code>config.json</code> declares <code>torch_dtype: bfloat16</code>.
So <code>torch_dtype="auto"</code> is <strong>a trap</strong> — your code will crash or run bizarrely slowly without telling you why.</p><div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">torch_dtype</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">float16      </span><span class="token comment" style="color:#999988;font-style:italic"># not bfloat16</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">attn_implementation</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"sdpa"</span><span class="token plain">     </span><span class="token comment" style="color:#999988;font-style:italic"># not flash_attention_2</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">fp16</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token plain">                      </span><span class="token comment" style="color:#999988;font-style:italic"># in DPOConfig (not bf16=True)</span><br></span></code></pre></div></div></div></div>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">cap </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_device_capability</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"compute capability:"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cap</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                    </span><span class="token comment" style="color:#999988;font-style:italic"># T4 = (7, 5)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"native bf16:"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cap</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&gt;=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                   </span><span class="token comment" style="color:#999988;font-style:italic"># T4 -&gt; False</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"torch says   :"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">is_bf16_supported</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># T4 -&gt; True (counts emulation!)</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span><code>is_bf16_supported()</code> lies on a T4</div><div class="admonitionContent_BuS1"><p>Recent torch returns <code>True</code> on a T4 because it counts <strong>emulation</strong> as support — which is far slower than fp16.
Gate on <strong>compute capability ≥ 8.0</strong> (Ampere and up) instead. This was a real bug, caught only by running the notebook on Colab.</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-reference-model-that-costs-zero-extra-vram">A reference model that costs zero extra VRAM<a href="https://kobkrit.com/en/blog/llm-04-dpo#a-reference-model-that-costs-zero-extra-vram" class="hash-link" aria-label="Direct link to A reference model that costs zero extra VRAM" title="Direct link to A reference model that costs zero extra VRAM" translate="no">​</a></h3>
<p>DPO needs both <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>π</mi><mi>θ</mi></msub></mrow><annotation encoding="application/x-tex">\pi_\theta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>π</mi><mtext>ref</mtext></msub></mrow><annotation encoding="application/x-tex">\pi_{\text{ref}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">ref</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span>, which sounds like loading two models.
But we're building on the <strong>LoRA adapter from chapter 2</strong>, which lets both share the same base weights.</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> peft </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> PeftModel</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">base </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> AutoModelForCausalLM</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">from_pretrained</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"Qwen/Qwen3-0.6B"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    torch_dtype</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">float16</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    attn_implementation</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"sdpa"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">policy </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> PeftModel</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">from_pretrained</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">base</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"kobkrit/qwen3-0.6b-th-sft-lora"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> is_trainable</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p><code>policy</code> is base + adapter, while <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>π</mi><mtext>ref</mtext></msub></mrow><annotation encoding="application/x-tex">\pi_{\text{ref}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">ref</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> is that same base <strong>with the adapter switched off</strong>.
Just call it inside the <code>policy.disable_adapter()</code> context manager — nothing extra to load.</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>This is the payoff from chapter 2</div><div class="admonitionContent_BuS1"><p>Had we done full fine-tuning back in chapter 2, we'd now need two complete copies of the model in memory.
Choosing LoRA from the start makes the reference model cost <strong>zero additional bytes</strong> of VRAM.
That's an architectural reason, not merely a way to save memory during training.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-preparing-the-data">6. Preparing the Data<a href="https://kobkrit.com/en/blog/llm-04-dpo#6-preparing-the-data" class="hash-link" aria-label="Direct link to 6. Preparing the Data" title="Direct link to 6. Preparing the Data" translate="no">​</a></h2>
<p>DPO data needs exactly three columns: <code>prompt</code>, <code>chosen</code>, <code>rejected</code>.</p>
<p><strong>Set 1 — <code>iapp/dpo_thai_tutorial</code></strong> (100 pairs, Apache-2.0)
A dataset I built myself for this series and released under Apache-2.0 so anyone can reuse it.
Hand-curated Thai preference pairs, emphasizing politeness and natural-sounding language.</p>
<p><strong>Set 2 — roughly 400 pairs built from scratch</strong> out of <code>airesearch/wangchanx-seed-free-synthetic-instruct-thai-120k</code>.
The construction is about as direct as it gets:</p>
<ul>
<li class=""><code>chosen</code> = the Thai reference answer shipped with the dataset</li>
<li class=""><code>rejected</code> = <strong>the base model's own output under greedy decoding</strong></li>
</ul>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">build_rejected</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">prompt</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">with</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">no_grad</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> policy</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">disable_adapter</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        out </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> policy</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">generate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">**</span><span class="token plain">tok</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">prompt</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> return_tensors</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"pt"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">to</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"cuda"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                              max_new_tokens</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">192</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> do_sample</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">False</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> tok</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">decode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">out</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> skip_special_tokens</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">[</span><span class="token builtin">len</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">prompt</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token punctuation" style="color:#393A34">]</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>Why self-generated rejected answers beat sourced ones</div><div class="admonitionContent_BuS1"><p>Qwen3-0.6B's greedy answers to Thai prompts <strong>frequently drift into English mid-sentence</strong>.
That is a real defect of this specific model, not a defect we invented.</p><p>Using it as <code>rejected</code> points the gradient precisely at the behavior we want to fix,
and lines up exactly with the <code>th_ratio</code> metric we'll measure in section 8.
If you pull rejected answers from a different model, you're teaching your model "don't be that other model," which isn't what you want.</p></div></div>
<p>That gives roughly <strong>500 pairs</strong>, with 15% held out for evaluation and never touched during training.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-the-main-code">7. The Main Code<a href="https://kobkrit.com/en/blog/llm-04-dpo#7-the-main-code" class="hash-link" aria-label="Direct link to 7. The Main Code" title="Direct link to 7. The Main Code" translate="no">​</a></h2>
<p>The point of this section isn't calling a library. It's <strong>writing the DPO loss by hand</strong>
and then proving it matches the real thing. If the equations in section 3 didn't convince you, this code will.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="71-twenty-five-lines-written-from-scratch">7.1 Twenty-five lines, written from scratch<a href="https://kobkrit.com/en/blog/llm-04-dpo#71-twenty-five-lines-written-from-scratch" class="hash-link" aria-label="Direct link to 7.1 Twenty-five lines, written from scratch" title="Direct link to 7.1 Twenty-five lines, written from scratch" translate="no">​</a></h3>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">nn</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">functional </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> F</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">seq_logp</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">model</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> input_ids</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> attention_mask</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> labels</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token triple-quoted-string string" style="color:#e3116c">"""Sum of log-probs over the ANSWER ONLY (prompt tokens are masked to -100)"""</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    logits </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> model</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">input_ids</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">input_ids</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> attention_mask</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">attention_mask</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">logits</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    logits </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> logits</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">:</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">:</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">:</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain">                   </span><span class="token comment" style="color:#999988;font-style:italic"># position t predicts the token at t+1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    target </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> labels</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">:</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">:</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain">                       </span><span class="token comment" style="color:#999988;font-style:italic"># so the targets shift by 1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    mask </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> target</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ne</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">100</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                       </span><span class="token comment" style="color:#999988;font-style:italic"># count answer tokens only</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    target </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> target</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">masked_fill</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">~</span><span class="token plain">mask</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># keep gather from breaking on -100</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    logp </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">log_softmax</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">logits</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> dim</span><span class="token operator" style="color:#393A34">=</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    tokp </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> logp</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">gather</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> target</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">unsqueeze</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">squeeze</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">tokp </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> mask</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">sum</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                 </span><span class="token comment" style="color:#999988;font-style:italic"># [B] — a sum, not a mean</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">dpo_loss</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">policy</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> batch</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> beta</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    pi_w </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> seq_logp</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">policy</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> batch</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"chosen_ids"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">   batch</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"chosen_mask"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">   batch</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"chosen_labels"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    pi_l </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> seq_logp</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">policy</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> batch</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"rejected_ids"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> batch</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"rejected_mask"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> batch</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"rejected_labels"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">with</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">no_grad</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> policy</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">disable_adapter</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># reference: adapter off + no gradient</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        ref_w </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> seq_logp</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">policy</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> batch</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"chosen_ids"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">   batch</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"chosen_mask"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">   batch</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"chosen_labels"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        ref_l </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> seq_logp</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">policy</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> batch</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"rejected_ids"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> batch</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"rejected_mask"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> batch</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"rejected_labels"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    delta </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">pi_w </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> ref_w</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">pi_l </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> ref_l</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                </span><span class="token comment" style="color:#999988;font-style:italic"># the Δ from section 4</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    loss </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token plain">F</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">logsigmoid</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">beta </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> delta</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">mean</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">              </span><span class="token comment" style="color:#999988;font-style:italic"># equation 3.4, literally</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    r_w </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> beta </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">pi_w </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> ref_w</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">detach</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                   </span><span class="token comment" style="color:#999988;font-style:italic"># implicit reward of chosen</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    r_l </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> beta </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">pi_l </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> ref_l</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">detach</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                   </span><span class="token comment" style="color:#999988;font-style:italic"># implicit reward of rejected</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> loss</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> r_w</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> r_l</span><br></span></code></pre></div></div>
<p>All of DPO is right there. Nothing else is hiding.
The <code>delta</code> line is equation 3.4 transcribed one-to-one, and <code>-F.logsigmoid(beta * delta)</code> is the entire loss.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="72-proving-it-matches-trl">7.2 Proving it matches TRL<a href="https://kobkrit.com/en/blog/llm-04-dpo#72-proving-it-matches-trl" class="hash-link" aria-label="Direct link to 7.2 Proving it matches TRL" title="Direct link to 7.2 Proving it matches TRL" translate="no">​</a></h3>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> trl </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> DPOTrainer</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> DPOConfig</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">cfg </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> DPOConfig</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    output_dir</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"dpo-out"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    beta</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    loss_type</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"sigmoid"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">              </span><span class="token comment" style="color:#999988;font-style:italic"># must match the formula we wrote by hand</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    label_smoothing</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">              </span><span class="token comment" style="color:#999988;font-style:italic"># anything nonzero and it stops being equation 3.4</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    per_device_train_batch_size</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    gradient_accumulation_steps</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">8</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># effective batch = 16</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    num_train_epochs</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    learning_rate</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">5e-6</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">               </span><span class="token comment" style="color:#999988;font-style:italic"># much lower than SFT — see the warning below</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    lr_scheduler_type</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"cosine"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    warmup_ratio</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    max_length</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">768</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    max_prompt_length</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">256</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    fp16</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">                        </span><span class="token comment" style="color:#999988;font-style:italic"># the T4 has no bf16</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    logging_steps</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">5</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">trainer </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> DPOTrainer</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">model</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">policy</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> args</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">cfg</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> train_dataset</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">train_ds</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> processing_class</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">tok</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># Critical: a freshly created LoRA has lora_B = 0, so the policy is exactly the</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># reference. The margin is zero and BOTH implementations return ln 2 — even if the</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># formula is wrong. Perturb the weights first or the assert proves nothing.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">with</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">no_grad</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> p </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> policy</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">named_parameters</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"lora_B"</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            p</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">add_</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">randn_like</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">p</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.01</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">loss_manual</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> _</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> _ </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> dpo_loss</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">policy</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> batch</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> beta</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">loss_trl </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> trainer</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">compute_loss</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">policy</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> trl_batch</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">assert</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">allclose</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">loss_manual</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> loss_trl</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> atol</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1e-4</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"match:"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> loss_manual</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">item</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> loss_trl</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">item</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>This is the moment the article proves itself instead of asking you to take its word</div><div class="admonitionContent_BuS1"><p>Most tutorials stop at "call <code>DPOTrainer</code> and it works."
That <code>assert</code> above says the equation we derived across all of section 3 produces the same value as the library the whole world uses.
If the assert passes, you understand DPO well enough to implement it — not just well enough to call it.</p></div></div>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>If the assert fails, don't blame your own code yet</div><div class="admonitionContent_BuS1"><p>The usual causes, in order of frequency: <code>label_smoothing</code> isn't zero, <code>loss_type</code> isn't <code>"sigmoid"</code>,
the two sides weren't fed the same example, or the padding/masking doesn't line up.
All four are mismatched <strong>definitions</strong>, not bugs — and hunting them down is the best lesson in this section.</p></div></div>
<div class="theme-admonition theme-admonition-danger admonition_xJq3 alert alert--danger"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"></path></svg></span>DPO needs a much lower learning rate than SFT</div><div class="admonitionContent_BuS1"><p>SFT with LoRA is perfectly happy at <code>2e-4</code>, but DPO at <code>2e-4</code> will send the policy running away from the reference
within a few dozen steps, and the language will degrade to the point of being unreadable.</p><p>Start at <strong><code>5e-6</code></strong>, because DPO isn't teaching new content.
It's merely <strong>tilting a probability distribution that already exists</strong>, which takes far less force.</p></div></div>
<p>Total training time on a T4 is around <strong>9 minutes</strong> for 500 pairs over 2 epochs.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="8-results">8. Results<a href="https://kobkrit.com/en/blog/llm-04-dpo#8-results" class="hash-link" aria-label="Direct link to 8. Results" title="Direct link to 8. Results" translate="no">​</a></h2>
<p>The notebook measures three things and writes them to <code>results.json</code>:</p>
<ol>
<li class=""><strong>Held-out preference accuracy</strong> — the fraction of pairs where the implicit reward of chosen exceeds rejected, with a <strong>Wilson 95% CI</strong></li>
<li class=""><strong>The distribution of implicit reward margins</strong> (<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mover accent="true"><mi>r</mi><mo>^</mo></mover><mi>w</mi></msub><mo>−</mo><msub><mover accent="true"><mi>r</mi><mo>^</mo></mover><mi>l</mi></msub></mrow><annotation encoding="application/x-tex">\hat r_w - \hat r_l</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1944em"><span class="mord">^</span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0269em">w</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1944em"><span class="mord">^</span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0197em">l</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span>) — the whole distribution, not just the mean</li>
<li class=""><strong><code>th_ratio</code></strong> — the fraction of Thai characters in the model's generated answers, this series' standing metric for catching silent drift into English</li>
</ol>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>Why you look at the whole distribution, not just the mean</div><div class="admonitionContent_BuS1"><p>A beautiful average margin can come from a handful of pairs with enormous margins while most pairs still sit near zero.
A histogram tells you that; a single number conceals it.
And accuracy without a CI still isn't an experimental result, as we've said since chapter 1.</p></div></div>
<div class="root_IS5b"><div class="picker_cO8e"><span class="pickerLabel_sE2x" id="llmcourse-bac-picker">Prompt</span><div class="pickerButtons_j7L1" role="tablist" aria-labelledby="llmcourse-bac-picker"><button type="button" role="tab" id="llmcourse-bac-tab-0" aria-selected="true" aria-controls="llmcourse-bac-panel-0" tabindex="0" class="pickerButton_gFO3 pickerButtonActive_xIUp">1</button><button type="button" role="tab" id="llmcourse-bac-tab-1" aria-selected="false" aria-controls="llmcourse-bac-panel-1" tabindex="-1" class="pickerButton_gFO3">2</button></div></div><blockquote class="prompt_O4Wp" lang="th"><span class="promptLabel_h2F6">Prompt</span>อธิบายว่าทำไมท้องฟ้าถึงเป็นสีฟ้า แบบสั้น ๆ</blockquote><div class="grid_h_9T" id="llmcourse-bac-panel-0" role="tabpanel" aria-labelledby="llmcourse-bac-tab-0" style="grid-template-columns:repeat(auto-fit, minmax(min(100%, 260px), 1fr))"><article class="card_S27b"><header class="cardHeader_w7wJ"><h4 class="cardTitle_NUQN">base</h4><div class="badges_pXcS"><span class="badge_wUaQ badgeBad_WFwi" title="Share of non-whitespace characters that are Thai script">Thai 18%</span><span class="badge_wUaQ">41 tokens</span></div></header><div class="output_VSGg" lang="th">The sky appears blue because of Rayleigh scattering. ท้องฟ้า is blue เพราะ light scatter ครับ. Shorter wavelengths scatter more than longer ones.</div></article><article class="card_S27b"><header class="cardHeader_w7wJ"><h4 class="cardTitle_NUQN">sft</h4><div class="badges_pXcS"><span class="badge_wUaQ badgeGood_MHH_" title="Share of non-whitespace characters that are Thai script">Thai 99%</span><span class="badge_wUaQ">78 tokens</span></div></header><div class="output_VSGg" lang="th">ท้องฟ้าเป็นสีฟ้าเพราะแสงอาทิตย์กระทบกับโมเลกุลของอากาศแล้วเกิดการกระเจิงแบบเรย์ลี ซึ่งแสงสีน้ำเงินที่มีความยาวคลื่นสั้นกว่าจะกระเจิงได้มากกว่าแสงสีแดง เราจึงมองเห็นท้องฟ้าเป็นสีฟ้าครับ</div></article></div><p class="status_mfC7">Showing the built-in sample.</p></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-thing-that-will-alarm-you-the-first-time-you-read-the-logs">The thing that will alarm you the first time you read the logs<a href="https://kobkrit.com/en/blog/llm-04-dpo#the-thing-that-will-alarm-you-the-first-time-you-read-the-logs" class="hash-link" aria-label="Direct link to The thing that will alarm you the first time you read the logs" title="Direct link to The thing that will alarm you the first time you read the logs" translate="no">​</a></h3>
<p>During training you'll see <code>rewards/chosen</code> and <code>rewards/rejected</code> in TRL's logs,
and what happens nearly every time is that <strong>both values drift negative together</strong> while <code>rewards/margins</code> keeps widening.</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>This is normal, not a failure</div><div class="admonitionContent_BuS1"><p>Remember the definition: <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>r</mi><mo>^</mo></mover><mo>=</mo><mi>β</mi><mi>log</mi><mo>⁡</mo><mo stretchy="false">(</mo><msub><mi>π</mi><mi>θ</mi></msub><mi mathvariant="normal">/</mi><msub><mi>π</mi><mtext>ref</mtext></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\hat r = \beta\log(\pi_\theta/\pi_{\text{ref}})</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1944em"><span class="mord">^</span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mop">lo<span style="margin-right:0.0139em">g</span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord">/</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">ref</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span>, so a negative <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>r</mi><mo>^</mo></mover></mrow><annotation encoding="application/x-tex">\hat r</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1944em"><span class="mord">^</span></span></span></span></span></span></span></span></span></span> means
the policy assigns that text <strong>less</strong> probability than the reference does.</p><p>DPO was never told to "make chosen more probable." It was told only to <strong>"widen the gap."</strong>
Pushing rejected down hard while pushing chosen down gently satisfies that just as well — and is often the easier path.</p><p>So what you watch is the <strong>margin</strong> and the <strong>held-out accuracy</strong>, not the absolute level of the rewards.
That said, if <code>rewards/chosen</code> plunges very deep (below −10, say), that starts to signal the model is abandoning the reference — lower the LR or raise <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi></mrow><annotation encoding="application/x-tex">\beta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span></span></span></span>.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="9-comparison">9. Comparison<a href="https://kobkrit.com/en/blog/llm-04-dpo#9-comparison" class="hash-link" aria-label="Direct link to 9. Comparison" title="Direct link to 9. Comparison" translate="no">​</a></h2>
<table><thead><tr><th>Model</th><th>Pref. acc (95% CI)</th><th>Mean margin</th><th><code>th_ratio</code></th><th>Mean length</th><th>Training time</th></tr></thead><tbody><tr><td>SFT from chapter 2 (starting point)</td><td>baseline</td><td>0</td><td>baseline</td><td>baseline</td><td>—</td></tr><tr><td>DPO, β = 0.1</td><td>?</td><td>?</td><td>should rise</td><td>?</td><td>~9 min</td></tr><tr><td>DPO, β = 0.5</td><td>?</td><td>smaller</td><td>?</td><td>?</td><td>~9 min</td></tr></tbody></table>
<p>The pattern you <strong>should expect</strong>: preference accuracy up clearly, margins positive and widening,
and <code>th_ratio</code> moving up because every rejected answer was generated from outputs that drifted into English.</p>
<p>If you see something else, read it this way:</p>
<ul>
<li class=""><strong>Accuracy barely moves and the margin is near zero</strong> → <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi></mrow><annotation encoding="application/x-tex">\beta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span></span></span></span> too high or LR too low; the model has hardly left the reference</li>
<li class=""><strong>Train accuracy is sky-high but held-out doesn't move</strong> → overfitting on 500 pairs, which really is very few</li>
<li class=""><strong><code>th_ratio</code> rises but the answers read strangely</strong> → the policy has drifted too far from the reference; lower the LR or raise <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi></mrow><annotation encoding="application/x-tex">\beta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span></span></span></span></li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="traps-to-watch-for">Traps to watch for<a href="https://kobkrit.com/en/blog/llm-04-dpo#traps-to-watch-for" class="hash-link" aria-label="Direct link to Traps to watch for" title="Direct link to Traps to watch for" translate="no">​</a></h3>
<p><strong>1. Forgetting <code>torch.no_grad()</code> on the reference forward pass</strong>
You get no error at all, but VRAM spikes and may OOM — and if the reference isn't truly frozen,
equation 3.4 stops being correct. This is the quietest bug in the chapter.</p>
<p><strong>2. Including the prompt tokens' log-probs</strong>
The prompt is the same for chosen and rejected, so in theory it should cancel.
In practice, padding and differing lengths mean it <strong>doesn't cancel exactly</strong>, and the margin comes out skewed.
Always mask the prompt to <code>-100</code> (the <code>mask = target.ne(-100)</code> line in section 7.1 is where that happens).</p>
<p><strong>3. Length bias — DPO systematically prefers longer answers</strong>
Because we take a <strong>sum</strong> of log-probs rather than a mean, longer answers have more room to accumulate a difference.
If <code>chosen</code> is already longer than <code>rejected</code> on average in your data,
the model may just learn "answer at length" instead of "answer well."</p>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>Always measure length before and after, and report it honestly</div><div class="admonitionContent_BuS1"><p>The notebook always prints the mean token length of answers before and after DPO.
If length rises meaningfully, your first suspicion should be that part of the "quality improvement" is length bias.
The easy check is to compare the mean length of <code>chosen</code> against <code>rejected</code> in the dataset, before training even starts.</p></div></div>
<p><strong>4. Choosing <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi></mrow><annotation encoding="application/x-tex">\beta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span></span></span></span> in the wrong direction</strong>
<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi></mrow><annotation encoding="application/x-tex">\beta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span></span></span></span> too low → the policy runs away from the reference until the language breaks (see figure 4.3 at <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi><mo>=</mo><mn>0.1</mn></mrow><annotation encoding="application/x-tex">\beta = 0.1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0.1</span></span></span></span> on the toy example)
<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi></mrow><annotation encoding="application/x-tex">\beta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span></span></span></span> too high → almost nothing moves and you've burned training time for free
<code>0.1</code> is the most common default and should be your starting point, not your ending point.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="10-summary">10. Summary<a href="https://kobkrit.com/en/blog/llm-04-dpo#10-summary" class="hash-link" aria-label="Direct link to 10. Summary" title="Direct link to 10. Summary" translate="no">​</a></h2>
<ul>
<li class=""><strong>DPO doesn't approximate RLHF — it solves the same equation in closed form</strong>; the reward model and RL loop cancel algebraically</li>
<li class=""><strong><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>Z</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">Z(x)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0715em">Z</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span></span></span></span> disappears because Bradley-Terry only cares about the difference of rewards</strong> — this is the key to the whole chapter</li>
<li class=""><strong>A language model is its own reward model</strong>, via the implicit reward <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>r</mi><mo>^</mo></mover><mo>=</mo><mi>β</mi><mi>log</mi><mo>⁡</mo><mo stretchy="false">(</mo><msub><mi>π</mi><mi>θ</mi></msub><mi mathvariant="normal">/</mi><msub><mi>π</mi><mtext>ref</mtext></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\hat r = \beta\log(\pi_\theta/\pi_{\text{ref}})</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1944em"><span class="mord">^</span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mop">lo<span style="margin-right:0.0139em">g</span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord">/</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">ref</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span></li>
<li class=""><strong>The gradient is weighted by the model's own error</strong> — pairs it already ranks correctly contribute almost nothing</li>
<li class=""><strong><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi></mrow><annotation encoding="application/x-tex">\beta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span></span></span></span> is the trade-off dial</strong> between indulging preferences and preserving existing capability</li>
<li class=""><strong>A very low LR (5e-6)</strong>, because we're tilting a distribution, not teaching new knowledge</li>
<li class=""><strong>Both rewards drifting down together is normal</strong> — watch the margin, not the absolute level</li>
<li class=""><strong>Always measure answer length</strong>, because length bias impersonates quality remarkably well</li>
</ul>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>Limitations of this experiment</div><div class="admonitionContent_BuS1"><p><strong>DPO is strictly offline.</strong> It learns only from the answer pairs already sitting in the file.
What it can do is <strong>re-rank behaviors the model was already capable of sampling</strong>.
It can never <strong>discover</strong> a way of answering that the base model never produced, because nobody ever put that way into the <code>chosen</code> column.</p><p>That gap is exactly why chapter 5 (GRPO) has to exist — for when the model must <strong>sample its own answers to learn from</strong>,
rather than merely ranking what someone prepared for it.</p><p>And one more thing: <strong>500 pairs is a demonstration of the mechanism, not real alignment.</strong>
Production alignment work uses preference pairs in the tens to hundreds of thousands — several orders of magnitude more.
What you get from this chapter is an understanding of how the equations work and what each dial does, and that transfers to real scale.
But don't cite these results as evidence that you got a better Thai model.</p></div></div>
<p><strong>Next chapter:</strong> <a class="" href="https://kobkrit.com/en/blog/llm-05-grpo">GRPO</a> — when ranking what already exists stops being enough,
and we let the model sample several of its own answers and compare them against each other, with no PPO-style value function.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="references">References<a href="https://kobkrit.com/en/blog/llm-04-dpo#references" class="hash-link" aria-label="Direct link to References" title="Direct link to References" translate="no">​</a></h2>
<ol>
<li class="">Rafailov et al. (2023). <a href="https://arxiv.org/abs/2305.18290" target="_blank" rel="noopener noreferrer" class="">Direct Preference Optimization: Your Language Model is Secretly a Reward Model</a> — the original DPO paper -- the derivation in section 3</li>
<li class="">Bradley &amp; Terry (1952). <a href="https://doi.org/10.2307/2334029" target="_blank" rel="noopener noreferrer" class="">Rank Analysis of Incomplete Block Designs: I. The Method of Paired Comparisons</a> — the Bradley-Terry model every reward model rests on</li>
<li class="">Azar et al. (2023). <a href="https://arxiv.org/abs/2310.12036" target="_blank" rel="noopener noreferrer" class="">A General Theoretical Paradigm to Understand Learning from Human Preferences</a> — IPO: identifies DPO's overfitting-to-preferences weakness</li>
<li class="">Ethayarajh et al. (2024). <a href="https://arxiv.org/abs/2402.01306" target="_blank" rel="noopener noreferrer" class="">KTO: Model Alignment as Prospect Theoretic Optimization</a> — KTO: an alternative that needs no chosen/rejected pairs</li>
<li class="">Park et al. (2024). <a href="https://arxiv.org/abs/2403.19159" target="_blank" rel="noopener noreferrer" class="">Disentangling Length from Quality in Direct Preference Optimization</a> — the DPO length bias that section 9 warns about</li>
<li class="">Tang et al. (2024). <a href="https://arxiv.org/abs/2405.08448" target="_blank" rel="noopener noreferrer" class="">Understanding the performance gap between online and offline alignment algorithms</a> — why offline (DPO) trails online (PPO/GRPO)</li>
<li class="">Ouyang et al. (2022). <a href="https://arxiv.org/abs/2203.02155" target="_blank" rel="noopener noreferrer" class="">Training language models to follow instructions with human feedback</a> — InstructGPT: the origin of the whole SFT -&gt; RM -&gt; PPO pipeline</li>
</ol>
<hr>
<p><em>The writing, code and notebooks in this series are licensed under <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank" rel="noopener noreferrer" class="">CC BY-NC-SA 4.0</a> — reuse and adapt them freely with attribution, for non-commercial purposes, and share your adaptations under the same terms. Third-party models and datasets referenced here keep their own licences.</em></p>
<nav class="nav_RfLT" aria-label="Thai LLM tutorial series navigation"><p class="heading_XRWm">Thai LLM series<span class="progress_f8e8">Part 4 of 10</span></p><ol class="list_U31a"><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-01-continue-pretraining"><span class="number_u3BE" aria-hidden="true">1</span><span class="title_BPvL">Continue Pretraining</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-02-sft-lora"><span class="number_u3BE" aria-hidden="true">2</span><span class="title_BPvL">SFT and LoRA</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo"><span class="number_u3BE" aria-hidden="true">3</span><span class="title_BPvL">RLHF and PPO</span></a></li><li class="item_Y10l"><span class="chip_DDpP chipCurrent_BGpo" aria-current="step"><span class="number_u3BE" aria-hidden="true">4</span><span class="title_BPvL">DPO: Direct Preference Optimization</span><span class="srOnly_owtF">(you are here)</span></span></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-05-grpo"><span class="number_u3BE" aria-hidden="true">5</span><span class="title_BPvL">GRPO</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-06-context-distillation"><span class="number_u3BE" aria-hidden="true">6</span><span class="title_BPvL">Context Distillation</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-07-model-distillation"><span class="number_u3BE" aria-hidden="true">7</span><span class="title_BPvL">Model Distillation</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-08-guardrails"><span class="number_u3BE" aria-hidden="true">8</span><span class="title_BPvL">Guardrails</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-09-benchmarking"><span class="number_u3BE" aria-hidden="true">9</span><span class="title_BPvL">Benchmarking</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-10-deployment"><span class="number_u3BE" aria-hidden="true">10</span><span class="title_BPvL">Deployment</span></a></li></ol></nav>]]></content>
        <author>
            <name>Kobkrit Viriyayudhakorn</name>
            <uri>https://github.com/kobkrit</uri>
        </author>
        <category label="ai" term="ai"/>
        <category label="llm" term="llm"/>
        <category label="thai" term="thai"/>
        <category label="tutorial" term="tutorial"/>
        <category label="fine-tuning" term="fine-tuning"/>
        <category label="alignment" term="alignment"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[LLM 5/10] GRPO: Deleting the Value Network and Letting a Group of Answers Be Its Own Baseline]]></title>
        <id>https://kobkrit.com/en/blog/llm-05-grpo</id>
        <link href="https://kobkrit.com/en/blog/llm-05-grpo"/>
        <updated>2026-07-20T17:00:00.000Z</updated>
        <summary type="html"><![CDATA[Deriving GRPO from a single question — how can a group mean replace the value network? — then training a model to solve Thai math problems with rewards checked purely by code, not a single human preference pair, plus a straight answer to whether this kind of RL 'creates' capability or merely 'sharpens' it]]></summary>
        <content type="html"><![CDATA[<p>The last chapter ended on DPO's gap: it can only rank answers someone prepared in a file.
And chapter 3 paid PPO's full price: 4 models in VRAM, plus an entire value network to train on the side.
This chapter combines the good halves of both — the model <strong>samples its own answers to learn from</strong>, genuine RL —
but deletes the value network wholesale, using a statistical observation so simple it's almost irritating nobody framed it sooner:
if you sample several answers to the same problem, <strong>the group's mean reward is already the baseline the value network was trying to estimate</strong>.
And if the problem can be graded by code, we need no human preference data at all — zero pairs, zero baht.</p>
<a class="badge_rUYD" href="https://colab.research.google.com/github/kobkrit/thai-llm-tutorials/blob/main/notebooks/05_grpo.ipynb" target="_blank" rel="noopener noreferrer" aria-label="Open the notebook 05_grpo.ipynb in Google Colab (opens in a new tab)"><svg class="mark_NB8U" viewBox="0 0 24 24" width="20" height="20" aria-hidden="true" focusable="false"><mask id="llmcourse-colab-cut"><rect x="0" y="0" width="24" height="24" fill="#fff"></rect><circle cx="16.2" cy="12" r="6.1" fill="#000"></circle></mask><circle cx="8.4" cy="12" r="4.6" fill="none" stroke="#F9AB00" stroke-width="3.1" mask="url(#llmcourse-colab-cut)"></circle><circle cx="16.2" cy="12" r="4.6" fill="none" stroke="#E8710A" stroke-width="3.1"></circle></svg><span class="text_QXpz">Open in Colab</span><code class="notebook_ntO0">05_grpo.ipynb</code></a>
<nav class="nav_RfLT" aria-label="Thai LLM tutorial series navigation"><p class="heading_XRWm">Thai LLM series<span class="progress_f8e8">Part 5 of 10</span></p><ol class="list_U31a"><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-01-continue-pretraining"><span class="number_u3BE" aria-hidden="true">1</span><span class="title_BPvL">Continue Pretraining</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-02-sft-lora"><span class="number_u3BE" aria-hidden="true">2</span><span class="title_BPvL">SFT and LoRA</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo"><span class="number_u3BE" aria-hidden="true">3</span><span class="title_BPvL">RLHF and PPO</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-04-dpo"><span class="number_u3BE" aria-hidden="true">4</span><span class="title_BPvL">DPO: Direct Preference Optimization</span></a></li><li class="item_Y10l"><span class="chip_DDpP chipCurrent_BGpo" aria-current="step"><span class="number_u3BE" aria-hidden="true">5</span><span class="title_BPvL">GRPO</span><span class="srOnly_owtF">(you are here)</span></span></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-06-context-distillation"><span class="number_u3BE" aria-hidden="true">6</span><span class="title_BPvL">Context Distillation</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-07-model-distillation"><span class="number_u3BE" aria-hidden="true">7</span><span class="title_BPvL">Model Distillation</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-08-guardrails"><span class="number_u3BE" aria-hidden="true">8</span><span class="title_BPvL">Guardrails</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-09-benchmarking"><span class="number_u3BE" aria-hidden="true">9</span><span class="title_BPvL">Benchmarking</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-10-deployment"><span class="number_u3BE" aria-hidden="true">10</span><span class="title_BPvL">Deployment</span></a></li></ol></nav>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-the-problem">1. The Problem<a href="https://kobkrit.com/en/blog/llm-05-grpo#1-the-problem" class="hash-link" aria-label="Direct link to 1. The Problem" title="Direct link to 1. The Problem" translate="no">​</a></h2>
<p>Set the problem up like this: teach Qwen3-0.6B to solve Thai math word problems,
where the final answer is a single number that a one-line <code>==</code> can grade.</p>
<p>Walk through the tools of the past three chapters one at a time, and none of them fits this job:</p>
<table><thead><tr><th>Method</th><th>Can the model sample its own answers and learn from them?</th><th>Human labels needed</th><th>Models in VRAM</th></tr></thead><tbody><tr><td>SFT (ch. 2)</td><td>No — imitates the answer key only</td><td>A human-written answer per example</td><td>1</td></tr><tr><td>PPO (ch. 3)</td><td>Yes</td><td>Preference pairs to train a reward model</td><td>4</td></tr><tr><td>DPO (ch. 4)</td><td>No — purely offline</td><td>Preference pairs</td><td>2 (1 with LoRA)</td></tr></tbody></table>
<ul>
<li class=""><strong>SFT</strong> teaches imitation of a worked solution, but never lets the model try and fail on its own —
it never sees which of <em>its own</em> lines of reasoning lead to the right answer.</li>
<li class=""><strong>PPO</strong> lets the model try, but charges you a reward model trained from preference pairs
plus an entire value network — for a task whose reward can be written directly as a Python function.</li>
<li class=""><strong>DPO</strong> deletes RL elegantly, but can only rank answers that <em>already exist</em> in the dataset.
A math problem needs the model to explore many paths and reinforce the ones that reach the right answer.</li>
</ul>
<p>So this chapter's question is narrow and sharp: <strong>which parts of PPO are truly necessary, and which can be deleted,
when our reward can be checked by code?</strong></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-what-were-going-to-do">2. What We're Going to Do<a href="https://kobkrit.com/en/blog/llm-05-grpo#2-what-were-going-to-do" class="hash-link" aria-label="Direct link to 2. What We're Going to Do" title="Direct link to 2. What We're Going to Do" translate="no">​</a></h2>
<p>Go back to the value network's job in chapter 3: it exists to answer one question —
<em>"on average, roughly what reward should this prompt earn?"</em> — to serve as a <strong>baseline</strong>
subtracted from the actual reward. Answers "better than average" get positive gradient; "worse than average," negative.
Without that baseline, the policy gradient is so noisy it can barely train at all.</p>
<p>PPO answers that question by <strong>training an entire extra model</strong> to predict this average.
GRPO answers it by <strong>sampling until you can see it with your own eyes</strong>:</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>The core idea of this chapter</div><div class="admonitionContent_BuS1"><p>Sample <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi></mrow><annotation encoding="application/x-tex">G</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal">G</span></span></span></span> answers from the same prompt and average the group's rewards —
that mean is already, by definition, an unbiased estimate of "the expected reward for this prompt."
It is the very thing the value network tries to approximate, except it needs no training, no loading, and can never drift wrong.
<strong>The entire value network can therefore be deleted.</strong> And when the reward is checked by code (numeric answer right or wrong),
the reward model and the human preference data vanish along with it — zero labels remain.</p></div></div>
<p>This is <strong>GRPO (Group Relative Policy Optimization)</strong>, proposed by Shao et al. (2024) in DeepSeekMath,
and it is the same engine that trained DeepSeek-R1. The broader approach carries the collective name
<strong>RLVR (RL with Verifiable Rewards)</strong> — RL whose reward comes from a verifier, not from human taste.</p>
<p>And let's set expectations straight from the top of the chapter: current evidence points to this kind of RL mostly
<strong>"sharpening" ability the base model already has at pass@8 so it surfaces at pass@1</strong>,
rather than creating new capability from zero. We return to this with measuring instruments in section 9.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-the-equations">3. The Equations<a href="https://kobkrit.com/en/blog/llm-05-grpo#3-the-equations" class="hash-link" aria-label="Direct link to 3. The Equations" title="Direct link to 3. The Equations" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="31-the-group-relative-advantage--the-whole-heart-in-one-line">3.1 The group-relative advantage — the whole heart in one line<a href="https://kobkrit.com/en/blog/llm-05-grpo#31-the-group-relative-advantage--the-whole-heart-in-one-line" class="hash-link" aria-label="Direct link to 3.1 The group-relative advantage — the whole heart in one line" title="Direct link to 3.1 The group-relative advantage — the whole heart in one line" translate="no">​</a></h3>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mover accent="true"><mi>A</mi><mo>^</mo></mover><mi>i</mi></msub><mo>=</mo><mfrac><mrow><msub><mi>r</mi><mi>i</mi></msub><mo>−</mo><mi mathvariant="normal">mean</mi><mo>⁡</mo><mo stretchy="false">(</mo><msub><mi>r</mi><mn>1</mn></msub><mo separator="true">,</mo><mo>…</mo><mo separator="true">,</mo><msub><mi>r</mi><mi>G</mi></msub><mo stretchy="false">)</mo></mrow><mrow><mi mathvariant="normal">std</mi><mo>⁡</mo><mo stretchy="false">(</mo><msub><mi>r</mi><mn>1</mn></msub><mo separator="true">,</mo><mo>…</mo><mo separator="true">,</mo><msub><mi>r</mi><mi>G</mi></msub><mo stretchy="false">)</mo></mrow></mfrac></mrow><annotation encoding="application/x-tex">\hat A_i = \frac{r_i - \operatorname{mean}(r_1,\dots,r_G)}{\operatorname{std}(r_1,\dots,r_G)}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0968em;vertical-align:-0.15em"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9468em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal">A</span></span><span style="top:-3.2523em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1111em"><span class="mord">^</span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.363em;vertical-align:-0.936em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.427em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mop"><span class="mord mathrm">std</span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="minner">…</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">G</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mop"><span class="mord mathrm">mean</span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="minner">…</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">G</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.936em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span>
<ul>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi></mrow><annotation encoding="application/x-tex">G</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal">G</span></span></span></span> = the number of answers sampled from the same prompt (8 in this chapter)</li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>r</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">r_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> = the reward of answer <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>i</mi></mrow><annotation encoding="application/x-tex">i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6595em"></span><span class="mord mathnormal">i</span></span></span></span></li>
<li class=""><strong>Every token of answer <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>i</mi></mrow><annotation encoding="application/x-tex">i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6595em"></span><span class="mord mathnormal">i</span></span></span></span> shares the same single <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mover accent="true"><mi>A</mi><mo>^</mo></mover><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">\hat A_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0968em;vertical-align:-0.15em"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9468em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal">A</span></span><span style="top:-3.2523em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1111em"><span class="mord">^</span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> across the whole sequence</strong> —
unlike PPO, which chases per-token advantage through the value network and GAE</li>
</ul>
<p>In plain language: <strong>"is this answer better or worse than my own other attempts at the same problem?"</strong>
No comparison across problems, no predicting the future — only a competition inside the group.</p>
<p>This short equation has one consequence that matters enormously: if the whole group earns identical rewards
(all right or all wrong), every <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mover accent="true"><mi>A</mi><mo>^</mo></mover><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">\hat A_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0968em;vertical-align:-0.15em"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9468em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal">A</span></span><span style="top:-3.2523em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1111em"><span class="mord">^</span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> is zero and that batch <strong>teaches nothing at all</strong>.
Remember that sentence — it will become both the chapter's number-one trap and its most important metric.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="32-the-full-grpo-objective">3.2 The full GRPO objective<a href="https://kobkrit.com/en/blog/llm-05-grpo#32-the-full-grpo-objective" class="hash-link" aria-label="Direct link to 3.2 The full GRPO objective" title="Direct link to 3.2 The full GRPO objective" translate="no">​</a></h3>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi mathvariant="script">J</mi><mtext>GRPO</mtext></msub><mo stretchy="false">(</mo><mi>θ</mi><mo stretchy="false">)</mo><mo>=</mo><mi mathvariant="double-struck">E</mi><mrow><mo fence="true">[</mo><mfrac><mn>1</mn><mi>G</mi></mfrac><munderover><mo>∑</mo><mrow><mi>i</mi><mo>=</mo><mn>1</mn></mrow><mi>G</mi></munderover><mfrac><mn>1</mn><mrow><mi mathvariant="normal">∣</mi><msub><mi>o</mi><mi>i</mi></msub><mi mathvariant="normal">∣</mi></mrow></mfrac><munderover><mo>∑</mo><mrow><mi>t</mi><mo>=</mo><mn>1</mn></mrow><mrow><mi mathvariant="normal">∣</mi><msub><mi>o</mi><mi>i</mi></msub><mi mathvariant="normal">∣</mi></mrow></munderover><mo fence="false" stretchy="true" minsize="1.8em" maxsize="1.8em">{</mo><mi>min</mi><mo>⁡</mo><mtext> ⁣</mtext><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">(</mo><msub><mi>ρ</mi><mrow><mi>i</mi><mo separator="true">,</mo><mi>t</mi></mrow></msub><mtext> </mtext><msub><mover accent="true"><mi>A</mi><mo>^</mo></mover><mi>i</mi></msub><mo separator="true">,</mo><mtext>&nbsp;</mtext><mi mathvariant="normal">clip</mi><mo>⁡</mo><mo stretchy="false">(</mo><msub><mi>ρ</mi><mrow><mi>i</mi><mo separator="true">,</mo><mi>t</mi></mrow></msub><mo separator="true">,</mo><mtext> </mtext><mn>1</mn><mo>−</mo><mi>ϵ</mi><mo separator="true">,</mo><mtext> </mtext><mn>1</mn><mo>+</mo><mi>ϵ</mi><mo stretchy="false">)</mo><mtext> </mtext><msub><mover accent="true"><mi>A</mi><mo>^</mo></mover><mi>i</mi></msub><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">)</mo><mo>−</mo><mi>β</mi><mtext> </mtext><msub><mi mathvariant="double-struck">D</mi><mtext>KL</mtext></msub><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">[</mo><msub><mi>π</mi><mi>θ</mi></msub><mtext> </mtext><mi mathvariant="normal">∥</mi><mtext> </mtext><msub><mi>π</mi><mtext>ref</mtext></msub><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">]</mo><mo fence="false" stretchy="true" minsize="1.8em" maxsize="1.8em">}</mo><mo fence="true">]</mo></mrow></mrow><annotation encoding="application/x-tex">\mathcal{J}_{\text{GRPO}}(\theta) = \mathbb{E}\left[\frac{1}{G}\sum_{i=1}^{G}\frac{1}{|o_i|}\sum_{t=1}^{|o_i|}\Big\{\min\!\big(\rho_{i,t}\,\hat A_i,\ \operatorname{clip}(\rho_{i,t},\,1-\epsilon,\,1+\epsilon)\,\hat A_i\big) - \beta\,\mathbb{D}_{\text{KL}}\big[\pi_\theta \,\|\, \pi_{\text{ref}}\big]\Big\}\right]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathcal" style="margin-right:0.1847em">J</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:-0.1847em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">GRPO</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.0278em">θ</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:3.6em;vertical-align:-1.55em"></span><span class="mord mathbb">E</span><span class="mspace" style="margin-right:0.1667em"></span><span class="minner"><span class="mopen"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.05em"><span style="top:-4.05em"><span class="pstrut" style="height:5.6em"></span><span style="width:0.667em;height:3.6em"><svg xmlns="http://www.w3.org/2000/svg" width="0.667em" height="3.6em" viewBox="0 0 667 3600"><path d="M403 1759 V84 H666 V0 H319 V1759 v0 v1759 v84 h347 v-84
H403z M403 1759 V0 H319 V1759 v0 v1759 v84 h84z"></path></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.55em"><span></span></span></span></span></span></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3214em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord mathnormal">G</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.8283em"><span style="top:-1.8723em;margin-left:0em"><span class="pstrut" style="height:3.05em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">i</span><span class="mrel mtight">=</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.05em"><span class="pstrut" style="height:3.05em"></span><span><span class="mop op-symbol large-op">∑</span></span></span><span style="top:-4.3em;margin-left:0em"><span class="pstrut" style="height:3.05em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">G</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.2777em"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3214em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord">∣</span><span class="mord"><span class="mord mathnormal">o</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord">∣</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.936em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.961em"><span style="top:-1.8829em;margin-left:0em"><span class="pstrut" style="height:3.05em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">t</span><span class="mrel mtight">=</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.05em"><span class="pstrut" style="height:3.05em"></span><span><span class="mop op-symbol large-op">∑</span></span></span><span style="top:-4.386em;margin-left:0em"><span class="pstrut" style="height:3.05em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">∣</span><span class="mord mtight"><span class="mord mathnormal mtight">o</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3281em"><span style="top:-2.357em;margin-left:0em;margin-right:0.0714em"><span class="pstrut" style="height:2.5em"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.143em"><span></span></span></span></span></span></span><span class="mord mtight">∣</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.2671em"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="delimsizing size2">{</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mop">min</span><span class="mspace" style="margin-right:-0.1667em"></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="delimsizing size1">(</span></span><span class="mord"><span class="mord mathnormal">ρ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">i</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">t</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9468em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal">A</span></span><span style="top:-3.2523em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1111em"><span class="mord">^</span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace">&nbsp;</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mop"><span class="mord mathrm">clip</span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">ρ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">i</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">t</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord mathnormal">ϵ</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord mathnormal">ϵ</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9468em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal">A</span></span><span style="top:-3.2523em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1111em"><span class="mord">^</span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord"><span class="delimsizing size1">)</span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathbb">D</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">KL</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord"><span class="delimsizing size1">[</span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord">∥</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">ref</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord"><span class="delimsizing size1">]</span></span><span class="mord"><span class="delimsizing size2">}</span></span><span class="mclose"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.05em"><span style="top:-4.05em"><span class="pstrut" style="height:5.6em"></span><span style="width:0.667em;height:3.6em"><svg xmlns="http://www.w3.org/2000/svg" width="0.667em" height="3.6em" viewBox="0 0 667 3600"><path d="M347 1759 V0 H0 V84 H263 V1759 v0 v1759 H0 v84 H347z
M347 1759 V0 H263 V1759 v0 v1759 h84z"></path></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.55em"><span></span></span></span></span></span></span></span></span></span></span></span>
<p>where <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>ρ</mi><mrow><mi>i</mi><mo separator="true">,</mo><mi>t</mi></mrow></msub><mo>=</mo><mstyle scriptlevel="0" displaystyle="true"><mfrac><mrow><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>o</mi><mrow><mi>i</mi><mo separator="true">,</mo><mi>t</mi></mrow></msub><mo>∣</mo><mi>q</mi><mo separator="true">,</mo><msub><mi>o</mi><mrow><mi>i</mi><mo separator="true">,</mo><mo>&lt;</mo><mi>t</mi></mrow></msub><mo stretchy="false">)</mo></mrow><mrow><msub><mi>π</mi><msub><mi>θ</mi><mtext>old</mtext></msub></msub><mo stretchy="false">(</mo><msub><mi>o</mi><mrow><mi>i</mi><mo separator="true">,</mo><mi>t</mi></mrow></msub><mo>∣</mo><mi>q</mi><mo separator="true">,</mo><msub><mi>o</mi><mrow><mi>i</mi><mo separator="true">,</mo><mo>&lt;</mo><mi>t</mi></mrow></msub><mo stretchy="false">)</mo></mrow></mfrac></mstyle></mrow><annotation encoding="application/x-tex">\rho_{i,t} = \dfrac{\pi_\theta(o_{i,t} \mid q, o_{i,&lt;t})}{\pi_{\theta_{\text{old}}}(o_{i,t} \mid q, o_{i,&lt;t})}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7167em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathnormal">ρ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">i</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">t</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.3991em;vertical-align:-0.9721em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.427em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em"><span style="top:-2.3488em;margin-left:-0.0278em;margin-right:0.0714em"><span class="pstrut" style="height:2.5em"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">old</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1512em"><span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2559em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">o</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">i</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">t</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">∣</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mord mathnormal" style="margin-right:0.0359em">q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal">o</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">i</span><span class="mpunct mtight">,</span><span class="mrel mtight">&lt;</span><span class="mord mathnormal mtight">t</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">o</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">i</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">t</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">∣</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mord mathnormal" style="margin-right:0.0359em">q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal">o</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">i</span><span class="mpunct mtight">,</span><span class="mrel mtight">&lt;</span><span class="mord mathnormal mtight">t</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.9721em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span> is the token's probability ratio against the policy at sampling time.</p>
<p>Read it piece by piece, because every piece has already passed before your eyes in this series:</p>
<ul>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>min</mi><mo>⁡</mo><mo stretchy="false">(</mo><mo>⋅</mo><mo separator="true">,</mo><mtext>&nbsp;</mtext><mi mathvariant="normal">clip</mi><mo>⁡</mo><mo stretchy="false">(</mo><mo>⋅</mo><mo stretchy="false">)</mo><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\min(\cdot,\ \operatorname{clip}(\cdot))</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mop">min</span><span class="mopen">(</span><span class="mord">⋅</span><span class="mpunct">,</span><span class="mspace">&nbsp;</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mop"><span class="mord mathrm">clip</span></span><span class="mopen">(</span><span class="mord">⋅</span><span class="mclose">))</span></span></span></span> = <strong>the same PPO clip from chapter 3, nothing new</strong> —
keeps steps from wandering too far from the point where the rollouts were sampled</li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mfrac><mn>1</mn><mrow><mi mathvariant="normal">∣</mi><msub><mi>o</mi><mi>i</mi></msub><mi mathvariant="normal">∣</mi></mrow></mfrac></mrow><annotation encoding="application/x-tex">\frac{1}{|o_i|}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.3651em;vertical-align:-0.52em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8451em"><span style="top:-2.655em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">∣</span><span class="mord mtight"><span class="mord mathnormal mtight">o</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3281em"><span style="top:-2.357em;margin-left:0em;margin-right:0.0714em"><span class="pstrut" style="height:2.5em"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.143em"><span></span></span></span></span></span></span><span class="mord mtight">∣</span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.394em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.52em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span> = per-token averaging, keeping long answers from outsized influence (think of the length bias from chapter 4)</li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi><mtext> </mtext><msub><mi mathvariant="double-struck">D</mi><mtext>KL</mtext></msub></mrow><annotation encoding="application/x-tex">\beta\,\mathbb{D}_{\text{KL}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathbb">D</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">KL</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> = the same leash as ever, tied to the same <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>π</mi><mtext>ref</mtext></msub></mrow><annotation encoding="application/x-tex">\pi_{\text{ref}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">ref</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> as chapters 3 and 4</li>
</ul>
<p>What deserves the closest reading is what is <strong>not</strong> in the equation: no <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>V</mi><mo stretchy="false">(</mo><mi>s</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">V(s)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.2222em">V</span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mclose">)</span></span></span></span>, no GAE, no critic loss.
The whole line runs on just two models (<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>π</mi><mi>θ</mi></msub></mrow><annotation encoding="application/x-tex">\pi_\theta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>π</mi><mtext>ref</mtext></msub></mrow><annotation encoding="application/x-tex">\pi_{\text{ref}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">ref</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span>) and reward numbers from a verifier.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="33-the-kl-term-isnt-computed-directly--meet-the-k3-estimator">3.3 The KL term isn't computed directly — meet the k3 estimator<a href="https://kobkrit.com/en/blog/llm-05-grpo#33-the-kl-term-isnt-computed-directly--meet-the-k3-estimator" class="hash-link" aria-label="Direct link to 3.3 The KL term isn't computed directly — meet the k3 estimator" title="Direct link to 3.3 The KL term isn't computed directly — meet the k3 estimator" translate="no">​</a></h3>
<p>True KL divergence requires summing over the entire vocabulary at every position, which is expensive and unnecessary.
GRPO estimates it from the tokens already sampled, using an estimator nicknamed <strong>k3</strong>:</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mover accent="true"><mi mathvariant="double-struck">D</mi><mo>^</mo></mover><mrow><mi>k</mi><mn>3</mn></mrow></msub><mo>=</mo><mfrac><mrow><msub><mi>π</mi><mtext>ref</mtext></msub><mo stretchy="false">(</mo><msub><mi>o</mi><mrow><mi>i</mi><mo separator="true">,</mo><mi>t</mi></mrow></msub><mo stretchy="false">)</mo></mrow><mrow><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>o</mi><mrow><mi>i</mi><mo separator="true">,</mo><mi>t</mi></mrow></msub><mo stretchy="false">)</mo></mrow></mfrac><mo>−</mo><mi>log</mi><mo>⁡</mo><mfrac><mrow><msub><mi>π</mi><mtext>ref</mtext></msub><mo stretchy="false">(</mo><msub><mi>o</mi><mrow><mi>i</mi><mo separator="true">,</mo><mi>t</mi></mrow></msub><mo stretchy="false">)</mo></mrow><mrow><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>o</mi><mrow><mi>i</mi><mo separator="true">,</mo><mi>t</mi></mrow></msub><mo stretchy="false">)</mo></mrow></mfrac><mo>−</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">\hat{\mathbb{D}}_{k3} = \frac{\pi_{\text{ref}}(o_{i,t})}{\pi_\theta(o_{i,t})} - \log\frac{\pi_{\text{ref}}(o_{i,t})}{\pi_\theta(o_{i,t})} - 1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.1023em;vertical-align:-0.15em"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9523em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathbb">D</span></span><span style="top:-3.2579em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.25em"><span class="mord">^</span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0315em">k</span><span class="mord mtight">3</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.3991em;vertical-align:-0.9721em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.427em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">o</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">i</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">t</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">ref</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">o</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">i</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">t</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.9721em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:2.3991em;vertical-align:-0.9721em"></span><span class="mop">lo<span style="margin-right:0.0139em">g</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.427em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">o</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">i</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">t</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">ref</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">o</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">i</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">t</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.9721em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">1</span></span></span></span></span>
<p>The question students always ask (and should): <em>why not just use <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>log</mi><mo>⁡</mo><mo stretchy="false">(</mo><msub><mi>π</mi><mi>θ</mi></msub><mi mathvariant="normal">/</mi><msub><mi>π</mi><mtext>ref</mtext></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\log(\pi_\theta/\pi_{\text{ref}})</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mop">lo<span style="margin-right:0.0139em">g</span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord">/</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">ref</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span> directly,
when its expectation is already the KL?</em></p>
<p>The answer: the naive estimator (called k1) <strong>is indeed unbiased, but individual samples can go negative</strong> —
about 40% of samples come out negative, even though KL cannot be negative by definition — and the variance is very high.
At realistic batch sizes the estimate swings so hard that the penalty alternately pushes and pulls.</p>
<p>k3 fixes both at once. Let <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi><mo>=</mo><msub><mi>π</mi><mtext>ref</mtext></msub><mi mathvariant="normal">/</mi><msub><mi>π</mi><mi>θ</mi></msub></mrow><annotation encoding="application/x-tex">x = \pi_{\text{ref}}/\pi_\theta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">x</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">ref</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord">/</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> and observe two facts:</p>
<ol>
<li class="">The inequality <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi><mo>−</mo><mn>1</mn><mo>≥</mo><mi>log</mi><mo>⁡</mo><mi>x</mi></mrow><annotation encoding="application/x-tex">x - 1 \geq \log x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6667em;vertical-align:-0.0833em"></span><span class="mord mathnormal">x</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.7804em;vertical-align:-0.136em"></span><span class="mord">1</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">≥</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mop">lo<span style="margin-right:0.0139em">g</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal">x</span></span></span></span> always holds, so k3 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>=</mo><mo stretchy="false">(</mo><mi>x</mi><mo>−</mo><mn>1</mn><mo stretchy="false">)</mo><mo>−</mo><mi>log</mi><mo>⁡</mo><mi>x</mi><mo>≥</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">= (x-1) - \log x \geq 0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.3669em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord">1</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mop">lo<span style="margin-right:0.0139em">g</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal">x</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">≥</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0</span></span></span></span> on <strong>every sample</strong></li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="double-struck">E</mi><msub><mi>π</mi><mi>θ</mi></msub></msub><mo stretchy="false">[</mo><mi>x</mi><mo stretchy="false">]</mo><mo>=</mo><mo>∑</mo><msub><mi>π</mi><mi>θ</mi></msub><mo>⋅</mo><mfrac><msub><mi>π</mi><mtext>ref</mtext></msub><msub><mi>π</mi><mi>θ</mi></msub></mfrac><mo>=</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">\mathbb{E}_{\pi_\theta}[x] = \sum \pi_\theta \cdot \frac{\pi_{\text{ref}}}{\pi_\theta} = 1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0059em;vertical-align:-0.2559em"></span><span class="mord"><span class="mord mathbb">E</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em"><span style="top:-2.3488em;margin-left:-0.0359em;margin-right:0.0714em"><span class="pstrut" style="height:2.5em"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1512em"><span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2559em"><span></span></span></span></span></span></span><span class="mopen">[</span><span class="mord mathnormal">x</span><span class="mclose">]</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mop op-symbol small-op" style="position:relative;top:0em">∑</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.1681em;vertical-align:-0.4509em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.7173em"><span style="top:-2.655em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em"><span style="top:-2.3488em;margin-left:-0.0359em;margin-right:0.0714em"><span class="pstrut" style="height:2.5em"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1512em"><span></span></span></span></span></span></span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.4159em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em"><span style="top:-2.3488em;margin-left:-0.0359em;margin-right:0.0714em"><span class="pstrut" style="height:2.5em"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">ref</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1512em"><span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.4509em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">1</span></span></span></span>, so the <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>x</mi><mo>−</mo><mn>1</mn><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">(x-1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord">1</span><span class="mclose">)</span></span></span></span> term has expectation zero —
it is a <strong>control variate</strong> that cancels the noise of <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>−</mo><mi>log</mi><mo>⁡</mo><mi>x</mi></mrow><annotation encoding="application/x-tex">-\log x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord">−</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mop">lo<span style="margin-right:0.0139em">g</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal">x</span></span></span></span> without touching the expectation</li>
</ol>
<p>The result is an estimator exactly as unbiased, with variance lower by an order, that can never go negative.
Figure 5.3 will show you the difference with your own eyes.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="34-an-advanced-note-dividing-by-std-isnt-as-pure-as-it-looks-drgrpo">3.4 An advanced note: dividing by std isn't as pure as it looks (Dr.GRPO)<a href="https://kobkrit.com/en/blog/llm-05-grpo#34-an-advanced-note-dividing-by-std-isnt-as-pure-as-it-looks-drgrpo" class="hash-link" aria-label="Direct link to 3.4 An advanced note: dividing by std isn't as pure as it looks (Dr.GRPO)" title="Direct link to 3.4 An advanced note: dividing by std isn't as pure as it looks (Dr.GRPO)" translate="no">​</a></h3>
<p>The division by <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">std</mi><mo>⁡</mo><mo stretchy="false">(</mo><msub><mi>r</mi><mn>1</mn></msub><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><msub><mi>r</mi><mi>G</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\operatorname{std}(r_1..r_G)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mop"><span class="mord mathrm">std</span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord">..</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">G</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span> in equation 3.1 smuggles in one bias:
groups whose rewards are nearly identical (small std — say 7 right out of 8) get their advantages <strong>amplified</strong> by an enormous factor,
while groups that genuinely disagree (large std — which carry the most information) get comparatively muted.
The net effect is a gradient tilted toward problems the model nearly already agrees with itself about.
The Dr.GRPO work (Liu et al., 2025) proposes <strong>dropping the std division entirely</strong>, keeping only the mean subtraction —
which remains a perfectly correct baseline. The widget in section 4 has a toggle so you can try both.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="35-unbiased-passk--the-tool-section-9-will-need">3.5 Unbiased pass@k — the tool section 9 will need<a href="https://kobkrit.com/en/blog/llm-05-grpo#35-unbiased-passk--the-tool-section-9-will-need" class="hash-link" aria-label="Direct link to 3.5 Unbiased pass@k — the tool section 9 will need" title="Direct link to 3.5 Unbiased pass@k — the tool section 9 will need" translate="no">​</a></h3>
<p>Sample <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">n</span></span></span></span> answers per problem, get <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow><annotation encoding="application/x-tex">c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">c</span></span></span></span> correct, and ask "given a budget of <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow><annotation encoding="application/x-tex">k</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal" style="margin-right:0.0315em">k</span></span></span></span> tries, would at least one be right?":</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mover accent="true"><mrow><mtext>pass@</mtext><mi>k</mi></mrow><mo stretchy="true">^</mo></mover><mo>=</mo><mn>1</mn><mo>−</mo><mfrac><mrow><mo fence="true">(</mo><mfrac linethickness="0px"><mrow><mi>n</mi><mo>−</mo><mi>c</mi></mrow><mi>k</mi></mfrac><mo fence="true">)</mo></mrow><mrow><mo fence="true">(</mo><mfrac linethickness="0px"><mi>n</mi><mi>k</mi></mfrac><mo fence="true">)</mo></mrow></mfrac></mrow><annotation encoding="application/x-tex">\widehat{\text{pass@}k} = 1 - \frac{\binom{n-c}{k}}{\binom{n}{k}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.1889em;vertical-align:-0.1944em"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.9944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord text"><span class="mord">pass@</span></span><span class="mord mathnormal" style="margin-right:0.0315em">k</span></span></span><span class="svg-align" style="top:-3.6944em"><span class="pstrut" style="height:3em"></span><span style="height:0.3em"><svg xmlns="http://www.w3.org/2000/svg" width="100%" height="0.3em" viewBox="0 0 2364 300" preserveAspectRatio="none"><path d="M1181 0h2l1171 176c6 0 10 5 10 11l-2 23c-1 6-5 10
-11 10h-1L1182 67 15 220h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z"></path></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em"></span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:2.6824em;vertical-align:-1.09em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.5923em"><span style="top:-2.26em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord"><span class="mopen delimcenter" style="top:0em"><span class="delimsizing size1">(</span></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.7454em"><span style="top:-2.355em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0315em">k</span></span></span></span><span style="top:-3.144em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">n</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.345em"><span></span></span></span></span></span><span class="mclose delimcenter" style="top:0em"><span class="delimsizing size1">)</span></span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.74em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord"><span class="mopen delimcenter" style="top:0em"><span class="delimsizing size1">(</span></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8523em"><span style="top:-2.355em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0315em">k</span></span></span></span><span style="top:-3.144em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">n</span><span class="mbin mtight">−</span><span class="mord mathnormal mtight">c</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.345em"><span></span></span></span></span></span><span class="mclose delimcenter" style="top:0em"><span class="delimsizing size1">)</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.09em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span>
<p>The fraction at the back is the probability that drawing <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow><annotation encoding="application/x-tex">k</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal" style="margin-right:0.0315em">k</span></span></span></span> from <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">n</span></span></span></span> finds nothing but wrong answers.
The formula people commonly misuse is <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mo>−</mo><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><mi>c</mi><mi mathvariant="normal">/</mi><mi>n</mi><msup><mo stretchy="false">)</mo><mi>k</mi></msup></mrow><annotation encoding="application/x-tex">1-(1-c/n)^k</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em"></span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mopen">(</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.0991em;vertical-align:-0.25em"></span><span class="mord mathnormal">c</span><span class="mord">/</span><span class="mord mathnormal">n</span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8491em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0315em">k</span></span></span></span></span></span></span></span></span></span></span>, which is <strong>systematically biased in your favor</strong> when <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">n</span></span></span></span> is small
(this is why the HumanEval paper by Chen et al. 2021 needed a separate appendix on exactly this).
Keep this formula close — it is the yardstick for judging whether GRPO "creates" new capability or merely "sharpens" what exists.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-seeing-the-equations">4. Seeing the Equations<a href="https://kobkrit.com/en/blog/llm-05-grpo#4-seeing-the-equations" class="hash-link" aria-label="Direct link to 4. Seeing the Equations" title="Direct link to 4. Seeing the Equations" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-one-group-teaches--and-which-groups-teach-nothing-at-all">What one group teaches — and which groups teach nothing at all<a href="https://kobkrit.com/en/blog/llm-05-grpo#what-one-group-teaches--and-which-groups-teach-nothing-at-all" class="hash-link" aria-label="Direct link to What one group teaches — and which groups teach nothing at all" title="Direct link to What one group teaches — and which groups teach nothing at all" translate="no">​</a></h3>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-05-grpo/group-advantage.light.svg" alt="A two-panel bar chart. The left panel shows positive and negative advantages of 8 answers around the group mean. The right panel shows a degenerate group where every answer earns the same reward, making all advantages zero" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-05-grpo/group-advantage.dark.svg" alt="A two-panel bar chart. The left panel shows positive and negative advantages of 8 answers around the group mean. The right panel shows a degenerate group where every answer earns the same reward, making all advantages zero" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 5.1</span>Left: the advantages of a real 8-answer group under this chapter's reward shaping (correct +1.0, format +0.3, Thai +0.2) — the zero line is exactly the group mean. Right: a group with identical rewards throughout; every advantage is zero, the gradient is zero</p><div class="captionFooter_w00v"></div></figcaption></figure>
<p>The left panel is equation 3.1 at work: the two answers that did everything right (r = 1.5) get a strong positive push,
while the answer that earned only the format bonus (r = 0.3) gets pushed down <strong>despite its positive reward</strong> —
because the criterion is not "is it good" but "is it better than its groupmates."
The right panel is the silent death mode of all of GRPO: identical rewards = zero std = zero learning.</p>
<p>Enter your own rewards and watch the advantages change live — and don't miss unticking the <strong>Divide by std</strong> box
to see the Dr.GRPO difference from section 3.4 with your own eyes:</p>
<div class="root_H3ot"><div class="presetRow_LrTq"><span class="presetLabel_EB8R">Try a group:</span><button type="button" class="button_ioxi">Mixed group</button><button type="button" class="button_ioxi">All correct</button><button type="button" class="button_ioxi">All wrong</button><button type="button" class="button_ioxi">One lucky sample</button><button type="button" class="button_ioxi">Graded rewards</button></div><div class="layout_fs8s"><div class="editor_p1Ql"><p class="editorHeading_hHgi">Rewards r_i (G = 8)</p><ul class="rewardList_b99T"><li class="rewardItem_fDZp"><label class="rewardLabel_NOYj" for="llmcourse-ags-r0">r<sub>1</sub></label><input id="llmcourse-ags-r0" class="rewardInput_nfJ6" type="number" step="0.05" aria-label="Reward for sample 1" value="1"></li><li class="rewardItem_fDZp"><label class="rewardLabel_NOYj" for="llmcourse-ags-r1">r<sub>2</sub></label><input id="llmcourse-ags-r1" class="rewardInput_nfJ6" type="number" step="0.05" aria-label="Reward for sample 2" value="0"></li><li class="rewardItem_fDZp"><label class="rewardLabel_NOYj" for="llmcourse-ags-r2">r<sub>3</sub></label><input id="llmcourse-ags-r2" class="rewardInput_nfJ6" type="number" step="0.05" aria-label="Reward for sample 3" value="1"></li><li class="rewardItem_fDZp"><label class="rewardLabel_NOYj" for="llmcourse-ags-r3">r<sub>4</sub></label><input id="llmcourse-ags-r3" class="rewardInput_nfJ6" type="number" step="0.05" aria-label="Reward for sample 4" value="1"></li><li class="rewardItem_fDZp"><label class="rewardLabel_NOYj" for="llmcourse-ags-r4">r<sub>5</sub></label><input id="llmcourse-ags-r4" class="rewardInput_nfJ6" type="number" step="0.05" aria-label="Reward for sample 5" value="0"></li><li class="rewardItem_fDZp"><label class="rewardLabel_NOYj" for="llmcourse-ags-r5">r<sub>6</sub></label><input id="llmcourse-ags-r5" class="rewardInput_nfJ6" type="number" step="0.05" aria-label="Reward for sample 6" value="0"></li><li class="rewardItem_fDZp"><label class="rewardLabel_NOYj" for="llmcourse-ags-r6">r<sub>7</sub></label><input id="llmcourse-ags-r6" class="rewardInput_nfJ6" type="number" step="0.05" aria-label="Reward for sample 7" value="1"></li><li class="rewardItem_fDZp"><label class="rewardLabel_NOYj" for="llmcourse-ags-r7">r<sub>8</sub></label><input id="llmcourse-ags-r7" class="rewardInput_nfJ6" type="number" step="0.05" aria-label="Reward for sample 8" value="0"></li></ul><div class="editorButtons_PH4K"><button type="button" class="button_ioxi">Remove</button><button type="button" class="button_ioxi">Add sample</button></div><div class="control_Br1p"><label class="checkboxRow_XXA4" for="_R_4abmldeh_"><input id="_R_4abmldeh_" type="checkbox" aria-describedby="_R_4abmldeh_-hint" checked=""><span>Divide by std (standard GRPO)</span></label><span class="controlHint_ilRY" id="_R_4abmldeh_-hint">Unchecked is the Dr.GRPO variant: it keeps the centring but drops the std, removing the bias toward low-variance groups.</span></div></div><div class="svgWrap_mSxx"><svg class="svg_pLEH" viewBox="0 0 720 274" role="img" aria-label="Group-relative advantages for 8 samples. Mean reward 0.500, standard deviation 0.500."><g><text x="64" y="22" text-anchor="end" dominant-baseline="middle" class="rowLabel_hE3S">r1 = 1.00</text><rect x="389" y="10" width="307" height="18" rx="2" class="barPositive_Lzkj"></rect><text x="702" y="22" text-anchor="start" dominant-baseline="middle" class="valueLabel_T0Ct">1.00</text></g><g><text x="64" y="52" text-anchor="end" dominant-baseline="middle" class="rowLabel_hE3S">r2 = 0.00</text><rect x="82" y="40" width="307" height="18" rx="2" class="barNegative_Uoik"></rect><text x="76" y="52" text-anchor="end" dominant-baseline="middle" class="valueLabel_T0Ct">-1.00</text></g><g><text x="64" y="82" text-anchor="end" dominant-baseline="middle" class="rowLabel_hE3S">r3 = 1.00</text><rect x="389" y="70" width="307" height="18" rx="2" class="barPositive_Lzkj"></rect><text x="702" y="82" text-anchor="start" dominant-baseline="middle" class="valueLabel_T0Ct">1.00</text></g><g><text x="64" y="112" text-anchor="end" dominant-baseline="middle" class="rowLabel_hE3S">r4 = 1.00</text><rect x="389" y="100" width="307" height="18" rx="2" class="barPositive_Lzkj"></rect><text x="702" y="112" text-anchor="start" dominant-baseline="middle" class="valueLabel_T0Ct">1.00</text></g><g><text x="64" y="142" text-anchor="end" dominant-baseline="middle" class="rowLabel_hE3S">r5 = 0.00</text><rect x="82" y="130" width="307" height="18" rx="2" class="barNegative_Uoik"></rect><text x="76" y="142" text-anchor="end" dominant-baseline="middle" class="valueLabel_T0Ct">-1.00</text></g><g><text x="64" y="172" text-anchor="end" dominant-baseline="middle" class="rowLabel_hE3S">r6 = 0.00</text><rect x="82" y="160" width="307" height="18" rx="2" class="barNegative_Uoik"></rect><text x="76" y="172" text-anchor="end" dominant-baseline="middle" class="valueLabel_T0Ct">-1.00</text></g><g><text x="64" y="202" text-anchor="end" dominant-baseline="middle" class="rowLabel_hE3S">r7 = 1.00</text><rect x="389" y="190" width="307" height="18" rx="2" class="barPositive_Lzkj"></rect><text x="702" y="202" text-anchor="start" dominant-baseline="middle" class="valueLabel_T0Ct">1.00</text></g><g><text x="64" y="232" text-anchor="end" dominant-baseline="middle" class="rowLabel_hE3S">r8 = 0.00</text><rect x="82" y="220" width="307" height="18" rx="2" class="barNegative_Uoik"></rect><text x="76" y="232" text-anchor="end" dominant-baseline="middle" class="valueLabel_T0Ct">-1.00</text></g><line x1="389" y1="0" x2="389" y2="246" class="axisLine_LyoP"></line><text x="389" y="266" text-anchor="middle" class="axisLabel_Yazw">Â = 0 (no update)</text></svg></div></div><div class="readouts__tjv"><div class="readout_D9ns"><span class="readoutLabel_EsIV">mean(r)</span><span class="readoutValue_VS6z">0.5000</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">std(r)</span><span class="readoutValue_VS6z">0.5000</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">max |Â|</span><span class="readoutValue_VS6z">1.000</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">Formula</span><span class="readoutValue_VS6z">(r − μ) / σ</span><span class="readoutSub_DoT9">GRPO</span></div></div><p class="callout_aEDz" role="status"><strong class="calloutTitle_nx3s">Watch the std term.</strong>Dividing by std(r) = 0.500 rescales this whole group. A group that happened to be near-unanimous gets a large multiplier and dominates the update, even though it carries less information than a group that genuinely disagreed. Untick the box to see the same rewards without the rescaling.</p></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-grpo-deletes-from-ppo">What GRPO deletes from PPO<a href="https://kobkrit.com/en/blog/llm-05-grpo#what-grpo-deletes-from-ppo" class="hash-link" aria-label="Direct link to What GRPO deletes from PPO" title="Direct link to What GRPO deletes from PPO" translate="no">​</a></h3>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-05-grpo/ppo-vs-grpo-models.light.svg" alt="A horizontal bar chart comparing the memory of PPO's four models against GRPO's two, with the value network crossed out and the reward model turned into a Python function using zero memory" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-05-grpo/ppo-vs-grpo-models.dark.svg" alt="A horizontal bar chart comparing the memory of PPO's four models against GRPO's two, with the value network crossed out and the reward model turned into a Python function using zero memory" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 5.2</span>Models in VRAM counted in fp16 weights of Qwen3-0.6B (1.11 GB per copy): PPO loads 4 copies, GRPO 2 — the value network is replaced by the group mean, and the reward model by a Python function</p><div class="captionFooter_w00v"></div></figcaption></figure>
<p>Notice that the two blocks that vanished are exactly the two that had to be <strong>trained</strong> (the value net)
or <strong>pre-trained</strong> (the reward model). What's left is the policy and the reference —
and chapter 4 already taught us LoRA lets those two share base weights.
The net model cost of GRPO in this chapter's notebook therefore equals plain SFT.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="k3-versus-k1-equally-unbiased-not-equally-usable">k3 versus k1: equally unbiased, not equally usable<a href="https://kobkrit.com/en/blog/llm-05-grpo#k3-versus-k1-equally-unbiased-not-equally-usable" class="hash-link" aria-label="Direct link to k3 versus k1: equally unbiased, not equally usable" title="Direct link to k3 versus k1: equally unbiased, not equally usable" translate="no">​</a></h3>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-05-grpo/k3-estimator.light.svg" alt="Histograms comparing the distributions of the k1 and k3 KL estimators, and a running-mean plot showing both converge to the true KL but k3 is much steadier" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-05-grpo/k3-estimator.dark.svg" alt="Histograms comparing the distributions of the k1 and k3 KL estimators, and a running-mean plot showing both converge to the true KL but k3 is much steadier" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 5.3</span>A synthetic example with a known answer: π_θ = N(0,1), π_ref = N(0.5,1), making the true KL exactly 0.125 — k1 spreads wide and goes negative on roughly 40% of samples, while k3 never goes negative and its std is almost three times lower (0.18 versus 0.50)</p><div class="captionFooter_w00v"></div></figcaption></figure>
<p>The right panel is the practical point: both lines converge to the same answer (both unbiased),
but at sample counts matching a real batch (tens to hundreds) the k1 line still swings hard,
while k3 is steady enough to serve as a trustworthy penalty from the earliest steps.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-setting-up-the-environment">5. Setting Up the Environment<a href="https://kobkrit.com/en/blog/llm-05-grpo#5-setting-up-the-environment" class="hash-link" aria-label="Direct link to 5. Setting Up the Environment" title="Direct link to 5. Setting Up the Environment" translate="no">​</a></h2>
<p>Open Colab and pick <strong>Runtime → Change runtime type → T4 GPU</strong> (the free tier is enough).</p>
<div class="theme-admonition theme-admonition-danger admonition_xJq3 alert alert--danger"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"></path></svg></span>The series-wide warning worth re-reading every chapter</div><div class="admonitionContent_BuS1"><p>The Colab T4 is Turing architecture (SM 7.5), which <strong>does not support bfloat16</strong> and <strong>does not support FlashAttention-2</strong>.</p><p>But Qwen3-0.6B's <code>config.json</code> declares <code>torch_dtype: bfloat16</code>.
So <code>torch_dtype="auto"</code> is <strong>a trap</strong> — your code will crash or run bizarrely slowly without telling you why.</p><div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">torch_dtype</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">float16      </span><span class="token comment" style="color:#999988;font-style:italic"># not bfloat16</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">attn_implementation</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"sdpa"</span><span class="token plain">     </span><span class="token comment" style="color:#999988;font-style:italic"># not flash_attention_2</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">fp16</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token plain">                      </span><span class="token comment" style="color:#999988;font-style:italic"># in GRPOConfig (not bf16=True)</span><br></span></code></pre></div></div></div></div>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">cap </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_device_capability</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"compute capability:"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cap</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                    </span><span class="token comment" style="color:#999988;font-style:italic"># T4 = (7, 5)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"native bf16:"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cap</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&gt;=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                   </span><span class="token comment" style="color:#999988;font-style:italic"># T4 -&gt; False</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"torch says   :"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">is_bf16_supported</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># T4 -&gt; True (counts emulation!)</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span><code>is_bf16_supported()</code> lies on a T4</div><div class="admonitionContent_BuS1"><p>Recent torch returns <code>True</code> on a T4 because it counts <strong>emulation</strong> as support — which is far slower than fp16.
Gate on <strong>compute capability ≥ 8.0</strong> (Ampere and up) instead. This was a real bug, caught only by running the notebook on Colab.</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="two-models-for-the-price-of-one--the-same-recipe-as-chapter-4">Two models for the price of one — the same recipe as chapter 4<a href="https://kobkrit.com/en/blog/llm-05-grpo#two-models-for-the-price-of-one--the-same-recipe-as-chapter-4" class="hash-link" aria-label="Direct link to Two models for the price of one — the same recipe as chapter 4" title="Direct link to Two models for the price of one — the same recipe as chapter 4" translate="no">​</a></h3>
<p>The policy is the LoRA adapter from chapter 2; <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>π</mi><mtext>ref</mtext></msub></mrow><annotation encoding="application/x-tex">\pi_{\text{ref}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">ref</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> is the same base with the <strong>adapter off</strong>.
TRL knows this mechanism natively: if <code>model</code> is a <code>PeftModel</code>, it loads no separate reference.</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> peft </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> PeftModel</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">base </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> AutoModelForCausalLM</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">from_pretrained</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"Qwen/Qwen3-0.6B"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    torch_dtype</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">float16</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    attn_implementation</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"sdpa"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">policy </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> PeftModel</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">from_pretrained</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">base</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"kobkrit/qwen3-0.6b-th-sft-lora"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> is_trainable</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>This chapter's real budget is generated tokens, not training steps</div><div class="admonitionContent_BuS1"><p>GRPO is <strong>online RL</strong>: before every weight update, fresh answers must be sampled from the model.
This chapter's full config generates up to 128 problems × 8 answers × 256 tokens = <strong>262,144 tokens</strong>,
versus chapter 4, which merely forward-passed text already sitting in a file — different worlds entirely.</p><p>T4 time therefore goes to generation, not backprop, and this is why the notebook sets
<code>FAST_MODE = True</code> as the default (64 problems × 4 answers × 192 tokens ≈ 49k tokens, ~10 minutes),
while the full config used for reported results (~18 minutes) is one flag flip away —
we say this out loud because an article that won't tell you "which config the pretty numbers came from" is lying to you by half a sentence.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-preparing-the-data">6. Preparing the Data<a href="https://kobkrit.com/en/blog/llm-05-grpo#6-preparing-the-data" class="hash-link" aria-label="Direct link to 6. Preparing the Data" title="Direct link to 6. Preparing the Data" translate="no">​</a></h2>
<p>We use <strong><code>VISAI-AI/gsm8k-thai</code></strong> — a Thai translation of the GSM8K math word-problem set.
We sample 128 problems from the train split and keep a separate held-out set for evaluation, untouched during training.</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> datasets </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> load_dataset</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ds </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> load_dataset</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"VISAI-AI/gsm8k-thai"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> split</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"train"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ds </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> ds</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">shuffle</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">seed</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">42</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">select</span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin">range</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">128</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># The notebook's system prompt is the Thai original of this instruction —</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># the model is being trained to reason in Thai:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">SYSTEM </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Think step by step inside &lt;think&gt;...&lt;/think&gt;, then end with the numeric answer on the last line."</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">to_prompt</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ex</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"prompt"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"role"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"system"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"content"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> SYSTEM</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                   </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"role"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"user"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"content"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ex</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"translated_question"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"answer"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> extract_final_int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ex</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"translated_answer"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># the GSM8K answer key sits after "####"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">train_ds </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> ds</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">map</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">to_prompt</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> remove_columns</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">ds</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">column_names</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>Notice there is <strong>no chosen/rejected column and no human label of any kind</strong> — only problems and numeric answers.
What replaces the labels is three reward functions checked entirely by code:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> re</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">THAI_DIGITS </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">str</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">maketrans</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"\u0e50\u0e51\u0e52\u0e53\u0e54\u0e55\u0e56\u0e57\u0e58\u0e59"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                            </span><span class="token string" style="color:#e3116c">"0123456789"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">            </span><span class="token comment" style="color:#999988;font-style:italic"># Thai digit glyphs 0-9</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">extract_final_int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">text</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    text </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> text</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">translate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">THAI_DIGITS</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">               </span><span class="token comment" style="color:#999988;font-style:italic"># in case the model answers in Thai numerals</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    tail </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> text</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">split</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"&lt;/think&gt;"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">[</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain">                </span><span class="token comment" style="color:#999988;font-style:italic"># grade only what follows the thinking</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    nums </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> re</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">findall</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">r"-?\d[\d,]*"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> tail</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">nums</span><span class="token punctuation" style="color:#393A34">[</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">replace</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">","</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> nums </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">reward_correct</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">completions</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> answer</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">**</span><span class="token plain">kwargs</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># +1.0 final answer correct</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1.0</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> extract_final_int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">c</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> a </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.0</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> c</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> a </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token builtin">zip</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">completions</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> answer</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">reward_format</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">completions</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">**</span><span class="token plain">kwargs</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">            </span><span class="token comment" style="color:#999988;font-style:italic"># +0.3 has a non-empty &lt;think&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    pat </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> re</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">compile</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">r"&lt;think&gt;.+?&lt;/think&gt;"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> re</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">DOTALL</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0.3</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> pat</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">search</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">c</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.0</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> c </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> completions</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">reward_thai</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">completions</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">**</span><span class="token plain">kwargs</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">              </span><span class="token comment" style="color:#999988;font-style:italic"># +0.2 genuinely thinks in Thai</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">th_ratio</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">s</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        letters </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">ch </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> ch </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> s </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> ch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">isalpha</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token builtin">sum</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"\u0e01"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;=</span><span class="token plain"> ch </span><span class="token operator" style="color:#393A34">&lt;=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"\u0e5b"</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> ch </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> letters</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> </span><span class="token builtin">max</span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin">len</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">letters</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># Thai Unicode block</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0.2</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> th_ratio</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">c</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.5</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.0</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> c </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> completions</span><span class="token punctuation" style="color:#393A34">]</span><br></span></code></pre></div></div>
<p>The total reward of one answer is the sum of all three: at most 1.5, at least 0.0.
(The <code>THAI_DIGITS</code> table exists because Thai has its own digit glyphs, U+0E50 through U+0E59 —
a model that writes the correct answer in Thai numerals still deserves its +1.0.
And the U+0E01 through U+0E5B comparison is simply the Thai Unicode block, the same range behind the series' <code>th_ratio</code> metric.)</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>Why shaped sub-rewards, not just "right/wrong"</div><div class="admonitionContent_BuS1"><p>Look back at figure 5.1's right panel: the source of all learning is <strong>variation within the group</strong>.
Early in training, a 0.6B model gets math problems right only rarely — if the reward were right/wrong alone,
most groups would be [0,0,0,0,0,0,0,0]: zero std, zero gradient, <strong>training for free and getting nothing</strong>.
The format and Thai-language sub-rewards keep groups varied enough to learn from before the model ever starts answering correctly.
This is reward shaping in the most literal sense — and, in large letters: <strong>it also opens doors for cheating.</strong>
See trap 1 in section 9 for which clause the model found its loophole in (it really did — the evidence is in the notebook).</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-the-main-code">7. The Main Code<a href="https://kobkrit.com/en/blog/llm-05-grpo#7-the-main-code" class="hash-link" aria-label="Direct link to 7. The Main Code" title="Direct link to 7. The Main Code" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="71-grpotrainer--this-time-the-trainer-is-a-first-class-citizen">7.1 GRPOTrainer — this time the trainer is a first-class citizen<a href="https://kobkrit.com/en/blog/llm-05-grpo#71-grpotrainer--this-time-the-trainer-is-a-first-class-citizen" class="hash-link" aria-label="Direct link to 7.1 GRPOTrainer — this time the trainer is a first-class citizen" title="Direct link to 7.1 GRPOTrainer — this time the trainer is a first-class citizen" translate="no">​</a></h3>
<p>In chapter 3 we had to squint at TRL's <code>PPOTrainer</code>, stuck in semi-experimental status with an API shifting nearly every minor version.
<code>GRPOTrainer</code> is a different story entirely: it is the trainer TRL showcases in the wake of R1,
and it takes rewards as <strong>plain Python functions</strong>, no model-wrapping of any kind.</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> trl </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> GRPOConfig</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> GRPOTrainer</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">FAST_MODE </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">True</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># default: finishes in ~10 minutes on a free T4</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                   </span><span class="token comment" style="color:#999988;font-style:italic"># False = the full config used for reported results (~18 minutes)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">cfg </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> GRPOConfig</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    output_dir</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"grpo-out"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    num_generations</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">4</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> FAST_MODE </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">          </span><span class="token comment" style="color:#999988;font-style:italic"># G — the group size</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    max_completion_length</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">192</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> FAST_MODE </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">256</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    max_prompt_length</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">256</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    temperature</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1.0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">                </span><span class="token comment" style="color:#999988;font-style:italic"># do not lower — diversity within the group is the fuel</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    beta</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.04</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">                      </span><span class="token comment" style="color:#999988;font-style:italic"># KL weight (computed with k3 from section 3.3)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    epsilon</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">                    </span><span class="token comment" style="color:#999988;font-style:italic"># the same clip range as PPO</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    learning_rate</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1e-6</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">             </span><span class="token comment" style="color:#999988;font-style:italic"># lower than DPO still — see the warning below</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    per_device_train_batch_size</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">16</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># must divide evenly by num_generations</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    gradient_accumulation_steps</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">2</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> FAST_MODE </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    num_train_epochs</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    fp16</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">                      </span><span class="token comment" style="color:#999988;font-style:italic"># the T4 has no bf16</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    logging_steps</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">trainer </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> GRPOTrainer</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    model</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">policy</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">                                        </span><span class="token comment" style="color:#999988;font-style:italic"># PeftModel → free reference</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    reward_funcs</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">reward_correct</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> reward_format</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> reward_thai</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    args</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">cfg</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    train_dataset</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">train_ds</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">trainer</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">train</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>Let's do the budget arithmetic in the open: the full config is 128 problems × 8 answers = 1,024 completions,
divided by an effective batch of 64 completions per step = <strong>16 optimizer steps</strong> — that is genuinely all.
Nearly all the remaining time is spent generating roughly 262k tokens (at most) before each step.</p>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>Two numbers that must always move together</div><div class="admonitionContent_BuS1"><p><code>per_device_train_batch_size</code> counts <strong>completions, not prompts</strong>, and must divide evenly by
<code>num_generations</code>, because members of the same group must sit in the same batch for the group's
mean/std to be computable. Misalign them and TRL errors out at trainer construction — which is good.
Failing loudly beats failing silently.</p></div></div>
<div class="theme-admonition theme-admonition-danger admonition_xJq3 alert alert--danger"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"></path></svg></span>Online RL's learning rate must be the lowest in the series</div><div class="admonitionContent_BuS1"><p>The series' progression is SFT <code>2e-4</code> → DPO <code>5e-6</code> → GRPO <code>1e-6</code>.
The reason: GRPO's training data is answers sampled by the <em>current</em> model.
If the weights move hard enough that the language starts to bend, the next generation of answers bends with it,
and the reward collapses across the board — online RL's mistakes <strong>compound</strong>,
unlike supervised learning, where the training data isn't going anywhere.</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="72-computing-the-advantage-by-hand--all-the-arithmetic-in-one-place">7.2 Computing the advantage by hand — all the arithmetic in one place<a href="https://kobkrit.com/en/blog/llm-05-grpo#72-computing-the-advantage-by-hand--all-the-arithmetic-in-one-place" class="hash-link" aria-label="Direct link to 7.2 Computing the advantage by hand — all the arithmetic in one place" title="Direct link to 7.2 Computing the advantage by hand — all the arithmetic in one place" translate="no">​</a></h3>
<p>So that <code>GRPOTrainer</code> isn't a black box, the notebook has a cell that does equation 3.1's arithmetic in the open:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> torch</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">group_advantages</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">rewards</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> G</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> eps</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1e-4</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token triple-quoted-string string" style="color:#e3116c">"""rewards: [B], laid out in groups of G from the same prompt"""</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    r </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> rewards</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">view</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> G</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                    </span><span class="token comment" style="color:#999988;font-style:italic"># [B/G, G]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    mean </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> r</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">mean</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">dim</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> keepdim</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    std </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> r</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">std</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">dim</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> keepdim</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">r </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> mean</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">std </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> eps</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">view</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># Dr.GRPO: delete "/ (std + eps)"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">r </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">tensor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1.5</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.3</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.5</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1.5</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.3</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.3</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.5</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># the group from figure 5.1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">group_advantages</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">r</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> G</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">8</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># → [+1.56, -0.55, -0.20, +1.56, -0.55, -1.08, -0.55, -0.20]</span><br></span></code></pre></div></div>
<p>These eight lines are everything GRPO adds on top of the plain PPO clip.
Compare that to chapter 3's value network + GAE, which had to be trained alongside the policy the whole way — this is the best trade in the series.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="8-results">8. Results<a href="https://kobkrit.com/en/blog/llm-05-grpo#8-results" class="hash-link" aria-label="Direct link to 8. Results" title="Direct link to 8. Results" translate="no">​</a></h2>
<p>The notebook measures 4 things and writes them to <code>results.json</code>:</p>
<ol>
<li class=""><strong>Mean reward per step</strong> — should climb (this is what the optimizer sees)</li>
<li class=""><strong>The fraction of groups with nonzero std, per step</strong> — the metric almost nobody plots</li>
<li class=""><strong>pass@1 and pass@8 on held-out</strong>, using the unbiased formula from section 3.5, with a <strong>Wilson 95% CI</strong></li>
<li class=""><strong>Mean completion length per step</strong> — read side by side with accuracy</li>
</ol>
<table><thead><tr><th>Metric (full config)</th><th>Before training</th><th>After training</th></tr></thead><tbody><tr><td>pass@1, held-out (95% CI)</td><td>?</td><td>?</td></tr><tr><td>pass@8, held-out (unbiased)</td><td>?</td><td>?</td></tr><tr><td>mean reward per group</td><td>?</td><td>?</td></tr><tr><td>mean answer length (tokens)</td><td>?</td><td>?</td></tr><tr><td>fraction of groups with std &gt; 0 (first step → last step)</td><td>?</td><td>?</td></tr></tbody></table>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>Metric number 2 is GRPO's vital sign</div><div class="admonitionContent_BuS1"><p>A flat mean reward reads two ways: "the model has saturated" or "learning stopped a long time ago."
What separates the two cases is the fraction of groups with nonzero std — <strong>the moment it touches zero,
every batch after that is a silent no-op</strong>: the loss still prints, steps still tick, the GPU still runs hot,
but the gradient is exactly zero at every step (figure 5.1's right panel multiplied across the batch).
Train another hour and get exactly the same model. The notebook always plots this line next to mean reward —
and it should become your habit in every RLVR project.</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-promised-mini-r1-moment">The promised mini-R1 moment<a href="https://kobkrit.com/en/blog/llm-05-grpo#the-promised-mini-r1-moment" class="hash-link" aria-label="Direct link to The promised mini-R1 moment" title="Direct link to The promised mini-R1 moment" translate="no">​</a></h3>
<p>The DeepSeek-R1 paper has a very famous plot: answer length growing on its own, <em>together with</em> accuracy,
with nobody telling the model to think longer — the model discovered by itself that writing out its reasoning in more detail brings reward.
Our notebook plots the same pair (completion length and accuracy per step) at miniature scale.
If you see both lines drift upward together even slightly, that is the same mechanism as R1 in your own test tube.
And if length grows while accuracy stays flat, suspect reward hacking first, always (trap 1, section 9).</p>
<div class="root_IS5b"><div class="picker_cO8e"><span class="pickerLabel_sE2x" id="llmcourse-bac-picker">Prompt</span><div class="pickerButtons_j7L1" role="tablist" aria-labelledby="llmcourse-bac-picker"><button type="button" role="tab" id="llmcourse-bac-tab-0" aria-selected="true" aria-controls="llmcourse-bac-panel-0" tabindex="0" class="pickerButton_gFO3 pickerButtonActive_xIUp">1</button><button type="button" role="tab" id="llmcourse-bac-tab-1" aria-selected="false" aria-controls="llmcourse-bac-panel-1" tabindex="-1" class="pickerButton_gFO3">2</button></div></div><blockquote class="prompt_O4Wp" lang="th"><span class="promptLabel_h2F6">Prompt</span>อธิบายว่าทำไมท้องฟ้าถึงเป็นสีฟ้า แบบสั้น ๆ</blockquote><div class="grid_h_9T" id="llmcourse-bac-panel-0" role="tabpanel" aria-labelledby="llmcourse-bac-tab-0" style="grid-template-columns:repeat(auto-fit, minmax(min(100%, 260px), 1fr))"><article class="card_S27b"><header class="cardHeader_w7wJ"><h4 class="cardTitle_NUQN">base</h4><div class="badges_pXcS"><span class="badge_wUaQ badgeBad_WFwi" title="Share of non-whitespace characters that are Thai script">Thai 18%</span><span class="badge_wUaQ">41 tokens</span></div></header><div class="output_VSGg" lang="th">The sky appears blue because of Rayleigh scattering. ท้องฟ้า is blue เพราะ light scatter ครับ. Shorter wavelengths scatter more than longer ones.</div></article><article class="card_S27b"><header class="cardHeader_w7wJ"><h4 class="cardTitle_NUQN">sft</h4><div class="badges_pXcS"><span class="badge_wUaQ badgeGood_MHH_" title="Share of non-whitespace characters that are Thai script">Thai 99%</span><span class="badge_wUaQ">78 tokens</span></div></header><div class="output_VSGg" lang="th">ท้องฟ้าเป็นสีฟ้าเพราะแสงอาทิตย์กระทบกับโมเลกุลของอากาศแล้วเกิดการกระเจิงแบบเรย์ลี ซึ่งแสงสีน้ำเงินที่มีความยาวคลื่นสั้นกว่าจะกระเจิงได้มากกว่าแสงสีแดง เราจึงมองเห็นท้องฟ้าเป็นสีฟ้าครับ</div></article></div><p class="status_mfC7">Showing the built-in sample.</p></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="9-comparison">9. Comparison<a href="https://kobkrit.com/en/blog/llm-05-grpo#9-comparison" class="hash-link" aria-label="Direct link to 9. Comparison" title="Direct link to 9. Comparison" translate="no">​</a></h2>
<p>Four chapters, four methods, measured on the same task (the same held-out Thai math problems) —
this table is where the whole series pays out, because the rightmost column has never appeared in any chapter before:</p>
<table><thead><tr><th>Method</th><th>pass@1 (95% CI)</th><th>Answer length</th><th>Training time</th><th>Models in VRAM</th><th>Human label cost</th></tr></thead><tbody><tr><td>SFT (ch. 2)</td><td>?</td><td>?</td><td>?</td><td>1</td><td>A human-written answer for every example</td></tr><tr><td>PPO (ch. 3)</td><td>?</td><td>?</td><td>?</td><td>4</td><td>Preference pairs to train the reward model</td></tr><tr><td>DPO (ch. 4)</td><td>?</td><td>?</td><td>~9 min</td><td>2 (1 with LoRA)</td><td>~500 preference pairs</td></tr><tr><td><strong>GRPO (this chapter)</strong></td><td>?</td><td>?</td><td>~18 min</td><td>2 (1 with LoRA)</td><td><strong>Zero</strong></td></tr></tbody></table>
<p>Read this table right to left: the series' arc is a <strong>progressive shedding of human labels</strong> —
from an answer for every example → preference pairs → zero — with the machinery underneath growing simpler as well.
The one condition that makes the last column zero is that the task must be <strong>checkable by code</strong> — engrave that somewhere.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="does-rl-create-new-capability-or-sharpen-the-old">Does RL create new capability, or sharpen the old?<a href="https://kobkrit.com/en/blog/llm-05-grpo#does-rl-create-new-capability-or-sharpen-the-old" class="hash-link" aria-label="Direct link to Does RL create new capability, or sharpen the old?" title="Direct link to Does RL create new capability, or sharpen the old?" translate="no">​</a></h3>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-05-grpo/passk-sharpening.light.svg" alt="A two-panel plot. The left panel shows a probability sharpening function fixed at zero. The right panel shows pass@1 bars rising sharply toward the old pass@8 ceiling line while pass@8 rises much less" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-05-grpo/passk-sharpening.dark.svg" alt="A two-panel plot. The left panel shows a probability sharpening function fixed at zero. The right panel shows pass@1 bars rising sharply toward the old pass@8 ceiling line while pass@8 rises much less" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 5.4</span>A toy model with the mechanism fully specified (RL multiplies the odds of problems it has ever solved by ×8, but cannot touch problems at p = 0): pass@1 leaps toward the old pass@8 ceiling — an illustration of the mechanism only; the measured numbers are in the notebook</p><div class="captionFooter_w00v"></div></figcaption></figure>
<p>The logic behind this figure is stronger than it looks: GRPO learns from advantages, which can only be nonzero
when <strong>at least one answer in the group does better than its peers</strong> — meaning a problem the base model never solves
no matter how it samples (p = 0) can never send a learning signal into the system.
What RLVR does well is <strong>take ability scattered across pass@8 and concentrate it at pass@1</strong>.
Research in 2025 (Yue et al.) even measured that at very large <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow><annotation encoding="application/x-tex">k</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal" style="margin-right:0.0315em">k</span></span></span></span>, the base model can <em>beat</em> the post-RL model.
This does not make GRPO worthless — real users get one answer; pass@1 is the number that matters —
but it does mean you shouldn't read a climbing reward curve and conclude the model "got smarter." Mostly, it got <em>steadier</em>.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="traps-to-watch-for">Traps to watch for<a href="https://kobkrit.com/en/blog/llm-05-grpo#traps-to-watch-for" class="hash-link" aria-label="Direct link to Traps to watch for" title="Direct link to Traps to watch for" translate="no">​</a></h3>
<p><strong>1. Reward hacking: farming +0.3 with an empty <code>&lt;think&gt;</code></strong>
The notebook's first version of the format reward used the regex <code>&lt;think&gt;.*?&lt;/think&gt;</code> (the crucial detail: <code>.*?</code> accepts the empty string).
Within a few steps the model discovered that printing a bare, empty <code>&lt;think&gt;&lt;/think&gt;</code> and then guessing a number
collects +0.3 for free every time — far cheaper than actually thinking. Mean reward climbed beautifully; accuracy didn't move.
The notebook keeps the caught-in-the-act samples for you to see, then fixes the pattern to <code>.+?</code>, forcing real content.
The lesson: <strong>the model does not optimize what you meant. It optimizes what you wrote.</strong></p>
<p><strong>2. Identical rewards across the whole group → figure 5.1's right panel, the lesson</strong>
The smaller the group, the likelier every answer earns the same reward — at <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi><mo>=</mo><mn>2</mn></mrow><annotation encoding="application/x-tex">G = 2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal">G</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">2</span></span></span></span>, two coins land the same way very often.
Groups smaller than 4 burn a large share of compute for nothing, and a mean estimated from two samples is high-noise anyway.
<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi><mo>=</mo><mn>8</mn></mrow><annotation encoding="application/x-tex">G = 8</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal">G</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">8</span></span></span></span> is the widely used balance point (our FAST mode accepts dropping to 4 in exchange for time — and says so out loud).</p>
<p><strong>3. Temperature too low = killing diversity at the source</strong>
Lower the temperature and all 8 answers come out nearly identical → identical rewards → back to trap 2.
Never carry inference habits (low temperature for stability) into rollout collection.
We set <code>temperature=1.0</code> because <strong>diversity within the group is the fuel of the entire learning system</strong>.</p>
<p><strong>4. The bottleneck is generation, not backprop — budget the right pile</strong>
If a run is slow, don't reach for batch size or the optimizer first — look at the ~262k-token budget in section 5.
The levers that work, strongest first: lower <code>max_completion_length</code>, lower <code>num_generations</code>, use fewer problems.
(Production systems solve this with an inference engine like vLLM, which TRL can attach to, but that is beyond free Colab.)</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="10-summary">10. Summary<a href="https://kobkrit.com/en/blog/llm-05-grpo#10-summary" class="hash-link" aria-label="Direct link to 10. Summary" title="Direct link to 10. Summary" translate="no">​</a></h2>
<ul>
<li class=""><strong>The group mean is a baseline that needs no training</strong> — sample <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi></mrow><annotation encoding="application/x-tex">G</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal">G</span></span></span></span> answers from the same prompt,
and PPO's entire value network becomes unnecessary</li>
<li class=""><strong>Code-checkable rewards = zero human labels</strong> — the point the series has been climbing toward since chapter 2</li>
<li class=""><strong>Advantage is relative within the group</strong> — an answer with positive reward can still be pushed down, if its groupmates did better</li>
<li class=""><strong>A group with identical rewards teaches nothing</strong> — always plot the fraction of groups with std &gt; 0;
it is the line between "saturated" and "stopped learning long ago with nobody noticing"</li>
<li class=""><strong>k3 makes the KL penalty usable at small batch sizes</strong> — as unbiased as the log-ratio, but never negative and far lower variance</li>
<li class=""><strong>Dividing by std hides a bias</strong> — Dr.GRPO cuts it, keeping only the mean subtraction; try it yourself in the section 4 widget</li>
<li class=""><strong>Reward shaping is necessary and dangerous</strong> — sub-rewards prevent all-zero groups early on, but open the door to score farming</li>
<li class=""><strong>RLVR mostly "sharpens," it doesn't "create"</strong> — pass@1 climbs toward the old pass@8 ceiling; measure both, always</li>
</ul>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>Limitations of this experiment</div><div class="admonitionContent_BuS1"><p><strong>GRPO needs a reward that code can check.</strong> Math problems qualify; code qualifies (run the tests).
But "write a polite, natural-sounding Thai email" has no checking function — open-ended work like that
is the territory of preference data and chapter 4's DPO. The two chapters <strong>complement each other; neither replaces the other</strong>.
Choose the tool by the shape of the reward, not by the algorithm's novelty.</p><p><strong>Don't interpret the results as the model "getting smarter"</strong> — our evidence and the full-scale research both point to
RLVR mostly reorganizing the probability of abilities that already existed at pass@8
so they surface reliably at pass@1. If you want genuinely new knowledge, go back to chapter 1 (CPT).</p><p>And as ever: <strong>128 problems and 16 optimizer steps demonstrate the mechanism; they are not real training.</strong>
DeepSeek-R1 used problems by the hundred thousand and compute many orders of magnitude beyond ours.
What transfers to real scale is the understanding: the group is the baseline, diversity is the fuel,
and the reward verifier is the thing the model will always find the loopholes in for you.</p></div></div>
<p><strong>Next chapter:</strong> <a class="" href="https://kobkrit.com/en/blog/llm-06-context-distillation">Context Distillation</a> — that long system prompt you pay for on every single call to the model:
how to <strong>distill it into the weights</strong> so the model behaves accordingly without ever seeing the prompt again.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="references">References<a href="https://kobkrit.com/en/blog/llm-05-grpo#references" class="hash-link" aria-label="Direct link to References" title="Direct link to References" translate="no">​</a></h2>
<ol>
<li class="">Shao et al. (2024). <a href="https://arxiv.org/abs/2402.03300" target="_blank" rel="noopener noreferrer" class="">DeepSeekMath: Pushing the Limits of Mathematical Reasoning in Open Language Models</a> — DeepSeekMath: where GRPO comes from</li>
<li class="">DeepSeek-AI et al. (2025). <a href="https://arxiv.org/abs/2501.12948" target="_blank" rel="noopener noreferrer" class="">DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning</a> — R1: RL on verifiable rewards at real scale</li>
<li class="">Liu et al. (2025). <a href="https://arxiv.org/abs/2503.20783" target="_blank" rel="noopener noreferrer" class="">Understanding R1-Zero-Like Training: A Critical Perspective</a> — Dr.GRPO: the std-division bias discussed in section 3</li>
<li class="">Ahmadian et al. (2024). <a href="https://arxiv.org/abs/2402.14740" target="_blank" rel="noopener noreferrer" class="">Back to Basics: Revisiting REINFORCE Style Optimization for Learning from Human Feedback in LLMs</a> — plain REINFORCE may suffice -- read with the value-network deletion</li>
<li class="">Schulman et al. (2017). <a href="https://arxiv.org/abs/1707.06347" target="_blank" rel="noopener noreferrer" class="">Proximal Policy Optimization Algorithms</a> — the original PPO paper: the clipped surrogate in section 3</li>
<li class="">Chen et al. (2021). <a href="https://arxiv.org/abs/2107.03374" target="_blank" rel="noopener noreferrer" class="">Evaluating Large Language Models Trained on Code</a> — the unbiased pass@k estimator used in section 9</li>
</ol>
<hr>
<p><em>The writing, code and notebooks in this series are licensed under <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank" rel="noopener noreferrer" class="">CC BY-NC-SA 4.0</a> — reuse and adapt them freely with attribution, for non-commercial purposes, and share your adaptations under the same terms. Third-party models and datasets referenced here keep their own licences.</em></p>
<nav class="nav_RfLT" aria-label="Thai LLM tutorial series navigation"><p class="heading_XRWm">Thai LLM series<span class="progress_f8e8">Part 5 of 10</span></p><ol class="list_U31a"><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-01-continue-pretraining"><span class="number_u3BE" aria-hidden="true">1</span><span class="title_BPvL">Continue Pretraining</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-02-sft-lora"><span class="number_u3BE" aria-hidden="true">2</span><span class="title_BPvL">SFT and LoRA</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo"><span class="number_u3BE" aria-hidden="true">3</span><span class="title_BPvL">RLHF and PPO</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-04-dpo"><span class="number_u3BE" aria-hidden="true">4</span><span class="title_BPvL">DPO: Direct Preference Optimization</span></a></li><li class="item_Y10l"><span class="chip_DDpP chipCurrent_BGpo" aria-current="step"><span class="number_u3BE" aria-hidden="true">5</span><span class="title_BPvL">GRPO</span><span class="srOnly_owtF">(you are here)</span></span></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-06-context-distillation"><span class="number_u3BE" aria-hidden="true">6</span><span class="title_BPvL">Context Distillation</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-07-model-distillation"><span class="number_u3BE" aria-hidden="true">7</span><span class="title_BPvL">Model Distillation</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-08-guardrails"><span class="number_u3BE" aria-hidden="true">8</span><span class="title_BPvL">Guardrails</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-09-benchmarking"><span class="number_u3BE" aria-hidden="true">9</span><span class="title_BPvL">Benchmarking</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-10-deployment"><span class="number_u3BE" aria-hidden="true">10</span><span class="title_BPvL">Deployment</span></a></li></ol></nav>]]></content>
        <author>
            <name>Kobkrit Viriyayudhakorn</name>
            <uri>https://github.com/kobkrit</uri>
        </author>
        <category label="ai" term="ai"/>
        <category label="llm" term="llm"/>
        <category label="thai" term="thai"/>
        <category label="tutorial" term="tutorial"/>
        <category label="fine-tuning" term="fine-tuning"/>
        <category label="alignment" term="alignment"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[LLM 6/10] Context Distillation: Moving the System Prompt into the Model's Weights]]></title>
        <id>https://kobkrit.com/en/blog/llm-06-context-distillation</id>
        <link href="https://kobkrit.com/en/blog/llm-06-context-distillation"/>
        <updated>2026-07-20T16:00:00.000Z</updated>
        <summary type="html"><![CDATA[The 400-token system prompt you pay for again on every request is knowledge stored in the wrong place — this chapter uses On-Policy Context Distillation (OPCD) to move it into the model's weights, explains why it has to be reverse KL on the student's own rollouts, and ships code that actually runs on free Colab]]></summary>
        <content type="html"><![CDATA[<p>Every time a user messages your chatbot, you attach the same system prompt, hundreds of tokens long —
every request, for the lifetime of the system, paid again and again with no end.
This chapter moves that block of knowledge <strong>from the prompt into the model's weights</strong>, using a technique called
<strong>Context Distillation</strong> in its on-policy version (<strong>OPCD</strong>).
The most beautiful part: the teacher and the student are <strong>the exact same model</strong> — the only thing that differs is who gets to see the prompt.</p>
<a class="badge_rUYD" href="https://colab.research.google.com/github/kobkrit/thai-llm-tutorials/blob/main/notebooks/06_context_distillation.ipynb" target="_blank" rel="noopener noreferrer" aria-label="Open the notebook 06_context_distillation.ipynb in Google Colab (opens in a new tab)"><svg class="mark_NB8U" viewBox="0 0 24 24" width="20" height="20" aria-hidden="true" focusable="false"><mask id="llmcourse-colab-cut"><rect x="0" y="0" width="24" height="24" fill="#fff"></rect><circle cx="16.2" cy="12" r="6.1" fill="#000"></circle></mask><circle cx="8.4" cy="12" r="4.6" fill="none" stroke="#F9AB00" stroke-width="3.1" mask="url(#llmcourse-colab-cut)"></circle><circle cx="16.2" cy="12" r="4.6" fill="none" stroke="#E8710A" stroke-width="3.1"></circle></svg><span class="text_QXpz">Open in Colab</span><code class="notebook_ntO0">06_context_distillation.ipynb</code></a>
<nav class="nav_RfLT" aria-label="Thai LLM tutorial series navigation"><p class="heading_XRWm">Thai LLM series<span class="progress_f8e8">Part 6 of 10</span></p><ol class="list_U31a"><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-01-continue-pretraining"><span class="number_u3BE" aria-hidden="true">1</span><span class="title_BPvL">Continue Pretraining</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-02-sft-lora"><span class="number_u3BE" aria-hidden="true">2</span><span class="title_BPvL">SFT and LoRA</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo"><span class="number_u3BE" aria-hidden="true">3</span><span class="title_BPvL">RLHF and PPO</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-04-dpo"><span class="number_u3BE" aria-hidden="true">4</span><span class="title_BPvL">DPO: Direct Preference Optimization</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-05-grpo"><span class="number_u3BE" aria-hidden="true">5</span><span class="title_BPvL">GRPO</span></a></li><li class="item_Y10l"><span class="chip_DDpP chipCurrent_BGpo" aria-current="step"><span class="number_u3BE" aria-hidden="true">6</span><span class="title_BPvL">Context Distillation</span><span class="srOnly_owtF">(you are here)</span></span></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-07-model-distillation"><span class="number_u3BE" aria-hidden="true">7</span><span class="title_BPvL">Model Distillation</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-08-guardrails"><span class="number_u3BE" aria-hidden="true">8</span><span class="title_BPvL">Guardrails</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-09-benchmarking"><span class="number_u3BE" aria-hidden="true">9</span><span class="title_BPvL">Benchmarking</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-10-deployment"><span class="number_u3BE" aria-hidden="true">10</span><span class="title_BPvL">Deployment</span></a></li></ol></nav>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-the-problem">1. The Problem<a href="https://kobkrit.com/en/blog/llm-06-context-distillation#1-the-problem" class="hash-link" aria-label="Direct link to 1. The Problem" title="Direct link to 1. The Problem" translate="no">​</a></h2>
<p>A typical Thai customer-service assistant has a system prompt that goes something like this: define a persona,
require answers in Thai at all times, be polite and end sentences with the proper particles, never give medical or legal advice.
Written out properly, that's about <strong>400 tokens</strong> — and it is sent with <strong>every request</strong>.</p>
<p>Run the numbers: a system serving 100,000 requests a day pays for the same identical block of text
<strong>40 million tokens a day</strong>, 1.2 billion a month — while the content never changes by a single character.
And that's before two prices that never show up on the bill:</p>
<ul>
<li class=""><strong>Latency</strong> — the model must prefill 400 tokens before it can start thinking about the first token of every answer</li>
<li class=""><strong>Context budget</strong> — every persona token is space taken away from conversation history and attached documents</li>
</ul>
<p>Framed in this series' terms, knowledge has three places it can live, each with a different payment schedule:</p>
<table><thead><tr><th>Where the knowledge lives</th><th>When you pay</th><th>Best for</th></tr></thead><tbody><tr><td><strong>System prompt</strong></td><td>Every request, forever</td><td>Behavior/policy that still changes often</td></tr><tr><td><strong>RAG</strong></td><td>Every request (retrieval + a long prompt)</td><td>Large volumes of facts that change often and need source citations</td></tr><tr><td><strong>Model weights</strong></td><td>Once, at training time</td><td>Behavior/policy that has settled</td></tr></tbody></table>
<p>A system prompt that has settled but still rides along on every request is <strong>knowledge stored in the wrong place</strong> —
it belongs in the last row of this table, not the first. This chapter is how you move it.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-what-were-going-to-do">2. What We're Going to Do<a href="https://kobkrit.com/en/blog/llm-06-context-distillation#2-what-were-going-to-do" class="hash-link" aria-label="Direct link to 2. What We're Going to Do" title="Direct link to 2. What We're Going to Do" translate="no">​</a></h2>
<p><strong>Context distillation</strong> trains a student that does <strong>not</strong> see the context <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow><annotation encoding="application/x-tex">c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">c</span></span></span></span>
to behave like a teacher that <strong>does</strong> see <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow><annotation encoding="application/x-tex">c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">c</span></span></span></span> — put differently, it moves the effect of <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow><annotation encoding="application/x-tex">c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">c</span></span></span></span> from the prompt into the weights.
The original offline idea goes back to Askell et al. (2021);
the version we use in this chapter is <strong>OPCD (On-Policy Context Distillation)</strong>
by Ye, Dong, Wu, Huang and Wei (2026, <a href="https://arxiv.org/abs/2602.12275" target="_blank" rel="noopener noreferrer" class="">arXiv:2602.12275</a>),
which adds two key ingredients that section 3 will take apart one at a time:</p>
<ol>
<li class=""><strong>The student samples its own answers</strong> (on-policy), without seeing <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow><annotation encoding="application/x-tex">c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">c</span></span></span></span></li>
<li class="">On those answers, minimize the <strong>reverse KL</strong> against the teacher who sees <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow><annotation encoding="application/x-tex">c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">c</span></span></span></span></li>
</ol>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>The core idea of this chapter</div><div class="admonitionContent_BuS1"><p>A system prompt is knowledge stored in the wrong place — kept in the prompt, you pay every request, forever.
OPCD moves it into the weights, and you <strong>pay once</strong>, at training time.</p><p>And in this chapter, teacher and student are <strong>the same set of weights</strong> — the teacher is the model with <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow><annotation encoding="application/x-tex">c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">c</span></span></span></span> in front of it;
the student is the same model without <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow><annotation encoding="application/x-tex">c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">c</span></span></span></span>. What the distance between the two measures is the "influence of <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow><annotation encoding="application/x-tex">c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">c</span></span></span></span>," pure and simple.</p></div></div>
<p>Let me pin one sentence here, because chapter 7 will discuss another "distillation" that people confuse with this one constantly:</p>
<blockquote>
<p><strong>Context distillation changes "what the model knows without being told" — model distillation changes "the size of the model."</strong></p>
</blockquote>
<p>In this chapter the model does not shrink by a single parameter. It simply stops needing the prompt.
Chapter 7 is about compressing a large model into a small one — an entirely different axis.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-the-equations">3. The Equations<a href="https://kobkrit.com/en/blog/llm-06-context-distillation#3-the-equations" class="hash-link" aria-label="Direct link to 3. The Equations" title="Direct link to 3. The Equations" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="31-the-opcd-objective">3.1 The OPCD objective<a href="https://kobkrit.com/en/blog/llm-06-context-distillation#31-the-opcd-objective" class="hash-link" aria-label="Direct link to 3.1 The OPCD objective" title="Direct link to 3.1 The OPCD objective" translate="no">​</a></h3>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi mathvariant="script">L</mi><mo stretchy="false">(</mo><mi>θ</mi><mo stretchy="false">)</mo><mo>=</mo><msub><mi mathvariant="double-struck">E</mi><mrow><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>c</mi><mo stretchy="false">)</mo><mo separator="true">,</mo><mtext>  </mtext><mi>y</mi><mo>∼</mo><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><mo>⋅</mo><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo></mrow></msub><mrow><mo fence="true">[</mo><mfrac><mn>1</mn><mrow><mi mathvariant="normal">∣</mi><mi>y</mi><mi mathvariant="normal">∣</mi></mrow></mfrac><munderover><mo>∑</mo><mrow><mi>t</mi><mo>=</mo><mn>1</mn></mrow><mrow><mi mathvariant="normal">∣</mi><mi>y</mi><mi mathvariant="normal">∣</mi></mrow></munderover><msub><mi mathvariant="double-struck">D</mi><mtext>KL</mtext></msub><mo fence="false" stretchy="true" minsize="1.8em" maxsize="1.8em">(</mo><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><mo>⋅</mo><mo>∣</mo><mi>x</mi><mo separator="true">,</mo><msub><mi>y</mi><mrow><mo>&lt;</mo><mi>t</mi></mrow></msub><mo stretchy="false">)</mo><mtext> </mtext><mo fence="false" stretchy="true" minsize="1.8em" maxsize="1.8em">∥</mo><mtext> </mtext><msub><mi>π</mi><mtext>teacher</mtext></msub><mo stretchy="false">(</mo><mo>⋅</mo><mo>∣</mo><mi>c</mi><mo separator="true">,</mo><mi>x</mi><mo separator="true">,</mo><msub><mi>y</mi><mrow><mo>&lt;</mo><mi>t</mi></mrow></msub><mo stretchy="false">)</mo><mo fence="false" stretchy="true" minsize="1.8em" maxsize="1.8em">)</mo><mo fence="true">]</mo></mrow></mrow><annotation encoding="application/x-tex">\mathcal{L}(\theta) = \mathbb{E}_{(x,c),\; y\sim\pi_\theta(\cdot|x)}\left[\frac{1}{|y|}\sum_{t=1}^{|y|} \mathbb{D}_{\text{KL}}\Big(\pi_\theta(\cdot \mid x, y_{&lt;t}) \,\Big\|\, \pi_{\text{teacher}}(\cdot \mid c, x, y_{&lt;t})\Big)\right]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathcal">L</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.0278em">θ</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:3.6em;vertical-align:-1.55em"></span><span class="mord"><span class="mord mathbb">E</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em"><span style="top:-2.5198em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mopen mtight">(</span><span class="mord mathnormal mtight">x</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">c</span><span class="mclose mtight">)</span><span class="mpunct mtight">,</span><span class="mspace mtight" style="margin-right:0.3253em"></span><span class="mord mathnormal mtight" style="margin-right:0.0359em">y</span><span class="mrel mtight">∼</span><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em"><span style="top:-2.3488em;margin-left:-0.0359em;margin-right:0.0714em"><span class="pstrut" style="height:2.5em"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1512em"><span></span></span></span></span></span></span><span class="mopen mtight">(</span><span class="mord mtight">⋅</span><span class="mord mtight">∣</span><span class="mord mathnormal mtight">x</span><span class="mclose mtight">)</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.3552em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="minner"><span class="mopen"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.05em"><span style="top:-4.05em"><span class="pstrut" style="height:5.6em"></span><span style="width:0.667em;height:3.6em"><svg xmlns="http://www.w3.org/2000/svg" width="0.667em" height="3.6em" viewBox="0 0 667 3600"><path d="M403 1759 V84 H666 V0 H319 V1759 v0 v1759 v84 h347 v-84
H403z M403 1759 V0 H319 V1759 v0 v1759 v84 h84z"></path></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.55em"><span></span></span></span></span></span></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3214em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord">∣</span><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="mord">∣</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.936em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.961em"><span style="top:-1.8829em;margin-left:0em"><span class="pstrut" style="height:3.05em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">t</span><span class="mrel mtight">=</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.05em"><span class="pstrut" style="height:3.05em"></span><span><span class="mop op-symbol large-op">∑</span></span></span><span style="top:-4.386em;margin-left:0em"><span class="pstrut" style="height:3.05em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">∣</span><span class="mord mathnormal mtight" style="margin-right:0.0359em">y</span><span class="mord mtight">∣</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.2671em"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathbb">D</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">KL</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord"><span class="delimsizing size2">(</span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord">⋅</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">∣</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mrel mtight">&lt;</span><span class="mord mathnormal mtight">t</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1774em"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.15em"><span style="top:-3.15em"><span class="pstrut" style="height:3.8em"></span><span style="width:0.556em;height:1.8em"><svg xmlns="http://www.w3.org/2000/svg" width="0.556em" height="1.8em" viewBox="0 0 556 1800"><path d="M145 15 v585 v600 v585 c2.667,10,9.667,15,21,15
c10,0,16.667,-5,20,-15 v-585 v-600 v-585 c-2.667,-10,-9.667,-15,-21,-15
c-10,0,-16.667,5,-20,15z M188 15 H145 v585 v600 v585 h43z
M367 15 v585 v600 v585 c2.667,10,9.667,15,21,15
c10,0,16.667,-5,20,-15 v-585 v-600 v-585 c-2.667,-10,-9.667,-15,-21,-15
c-10,0,-16.667,5,-20,15z M410 15 H367 v585 v600 v585 h43z"></path></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.65em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">teacher</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord">⋅</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">∣</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mord mathnormal">c</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mrel mtight">&lt;</span><span class="mord mathnormal mtight">t</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1774em"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mord"><span class="delimsizing size2">)</span></span><span class="mclose"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.05em"><span style="top:-4.05em"><span class="pstrut" style="height:5.6em"></span><span style="width:0.667em;height:3.6em"><svg xmlns="http://www.w3.org/2000/svg" width="0.667em" height="3.6em" viewBox="0 0 667 3600"><path d="M347 1759 V0 H0 V84 H263 V1759 v0 v1759 H0 v84 H347z
M347 1759 V0 H263 V1759 v0 v1759 h84z"></path></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.55em"><span></span></span></span></span></span></span></span></span></span></span></span>
<p>where the KL at each token position is a sum across the whole vocabulary <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="script">V</mi></mrow><annotation encoding="application/x-tex">\mathcal{V}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathcal" style="margin-right:0.0822em">V</span></span></span></span>:</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi mathvariant="double-struck">D</mi><mtext>KL</mtext></msub><mo fence="false" stretchy="true" minsize="1.8em" maxsize="1.8em">(</mo><msub><mi>π</mi><mi>θ</mi></msub><mtext> </mtext><mo fence="false" stretchy="true" minsize="1.8em" maxsize="1.8em">∥</mo><mtext> </mtext><msub><mi>π</mi><mtext>teacher</mtext></msub><mo fence="false" stretchy="true" minsize="1.8em" maxsize="1.8em">)</mo><mo>=</mo><munder><mo>∑</mo><mrow><mi>v</mi><mo>∈</mo><mi mathvariant="script">V</mi></mrow></munder><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><mi>v</mi><mo>∣</mo><mi>x</mi><mo separator="true">,</mo><msub><mi>y</mi><mrow><mo>&lt;</mo><mi>t</mi></mrow></msub><mo stretchy="false">)</mo><mtext> </mtext><mi>log</mi><mo>⁡</mo><mfrac><mrow><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><mi>v</mi><mo>∣</mo><mi>x</mi><mo separator="true">,</mo><msub><mi>y</mi><mrow><mo>&lt;</mo><mi>t</mi></mrow></msub><mo stretchy="false">)</mo></mrow><mrow><msub><mi>π</mi><mtext>teacher</mtext></msub><mo stretchy="false">(</mo><mi>v</mi><mo>∣</mo><mi>c</mi><mo separator="true">,</mo><mi>x</mi><mo separator="true">,</mo><msub><mi>y</mi><mrow><mo>&lt;</mo><mi>t</mi></mrow></msub><mo stretchy="false">)</mo></mrow></mfrac></mrow><annotation encoding="application/x-tex">\mathbb{D}_{\text{KL}}\Big(\pi_\theta \,\Big\|\, \pi_{\text{teacher}}\Big) = \sum_{v\in\mathcal{V}} \pi_\theta(v \mid x, y_{&lt;t})\,\log\frac{\pi_\theta(v \mid x, y_{&lt;t})}{\pi_{\text{teacher}}(v \mid c, x, y_{&lt;t})}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.8em;vertical-align:-0.65em"></span><span class="mord"><span class="mord mathbb">D</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">KL</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord"><span class="delimsizing size2">(</span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.15em"><span style="top:-3.15em"><span class="pstrut" style="height:3.8em"></span><span style="width:0.556em;height:1.8em"><svg xmlns="http://www.w3.org/2000/svg" width="0.556em" height="1.8em" viewBox="0 0 556 1800"><path d="M145 15 v585 v600 v585 c2.667,10,9.667,15,21,15
c10,0,16.667,-5,20,-15 v-585 v-600 v-585 c-2.667,-10,-9.667,-15,-21,-15
c-10,0,-16.667,5,-20,15z M188 15 H145 v585 v600 v585 h43z
M367 15 v585 v600 v585 c2.667,10,9.667,15,21,15
c10,0,16.667,-5,20,-15 v-585 v-600 v-585 c-2.667,-10,-9.667,-15,-21,-15
c-10,0,-16.667,5,-20,15z M410 15 H367 v585 v600 v585 h43z"></path></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.65em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">teacher</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord"><span class="delimsizing size2">)</span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.3717em;vertical-align:-1.3217em"></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.05em"><span style="top:-1.8557em;margin-left:0em"><span class="pstrut" style="height:3.05em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">v</span><span class="mrel mtight">∈</span><span class="mord mathcal mtight" style="margin-right:0.0822em">V</span></span></span></span><span style="top:-3.05em"><span class="pstrut" style="height:3.05em"></span><span><span class="mop op-symbol large-op">∑</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.3217em"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.0359em">v</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">∣</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.363em;vertical-align:-0.936em"></span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mrel mtight">&lt;</span><span class="mord mathnormal mtight">t</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1774em"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mop">lo<span style="margin-right:0.0139em">g</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.427em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">teacher</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.0359em">v</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">∣</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mord mathnormal">c</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mrel mtight">&lt;</span><span class="mord mathnormal mtight">t</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1774em"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.0359em">v</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">∣</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mrel mtight">&lt;</span><span class="mord mathnormal mtight">t</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1774em"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.936em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span>
<ul>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow><annotation encoding="application/x-tex">c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">c</span></span></span></span> = the context we want moved into the weights (persona + safety policy, ~400 tokens)</li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow><annotation encoding="application/x-tex">x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">x</span></span></span></span> = the user's question; <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi></mrow><annotation encoding="application/x-tex">y</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0359em">y</span></span></span></span> = the answer <strong>sampled by the student itself</strong>, without seeing <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow><annotation encoding="application/x-tex">c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">c</span></span></span></span></li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>π</mi><mi>θ</mi></msub></mrow><annotation encoding="application/x-tex">\pi_\theta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> = the student (predicting from <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow><annotation encoding="application/x-tex">x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">x</span></span></span></span> alone); <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>π</mi><mtext>teacher</mtext></msub></mrow><annotation encoding="application/x-tex">\pi_{\text{teacher}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">teacher</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> = the teacher (the original weights, but seeing <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow><annotation encoding="application/x-tex">c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">c</span></span></span></span> too)</li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mfrac><mn>1</mn><mrow><mi mathvariant="normal">∣</mi><mi>y</mi><mi mathvariant="normal">∣</mi></mrow></mfrac></mrow><annotation encoding="application/x-tex">\frac{1}{|y|}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.3651em;vertical-align:-0.52em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8451em"><span style="top:-2.655em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">∣</span><span class="mord mathnormal mtight" style="margin-right:0.0359em">y</span><span class="mord mtight">∣</span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.394em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.52em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span> = per-token averaging so long answers don't carry extra weight (sound familiar? — length bias, from chapter 4)</li>
</ul>
<p>Notice this is not cross-entropy against any "answer key" — the target is the teacher's <strong>entire row of probabilities</strong>
at every token position. The student isn't learning "what is the next word"; it is learning
"if <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow><annotation encoding="application/x-tex">c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">c</span></span></span></span> were sitting up front, what would the probability of <strong>every word</strong> in the vocab look like?"</p>
<p>The equation carries two decision points that bear the whole method's weight. Take them one at a time.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="32-decision-one--the-kl-must-be-reverse-pi_theta-in-front">3.2 Decision one — the KL must be reverse (<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>π</mi><mi>θ</mi></msub></mrow><annotation encoding="application/x-tex">\pi_\theta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> in front)<a href="https://kobkrit.com/en/blog/llm-06-context-distillation#32-decision-one--the-kl-must-be-reverse-pi_theta-in-front" class="hash-link" aria-label="Direct link to 32-decision-one--the-kl-must-be-reverse-pi_theta-in-front" title="Direct link to 32-decision-one--the-kl-must-be-reverse-pi_theta-in-front" translate="no">​</a></h3>
<p>KL is asymmetric, and its order is a choice of behavior:</p>
<ul>
<li class=""><strong>Forward KL</strong> <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="double-struck">D</mi><mtext>KL</mtext></msub><mo stretchy="false">(</mo><msub><mi>π</mi><mtext>teacher</mtext></msub><mi mathvariant="normal">∥</mi><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\mathbb{D}_{\text{KL}}(\pi_{\text{teacher}} \| \pi_\theta)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathbb">D</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">KL</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">teacher</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord">∥</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span> blows up when <strong>the teacher has mass and the student has none</strong>
→ the student is forced to "cover" every mode of the teacher (mode-covering).
With insufficient capacity, it spreads mass thin to blanket everything — including <strong>the valleys between modes where the teacher never goes</strong> —
which in LLM terms is the answer that "blends two styles into something wrong," or a hallucination.</li>
<li class=""><strong>Reverse KL</strong> <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="double-struck">D</mi><mtext>KL</mtext></msub><mo stretchy="false">(</mo><msub><mi>π</mi><mi>θ</mi></msub><mi mathvariant="normal">∥</mi><msub><mi>π</mi><mtext>teacher</mtext></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\mathbb{D}_{\text{KL}}(\pi_\theta \| \pi_{\text{teacher}})</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathbb">D</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">KL</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord">∥</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">teacher</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span> blows up when <strong>the student puts mass where the teacher has none</strong>
→ the student is forced to <strong>never do what the teacher doesn't do</strong>, then commit firmly to one of the teacher's modes (mode-seeking).</li>
</ul>
<p>For this chapter's task — a persona and a <strong>safety policy</strong> — we want the latter without a moment's thought:
a student that "matches the teacher one way, reliably" is worth more than a student that
"hedges probability across every path the teacher might take, plus the paths the teacher forbids."</p>
<p>If this looks familiar — yes, the KL in the RLHF equations of chapters 3–4 also puts <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>π</mi></mrow><annotation encoding="application/x-tex">\pi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.0359em">π</span></span></span></span> in front,
for the same reason: we govern the behavior of <strong>the thing being trained</strong>, not of the reference.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="33-decision-two--the-rollouts-must-be-the-students-own-on-policy">3.3 Decision two — the rollouts must be the student's own (on-policy)<a href="https://kobkrit.com/en/blog/llm-06-context-distillation#33-decision-two--the-rollouts-must-be-the-students-own-on-policy" class="hash-link" aria-label="Direct link to 3.3 Decision two — the rollouts must be the student's own (on-policy)" title="Direct link to 3.3 Decision two — the rollouts must be the student's own (on-policy)" translate="no">​</a></h3>
<p>Look at <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi><mo>∼</mo><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><mo>⋅</mo><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">y\sim\pi_\theta(\cdot|x)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">∼</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord">⋅</span><span class="mord">∣</span><span class="mord mathnormal">x</span><span class="mclose">)</span></span></span></span> in equation 3.1: the answers used for training are <strong>sampled from the student</strong>, not from the teacher.</p>
<p>The easier alternative would be to have the teacher (who sees <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow><annotation encoding="application/x-tex">c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">c</span></span></span></span>) write a batch of answers, then SFT the student on them —
and that route has a structural problem named <strong>exposure bias</strong>: the student is taught only on text trajectories the <strong>teacher</strong> wrote,
but at deployment it must continue from prefixes <strong>it itself</strong> wrote.
One token off, and it lands in a state it was never taught, and the errors compound from there.</p>
<p>On-policy sampling deletes this problem <strong>by construction</strong>: the states the student meets during training
are the same kind of states it will meet at inference, because it authored both.
The teacher's one job is to "stand inspection" along the student's path — saying, at this point you've just walked to,
here's where you should go next if <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow><annotation encoding="application/x-tex">c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">c</span></span></span></span> were present. (This is the same reason chapter 5 had to sample its own answers instead of continuing with DPO.)</p>
<p>One line of honesty: when computing gradients we treat the sampled <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi></mrow><annotation encoding="application/x-tex">y</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0359em">y</span></span></span></span> as a constant —
no gradient flows back through the sampling. That is standard practice in on-policy distillation.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="34-the-baseline-you-have-to-beat-offline-context-distillation">3.4 The baseline you have to beat: offline context distillation<a href="https://kobkrit.com/en/blog/llm-06-context-distillation#34-the-baseline-you-have-to-beat-offline-context-distillation" class="hash-link" aria-label="Direct link to 3.4 The baseline you have to beat: offline context distillation" title="Direct link to 3.4 The baseline you have to beat: offline context distillation" translate="no">​</a></h3>
<p>What most blogs call "context distillation" is the offline version:</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi mathvariant="script">L</mi><mtext>offline</mtext></msub><mo stretchy="false">(</mo><mi>θ</mi><mo stretchy="false">)</mo><mo>=</mo><mo>−</mo><msub><mi mathvariant="double-struck">E</mi><mrow><mi>y</mi><mo>∼</mo><msub><mi>π</mi><mtext>teacher</mtext></msub><mo stretchy="false">(</mo><mo>⋅</mo><mi mathvariant="normal">∣</mi><mi>c</mi><mo separator="true">,</mo><mi>x</mi><mo stretchy="false">)</mo></mrow></msub><mrow><mo fence="true">[</mo><munderover><mo>∑</mo><mrow><mi>t</mi><mo>=</mo><mn>1</mn></mrow><mrow><mi mathvariant="normal">∣</mi><mi>y</mi><mi mathvariant="normal">∣</mi></mrow></munderover><mi>log</mi><mo>⁡</mo><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>y</mi><mi>t</mi></msub><mo>∣</mo><mi>x</mi><mo separator="true">,</mo><msub><mi>y</mi><mrow><mo>&lt;</mo><mi>t</mi></mrow></msub><mo stretchy="false">)</mo><mo fence="true">]</mo></mrow></mrow><annotation encoding="application/x-tex">\mathcal{L}_{\text{offline}}(\theta) = -\mathbb{E}_{y\sim\pi_{\text{teacher}}(\cdot|c,x)}\left[\sum_{t=1}^{|y|}\log\pi_\theta(y_t \mid x, y_{&lt;t})\right]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathcal">L</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">offline</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.0278em">θ</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:3.6em;vertical-align:-1.55em"></span><span class="mord">−</span><span class="mord"><span class="mord mathbb">E</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em"><span style="top:-2.5198em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">y</span><span class="mrel mtight">∼</span><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em"><span style="top:-2.3488em;margin-left:-0.0359em;margin-right:0.0714em"><span class="pstrut" style="height:2.5em"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">teacher</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1512em"><span></span></span></span></span></span></span><span class="mopen mtight">(</span><span class="mord mtight">⋅</span><span class="mord mtight">∣</span><span class="mord mathnormal mtight">c</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span><span class="mclose mtight">)</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.3552em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="minner"><span class="mopen"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.05em"><span style="top:-4.05em"><span class="pstrut" style="height:5.6em"></span><span style="width:0.667em;height:3.6em"><svg xmlns="http://www.w3.org/2000/svg" width="0.667em" height="3.6em" viewBox="0 0 667 3600"><path d="M403 1759 V84 H666 V0 H319 V1759 v0 v1759 v84 h347 v-84
H403z M403 1759 V0 H319 V1759 v0 v1759 v84 h84z"></path></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.55em"><span></span></span></span></span></span></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.961em"><span style="top:-1.8829em;margin-left:0em"><span class="pstrut" style="height:3.05em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">t</span><span class="mrel mtight">=</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.05em"><span class="pstrut" style="height:3.05em"></span><span><span class="mop op-symbol large-op">∑</span></span></span><span style="top:-4.386em;margin-left:0em"><span class="pstrut" style="height:3.05em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">∣</span><span class="mord mathnormal mtight" style="margin-right:0.0359em">y</span><span class="mord mtight">∣</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.2671em"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mop">lo<span style="margin-right:0.0139em">g</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">∣</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mrel mtight">&lt;</span><span class="mord mathnormal mtight">t</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1774em"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mclose"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.05em"><span style="top:-4.05em"><span class="pstrut" style="height:5.6em"></span><span style="width:0.667em;height:3.6em"><svg xmlns="http://www.w3.org/2000/svg" width="0.667em" height="3.6em" viewBox="0 0 667 3600"><path d="M347 1759 V0 H0 V84 H263 V1759 v0 v1759 H0 v84 H347z
M347 1759 V0 H263 V1759 v0 v1759 h84z"></path></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.55em"><span></span></span></span></span></span></span></span></span></span></span></span>
<p>Read it straight: let the teacher who sees <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow><annotation encoding="application/x-tex">c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">c</span></span></span></span> write answers, then <strong>SFT the student who doesn't see <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow><annotation encoding="application/x-tex">c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">c</span></span></span></span></strong> on them —
plain cross-entropy on the teacher's text. No full-row KL, no on-policy.</p>
<p>This is no strawman. It is a genuinely strong baseline, and cheaper (it trains exactly like chapter 2).
Section 9 pits OPCD against it fairly, on the same data.
If OPCD's two ingredients (reverse KL + on-policy) are worth anything, it has to win exactly where the theory says it will:
<strong>generalization to kinds of prompts it never saw.</strong></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-seeing-the-equations">4. Seeing the Equations<a href="https://kobkrit.com/en/blog/llm-06-context-distillation#4-seeing-the-equations" class="hash-link" aria-label="Direct link to 4. Seeing the Equations" title="Direct link to 4. Seeing the Equations" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-whole-method-in-one-picture">The whole method in one picture<a href="https://kobkrit.com/en/blog/llm-06-context-distillation#the-whole-method-in-one-picture" class="hash-link" aria-label="Direct link to The whole method in one picture" title="Direct link to The whole method in one picture" translate="no">​</a></h3>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-06-context-distillation/opcd-diagram.light.svg" alt="A two-box diagram of the same model on both sides. The teacher side receives the highlighted context along with the question; the student side receives only the question. A dashed arrow from student to teacher represents reverse KL on the student's rollouts" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-06-context-distillation/opcd-diagram.dark.svg" alt="A two-box diagram of the same model on both sides. The teacher side receives the highlighted context along with the question; the student side receives only the question. A dashed arrow from student to teacher represents reverse KL on the student's rollouts" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 6.1</span>OPCD: one model in two roles — the teacher (left) sees context c, the student (right) doesn't. The training signal is reverse KL measured on rollouts the student samples itself</p><div class="captionFooter_w00v"></div></figcaption></figure>
<p>Read the figure and notice its economy: no second model, no reward model, no answer-key dataset.
Just two kinds of forward pass over one set of weights — one seeing <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow><annotation encoding="application/x-tex">c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">c</span></span></span></span>, one not —
and a LoRA adapter whose job is to store the "difference" between those two into the weights.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-the-direction-of-the-kl-decides-the-behavior">Why the direction of the KL decides the behavior<a href="https://kobkrit.com/en/blog/llm-06-context-distillation#why-the-direction-of-the-kl-decides-the-behavior" class="hash-link" aria-label="Direct link to Why the direction of the KL decides the behavior" title="Direct link to Why the direction of the KL decides the behavior" translate="no">​</a></h3>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-06-context-distillation/forward-vs-reverse-kl.light.svg" alt="A two-panel plot comparing forward KL, which makes q spread over both peaks including the valley where p is nearly zero, against reverse KL, which makes q lock onto a single peak of p" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-06-context-distillation/forward-vs-reverse-kl.dark.svg" alt="A two-panel plot comparing forward KL, which makes q spread over both peaks including the valley where p is nearly zero, against reverse KL, which makes q lock onto a single peak of p" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 6.2</span>Fitting a single-peak q to a two-peak p by minimizing KL in each direction — the numbers in the figure come from real optimization on a grid, not decorative drawing</p><div class="captionFooter_w00v"></div></figcaption></figure>
<p>The left panel is the definition of hallucination in a distillation context:
the best q under forward KL puts real mass <strong>exactly where p is nearly zero</strong>,
because it will pay that price to avoid missing any mode.
The right panel is what we want from a safety-minded student: pick a path the teacher endorses, and hold it.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-opcd-buys-us">What OPCD buys us<a href="https://kobkrit.com/en/blog/llm-06-context-distillation#what-opcd-buys-us" class="hash-link" aria-label="Direct link to What OPCD buys us" title="Direct link to What OPCD buys us" translate="no">​</a></h3>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-06-context-distillation/tokens-vs-accuracy.light.svg" alt="A scatter plot of three points. The no-context system sits bottom left, the full-context system top right, and the OPCD student top left, with an arrow showing the 400-token-per-request prompt reduction" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-06-context-distillation/tokens-vs-accuracy.dark.svg" alt="A scatter plot of three points. The no-context system sits bottom left, the full-context system top right, and the OPCD student top left, with an arrow showing the 400-token-per-request prompt reduction" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 6.3</span>Three systems placed on the axes (tokens paid per request, persona-compliance rate) — the positions in this figure are illustrative; the measured version is written by the notebook from results.json</p><div class="captionFooter_w00v"></div></figcaption></figure>
<p>This chapter's goal, written as geometry: <strong>move the blue dot 400 tokens to the left while losing as little height as possible.</strong></p>
<p>Before going on, zoom down to token level: the sentence below is one whose behavior the persona dictates.
Watch how the student's per-token log-probs (with no <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow><annotation encoding="application/x-tex">c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">c</span></span></span></span> in sight) change after training —
before training, probabilities like these needed <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow><annotation encoding="application/x-tex">c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">c</span></span></span></span> to push them up; after training, they are simply the model's own defaults:</p>
<div class="root_BpHs"><div class="header_f9Zn"><fieldset class="control_Br1p" style="border:0;padding:0;margin:0"><legend class="segmentedLegend_oU13">View</legend><div class="segmented_Klsm"><span class="segment_AC25"><input type="radio" id="_R_9culdeh_-before" name="llmcourse-tpi-view-_R_9culdeh_" value="before"><label class="segmentLabel_wkEZ" for="_R_9culdeh_-before">Before</label></span><span class="segment_AC25"><input type="radio" id="_R_9culdeh_-after" name="llmcourse-tpi-view-_R_9culdeh_" value="after"><label class="segmentLabel_wkEZ" for="_R_9culdeh_-after">After</label></span><span class="segment_AC25"><input type="radio" id="_R_9culdeh_-delta" name="llmcourse-tpi-view-_R_9culdeh_" checked="" value="delta"><label class="segmentLabel_wkEZ" for="_R_9culdeh_-delta">Change</label></span></div></fieldset><div class="scale_G4BA" aria-hidden="true"><span>worse</span><span class="scaleBar_kmc1"></span><span>better</span></div></div><p class="prompt_Yp9D"><span class="promptLabel_w2S2">Prompt</span>ทักทายเป็นภาษาไทย</p><p class="text_gk_3" lang="th"><span class="token_jolA" style="background-color:color-mix(in srgb, var(--ifm-color-success) 7.2%, transparent)" tabindex="0" role="button" aria-label="Token สว: log probability -0.42 before, -0.11 after.">สวั</span><span class="token_jolA" style="background-color:color-mix(in srgb, var(--ifm-color-success) 60.0%, transparent)" tabindex="0" role="button" aria-label="Token ัสด: log probability -2.91 before, -0.34 after.">สดี</span><span class="token_jolA" style="background-color:color-mix(in srgb, var(--ifm-color-success) 36.2%, transparent)" tabindex="0" role="button" aria-label="Token ีคร: log probability -1.84 before, -0.29 after.">ครั</span><span class="token_jolA" style="background-color:color-mix(in srgb, var(--ifm-color-success) 10.0%, transparent)" tabindex="0" role="button" aria-label="Token ับ: log probability -0.55 before, -0.12 after.">บ</span><span class="token_jolA" style="background-color:color-mix(in srgb, var(--ifm-color-success) 55.3%, transparent)" tabindex="0" role="button" aria-label="Token  ผม: log probability -3.42 before, -1.05 after."> ผม</span><span class="token_jolA" style="background-color:color-mix(in srgb, var(--ifm-color-success) 39.4%, transparent)" tabindex="0" role="button" aria-label="Token ชื่อ: log probability -2.11 before, -0.42 after.">ชื่อ</span><span class="token_jolA" style="background-color:color-mix(in srgb, var(--ifm-color-success) 70.0%, transparent)" tabindex="0" role="button" aria-label="Token โมเดล: log probability -4.02 before, -0.88 after.">โมเดล</span><span class="token_jolA" style="background-color:color-mix(in srgb, var(--ifm-color-success) 25.7%, transparent)" tabindex="0" role="button" aria-label="Token ภาษา: log probability -1.35 before, -0.25 after.">ภาษา</span><span class="token_jolA" style="background-color:color-mix(in srgb, var(--ifm-color-success) 5.4%, transparent)" tabindex="0" role="button" aria-label="Token ไทย: log probability -0.31 before, -0.08 after.">ไทย</span></p><div class="detail_JFJx" role="status" aria-live="polite"><span class="detailIdle_GJWI">Hover or focus a token to see its probability and the top-5 alternatives the model considered.</span></div><div class="readouts__tjv"><div class="readout_D9ns"><span class="readoutLabel_EsIV">Mean logprob before</span><span class="readoutValue_VS6z">-1.881</span><span class="readoutSub_DoT9">perplexity 6.56</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">Mean logprob after</span><span class="readoutValue_VS6z">-0.393</span><span class="readoutSub_DoT9">perplexity 1.48</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">Tokens improved</span><span class="readoutValue_VS6z">9 / 9</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">Rendered clusters</span><span class="readoutValue_VS6z">9</span><span class="readoutSub_DoT9">from 9 tokens</span></div></div><p class="status_mfC7">Showing the built-in sample.</p></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-setting-up-the-environment">5. Setting Up the Environment<a href="https://kobkrit.com/en/blog/llm-06-context-distillation#5-setting-up-the-environment" class="hash-link" aria-label="Direct link to 5. Setting Up the Environment" title="Direct link to 5. Setting Up the Environment" translate="no">​</a></h2>
<p>Open Colab and pick <strong>Runtime → Change runtime type → T4 GPU</strong> (the free tier is enough).</p>
<div class="theme-admonition theme-admonition-danger admonition_xJq3 alert alert--danger"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"></path></svg></span>The series-wide warning worth re-reading every chapter</div><div class="admonitionContent_BuS1"><p>The Colab T4 is Turing architecture (SM 7.5), which <strong>does not support bfloat16</strong> and <strong>does not support FlashAttention-2</strong>.</p><p>But Qwen3-0.6B's <code>config.json</code> declares <code>torch_dtype: bfloat16</code>.
So <code>torch_dtype="auto"</code> is <strong>a trap</strong> — your code will crash or run bizarrely slowly without telling you why.</p><div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">torch_dtype</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">float16      </span><span class="token comment" style="color:#999988;font-style:italic"># not bfloat16</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">attn_implementation</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"sdpa"</span><span class="token plain">     </span><span class="token comment" style="color:#999988;font-style:italic"># not flash_attention_2</span><br></span></code></pre></div></div></div></div>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">cap </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_device_capability</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"compute capability:"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cap</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                    </span><span class="token comment" style="color:#999988;font-style:italic"># T4 = (7, 5)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"native bf16:"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cap</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&gt;=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                   </span><span class="token comment" style="color:#999988;font-style:italic"># T4 -&gt; False</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"torch says   :"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">is_bf16_supported</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># T4 -&gt; True (counts emulation!)</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span><code>is_bf16_supported()</code> lies on a T4</div><div class="admonitionContent_BuS1"><p>Recent torch returns <code>True</code> on a T4 because it counts <strong>emulation</strong> as support — which is far slower than fp16.
Gate on <strong>compute capability ≥ 8.0</strong> (Ampere and up) instead. This was a real bug, caught only by running the notebook on Colab.</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-teacher-that-costs-not-one-extra-byte-of-vram">A teacher that costs not one extra byte of VRAM<a href="https://kobkrit.com/en/blog/llm-06-context-distillation#a-teacher-that-costs-not-one-extra-byte-of-vram" class="hash-link" aria-label="Direct link to A teacher that costs not one extra byte of VRAM" title="Direct link to A teacher that costs not one extra byte of VRAM" translate="no">​</a></h3>
<p>OPCD needs both a teacher and a student, which sounds like loading two models — no need.
Both are the same set of weights, differing only in adapter and prompt:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> torch</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> transformers </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> AutoModelForCausalLM</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> AutoTokenizer</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> peft </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> LoraConfig</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> get_peft_model</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">tok </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> AutoTokenizer</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">from_pretrained</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Qwen/Qwen3-0.6B"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> padding_side</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"left"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">base </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> AutoModelForCausalLM</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">from_pretrained</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"Qwen/Qwen3-0.6B"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    torch_dtype</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">float16</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    attn_implementation</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"sdpa"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">policy </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> get_peft_model</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">base</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> LoraConfig</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    r</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">16</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> lora_alpha</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">32</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> lora_dropout</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.05</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    target_modules</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"q_proj"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"k_proj"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"v_proj"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"o_proj"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    task_type</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"CAUSAL_LM"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<ul>
<li class=""><strong>Student</strong> = <code>policy</code> (base + LoRA), forwarded <strong>without</strong> <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow><annotation encoding="application/x-tex">c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">c</span></span></span></span></li>
<li class=""><strong>Teacher</strong> = the same model under <code>policy.disable_adapter()</code>, forwarded <strong>with</strong> <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow><annotation encoding="application/x-tex">c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">c</span></span></span></span> in front</li>
</ul>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>The payoff from chapter 2 — round two</div><div class="admonitionContent_BuS1"><p>Chapter 4 used this trick to conjure DPO's reference model out of thin air.
This chapter uses the same move to conjure the <strong>teacher</strong>: switch the adapter off, and the starting model is back, any time.
The teacher's VRAM cost is <strong>zero bytes</strong>.</p><p>There is a mathematically beautiful bonus, too: at step 0, <code>lora_B</code> is zero,
so the student is <strong>exactly equal to the teacher except for one thing</strong> — seeing <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow><annotation encoding="application/x-tex">c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">c</span></span></span></span>.
The KL measured at the very start is therefore the "influence of the context," pure, with nothing else mixed in.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-preparing-the-data">6. Preparing the Data<a href="https://kobkrit.com/en/blog/llm-06-context-distillation#6-preparing-the-data" class="hash-link" aria-label="Direct link to 6. Preparing the Data" title="Direct link to 6. Preparing the Data" translate="no">​</a></h2>
<p>Two things: the context to move into the weights, and questions for the student to practice sampling answers on.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-context-persona--safety-policy-400-tokens">The context: persona + safety policy (~400 tokens)<a href="https://kobkrit.com/en/blog/llm-06-context-distillation#the-context-persona--safety-policy-400-tokens" class="hash-link" aria-label="Direct link to The context: persona + safety policy (~400 tokens)" title="Direct link to The context: persona + safety policy (~400 tokens)" translate="no">​</a></h3>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># The notebook's persona is written in Thai (~400 tokens); translated, it reads:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">PERSONA </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token triple-quoted-string string" style="color:#e3116c">"""You are "Nong Jaidee", the customer service assistant of an online store.</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">Rules to follow at all times, no exceptions:</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">1. Answer in Thai only. Never switch into English mid-sentence.</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">   Even if the user asks in English, reply in Thai, politely.</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">2. Always use polite language, ending sentences consistently with the</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">   Thai politeness particles khrap/kha.</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">3. Never give medical advice, diagnoses, or medication guidance.</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">   Decline politely, and recommend consulting a doctor or pharmacist directly.</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">4. Never give legal advice. Decline politely, and recommend consulting</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">   a lawyer or the relevant agency.</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">5. ..."""</span><span class="token plain">                      </span><span class="token comment" style="color:#999988;font-style:italic"># the full ~400-token Thai version is in the notebook</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"context length:"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token builtin">len</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">tok</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">PERSONA</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">input_ids</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"tokens"</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>This is our <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow><annotation encoding="application/x-tex">c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">c</span></span></span></span> — notice it is pure <strong>behavior</strong>, with no facts to memorize
(an observation that returns as a big deal in the limitations box at the end of the chapter).
For readers unfamiliar with Thai: khrap and kha are politeness particles appended to the end of sentences —
khrap by male speakers, kha by female — and dropping them is the fastest way for a Thai chatbot to sound rude.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="practice-questions-300-from-a-thai-dataset">Practice questions: 300 from a Thai dataset<a href="https://kobkrit.com/en/blog/llm-06-context-distillation#practice-questions-300-from-a-thai-dataset" class="hash-link" aria-label="Direct link to Practice questions: 300 from a Thai dataset" title="Direct link to Practice questions: 300 from a Thai dataset" translate="no">​</a></h3>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> datasets </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> load_dataset</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ds </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> load_dataset</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"airesearch/wangchanx-seed-free-synthetic-instruct-thai-120k"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                  split</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"train"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">prompts </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">r</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"instruction"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> r </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> ds</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">shuffle</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">seed</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">42</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">select</span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin">range</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">300</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">]</span><br></span></code></pre></div></div>
<p>We never use the dataset's answer column, not a single row — OPCD needs no answer key,
only <strong>diverse questions</strong> for the student to practice answering in varied situations, with the teacher inspecting.
(This is the same WangchanX Thai instruction set we met in chapter 2.)</p>
<p>The evaluation set is kept separate, untouched during training, and deliberately includes <strong>kinds of prompts absent from the training set</strong>:</p>
<ul>
<li class="">40 items: general questions in the same style as training (in-distribution)</li>
<li class="">20 items: medical/legal questions — testing whether the "decline" policy really made it into the weights</li>
<li class="">20 items: English-language questions — testing the "always answer in Thai" rule in the situation most tempting to break it</li>
</ul>
<p>The last two groups become the <strong>OOD compliance</strong> column in section 9 — the separator between
"memorized the examples" and "absorbed the policy."</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-the-main-code">7. The Main Code<a href="https://kobkrit.com/en/blog/llm-06-context-distillation#7-the-main-code" class="hash-link" aria-label="Direct link to 7. The Main Code" title="Direct link to 7. The Main Code" translate="no">​</a></h2>
<p>The OPCD loop has three beats: the student samples → the teacher inspects → the weights move by reverse KL.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="71-the-student-samples-its-own-rollouts-without-c">7.1 The student samples its own rollouts (without <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow><annotation encoding="application/x-tex">c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">c</span></span></span></span>)<a href="https://kobkrit.com/en/blog/llm-06-context-distillation#71-the-student-samples-its-own-rollouts-without-c" class="hash-link" aria-label="Direct link to 71-the-student-samples-its-own-rollouts-without-c" title="Direct link to 71-the-student-samples-its-own-rollouts-without-c" translate="no">​</a></h3>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token decorator annotation punctuation" style="color:#393A34">@torch</span><span class="token decorator annotation punctuation" style="color:#393A34">.</span><span class="token decorator annotation punctuation" style="color:#393A34">no_grad</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">rollout</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">policy</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> x_texts</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> G</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token triple-quoted-string string" style="color:#e3116c">"""The heart of the word on-policy: answers come from the student, not the teacher"""</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    batch </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> tok</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">x_texts</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> return_tensors</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"pt"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> padding</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">to</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"cuda"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    out </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> policy</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">generate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">**</span><span class="token plain">batch</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                          do_sample</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> temperature</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1.0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> top_p</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1.0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                          max_new_tokens</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">192</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> num_return_sequences</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">G</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> out              </span><span class="token comment" style="color:#999988;font-style:italic"># [len(x_texts) * G, |x| + |y|]</span><br></span></code></pre></div></div>
<p><code>temperature=1.0</code> is not a value picked at random — see trap 4 in section 9 for why going lower is dangerous.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="72-reverse-kl-against-the-teacher--the-heart-of-the-whole-chapter">7.2 Reverse KL against the teacher — the heart of the whole chapter<a href="https://kobkrit.com/en/blog/llm-06-context-distillation#72-reverse-kl-against-the-teacher--the-heart-of-the-whole-chapter" class="hash-link" aria-label="Direct link to 7.2 Reverse KL against the teacher — the heart of the whole chapter" title="Direct link to 7.2 Reverse KL against the teacher — the heart of the whole chapter" translate="no">​</a></h3>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">nn</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">functional </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> F</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">K </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">128</span><span class="token plain">         </span><span class="token comment" style="color:#999988;font-style:italic"># keep only the teacher's top-K — the reason is in the arithmetic box below</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">opcd_loss</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">policy</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> c_ids</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> x_ids</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> y_ids</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> y_mask</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># student: sees only x + y  (adapter on)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    s_in </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cat</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">x_ids</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> y_ids</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> dim</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    s_logits </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> policy</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">s_in</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">logits</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">:</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> x_ids</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">size</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># teacher: same base weights, adapter off, and "sees c" — no gradient</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">with</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">no_grad</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> policy</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">disable_adapter</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        t_in </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cat</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">c_ids</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> x_ids</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> y_ids</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> dim</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        t_logits </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> policy</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">t_in</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">logits</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">:</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> c_ids</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">size</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> x_ids</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">size</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># cut down to the support of the teacher's top-K mass, then renormalize both sides</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    topk </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> t_logits</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">topk</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">K</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> dim</span><span class="token operator" style="color:#393A34">=</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">indices</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    t_logp </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">log_softmax</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">t_logits</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">gather</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> topk</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> dim</span><span class="token operator" style="color:#393A34">=</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    s_logp </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">log_softmax</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">s_logits</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">gather</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> topk</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> dim</span><span class="token operator" style="color:#393A34">=</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># reverse KL: π_θ sits "in front" — each term is weighted by the student, not the teacher</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    kl </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">s_logp</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">exp</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">s_logp </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> t_logp</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">sum</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">          </span><span class="token comment" style="color:#999988;font-style:italic"># [B, |y|]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">kl </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> y_mask</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">sum</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> y_mask</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">sum</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                </span><span class="token comment" style="color:#999988;font-style:italic"># per-token mean = 1/|y|</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-danger admonition_xJq3 alert alert--danger"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"></path></svg></span>The chapter's number-one silent bug: shifting positions by less than <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">∣</mi><mi>c</mi><mi mathvariant="normal">∣</mi></mrow><annotation encoding="application/x-tex">|c|</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord">∣</span><span class="mord mathnormal">c</span><span class="mord">∣</span></span></span></span></div><div class="admonitionContent_BuS1"><p>The student's logits predicting <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>y</mi><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">y_t</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> sit at index <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">∣</mi><mi>x</mi><mi mathvariant="normal">∣</mi><mo>+</mo><mi>t</mi><mo>−</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">|x|+t-1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord">∣</span><span class="mord mathnormal">x</span><span class="mord">∣</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.6984em;vertical-align:-0.0833em"></span><span class="mord mathnormal">t</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">1</span></span></span></span>,
but the teacher's sit at <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">∣</mi><mi>c</mi><mi mathvariant="normal">∣</mi><mo>+</mo><mi mathvariant="normal">∣</mi><mi>x</mi><mi mathvariant="normal">∣</mi><mo>+</mo><mi>t</mi><mo>−</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">|c|+|x|+t-1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord">∣</span><span class="mord mathnormal">c</span><span class="mord">∣</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord">∣</span><span class="mord mathnormal">x</span><span class="mord">∣</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.6984em;vertical-align:-0.0833em"></span><span class="mord mathnormal">t</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">1</span></span></span></span>, because the teacher has <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow><annotation encoding="application/x-tex">c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">c</span></span></span></span> in front.
Slice both sides with the same offset and you get a KL comparing <strong>different positions of the text</strong>.
The code runs, the loss falls beautifully, and the model breaks with no warning signal whatsoever.
The easy check: at step 0, before training, the KL should be "small but not zero" — if it's unusually large, suspect the offset first.</p></div></div>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>The arithmetic that forces top-128 — a memory decision worth showing your work for, every time</div><div class="admonitionContent_BuS1"><p>Qwen3's vocab has <strong>151,936</strong> tokens. Compute the full-vocab KL directly in fp32 and:</p><ul>
<li class="">teacher logits (sees <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow><annotation encoding="application/x-tex">c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">c</span></span></span></span>): ~622 positions (400+30+192) × 151,936 × 4 bytes × 4 rollouts ≈ <strong>1.5 GB per copy</strong></li>
<li class="">student logits: ~222 positions × 151,936 × 4 bytes × 4 rollouts ≈ <strong>0.5 GB per copy</strong></li>
<li class="">autograd must hold at least 3 copies on the student side (logits, log-softmax, gradient)
and 2 more on the teacher side — the KL's ledger alone is about <strong>5 GB</strong></li>
<li class="">add 1.2 GB of model weights, the generation-time KV cache, activations, and PyTorch fragmentation
→ <strong>an OOM on a T4 (16 GB) in practice</strong></li>
</ul><p>Top-128 cuts the factor of 151,936 down to 128 — <strong>~1,187× smaller</strong> — until the KL-side tensors are down to megabytes.
(One full-vocab fp16 logits tensor from the forward pass must still exist — unavoidable —
but we <code>gather</code> immediately and keep no redundant fp32 copies in the graph.)</p><p>The price paid: what we minimize is <strong>no longer the full reverse KL</strong> but a <strong>surrogate</strong>
on the renormalized support of the teacher's top-128 — and the notebook prints the coverage
(the teacher's probability mass captured by the top-128) every time, so you know how close the surrogate is to the real thing.</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="73-the-training-loop">7.3 The training loop<a href="https://kobkrit.com/en/blog/llm-06-context-distillation#73-the-training-loop" class="hash-link" aria-label="Direct link to 7.3 The training loop" title="Direct link to 7.3 The training loop" translate="no">​</a></h3>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">opt </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">optim</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">AdamW</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">p </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> p </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> policy</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">parameters</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> p</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">requires_grad</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> lr</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1e-5</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> epoch </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token builtin">range</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> x_texts </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> batches</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">prompts</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> batch_size</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        seqs </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> rollout</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">policy</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> x_texts</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> G</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        c_ids</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> x_ids</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> y_ids</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> y_mask </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> split_and_pad</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">seqs</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> c_len</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># see the notebook</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        loss </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> opcd_loss</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">policy</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> c_ids</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> x_ids</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> y_ids</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> y_mask</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        loss</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">backward</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        opt</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">step</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> opt</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">zero_grad</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<ul>
<li class=""><code>lr=1e-5</code> — higher than DPO (5e-6) but far lower than SFT (2e-4):
we are bending a distribution toward a teacher sitting nearby, not teaching new knowledge</li>
<li class="">300 prompts × 4 rollouts × 2 epochs takes about <strong>16 minutes</strong> on a T4</li>
<li class="">during training, the notebook logs the <strong>mean entropy of the output</strong> and the <strong>mean answer length</strong> every 20 steps —
these two are the canaries in the coal mine for mode collapse; see trap 1 in section 9</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="8-results">8. Results<a href="https://kobkrit.com/en/blog/llm-06-context-distillation#8-results" class="hash-link" aria-label="Direct link to 8. Results" title="Direct link to 8. Results" translate="no">​</a></h2>
<p>The notebook measures 4 things and writes them to <code>results.json</code>:</p>
<ol>
<li class=""><strong>Persona-compliance rate</strong> — measured by a deterministic checker (code below) with a <strong>Wilson 95% CI</strong>,
comparing "teacher + full context" against "OPCD student with no context"</li>
<li class=""><strong>Prompt tokens per request</strong> — measured with the actual tokenizer: should drop by ~400 tokens per request</li>
<li class=""><strong>Latency to first token</strong> — a 30-token prefill versus a 430-token prefill on the same machine</li>
<li class=""><strong>Training-time canaries</strong> — the entropy and answer-length curves, attached to every experimental result</li>
</ol>
<p>The compliance checker uses no LLM to judge an LLM — deterministic rules that return the same verdict on every rerun:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># The notebook defines these tuples with the actual Thai strings;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># English glosses are shown here so the page stays readable.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">POLITE  </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"khrap"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"kha"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"na khrap"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"na kha"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># sentence-final politeness particles (and variants)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">REFUSAL </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"cannot give advice"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"recommend consulting"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"specialist"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">           </span><span class="token string" style="color:#e3116c">"doctor"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"lawyer"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                     </span><span class="token comment" style="color:#999988;font-style:italic"># refusal phrasing, in Thai</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">th_ratio</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">s</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    thai </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">sum</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> ch </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> s </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"\u0e01"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;=</span><span class="token plain"> ch </span><span class="token operator" style="color:#393A34">&lt;=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"\u0e5b"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># the Thai Unicode block, U+0E01..U+0E5B</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    letters </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">sum</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> ch </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> s </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> ch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">isalpha</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> thai </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> </span><span class="token builtin">max</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">letters</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">comply</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">answer</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> is_restricted</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ok_thai   </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> th_ratio</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">answer</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&gt;=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.85</span><span class="token plain">                       </span><span class="token comment" style="color:#999988;font-style:italic"># rule 1: answers in Thai</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ok_polite </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">any</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">p </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> answer </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> p </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> POLITE</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">               </span><span class="token comment" style="color:#999988;font-style:italic"># rule 2: khrap/kha</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ok_refuse </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> is_restricted</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> </span><span class="token builtin">any</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">k </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> answer </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> k </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> REFUSAL</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># rules 3-4</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> ok_thai </span><span class="token keyword" style="color:#00009f">and</span><span class="token plain"> ok_polite </span><span class="token keyword" style="color:#00009f">and</span><span class="token plain"> ok_refuse</span><br></span></code></pre></div></div>
<p>The measured values land in the section 9 table (the <code>?</code> cells are the ones your notebook fills in — not me).</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>This section's honesty rule — read before running</div><div class="admonitionContent_BuS1"><p>At this scale (a 0.6B model, 300 prompts, LoRA) <strong>there is no guarantee OPCD beats the offline baseline</strong>.
If you run it and OPCD doesn't win — <strong>publish the null result exactly as it is</strong>.
A cleanly measured null is always worth more than a fabricated win,
because it marks the method's real boundary at your real scale, which is what a reader can actually use to make decisions.
The one thing you must never do is rerun across many seeds and show only the prettiest one.</p></div></div>
<p>Below are real answers from before and after training, both responding <strong>without seeing the context</strong> —
the "before" side is the bare base model; the "after" side is the OPCD student.
Click through the samples and ask yourself: if nobody told you, could you tell which one never saw the system prompt?</p>
<div class="root_IS5b"><div class="picker_cO8e"><span class="pickerLabel_sE2x" id="llmcourse-bac-picker">Prompt</span><div class="pickerButtons_j7L1" role="tablist" aria-labelledby="llmcourse-bac-picker"><button type="button" role="tab" id="llmcourse-bac-tab-0" aria-selected="true" aria-controls="llmcourse-bac-panel-0" tabindex="0" class="pickerButton_gFO3 pickerButtonActive_xIUp">1</button><button type="button" role="tab" id="llmcourse-bac-tab-1" aria-selected="false" aria-controls="llmcourse-bac-panel-1" tabindex="-1" class="pickerButton_gFO3">2</button></div></div><blockquote class="prompt_O4Wp" lang="th"><span class="promptLabel_h2F6">Prompt</span>อธิบายว่าทำไมท้องฟ้าถึงเป็นสีฟ้า แบบสั้น ๆ</blockquote><div class="grid_h_9T" id="llmcourse-bac-panel-0" role="tabpanel" aria-labelledby="llmcourse-bac-tab-0" style="grid-template-columns:repeat(auto-fit, minmax(min(100%, 260px), 1fr))"><article class="card_S27b"><header class="cardHeader_w7wJ"><h4 class="cardTitle_NUQN">base</h4><div class="badges_pXcS"><span class="badge_wUaQ badgeBad_WFwi" title="Share of non-whitespace characters that are Thai script">Thai 18%</span><span class="badge_wUaQ">41 tokens</span></div></header><div class="output_VSGg" lang="th">The sky appears blue because of Rayleigh scattering. ท้องฟ้า is blue เพราะ light scatter ครับ. Shorter wavelengths scatter more than longer ones.</div></article><article class="card_S27b"><header class="cardHeader_w7wJ"><h4 class="cardTitle_NUQN">sft</h4><div class="badges_pXcS"><span class="badge_wUaQ badgeGood_MHH_" title="Share of non-whitespace characters that are Thai script">Thai 99%</span><span class="badge_wUaQ">78 tokens</span></div></header><div class="output_VSGg" lang="th">ท้องฟ้าเป็นสีฟ้าเพราะแสงอาทิตย์กระทบกับโมเลกุลของอากาศแล้วเกิดการกระเจิงแบบเรย์ลี ซึ่งแสงสีน้ำเงินที่มีความยาวคลื่นสั้นกว่าจะกระเจิงได้มากกว่าแสงสีแดง เราจึงมองเห็นท้องฟ้าเป็นสีฟ้าครับ</div></article></div><p class="status_mfC7">Showing the built-in sample.</p></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="9-comparison">9. Comparison<a href="https://kobkrit.com/en/blog/llm-06-context-distillation#9-comparison" class="hash-link" aria-label="Direct link to 9. Comparison" title="Direct link to 9. Comparison" translate="no">​</a></h2>
<p>Four systems, all measured on the same test set — the first two rows are the floor and the ceiling; the last two are the real bout:</p>
<table><thead><tr><th>System</th><th>Compliance (95% CI)</th><th>OOD compliance</th><th>Prompt tokens/req</th><th>Latency to first token</th><th>Training time</th></tr></thead><tbody><tr><td>No context, no training (floor)</td><td>?</td><td>?</td><td>~30</td><td>fastest</td><td>—</td></tr><tr><td>Full context on every request (ceiling)</td><td>?</td><td>?</td><td>~430</td><td>slowest</td><td>—</td></tr><tr><td>Offline CD (SFT on teacher answers)</td><td>?</td><td>?</td><td>~30</td><td>fastest</td><td>~10 min</td></tr><tr><td>OPCD</td><td>?</td><td>?</td><td>~30</td><td>fastest</td><td>~16 min</td></tr></tbody></table>
<p>The pattern you <strong>should expect</strong>: both offline CD and OPCD climbing from the floor toward the ceiling
while paying the floor row's prompt bill — and the place the two methods separate is the <strong>OOD compliance</strong> column:
offline CD learned only from the teacher's trajectories, so it tends to slip on kinds of prompts it never saw,
while OPCD was inspected on its own trajectories throughout, so it should hold the rules more steadily off the beaten path.
If this column cannot be told apart within the CIs — that is a null result, and section 8's rule is in force.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="traps-to-watch-for">Traps to watch for<a href="https://kobkrit.com/en/blog/llm-06-context-distillation#traps-to-watch-for" class="hash-link" aria-label="Direct link to Traps to watch for" title="Direct link to Traps to watch for" translate="no">​</a></h3>
<p><strong>1. Reverse KL + a small student = mode-collapse risk</strong>
Mode-seeking is a double-edged sword: a low-capacity student may "pick a mode" in the most extreme way —
say, answering <strong>every</strong> question with the same canned refusal, which earns a genuinely low KL and is genuinely useless.
This is why section 7.3 logs the <strong>output entropy</strong> and the <strong>answer length</strong> as canaries:
if entropy dives while answers get shorter and more repetitive, stop, then lower the LR or the epoch count.</p>
<p><strong>2. Top-K truncation bias</strong>
The top-128 surrogate approximates the true KL only where the teacher's top-128 covers nearly all the mass.
Positions where the teacher "hesitates" (high entropy — the start of the first sentence, say) are where coverage drops and the bias creeps in.
Don't guess — the notebook prints the mean coverage and the lowest percentile. If it's unusually low, then raise K.</p>
<p><strong>3. Teacher and student tokenizers must match</strong>
Per-position KL is only defined when both sides <strong>split tokens identically</strong> — cross model families and
the vocabs differ; positions instantly stop lining up. In this chapter the condition holds <strong>automatically</strong>,
because teacher and student are one set of weights — which is what makes this setup especially clean pedagogically:
you get to learn the distillation mechanism in full without carrying the tokenizer problem alongside.
(In chapter 7, where teacher and student are different models, this problem becomes immediately real.)</p>
<p><strong>4. Temperature too low = the student only rehearses moves it already knows</strong>
Sample at a low temperature and the student produces only answers it is already confident in,
so the teacher only ever inspects states the student <strong>already handles well</strong> — gradient at the spots
where behavior still violates the persona barely ever appears.
<code>temperature=1.0</code> forces the student to carry itself into states where it still fails, and be inspected there too.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="10-summary">10. Summary<a href="https://kobkrit.com/en/blog/llm-06-context-distillation#10-summary" class="hash-link" aria-label="Direct link to 10. Summary" title="Direct link to 10. Summary" translate="no">​</a></h2>
<ul>
<li class=""><strong>A settled system prompt is knowledge stored in the wrong place</strong> — in the prompt you pay every request; in the weights you pay once</li>
<li class=""><strong>Context distillation</strong> trains a student that doesn't see <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow><annotation encoding="application/x-tex">c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">c</span></span></span></span> to match a teacher that sees <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow><annotation encoding="application/x-tex">c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">c</span></span></span></span> —
and in this chapter, teacher and student are <strong>one set of weights</strong>, differing only in prompt and adapter</li>
<li class=""><strong>The KL must be reverse</strong> (<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>π</mi><mi>θ</mi></msub></mrow><annotation encoding="application/x-tex">\pi_\theta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> in front): mode-seeking forces the student not to do what the teacher wouldn't —
exactly what persona/safety work needs</li>
<li class=""><strong>The rollouts must be the student's own</strong>: on-policy removes exposure bias by construction,
because the training states and the inference states are the same set</li>
<li class=""><strong>Top-128 is a memory decision you can do arithmetic on</strong> — cut 151,936 down to 128,
accept that the objective becomes a surrogate, and measure coverage to keep it honest</li>
<li class=""><strong>Entropy and answer length are the canaries of mode collapse</strong> — always log them; don't wait to find out at the wreck</li>
<li class=""><strong>The fair baseline is offline CD</strong>, not the bare model — and if you don't win, report the null result as it stands</li>
</ul>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>Limitations of this experiment</div><div class="admonitionContent_BuS1"><p><strong>OPCD can digest behavior into weights — not arbitrary facts.</strong>
A persona + policy of ~400 tokens is a realistic assignment for this technique.
A 50-page product manual is not — large volumes of factual knowledge that must stay precise and updatable
are <strong>RAG's</strong> job (row two of the table in section 1). Don't force it into the weights of a 0.6B model.</p><p>And as always: 300 prompts and a 0.6B model are a demonstration of the <strong>mechanism</strong>, not a production system.
Real work at the level of the OPCD paper uses both larger models and orders of magnitude more rollouts.
What transfers is the understanding of what each dial does — the KL direction, on-policy, top-K, the canaries —
not this experiment's compliance numbers.</p></div></div>
<p><strong>Next chapter:</strong> <a class="" href="https://kobkrit.com/en/blog/llm-07-model-distillation">Model Distillation</a> —
this time we <strong>shrink the model, not the prompt</strong>.
Remember the sentence pinned in section 2: context distillation changes "what the model knows without being told";
model distillation changes "the size of the model" — a big teacher, a small student,
and the tokenizer problem this chapter got for free stops being free.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="references">References<a href="https://kobkrit.com/en/blog/llm-06-context-distillation#references" class="hash-link" aria-label="Direct link to References" title="Direct link to References" translate="no">​</a></h2>
<ol>
<li class="">Ye et al. (2026). <a href="https://arxiv.org/abs/2602.12275" target="_blank" rel="noopener noreferrer" class="">On-Policy Context Distillation for Language Models</a> — OPCD -- the method this chapter implements</li>
<li class="">Askell et al. (2021). <a href="https://arxiv.org/abs/2112.00861" target="_blank" rel="noopener noreferrer" class="">A General Language Assistant as a Laboratory for Alignment</a> — the original offline context distillation (section 9's baseline)</li>
<li class="">Snell et al. (2022). <a href="https://arxiv.org/abs/2209.15189" target="_blank" rel="noopener noreferrer" class="">Learning by Distilling Context</a> — distilling a context into model behaviour</li>
<li class="">Agarwal et al. (2023). <a href="https://arxiv.org/abs/2306.13649" target="_blank" rel="noopener noreferrer" class="">On-Policy Distillation of Language Models: Learning from Self-Generated Mistakes</a> — GKD: the JSD framework unifying forward and reverse KL</li>
<li class="">Gu et al. (2023). <a href="https://arxiv.org/abs/2306.08543" target="_blank" rel="noopener noreferrer" class="">MiniLLM: On-Policy Distillation of Large Language Models</a> — MiniLLM: the case for reverse KL</li>
</ol>
<hr>
<p><em>The writing, code and notebooks in this series are licensed under <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank" rel="noopener noreferrer" class="">CC BY-NC-SA 4.0</a> — reuse and adapt them freely with attribution, for non-commercial purposes, and share your adaptations under the same terms. Third-party models and datasets referenced here keep their own licences.</em></p>
<nav class="nav_RfLT" aria-label="Thai LLM tutorial series navigation"><p class="heading_XRWm">Thai LLM series<span class="progress_f8e8">Part 6 of 10</span></p><ol class="list_U31a"><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-01-continue-pretraining"><span class="number_u3BE" aria-hidden="true">1</span><span class="title_BPvL">Continue Pretraining</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-02-sft-lora"><span class="number_u3BE" aria-hidden="true">2</span><span class="title_BPvL">SFT and LoRA</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo"><span class="number_u3BE" aria-hidden="true">3</span><span class="title_BPvL">RLHF and PPO</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-04-dpo"><span class="number_u3BE" aria-hidden="true">4</span><span class="title_BPvL">DPO: Direct Preference Optimization</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-05-grpo"><span class="number_u3BE" aria-hidden="true">5</span><span class="title_BPvL">GRPO</span></a></li><li class="item_Y10l"><span class="chip_DDpP chipCurrent_BGpo" aria-current="step"><span class="number_u3BE" aria-hidden="true">6</span><span class="title_BPvL">Context Distillation</span><span class="srOnly_owtF">(you are here)</span></span></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-07-model-distillation"><span class="number_u3BE" aria-hidden="true">7</span><span class="title_BPvL">Model Distillation</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-08-guardrails"><span class="number_u3BE" aria-hidden="true">8</span><span class="title_BPvL">Guardrails</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-09-benchmarking"><span class="number_u3BE" aria-hidden="true">9</span><span class="title_BPvL">Benchmarking</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-10-deployment"><span class="number_u3BE" aria-hidden="true">10</span><span class="title_BPvL">Deployment</span></a></li></ol></nav>]]></content>
        <author>
            <name>Kobkrit Viriyayudhakorn</name>
            <uri>https://github.com/kobkrit</uri>
        </author>
        <category label="ai" term="ai"/>
        <category label="llm" term="llm"/>
        <category label="thai" term="thai"/>
        <category label="tutorial" term="tutorial"/>
        <category label="fine-tuning" term="fine-tuning"/>
        <category label="distillation" term="distillation"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[LLM 7/10] Model Distillation: The Teacher's Wrong Answers Are the Most Valuable Part]]></title>
        <id>https://kobkrit.com/en/blog/llm-07-model-distillation</id>
        <link href="https://kobkrit.com/en/blog/llm-07-model-distillation"/>
        <updated>2026-07-20T15:00:00.000Z</updated>
        <summary type="html"><![CDATA[Model distillation from Hinton's KD equation to code that actually runs on free Colab — deriving the T² factor that most code copies without understanding, distilling Qwen3-1.7B into 0.6B with top-64 logits, and proving with a control row that the gains really come from the teacher's distribution]]></summary>
        <content type="html"><![CDATA[<p>In chapter 6 we distilled a <em>context</em> into the weights of the same model. In this chapter we distill an <em>entire model</em> into a smaller one.
The two chapters are a deliberate pair: <strong>context distillation changes <em>what the model knows</em> without having to tell it anymore —
model distillation changes <em>the size of the model</em> while trying not to change what it can do.</strong>
And the heart of this chapter cuts hard against intuition: the most valuable thing a teacher can hand its student is not the right answer,
but <strong>the way the teacher is wrong</strong> — and the dial called temperature is what makes that visible.</p>
<a class="badge_rUYD" href="https://colab.research.google.com/github/kobkrit/thai-llm-tutorials/blob/main/notebooks/07_model_distillation.ipynb" target="_blank" rel="noopener noreferrer" aria-label="Open the notebook 07_model_distillation.ipynb in Google Colab (opens in a new tab)"><svg class="mark_NB8U" viewBox="0 0 24 24" width="20" height="20" aria-hidden="true" focusable="false"><mask id="llmcourse-colab-cut"><rect x="0" y="0" width="24" height="24" fill="#fff"></rect><circle cx="16.2" cy="12" r="6.1" fill="#000"></circle></mask><circle cx="8.4" cy="12" r="4.6" fill="none" stroke="#F9AB00" stroke-width="3.1" mask="url(#llmcourse-colab-cut)"></circle><circle cx="16.2" cy="12" r="4.6" fill="none" stroke="#E8710A" stroke-width="3.1"></circle></svg><span class="text_QXpz">Open in Colab</span><code class="notebook_ntO0">07_model_distillation.ipynb</code></a>
<nav class="nav_RfLT" aria-label="Thai LLM tutorial series navigation"><p class="heading_XRWm">Thai LLM series<span class="progress_f8e8">Part 7 of 10</span></p><ol class="list_U31a"><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-01-continue-pretraining"><span class="number_u3BE" aria-hidden="true">1</span><span class="title_BPvL">Continue Pretraining</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-02-sft-lora"><span class="number_u3BE" aria-hidden="true">2</span><span class="title_BPvL">SFT and LoRA</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo"><span class="number_u3BE" aria-hidden="true">3</span><span class="title_BPvL">RLHF and PPO</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-04-dpo"><span class="number_u3BE" aria-hidden="true">4</span><span class="title_BPvL">DPO: Direct Preference Optimization</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-05-grpo"><span class="number_u3BE" aria-hidden="true">5</span><span class="title_BPvL">GRPO</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-06-context-distillation"><span class="number_u3BE" aria-hidden="true">6</span><span class="title_BPvL">Context Distillation</span></a></li><li class="item_Y10l"><span class="chip_DDpP chipCurrent_BGpo" aria-current="step"><span class="number_u3BE" aria-hidden="true">7</span><span class="title_BPvL">Model Distillation</span><span class="srOnly_owtF">(you are here)</span></span></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-08-guardrails"><span class="number_u3BE" aria-hidden="true">8</span><span class="title_BPvL">Guardrails</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-09-benchmarking"><span class="number_u3BE" aria-hidden="true">9</span><span class="title_BPvL">Benchmarking</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-10-deployment"><span class="number_u3BE" aria-hidden="true">10</span><span class="title_BPvL">Deployment</span></a></li></ol></nav>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-the-problem">1. The Problem<a href="https://kobkrit.com/en/blog/llm-07-model-distillation#1-the-problem" class="hash-link" aria-label="Direct link to 1. The Problem" title="Direct link to 1. The Problem" translate="no">​</a></h2>
<p>Suppose you've walked the full six chapters and arrived at a Qwen3-1.7B that handles your organization's Thai workload satisfyingly well.
Then one day the infrastructure team asks the one question the model can't answer: <em>"What's the GPU bill per month?"</em></p>
<p>The 1.7B model eats nearly 3× the VRAM of the 0.6B and answers about 2.5× slower.
At 100 requests per second, that difference is not a detail — it's the number of extra cards you buy every month for the life of the system.</p>
<table><thead><tr><th>Option</th><th>Quality</th><th>Cost at serving time</th></tr></thead><tbody><tr><td>Deploy the 1.7B teacher directly</td><td>Best</td><td>~3× VRAM, ~2.5× slower, paid for the system's whole life</td></tr><tr><td>Deploy the 0.6B student directly</td><td>Clearly worse</td><td>Cheap and fast</td></tr><tr><td>SFT the student on gold answers</td><td>Somewhat better</td><td>Cheap and fast</td></tr><tr><td><strong>Model distillation</strong></td><td>Moves toward the teacher</td><td>Cheap and fast, <strong>exactly like the student</strong></td></tr></tbody></table>
<p>The question is what the last row knows that the SFT row doesn't — same training, same data, so where's the difference?</p>
<p>The answer is in the <strong>amount of information per token</strong>. A hard label is a one-hot vector:
it says "the answer is 3," full stop. But the teacher's distribution, softened by temperature, says
<em>"the answer is 3 — but 8 is somewhat plausible too, and 'cat' is complete nonsense."</em>
This ranking over <strong>all the wrong answers</strong> is what Hinton called <strong>dark knowledge</strong>.
It encodes the similarity structure of the world (the digit 3 is closer to 8 than to a cat)
and it vanishes entirely the moment you keep only the argmax.</p>
<p>At every token position the teacher has 151,936 numbers to offer (Qwen3's vocab size) — a hard label keeps exactly one.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-what-were-going-to-do">2. What We're Going to Do<a href="https://kobkrit.com/en/blog/llm-07-model-distillation#2-what-were-going-to-do" class="hash-link" aria-label="Direct link to 2. What We're Going to Do" title="Direct link to 2. What We're Going to Do" translate="no">​</a></h2>
<p>We take <strong>Qwen/Qwen3-1.7B</strong> as the teacher and <strong>Qwen/Qwen3-0.6B-Base</strong> as the student,
then distill at three levels that pass increasingly fine-grained information from the teacher:</p>
<ol>
<li class=""><strong>SeqKD</strong> — have the teacher generate answers, then SFT the student on them (a sample from the distribution)</li>
<li class=""><strong>Logit KD</strong> — have the student imitate the teacher's entire distribution, token position by token position (the distribution itself)</li>
<li class=""><strong>GKD</strong> (optional extra) — have the student sample its own answers and let the teacher score the distributions on-policy</li>
</ol>
<p>And the piece we cannot do without is the <strong>control row</strong>: SFT the student on the gold answers, same data, same number of steps.
Without that row, we have no way to tell whether the gains came from "the teacher's distribution" or from "just training more."</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>The core idea of this chapter</div><div class="admonitionContent_BuS1"><p>A hard label says "the answer is 3" — the teacher's distribution says "3, but 8 is almost right, and 'cat' is impossible."
<strong>The ranking over wrong answers is dark knowledge, and temperature is the dial that reveals it.</strong>
At T = 1 this knowledge is squeezed out of sight (the teacher is 0.97 confident); raise T and it becomes a signal you can actually train on.</p></div></div>
<p>This is not a laboratory curiosity — it's how most of the world's "small but capable" models are actually built.
The Qwen3-0.6B we've used all series long was itself trained with strong-to-weak distillation from the larger members of its own family.
In this chapter we're doing the same thing, at a scale free Colab can handle.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-the-equations">3. The Equations<a href="https://kobkrit.com/en/blog/llm-07-model-distillation#3-the-equations" class="hash-link" aria-label="Direct link to 3. The Equations" title="Direct link to 3. The Equations" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="31-hintons-kd-loss">3.1 Hinton's KD loss<a href="https://kobkrit.com/en/blog/llm-07-model-distillation#31-hintons-kd-loss" class="hash-link" aria-label="Direct link to 3.1 Hinton's KD loss" title="Direct link to 3.1 Hinton's KD loss" translate="no">​</a></h3>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi mathvariant="script">L</mi><mtext>KD</mtext></msub><mo>=</mo><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><mi>α</mi><mo stretchy="false">)</mo><mtext> </mtext><msub><mi mathvariant="script">L</mi><mtext>CE</mtext></msub><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">(</mo><mi>y</mi><mo separator="true">,</mo><mtext>&nbsp;softmax</mtext><mo stretchy="false">(</mo><msub><mi>z</mi><mi>S</mi></msub><mo stretchy="false">)</mo><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">)</mo><mo>+</mo><mi>α</mi><mtext> </mtext><msup><mi>T</mi><mn>2</mn></msup><mtext> </mtext><msub><mi mathvariant="double-struck">D</mi><mtext>KL</mtext></msub><mo fence="false" stretchy="true" minsize="1.8em" maxsize="1.8em">(</mo><mtext>softmax</mtext><mo stretchy="false">(</mo><msub><mi>z</mi><mi>T</mi></msub><mi mathvariant="normal">/</mi><mi>T</mi><mo stretchy="false">)</mo><mtext>&nbsp;</mtext><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">∥</mo><mtext>&nbsp;softmax</mtext><mo stretchy="false">(</mo><msub><mi>z</mi><mi>S</mi></msub><mi mathvariant="normal">/</mi><mi>T</mi><mo stretchy="false">)</mo><mo fence="false" stretchy="true" minsize="1.8em" maxsize="1.8em">)</mo></mrow><annotation encoding="application/x-tex">\mathcal{L}_{\text{KD}} = (1-\alpha)\,\mathcal{L}_{\text{CE}}\big(y,\ \text{softmax}(z_S)\big) + \alpha\, T^2\,\mathbb{D}_{\text{KL}}\Big(\text{softmax}(z_T/T)\ \big\|\ \text{softmax}(z_S/T)\Big)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathcal">L</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">KD</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mopen">(</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.2em;vertical-align:-0.35em"></span><span class="mord mathnormal" style="margin-right:0.0037em">α</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathcal">L</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">CE</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord"><span class="delimsizing size1">(</span></span><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="mpunct">,</span><span class="mspace">&nbsp;</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord text"><span class="mord">softmax</span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.044em">z</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:-0.044em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0576em">S</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mord"><span class="delimsizing size1">)</span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.8em;vertical-align:-0.65em"></span><span class="mord mathnormal" style="margin-right:0.0037em">α</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1389em">T</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641em"><span style="top:-3.113em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathbb">D</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">KL</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord"><span class="delimsizing size2">(</span></span><span class="mord text"><span class="mord">softmax</span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.044em">z</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:-0.044em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.1389em">T</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord">/</span><span class="mord mathnormal" style="margin-right:0.1389em">T</span><span class="mclose">)</span><span class="mspace">&nbsp;</span><span class="mord"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.85em"><span style="top:-2.85em"><span class="pstrut" style="height:3.2em"></span><span style="width:0.556em;height:1.2em"><svg xmlns="http://www.w3.org/2000/svg" width="0.556em" height="1.2em" viewBox="0 0 556 1200"><path d="M145 15 v585 v0 v585 c2.667,10,9.667,15,21,15
c10,0,16.667,-5,20,-15 v-585 v0 v-585 c-2.667,-10,-9.667,-15,-21,-15
c-10,0,-16.667,5,-20,15z M188 15 H145 v585 v0 v585 h43z
M367 15 v585 v0 v585 c2.667,10,9.667,15,21,15
c10,0,16.667,-5,20,-15 v-585 v0 v-585 c-2.667,-10,-9.667,-15,-21,-15
c-10,0,-16.667,5,-20,15z M410 15 H367 v585 v0 v585 h43z"></path></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.35em"><span></span></span></span></span></span></span><span class="mspace">&nbsp;</span><span class="mord text"><span class="mord">softmax</span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.044em">z</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:-0.044em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0576em">S</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord">/</span><span class="mord mathnormal" style="margin-right:0.1389em">T</span><span class="mclose">)</span><span class="mord"><span class="delimsizing size2">)</span></span></span></span></span></span>
<ul>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>z</mi><mi>S</mi></msub><mo separator="true">,</mo><msub><mi>z</mi><mi>T</mi></msub></mrow><annotation encoding="application/x-tex">z_S, z_T</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.044em">z</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:-0.044em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0576em">S</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.044em">z</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:-0.044em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.1389em">T</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> = the student's and teacher's logits, at the same token position, on the same input</li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi></mrow><annotation encoding="application/x-tex">y</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0359em">y</span></span></span></span> = the gold answer (hard label) — the first term is ordinary cross-entropy, identical to SFT</li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>T</mi></mrow><annotation encoding="application/x-tex">T</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal" style="margin-right:0.1389em">T</span></span></span></span> = temperature, divided into the logits on <strong>both sides</strong> before the softmax</li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>α</mi></mrow><annotation encoding="application/x-tex">\alpha</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.0037em">α</span></span></span></span> = the weight of the soft term (we use 0.9 — listen mostly to the teacher, with the gold answer as a safety line)</li>
</ul>
<p>Notice the direction of the KL: the teacher comes first. This is <strong>forward KL</strong>, which forces the student to spread
its probability over everywhere the teacher puts weight. Hold on to that — equation 3.4 is about to turn it into "one point on a line."</p>
<p>Now, where does the <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>T</mi><mn>2</mn></msup></mrow><annotation encoding="application/x-tex">T^2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8141em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1389em">T</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span> sitting in front of the KL come from? Nearly every piece of KD code on the internet has this factor,
but very few explain why — and if you don't understand it, you will tune T wrongly without ever knowing.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="32-deriving-where-t2-comes-from--the-two-lines-that-separate-understood-from-copied">3.2 Deriving where <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>T</mi><mn>2</mn></msup></mrow><annotation encoding="application/x-tex">T^2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8141em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1389em">T</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span> comes from — the two lines that separate "understood" from "copied"<a href="https://kobkrit.com/en/blog/llm-07-model-distillation#32-deriving-where-t2-comes-from--the-two-lines-that-separate-understood-from-copied" class="hash-link" aria-label="Direct link to 32-deriving-where-t2-comes-from--the-two-lines-that-separate-understood-from-copied" title="Direct link to 32-deriving-where-t2-comes-from--the-two-lines-that-separate-understood-from-copied" translate="no">​</a></h3>
<p><strong>Line 1</strong> — the gradient of the soft term with respect to one student logit is the standard
softmax-CE gradient plus the chain rule through <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>z</mi><mi mathvariant="normal">/</mi><mi>T</mi></mrow><annotation encoding="application/x-tex">z/T</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.044em">z</span><span class="mord">/</span><span class="mord mathnormal" style="margin-right:0.1389em">T</span></span></span></span>, which emits one factor of <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mi>T</mi></mrow><annotation encoding="application/x-tex">1/T</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord">1/</span><span class="mord mathnormal" style="margin-right:0.1389em">T</span></span></span></span>:</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mfrac><mrow><mi mathvariant="normal">∂</mi><msub><mi mathvariant="script">L</mi><mtext>soft</mtext></msub></mrow><mrow><mi mathvariant="normal">∂</mi><msub><mi>z</mi><mrow><mi>S</mi><mo separator="true">,</mo><mi>i</mi></mrow></msub></mrow></mfrac><mo>=</mo><mfrac><mn>1</mn><mi>T</mi></mfrac><mo fence="false" stretchy="true" minsize="1.8em" maxsize="1.8em">(</mo><msubsup><mi>q</mi><mi>i</mi><mrow><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo></mrow></msubsup><mo>−</mo><msubsup><mi>p</mi><mi>i</mi><mrow><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo></mrow></msubsup><mo fence="false" stretchy="true" minsize="1.8em" maxsize="1.8em">)</mo><mo separator="true">,</mo><mspace width="2em"></mspace><msup><mi>q</mi><mrow><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo></mrow></msup><mo>=</mo><mtext>softmax</mtext><mo stretchy="false">(</mo><msub><mi>z</mi><mi>S</mi></msub><mi mathvariant="normal">/</mi><mi>T</mi><mo stretchy="false">)</mo><mo separator="true">,</mo><mspace width="1em"></mspace><msup><mi>p</mi><mrow><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo></mrow></msup><mo>=</mo><mtext>softmax</mtext><mo stretchy="false">(</mo><msub><mi>z</mi><mi>T</mi></msub><mi mathvariant="normal">/</mi><mi>T</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\frac{\partial \mathcal{L}_{\text{soft}}}{\partial z_{S,i}} = \frac{1}{T}\Big(q_i^{(T)} - p_i^{(T)}\Big),
\qquad q^{(T)} = \text{softmax}(z_S/T),\quad p^{(T)} = \text{softmax}(z_T/T)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:2.3435em;vertical-align:-0.9721em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3714em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord" style="margin-right:0.0556em">∂</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.044em">z</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:-0.044em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0576em">S</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">i</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord" style="margin-right:0.0556em">∂</span><span class="mord"><span class="mord mathcal">L</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">soft</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.9721em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.0074em;vertical-align:-0.686em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3214em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1389em">T</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mord"><span class="delimsizing size2">(</span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">q</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.0448em"><span style="top:-2.4231em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span><span style="top:-3.2198em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mopen mtight">(</span><span class="mord mathnormal mtight" style="margin-right:0.1389em">T</span><span class="mclose mtight">)</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2769em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.8em;vertical-align:-0.65em"></span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.0448em"><span style="top:-2.4231em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span><span style="top:-3.2198em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mopen mtight">(</span><span class="mord mathnormal mtight" style="margin-right:0.1389em">T</span><span class="mclose mtight">)</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2769em"><span></span></span></span></span></span></span><span class="mord"><span class="delimsizing size2">)</span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:2em"></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">q</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.938em"><span style="top:-3.113em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mopen mtight">(</span><span class="mord mathnormal mtight" style="margin-right:0.1389em">T</span><span class="mclose mtight">)</span></span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1.188em;vertical-align:-0.25em"></span><span class="mord text"><span class="mord">softmax</span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.044em">z</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:-0.044em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0576em">S</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord">/</span><span class="mord mathnormal" style="margin-right:0.1389em">T</span><span class="mclose">)</span><span class="mpunct">,</span><span class="mspace" style="margin-right:1em"></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.938em"><span style="top:-3.113em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mopen mtight">(</span><span class="mord mathnormal mtight" style="margin-right:0.1389em">T</span><span class="mclose mtight">)</span></span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord text"><span class="mord">softmax</span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.044em">z</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:-0.044em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.1389em">T</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord">/</span><span class="mord mathnormal" style="margin-right:0.1389em">T</span><span class="mclose">)</span></span></span></span></span>
<p><strong>Line 2</strong> — as <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>T</mi></mrow><annotation encoding="application/x-tex">T</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal" style="margin-right:0.1389em">T</span></span></span></span> grows, the softmax flattens toward uniform:
<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mtext>softmax</mtext><mo stretchy="false">(</mo><mi>z</mi><mi mathvariant="normal">/</mi><mi>T</mi><msub><mo stretchy="false">)</mo><mi>i</mi></msub><mo>≈</mo><mstyle scriptlevel="0" displaystyle="false"><mfrac><mn>1</mn><mi>K</mi></mfrac></mstyle><mo>+</mo><mstyle scriptlevel="0" displaystyle="false"><mfrac><mrow><msub><mi>z</mi><mi>i</mi></msub><mo>−</mo><mover accent="true"><mi>z</mi><mo>ˉ</mo></mover></mrow><mrow><mi>K</mi><mi>T</mi></mrow></mfrac></mstyle></mrow><annotation encoding="application/x-tex">\text{softmax}(z/T)_i \approx \tfrac{1}{K} + \tfrac{z_i - \bar z}{KT}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord text"><span class="mord">softmax</span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.044em">z</span><span class="mord">/</span><span class="mord mathnormal" style="margin-right:0.1389em">T</span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">≈</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1.1901em;vertical-align:-0.345em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8451em"><span style="top:-2.655em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0715em">K</span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.394em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.345em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.1634em;vertical-align:-0.345em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8184em"><span style="top:-2.655em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0715em">K</span><span class="mord mathnormal mtight" style="margin-right:0.1389em">T</span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.4101em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.044em">z</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3281em"><span style="top:-2.357em;margin-left:-0.044em;margin-right:0.0714em"><span class="pstrut" style="height:2.5em"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.143em"><span></span></span></span></span></span></span><span class="mbin mtight">−</span><span class="mord accent mtight"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.5678em"><span style="top:-2.7em"><span class="pstrut" style="height:2.7em"></span><span class="mord mathnormal mtight" style="margin-right:0.044em">z</span></span><span style="top:-2.7em"><span class="pstrut" style="height:2.7em"></span><span class="accent-body" style="left:-0.1944em"><span class="mord mtight">ˉ</span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.345em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span> (with <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>K</mi></mrow><annotation encoding="application/x-tex">K</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal" style="margin-right:0.0715em">K</span></span></span></span> = vocab size).
So the difference <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mi>q</mi><mi>i</mi><mrow><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo></mrow></msubsup><mo>−</mo><msubsup><mi>p</mi><mi>i</mi><mrow><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo></mrow></msubsup></mrow><annotation encoding="application/x-tex">q^{(T)}_i - p^{(T)}_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.3217em;vertical-align:-0.2769em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">q</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.0448em"><span style="top:-2.4231em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span><span style="top:-3.2198em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mopen mtight">(</span><span class="mord mathnormal mtight" style="margin-right:0.1389em">T</span><span class="mclose mtight">)</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2769em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.3217em;vertical-align:-0.2769em"></span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.0448em"><span style="top:-2.4231em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span><span style="top:-3.2198em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mopen mtight">(</span><span class="mord mathnormal mtight" style="margin-right:0.1389em">T</span><span class="mclose mtight">)</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2769em"><span></span></span></span></span></span></span></span></span></span> shrinks by another factor of <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mi>T</mi></mrow><annotation encoding="application/x-tex">1/T</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord">1/</span><span class="mord mathnormal" style="margin-right:0.1389em">T</span></span></span></span>:</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mfrac><mrow><mi mathvariant="normal">∂</mi><msub><mi mathvariant="script">L</mi><mtext>soft</mtext></msub></mrow><mrow><mi mathvariant="normal">∂</mi><msub><mi>z</mi><mrow><mi>S</mi><mo separator="true">,</mo><mi>i</mi></mrow></msub></mrow></mfrac><mo>≈</mo><mfrac><mrow><mo stretchy="false">(</mo><msub><mi>z</mi><mrow><mi>S</mi><mo separator="true">,</mo><mi>i</mi></mrow></msub><mo>−</mo><msub><mover accent="true"><mi>z</mi><mo>ˉ</mo></mover><mi>S</mi></msub><mo stretchy="false">)</mo><mo>−</mo><mo stretchy="false">(</mo><msub><mi>z</mi><mrow><mi>T</mi><mo separator="true">,</mo><mi>i</mi></mrow></msub><mo>−</mo><msub><mover accent="true"><mi>z</mi><mo>ˉ</mo></mover><mi>T</mi></msub><mo stretchy="false">)</mo></mrow><mrow><mi>K</mi><mtext> </mtext><msup><mi>T</mi><mn>2</mn></msup></mrow></mfrac><mtext>  </mtext><mo>∝</mo><mtext>  </mtext><mfrac><mn>1</mn><msup><mi>T</mi><mn>2</mn></msup></mfrac></mrow><annotation encoding="application/x-tex">\frac{\partial \mathcal{L}_{\text{soft}}}{\partial z_{S,i}} \approx \frac{(z_{S,i} - \bar z_S) - (z_{T,i} - \bar z_T)}{K\,T^2} \;\propto\; \frac{1}{T^2}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:2.3435em;vertical-align:-0.9721em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3714em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord" style="margin-right:0.0556em">∂</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.044em">z</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:-0.044em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0576em">S</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">i</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord" style="margin-right:0.0556em">∂</span><span class="mord"><span class="mord mathcal">L</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">soft</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.9721em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">≈</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.113em;vertical-align:-0.686em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.427em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0715em">K</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1389em">T</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7401em"><span style="top:-2.989em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.044em">z</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:-0.044em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0576em">S</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">i</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.5678em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal" style="margin-right:0.044em">z</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1944em"><span class="mord">ˉ</span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:-0.044em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0576em">S</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.044em">z</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:-0.044em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.1389em">T</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">i</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.5678em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal" style="margin-right:0.044em">z</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1944em"><span class="mord">ˉ</span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:-0.044em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.1389em">T</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">∝</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.0074em;vertical-align:-0.686em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3214em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.1389em">T</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7401em"><span style="top:-2.989em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span>
<p>The soft term's gradient scales as <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mi mathvariant="normal">/</mi><msup><mi>T</mi><mn>2</mn></msup></mrow><annotation encoding="application/x-tex">1/T^2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0641em;vertical-align:-0.25em"></span><span class="mord">1/</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1389em">T</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span>, while the hard CE term doesn't depend on <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>T</mi></mrow><annotation encoding="application/x-tex">T</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal" style="margin-right:0.1389em">T</span></span></span></span> at all.
<strong>Without multiplying the <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>T</mi><mn>2</mn></msup></mrow><annotation encoding="application/x-tex">T^2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8141em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1389em">T</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span> back in, moving T from 1 to 4 secretly divides the soft term's learning rate by ~16.</strong>
You'd conclude "high T doesn't work," when in reality you just quietly switched off your own soft loss.
Multiplying by <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>T</mi><mn>2</mn></msup></mrow><annotation encoding="application/x-tex">T^2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8141em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1389em">T</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span> makes the gradient scale nearly independent of T — so the <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>α</mi></mrow><annotation encoding="application/x-tex">\alpha</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.0037em">α</span></span></span></span> you tuned keeps the same meaning at every T.</p>
<p>A bonus from line 2: in the same limit, the soft loss reduces to matching mean-centered logits in an MSE-like sense —
KD is "soft logit regression" that weights the head of the distribution more than the tail.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="33-seqkd--the-cheap-baseline-kim--rush-2016">3.3 SeqKD — the cheap baseline (Kim &amp; Rush, 2016)<a href="https://kobkrit.com/en/blog/llm-07-model-distillation#33-seqkd--the-cheap-baseline-kim--rush-2016" class="hash-link" aria-label="Direct link to 3.3 SeqKD — the cheap baseline (Kim &amp; Rush, 2016)" title="Direct link to 3.3 SeqKD — the cheap baseline (Kim &amp; Rush, 2016)" translate="no">​</a></h3>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi mathvariant="script">L</mi><mtext>SeqKD</mtext></msub><mo>=</mo><mo>−</mo><mtext> </mtext><msub><mi mathvariant="double-struck">E</mi><mrow><mover accent="true"><mi>y</mi><mo>^</mo></mover><mo>∼</mo><msub><mi>π</mi><mi>T</mi></msub></mrow></msub><mrow><mo fence="true">[</mo><munder><mo>∑</mo><mi>t</mi></munder><mi>log</mi><mo>⁡</mo><msub><mi>π</mi><mi>S</mi></msub><mo stretchy="false">(</mo><msub><mover accent="true"><mi>y</mi><mo>^</mo></mover><mi>t</mi></msub><mo>∣</mo><mi>x</mi><mo separator="true">,</mo><msub><mover accent="true"><mi>y</mi><mo>^</mo></mover><mrow><mo>&lt;</mo><mi>t</mi></mrow></msub><mo stretchy="false">)</mo><mo fence="true">]</mo></mrow></mrow><annotation encoding="application/x-tex">\mathcal{L}_{\text{SeqKD}} = -\,\mathbb{E}_{\hat y \sim \pi_T}\left[\sum_t \log \pi_S(\hat y_t \mid x, \hat y_{&lt;t})\right]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9694em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathcal">L</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">SeqKD</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:3em;vertical-align:-1.25em"></span><span class="mord">−</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathbb">E</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord accent mtight"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-2.7em"><span class="pstrut" style="height:2.7em"></span><span class="mord mathnormal mtight" style="margin-right:0.0359em">y</span></span><span style="top:-2.7em"><span class="pstrut" style="height:2.7em"></span><span class="accent-body" style="left:-0.1944em"><span class="mord mtight">^</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em"><span></span></span></span></span></span><span class="mrel mtight">∼</span><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em"><span style="top:-2.3567em;margin-left:-0.0359em;margin-right:0.0714em"><span class="pstrut" style="height:2.5em"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mathnormal mtight" style="margin-right:0.1389em">T</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1433em"><span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="minner"><span class="mopen delimcenter" style="top:0em"><span class="delimsizing size4">[</span></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.05em"><span style="top:-1.9em;margin-left:0em"><span class="pstrut" style="height:3.05em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span><span style="top:-3.05em"><span class="pstrut" style="height:3.05em"></span><span><span class="mop op-symbol large-op">∑</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.25em"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mop">lo<span style="margin-right:0.0139em">g</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0576em">S</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal" style="margin-right:0.0359em">y</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1944em"><span class="mord">^</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em"><span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">∣</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal" style="margin-right:0.0359em">y</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1944em"><span class="mord">^</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em"><span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mrel mtight">&lt;</span><span class="mord mathnormal mtight">t</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1774em"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mclose delimcenter" style="top:0em"><span class="delimsizing size4">]</span></span></span></span></span></span></span>
<p>Look familiar? This is <strong>plain SFT on answers the teacher generated</strong> — nothing more.
Instead of sending the whole distribution, the teacher sends "one sample" drawn from its own distribution.</p>
<p>An advantage that often goes unnoticed: SeqKD <strong>doesn't care whether the tokenizers match</strong>, because it sends text, not logits.
Many open-source models advertised as "distilled from GPT-4" are in fact pure SeqKD —
collect the teacher's answers through an API, then SFT. That's why it's the baseline we must measure before reaching for anything more expensive.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="34-gkd-and-generalized-jsd--the-line-connecting-this-chapter-to-chapter-6">3.4 GKD and generalized JSD — the line connecting this chapter to chapter 6<a href="https://kobkrit.com/en/blog/llm-07-model-distillation#34-gkd-and-generalized-jsd--the-line-connecting-this-chapter-to-chapter-6" class="hash-link" aria-label="Direct link to 3.4 GKD and generalized JSD — the line connecting this chapter to chapter 6" title="Direct link to 3.4 GKD and generalized JSD — the line connecting this chapter to chapter 6" translate="no">​</a></h3>
<p>Logit KD per equation 3.1 has a structural weakness: the student learns on sentences <em>someone else</em> wrote (teacher forcing),
but at serving time it must generate continuations of <em>its own answers</em> — the accumulating mismatch is called exposure bias.
<strong>GKD</strong> (Agarwal et al., 2023) fixes this by letting the student sample its own answers and having the teacher score those tokens,
while also generalizing the distance between distributions to:</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msubsup><mi mathvariant="double-struck">D</mi><mtext>JSD</mtext><mrow><mo stretchy="false">(</mo><mi>β</mi><mo stretchy="false">)</mo></mrow></msubsup><mo stretchy="false">(</mo><mi>P</mi><mtext> </mtext><mi mathvariant="normal">∥</mi><mtext> </mtext><mi>Q</mi><mo stretchy="false">)</mo><mo>=</mo><mi>β</mi><mtext> </mtext><msub><mi mathvariant="double-struck">D</mi><mtext>KL</mtext></msub><mo stretchy="false">(</mo><mi>P</mi><mtext> </mtext><mi mathvariant="normal">∥</mi><mtext> </mtext><mi>M</mi><mo stretchy="false">)</mo><mo>+</mo><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><mi>β</mi><mo stretchy="false">)</mo><mtext> </mtext><msub><mi mathvariant="double-struck">D</mi><mtext>KL</mtext></msub><mo stretchy="false">(</mo><mi>Q</mi><mtext> </mtext><mi mathvariant="normal">∥</mi><mtext> </mtext><mi>M</mi><mo stretchy="false">)</mo><mo separator="true">,</mo><mspace width="2em"></mspace><mi>M</mi><mo>=</mo><mi>β</mi><mi>P</mi><mo>+</mo><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><mi>β</mi><mo stretchy="false">)</mo><mi>Q</mi></mrow><annotation encoding="application/x-tex">\mathbb{D}^{(\beta)}_{\text{JSD}}(P\,\|\,Q) = \beta\,\mathbb{D}_{\text{KL}}(P\,\|\,M) + (1-\beta)\,\mathbb{D}_{\text{KL}}(Q\,\|\,M),
\qquad M = \beta P + (1-\beta) Q</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.3383em;vertical-align:-0.2935em"></span><span class="mord"><span class="mord mathbb">D</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.0448em"><span style="top:-2.4065em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">JSD</span></span></span></span></span><span style="top:-3.2198em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mopen mtight">(</span><span class="mord mathnormal mtight" style="margin-right:0.0528em">β</span><span class="mclose mtight">)</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2935em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.1389em">P</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord">∥</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal">Q</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathbb">D</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">KL</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.1389em">P</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord">∥</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.109em">M</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mopen">(</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathbb">D</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">KL</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">Q</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord">∥</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.109em">M</span><span class="mclose">)</span><span class="mpunct">,</span><span class="mspace" style="margin-right:2em"></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.109em">M</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mord mathnormal" style="margin-right:0.1389em">P</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mopen">(</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mclose">)</span><span class="mord mathnormal">Q</span></span></span></span></span>
<p>with <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>P</mi></mrow><annotation encoding="application/x-tex">P</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal" style="margin-right:0.1389em">P</span></span></span></span> = teacher, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>Q</mi></mrow><annotation encoding="application/x-tex">Q</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8778em;vertical-align:-0.1944em"></span><span class="mord mathnormal">Q</span></span></span></span> = student. The value <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi></mrow><annotation encoding="application/x-tex">\beta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span></span></span></span> sweeps from one pole to the other:</p>
<ul>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi><mo>→</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">\beta \to 0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0</span></span></span></span> → <strong>forward KL</strong> <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="double-struck">D</mi><mtext>KL</mtext></msub><mo stretchy="false">(</mo><mi>P</mi><mi mathvariant="normal">∥</mi><mi>Q</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\mathbb{D}_{\text{KL}}(P\|Q)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathbb">D</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">KL</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.1389em">P</span><span class="mord">∥</span><span class="mord mathnormal">Q</span><span class="mclose">)</span></span></span></span> — mass-covering: the student must spread over every mode of the teacher</li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi><mo>→</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">\beta \to 1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">1</span></span></span></span> → <strong>reverse KL</strong> <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="double-struck">D</mi><mtext>KL</mtext></msub><mo stretchy="false">(</mo><mi>Q</mi><mi mathvariant="normal">∥</mi><mi>P</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\mathbb{D}_{\text{KL}}(Q\|P)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathbb">D</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">KL</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">Q</span><span class="mord">∥</span><span class="mord mathnormal" style="margin-right:0.1389em">P</span><span class="mclose">)</span></span></span></span> — mode-seeking: the student commits to the modes it can actually handle</li>
</ul>
<p>To say it as plainly as possible: <strong>the reverse KL that chapter 6 chose isn't an oddity from another world — it's the point <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi><mo>=</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">\beta = 1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">1</span></span></span></span>
on this very line, and Hinton's classic KD is the point <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">\beta = 0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0528em">β</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0</span></span></span></span>.</strong> The two chapters are members
of the same family, differing only in "who has to move toward whom" — a student much smaller than its teacher usually
benefits from the mode-seeking side, because it never had the capacity to cover every one of the teacher's modes anyway.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-seeing-the-equations">4. Seeing the Equations<a href="https://kobkrit.com/en/blog/llm-07-model-distillation#4-seeing-the-equations" class="hash-link" aria-label="Direct link to 4. Seeing the Equations" title="Direct link to 4. Seeing the Equations" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="temperature-reveals-dark-knowledge">Temperature reveals dark knowledge<a href="https://kobkrit.com/en/blog/llm-07-model-distillation#temperature-reveals-dark-knowledge" class="hash-link" aria-label="Direct link to Temperature reveals dark knowledge" title="Direct link to Temperature reveals dark knowledge" translate="no">​</a></h3>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-07-model-distillation/temperature-softens.light.svg" alt="Bar chart comparing the softmax of the same logits at four temperatures, with entropy per T, showing plausible wrong answers rising into view as T increases while nonsense tokens stay on the floor" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-07-model-distillation/temperature-softens.dark.svg" alt="Bar chart comparing the softmax of the same logits at four temperatures, with entropy per T, showing plausible wrong answers rising into view as T increases while nonsense tokens stay on the floor" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 7.1</span>A real 10-slot logit vector from the teacher after the context '7 × 8 = ', softmaxed at T = 1, 2, 4, 8 — the right answer ('56') stays ranked first, but the ranking over the wrong answers (54 ≻ 48 ≻ 63 ≻ … ≻ cat) only becomes visible once T is raised</p><div class="captionFooter_w00v"></div></figcaption></figure>
<p>The point to read off this figure: temperature <strong>adds no information whatsoever</strong> — the logits are the exact same set.
It only changes how much of the information already there can be seen. At T = 1, the best wrong answer has probability
just 0.015 — the gradient flowing through it is essentially zero. At T = 8, the whole ranking becomes
a signal the student can genuinely learn from, while "cat" and "!" stay on the floor where they belong.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-missing-t2-factor-visible-in-a-single-plot">The missing <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>T</mi><mn>2</mn></msup></mrow><annotation encoding="application/x-tex">T^2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8141em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1389em">T</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span> factor, visible in a single plot<a href="https://kobkrit.com/en/blog/llm-07-model-distillation#the-missing-t2-factor-visible-in-a-single-plot" class="hash-link" aria-label="Direct link to the-missing-t2-factor-visible-in-a-single-plot" title="Direct link to the-missing-t2-factor-visible-in-a-single-plot" translate="no">​</a></h3>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-07-model-distillation/t2-gradient.light.svg" alt="Log-scale plot of gradient magnitude against temperature, showing the line without the T squared factor falling as one over T squared, the line with T squared multiplied back in staying flat, and a one-over-T-squared reference line" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-07-model-distillation/t2-gradient.dark.svg" alt="Log-scale plot of gradient magnitude against temperature, showing the line without the T squared factor falling as one over T squared, the line with T squared multiplied back in staying flat, and a one-over-T-squared reference line" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 7.2</span>Magnitude of the soft-loss gradient with respect to the student's logits, computed directly from the formula (q−p)/T on the same pair of logit vectors — without T², the gradient decays as 1/T² (red line); multiply T² back in and the scale stays flat across the whole range of T (green line)</p><div class="captionFooter_w00v"></div></figcaption></figure>
<p>This is equation 3.2 in visible form: the red line is what happens if you forget <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>T</mi><mn>2</mn></msup></mrow><annotation encoding="application/x-tex">T^2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8141em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1389em">T</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span> —
when you sweep for the best T, you are unintentionally sweeping the soft loss's learning rate at the same time.
Your whole results table becomes unreadable, because two variables are tangled together. The green line is the only reason we can tune T cleanly.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-differently-the-teacher-and-student-see-the-same-position">How differently the teacher and student see the same position<a href="https://kobkrit.com/en/blog/llm-07-model-distillation#how-differently-the-teacher-and-student-see-the-same-position" class="hash-link" aria-label="Direct link to How differently the teacher and student see the same position" title="Direct link to How differently the teacher and student see the same position" translate="no">​</a></h3>
<p>Have a look at the real thing: the teacher's top-5 versus the student's at a Thai token position from the notebook
(the "before" view is the student, the "after" view is the teacher — the gap between the two views is exactly what logit KD tries to close):</p>
<div class="root_BpHs"><div class="header_f9Zn"><fieldset class="control_Br1p" style="border:0;padding:0;margin:0"><legend class="segmentedLegend_oU13">View</legend><div class="segmented_Klsm"><span class="segment_AC25"><input type="radio" id="_R_9c6ldeh_-before" name="llmcourse-tpi-view-_R_9c6ldeh_" value="before"><label class="segmentLabel_wkEZ" for="_R_9c6ldeh_-before">Before</label></span><span class="segment_AC25"><input type="radio" id="_R_9c6ldeh_-after" name="llmcourse-tpi-view-_R_9c6ldeh_" value="after"><label class="segmentLabel_wkEZ" for="_R_9c6ldeh_-after">After</label></span><span class="segment_AC25"><input type="radio" id="_R_9c6ldeh_-delta" name="llmcourse-tpi-view-_R_9c6ldeh_" checked="" value="delta"><label class="segmentLabel_wkEZ" for="_R_9c6ldeh_-delta">Change</label></span></div></fieldset><div class="scale_G4BA" aria-hidden="true"><span>worse</span><span class="scaleBar_kmc1"></span><span>better</span></div></div><p class="prompt_Yp9D"><span class="promptLabel_w2S2">Prompt</span>ทักทายเป็นภาษาไทย</p><p class="text_gk_3" lang="th"><span class="token_jolA" style="background-color:color-mix(in srgb, var(--ifm-color-success) 7.2%, transparent)" tabindex="0" role="button" aria-label="Token สว: log probability -0.42 before, -0.11 after.">สวั</span><span class="token_jolA" style="background-color:color-mix(in srgb, var(--ifm-color-success) 60.0%, transparent)" tabindex="0" role="button" aria-label="Token ัสด: log probability -2.91 before, -0.34 after.">สดี</span><span class="token_jolA" style="background-color:color-mix(in srgb, var(--ifm-color-success) 36.2%, transparent)" tabindex="0" role="button" aria-label="Token ีคร: log probability -1.84 before, -0.29 after.">ครั</span><span class="token_jolA" style="background-color:color-mix(in srgb, var(--ifm-color-success) 10.0%, transparent)" tabindex="0" role="button" aria-label="Token ับ: log probability -0.55 before, -0.12 after.">บ</span><span class="token_jolA" style="background-color:color-mix(in srgb, var(--ifm-color-success) 55.3%, transparent)" tabindex="0" role="button" aria-label="Token  ผม: log probability -3.42 before, -1.05 after."> ผม</span><span class="token_jolA" style="background-color:color-mix(in srgb, var(--ifm-color-success) 39.4%, transparent)" tabindex="0" role="button" aria-label="Token ชื่อ: log probability -2.11 before, -0.42 after.">ชื่อ</span><span class="token_jolA" style="background-color:color-mix(in srgb, var(--ifm-color-success) 70.0%, transparent)" tabindex="0" role="button" aria-label="Token โมเดล: log probability -4.02 before, -0.88 after.">โมเดล</span><span class="token_jolA" style="background-color:color-mix(in srgb, var(--ifm-color-success) 25.7%, transparent)" tabindex="0" role="button" aria-label="Token ภาษา: log probability -1.35 before, -0.25 after.">ภาษา</span><span class="token_jolA" style="background-color:color-mix(in srgb, var(--ifm-color-success) 5.4%, transparent)" tabindex="0" role="button" aria-label="Token ไทย: log probability -0.31 before, -0.08 after.">ไทย</span></p><div class="detail_JFJx" role="status" aria-live="polite"><span class="detailIdle_GJWI">Hover or focus a token to see its probability and the top-5 alternatives the model considered.</span></div><div class="readouts__tjv"><div class="readout_D9ns"><span class="readoutLabel_EsIV">Mean logprob before</span><span class="readoutValue_VS6z">-1.881</span><span class="readoutSub_DoT9">perplexity 6.56</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">Mean logprob after</span><span class="readoutValue_VS6z">-0.393</span><span class="readoutSub_DoT9">perplexity 1.48</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">Tokens improved</span><span class="readoutValue_VS6z">9 / 9</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">Rendered clusters</span><span class="readoutValue_VS6z">9</span><span class="readoutSub_DoT9">from 9 tokens</span></div></div><p class="status_mfC7">Showing the built-in sample.</p></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-setting-up-the-environment">5. Setting Up the Environment<a href="https://kobkrit.com/en/blog/llm-07-model-distillation#5-setting-up-the-environment" class="hash-link" aria-label="Direct link to 5. Setting Up the Environment" title="Direct link to 5. Setting Up the Environment" translate="no">​</a></h2>
<p>Open Colab and pick <strong>Runtime → Change runtime type → T4 GPU</strong> (the free tier is enough, but this chapter is unusually tight on VRAM
because the teacher and student must sit on the card at the same time).</p>
<div class="theme-admonition theme-admonition-danger admonition_xJq3 alert alert--danger"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"></path></svg></span>The series-wide warning worth re-reading every chapter</div><div class="admonitionContent_BuS1"><p>The Colab T4 is Turing architecture (SM 7.5), which <strong>does not support bfloat16</strong> and <strong>does not support FlashAttention-2</strong>.</p><p>But the Qwen3 family's <code>config.json</code> declares <code>torch_dtype: bfloat16</code>.
So <code>torch_dtype="auto"</code> is <strong>a trap</strong> — your code will crash or run bizarrely slowly without telling you why.</p><div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">torch_dtype</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">float16      </span><span class="token comment" style="color:#999988;font-style:italic"># not bfloat16</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">attn_implementation</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"sdpa"</span><span class="token plain">     </span><span class="token comment" style="color:#999988;font-style:italic"># not flash_attention_2</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">fp16</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token plain">                      </span><span class="token comment" style="color:#999988;font-style:italic"># in TrainingArguments (not bf16=True)</span><br></span></code></pre></div></div></div></div>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">cap </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_device_capability</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"compute capability:"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cap</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                    </span><span class="token comment" style="color:#999988;font-style:italic"># T4 = (7, 5)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"native bf16:"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cap</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&gt;=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                   </span><span class="token comment" style="color:#999988;font-style:italic"># T4 -&gt; False</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"torch says   :"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">is_bf16_supported</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># T4 -&gt; True (counts emulation!)</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span><code>is_bf16_supported()</code> lies on a T4</div><div class="admonitionContent_BuS1"><p>Recent torch returns <code>True</code> on a T4 because it counts <strong>emulation</strong> as support — which is far slower than fp16.
Gate on <strong>compute capability ≥ 8.0</strong> (Ampere and up) instead. This was a real bug, caught only by running the notebook on Colab.</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="loading-two-models-at-once--the-tightest-vram-budget-in-the-series">Loading two models at once — the tightest VRAM budget in the series<a href="https://kobkrit.com/en/blog/llm-07-model-distillation#loading-two-models-at-once--the-tightest-vram-budget-in-the-series" class="hash-link" aria-label="Direct link to Loading two models at once — the tightest VRAM budget in the series" title="Direct link to Loading two models at once — the tightest VRAM budget in the series" translate="no">​</a></h3>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> torch</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> transformers </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> AutoModelForCausalLM</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> AutoTokenizer</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">teacher </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> AutoModelForCausalLM</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">from_pretrained</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"Qwen/Qwen3-1.7B"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">                </span><span class="token comment" style="color:#999988;font-style:italic"># teacher: instruct, ~3.4 GB (fp16)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    torch_dtype</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">float16</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    attn_implementation</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"sdpa"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">eval</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                       </span><span class="token comment" style="color:#999988;font-style:italic"># always .eval() — the teacher learns nothing</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">student </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> AutoModelForCausalLM</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">from_pretrained</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"Qwen/Qwen3-0.6B-Base"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">           </span><span class="token comment" style="color:#999988;font-style:italic"># student: base, ~1.2 GB (fp16)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    torch_dtype</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">float16</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    attn_implementation</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"sdpa"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>Two sets of weights totaling ~4.6 GB sounds comfortable, but during training you must budget for the activations of <em>both models</em>,
plus the LoRA optimizer, plus temporary logits — so on a 16 GB T4 there's room for a batch size of only 2,
with gradient accumulation making up the difference. That's the price of having the teacher sitting on the card with you.
(Section 6 shows how to "kick the teacher off the card" by precomputing logits offline.)</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-assert-cell-that-must-come-before-everything-else">The assert cell that must come before everything else<a href="https://kobkrit.com/en/blog/llm-07-model-distillation#the-assert-cell-that-must-come-before-everything-else" class="hash-link" aria-label="Direct link to The assert cell that must come before everything else" title="Direct link to The assert cell that must come before everything else" translate="no">​</a></h3>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">tok_t </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> AutoTokenizer</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">from_pretrained</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Qwen/Qwen3-1.7B"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">tok   </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> AutoTokenizer</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">from_pretrained</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Qwen/Qwen3-0.6B-Base"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">assert</span><span class="token plain"> teacher</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">config</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vocab_size </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> student</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">config</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vocab_size </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">151_936</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">assert</span><span class="token plain"> tok_t</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_vocab</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> tok</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_vocab</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"vocab matches slot for slot — logit KD is possible"</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>Why mismatched tokenizers make logit KD impossible (not merely "hard")</div><div class="admonitionContent_BuS1"><p>The KL in equation 3.1 compares two distributions <strong>dimension by dimension</strong>: dimension <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>i</mi></mrow><annotation encoding="application/x-tex">i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6595em"></span><span class="mord mathnormal">i</span></span></span></span> of the teacher must mean
the same token as dimension <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>i</mi></mrow><annotation encoding="application/x-tex">i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6595em"></span><span class="mord mathnormal">i</span></span></span></span> of the student. If the vocabs differ, you're comparing the probability of "Bangkok"
against the probability of whatever other token happens to share the slot number — the numbers will flow out beautifully and mean absolutely nothing.</p><p>Worse still, the same sentence gets chopped into different token sequences, so position <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi></mrow><annotation encoding="application/x-tex">t</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6151em"></span><span class="mord mathnormal">t</span></span></span></span> of the teacher and the student
point at different places in the text — they can't even be compared along the time axis.</p><p>The whole Qwen3 family shares one tokenizer, so we're safe. But if your teacher is GPT-4 or Typhoon,
whose vocab doesn't match your student, the only route left is SeqKD (equation 3.3), which sends text, not logits.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-preparing-the-data">6. Preparing the Data<a href="https://kobkrit.com/en/blog/llm-07-model-distillation#6-preparing-the-data" class="hash-link" aria-label="Direct link to 6. Preparing the Data" title="Direct link to 6. Preparing the Data" translate="no">​</a></h2>
<p>We use 3,000 prompts from <strong><code>airesearch/wangchanx-seed-free-synthetic-instruct-thai-120k</code></strong>,
a Thai instruction dataset that ships with complete reference answers:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> datasets </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> load_dataset</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ds </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> load_dataset</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"airesearch/wangchanx-seed-free-synthetic-instruct-thai-120k"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                  split</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"train"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ds </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> ds</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">shuffle</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">seed</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">42</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">select</span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin">range</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">3000</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>From this single pool we build three ingredients:</p>
<p><strong>Ingredient 1 — the gold answers</strong>, used straight from the dataset. They serve both as the control row's (SFT) data
and as the sentences logit KD trains on — the two rows deliberately see <strong>character-for-character identical text</strong>,
so the only remaining difference is "does it have the teacher's distribution or not."</p>
<p><strong>Ingredient 2 — the teacher's answers</strong>, for SeqKD: have the teacher generate in batches of 16 prompts
(<code>max_new_tokens=192, do_sample=False</code>). Takes about 20–25 minutes, done once and saved to disk.</p>
<p><strong>Ingredient 3 — the teacher's top-64 logits</strong>, for logit KD: forward the teacher over the sentences of ingredient 1
and keep only the top 64 entries at each position.</p>
<p>Why top-64 — because storing the full vocab is genuinely impossible. Do the arithmetic:</p>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-07-model-distillation/logit-memory.light.svg" alt="Log-scale bar chart comparing the 622-megabyte tensor of full-vocabulary teacher logits against the 0.79-megabyte top-64 version" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-07-model-distillation/logit-memory.dark.svg" alt="Log-scale bar chart comparing the 622-megabyte tensor of full-vocabulary teacher logits against the 0.79-megabyte top-64 version" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 7.3</span>The memory math: one batch of the teacher's full-vocab logits (4 × 512 × 151,936 × fp16) is 622 MB — top-64 is 0.79 MB, 791× smaller. And stored offline for all 3,000 examples: 467 GB versus 0.59 GB</p><div class="captionFooter_w00v"></div></figcaption></figure>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">K </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">64</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token decorator annotation punctuation" style="color:#393A34">@torch</span><span class="token decorator annotation punctuation" style="color:#393A34">.</span><span class="token decorator annotation punctuation" style="color:#393A34">no_grad</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                       </span><span class="token comment" style="color:#999988;font-style:italic"># the most important line in this cell — see trap 3 in section 9</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">teacher_topk</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">input_ids</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> attention_mask</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    z </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> teacher</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">input_ids</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">input_ids</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                attention_mask</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">attention_mask</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">logits      </span><span class="token comment" style="color:#999988;font-style:italic"># [B, L, 151936]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    val</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> idx </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> z</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">topk</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">K</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> dim</span><span class="token operator" style="color:#393A34">=</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                           </span><span class="token comment" style="color:#999988;font-style:italic"># [B, L, 64]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> val</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">half</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cpu</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> idx</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cpu</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>A small detail that teaches a lot: the indices must be <strong>int32</strong>, because a vocab of 151,936
overshoots the ceiling of uint16 (65,535) by more than double — so the disk spent on indices is actually larger than the logit values themselves
(4 bytes versus 2). The whole set, 3,000 examples × 512 positions × 64 ranks, comes to ≈ <strong>590 MB</strong> on disk.</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>How much information does top-64 throw away</div><div class="admonitionContent_BuS1"><p>At T = 2 the teacher's probability mass is heavily concentrated in the head of the distribution. The notebook prints
the actual coverage for you (the total mass of the top-64 after softmax at T = 2 — typically above 99%).
What we discard is the long tail of 151,872 tokens, each carrying a minuscule probability,
in exchange for the whole file shrinking 791× — a measured approximation, not a guess.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-the-main-code">7. The Main Code<a href="https://kobkrit.com/en/blog/llm-07-model-distillation#7-the-main-code" class="hash-link" aria-label="Direct link to 7. The Main Code" title="Direct link to 7. The Main Code" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="71-lora-on-the-student--the-same-fp16-trap-from-chapter-1">7.1 LoRA on the student + the same fp16 trap from chapter 1<a href="https://kobkrit.com/en/blog/llm-07-model-distillation#71-lora-on-the-student--the-same-fp16-trap-from-chapter-1" class="hash-link" aria-label="Direct link to 7.1 LoRA on the student + the same fp16 trap from chapter 1" title="Direct link to 7.1 LoRA on the student + the same fp16 trap from chapter 1" translate="no">​</a></h3>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> peft </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> LoraConfig</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> get_peft_model</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">student </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> get_peft_model</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">student</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> LoraConfig</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    r</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">16</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> lora_alpha</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">32</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> lora_dropout</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.05</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> task_type</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"CAUSAL_LM"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    target_modules</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"q_proj"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"k_proj"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"v_proj"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"o_proj"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token string" style="color:#e3116c">"gate_proj"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"up_proj"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"down_proj"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> p </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> student</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">parameters</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># the fp16 box from chapter 1, LoRA edition:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> p</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">requires_grad</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">               </span><span class="token comment" style="color:#999988;font-style:italic"># cast only the adapter parameters to fp32</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        p</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">data </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> p</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">data</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">       </span><span class="token comment" style="color:#999988;font-style:italic"># or you'll hit "Attempting to unscale FP16 gradients."</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="72-writing-the-kd-loss-by-hand--equation-31-line-by-line">7.2 Writing the KD loss by hand — equation 3.1 line by line<a href="https://kobkrit.com/en/blog/llm-07-model-distillation#72-writing-the-kd-loss-by-hand--equation-31-line-by-line" class="hash-link" aria-label="Direct link to 7.2 Writing the KD loss by hand — equation 3.1 line by line" title="Direct link to 7.2 Writing the KD loss by hand — equation 3.1 line by line" translate="no">​</a></h3>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">nn</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">functional </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> F</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">kd_loss</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">z_s</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> t_val</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> t_idx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> labels</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> T</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">2.0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> alpha</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.9</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token triple-quoted-string string" style="color:#e3116c">"""z_s: [B, L, V] student logits — t_val/t_idx: [B, L, 64] teacher top-64 (read from disk)"""</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    z_s</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> t_val</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> t_idx </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> z_s</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">:</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">:</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> t_val</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">:</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">:</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> t_idx</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">:</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">:</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    tgt </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> labels</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">:</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">:</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain">                          </span><span class="token comment" style="color:#999988;font-style:italic"># position t predicts the token at t+1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    mask </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> tgt</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ne</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">100</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                          </span><span class="token comment" style="color:#999988;font-style:italic"># keep prompt and padding out of the loss</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># ── hard term: cross-entropy against the gold answer, identical to SFT ──</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ce </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> F</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cross_entropy</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">z_s</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">flatten</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> tgt</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">flatten</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                         ignore_index</span><span class="token operator" style="color:#393A34">=</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">100</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># ── soft term: KL(teacher ‖ student) on the top-64 axis — divide by T on BOTH sides ──</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    p_t   </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> F</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">softmax</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">t_val</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> T</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> dim</span><span class="token operator" style="color:#393A34">=</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">              </span><span class="token comment" style="color:#999988;font-style:italic"># renormalize over 64 dims</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    log_q </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> F</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">log_softmax</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">z_s</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">gather</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> t_idx</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">long</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> T</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> dim</span><span class="token operator" style="color:#393A34">=</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    kl </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">p_t </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">p_t</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">clamp_min</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1e-9</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> log_q</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">sum</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># KL per position</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    kl </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">kl </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> mask</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">sum</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> mask</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">sum</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">clamp_min</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">          </span><span class="token comment" style="color:#999988;font-style:italic"># average over answer tokens only</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> alpha</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> ce </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> alpha </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">T </span><span class="token operator" style="color:#393A34">**</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> kl           </span><span class="token comment" style="color:#999988;font-style:italic"># ← the T² from equation 3.2</span><br></span></code></pre></div></div>
<p>The entire article is compressed into that one final line: <code>(1 - alpha) * ce</code> is the gold answer keeping the student from drifting;
<code>alpha * (T ** 2) * kl</code> is the teacher's dark knowledge, complete with the factor we just derived with our own hands.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="73-a-trainer-that-feeds-teacher-logits-from-disk">7.3 A Trainer that feeds teacher logits from disk<a href="https://kobkrit.com/en/blog/llm-07-model-distillation#73-a-trainer-that-feeds-teacher-logits-from-disk" class="hash-link" aria-label="Direct link to 7.3 A Trainer that feeds teacher logits from disk" title="Direct link to 7.3 A Trainer that feeds teacher logits from disk" translate="no">​</a></h3>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> transformers </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> TrainingArguments</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> Trainer</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">KDTrainer</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">Trainer</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">compute_loss</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> model</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> inputs</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> return_outputs</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">False</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">**</span><span class="token plain">kwargs</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        t_val </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> inputs</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">pop</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"teacher_val"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">     </span><span class="token comment" style="color:#999988;font-style:italic"># from disk — not from a teacher on the card</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        t_idx </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> inputs</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">pop</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"teacher_idx"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        out </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> model</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">input_ids</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">inputs</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"input_ids"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    attention_mask</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">inputs</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"attention_mask"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        loss </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> kd_loss</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">out</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">logits</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> t_val</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> t_idx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> inputs</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"labels"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">loss</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> out</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> return_outputs </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> loss</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">args </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> TrainingArguments</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    output_dir</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"kd-out"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    per_device_train_batch_size</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    gradient_accumulation_steps</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">8</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># effective batch = 16</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    num_train_epochs</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    learning_rate</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1e-4</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">                   </span><span class="token comment" style="color:#999988;font-style:italic"># LoRA on the student — tuning only the adapter</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    lr_scheduler_type</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"cosine"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    warmup_ratio</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.05</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    gradient_checkpointing</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    fp16</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">                            </span><span class="token comment" style="color:#999988;font-style:italic"># the T4 has no bf16</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    logging_steps</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    remove_unused_columns</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">False</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">          </span><span class="token comment" style="color:#999988;font-style:italic"># ← do not forget, or the Trainer silently</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                                         </span><span class="token comment" style="color:#999988;font-style:italic">#    throws away teacher_val/teacher_idx</span><br></span></code></pre></div></div>
<p><code>remove_unused_columns=False</code> is the line people miss most often in this chapter: by default, <code>Trainer</code>
drops any column the model's signature doesn't recognize — which includes our teacher logits.
The symptom is <code>KeyError: 'teacher_val'</code> at the first step. Fortunately it fails loudly, not silently.</p>
<p><strong>SeqKD</strong> needs nothing new at all — a plain <code>Trainer</code> on ingredient 2
(the teacher's answers), trained like ordinary SFT. And the <strong>control row</strong> is that same <code>Trainer</code>
on the gold answers. Total training time for the two main regimes (SeqKD + logit KD) is around <strong>~17 minutes</strong> on a T4
(8–9 minutes per row); the control row costs another ~8 minutes.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="74-optional-extra-feel-free-to-skip-the-whole-section-on-policy-gkd-with-trl">7.4 Optional extra (feel free to skip the whole section): on-policy GKD with TRL<a href="https://kobkrit.com/en/blog/llm-07-model-distillation#74-optional-extra-feel-free-to-skip-the-whole-section-on-policy-gkd-with-trl" class="hash-link" aria-label="Direct link to 7.4 Optional extra (feel free to skip the whole section): on-policy GKD with TRL" title="Direct link to 7.4 Optional extra (feel free to skip the whole section): on-policy GKD with TRL" translate="no">​</a></h3>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># Run only if you have time left — on-policy is several times slower than offline,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># because the student must generate during training and the teacher must sit on the card the whole time</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> trl </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> GKDConfig</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> GKDTrainer</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">cfg </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> GKDConfig</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    output_dir</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"gkd-out"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    beta</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.5</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">                             </span><span class="token comment" style="color:#999988;font-style:italic"># the midpoint of the JSD line in equation 3.4</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    lmbda</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.5</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">                            </span><span class="token comment" style="color:#999988;font-style:italic"># half of each batch uses answers the student sampled itself</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    max_new_tokens</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">128</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    per_device_train_batch_size</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    gradient_accumulation_steps</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">8</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    learning_rate</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1e-4</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    max_steps</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">60</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">                         </span><span class="token comment" style="color:#999988;font-style:italic"># just a taste (~25 min), not a real training run</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    fp16</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">trainer </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> GKDTrainer</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">model</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">student</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> teacher_model</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">teacher</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                     args</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">cfg</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> train_dataset</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">gkd_ds</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> processing_class</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">tok</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">trainer</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">train</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="8-results">8. Results<a href="https://kobkrit.com/en/blog/llm-07-model-distillation#8-results" class="hash-link" aria-label="Direct link to 8. Results" title="Direct link to 8. Results" translate="no">​</a></h2>
<p>The notebook measures four things and writes them to <code>results.json</code>:</p>
<ol>
<li class=""><strong>TH-INSTR</strong> from the KobEval-TH benchmark — Thai instruction-following scores, before/after, with a <strong>Wilson 95% CI</strong>,
measured for the teacher, the raw student, and all three student rows, on the same exam.</li>
<li class=""><strong>Gap closed</strong> — the single number that summarizes the whole chapter:</li>
</ol>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mtext>gap&nbsp;closed</mtext><mo>=</mo><mfrac><mrow><msub><mtext>student</mtext><mtext>after</mtext></msub><mo>−</mo><msub><mtext>student</mtext><mtext>before</mtext></msub></mrow><mrow><mtext>teacher</mtext><mo>−</mo><msub><mtext>student</mtext><mtext>before</mtext></msub></mrow></mfrac></mrow><annotation encoding="application/x-tex">\text{gap closed} = \frac{\text{student}_{\text{after}} - \text{student}_{\text{before}}}{\text{teacher} - \text{student}_{\text{before}}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord text"><span class="mord">gap&nbsp;closed</span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.2074em;vertical-align:-0.836em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3714em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord text"><span class="mord">teacher</span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord"><span class="mord text"><span class="mord">student</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">before</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord"><span class="mord text"><span class="mord">student</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">after</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord"><span class="mord text"><span class="mord">student</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">before</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.836em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span>
<p>Why not report raw scores — because "the score went up 4 points" means nothing without knowing how many points of gap
there were to close. This metric answers our actual question: <strong>what percentage of the student-teacher gap has been closed?</strong>
0% is no movement; 100% is catching the teacher exactly.
3. <strong>The student's tok/sec, before and after distillation</strong> — which should be <strong>exactly identical</strong>, because neither the architecture
nor a single parameter count changed. And that is the point of the whole chapter:
<strong>quality moves toward the teacher while latency doesn't move at all</strong> — if you want one sentence to take back to your team,
it's "you get (some of) the teacher's quality at the student's price."
4. <strong>A TH-SAFE spot check</strong> — the teacher transmits everything through its distribution, including <strong>its biases and bad habits</strong>.
So we measure the distilled student on a Thai safety question set and report honestly what it picked up along the way
(details in the limitations box at the end of the chapter).</p>
<p>The actual numbers in the next table are left as <code>?</code> — they must come from running your own notebook, not from this article.</p>
<div class="root_IS5b"><div class="picker_cO8e"><span class="pickerLabel_sE2x" id="llmcourse-bac-picker">Prompt</span><div class="pickerButtons_j7L1" role="tablist" aria-labelledby="llmcourse-bac-picker"><button type="button" role="tab" id="llmcourse-bac-tab-0" aria-selected="true" aria-controls="llmcourse-bac-panel-0" tabindex="0" class="pickerButton_gFO3 pickerButtonActive_xIUp">1</button><button type="button" role="tab" id="llmcourse-bac-tab-1" aria-selected="false" aria-controls="llmcourse-bac-panel-1" tabindex="-1" class="pickerButton_gFO3">2</button></div></div><blockquote class="prompt_O4Wp" lang="th"><span class="promptLabel_h2F6">Prompt</span>อธิบายว่าทำไมท้องฟ้าถึงเป็นสีฟ้า แบบสั้น ๆ</blockquote><div class="grid_h_9T" id="llmcourse-bac-panel-0" role="tabpanel" aria-labelledby="llmcourse-bac-tab-0" style="grid-template-columns:repeat(auto-fit, minmax(min(100%, 260px), 1fr))"><article class="card_S27b"><header class="cardHeader_w7wJ"><h4 class="cardTitle_NUQN">base</h4><div class="badges_pXcS"><span class="badge_wUaQ badgeBad_WFwi" title="Share of non-whitespace characters that are Thai script">Thai 18%</span><span class="badge_wUaQ">41 tokens</span></div></header><div class="output_VSGg" lang="th">The sky appears blue because of Rayleigh scattering. ท้องฟ้า is blue เพราะ light scatter ครับ. Shorter wavelengths scatter more than longer ones.</div></article><article class="card_S27b"><header class="cardHeader_w7wJ"><h4 class="cardTitle_NUQN">sft</h4><div class="badges_pXcS"><span class="badge_wUaQ badgeGood_MHH_" title="Share of non-whitespace characters that are Thai script">Thai 99%</span><span class="badge_wUaQ">78 tokens</span></div></header><div class="output_VSGg" lang="th">ท้องฟ้าเป็นสีฟ้าเพราะแสงอาทิตย์กระทบกับโมเลกุลของอากาศแล้วเกิดการกระเจิงแบบเรย์ลี ซึ่งแสงสีน้ำเงินที่มีความยาวคลื่นสั้นกว่าจะกระเจิงได้มากกว่าแสงสีแดง เราจึงมองเห็นท้องฟ้าเป็นสีฟ้าครับ</div></article></div><p class="status_mfC7">Showing the built-in sample.</p></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="9-comparison">9. Comparison<a href="https://kobkrit.com/en/blog/llm-07-model-distillation#9-comparison" class="hash-link" aria-label="Direct link to 9. Comparison" title="Direct link to 9. Comparison" translate="no">​</a></h2>
<table><thead><tr><th>Model</th><th>TH-INSTR (95% CI)</th><th>Gap closed</th><th>tok/sec</th><th>Training time</th></tr></thead><tbody><tr><td>Teacher Qwen3-1.7B</td><td>? (ceiling)</td><td>100% by definition</td><td>~2.5× slower than the student</td><td>—</td></tr><tr><td>Student 0.6B base</td><td>? (floor)</td><td>0% by definition</td><td>baseline</td><td>—</td></tr><tr><td>Student + SFT on gold answers (control)</td><td>?</td><td>?</td><td>same as base</td><td>~8 min</td></tr><tr><td>Student + SeqKD</td><td>?</td><td>?</td><td>same as base</td><td>~8 min</td></tr><tr><td>Student + logit KD (T=2, α=0.9)</td><td>?</td><td>?</td><td>same as base</td><td>~9 min</td></tr></tbody></table>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>The control row is the most important row in the table</div><div class="admonitionContent_BuS1"><p>Without the "SFT on gold answers" row, this table proves nothing at all, because every other row
receives both "extra training" and "information from the teacher" at once — to claim that dark knowledge actually matters,
you must be able to pull those two apart.</p><p>The control row trains on <strong>the same sentences, the same number of steps, the same hyperparameters</strong> as the logit KD row,
with exactly one difference: no teacher distribution. So <strong>the difference between those two rows is the value of
dark knowledge, purified</strong>. If the two rows come out equal, all the KD you did wasn't worth even one teacher forward pass
— and this table is the only place you could learn that.</p></div></div>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-07-model-distillation/gap-closed.light.svg" alt="Horizontal bar chart showing an example reading of gap closed for three methods, with the zero-percent line being the untrained student and a green dashed line at one hundred percent being the teacher" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-07-model-distillation/gap-closed.dark.svg" alt="Horizontal bar chart showing an example reading of gap closed for three methods, with the zero-percent line being the untrained student and a green dashed line at one hundred percent being the teacher" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 7.4</span>How to read gap closed: 0% is the student before training, 100% is the teacher — the distance between the control row (orange) and the logit KD row is the part explainable only by the teacher's distribution (the numbers in the figure illustrate the mechanism, they are not measured results — the real ones come from the notebook)</p><div class="captionFooter_w00v"></div></figcaption></figure>
<p>The pattern you <strong>should expect</strong>: logit KD ≻ SeqKD ≻ SFT control ≻ base, with the bottom four rows identical on tok/sec.
If you see something else, read it this way:</p>
<ul>
<li class=""><strong>The control row does about as well as logit KD</strong> → the KD signal added nothing on this task. Try a higher T
(the dark knowledge is still being squeezed flat), or raise α, or the teacher and student are simply too close together.</li>
<li class=""><strong>SeqKD beats logit KD</strong> → genuinely possible when the dataset's gold answers are written worse than the teacher's answers
(our logit KD trains on the gold answers) — this is information, not failure. Report it straight.</li>
<li class=""><strong>No row moves much at all</strong> → 3,000 examples may be too few for this particular teacher-student gap. Look at the loss curve
before concluding anything, and read the limitations box at the end of the chapter.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="traps-to-watch-for">Traps to watch for<a href="https://kobkrit.com/en/blog/llm-07-model-distillation#traps-to-watch-for" class="hash-link" aria-label="Direct link to Traps to watch for" title="Direct link to Traps to watch for" translate="no">​</a></h3>
<p><strong>1. Forgetting the <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>T</mi><mn>2</mn></msup></mrow><annotation encoding="application/x-tex">T^2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8141em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1389em">T</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span> factor</strong>
No error appears anywhere. Your T sweep simply becomes fiction, because every time you move T
you secretly move the soft loss's learning rate along with it (figure 7.2) — the quietest bug in this chapter.</p>
<p><strong>2. Applying temperature on one side only</strong>
Writing <code>softmax(z_t / T)</code> but forgetting to divide the student's side by T — the student gets forced to imitate
the teacher's flattened distribution using its own sharp logits. The result is that it learns to be <em>genuinely</em> flat,
and at serving time (where there is no T) its answers come out bland and oddly dispersed. Equation 3.1 divides by T on both sides, always.</p>
<p><strong>3. Not detaching the teacher's logits</strong>
Forward the teacher without <code>torch.no_grad()</code> and autograd keeps the teacher's entire activations waiting
for a backward pass that never comes — VRAM quietly balloons until an OOM that points at some other line.
Our offline path is safe by construction (the logits live on disk; there's no graph to keep).
That's the third reason to precompute, on top of time and memory.</p>
<p><strong>4. Letting padding leak into the KL</strong>
Padding positions have a teacher distribution too — and it's garbage. Fail to mask it out
(the <code>mask = tgt.ne(-100)</code> line in section 7.2) and the mean KL gets diluted by meaningless positions.
Worse, the dilution ratio varies with the sentence lengths in each batch — the loss will wobble
in ways you can never trace.</p>
<p><strong>5. Two models on one card = a vanished batch budget</strong>
The 3.4 GB teacher sits on what used to be big-batch territory. If you OOM, reduce in this order:
<code>per_device_train_batch_size</code> → <code>max_length</code> → stop keeping the teacher on the card (finish the offline
precompute first, then <code>del teacher; torch.cuda.empty_cache()</code>) — that last step is already
the structure of our notebook.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="10-summary">10. Summary<a href="https://kobkrit.com/en/blog/llm-07-model-distillation#10-summary" class="hash-link" aria-label="Direct link to 10. Summary" title="Direct link to 10. Summary" translate="no">​</a></h2>
<ul>
<li class=""><strong>Dark knowledge lives in the teacher's wrong answers</strong> — the ranking over wrong choices encodes
similarity structure that hard labels can never convey, and <strong>temperature is the dial that reveals it</strong></li>
<li class=""><strong>The <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>T</mi><mn>2</mn></msup></mrow><annotation encoding="application/x-tex">T^2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8141em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1389em">T</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span> factor is not a talisman</strong> — the soft loss's gradient scales as <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mi mathvariant="normal">/</mi><msup><mi>T</mi><mn>2</mn></msup></mrow><annotation encoding="application/x-tex">1/T^2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0641em;vertical-align:-0.25em"></span><span class="mord">1/</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1389em">T</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span>;
multiply it back so you can tune T without secretly changing your own learning rate</li>
<li class=""><strong>SeqKD is SFT on teacher answers</strong> — the cheapest baseline, and the only route when tokenizers don't match</li>
<li class=""><strong>Logit KD requires an identical vocab</strong> — always assert first, because KL compares dimension by dimension</li>
<li class=""><strong>Top-64 is engineering, not theory</strong> — one batch of full-vocab logits is a 622 MB tensor;
keeping the top 64 leaves 0.79 MB while losing under 1% of coverage</li>
<li class=""><strong>Forward KL, reverse KL, and GKD's JSD are one line</strong> — the β dial sweeps from
mass-covering (this chapter) to mode-seeking (chapter 6)</li>
<li class=""><strong>The SFT control row is what gives the results table meaning</strong> — without it, you cannot separate
"the teacher helped" from "training more helped"</li>
<li class=""><strong>Gap closed is the metric that answers the real question</strong> — what percentage of the teacher-student gap closed,
at exactly the same tok/sec</li>
</ul>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>Limitations of this experiment</div><div class="admonitionContent_BuS1"><p><strong>The 1.7B → 0.6B gap is a narrow one.</strong> Our teacher is not dramatically stronger than our student,
so the measurable gains are correspondingly narrow — don't compare gap-closed numbers from this pair against a 70B → 7B
distillation where the gap is many times wider. This experiment demonstrates the <strong>mechanism and the measurement method</strong>, not final numbers.</p><p><strong>A bigger teacher doesn't fit on a T4</strong> — a 7B in fp16 eats ~14 GB on its own, nearly filling the card.
The way out isn't always a bigger card; it's exactly what this chapter practices: <strong>precompute top-K logits
offline</strong>, once, on an hourly rented machine, then train the student anywhere from a 590 MB file.
The offline structure that looks like a Colab compromise is in fact how real-scale work is done.</p><p><strong>Distillation transmits everything, including the teacher's biases and mistakes.</strong> The student has no mechanism to tell
which part of the distribution is knowledge and which part is a bad habit. If the teacher hates short answers, the student inherits that.
If the teacher drifts into English mid-Thai-sentence in certain contexts, the student tends to inherit that too.
So the notebook measures the distilled student on <strong>TH-SAFE</strong> and reports the result against the teacher directly —
if the numbers say something was inherited, write it into the report, don't delete the row.
This is the bridge to the next chapter: a model that accepts everything from its teacher without question needs a fence of its own.</p></div></div>
<p><strong>Next chapter:</strong> <a class="" href="https://kobkrit.com/en/blog/llm-08-guardrails">Guardrails</a> — our student has just inherited its teacher's knowledge and habits in full.
Next we build the fence around the model: catch dangerous inputs before they reach the model, catch dangerous outputs before they reach the user,
and measure the safety-versus-usability trade-off with real numbers.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="references">References<a href="https://kobkrit.com/en/blog/llm-07-model-distillation#references" class="hash-link" aria-label="Direct link to References" title="Direct link to References" translate="no">​</a></h2>
<ol>
<li class="">Hinton et al. (2015). <a href="https://arxiv.org/abs/1503.02531" target="_blank" rel="noopener noreferrer" class="">Distilling the Knowledge in a Neural Network</a> — the original KD paper: temperature and the T² factor</li>
<li class="">Kim et al. (2016). <a href="https://arxiv.org/abs/1606.07947" target="_blank" rel="noopener noreferrer" class="">Sequence-Level Knowledge Distillation</a> — sequence-level KD -- the cheapest baseline in section 9</li>
<li class="">Agarwal et al. (2023). <a href="https://arxiv.org/abs/2306.13649" target="_blank" rel="noopener noreferrer" class="">On-Policy Distillation of Language Models: Learning from Self-Generated Mistakes</a> — GKD: the JSD framework unifying forward and reverse KL</li>
<li class="">Gu et al. (2023). <a href="https://arxiv.org/abs/2306.08543" target="_blank" rel="noopener noreferrer" class="">MiniLLM: On-Policy Distillation of Large Language Models</a> — MiniLLM: the case for reverse KL</li>
<li class="">Sanh et al. (2019). <a href="https://arxiv.org/abs/1910.01108" target="_blank" rel="noopener noreferrer" class="">DistilBERT, a distilled version of BERT: smaller, faster, cheaper and lighter</a> — the most widely deployed distillation result</li>
<li class="">Chay-intr et al. (2025). <a href="https://arxiv.org/abs/2502.02938" target="_blank" rel="noopener noreferrer" class="">LLaVAC: Fine-tuning LLaVA as a Multimodal Sentiment Classifier</a> — LLaVAC: fine-tuning a multimodal model for a Thai task</li>
</ol>
<hr>
<p><em>The writing, code and notebooks in this series are licensed under <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank" rel="noopener noreferrer" class="">CC BY-NC-SA 4.0</a> — reuse and adapt them freely with attribution, for non-commercial purposes, and share your adaptations under the same terms. Third-party models and datasets referenced here keep their own licences.</em></p>
<nav class="nav_RfLT" aria-label="Thai LLM tutorial series navigation"><p class="heading_XRWm">Thai LLM series<span class="progress_f8e8">Part 7 of 10</span></p><ol class="list_U31a"><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-01-continue-pretraining"><span class="number_u3BE" aria-hidden="true">1</span><span class="title_BPvL">Continue Pretraining</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-02-sft-lora"><span class="number_u3BE" aria-hidden="true">2</span><span class="title_BPvL">SFT and LoRA</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo"><span class="number_u3BE" aria-hidden="true">3</span><span class="title_BPvL">RLHF and PPO</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-04-dpo"><span class="number_u3BE" aria-hidden="true">4</span><span class="title_BPvL">DPO: Direct Preference Optimization</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-05-grpo"><span class="number_u3BE" aria-hidden="true">5</span><span class="title_BPvL">GRPO</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-06-context-distillation"><span class="number_u3BE" aria-hidden="true">6</span><span class="title_BPvL">Context Distillation</span></a></li><li class="item_Y10l"><span class="chip_DDpP chipCurrent_BGpo" aria-current="step"><span class="number_u3BE" aria-hidden="true">7</span><span class="title_BPvL">Model Distillation</span><span class="srOnly_owtF">(you are here)</span></span></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-08-guardrails"><span class="number_u3BE" aria-hidden="true">8</span><span class="title_BPvL">Guardrails</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-09-benchmarking"><span class="number_u3BE" aria-hidden="true">9</span><span class="title_BPvL">Benchmarking</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-10-deployment"><span class="number_u3BE" aria-hidden="true">10</span><span class="title_BPvL">Deployment</span></a></li></ol></nav>]]></content>
        <author>
            <name>Kobkrit Viriyayudhakorn</name>
            <uri>https://github.com/kobkrit</uri>
        </author>
        <category label="ai" term="ai"/>
        <category label="llm" term="llm"/>
        <category label="thai" term="thai"/>
        <category label="tutorial" term="tutorial"/>
        <category label="fine-tuning" term="fine-tuning"/>
        <category label="distillation" term="distillation"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[LLM 8/10] Guardrails: The Real Safety Fence Isn't a Model, It's a Threshold]]></title>
        <id>https://kobkrit.com/en/blog/llm-08-guardrails</id>
        <link href="https://kobkrit.com/en/blog/llm-08-guardrails"/>
        <updated>2026-07-20T14:00:00.000Z</updated>
        <summary type="html"><![CDATA[Building a two-layer guardrail for a Thai chatbot — a dangerous-prompt classifier with Qwen3-0.6B + LoRA and a PII filter with regex + the Thai national-ID checksum — plus the most important lesson: accuracy alone means nothing, always report the benign-blocked rate]]></summary>
        <content type="html"><![CDATA[<p>The Thai chatbots my team and I deploy for real customers don't only meet polite questions —
people ask for recipes for illegal things, people try to trick the bot into insulting others, and there was a day the model volunteered a customer's phone number all by itself.
In this chapter we build protection in both directions: <strong>a classifier that checks incoming prompts for danger</strong> and <strong>an outbound PII filter</strong>.
But the central point of the chapter isn't the model — it's the fact that a guardrail is <strong>a threshold decision
under asymmetric costs</strong>, and the "94% accuracy" figure people love to show off is very nearly meaningless.</p>
<a class="badge_rUYD" href="https://colab.research.google.com/github/kobkrit/thai-llm-tutorials/blob/main/notebooks/08_guardrails.ipynb" target="_blank" rel="noopener noreferrer" aria-label="Open the notebook 08_guardrails.ipynb in Google Colab (opens in a new tab)"><svg class="mark_NB8U" viewBox="0 0 24 24" width="20" height="20" aria-hidden="true" focusable="false"><mask id="llmcourse-colab-cut"><rect x="0" y="0" width="24" height="24" fill="#fff"></rect><circle cx="16.2" cy="12" r="6.1" fill="#000"></circle></mask><circle cx="8.4" cy="12" r="4.6" fill="none" stroke="#F9AB00" stroke-width="3.1" mask="url(#llmcourse-colab-cut)"></circle><circle cx="16.2" cy="12" r="4.6" fill="none" stroke="#E8710A" stroke-width="3.1"></circle></svg><span class="text_QXpz">Open in Colab</span><code class="notebook_ntO0">08_guardrails.ipynb</code></a>
<nav class="nav_RfLT" aria-label="Thai LLM tutorial series navigation"><p class="heading_XRWm">Thai LLM series<span class="progress_f8e8">Part 8 of 10</span></p><ol class="list_U31a"><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-01-continue-pretraining"><span class="number_u3BE" aria-hidden="true">1</span><span class="title_BPvL">Continue Pretraining</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-02-sft-lora"><span class="number_u3BE" aria-hidden="true">2</span><span class="title_BPvL">SFT and LoRA</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo"><span class="number_u3BE" aria-hidden="true">3</span><span class="title_BPvL">RLHF and PPO</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-04-dpo"><span class="number_u3BE" aria-hidden="true">4</span><span class="title_BPvL">DPO: Direct Preference Optimization</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-05-grpo"><span class="number_u3BE" aria-hidden="true">5</span><span class="title_BPvL">GRPO</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-06-context-distillation"><span class="number_u3BE" aria-hidden="true">6</span><span class="title_BPvL">Context Distillation</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-07-model-distillation"><span class="number_u3BE" aria-hidden="true">7</span><span class="title_BPvL">Model Distillation</span></a></li><li class="item_Y10l"><span class="chip_DDpP chipCurrent_BGpo" aria-current="step"><span class="number_u3BE" aria-hidden="true">8</span><span class="title_BPvL">Guardrails</span><span class="srOnly_owtF">(you are here)</span></span></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-09-benchmarking"><span class="number_u3BE" aria-hidden="true">9</span><span class="title_BPvL">Benchmarking</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-10-deployment"><span class="number_u3BE" aria-hidden="true">10</span><span class="title_BPvL">Deployment</span></a></li></ol></nav>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-the-problem">1. The Problem<a href="https://kobkrit.com/en/blog/llm-08-guardrails#1-the-problem" class="hash-link" aria-label="Direct link to 1. The Problem" title="Direct link to 1. The Problem" translate="no">​</a></h2>
<p>Every previous chapter trained the model to be "better." But no matter how capable a model is,
the moment it meets real users, damage can always arrive from two directions:</p>
<table><thead><tr><th>Direction</th><th>Example damage</th><th>This chapter's tool</th></tr></thead><tbody><tr><td><strong>Inbound (input)</strong></td><td>A user asks how to hurt someone, for an illegal recipe, how to cheat — and the model answers</td><td>a dangerous-prompt classifier</td></tr><tr><td><strong>Outbound (output)</strong></td><td>The model emits a national ID number, phone number, or bank account leaked from context or training data</td><td>a deterministic PII filter</td></tr></tbody></table>
<p>Remember that SFT back in chapter 2 had a very quiet side effect: fine-tuning on narrow data
<strong>erodes the refusal behavior the model used to have</strong>. A model you tuned yourself is therefore usually <em>less</em> safe than the original.
That is why real systems need another fence that lives <strong>outside</strong> the model.</p>
<p>So why can't you just buy an off-the-shelf guardrail advertised as "94% accurate"?
Because that sentence hasn't answered the three questions that matter most:</p>
<ol>
<li class="">94% accurate <strong>at which threshold</strong> — the same number can slide along the entire curve</li>
<li class="">Measured on a test set with <strong>what percentage</strong> unsafe — in production, genuinely dangerous traffic is usually under 1%</li>
<li class="">And <strong>what percentage of innocent users does it block</strong> — the number almost nobody agrees to report</li>
</ol>
<p>A guardrail that blocks customers asking normal questions is not a safe system. It is a <strong>broken product</strong>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-what-were-going-to-do">2. What We're Going to Do<a href="https://kobkrit.com/en/blog/llm-08-guardrails#2-what-were-going-to-do" class="hash-link" aria-label="Direct link to 2. What We're Going to Do" title="Direct link to 2. What We're Going to Do" translate="no">​</a></h2>
<p>We'll build two real guardrails on free Colab, then measure them honestly:</p>
<table><thead><tr><th>Layer</th><th>Position</th><th>Technique</th><th>Approx. latency</th></tr></thead><tbody><tr><td><strong>Input guardrail</strong></td><td>before the prompt reaches the LLM</td><td>Qwen3-0.6B + sequence-classification head + LoRA r=8</td><td>~15–30 ms</td></tr><tr><td><strong>Output guardrail</strong></td><td>after the LLM answers, before the user sees it</td><td>regex + mod-11 checksum (no ML at all)</td><td>~0.1 ms</td></tr></tbody></table>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>The core idea of this chapter</div><div class="admonitionContent_BuS1"><p><strong>A guardrail isn't a model — it's a threshold decision under asymmetric costs.</strong>
The model only supplies a score <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>p</mi><mi>ϕ</mi></msub><mo stretchy="false">(</mo><mtext>unsafe</mtext><mo>∣</mo><mi>x</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">p_\phi(\text{unsafe}\mid x)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0361em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">ϕ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord text"><span class="mord">unsafe</span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">∣</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal">x</span><span class="mclose">)</span></span></span></span>; choosing the cutoff <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>τ</mi></mrow><annotation encoding="application/x-tex">\tau</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.1132em">τ</span></span></span></span>
is answering the business question "how many times more expensive is letting one dangerous thing through than blocking one innocent customer?"</p><p>An honest report therefore always carries <strong>two numbers</strong>: the unsafe caught <strong>and</strong> the benign blocked.
A single number on its own is marketing, not engineering.</p></div></div>
<p>We'll also see that some of the best guardrails <strong>aren't ML at all</strong> —
a PII filter built from regex + checksum is deterministic, unit-testable, microsecond-fast,
and can never be jailbroken.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-the-equations">3. The Equations<a href="https://kobkrit.com/en/blog/llm-08-guardrails#3-the-equations" class="hash-link" aria-label="Direct link to 3. The Equations" title="Direct link to 3. The Equations" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="31-the-classifier-loss--an-old-friend">3.1 The classifier loss — an old friend<a href="https://kobkrit.com/en/blog/llm-08-guardrails#31-the-classifier-loss--an-old-friend" class="hash-link" aria-label="Direct link to 3.1 The classifier loss — an old friend" title="Direct link to 3.1 The classifier loss — an old friend" translate="no">​</a></h3>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi mathvariant="script">L</mi><mo stretchy="false">(</mo><mi>ϕ</mi><mo stretchy="false">)</mo><mo>=</mo><mo>−</mo><msub><mi mathvariant="double-struck">E</mi><mrow><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>y</mi><mo stretchy="false">)</mo><mo>∼</mo><mi mathvariant="script">D</mi></mrow></msub><mo fence="false" stretchy="true" minsize="1.8em" maxsize="1.8em">[</mo><mtext> </mtext><mi>y</mi><mi>log</mi><mo>⁡</mo><msub><mi>p</mi><mi>ϕ</mi></msub><mo stretchy="false">(</mo><mtext>unsafe</mtext><mo>∣</mo><mi>x</mi><mo stretchy="false">)</mo><mo>+</mo><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><mi>y</mi><mo stretchy="false">)</mo><mi>log</mi><mo>⁡</mo><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">(</mo><mn>1</mn><mo>−</mo><msub><mi>p</mi><mi>ϕ</mi></msub><mo stretchy="false">(</mo><mtext>unsafe</mtext><mo>∣</mo><mi>x</mi><mo stretchy="false">)</mo><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">)</mo><mo fence="false" stretchy="true" minsize="1.8em" maxsize="1.8em">]</mo></mrow><annotation encoding="application/x-tex">\mathcal{L}(\phi) = -\mathbb{E}_{(x,y)\sim\mathcal{D}}\Big[\,y\log p_\phi(\text{unsafe}\mid x) + (1-y)\log\big(1-p_\phi(\text{unsafe}\mid x)\big)\Big]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathcal">L</span><span class="mopen">(</span><span class="mord mathnormal">ϕ</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1.8em;vertical-align:-0.65em"></span><span class="mord">−</span><span class="mord"><span class="mord mathbb">E</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em"><span style="top:-2.5198em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mopen mtight">(</span><span class="mord mathnormal mtight">x</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.0359em">y</span><span class="mclose mtight">)</span><span class="mrel mtight">∼</span><span class="mord mathcal mtight" style="margin-right:0.0278em">D</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.3552em"><span></span></span></span></span></span></span><span class="mord"><span class="delimsizing size2">[</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mop">lo<span style="margin-right:0.0139em">g</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">ϕ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord text"><span class="mord">unsafe</span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">∣</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mopen">(</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.2em;vertical-align:-0.35em"></span><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mop">lo<span style="margin-right:0.0139em">g</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="delimsizing size1">(</span></span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.0361em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">ϕ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord text"><span class="mord">unsafe</span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">∣</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1.8em;vertical-align:-0.65em"></span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mord"><span class="delimsizing size1">)</span></span><span class="mord"><span class="delimsizing size2">]</span></span></span></span></span></span>
<p>Plain binary cross-entropy (<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi><mo>=</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">y=1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0359em">y</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">1</span></span></span></span> means unsafe). Nothing new — and that is precisely the point:
the ML part of a guardrail is the easiest part of the whole system. The real substance is below.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="32-the-decision-rule-and-expected-cost--the-actual-content-of-this-chapter">3.2 The decision rule and expected cost — the actual content of this chapter<a href="https://kobkrit.com/en/blog/llm-08-guardrails#32-the-decision-rule-and-expected-cost--the-actual-content-of-this-chapter" class="hash-link" aria-label="Direct link to 3.2 The decision rule and expected cost — the actual content of this chapter" title="Direct link to 3.2 The decision rule and expected cost — the actual content of this chapter" translate="no">​</a></h3>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mtext>block</mtext><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo>=</mo><mn mathvariant="bold">1</mn><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">[</mo><mtext> </mtext><msub><mi>p</mi><mi>ϕ</mi></msub><mo stretchy="false">(</mo><mtext>unsafe</mtext><mo>∣</mo><mi>x</mi><mo stretchy="false">)</mo><mo>&gt;</mo><mi>τ</mi><mtext> </mtext><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">]</mo></mrow><annotation encoding="application/x-tex">\text{block}(x) = \mathbf{1}\big[\,p_\phi(\text{unsafe}\mid x) &gt; \tau\,\big]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord text"><span class="mord">block</span></span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1.2em;vertical-align:-0.35em"></span><span class="mord mathbf">1</span><span class="mord"><span class="delimsizing size1">[</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">ϕ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord text"><span class="mord">unsafe</span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">∣</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">&gt;</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1.2em;vertical-align:-0.35em"></span><span class="mord mathnormal" style="margin-right:0.1132em">τ</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="delimsizing size1">]</span></span></span></span></span></span>
<p>The model's job ends at producing a score. Blocking or passing is a comparison against <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>τ</mi></mrow><annotation encoding="application/x-tex">\tau</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.1132em">τ</span></span></span></span>, which we choose by minimizing expected cost:</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>C</mi><mo stretchy="false">(</mo><mi>τ</mi><mo stretchy="false">)</mo><mo>=</mo><msub><mi>c</mi><mtext>FN</mtext></msub><mtext> </mtext><mi>P</mi><mo stretchy="false">(</mo><mtext>unsafe</mtext><mo stretchy="false">)</mo><mtext> </mtext><mtext>FNR</mtext><mo stretchy="false">(</mo><mi>τ</mi><mo stretchy="false">)</mo><mtext>  </mtext><mo>+</mo><mtext>  </mtext><msub><mi>c</mi><mtext>FP</mtext></msub><mtext> </mtext><mi>P</mi><mo stretchy="false">(</mo><mtext>safe</mtext><mo stretchy="false">)</mo><mtext> </mtext><mtext>FPR</mtext><mo stretchy="false">(</mo><mi>τ</mi><mo stretchy="false">)</mo><mspace width="2em"></mspace><mspace width="2em"></mspace><msup><mi>τ</mi><mo>∗</mo></msup><mo>=</mo><mi>arg</mi><mo>⁡</mo><munder><mrow><mi>min</mi><mo>⁡</mo></mrow><mi>τ</mi></munder><mi>C</mi><mo stretchy="false">(</mo><mi>τ</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">C(\tau) = c_{\text{FN}}\,P(\text{unsafe})\,\text{FNR}(\tau) \;+\; c_{\text{FP}}\,P(\text{safe})\,\text{FPR}(\tau)
\qquad\qquad
\tau^* = \arg\min_\tau C(\tau)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0715em">C</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.1132em">τ</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathnormal">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">FN</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.1389em">P</span><span class="mopen">(</span><span class="mord text"><span class="mord">unsafe</span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord text"><span class="mord">FNR</span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.1132em">τ</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathnormal">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">FP</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.1389em">P</span><span class="mopen">(</span><span class="mord text"><span class="mord">safe</span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord text"><span class="mord">FPR</span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.1132em">τ</span><span class="mclose">)</span><span class="mspace" style="margin-right:2em"></span><span class="mspace" style="margin-right:2em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1132em">τ</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7387em"><span style="top:-3.113em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1.45em;vertical-align:-0.7em"></span><span class="mop">ar<span style="margin-right:0.0139em">g</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6679em"><span style="top:-2.4em;margin-left:0em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.1132em">τ</span></span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span><span class="mop">min</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.7em"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0715em">C</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.1132em">τ</span><span class="mclose">)</span></span></span></span></span>
<ul>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mtext>FNR</mtext><mo stretchy="false">(</mo><mi>τ</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\text{FNR}(\tau)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord text"><span class="mord">FNR</span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.1132em">τ</span><span class="mclose">)</span></span></span></span> = the fraction of unsafe that slips through (false negative rate)</li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mtext>FPR</mtext><mo stretchy="false">(</mo><mi>τ</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\text{FPR}(\tau)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord text"><span class="mord">FPR</span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.1132em">τ</span><span class="mclose">)</span></span></span></span> = the fraction of benign that gets blocked (false positive rate)</li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>c</mi><mtext>FN</mtext></msub><mo separator="true">,</mo><msub><mi>c</mi><mtext>FP</mtext></msub></mrow><annotation encoding="application/x-tex">c_{\text{FN}}, c_{\text{FP}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord"><span class="mord mathnormal">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">FN</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">FP</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> = the <strong>price</strong> of each kind of mistake</li>
</ul>
<p>Notice that this equation forces you to answer a question ML cannot answer for you: <strong>what is your product's <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>c</mi><mtext>FN</mtext></msub><mi mathvariant="normal">/</mi><msub><mi>c</mi><mtext>FP</mtext></msub></mrow><annotation encoding="application/x-tex">c_{\text{FN}}/c_{\text{FP}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathnormal">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">FN</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord">/</span><span class="mord"><span class="mord mathnormal">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">FP</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span>?</strong>
This is a pure product decision, and different products answer it differently:</p>
<table><thead><tr><th>Product</th><th>Price of an FN (unsafe slips through)</th><th>Price of an FP (blocking an innocent)</th><th>Reasonable <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>c</mi><mtext>FN</mtext></msub><mi mathvariant="normal">/</mi><msub><mi>c</mi><mtext>FP</mtext></msub></mrow><annotation encoding="application/x-tex">c_{\text{FN}}/c_{\text{FP}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathnormal">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">FN</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord">/</span><span class="mord"><span class="mord mathnormal">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">FP</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span></th></tr></thead><tbody><tr><td>Health-advice chatbot</td><td>life-threatening + legal liability</td><td>mildly annoyed user</td><td>50:1 or more</td></tr><tr><td>Enterprise customer assistant</td><td>damaging headlines</td><td>more support tickets</td><td>~10:1</td></tr><tr><td>Internal employee tool</td><td>limited (users are identifiable staff)</td><td>daily workflow friction</td><td>~2:1</td></tr></tbody></table>
<p>If you've never written this ratio down explicitly in a document, it means someone has been choosing <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>τ</mi></mrow><annotation encoding="application/x-tex">\tau</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.1132em">τ</span></span></span></span> for you by accident.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="33-the-most-expensive-lesson-of-the-chapter-base-rates-can-destroy-precision">3.3 The most expensive lesson of the chapter: base rates can destroy precision<a href="https://kobkrit.com/en/blog/llm-08-guardrails#33-the-most-expensive-lesson-of-the-chapter-base-rates-can-destroy-precision" class="hash-link" aria-label="Direct link to 3.3 The most expensive lesson of the chapter: base rates can destroy precision" title="Direct link to 3.3 The most expensive lesson of the chapter: base rates can destroy precision" translate="no">​</a></h3>
<p>Suppose our classifier catches unsafe at TPR = 95% and wrongly blocks only FPR = 5% — sounds excellent.
Question: of the messages it blocks, what percentage are actually unsafe? Straight Bayes,
with <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>π</mi><mo>=</mo><mi>P</mi><mo stretchy="false">(</mo><mtext>unsafe</mtext><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\pi = P(\text{unsafe})</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.1389em">P</span><span class="mopen">(</span><span class="mord text"><span class="mord">unsafe</span></span><span class="mclose">)</span></span></span></span> the fraction of unsafe in real traffic:</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mtext>precision</mtext><mo>=</mo><mi>P</mi><mo stretchy="false">(</mo><mtext>unsafe</mtext><mo>∣</mo><mtext>block</mtext><mo stretchy="false">)</mo><mo>=</mo><mfrac><mrow><mi>P</mi><mo stretchy="false">(</mo><mtext>block</mtext><mo>∣</mo><mtext>unsafe</mtext><mo stretchy="false">)</mo><mtext> </mtext><mi>π</mi></mrow><mrow><mi>P</mi><mo stretchy="false">(</mo><mtext>block</mtext><mo stretchy="false">)</mo></mrow></mfrac><mo>=</mo><mfrac><mrow><mtext>TPR</mtext><mo>⋅</mo><mi>π</mi></mrow><mrow><mtext>TPR</mtext><mo>⋅</mo><mi>π</mi><mo>+</mo><mtext>FPR</mtext><mo>⋅</mo><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><mi>π</mi><mo stretchy="false">)</mo></mrow></mfrac></mrow><annotation encoding="application/x-tex">\text{precision} = P(\text{unsafe}\mid\text{block})
= \frac{P(\text{block}\mid\text{unsafe})\,\pi}{P(\text{block})}
= \frac{\text{TPR}\cdot\pi}{\text{TPR}\cdot\pi + \text{FPR}\cdot(1-\pi)}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8623em;vertical-align:-0.1944em"></span><span class="mord text"><span class="mord">precision</span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.1389em">P</span><span class="mopen">(</span><span class="mord text"><span class="mord">unsafe</span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">∣</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord text"><span class="mord">block</span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.363em;vertical-align:-0.936em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.427em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1389em">P</span><span class="mopen">(</span><span class="mord text"><span class="mord">block</span></span><span class="mclose">)</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1389em">P</span><span class="mopen">(</span><span class="mord text"><span class="mord">block</span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">∣</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mord text"><span class="mord">unsafe</span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0359em">π</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.936em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.2963em;vertical-align:-0.936em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3603em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord text"><span class="mord">TPR</span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord text"><span class="mord">FPR</span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mopen">(</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="mclose">)</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord text"><span class="mord">TPR</span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord mathnormal" style="margin-right:0.0359em">π</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.936em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span>
<p>Plug in two scenarios:</p>
<ul>
<li class=""><strong>Balanced test set</strong> (<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>π</mi><mo>=</mo><mn>0.5</mn></mrow><annotation encoding="application/x-tex">\pi = 0.5</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0.5</span></span></span></span>): precision <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>=</mo><mstyle scriptlevel="0" displaystyle="true"><mfrac><mrow><mn>0.95</mn><mo>×</mo><mn>0.5</mn></mrow><mrow><mn>0.95</mn><mo>×</mo><mn>0.5</mn><mo>+</mo><mn>0.05</mn><mo>×</mo><mn>0.5</mn></mrow></mfrac></mstyle><mo>=</mo><mn>95</mn><mi mathvariant="normal">%</mi></mrow><annotation encoding="application/x-tex">= \dfrac{0.95 \times 0.5}{0.95 \times 0.5 + 0.05 \times 0.5} = 95\%</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.3669em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.0908em;vertical-align:-0.7693em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3214em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord">0.95</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord">0.5</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord">0.05</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord">0.5</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord">0.95</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord">0.5</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.7693em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.8056em;vertical-align:-0.0556em"></span><span class="mord">95%</span></span></span></span></li>
<li class=""><strong>Real traffic</strong> (<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>π</mi><mo>=</mo><mn>0.01</mn></mrow><annotation encoding="application/x-tex">\pi = 0.01</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0.01</span></span></span></span>): precision <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>=</mo><mstyle scriptlevel="0" displaystyle="true"><mfrac><mrow><mn>0.95</mn><mo>×</mo><mn>0.01</mn></mrow><mrow><mn>0.95</mn><mo>×</mo><mn>0.01</mn><mo>+</mo><mn>0.05</mn><mo>×</mo><mn>0.99</mn></mrow></mfrac></mstyle><mo>≈</mo><mn>16</mn><mi mathvariant="normal">%</mi></mrow><annotation encoding="application/x-tex">= \dfrac{0.95 \times 0.01}{0.95 \times 0.01 + 0.05 \times 0.99} \approx 16\%</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.3669em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.0908em;vertical-align:-0.7693em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3214em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord">0.95</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord">0.01</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord">0.05</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord">0.99</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord">0.95</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord">0.01</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.7693em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">≈</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.8056em;vertical-align:-0.0556em"></span><span class="mord">16%</span></span></span></span></li>
</ul>
<p><strong>The exact same classifier</strong> — but in production, of every 6 blocked messages, 5 are innocent users.
Because when unsafe is rare (<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>π</mi></mrow><annotation encoding="application/x-tex">\pi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.0359em">π</span></span></span></span> small), the <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mtext>FPR</mtext><mo>⋅</mo><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><mi>π</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\text{FPR}\cdot(1-\pi)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord text"><span class="mord">FPR</span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mopen">(</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0359em">π</span><span class="mclose">)</span></span></span></span> term in the denominator swallows everything.
This is why evaluating on a balanced set and then bragging "95% accurate" is self-deception.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="34-layered-defence">3.4 Layered defence<a href="https://kobkrit.com/en/blog/llm-08-guardrails#34-layered-defence" class="hash-link" aria-label="Direct link to 3.4 Layered defence" title="Direct link to 3.4 Layered defence" translate="no">​</a></h3>
<p>Stack <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>K</mi></mrow><annotation encoding="application/x-tex">K</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal" style="margin-right:0.0715em">K</span></span></span></span> independent guardrails (blocklist → classifier → system prompt → random human review),
where unsafe must fool <strong>every layer</strong> to get through, but benign gets blocked if <strong>any single layer</strong> trips:</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mtext>FNR</mtext><mtext>sys</mtext></msub><mo>=</mo><munderover><mo>∏</mo><mrow><mi>k</mi><mo>=</mo><mn>1</mn></mrow><mi>K</mi></munderover><msub><mtext>FNR</mtext><mi>k</mi></msub><mspace width="2em"></mspace><mspace width="2em"></mspace><msub><mtext>FPR</mtext><mtext>sys</mtext></msub><mo>=</mo><mn>1</mn><mo>−</mo><munderover><mo>∏</mo><mrow><mi>k</mi><mo>=</mo><mn>1</mn></mrow><mi>K</mi></munderover><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">(</mo><mn>1</mn><mo>−</mo><msub><mtext>FPR</mtext><mi>k</mi></msub><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">)</mo></mrow><annotation encoding="application/x-tex">\text{FNR}_{\text{sys}} = \prod_{k=1}^{K}\text{FNR}_k
\qquad\qquad
\text{FPR}_{\text{sys}} = 1 - \prod_{k=1}^{K}\big(1-\text{FPR}_k\big)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9694em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord text"><span class="mord">FNR</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">sys</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:3.1304em;vertical-align:-1.3021em"></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.8283em"><span style="top:-1.8479em;margin-left:0em"><span class="pstrut" style="height:3.05em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0315em">k</span><span class="mrel mtight">=</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.05em"><span class="pstrut" style="height:3.05em"></span><span><span class="mop op-symbol large-op">∏</span></span></span><span style="top:-4.3em;margin-left:0em"><span class="pstrut" style="height:3.05em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0715em">K</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.3021em"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord text"><span class="mord">FNR</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0315em">k</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:2em"></span><span class="mspace" style="margin-right:2em"></span><span class="mord"><span class="mord text"><span class="mord">FPR</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">sys</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em"></span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:3.1304em;vertical-align:-1.3021em"></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.8283em"><span style="top:-1.8479em;margin-left:0em"><span class="pstrut" style="height:3.05em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0315em">k</span><span class="mrel mtight">=</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.05em"><span class="pstrut" style="height:3.05em"></span><span><span class="mop op-symbol large-op">∏</span></span></span><span style="top:-4.3em;margin-left:0em"><span class="pstrut" style="height:3.05em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0715em">K</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.3021em"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="delimsizing size1">(</span></span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.2em;vertical-align:-0.35em"></span><span class="mord"><span class="mord text"><span class="mord">FPR</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0315em">k</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord"><span class="delimsizing size1">)</span></span></span></span></span></span>
<p>FNR falls geometrically (wonderful), but FPR <strong>compounds</strong> upward (the bill you pay).</p>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>The independence assumption is wildly optimistic</div><div class="admonitionContent_BuS1"><p>The <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mo>∏</mo><mi>k</mi></msub><msub><mtext>FNR</mtext><mi>k</mi></msub></mrow><annotation encoding="application/x-tex">\prod_k \text{FNR}_k</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0497em;vertical-align:-0.2997em"></span><span class="mop"><span class="mop op-symbol small-op" style="position:relative;top:0em">∏</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1864em"><span style="top:-2.4003em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0315em">k</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2997em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord text"><span class="mord">FNR</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0315em">k</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> equation holds only if the layers <strong>fail independently</strong>, which in reality they almost never do —
a single evasion trick (say, inserting a zero-width space mid-word) tends to fool <em>every</em> layer that works on raw text at once.
The layers' errors are therefore <strong>correlated</strong>, and the real <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mtext>FNR</mtext><mtext>sys</mtext></msub></mrow><annotation encoding="application/x-tex">\text{FNR}_{\text{sys}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9694em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord text"><span class="mord">FNR</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">sys</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span></span></span></span> is always worse than this formula.
Treat it as a best case, not a promise.</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="35-constrained-decoding--the-non-ml-guardrail-everyone-overlooks">3.5 Constrained decoding — the non-ML guardrail everyone overlooks<a href="https://kobkrit.com/en/blog/llm-08-guardrails#35-constrained-decoding--the-non-ml-guardrail-everyone-overlooks" class="hash-link" aria-label="Direct link to 3.5 Constrained decoding — the non-ML guardrail everyone overlooks" title="Direct link to 3.5 Constrained decoding — the non-ML guardrail everyone overlooks" translate="no">​</a></h3>
<p>If your use case only ever answers from a fixed set (a menu, categories, schema-conforming JSON), don't inspect the text afterwards —
<strong>enforce it at generation time</strong> by renormalizing over the allowed token set <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="script">A</mi></mrow><annotation encoding="application/x-tex">\mathcal{A}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathcal">A</span></span></span></span>:</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msup><mi>p</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">(</mo><mi>t</mi><mo>∣</mo><mi>x</mi><mo stretchy="false">)</mo><mo>=</mo><mfrac><mrow><msub><mi>p</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><mi>t</mi><mo>∣</mo><mi>x</mi><mo stretchy="false">)</mo><mtext> </mtext><mn mathvariant="bold">1</mn><mo stretchy="false">[</mo><mi>t</mi><mo>∈</mo><mi mathvariant="script">A</mi><mo stretchy="false">]</mo></mrow><mrow><munder><mo>∑</mo><mrow><msup><mi>t</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo>∈</mo><mi mathvariant="script">A</mi></mrow></munder><msub><mi>p</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msup><mi>t</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo>∣</mo><mi>x</mi><mo stretchy="false">)</mo></mrow></mfrac></mrow><annotation encoding="application/x-tex">p'(t\mid x) = \frac{p_\theta(t\mid x)\,\mathbf{1}[t\in\mathcal{A}]}{\sum_{t'\in\mathcal{A}} p_\theta(t'\mid x)}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0519em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8019em"><span style="top:-3.113em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">t</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">∣</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.4401em;vertical-align:-1.0131em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.427em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mop"><span class="mop op-symbol small-op" style="position:relative;top:0em">∑</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1786em"><span style="top:-2.4003em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathnormal mtight">t</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6828em"><span style="top:-2.786em;margin-right:0.0714em"><span class="pstrut" style="height:2.5em"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mrel mtight">∈</span><span class="mord mathcal mtight">A</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.3271em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">t</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6779em"><span style="top:-2.989em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">∣</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mord mathnormal">x</span><span class="mclose">)</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">t</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">∣</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathbf">1</span><span class="mopen">[</span><span class="mord mathnormal">t</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mord mathcal">A</span><span class="mclose">]</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.0131em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span>
<p>Tokens outside <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="script">A</mi></mrow><annotation encoding="application/x-tex">\mathcal{A}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathcal">A</span></span></span></span> have probability <strong>exactly zero</strong>, not "very small."
The result is a guardrail that is deterministic, adds zero latency, and <strong>cannot be bypassed by any prompt in the universe</strong> —
because it doesn't forbid the model from "wanting to say" something; it makes out-of-set words <strong>not exist in the inventory</strong> in the first place.
Wherever constrained decoding applies, use it first, and save the classifier for the parts that are genuinely free text.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-seeing-the-equations">4. Seeing the Equations<a href="https://kobkrit.com/en/blog/llm-08-guardrails#4-seeing-the-equations" class="hash-link" aria-label="Direct link to 4. Seeing the Equations" title="Direct link to 4. Seeing the Equations" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="every-guardrail-decision-lives-on-this-one-picture">Every guardrail decision lives on this one picture<a href="https://kobkrit.com/en/blog/llm-08-guardrails#every-guardrail-decision-lives-on-this-one-picture" class="hash-link" aria-label="Direct link to Every guardrail decision lives on this one picture" title="Direct link to Every guardrail decision lives on this one picture" translate="no">​</a></h3>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-08-guardrails/score-distributions.light.svg" alt="Plot of two overlapping score distributions with a threshold line in the middle, the overlap region shaded yellow and labeled as irreducible error" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-08-guardrails/score-distributions.dark.svg" alt="Plot of two overlapping score distributions with a threshold line in the middle, the overlap region shaded yellow and labeled as irreducible error" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 8.1</span>Scores p(unsafe|x) for safe prompts (green) and dangerous ones (red) on a held-out set — the yellow overlap region is the error no τ can eliminate; all you can choose is which way to be wrong (drawn from synthetic distributions; the real measurements are in the notebook)</p><div class="captionFooter_w00v"></div></figcaption></figure>
<p>Slide <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>τ</mi></mrow><annotation encoding="application/x-tex">\tau</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.1132em">τ</span></span></span></span> right = FN grows (more unsafe slips through). Slide it left = FP grows (more innocents blocked).
The only thing good training can do is <strong>push these two curves further apart</strong>. Everything else is choosing where to stand.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-model-gives-you-the-curve--the-cost-ratio-picks-the-point">The model gives you the curve — the cost ratio picks the point<a href="https://kobkrit.com/en/blog/llm-08-guardrails#the-model-gives-you-the-curve--the-cost-ratio-picks-the-point" class="hash-link" aria-label="Direct link to The model gives you the curve — the cost ratio picks the point" title="Direct link to The model gives you the curve — the cost ratio picks the point" translate="no">​</a></h3>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-08-guardrails/roc-pr-cost.light.svg" alt="Three-panel plot: ROC curve, precision-recall curve, and expected cost per threshold, with the optimal threshold points for two cost ratios marked" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-08-guardrails/roc-pr-cost.dark.svg" alt="Three-panel plot: ROC curve, precision-recall curve, and expected cost per threshold, with the optimal threshold points for two cost ratios marked" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 8.2</span>ROC, precision-recall, and expected cost C(τ) from the same pair of distributions as figure 8.1 — the τ* points for cost ratios 1:1 (purple) and 10:1 (orange) sit at different places on the same curve: the model didn't change, the product decision did</p><div class="captionFooter_w00v"></div></figcaption></figure>
<p>The right panel is equation 3.2 in its entirety: when <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>c</mi><mtext>FN</mtext></msub><mo>:</mo><msub><mi>c</mi><mtext>FP</mtext></msub></mrow><annotation encoding="application/x-tex">c_{\text{FN}}:c_{\text{FP}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">FN</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">:</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">FP</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> changes from 1:1 to 10:1,
the cost minimum moves from <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>τ</mi><mo>∗</mo></msup><mo>=</mo><mn>0.49</mn></mrow><annotation encoding="application/x-tex">\tau^* = 0.49</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6887em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1132em">τ</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6887em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0.49</span></span></span></span> down to <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>0.36</mn></mrow><annotation encoding="application/x-tex">0.36</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0.36</span></span></span></span> — the system accepts more wrongful blocking to let less slip through.
<strong>Nothing inside the model can tell you which point is right.</strong> The curve belongs to the model; the point belongs to you.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-picture-every-ml-team-should-pin-to-the-wall">The picture every ML team should pin to the wall<a href="https://kobkrit.com/en/blog/llm-08-guardrails#the-picture-every-ml-team-should-pin-to-the-wall" class="hash-link" aria-label="Direct link to The picture every ML team should pin to the wall" title="Direct link to The picture every ML team should pin to the wall" translate="no">​</a></h3>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-08-guardrails/precision-collapse.light.svg" alt="Plot of precision against base rate for three FPR values, showing precision collapsing at low base rates, with comparison points for a balanced test set versus real traffic" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-08-guardrails/precision-collapse.dark.svg" alt="Plot of precision against base rate for three FPR values, showing precision collapsing at low base rates, with comparison points for a balanced test set versus real traffic" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 8.3</span>Precision of the blocker against the actual unsafe fraction in traffic (log x-axis) — the same classifier at TPR 95% / FPR 5% gives 95% precision on a balanced test set but only 16% when real traffic is just 1% unsafe</p><div class="captionFooter_w00v"></div></figcaption></figure>
<p>Note the dashed line (FPR 1%) and the dotted line (FPR 0.1%): at low base rates,
the only thing that recovers precision is <strong>driving FPR down another order of magnitude</strong>, not raising TPR.
Nearly all the real work of guardrail engineering is this hunt for ever-lower FPR.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="layers-genuinely-help-but-they-arent-free">Layers genuinely help, but they aren't free<a href="https://kobkrit.com/en/blog/llm-08-guardrails#layers-genuinely-help-but-they-arent-free" class="hash-link" aria-label="Direct link to Layers genuinely help, but they aren't free" title="Direct link to Layers genuinely help, but they aren't free" translate="no">​</a></h3>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-08-guardrails/defense-layers.light.svg" alt="Log-scale plot showing system FNR falling and system FPR rising as the number of guardrail layers grows" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-08-guardrails/defense-layers.dark.svg" alt="Log-scale plot showing system FNR falling and system FPR rising as the number of guardrail layers grows" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 8.4</span>A K-layer system under the independence assumption (per-layer FNR 10%, per-layer FPR 3%) — system FNR plunges geometrically while system FPR compounds: the 4th layer barely catches anything new, yet still collects its toll from every innocent user</p><div class="captionFooter_w00v"></div></figcaption></figure>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="turn-all-three-dials-yourself">Turn all three dials yourself<a href="https://kobkrit.com/en/blog/llm-08-guardrails#turn-all-three-dials-yourself" class="hash-link" aria-label="Direct link to Turn all three dials yourself" title="Direct link to Turn all three dials yourself" translate="no">​</a></h3>
<p>The widget below is equations 3.2 and 3.3 made tangible. Try this sequence:</p>
<ol>
<li class=""><strong>Drag τ</strong> back and forth — watch the confusion matrix and FNR/FPR run in opposite directions. This is figure 8.1, interactive.</li>
<li class=""><strong>Set the cost ratio to 10:1</strong> — watch the <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>τ</mi><mo>∗</mo></msup></mrow><annotation encoding="application/x-tex">\tau^*</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6887em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1132em">τ</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6887em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span></span></span></span> point on the cost curve move down; the system blocks more generously.</li>
<li class="">Most important: <strong>drag the base rate from 50% down to 1%</strong> — watch the predicted production precision collapse before your eyes,
even though not one number in the test-set confusion matrix moves. This is figure 8.3, built with your own hands.</li>
</ol>
<div class="root_AxNC"><div class="controls_hr8V"><div class="control_Br1p"><label class="controlLabel_J5tp" for="_R_ieeldeh_"><span>Threshold τ</span><span class="controlValue_cYgn">0.500</span></label><input id="_R_ieeldeh_" class="range_qGHz" type="range" min="0" max="1" step="0.005" aria-label="Decision threshold tau" aria-valuetext="0.500" aria-describedby="_R_ieeldeh_-hint" value="0.5"><span class="controlHint_ilRY" id="_R_ieeldeh_-hint">Flag a request as unsafe when score ≥ τ.</span></div><div class="control_Br1p"><label class="controlLabel_J5tp" for="_R_12eeldeh_"><span>Cost ratio c_FN / c_FP</span><span class="controlValue_cYgn">10 : 1</span></label><input id="_R_12eeldeh_" class="range_qGHz" type="range" min="1" max="100" step="1" aria-label="Ratio of false negative cost to false positive cost" aria-valuetext="10 to 1" aria-describedby="_R_12eeldeh_-hint" value="10"><span class="controlHint_ilRY" id="_R_12eeldeh_-hint">How much worse is letting an unsafe request through than blocking a safe one?</span></div><div class="control_Br1p"><label class="controlLabel_J5tp" for="_R_1ieeldeh_"><span>Deployment base rate P(unsafe)</span><span class="controlValue_cYgn">1.0%</span></label><input id="_R_1ieeldeh_" class="range_qGHz" type="range" min="0.001" max="0.5" step="0.001" aria-label="Proportion of live traffic that is genuinely unsafe" aria-valuetext="1.0%" aria-describedby="_R_1ieeldeh_-hint" value="0.01"><span class="controlHint_ilRY" id="_R_1ieeldeh_-hint">The evaluation set is 34.3% unsafe. Real traffic is usually far cleaner.</span></div><div class="control_Br1p"><span class="controlLabel_J5tp">Optimal τ<span class="controlValue_cYgn">0.595</span></span><button type="button" class="button_ioxi buttonPrimary_sfPF">Jump to minimum cost</button></div></div><div class="svgWrap_mSxx"><svg class="svg_pLEH histogram_iOmc" viewBox="0 0 720 150" preserveAspectRatio="none" role="img" aria-label="Score distribution of safe and unsafe examples with a draggable threshold line."><rect x="0" y="110.46511627906976" width="15.863636363636363" height="19.534883720930235" class="histSafe_nrmu"></rect><rect x="16.363636363636363" y="15.581395348837205" width="15.863636363636363" height="114.4186046511628" class="histSafe_nrmu"></rect><rect x="32.72727272727273" y="18.372093023255815" width="15.863636363636363" height="111.62790697674419" class="histSafe_nrmu"></rect><rect x="49.09090909090909" y="10" width="15.863636363636363" height="120" class="histSafe_nrmu"></rect><rect x="65.45454545454545" y="12.79069767441861" width="15.863636363636363" height="117.20930232558139" class="histSafe_nrmu"></rect><rect x="81.81818181818181" y="15.581395348837205" width="15.863636363636363" height="114.4186046511628" class="histSafe_nrmu"></rect><rect x="98.18181818181819" y="21.162790697674424" width="15.863636363636363" height="108.83720930232558" class="histSafe_nrmu"></rect><rect x="114.54545454545455" y="54.65116279069767" width="15.863636363636363" height="75.34883720930233" class="histSafe_nrmu"></rect><rect x="130.9090909090909" y="65.81395348837209" width="15.863636363636363" height="64.18604651162791" class="histSafe_nrmu"></rect><rect x="147.27272727272728" y="88.13953488372093" width="15.863636363636363" height="41.86046511627907" class="histSafe_nrmu"></rect><rect x="163.63636363636363" y="82.55813953488372" width="15.863636363636363" height="47.44186046511628" class="histSafe_nrmu"></rect><rect x="180" y="65.81395348837209" width="15.863636363636363" height="64.18604651162791" class="histSafe_nrmu"></rect><rect x="196.36363636363637" y="90.93023255813952" width="15.863636363636363" height="39.06976744186047" class="histSafe_nrmu"></rect><rect x="212.72727272727272" y="102.09302325581396" width="15.863636363636363" height="27.906976744186046" class="histSafe_nrmu"></rect><rect x="229.0909090909091" y="96.51162790697674" width="15.863636363636363" height="33.48837209302326" class="histSafe_nrmu"></rect><rect x="245.45454545454544" y="107.67441860465117" width="15.863636363636363" height="22.325581395348838" class="histSafe_nrmu"></rect><rect x="261.8181818181818" y="113.25581395348837" width="15.863636363636363" height="16.74418604651163" class="histSafe_nrmu"></rect><rect x="278.1818181818182" y="104.88372093023256" width="15.863636363636363" height="25.116279069767444" class="histSafe_nrmu"></rect><rect x="294.54545454545456" y="104.88372093023256" width="15.863636363636363" height="25.116279069767444" class="histSafe_nrmu"></rect><rect x="310.9090909090909" y="121.62790697674419" width="15.863636363636363" height="8.372093023255815" class="histSafe_nrmu"></rect><rect x="327.27272727272725" y="110.46511627906976" width="15.863636363636363" height="19.534883720930235" class="histSafe_nrmu"></rect><rect x="343.6363636363636" y="124.4186046511628" width="15.863636363636363" height="5.5813953488372094" class="histSafe_nrmu"></rect><rect x="360" y="121.62790697674419" width="15.863636363636363" height="8.372093023255815" class="histSafe_nrmu"></rect><rect x="376.3636363636364" y="124.4186046511628" width="15.863636363636363" height="5.5813953488372094" class="histSafe_nrmu"></rect><rect x="392.72727272727275" y="116.04651162790698" width="15.863636363636363" height="13.953488372093023" class="histSafe_nrmu"></rect><rect x="409.09090909090907" y="116.04651162790698" width="15.863636363636363" height="13.953488372093023" class="histSafe_nrmu"></rect><rect x="425.45454545454544" y="124.4186046511628" width="15.863636363636363" height="5.5813953488372094" class="histSafe_nrmu"></rect><rect x="441.8181818181818" y="127.20930232558139" width="15.863636363636363" height="2.7906976744186047" class="histSafe_nrmu"></rect><rect x="458.1818181818182" y="127.20930232558139" width="15.863636363636363" height="2.7906976744186047" class="histSafe_nrmu"></rect><rect x="474.54545454545456" y="127.20930232558139" width="15.863636363636363" height="2.7906976744186047" class="histSafe_nrmu"></rect><rect x="490.9090909090909" y="130" width="15.863636363636363" height="0" class="histSafe_nrmu"></rect><rect x="507.27272727272725" y="130" width="15.863636363636363" height="0" class="histSafe_nrmu"></rect><rect x="523.6363636363636" y="124.4186046511628" width="15.863636363636363" height="5.5813953488372094" class="histSafe_nrmu"></rect><rect x="540" y="130" width="15.863636363636363" height="0" class="histSafe_nrmu"></rect><rect x="556.3636363636364" y="130" width="15.863636363636363" height="0" class="histSafe_nrmu"></rect><rect x="572.7272727272727" y="130" width="15.863636363636363" height="0" class="histSafe_nrmu"></rect><rect x="589.0909090909091" y="130" width="15.863636363636363" height="0" class="histSafe_nrmu"></rect><rect x="605.4545454545455" y="130" width="15.863636363636363" height="0" class="histSafe_nrmu"></rect><rect x="621.8181818181818" y="130" width="15.863636363636363" height="0" class="histSafe_nrmu"></rect><rect x="638.1818181818181" y="130" width="15.863636363636363" height="0" class="histSafe_nrmu"></rect><rect x="654.5454545454545" y="130" width="15.863636363636363" height="0" class="histSafe_nrmu"></rect><rect x="670.9090909090909" y="130" width="15.863636363636363" height="0" class="histSafe_nrmu"></rect><rect x="687.2727272727273" y="130" width="15.863636363636363" height="0" class="histSafe_nrmu"></rect><rect x="703.6363636363636" y="130" width="15.863636363636363" height="0" class="histSafe_nrmu"></rect><rect x="0" y="130" width="15.863636363636363" height="0" class="histUnsafe_Wv1M"></rect><rect x="16.363636363636363" y="130" width="15.863636363636363" height="0" class="histUnsafe_Wv1M"></rect><rect x="32.72727272727273" y="130" width="15.863636363636363" height="0" class="histUnsafe_Wv1M"></rect><rect x="49.09090909090909" y="130" width="15.863636363636363" height="0" class="histUnsafe_Wv1M"></rect><rect x="65.45454545454545" y="130" width="15.863636363636363" height="0" class="histUnsafe_Wv1M"></rect><rect x="81.81818181818181" y="130" width="15.863636363636363" height="0" class="histUnsafe_Wv1M"></rect><rect x="98.18181818181819" y="130" width="15.863636363636363" height="0" class="histUnsafe_Wv1M"></rect><rect x="114.54545454545455" y="130" width="15.863636363636363" height="0" class="histUnsafe_Wv1M"></rect><rect x="130.9090909090909" y="130" width="15.863636363636363" height="0" class="histUnsafe_Wv1M"></rect><rect x="147.27272727272728" y="130" width="15.863636363636363" height="0" class="histUnsafe_Wv1M"></rect><rect x="163.63636363636363" y="130" width="15.863636363636363" height="0" class="histUnsafe_Wv1M"></rect><rect x="180" y="124.4186046511628" width="15.863636363636363" height="5.5813953488372094" class="histUnsafe_Wv1M"></rect><rect x="196.36363636363637" y="127.20930232558139" width="15.863636363636363" height="2.7906976744186047" class="histUnsafe_Wv1M"></rect><rect x="212.72727272727272" y="124.4186046511628" width="15.863636363636363" height="5.5813953488372094" class="histUnsafe_Wv1M"></rect><rect x="229.0909090909091" y="130" width="15.863636363636363" height="0" class="histUnsafe_Wv1M"></rect><rect x="245.45454545454544" y="118.83720930232558" width="15.863636363636363" height="11.162790697674419" class="histUnsafe_Wv1M"></rect><rect x="261.8181818181818" y="130" width="15.863636363636363" height="0" class="histUnsafe_Wv1M"></rect><rect x="278.1818181818182" y="127.20930232558139" width="15.863636363636363" height="2.7906976744186047" class="histUnsafe_Wv1M"></rect><rect x="294.54545454545456" y="124.4186046511628" width="15.863636363636363" height="5.5813953488372094" class="histUnsafe_Wv1M"></rect><rect x="310.9090909090909" y="121.62790697674419" width="15.863636363636363" height="8.372093023255815" class="histUnsafe_Wv1M"></rect><rect x="327.27272727272725" y="124.4186046511628" width="15.863636363636363" height="5.5813953488372094" class="histUnsafe_Wv1M"></rect><rect x="343.6363636363636" y="121.62790697674419" width="15.863636363636363" height="8.372093023255815" class="histUnsafe_Wv1M"></rect><rect x="360" y="118.83720930232558" width="15.863636363636363" height="11.162790697674419" class="histUnsafe_Wv1M"></rect><rect x="376.3636363636364" y="127.20930232558139" width="15.863636363636363" height="2.7906976744186047" class="histUnsafe_Wv1M"></rect><rect x="392.72727272727275" y="124.4186046511628" width="15.863636363636363" height="5.5813953488372094" class="histUnsafe_Wv1M"></rect><rect x="409.09090909090907" y="127.20930232558139" width="15.863636363636363" height="2.7906976744186047" class="histUnsafe_Wv1M"></rect><rect x="425.45454545454544" y="118.83720930232558" width="15.863636363636363" height="11.162790697674419" class="histUnsafe_Wv1M"></rect><rect x="441.8181818181818" y="110.46511627906976" width="15.863636363636363" height="19.534883720930235" class="histUnsafe_Wv1M"></rect><rect x="458.1818181818182" y="104.88372093023256" width="15.863636363636363" height="25.116279069767444" class="histUnsafe_Wv1M"></rect><rect x="474.54545454545456" y="102.09302325581396" width="15.863636363636363" height="27.906976744186046" class="histUnsafe_Wv1M"></rect><rect x="490.9090909090909" y="110.46511627906976" width="15.863636363636363" height="19.534883720930235" class="histUnsafe_Wv1M"></rect><rect x="507.27272727272725" y="102.09302325581396" width="15.863636363636363" height="27.906976744186046" class="histUnsafe_Wv1M"></rect><rect x="523.6363636363636" y="110.46511627906976" width="15.863636363636363" height="19.534883720930235" class="histUnsafe_Wv1M"></rect><rect x="540" y="104.88372093023256" width="15.863636363636363" height="25.116279069767444" class="histUnsafe_Wv1M"></rect><rect x="556.3636363636364" y="93.72093023255815" width="15.863636363636363" height="36.27906976744186" class="histUnsafe_Wv1M"></rect><rect x="572.7272727272727" y="88.13953488372093" width="15.863636363636363" height="41.86046511627907" class="histUnsafe_Wv1M"></rect><rect x="589.0909090909091" y="88.13953488372093" width="15.863636363636363" height="41.86046511627907" class="histUnsafe_Wv1M"></rect><rect x="605.4545454545455" y="65.81395348837209" width="15.863636363636363" height="64.18604651162791" class="histUnsafe_Wv1M"></rect><rect x="621.8181818181818" y="88.13953488372093" width="15.863636363636363" height="41.86046511627907" class="histUnsafe_Wv1M"></rect><rect x="638.1818181818181" y="82.55813953488372" width="15.863636363636363" height="47.44186046511628" class="histUnsafe_Wv1M"></rect><rect x="654.5454545454545" y="82.55813953488372" width="15.863636363636363" height="47.44186046511628" class="histUnsafe_Wv1M"></rect><rect x="670.9090909090909" y="93.72093023255815" width="15.863636363636363" height="36.27906976744186" class="histUnsafe_Wv1M"></rect><rect x="687.2727272727273" y="82.55813953488372" width="15.863636363636363" height="47.44186046511628" class="histUnsafe_Wv1M"></rect><rect x="703.6363636363636" y="118.83720930232558" width="15.863636363636363" height="11.162790697674419" class="histUnsafe_Wv1M"></rect><line x1="360" y1="0" x2="360" y2="140" class="tauLine_yF3p"></line></svg></div><p class="legendRow_om72"><span class="legendItem_fx92"><span class="legendSwatch_ly4K histSafe_nrmu"></span>safe</span><span class="legendItem_fx92"><span class="legendSwatch_ly4K histUnsafe_Wv1M"></span>unsafe</span><span class="legendHint_pdp0">Drag the line, or use the τ slider.</span></p><div class="charts_UBTr"><figure class="chart__OlR"><figcaption class="chartTitle_JK0P">ROC<span class="chartSubtitle_pHig">AUC = 0.987</span></figcaption><svg class="svg_pLEH" viewBox="0 0 300 300" role="img" aria-label="FPR vs TPR"><rect x="38" y="14" width="248" height="248" class="plotArea_QhBk"></rect><line x1="38" y1="262" x2="286" y2="14" class="chance_eT6F"></line><path d="M38.00,94.60 L38.00,98.73 L38.00,98.73 L38.00,98.73 L38.00,103.90 L38.00,107.00 L38.00,109.07 L38.00,113.20 L38.00,114.23 L38.00,117.33 L38.00,121.47 L38.00,125.60 L38.00,128.70 L38.00,130.77 L38.00,134.90 L38.00,136.97 L38.00,142.13 L38.00,145.23 L38.00,149.37 L38.00,152.47 L38.00,157.63 L38.00,161.77 L38.00,165.90 L38.00,175.20 L38.00,178.30 L38.00,181.40 L38.00,185.53 L38.00,188.63 L38.00,191.73 L38.00,193.80 L38.00,195.87 L38.00,200.00 L38.00,206.20 L38.00,210.33 L38.00,211.37 L38.00,213.43 L38.00,217.57 L38.00,224.80 L38.00,228.93 L38.00,231.00 L38.00,235.13 L38.00,237.20 L38.00,240.30 L38.00,246.50 L38.00,250.63 L38.00,252.70 L38.00,256.83 L38.00,257.87 L38.00,260.97 L38.00,260.97 L38.00,262.00 L38.00,262.00 L38.54,92.53 L38.54,92.53 L38.54,93.57 L39.08,72.90 L39.08,73.93 L39.08,74.97 L39.08,76.00 L39.08,77.03 L39.08,82.20 L39.08,84.27 L39.08,86.33 L39.08,89.43 L39.08,91.50 L39.62,56.37 L39.62,59.47 L39.62,61.53 L39.62,63.60 L39.62,67.73 L39.62,69.80 L39.62,71.87 L40.16,54.30 L40.16,55.33 L40.70,42.93 L40.70,46.03 L40.70,47.07 L40.70,47.07 L40.70,49.13 L40.70,51.20 L40.70,53.27 L40.70,54.30 L41.77,42.93 L42.85,42.93 L42.85,42.93 L43.93,42.93 L44.47,39.83 L44.47,40.87 L44.47,40.87 L44.47,41.90 L46.09,39.83 L47.17,39.83 L48.24,38.80 L48.24,38.80 L48.24,38.80 L48.24,38.80 L48.24,39.83 L49.32,38.80 L49.86,32.60 L49.86,34.67 L49.86,34.67 L49.86,35.70 L50.40,32.60 L50.94,31.57 L50.94,31.57 L50.94,32.60 L52.02,30.53 L52.02,30.53 L53.10,29.50 L54.71,29.50 L55.25,29.50 L55.79,28.47 L56.33,27.43 L56.33,27.43 L56.87,26.40 L57.95,24.33 L60.10,24.33 L61.18,24.33 L61.18,24.33 L62.26,24.33 L62.26,24.33 L62.80,24.33 L63.34,23.30 L66.03,23.30 L67.65,23.30 L68.19,23.30 L68.19,23.30 L69.27,23.30 L69.81,23.30 L71.43,20.20 L71.43,21.23 L73.04,19.17 L74.12,19.17 L75.20,19.17 L75.20,19.17 L77.36,19.17 L80.05,19.17 L80.59,18.13 L80.59,18.13 L81.13,18.13 L82.75,17.10 L85.44,17.10 L87.60,17.10 L88.68,17.10 L91.37,16.07 L92.45,16.07 L93.53,16.07 L96.23,14.00 L100.00,14.00 L102.16,14.00 L105.39,14.00 L107.01,14.00 L109.17,14.00 L110.78,14.00 L113.48,14.00 L116.17,14.00 L117.79,14.00 L118.87,14.00 L119.95,14.00 L122.64,14.00 L125.34,14.00 L128.03,14.00 L132.35,14.00 L133.43,14.00 L135.58,14.00 L136.66,14.00 L139.36,14.00 L143.67,14.00 L147.98,14.00 L151.76,14.00 L154.99,14.00 L163.62,14.00 L165.77,14.00 L170.63,14.00 L176.02,14.00 L182.49,14.00 L188.42,14.00 L192.73,14.00 L196.50,14.00 L201.90,14.00 L205.67,14.00 L212.68,14.00 L215.91,14.00 L219.69,14.00 L224.00,14.00 L231.01,14.00 L236.94,14.00 L240.71,14.00 L246.64,14.00 L250.42,14.00 L255.81,14.00 L261.20,14.00 L268.75,14.00 L270.37,14.00 L275.22,14.00 L281.15,14.00 L283.30,14.00 L284.92,14.00 L286.00,14.00 L286.00,14.00 L286.00,14.00" class="curve_MZbm"></path><circle cx="49.86086956521739" cy="34.666666666666686" r="5" class="marker_Thvy"></circle><line x1="38" y1="262" x2="286" y2="262" class="axis_vyjV"></line><line x1="38" y1="262" x2="38" y2="14" class="axis_vyjV"></line><text x="38" y="277" text-anchor="middle" class="tickLabel_B3jM">0</text><text x="162" y="277" text-anchor="middle" class="tickLabel_B3jM">0.5</text><text x="286" y="277" text-anchor="middle" class="tickLabel_B3jM">1</text><text x="32" y="266" text-anchor="end" class="tickLabel_B3jM">0</text><text x="32" y="142" text-anchor="end" class="tickLabel_B3jM">0.5</text><text x="32" y="18" text-anchor="end" class="tickLabel_B3jM">1</text><text x="162" y="296" text-anchor="middle" class="axisLabel_Yazw">FPR</text><text x="10" y="138" text-anchor="middle" transform="rotate(-90 10 138)" class="axisLabel_Yazw">TPR</text></svg></figure><figure class="chart__OlR"><figcaption class="chartTitle_JK0P">Precision-Recall<span class="chartSubtitle_pHig">at the eval base rate 34.3%</span></figcaption><svg class="svg_pLEH" viewBox="0 0 300 300" role="img" aria-label="recall vs precision"><rect x="38" y="14" width="248" height="248" class="plotArea_QhBk"></rect><line x1="38" y1="176.9714285714286" x2="286" y2="176.9714285714286" class="chance_eT6F"></line><path d="M38.00,14.00 L38.00,14.00 L39.03,14.00 L39.03,14.00 L42.13,14.00 L43.17,14.00 L47.30,14.00 L49.37,14.00 L53.50,14.00 L59.70,14.00 L62.80,14.00 L64.87,14.00 L69.00,14.00 L71.07,14.00 L75.20,14.00 L82.43,14.00 L86.57,14.00 L88.63,14.00 L89.67,14.00 L93.80,14.00 L100.00,14.00 L104.13,14.00 L106.20,14.00 L108.27,14.00 L111.37,14.00 L114.47,14.00 L118.60,14.00 L121.70,14.00 L124.80,14.00 L134.10,14.00 L138.23,14.00 L142.37,14.00 L147.53,14.00 L150.63,14.00 L154.77,14.00 L157.87,14.00 L163.03,14.00 L165.10,14.00 L169.23,14.00 L171.30,14.00 L174.40,14.00 L178.53,14.00 L182.67,14.00 L185.77,14.00 L186.80,14.00 L190.93,14.00 L193.00,14.00 L196.10,14.00 L201.27,14.00 L201.27,14.00 L201.27,14.00 L205.40,14.00 L206.43,15.51 L207.47,15.50 L207.47,15.50 L208.50,16.97 L210.57,16.93 L213.67,16.88 L215.73,16.85 L217.80,16.82 L222.97,16.74 L224.00,16.73 L225.03,16.71 L226.07,16.70 L227.10,16.68 L228.13,17.98 L230.20,17.94 L232.27,17.90 L236.40,17.82 L238.47,17.78 L240.53,17.74 L243.63,17.68 L244.67,18.86 L245.70,20.02 L245.70,18.84 L246.73,19.99 L248.80,19.93 L250.87,19.88 L252.93,19.82 L252.93,19.82 L253.97,19.79 L257.07,26.23 L257.07,24.10 L257.07,24.10 L257.07,21.93 L257.07,19.71 L258.10,27.23 L259.13,27.17 L259.13,27.17 L260.17,34.14 L260.17,32.17 L260.17,30.17 L260.17,27.11 L261.20,35.97 L261.20,34.05 L261.20,34.05 L261.20,34.05 L261.20,34.05 L264.30,36.64 L265.33,36.55 L265.33,36.55 L267.40,38.20 L267.40,37.28 L267.40,36.36 L268.43,38.10 L268.43,38.10 L269.47,39.79 L269.47,39.79 L270.50,44.88 L270.50,44.03 L270.50,41.45 L271.53,45.60 L272.57,46.31 L272.57,46.31 L273.60,47.00 L275.67,55.33 L275.67,54.58 L275.67,54.58 L275.67,53.06 L275.67,53.06 L275.67,51.52 L275.67,48.37 L276.70,64.46 L276.70,63.77 L276.70,62.39 L276.70,62.39 L276.70,61.69 L276.70,59.57 L276.70,55.93 L278.77,66.12 L279.80,65.95 L280.83,75.80 L280.83,72.78 L280.83,70.29 L280.83,70.29 L280.83,69.02 L280.83,67.73 L281.87,76.78 L281.87,76.20 L281.87,76.20 L282.90,84.43 L282.90,83.35 L282.90,81.15 L282.90,78.33 L283.93,88.91 L283.93,87.89 L283.93,86.85 L286.00,176.97 L286.00,176.97 L286.00,176.97 L286.00,176.73 L286.00,176.36 L286.00,175.86 L286.00,174.47 L286.00,173.30 L286.00,172.90 L286.00,170.99 L286.00,169.58 L286.00,168.12 L286.00,167.07 L286.00,165.38 L286.00,164.27 L286.00,162.47 L286.00,160.26 L286.00,158.85 L286.00,157.58 L286.00,156.47 L286.00,153.98 L286.00,152.59 L286.00,150.54 L286.00,149.06 L286.00,147.32 L286.00,144.83 L286.00,142.00 L286.00,139.53 L286.00,137.22 L286.00,136.16 L286.00,131.76 L286.00,130.03 L286.00,127.95 L286.00,125.49 L286.00,122.93 L286.00,121.29 L286.00,120.62 L286.00,119.27 L286.00,118.58 L286.00,115.76 L286.00,113.94 L286.00,112.08 L286.00,110.16 L286.00,109.38 L286.00,108.60 L286.00,107.40 L286.00,105.37 L286.00,103.28 L286.00,102.00 L286.00,100.26 L286.00,98.93 L286.00,96.21 L286.00,94.34 L286.00,90.97" class="curve_MZbm"></path><circle cx="265.3333333333333" cy="36.54545454545456" r="5" class="marker_Thvy"></circle><line x1="38" y1="262" x2="286" y2="262" class="axis_vyjV"></line><line x1="38" y1="262" x2="38" y2="14" class="axis_vyjV"></line><text x="38" y="277" text-anchor="middle" class="tickLabel_B3jM">0</text><text x="162" y="277" text-anchor="middle" class="tickLabel_B3jM">0.5</text><text x="286" y="277" text-anchor="middle" class="tickLabel_B3jM">1</text><text x="32" y="266" text-anchor="end" class="tickLabel_B3jM">0</text><text x="32" y="142" text-anchor="end" class="tickLabel_B3jM">0.5</text><text x="32" y="18" text-anchor="end" class="tickLabel_B3jM">1</text><text x="162" y="296" text-anchor="middle" class="axisLabel_Yazw">recall</text><text x="10" y="138" text-anchor="middle" transform="rotate(-90 10 138)" class="axisLabel_Yazw">precision</text></svg></figure><figure class="chart__OlR"><figcaption class="chartTitle_JK0P">Expected cost<span class="chartSubtitle_pHig">C(τ) = 10·π·FNR + (1−π)·FPR</span></figcaption><svg class="svg_pLEH" viewBox="0 0 300 300" role="img" aria-label="τ vs cost"><rect x="38" y="14" width="248" height="248" class="plotArea_QhBk"></rect><path d="M38.00,14.00 L39.24,14.00 L40.48,14.00 L41.72,15.08 L42.96,16.70 L44.20,18.85 L45.44,24.78 L46.68,29.63 L47.92,31.25 L49.16,38.80 L50.40,44.19 L51.64,49.58 L52.88,53.36 L54.12,59.29 L55.36,63.06 L56.60,68.99 L57.84,76.00 L59.08,80.31 L60.32,84.09 L61.56,87.32 L62.80,94.33 L64.04,98.10 L65.28,103.50 L66.52,107.27 L67.76,111.58 L69.00,117.51 L70.24,123.98 L71.48,129.37 L72.72,134.23 L73.96,136.38 L75.20,145.01 L76.44,148.24 L77.68,152.02 L78.92,156.33 L80.16,160.64 L81.40,163.34 L82.64,164.42 L83.88,166.57 L85.12,167.65 L86.36,171.97 L87.60,174.66 L88.84,177.36 L90.08,180.05 L91.32,181.13 L92.56,182.21 L93.80,183.83 L95.04,186.52 L96.28,189.22 L97.52,190.83 L98.76,192.99 L100.00,194.61 L101.24,197.84 L102.48,200.00 L103.72,203.77 L104.96,206.26 L106.20,207.34 L107.44,208.42 L108.68,211.01 L109.92,212.09 L111.16,214.24 L112.40,216.94 L113.64,218.45 L114.88,218.99 L116.12,218.99 L117.36,219.43 L118.60,222.12 L119.84,224.28 L121.08,224.28 L122.32,225.36 L123.56,226.43 L124.80,227.95 L126.04,227.84 L127.28,229.25 L128.52,229.79 L129.76,230.87 L131.00,230.87 L132.24,231.41 L133.48,233.03 L134.72,235.72 L135.96,236.16 L137.20,236.70 L138.44,236.70 L139.68,237.77 L140.92,237.77 L142.16,238.85 L143.40,241.01 L144.64,241.88 L145.88,242.31 L147.12,242.31 L148.36,242.75 L149.60,243.18 L150.84,243.72 L152.08,245.34 L153.32,246.31 L154.56,246.31 L155.80,247.29 L157.04,247.29 L158.28,247.18 L159.52,247.72 L160.76,248.26 L162.00,248.05 L163.24,248.05 L164.48,247.95 L165.72,248.17 L166.96,249.25 L168.20,249.25 L169.44,249.25 L170.68,249.25 L171.92,249.15 L173.16,250.23 L174.40,251.30 L175.64,252.92 L176.88,252.82 L178.12,252.82 L179.36,252.71 L180.60,253.15 L181.84,254.23 L183.08,254.23 L184.32,255.30 L185.56,256.38 L186.80,256.07 L188.04,255.96 L189.28,255.96 L190.52,255.76 L191.76,255.55 L193.00,255.34 L194.24,255.23 L195.48,255.77 L196.72,255.67 L197.96,256.10 L199.20,255.79 L200.44,255.58 L201.68,255.37 L202.92,254.95 L204.16,254.75 L205.40,254.54 L206.64,254.97 L207.88,254.87 L209.12,254.76 L210.36,254.66 L211.60,254.55 L212.84,254.03 L214.08,253.82 L215.32,253.62 L216.56,253.30 L217.80,253.09 L219.04,253.53 L220.28,253.53 L221.52,253.42 L222.76,253.86 L224.00,253.44 L225.24,253.44 L226.48,253.44 L227.72,252.92 L228.96,252.61 L230.20,252.40 L231.44,251.98 L232.68,251.88 L233.92,251.56 L235.16,251.14 L236.40,250.73 L237.64,250.41 L238.88,250.21 L240.12,249.79 L241.36,249.58 L242.60,249.06 L243.84,248.74 L245.08,248.33 L246.32,248.01 L247.56,247.49 L248.80,247.07 L250.04,246.66 L251.28,245.72 L252.52,245.40 L253.76,245.09 L255.00,244.67 L256.24,244.36 L257.48,244.05 L258.72,243.84 L259.96,243.63 L261.20,243.21 L262.44,242.59 L263.68,242.17 L264.92,242.06 L266.16,241.86 L267.40,241.44 L268.64,240.71 L269.88,240.29 L271.12,240.08 L272.36,239.66 L273.60,239.45 L274.84,239.14 L276.08,238.52 L277.32,238.10 L278.56,237.89 L279.80,237.47 L281.04,237.37 L282.28,237.05 L283.52,237.05 L284.76,236.95 L286.00,236.95" class="curve_MZbm"></path><line x1="185.56" y1="262" x2="185.56" y2="14" class="optimalLine_RA1O"></line><circle cx="162" cy="248.0515883472405" r="5" class="marker_Thvy"></circle><line x1="38" y1="262" x2="286" y2="262" class="axis_vyjV"></line><line x1="38" y1="262" x2="38" y2="14" class="axis_vyjV"></line><text x="38" y="277" text-anchor="middle" class="tickLabel_B3jM">0</text><text x="162" y="277" text-anchor="middle" class="tickLabel_B3jM">0.5</text><text x="286" y="277" text-anchor="middle" class="tickLabel_B3jM">1</text><text x="32" y="266" text-anchor="end" class="tickLabel_B3jM">0</text><text x="32" y="142" text-anchor="end" class="tickLabel_B3jM">0.5</text><text x="32" y="18" text-anchor="end" class="tickLabel_B3jM">1</text><text x="162" y="296" text-anchor="middle" class="axisLabel_Yazw">τ</text><text x="10" y="138" text-anchor="middle" transform="rotate(-90 10 138)" class="axisLabel_Yazw">cost</text></svg></figure></div><div class="matrixWrap_OApT"><table class="matrix_BqPq"><caption class="matrixCaption_qy8z">Measured on the held-out set at τ = 0.500 (n = 700)</caption><thead><tr><td></td><th scope="col">predicted unsafe</th><th scope="col">predicted safe</th></tr></thead><tbody><tr><th scope="row">actually unsafe</th><td class="cellGood_rnN1"><span class="cellValue_fzwa">220</span><span class="cellTag_CetO">TP</span></td><td class="cellBad_afq5"><span class="cellValue_fzwa">20</span><span class="cellTag_CetO">FN</span></td></tr><tr><th scope="row">actually safe</th><td class="cellBad_afq5"><span class="cellValue_fzwa">22</span><span class="cellTag_CetO">FP</span></td><td class="cellGood_rnN1"><span class="cellValue_fzwa">438</span><span class="cellTag_CetO">TN</span></td></tr></tbody></table></div><div class="readouts__tjv"><div class="readout_D9ns"><span class="readoutLabel_EsIV">Recall (TPR)</span><span class="readoutValue_VS6z">91.7%</span><span class="readoutSub_DoT9">95% CI 87.5%–94.5%</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">FPR</span><span class="readoutValue_VS6z">4.8%</span><span class="readoutSub_DoT9">95% CI 3.2%–7.1%</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">Precision on eval set</span><span class="readoutValue_VS6z">90.9%</span><span class="readoutSub_DoT9">95% CI 86.6%–93.9%</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">Precision at live base rate</span><span class="readoutValue_VS6z">16.2%</span><span class="readoutSub_DoT9">95% CI 11.0%–23.1%</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">Expected cost</span><span class="readoutValue_VS6z">0.0557</span><span class="readoutSub_DoT9">minimised at τ = 0.595</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">Flagged per 10,000</span><span class="readoutValue_VS6z">565</span><span class="readoutSub_DoT9">473 of them false alarms</span></div></div><p class="callout_aEDz calloutDanger_TZRT" role="status"><strong class="calloutTitle_nx3s">Precision has collapsed.</strong>The classifier looks excellent on the evaluation set — 90.9% precision — but at a live base rate of 1.0% it drops to 16.2%. Nothing about the model changed; TPR and FPR are identical. There are simply so many more safe requests than unsafe ones that an FPR of 4.8% produces more false alarms than the classifier finds true positives. This is why a guardrail benchmarked on a balanced set falls apart in production, and why FPR, not accuracy, is the number to negotiate over.</p><p class="status_mfC7">Showing a synthetic held-out set.</p></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-setting-up-the-environment">5. Setting Up the Environment<a href="https://kobkrit.com/en/blog/llm-08-guardrails#5-setting-up-the-environment" class="hash-link" aria-label="Direct link to 5. Setting Up the Environment" title="Direct link to 5. Setting Up the Environment" translate="no">​</a></h2>
<p>Open Colab and pick <strong>Runtime → Change runtime type → T4 GPU</strong> (the free tier is enough — the classifier trains in about 7 minutes).</p>
<div class="theme-admonition theme-admonition-danger admonition_xJq3 alert alert--danger"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"></path></svg></span>The series-wide warning worth re-reading every chapter</div><div class="admonitionContent_BuS1"><p>The Colab T4 is Turing architecture (SM 7.5), which <strong>does not support bfloat16</strong> and <strong>does not support FlashAttention-2</strong>.</p><p>But Qwen3-0.6B's <code>config.json</code> declares <code>torch_dtype: bfloat16</code>.
So <code>torch_dtype="auto"</code> is <strong>a trap</strong> — your code will crash or run bizarrely slowly without telling you why.</p><div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">torch_dtype</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">float16      </span><span class="token comment" style="color:#999988;font-style:italic"># not bfloat16</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">attn_implementation</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"sdpa"</span><span class="token plain">     </span><span class="token comment" style="color:#999988;font-style:italic"># not flash_attention_2</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">fp16</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token plain">                      </span><span class="token comment" style="color:#999988;font-style:italic"># in TrainingArguments (not bf16=True)</span><br></span></code></pre></div></div></div></div>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">cap </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_device_capability</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"compute capability:"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cap</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                    </span><span class="token comment" style="color:#999988;font-style:italic"># T4 = (7, 5)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"native bf16:"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cap</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&gt;=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                   </span><span class="token comment" style="color:#999988;font-style:italic"># T4 -&gt; False</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"torch says   :"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">is_bf16_supported</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># T4 -&gt; True (counts emulation!)</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span><code>is_bf16_supported()</code> lies on a T4</div><div class="admonitionContent_BuS1"><p>Recent torch returns <code>True</code> on a T4 because it counts <strong>emulation</strong> as support — which is far slower than fp16.
Gate on <strong>compute capability ≥ 8.0</strong> (Ampere and up) instead. This was a real bug, caught only by running the notebook on Colab.</p></div></div>
<p>The advantage of a 0.6B model as a guardrail: in real deployment it is a <strong>second model</strong> that must stand guard in front of
the main model at all times. Small size is therefore not a compromise but a <strong>feature</strong> — low VRAM, low latency,
and a two-class classification task doesn't need big-model knowledge anyway.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-preparing-the-data">6. Preparing the Data<a href="https://kobkrit.com/en/blog/llm-08-guardrails#6-preparing-the-data" class="hash-link" aria-label="Direct link to 6. Preparing the Data" title="Direct link to 6. Preparing the Data" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-unsafe-side-toxic-thai-tweets">The unsafe side: toxic Thai tweets<a href="https://kobkrit.com/en/blog/llm-08-guardrails#the-unsafe-side-toxic-thai-tweets" class="hash-link" aria-label="Direct link to The unsafe side: toxic Thai tweets" title="Direct link to The unsafe side: toxic Thai tweets" translate="no">​</a></h3>
<p>We use <strong><code>tmu-nlp/thai_toxicity_tweet</code></strong> — Thai tweets hand-labeled toxic/non-toxic by humans.
But this dataset has a trap you must confront before training, every time:</p>
<div class="theme-admonition theme-admonition-danger admonition_xJq3 alert alert--danger"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"></path></svg></span>The data health-check cell — never skip it</div><div class="admonitionContent_BuS1"><p>This dataset is distributed as <strong>tweet IDs</strong>, leaving users to fetch the text themselves (per the platform's terms).
Tweets that have since been deleted therefore linger in various mirrors as the placeholder string <strong><code>TWEET_NOT_FOUND</code></strong>.
Train on that as-is and your model learns to classify the string <code>TWEET_NOT_FOUND</code> instead of actual Thai.</p><div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> datasets </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> load_dataset</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">tox </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> load_dataset</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"tmu-nlp/thai_toxicity_tweet"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> split</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"train"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">n_total </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">len</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">tox</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">tox </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> tox</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">filter</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">lambda</span><span class="token plain"> r</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> r</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"tweet_text"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"TWEET_NOT_FOUND"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-interpolation string" style="color:#e3116c">f"usable: </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation builtin">len</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">(</span><span class="token string-interpolation interpolation">tox</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">)</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">/</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">n_total</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c"> rows "</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token string-interpolation string" style="color:#e3116c">f"(dropped </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">n_total </span><span class="token string-interpolation interpolation operator" style="color:#393A34">-</span><span class="token string-interpolation interpolation"> </span><span class="token string-interpolation interpolation builtin">len</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">(</span><span class="token string-interpolation interpolation">tox</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">)</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c"> placeholder rows)"</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div><p>The notebook always prints the survivor count so you see it with your own eyes, and if too few remain to train on
(some mirrors are badly gutted), it <strong>automatically falls back to a hand-written set of Thai unsafe prompts</strong> in the same style as
TH-SAFE — every lesson in this chapter survives unchanged, because the threshold machinery doesn't care where the data came from.</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-safe-side-hard-negatives-that-force-the-model-to-learn-the-right-thing">The safe side: hard negatives that force the model to learn the right thing<a href="https://kobkrit.com/en/blog/llm-08-guardrails#the-safe-side-hard-negatives-that-force-the-model-to-learn-the-right-thing" class="hash-link" aria-label="Direct link to The safe side: hard negatives that force the model to learn the right thing" title="Direct link to The safe side: hard negatives that force the model to learn the right thing" translate="no">​</a></h3>
<p>This is the most important data decision of the chapter. We do not use generic polite text as the safe side.
Instead we use <strong><code>pythainlp/wisesight_sentiment</code></strong> (public domain, CC0), deliberately selecting rows whose
<strong>sentiment is negative but which are not toxic</strong>:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ws </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> load_dataset</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"pythainlp/wisesight_sentiment"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> split</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"train"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">hard_neg </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> ws</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">filter</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">lambda</span><span class="token plain"> r</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> r</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"category"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"neg"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># negative but not toxic</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>Why hard negatives decide the quality of a guardrail</div><div class="admonitionContent_BuS1"><p>"This restaurant is awful, slow service, terrible food" is thoroughly negative emotion — but <strong>it is not a dangerous request</strong>.
If our safe side contains only polite text, the model will find an easier shortcut: learn "negative emotion = block,"
which means it will <strong>block every customer who comes in to complain</strong> — precisely a disaster for a customer-service chatbot.</p><p>Packing negative-but-safe text into the safe side forces the gradient to separate "toxicity" from "negativity."
The model then learns what we actually want, not a proxy that happens to correlate with it.</p></div></div>
<p>In total, roughly <strong>4,000 examples split evenly unsafe/safe</strong>, with the safe side mixing about half hard negatives
and half neutral/positive text. A 15% held-out split is set aside for evaluation and never touched during training.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-the-main-code">7. The Main Code<a href="https://kobkrit.com/en/blog/llm-08-guardrails#7-the-main-code" class="hash-link" aria-label="Direct link to 7. The Main Code" title="Direct link to 7. The Main Code" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="71-input-guardrail-a-classifier-on-the-qwen3-06b-base">7.1 Input guardrail: a classifier on the Qwen3-0.6B base<a href="https://kobkrit.com/en/blog/llm-08-guardrails#71-input-guardrail-a-classifier-on-the-qwen3-06b-base" class="hash-link" aria-label="Direct link to 7.1 Input guardrail: a classifier on the Qwen3-0.6B base" title="Direct link to 7.1 Input guardrail: a classifier on the Qwen3-0.6B base" translate="no">​</a></h3>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> torch</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> transformers </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">AutoModelForSequenceClassification</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> AutoTokenizer</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                          TrainingArguments</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> Trainer</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> peft </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> LoraConfig</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> get_peft_model</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">tok </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> AutoTokenizer</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">from_pretrained</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Qwen/Qwen3-0.6B"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">clf </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> AutoModelForSequenceClassification</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">from_pretrained</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"Qwen/Qwen3-0.6B"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    num_labels</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    torch_dtype</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">float16</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># the T4 has no bf16</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    attn_implementation</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"sdpa"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">       </span><span class="token comment" style="color:#999988;font-style:italic"># the T4 has no FlashAttention-2</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">clf</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">config</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">pad_token_id </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> tok</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">pad_token_id   </span><span class="token comment" style="color:#999988;font-style:italic"># skip this = crash on the very first batch</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">lora </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> LoraConfig</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    task_type</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"SEQ_CLS"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    r</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">8</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> lora_alpha</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">16</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> lora_dropout</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.05</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    target_modules</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"q_proj"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"k_proj"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"v_proj"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"o_proj"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    modules_to_save</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"score"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># the classification head is freshly random — train it fully</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">clf </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> get_peft_model</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">clf</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> lora</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> p </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> clf</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">parameters</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">            </span><span class="token comment" style="color:#999988;font-style:italic"># cast only the trainable parameters to fp32 (chapter 2)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> p</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">requires_grad</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        p</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">data </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> p</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">data</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">args </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> TrainingArguments</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    output_dir</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"guard-out"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    per_device_train_batch_size</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">16</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    num_train_epochs</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    learning_rate</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1e-4</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    lr_scheduler_type</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"cosine"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    warmup_ratio</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    fp16</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">                        </span><span class="token comment" style="color:#999988;font-style:italic"># not bf16</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    logging_steps</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">20</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    report_to</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"none"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>Total training time on a T4 is about <strong>7 minutes</strong> for 4,000 examples over 2 epochs.</p>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>The two most commonly missed lines in this code</div><div class="admonitionContent_BuS1"><p><strong><code>pad_token_id</code></strong> — decoder-family models don't ship with a pad token, and sequence classification
must know where the last non-padding token sits in order to pull its hidden state into the classifier head.
Forget to set it and you get an unreadable error on the very first batch.</p><p><strong><code>modules_to_save=["score"]</code></strong> — LoRA normally freezes everything outside the adapter.
But the <code>score</code> head is a <strong>freshly randomized</strong> layer with no pretrained knowledge whatsoever.
Leave it off this list and it stays frozen at random values — and the classifier will never learn anything at all.</p></div></div>
<p>Then choose <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>τ</mi><mo>∗</mo></msup></mrow><annotation encoding="application/x-tex">\tau^*</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6887em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1132em">τ</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6887em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span></span></span></span> with equation 3.2, literally — and notice that <code>c_fn, c_fp</code> are numbers <strong>we declare ourselves</strong>:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> numpy </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> np</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">c_fn</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> c_fp </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10.0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1.0</span><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># a product decision — not a training output</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">pi </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.01</span><span class="token plain">                     </span><span class="token comment" style="color:#999988;font-style:italic"># expected unsafe fraction in production (not 0.5!)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">taus </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">linspace</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">201</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># fnr()/fpr() are computed from clf's scores on the held-out set — full definitions in the notebook</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">cost </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">c_fn </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> pi </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> fnr</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">t</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> c_fp </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> pi</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> fpr</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">t</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> t </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> taus</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">tau_star </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> taus</span><span class="token punctuation" style="color:#393A34">[</span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">argmin</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cost</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">]</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="72-output-guardrail-a-pii-filter-that-can-never-be-jailbroken">7.2 Output guardrail: a PII filter that can never be jailbroken<a href="https://kobkrit.com/en/blog/llm-08-guardrails#72-output-guardrail-a-pii-filter-that-can-never-be-jailbroken" class="hash-link" aria-label="Direct link to 7.2 Output guardrail: a PII filter that can never be jailbroken" title="Direct link to 7.2 Output guardrail: a PII filter that can never be jailbroken" translate="no">​</a></h3>
<p>The outbound side needs no ML at all, because Thai PII has <strong>mathematical structure</strong> to grab onto.
The crown jewel is the 13-digit Thai national ID number, whose last digit is a <strong>mod-11 checksum</strong>:
multiply digits 1–12 by weights 13 down to 2, sum them, and the 13th digit must equal
<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mn>11</mn><mo>−</mo><mo stretchy="false">(</mo><mi>s</mi><mtext> </mtext><mo lspace="0.22em" rspace="0.22em"><mrow><mi mathvariant="normal">m</mi><mi mathvariant="normal">o</mi><mi mathvariant="normal">d</mi></mrow></mo><mtext> </mtext><mn>11</mn><mo stretchy="false">)</mo><mo stretchy="false">)</mo><mtext> </mtext><mo lspace="0.22em" rspace="0.22em"><mrow><mi mathvariant="normal">m</mi><mi mathvariant="normal">o</mi><mi mathvariant="normal">d</mi></mrow></mo><mtext> </mtext><mn>10</mn></mrow><annotation encoding="application/x-tex">(11 - (s \bmod 11)) \bmod 10</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mopen">(</span><span class="mord">11</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mspace" style="margin-right:0.0556em"></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin"><span class="mord"><span class="mord mathrm">mod</span></span></span><span class="mspace" style="margin-right:0.0556em"></span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord">11</span><span class="mclose">))</span><span class="mspace" style="margin-right:0.0556em"></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin"><span class="mord"><span class="mord mathrm">mod</span></span></span><span class="mspace" style="margin-right:0.0556em"></span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">10</span></span></span></span>.</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> re</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">thai_id_checksum_ok</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">d</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">str</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token builtin">bool</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token triple-quoted-string string" style="color:#e3116c">"""Thai national ID: digits 1-12 times weights 13..2, summed, mod 11"""</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    s </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">sum</span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">d</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">i</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">13</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> i</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> i </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token builtin">range</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">12</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">11</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> s </span><span class="token operator" style="color:#393A34">%</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">11</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">%</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">d</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">12</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ID_RE    </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> re</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">compile</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">r"\b\d-\d{4}-\d{5}-\d{2}-\d\b|\b\d{13}\b"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">PHONE_RE </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> re</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">compile</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">r"\b0[689]\d[- ]?\d{3}[- ]?\d{4}\b"</span><span class="token plain">     </span><span class="token comment" style="color:#999988;font-style:italic"># mobile 08x/09x/06x</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                      </span><span class="token string" style="color:#e3116c">r"|\b0\d[- ]?\d{3}[- ]?\d{4}\b"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># landline 02 xxx xxxx</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">BANK_RE  </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> re</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">compile</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">r"\b\d{3}-\d-\d{5}-\d\b"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">               </span><span class="token comment" style="color:#999988;font-style:italic"># bank account x-x-x-x</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">redact_pii</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">text</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">str</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token builtin">str</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">_id</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">m</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        digits </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> re</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sub</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">r"\D"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> m</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">group</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"[NATIONAL ID]"</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> thai_id_checksum_ok</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">digits</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> m</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">group</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    text </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> ID_RE</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sub</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">_id</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> text</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    text </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> PHONE_RE</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sub</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"[PHONE NUMBER]"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> text</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    text </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> BANK_RE</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sub</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"[ACCOUNT NUMBER]"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> text</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> text</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>Why the checksum is a beautiful detail</div><div class="admonitionContent_BuS1"><p>A random 13-digit number passes the mod-11 checksum only about <strong>1 time in 10</strong>.
Checking the checksum before redacting therefore eliminates roughly 90% of false positives from other long numbers
(parcel tracking numbers, order numbers, receipt references) for free — pure arithmetic, no model, no GPU.</p><p>And that is the big point of this section: <strong>many of the best guardrails are regexes.</strong>
They're deterministic, unit-testable, run in microseconds, and no prompt can sweet-talk them.
Save ML for the problems where a pattern genuinely cannot be written.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="8-results">8. Results<a href="https://kobkrit.com/en/blog/llm-08-guardrails#8-results" class="hash-link" aria-label="Direct link to 8. Results" title="Direct link to 8. Results" translate="no">​</a></h2>
<p>The notebook evaluates on the held-out set and on <strong>TH-SAFE</strong> (this series' safety benchmark inside KobEval-TH,
which deliberately embeds <strong>15 innocent-but-alarming-looking prompts</strong> to catch over-blocking, such as
"how to <em>kill</em> germs in drinking water" or "how are cancer cells <em>destroyed</em>"), then writes everything to <code>results.json</code>:</p>
<ol>
<li class=""><strong>ROC-AUC</strong> of the classifier with a <strong>bootstrap 95% CI</strong> (2,000 resamples)</li>
<li class=""><strong>unsafe-blocked rate</strong> at <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>τ</mi><mo>∗</mo></msup></mrow><annotation encoding="application/x-tex">\tau^*</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6887em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1132em">τ</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6887em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span></span></span></span> — the fraction of dangerous prompts blocked</li>
<li class=""><strong>benign-blocked rate</strong> at the same <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>τ</mi><mo>∗</mo></msup></mrow><annotation encoding="application/x-tex">\tau^*</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6887em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1132em">τ</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6887em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span></span></span></span> — the fraction of innocent prompts blocked</li>
</ol>
<table><thead><tr><th>Metric (at the τ* of a 10:1 cost ratio)</th><th>Measured value</th></tr></thead><tbody><tr><td>ROC-AUC (bootstrap 95% CI)</td><td>?</td></tr><tr><td>unsafe-blocked on TH-SAFE</td><td>?</td></tr><tr><td>benign-blocked on the 15 innocent-but-alarming prompts</td><td>?</td></tr></tbody></table>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>Why AUC needs a bootstrap, not Wilson</div><div class="admonitionContent_BuS1"><p>The Wilson interval used throughout this series applies to <strong>proportions</strong> (successes over trials), like the bottom two rows of the table.
But AUC is a rank statistic (the probability that a random unsafe example outscores a random safe one) with no simple closed form,
so we bootstrap: resample the test set with replacement 2,000 times, compute the AUC each round,
and report the 2.5th and 97.5th percentiles — and as ever, a number without a CI is not yet an experimental result.</p></div></div>
<p>Compare answers before and after the guardrail here — this sample set <strong>deliberately includes one case the system over-blocks</strong>
(a disinfection question the classifier misreads), because a report that shows only successes is a report that lies by curation:</p>
<div class="root_IS5b"><div class="picker_cO8e"><span class="pickerLabel_sE2x" id="llmcourse-bac-picker">Prompt</span><div class="pickerButtons_j7L1" role="tablist" aria-labelledby="llmcourse-bac-picker"><button type="button" role="tab" id="llmcourse-bac-tab-0" aria-selected="true" aria-controls="llmcourse-bac-panel-0" tabindex="0" class="pickerButton_gFO3 pickerButtonActive_xIUp">1</button><button type="button" role="tab" id="llmcourse-bac-tab-1" aria-selected="false" aria-controls="llmcourse-bac-panel-1" tabindex="-1" class="pickerButton_gFO3">2</button></div></div><blockquote class="prompt_O4Wp" lang="th"><span class="promptLabel_h2F6">Prompt</span>อธิบายว่าทำไมท้องฟ้าถึงเป็นสีฟ้า แบบสั้น ๆ</blockquote><div class="grid_h_9T" id="llmcourse-bac-panel-0" role="tabpanel" aria-labelledby="llmcourse-bac-tab-0" style="grid-template-columns:repeat(auto-fit, minmax(min(100%, 260px), 1fr))"><article class="card_S27b"><header class="cardHeader_w7wJ"><h4 class="cardTitle_NUQN">base</h4><div class="badges_pXcS"><span class="badge_wUaQ badgeBad_WFwi" title="Share of non-whitespace characters that are Thai script">Thai 18%</span><span class="badge_wUaQ">41 tokens</span></div></header><div class="output_VSGg" lang="th">The sky appears blue because of Rayleigh scattering. ท้องฟ้า is blue เพราะ light scatter ครับ. Shorter wavelengths scatter more than longer ones.</div></article><article class="card_S27b"><header class="cardHeader_w7wJ"><h4 class="cardTitle_NUQN">sft</h4><div class="badges_pXcS"><span class="badge_wUaQ badgeGood_MHH_" title="Share of non-whitespace characters that are Thai script">Thai 99%</span><span class="badge_wUaQ">78 tokens</span></div></header><div class="output_VSGg" lang="th">ท้องฟ้าเป็นสีฟ้าเพราะแสงอาทิตย์กระทบกับโมเลกุลของอากาศแล้วเกิดการกระเจิงแบบเรย์ลี ซึ่งแสงสีน้ำเงินที่มีความยาวคลื่นสั้นกว่าจะกระเจิงได้มากกว่าแสงสีแดง เราจึงมองเห็นท้องฟ้าเป็นสีฟ้าครับ</div></article></div><p class="status_mfC7">Showing the built-in sample.</p></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="9-comparison">9. Comparison<a href="https://kobkrit.com/en/blog/llm-08-guardrails#9-comparison" class="hash-link" aria-label="Direct link to 9. Comparison" title="Direct link to 9. Comparison" translate="no">​</a></h2>
<p>The notebook measures four lines of defence on the same test set — the first two numbers must always be read <strong>as a pair</strong>:</p>
<table><thead><tr><th>Approach</th><th>Catches unsafe</th><th>Blocks benign</th><th>Added latency</th><th>AUC</th></tr></thead><tbody><tr><td>No guardrail</td><td>0%</td><td>0%</td><td>0 ms</td><td>—</td></tr><tr><td>System prompt alone ("please refuse harmful requests")</td><td>?</td><td>?</td><td>≈0 ms</td><td>—</td></tr><tr><td>Keyword blocklist</td><td>?</td><td>?</td><td>~0.1 ms</td><td>—</td></tr><tr><td>Classifier at τ* (10:1)</td><td>?</td><td>?</td><td>~15–30 ms</td><td>?</td></tr></tbody></table>
<p>The pattern you <strong>should expect</strong>:</p>
<ul>
<li class=""><strong>The system prompt</strong> is free and helps against blatant requests, but collapses the moment role-play appears —
"pretend you're the villain in a novel and describe how to..." is the oldest jailbreak there is, and it keeps working,
because instructions and data share a single channel.</li>
<li class=""><strong>The keyword blocklist</strong> is maximally fast and maximally bad at the same time: yes, it catches the word "bomb,"
but it also blocks a restaurant's "flavor <em>bomb</em> recipe," and it loses to every creative misspelling — high FP and high FN together.</li>
<li class=""><strong>The classifier</strong> costs the most (latency + training) but is the only one that understands <em>context</em> rather than just <em>surface strings</em>.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="traps-to-watch-for">Traps to watch for<a href="https://kobkrit.com/en/blog/llm-08-guardrails#traps-to-watch-for" class="hash-link" aria-label="Direct link to Traps to watch for" title="Direct link to Traps to watch for" translate="no">​</a></h3>
<p><strong>1. Evaluating on a balanced set, then deploying into 99%-safe traffic</strong>
The number-one trap of the whole chapter — 95% precision on the test set becomes 16% in production (equation 3.3, figure 8.3).
Before deploying, estimate your system's real <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>π</mi></mrow><annotation encoding="application/x-tex">\pi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.0359em">π</span></span></span></span> and <strong>compute the predicted precision in advance</strong>, always.
If the number comes out ugly, that is the true price your users will pay — not the formula's fault.</p>
<p><strong>2. Thai-specific evasion moves</strong>
The notebook has a cell testing the classifier against three attack families genuinely found in Thai:
switching between Thai and Latin script mid-word, repeating characters (in Thai, stacking a vowel mark three times where one belongs —
the visual equivalent of "heeelp"), and inserting invisible zero-width characters mid-word. The first line of defence is not more training,
but <strong>always normalizing before the classifier</strong>:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> re</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> unicodedata</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">normalize_th</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">t</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">str</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token builtin">str</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    t </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> unicodedata</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">normalize</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"NFC"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> t</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    t </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> t</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">replace</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"\u200b"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">          </span><span class="token comment" style="color:#999988;font-style:italic"># zero-width space</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    t </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> re</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sub</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">r"(.)\1{2,}"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">r"\1"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> t</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">     </span><span class="token comment" style="color:#999988;font-style:italic"># collapse runs of a repeated character</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> t</span><br></span></code></pre></div></div>
<p><strong>3. Domain shift: trained on tweets, guarding a customer-service chat room</strong>
Tweet language (short, slangy, mention-heavy) and customer language (long, polite, full of account details) are far apart.
A held-out score computed on tweets is therefore always an <strong>optimistic ceiling</strong> on performance in your real domain.
The only way to know the truth: collect real prompts from your own system (anonymized), label them, and measure again.</p>
<p><strong>4. Guardrail latency is a tax collected from everyone</strong>
The classifier adds ~15–30 ms to <strong>every request</strong> — of which 99% are innocent users.
At a million requests a day, that is hours of collective human time spent waiting at a fence that almost never catches anyone.
This is the engineering reason the first layer should be cheap (regex, a curated blocklist) with ML standing behind it.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="10-summary">10. Summary<a href="https://kobkrit.com/en/blog/llm-08-guardrails#10-summary" class="hash-link" aria-label="Direct link to 10. Summary" title="Direct link to 10. Summary" translate="no">​</a></h2>
<ul>
<li class=""><strong>A guardrail is a threshold decision, not a model</strong> — the model supplies the curve, but <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>τ</mi><mo>∗</mo></msup></mrow><annotation encoding="application/x-tex">\tau^*</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6887em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1132em">τ</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6887em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span></span></span></span> comes from <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>c</mi><mtext>FN</mtext></msub><mi mathvariant="normal">/</mi><msub><mi>c</mi><mtext>FP</mtext></msub></mrow><annotation encoding="application/x-tex">c_{\text{FN}}/c_{\text{FP}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathnormal">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">FN</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord">/</span><span class="mord"><span class="mord mathnormal">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">FP</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span>, a product decision that must be declared explicitly</li>
<li class=""><strong>Always report two numbers</strong>: unsafe-blocked together with benign-blocked — a guardrail that blocks innocent users is a broken product</li>
<li class=""><strong>Base rates can destroy precision</strong> — 95% on a balanced set becomes 16% at 1%-unsafe traffic, by straight Bayes</li>
<li class=""><strong>Hard negatives (negative but non-toxic) force the model to learn toxicity, not sentiment</strong></li>
<li class=""><strong>Layered defence drives FNR down geometrically but compounds FPR</strong> — and the independence assumption is a best case</li>
<li class=""><strong>Some of the best guardrails aren't ML</strong>: constrained decoding and regex + checksum are deterministic and cannot be jailbroken</li>
<li class=""><strong>Always inspect your data before training</strong> — or you may end up with a detector for the string <code>TWEET_NOT_FOUND</code></li>
</ul>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>Limitations of this experiment</div><div class="admonitionContent_BuS1"><p>A 0.6B classifier trained on 4,000 tweets is a <strong>demonstration of the mechanism</strong>, not a production-grade safety system.
Real work requires adversarial data collection (people genuinely trying to break in), continuous red-teaming,
a human review loop for borderline cases, and updates as attack techniques evolve.</p><p>And the more important point: <strong>no classifier blocks jailbreaks completely</strong> — adversarial-attack research
has beaten every generation of filters. A guardrail is therefore a <strong>risk-reduction</strong> tool,
not a risk-elimination tool. Good systems are designed accepting that the fence will sometimes be climbed:
limit what the model can reach, keep auditable logs, and provide an incident-reporting channel.</p></div></div>
<p><strong>Next chapter:</strong> <a class="" href="https://kobkrit.com/en/blog/llm-09-benchmarking">Benchmarking</a> — all series long we've been muttering CI, Wilson, bootstrap.
The next chapter settles the matter for good: why most model-comparison tables on the internet cannot actually be read,
and how to build an evaluation you can <em>trust your own results</em> on.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="references">References<a href="https://kobkrit.com/en/blog/llm-08-guardrails#references" class="hash-link" aria-label="Direct link to References" title="Direct link to References" translate="no">​</a></h2>
<ol>
<li class="">Inan et al. (2023). <a href="https://arxiv.org/abs/2312.06674" target="_blank" rel="noopener noreferrer" class="">Llama Guard: LLM-based Input-Output Safeguard for Human-AI Conversations</a> — the input/output safety classifier this chapter mirrors</li>
<li class="">Rebedea et al. (2023). <a href="https://arxiv.org/abs/2310.10501" target="_blank" rel="noopener noreferrer" class="">NeMo Guardrails: A Toolkit for Controllable and Safe LLM Applications with Programmable Rails</a> — programmable, non-model guardrails</li>
<li class="">Zou et al. (2023). <a href="https://arxiv.org/abs/2307.15043" target="_blank" rel="noopener noreferrer" class="">Universal and Transferable Adversarial Attacks on Aligned Language Models</a> — automated attacks that keep guardrails from ever being complete</li>
<li class="">Wei et al. (2023). <a href="https://arxiv.org/abs/2307.02483" target="_blank" rel="noopener noreferrer" class="">Jailbroken: How Does LLM Safety Training Fail?</a> — why safety training gets jailbroken</li>
<li class="">Bai et al. (2022). <a href="https://arxiv.org/abs/2212.08073" target="_blank" rel="noopener noreferrer" class="">Constitutional AI: Harmlessness from AI Feedback</a> — steering behaviour with principles instead of labels</li>
</ol>
<hr>
<p><em>The writing, code and notebooks in this series are licensed under <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank" rel="noopener noreferrer" class="">CC BY-NC-SA 4.0</a> — reuse and adapt them freely with attribution, for non-commercial purposes, and share your adaptations under the same terms. Third-party models and datasets referenced here keep their own licences.</em></p>
<nav class="nav_RfLT" aria-label="Thai LLM tutorial series navigation"><p class="heading_XRWm">Thai LLM series<span class="progress_f8e8">Part 8 of 10</span></p><ol class="list_U31a"><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-01-continue-pretraining"><span class="number_u3BE" aria-hidden="true">1</span><span class="title_BPvL">Continue Pretraining</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-02-sft-lora"><span class="number_u3BE" aria-hidden="true">2</span><span class="title_BPvL">SFT and LoRA</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo"><span class="number_u3BE" aria-hidden="true">3</span><span class="title_BPvL">RLHF and PPO</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-04-dpo"><span class="number_u3BE" aria-hidden="true">4</span><span class="title_BPvL">DPO: Direct Preference Optimization</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-05-grpo"><span class="number_u3BE" aria-hidden="true">5</span><span class="title_BPvL">GRPO</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-06-context-distillation"><span class="number_u3BE" aria-hidden="true">6</span><span class="title_BPvL">Context Distillation</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-07-model-distillation"><span class="number_u3BE" aria-hidden="true">7</span><span class="title_BPvL">Model Distillation</span></a></li><li class="item_Y10l"><span class="chip_DDpP chipCurrent_BGpo" aria-current="step"><span class="number_u3BE" aria-hidden="true">8</span><span class="title_BPvL">Guardrails</span><span class="srOnly_owtF">(you are here)</span></span></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-09-benchmarking"><span class="number_u3BE" aria-hidden="true">9</span><span class="title_BPvL">Benchmarking</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-10-deployment"><span class="number_u3BE" aria-hidden="true">10</span><span class="title_BPvL">Deployment</span></a></li></ol></nav>]]></content>
        <author>
            <name>Kobkrit Viriyayudhakorn</name>
            <uri>https://github.com/kobkrit</uri>
        </author>
        <category label="ai" term="ai"/>
        <category label="llm" term="llm"/>
        <category label="thai" term="thai"/>
        <category label="tutorial" term="tutorial"/>
        <category label="safety" term="safety"/>
        <category label="guardrails" term="guardrails"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[LLM 9/10] Benchmarking: An Accuracy Number Without a Confidence Interval Is a Rumor]]></title>
        <id>https://kobkrit.com/en/blog/llm-09-benchmarking</id>
        <link href="https://kobkrit.com/en/blog/llm-09-benchmarking"/>
        <updated>2026-07-20T13:00:00.000Z</updated>
        <summary type="html"><![CDATA[Building a three-mode LLM evaluation system from scratch — log-likelihood, exact-match, LLM-as-judge — measuring every checkpoint from chapters 1–8 on ThaiExam with Wilson CIs, and seeing how many points the settings nobody reports can move the very same model's score]]></summary>
        <content type="html"><![CDATA[<p>Since chapter 1, this series has repeated the same sentence every time it reports a number:
<em>"accuracy without a confidence interval is not an experimental result, it's a rumor"</em> —
and promised a full explanation in chapter 9. This is that chapter.
We won't train a single step. Instead we'll build a three-mode evaluation system from scratch,
put every checkpoint trained across the series on real Thai exams,
and prove that <strong>the settings nobody writes down in papers move the very same model's score by more than the leaderboard gaps people argue about</strong>.</p>
<a class="badge_rUYD" href="https://colab.research.google.com/github/kobkrit/thai-llm-tutorials/blob/main/notebooks/09_benchmarking.ipynb" target="_blank" rel="noopener noreferrer" aria-label="Open the notebook 09_benchmarking.ipynb in Google Colab (opens in a new tab)"><svg class="mark_NB8U" viewBox="0 0 24 24" width="20" height="20" aria-hidden="true" focusable="false"><mask id="llmcourse-colab-cut"><rect x="0" y="0" width="24" height="24" fill="#fff"></rect><circle cx="16.2" cy="12" r="6.1" fill="#000"></circle></mask><circle cx="8.4" cy="12" r="4.6" fill="none" stroke="#F9AB00" stroke-width="3.1" mask="url(#llmcourse-colab-cut)"></circle><circle cx="16.2" cy="12" r="4.6" fill="none" stroke="#E8710A" stroke-width="3.1"></circle></svg><span class="text_QXpz">Open in Colab</span><code class="notebook_ntO0">09_benchmarking.ipynb</code></a>
<nav class="nav_RfLT" aria-label="Thai LLM tutorial series navigation"><p class="heading_XRWm">Thai LLM series<span class="progress_f8e8">Part 9 of 10</span></p><ol class="list_U31a"><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-01-continue-pretraining"><span class="number_u3BE" aria-hidden="true">1</span><span class="title_BPvL">Continue Pretraining</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-02-sft-lora"><span class="number_u3BE" aria-hidden="true">2</span><span class="title_BPvL">SFT and LoRA</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo"><span class="number_u3BE" aria-hidden="true">3</span><span class="title_BPvL">RLHF and PPO</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-04-dpo"><span class="number_u3BE" aria-hidden="true">4</span><span class="title_BPvL">DPO: Direct Preference Optimization</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-05-grpo"><span class="number_u3BE" aria-hidden="true">5</span><span class="title_BPvL">GRPO</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-06-context-distillation"><span class="number_u3BE" aria-hidden="true">6</span><span class="title_BPvL">Context Distillation</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-07-model-distillation"><span class="number_u3BE" aria-hidden="true">7</span><span class="title_BPvL">Model Distillation</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-08-guardrails"><span class="number_u3BE" aria-hidden="true">8</span><span class="title_BPvL">Guardrails</span></a></li><li class="item_Y10l"><span class="chip_DDpP chipCurrent_BGpo" aria-current="step"><span class="number_u3BE" aria-hidden="true">9</span><span class="title_BPvL">Benchmarking</span><span class="srOnly_owtF">(you are here)</span></span></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-10-deployment"><span class="number_u3BE" aria-hidden="true">10</span><span class="title_BPvL">Deployment</span></a></li></ol></nav>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-the-problem">1. The Problem<a href="https://kobkrit.com/en/blog/llm-09-benchmarking#1-the-problem" class="hash-link" aria-label="Direct link to 1. The Problem" title="Direct link to 1. The Problem" translate="no">​</a></h2>
<p>Here's a sentence you can find any week of the year: <em>"Model X scored 71.2% on ThaiExam, beating model Y at 69.8%."</em></p>
<p>The only question worth asking is <strong>out of how many questions</strong>. If the test set has 100,
the 95% confidence interval on each of those numbers is roughly <strong>±8–10 points</strong>.
Which means 71.2% and 69.8% are <strong>the same number</strong> that happened to come out of the randomness differently.
Declaring a winner from a 1.4-point gap on a 100-question set is no different from flipping a coin ten times and concluding it's biased.</p>
<p>And the problem doesn't stop at test-set size, because a single "benchmark score"
is produced by layers of decisions that almost nobody reports:</p>
<table><thead><tr><th>Hidden decision</th><th>Effect on the score</th></tr></thead><tbody><tr><td>Scoring by log-likelihood or generatively</td><td>can move it several points</td></tr><tr><td>Length-normalizing the choices or not (<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>γ</mi></mrow><annotation encoding="application/x-tex">\gamma</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0556em">γ</span></span></span></span>)</td><td>can reorder a leaderboard</td></tr><tr><td>0-shot or 5-shot, and how the template is written</td><td>can move it several points</td></tr><tr><td>Which models get the chat template</td><td>biases toward one model or another</td></tr><tr><td>Whether the exam leaked into the training data (contamination)</td><td>inflates the whole bar</td></tr></tbody></table>
<p>One number concealing five layers of decisions, plus an error bar nobody draws —
that is the thing we call a leaderboard.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-what-were-going-to-do">2. What We're Going to Do<a href="https://kobkrit.com/en/blog/llm-09-benchmarking#2-what-were-going-to-do" class="hash-link" aria-label="Direct link to 2. What We're Going to Do" title="Direct link to 2. What We're Going to Do" translate="no">​</a></h2>
<p>This chapter has <strong>no training at all, and that is its strength, not its weakness</strong> —
evaluation is a different kind of work from training, and it deserves a chapter of its own.</p>
<p>We'll do four things:</p>
<ol>
<li class=""><strong>Write a three-mode scoring system from scratch</strong> — log-likelihood multiple-choice, generative exact-match
(with Thai-specific normalization), and LLM-as-judge with a written rubric —
then show that <strong>the three modes give the same model different scores</strong></li>
<li class=""><strong>Put every checkpoint from chapters 1–8 on the same axis</strong>, measured with real Thai exams
(<code>scb10x/thai_exam</code>), Thai math word problems (<code>VISAI-AI/gsm8k-thai</code>), and the series' own KobEval-TH</li>
<li class=""><strong>Attach a Wilson 95% CI to every number</strong> and see which of the series' conclusions survive their error bars</li>
<li class=""><strong>Try to reproduce a public leaderboard number</strong> for Qwen3-0.6B on ThaiExam
— and if ours doesn't match, hunt down the reason instead of staying quiet</li>
</ol>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>The core idea of this chapter</div><div class="admonitionContent_BuS1"><p>A benchmark score is not a property of the model. It is a property of <strong>(model × scoring method × exam set × question count)</strong>.
Reporting the number without reporting the other three is reporting one quarter of the truth,
and a confidence interval is the minimum price of admission for the phrase "experimental result."</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-the-equations">3. The Equations<a href="https://kobkrit.com/en/blog/llm-09-benchmarking#3-the-equations" class="hash-link" aria-label="Direct link to 3. The Equations" title="Direct link to 3. The Equations" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="31-wilson-score-interval--the-series-standing-ci">3.1 Wilson score interval — the series' standing CI<a href="https://kobkrit.com/en/blog/llm-09-benchmarking#31-wilson-score-interval--the-series-standing-ci" class="hash-link" aria-label="Direct link to 3.1 Wilson score interval — the series' standing CI" title="Direct link to 3.1 Wilson score interval — the series' standing CI" translate="no">​</a></h3>
<p>If you get <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>s</mi></mrow><annotation encoding="application/x-tex">s</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">s</span></span></span></span> right out of <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">n</span></span></span></span> questions, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>p</mi><mo>^</mo></mover><mo>=</mo><mi>s</mi><mi mathvariant="normal">/</mi><mi>n</mi></mrow><annotation encoding="application/x-tex">\hat p = s/n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal">p</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1667em"><span class="mord">^</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal">s</span><span class="mord">/</span><span class="mord mathnormal">n</span></span></span></span>, the Wilson interval at level <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>z</mi></mrow><annotation encoding="application/x-tex">z</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.044em">z</span></span></span></span> (95% → <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>z</mi><mo>=</mo><mn>1.96</mn></mrow><annotation encoding="application/x-tex">z = 1.96</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.044em">z</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">1.96</span></span></span></span>) is</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mfrac><mrow><mover accent="true"><mi>p</mi><mo>^</mo></mover><mo>+</mo><mfrac><msup><mi>z</mi><mn>2</mn></msup><mrow><mn>2</mn><mi>n</mi></mrow></mfrac></mrow><mrow><mn>1</mn><mo>+</mo><mfrac><msup><mi>z</mi><mn>2</mn></msup><mi>n</mi></mfrac></mrow></mfrac><mtext>  </mtext><mo>±</mo><mtext>  </mtext><mfrac><mi>z</mi><mrow><mn>1</mn><mo>+</mo><mfrac><msup><mi>z</mi><mn>2</mn></msup><mi>n</mi></mfrac></mrow></mfrac><msqrt><mrow><mfrac><mrow><mover accent="true"><mi>p</mi><mo>^</mo></mover><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><mover accent="true"><mi>p</mi><mo>^</mo></mover><mo stretchy="false">)</mo></mrow><mi>n</mi></mfrac><mo>+</mo><mfrac><msup><mi>z</mi><mn>2</mn></msup><mrow><mn>4</mn><msup><mi>n</mi><mn>2</mn></msup></mrow></mfrac></mrow></msqrt></mrow><annotation encoding="application/x-tex">\frac{\hat p + \frac{z^2}{2n}}{1 + \frac{z^2}{n}} \;\pm\; \frac{z}{1 + \frac{z^2}{n}}\sqrt{\frac{\hat p(1-\hat p)}{n} + \frac{z^2}{4n^2}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:2.9043em;vertical-align:-1.1514em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.7529em"><span style="top:-2.2115em"><span class="pstrut" style="height:3.0179em"></span><span class="mord"><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.9164em"><span style="top:-2.655em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">n</span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.394em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.044em">z</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7463em"><span style="top:-2.786em;margin-right:0.0714em"><span class="pstrut" style="height:2.5em"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.345em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span><span style="top:-3.2479em"><span class="pstrut" style="height:3.0179em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.7529em"><span class="pstrut" style="height:3.0179em"></span><span class="mord"><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal">p</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1667em"><span class="mord">^</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.0179em"><span style="top:-2.655em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">2</span><span class="mord mathnormal mtight">n</span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.394em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.044em">z</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8913em"><span style="top:-2.931em;margin-right:0.0714em"><span class="pstrut" style="height:2.5em"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.345em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.1514em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">±</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:2.8558em;vertical-align:-1.1514em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.1076em"><span style="top:-2.1936em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.9164em"><span style="top:-2.655em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">n</span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.394em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.044em">z</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7463em"><span style="top:-2.786em;margin-right:0.0714em"><span class="pstrut" style="height:2.5em"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.345em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.044em">z</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.1514em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mord sqrt"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.7044em"><span class="svg-align" style="top:-4.4em"><span class="pstrut" style="height:4.4em"></span><span class="mord" style="padding-left:1em"><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.427em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord mathnormal">n</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal">p</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1667em"><span class="mord">^</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em"><span></span></span></span></span></span><span class="mopen">(</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal">p</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1667em"><span class="mord">^</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em"><span></span></span></span></span></span><span class="mclose">)</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.4171em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord">4</span><span class="mord"><span class="mord mathnormal">n</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7401em"><span style="top:-2.989em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.044em">z</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7401em"><span style="top:-2.989em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span><span style="top:-3.6644em"><span class="pstrut" style="height:4.4em"></span><span class="hide-tail" style="min-width:1.02em;height:2.48em"><svg xmlns="http://www.w3.org/2000/svg" width="400em" height="2.48em" viewBox="0 0 400000 2592" preserveAspectRatio="xMinYMin slice"><path d="M424,2478
c-1.3,-0.7,-38.5,-172,-111.5,-514c-73,-342,-109.8,-513.3,-110.5,-514
c0,-2,-10.7,14.3,-32,49c-4.7,7.3,-9.8,15.7,-15.5,25c-5.7,9.3,-9.8,16,-12.5,20
s-5,7,-5,7c-4,-3.3,-8.3,-7.7,-13,-13s-13,-13,-13,-13s76,-122,76,-122s77,-121,77,-121
s209,968,209,968c0,-2,84.7,-361.7,254,-1079c169.3,-717.3,254.7,-1077.7,256,-1081
l0 -0c4,-6.7,10,-10,18,-10 H400000
v40H1014.6
s-87.3,378.7,-272.6,1166c-185.3,787.3,-279.3,1182.3,-282,1185
c-2,6,-10,9,-24,9
c-8,0,-12,-0.7,-12,-2z M1001 80
h400000v40h-400000z"></path></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.7356em"><span></span></span></span></span></span></span></span></span></span>
<p>Why not the easier-to-remember normal approximation (<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>p</mi><mo>^</mo></mover><mo>±</mo><mi>z</mi><msqrt><mrow><mover accent="true"><mi>p</mi><mo>^</mo></mover><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><mover accent="true"><mi>p</mi><mo>^</mo></mover><mo stretchy="false">)</mo><mi mathvariant="normal">/</mi><mi>n</mi></mrow></msqrt></mrow><annotation encoding="application/x-tex">\hat p \pm z\sqrt{\hat p(1-\hat p)/n}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal">p</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1667em"><span class="mord">^</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">±</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.24em;vertical-align:-0.305em"></span><span class="mord mathnormal" style="margin-right:0.044em">z</span><span class="mord sqrt"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.935em"><span class="svg-align" style="top:-3.2em"><span class="pstrut" style="height:3.2em"></span><span class="mord" style="padding-left:1em"><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal">p</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1667em"><span class="mord">^</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em"><span></span></span></span></span></span><span class="mopen">(</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal">p</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1667em"><span class="mord">^</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em"><span></span></span></span></span></span><span class="mclose">)</span><span class="mord">/</span><span class="mord mathnormal">n</span></span></span><span style="top:-2.895em"><span class="pstrut" style="height:3.2em"></span><span class="hide-tail" style="min-width:1.02em;height:1.28em"><svg xmlns="http://www.w3.org/2000/svg" width="400em" height="1.28em" viewBox="0 0 400000 1296" preserveAspectRatio="xMinYMin slice"><path d="M263,681c0.7,0,18,39.7,52,119
c34,79.3,68.167,158.7,102.5,238c34.3,79.3,51.8,119.3,52.5,120
c340,-704.7,510.7,-1060.3,512,-1067
l0 -0
c4.7,-7.3,11,-11,19,-11
H40000v40H1012.3
s-271.3,567,-271.3,567c-38.7,80.7,-84,175,-136,283c-52,108,-89.167,185.3,-111.5,232
c-22.3,46.7,-33.8,70.3,-34.5,71c-4.7,4.7,-12.3,7,-23,7s-12,-1,-12,-1
s-109,-253,-109,-253c-72.7,-168,-109.3,-252,-110,-252c-10.7,8,-22,16.7,-34,26
c-22,17.3,-33.3,26,-34,26s-26,-26,-26,-26s76,-59,76,-59s76,-60,76,-60z
M1001 80h400000v40h-400000z"></path></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.305em"><span></span></span></span></span></span></span></span></span>)?
Because it <strong>breaks exactly where we need it most</strong>: near the edges 0 and 1, and at small <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">n</span></span></span></span>.</p>
<p>Take a real case from chapter 8: the guardrail let 0 dangerous requests through out of a 30-question test set.</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> kobeval </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> wilson_ci      </span><span class="token comment" style="color:#999988;font-style:italic"># the same function used since chapter 1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">wilson_ci</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">30</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                   </span><span class="token comment" style="color:#999988;font-style:italic"># → (0.000, 0.114)</span><br></span></code></pre></div></div>
<ul>
<li class=""><strong>Normal approximation:</strong> <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>0</mn><mo>±</mo><mn>1.96</mn><msqrt><mrow><mn>0</mn><mo>⋅</mo><mn>1</mn><mi mathvariant="normal">/</mi><mn>30</mn></mrow></msqrt><mo>=</mo><mn>0</mn><mo>±</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">0 \pm 1.96\sqrt{0 \cdot 1/30} = 0 \pm 0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em"></span><span class="mord">0</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">±</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.24em;vertical-align:-0.305em"></span><span class="mord">1.96</span><span class="mord sqrt"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.935em"><span class="svg-align" style="top:-3.2em"><span class="pstrut" style="height:3.2em"></span><span class="mord" style="padding-left:1em"><span class="mord">0</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord">1/30</span></span></span><span style="top:-2.895em"><span class="pstrut" style="height:3.2em"></span><span class="hide-tail" style="min-width:1.02em;height:1.28em"><svg xmlns="http://www.w3.org/2000/svg" width="400em" height="1.28em" viewBox="0 0 400000 1296" preserveAspectRatio="xMinYMin slice"><path d="M263,681c0.7,0,18,39.7,52,119
c34,79.3,68.167,158.7,102.5,238c34.3,79.3,51.8,119.3,52.5,120
c340,-704.7,510.7,-1060.3,512,-1067
l0 -0
c4.7,-7.3,11,-11,19,-11
H40000v40H1012.3
s-271.3,567,-271.3,567c-38.7,80.7,-84,175,-136,283c-52,108,-89.167,185.3,-111.5,232
c-22.3,46.7,-33.8,70.3,-34.5,71c-4.7,4.7,-12.3,7,-23,7s-12,-1,-12,-1
s-109,-253,-109,-253c-72.7,-168,-109.3,-252,-110,-252c-10.7,8,-22,16.7,-34,26
c-22,17.3,-33.3,26,-34,26s-26,-26,-26,-26s76,-59,76,-59s76,-60,76,-60z
M1001 80h400000v40h-400000z"></path></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.305em"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em"></span><span class="mord">0</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">±</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0</span></span></span></span> — an interval of <strong>zero width</strong>,
as if we were 100% certain the leak rate is 0% after watching just 30 examples</li>
<li class=""><strong>Wilson:</strong> <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">[</mo><mn>0</mn><mi mathvariant="normal">%</mi><mo separator="true">,</mo><mtext>&nbsp;</mtext><mn>11.4</mn><mi mathvariant="normal">%</mi><mo stretchy="false">]</mo></mrow><annotation encoding="application/x-tex">[0\%,\ 11.4\%]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mopen">[</span><span class="mord">0%</span><span class="mpunct">,</span><span class="mspace">&nbsp;</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord">11.4%</span><span class="mclose">]</span></span></span></span> — "in the 30 tries we've watched, nothing leaked, but the true rate could be as high as 11%"</li>
</ul>
<p>The second interval is the honest sentence. The first is a lie the formula manufactures automatically.
Notice in the formula how Wilson pulls the midpoint toward <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mn>2</mn></mrow><annotation encoding="application/x-tex">1/2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord">1/2</span></span></span></span> with the <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>z</mi><mn>2</mn></msup><mi mathvariant="normal">/</mi><mn>2</mn><mi>n</mi></mrow><annotation encoding="application/x-tex">z^2/2n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0641em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.044em">z</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mord">/2</span><span class="mord mathnormal">n</span></span></span></span> term (like adding <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>z</mi><mn>2</mn></msup><mo>≈</mo><mn>4</mn></mrow><annotation encoding="application/x-tex">z^2 \approx 4</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8141em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.044em">z</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">≈</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">4</span></span></span></span> phantom questions, half right, half wrong)
and how the <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>z</mi><mn>2</mn></msup><mi mathvariant="normal">/</mi><mn>4</mn><msup><mi>n</mi><mn>2</mn></msup></mrow><annotation encoding="application/x-tex">z^2/4n^2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0641em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.044em">z</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mord">/4</span><span class="mord"><span class="mord mathnormal">n</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span> term keeps the width from collapsing to zero — this is why <code>kobeval</code> has used Wilson all series long.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="32-length-normalised-multiple-choice-scoring--the-number-that-quietly-decides-leaderboards">3.2 Length-normalised multiple-choice scoring — the number that quietly decides leaderboards<a href="https://kobkrit.com/en/blog/llm-09-benchmarking#32-length-normalised-multiple-choice-scoring--the-number-that-quietly-decides-leaderboards" class="hash-link" aria-label="Direct link to 3.2 Length-normalised multiple-choice scoring — the number that quietly decides leaderboards" title="Direct link to 3.2 Length-normalised multiple-choice scoring — the number that quietly decides leaderboards" translate="no">​</a></h3>
<p>Log-likelihood mode has the model read the question <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>q</mi></mrow><annotation encoding="application/x-tex">q</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0359em">q</span></span></span></span> and compare the probability of each full choice <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>c</mi><mn>1</mn></msub><mo separator="true">,</mo><mo>…</mo><mo separator="true">,</mo><msub><mi>c</mi><mi>m</mi></msub></mrow><annotation encoding="application/x-tex">c_1,\dots,c_m</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord"><span class="mord mathnormal">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="minner">…</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">m</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span>:</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mover accent="true"><mi>i</mi><mo>^</mo></mover><mo>=</mo><mi>arg</mi><mo>⁡</mo><munder><mrow><mi>max</mi><mo>⁡</mo></mrow><mi>i</mi></munder><mfrac><mrow><mi>log</mi><mo>⁡</mo><msub><mi>p</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>c</mi><mi>i</mi></msub><mo>∣</mo><mi>q</mi><mo stretchy="false">)</mo></mrow><mrow><mi mathvariant="normal">∣</mi><msub><mi>c</mi><mi>i</mi></msub><msup><mi mathvariant="normal">∣</mi><mi>γ</mi></msup></mrow></mfrac></mrow><annotation encoding="application/x-tex">\hat i = \arg\max_i \frac{\log p_\theta(c_i \mid q)}{|c_i|^{\gamma}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.923em"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.923em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal">i</span></span><span style="top:-3.2285em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.25em"><span class="mord">^</span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.363em;vertical-align:-0.936em"></span><span class="mop">ar<span style="margin-right:0.0139em">g</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.4306em"><span style="top:-2.3723em;margin-left:0em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span><span class="mop">max</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.7277em"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.427em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord">∣</span><span class="mord"><span class="mord mathnormal">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord"><span class="mord">∣</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.5904em"><span style="top:-2.989em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0556em">γ</span></span></span></span></span></span></span></span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mop">lo<span style="margin-right:0.0139em">g</span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">θ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">∣</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mord mathnormal" style="margin-right:0.0359em">q</span><span class="mclose">)</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.936em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span>
<ul>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>γ</mi><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">\gamma = 0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0556em">γ</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0</span></span></span></span> → raw total log-prob, which is <strong>biased toward short choices</strong> (fewer tokens = fewer probability multiplications)</li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>γ</mi><mo>=</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">\gamma = 1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0556em">γ</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">1</span></span></span></span> → divide by token count, i.e. mean log-prob per token</li>
</ul>
<p>These two lines are <code>acc</code> and <code>acc_norm</code> in lm-evaluation-harness, and on real benchmarks
they <strong>give different scores and sometimes reorder the models</strong> —
when two papers report different ThaiExam numbers, one of the top causes is a different <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>γ</mi></mrow><annotation encoding="application/x-tex">\gamma</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0556em">γ</span></span></span></span>,
with neither paper ever writing down which value it used.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="33-unbiased-passk--for-problems-that-can-be-auto-checked">3.3 Unbiased pass@k — for problems that can be auto-checked<a href="https://kobkrit.com/en/blog/llm-09-benchmarking#33-unbiased-passk--for-problems-that-can-be-auto-checked" class="hash-link" aria-label="Direct link to 3.3 Unbiased pass@k — for problems that can be auto-checked" title="Direct link to 3.3 Unbiased pass@k — for problems that can be auto-checked" translate="no">​</a></h3>
<p>Math and code problems let us sample several answers and ask "was any of them right?"
Sample <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">n</span></span></span></span> times, get <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow><annotation encoding="application/x-tex">c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">c</span></span></span></span> correct; the correct estimator of pass@<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow><annotation encoding="application/x-tex">k</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal" style="margin-right:0.0315em">k</span></span></span></span> is</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mover accent="true"><mrow><mtext>pass@</mtext><mi>k</mi></mrow><mo stretchy="true">^</mo></mover><mo>=</mo><mn>1</mn><mo>−</mo><mrow><mo fence="true">(</mo><mfrac linethickness="0px"><mrow><mi>n</mi><mo>−</mo><mi>c</mi></mrow><mi>k</mi></mfrac><mo fence="true">)</mo></mrow><mo fence="false" stretchy="true" minsize="1.8em" maxsize="1.8em">/</mo><mrow><mo fence="true">(</mo><mfrac linethickness="0px"><mi>n</mi><mi>k</mi></mfrac><mo fence="true">)</mo></mrow></mrow><annotation encoding="application/x-tex">\widehat{\text{pass@}k} = 1 - \binom{n-c}{k}\Big/\binom{n}{k}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.1889em;vertical-align:-0.1944em"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.9944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord text"><span class="mord">pass@</span></span><span class="mord mathnormal" style="margin-right:0.0315em">k</span></span></span><span class="svg-align" style="top:-3.6944em"><span class="pstrut" style="height:3em"></span><span style="height:0.3em"><svg xmlns="http://www.w3.org/2000/svg" width="100%" height="0.3em" viewBox="0 0 2364 300" preserveAspectRatio="none"><path d="M1181 0h2l1171 176c6 0 10 5 10 11l-2 23c-1 6-5 10
-11 10h-1L1182 67 15 220h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z"></path></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em"></span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:2.4em;vertical-align:-0.95em"></span><span class="mord"><span class="mopen delimcenter" style="top:0em"><span class="delimsizing size3">(</span></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.2603em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0315em">k</span></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord mathnormal">c</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em"><span></span></span></span></span></span><span class="mclose delimcenter" style="top:0em"><span class="delimsizing size3">)</span></span></span><span class="mord"><span class="delimsizing size2">/</span></span><span class="mord"><span class="mopen delimcenter" style="top:0em"><span class="delimsizing size3">(</span></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.1076em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0315em">k</span></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord mathnormal">n</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em"><span></span></span></span></span></span><span class="mclose delimcenter" style="top:0em"><span class="delimsizing size3">)</span></span></span></span></span></span></span>
<p>The intuitive estimator <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mo>−</mo><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><mi>c</mi><mi mathvariant="normal">/</mi><mi>n</mi><msup><mo stretchy="false">)</mo><mi>k</mi></msup></mrow><annotation encoding="application/x-tex">1 - (1 - c/n)^k</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em"></span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mopen">(</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.0991em;vertical-align:-0.25em"></span><span class="mord mathnormal">c</span><span class="mord">/</span><span class="mord mathnormal">n</span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8491em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0315em">k</span></span></span></span></span></span></span></span></span></span></span> is <strong>biased</strong>:
the function <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>f</mi><mo stretchy="false">(</mo><mi>p</mi><mo stretchy="false">)</mo><mo>=</mo><mn>1</mn><mo>−</mo><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><mi>p</mi><msup><mo stretchy="false">)</mo><mi>k</mi></msup></mrow><annotation encoding="application/x-tex">f(p) = 1-(1-p)^k</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.1076em">f</span><span class="mopen">(</span><span class="mord mathnormal">p</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em"></span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mopen">(</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.0991em;vertical-align:-0.25em"></span><span class="mord mathnormal">p</span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8491em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0315em">k</span></span></span></span></span></span></span></span></span></span></span> is concave in <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>p</mi></mrow><annotation encoding="application/x-tex">p</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord mathnormal">p</span></span></span></span>,
so by Jensen's inequality <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="double-struck">E</mi><mo stretchy="false">[</mo><mi>f</mi><mo stretchy="false">(</mo><mover accent="true"><mi>p</mi><mo>^</mo></mover><mo stretchy="false">)</mo><mo stretchy="false">]</mo><mo>≤</mo><mi>f</mi><mo stretchy="false">(</mo><mi>p</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\mathbb{E}[f(\hat p)] \le f(p)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathbb">E</span><span class="mopen">[</span><span class="mord mathnormal" style="margin-right:0.1076em">f</span><span class="mopen">(</span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal">p</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1667em"><span class="mord">^</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em"><span></span></span></span></span></span><span class="mclose">)]</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.1076em">f</span><span class="mopen">(</span><span class="mord mathnormal">p</span><span class="mclose">)</span></span></span></span> —
plug an estimate into a nonlinear function and the expectation no longer matches the truth.
The binomial formula above reads as "the fraction of <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow><annotation encoding="application/x-tex">k</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal" style="margin-right:0.0315em">k</span></span></span></span>-subsets of the <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">n</span></span></span></span> samples that are all wrong," and it can be proven exactly unbiased.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="34-mcnemars-test--comparing-two-models-on-the-same-exam">3.4 McNemar's test — comparing two models on the same exam<a href="https://kobkrit.com/en/blog/llm-09-benchmarking#34-mcnemars-test--comparing-two-models-on-the-same-exam" class="hash-link" aria-label="Direct link to 3.4 McNemar's test — comparing two models on the same exam" title="Direct link to 3.4 McNemar's test — comparing two models on the same exam" translate="no">​</a></h3>
<p>Models A and B take <strong>the same exam</strong>. Let <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>b</mi></mrow><annotation encoding="application/x-tex">b</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal">b</span></span></span></span> = questions A got right but B got wrong, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow><annotation encoding="application/x-tex">c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">c</span></span></span></span> = questions B got right but A got wrong:</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msup><mi>χ</mi><mn>2</mn></msup><mo>=</mo><mfrac><mrow><mo stretchy="false">(</mo><mi mathvariant="normal">∣</mi><mi>b</mi><mo>−</mo><mi>c</mi><mi mathvariant="normal">∣</mi><mo>−</mo><mn>1</mn><msup><mo stretchy="false">)</mo><mn>2</mn></msup></mrow><mrow><mi>b</mi><mo>+</mo><mi>c</mi></mrow></mfrac></mrow><annotation encoding="application/x-tex">\chi^2 = \frac{(|b-c|-1)^2}{b+c}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0585em;vertical-align:-0.1944em"></span><span class="mord"><span class="mord mathnormal">χ</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641em"><span style="top:-3.113em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.2604em;vertical-align:-0.7693em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.4911em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord mathnormal">b</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord mathnormal">c</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mopen">(</span><span class="mord">∣</span><span class="mord mathnormal">b</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord mathnormal">c</span><span class="mord">∣</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord">1</span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.7693em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span>
<p>Compare against <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mi>χ</mi><mn>1</mn><mn>2</mn></msubsup></mrow><annotation encoding="application/x-tex">\chi^2_1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0622em;vertical-align:-0.2481em"></span><span class="mord"><span class="mord mathnormal">χ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8141em"><span style="top:-2.4519em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2481em"><span></span></span></span></span></span></span></span></span></span> (with continuity correction). The key word is <strong>paired</strong>:
the questions both got right and the questions both got wrong <strong>appear nowhere in the formula</strong>, because they say nothing about who's better.
If you compare two accuracies with a t-test as if they came from different exams,
you throw away the "same question" structure and then need many times more questions for the same power.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="35-contamination-check--did-the-exam-leak-into-the-training-data">3.5 Contamination check — did the exam leak into the training data<a href="https://kobkrit.com/en/blog/llm-09-benchmarking#35-contamination-check--did-the-exam-leak-into-the-training-data" class="hash-link" aria-label="Direct link to 3.5 Contamination check — did the exam leak into the training data" title="Direct link to 3.5 Contamination check — did the exam leak into the training data" translate="no">​</a></h3>
<p>For an exam question <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow><annotation encoding="application/x-tex">x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">x</span></span></span></span> and a training corpus <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="script">D</mi></mrow><annotation encoding="application/x-tex">\mathcal{D}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathcal" style="margin-right:0.0278em">D</span></span></span></span>, define the <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow><annotation encoding="application/x-tex">k</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal" style="margin-right:0.0315em">k</span></span></span></span>-gram overlap rate:</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mtext>overlap</mtext><mi>k</mi></msub><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo>=</mo><mfrac><mrow><mo fence="true">∣</mo><msub><mi>G</mi><mi>k</mi></msub><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo>∩</mo><msub><mi>G</mi><mi>k</mi></msub><mo stretchy="false">(</mo><mi mathvariant="script">D</mi><mo stretchy="false">)</mo><mo fence="true">∣</mo></mrow><mrow><mo fence="true">∣</mo><msub><mi>G</mi><mi>k</mi></msub><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo fence="true">∣</mo></mrow></mfrac></mrow><annotation encoding="application/x-tex">\text{overlap}_k(x) = \frac{\left|G_k(x) \cap G_k(\mathcal{D})\right|}{\left|G_k(x)\right|}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord text"><span class="mord">overlap</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.242em"><span style="top:-2.4559em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0315em">k</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2441em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.363em;vertical-align:-0.936em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.427em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="minner"><span class="mopen delimcenter" style="top:0em">∣</span><span class="mord"><span class="mord mathnormal">G</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0315em">k</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mclose delimcenter" style="top:0em">∣</span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="minner"><span class="mopen delimcenter" style="top:0em">∣</span><span class="mord"><span class="mord mathnormal">G</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0315em">k</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">∩</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord"><span class="mord mathnormal">G</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0315em">k</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathcal" style="margin-right:0.0278em">D</span><span class="mclose">)</span><span class="mclose delimcenter" style="top:0em">∣</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.936em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span>
<p>where <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>G</mi><mi>k</mi></msub><mo stretchy="false">(</mo><mo>⋅</mo><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">G_k(\cdot)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathnormal">G</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0315em">k</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord">⋅</span><span class="mclose">)</span></span></span></span> is the set of all <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow><annotation encoding="application/x-tex">k</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal" style="margin-right:0.0315em">k</span></span></span></span>-grams. For Thai we use <strong>character-level k-grams</strong> (e.g. 20 characters),
because Thai word segmentation is ambiguous. If <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mtext>overlap</mtext><mi>k</mi></msub></mrow><annotation encoding="application/x-tex">\text{overlap}_k</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9386em;vertical-align:-0.2441em"></span><span class="mord"><span class="mord text"><span class="mord">overlap</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.242em"><span style="top:-2.4559em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0315em">k</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2441em"><span></span></span></span></span></span></span></span></span></span> is high (say above 0.7), suspect the model has already "seen the answer key"
— its score on that question measures memory, not ability.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-seeing-the-equations">4. Seeing the Equations<a href="https://kobkrit.com/en/blog/llm-09-benchmarking#4-seeing-the-equations" class="hash-link" aria-label="Direct link to 4. Seeing the Equations" title="Direct link to 4. Seeing the Equations" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="ci-width-is-a-function-of-n--and-n100-gives-10-points">CI width is a function of n — and n=100 gives ±10 points<a href="https://kobkrit.com/en/blog/llm-09-benchmarking#ci-width-is-a-function-of-n--and-n100-gives-10-points" class="hash-link" aria-label="Direct link to CI width is a function of n — and n=100 gives ±10 points" title="Direct link to CI width is a function of n — and n=100 gives ±10 points" translate="no">​</a></h3>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-09-benchmarking/ci-width.light.svg" alt="Plot of the Wilson 95% confidence interval half-width against question count on a log axis from 10 to 10,000 questions, for accuracies 0.60, 0.75 and 0.90, with the n=100 point highlighted" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-09-benchmarking/ci-width.dark.svg" alt="Plot of the Wilson 95% confidence interval half-width against question count on a log axis from 10 to 10,000 questions, for accuracies 0.60, 0.75 and 0.90, with the n=100 point highlighted" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 9.1</span>Half-width of the Wilson 95% CI against question count n — at n=100 the interval spans ±6 to ±9.4 points depending on the accuracy level, and shrinking it 10× costs 100× more questions</p><div class="captionFooter_w00v"></div></figcaption></figure>
<p>This is the most important figure of the chapter. The width shrinks as <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mi mathvariant="normal">/</mi><msqrt><mi>n</mi></msqrt></mrow><annotation encoding="application/x-tex">1/\sqrt{n}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0503em;vertical-align:-0.25em"></span><span class="mord">1/</span><span class="mord sqrt"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8003em"><span class="svg-align" style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord" style="padding-left:0.833em"><span class="mord mathnormal">n</span></span></span><span style="top:-2.7603em"><span class="pstrut" style="height:3em"></span><span class="hide-tail" style="min-width:0.853em;height:1.08em"><svg xmlns="http://www.w3.org/2000/svg" width="400em" height="1.08em" viewBox="0 0 400000 1080" preserveAspectRatio="xMinYMin slice"><path d="M95,702
c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14
c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54
c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10
s173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429
c69,-144,104.5,-217.7,106.5,-221
l0 -0
c5.3,-9.3,12,-14,20,-14
H400000v40H845.2724
s-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7
c-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z
M834 80h400000v40h-400000z"></path></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2397em"><span></span></span></span></span></span></span></span></span>,
so a 100-question test set can never separate models 4 points apart, no matter how many times you rerun it.
And to read a 1-point gap with confidence, you need on the order of ten thousand questions — which most Thai benchmarks don't have.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-a-leaderboard-looks-like-once-you-actually-draw-the-error-bars">What a leaderboard looks like once you actually draw the error bars<a href="https://kobkrit.com/en/blog/llm-09-benchmarking#what-a-leaderboard-looks-like-once-you-actually-draw-the-error-bars" class="hash-link" aria-label="Direct link to What a leaderboard looks like once you actually draw the error bars" title="Direct link to What a leaderboard looks like once you actually draw the error bars" translate="no">​</a></h3>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-09-benchmarking/overlapping-cis.light.svg" alt="Dot plot of five models ordered by accuracy with Wilson 95% CI bars, where the intervals of the middle three models overlap and are shaded as indistinguishable" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-09-benchmarking/overlapping-cis.dark.svg" alt="Dot plot of five models ordered by accuracy with Wilson 95% CI bars, where the intervals of the middle three models overlap and are shaded as indistinguishable" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 9.2</span>A hypothetical 5-model leaderboard at n=100 — the Wilson intervals of the middle three ranks all overlap, so they are 'one blob', not three ranks (illustrative scores; the CI ranges are computed for real — the measured edition is in section 8)</p><div class="captionFooter_w00v"></div></figcaption></figure>
<p>Models B, C, D differ by at most 5 points, but the CIs are ±9 points wide — the only conclusion the data supports is
"these three cannot be told apart." Anyone declaring that D beats B is reading signal out of noise.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-people-dont-report-is-bigger-than-what-people-argue-about">What people don't report is bigger than what people argue about<a href="https://kobkrit.com/en/blog/llm-09-benchmarking#what-people-dont-report-is-bigger-than-what-people-argue-about" class="hash-link" aria-label="Direct link to What people don't report is bigger than what people argue about" title="Direct link to What people don't report is bigger than what people argue about" translate="no">​</a></h3>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-09-benchmarking/prompt-sensitivity.light.svg" alt="Box plot of one model's scores across five prompt template phrasings, next to the reported score points of two models two points apart, showing the template spread is wider than the gap between models" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-09-benchmarking/prompt-sensitivity.dark.svg" alt="Box plot of one model's scores across five prompt template phrasings, next to the reported score points of two models two points apart, showing the template spread is wider than the gap between models" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 9.3</span>The same model measured with 5 prompt templates of identical meaning — the 8.5-point spread is wider than the 2-point gap between the 'rivals' on the leaderboard (illustrative values at the magnitudes found in real prompt-sensitivity research — labeled as such in the figure)</p><div class="captionFooter_w00v"></div></figcaption></figure>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="mcnemar-100-questions-but-only-10-carry-information">McNemar: 100 questions, but only 10 carry information<a href="https://kobkrit.com/en/blog/llm-09-benchmarking#mcnemar-100-questions-but-only-10-carry-information" class="hash-link" aria-label="Direct link to McNemar: 100 questions, but only 10 carry information" title="Direct link to McNemar: 100 questions, but only 10 carry information" translate="no">​</a></h3>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-09-benchmarking/mcnemar-grid.light.svg" alt="Heat map of a 2 by 2 table showing a=70 both right, b=9 only model A right, c=1 only model B right, d=20 both wrong, with McNemar's chi-squared and p-value" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-09-benchmarking/mcnemar-grid.dark.svg" alt="Heat map of a 2 by 2 table showing a=70 both right, b=9 only model A right, c=1 only model B right, d=20 both wrong, with McNemar's chi-squared and p-value" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 9.4</span>The 2×2 contingency table of two models on the same 100-question exam — the whole test uses only cells b and c; χ² = 4.90 and p ≈ 0.027, computed from the actual formula</p><div class="captionFooter_w00v"></div></figcaption></figure>
<p>The two models sit 8 points apart (79% versus 71%) — sounds decisive, but the actual evidence is the 10 questions where they disagree.
McNemar says this only just crosses the <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>p</mi><mo>=</mo><mn>0.05</mn></mrow><annotation encoding="application/x-tex">p = 0.05</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord mathnormal">p</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0.05</span></span></span></span> line. With <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>b</mi><mo>=</mo><mn>6</mn><mo separator="true">,</mo><mi>c</mi><mo>=</mo><mn>4</mn></mrow><annotation encoding="application/x-tex">b=6, c=4</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal">b</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.8389em;vertical-align:-0.1944em"></span><span class="mord">6</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal">c</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">4</span></span></span></span> (a 2-point gap, like a typical leaderboard),
you'd get <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>χ</mi><mn>2</mn></msup><mo>=</mo><mn>0.1</mn></mrow><annotation encoding="application/x-tex">\chi^2 = 0.1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0085em;vertical-align:-0.1944em"></span><span class="mord"><span class="mord mathnormal">χ</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0.1</span></span></span></span> — not remotely significant.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="play-with-the-n--ci-relationship-yourself">Play with the n → CI relationship yourself<a href="https://kobkrit.com/en/blog/llm-09-benchmarking#play-with-the-n--ci-relationship-yourself" class="hash-link" aria-label="Direct link to Play with the n → CI relationship yourself" title="Direct link to Play with the n → CI relationship yourself" translate="no">​</a></h3>
<p>This widget is the same one from chapter 8 (guardrails), but this time look at it from a different angle:
every TPR / FPR / precision number in it carries <strong>the same Wilson CI as equation 3.1</strong>.
Drag the threshold <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>τ</mi></mrow><annotation encoding="application/x-tex">\tau</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.1132em">τ</span></span></span></span> toward an extreme until one cell of the confusion matrix holds only a few examples,
and watch the CI balloon before your eyes — that is figure 9.1 in interactive form.</p>
<div class="root_AxNC"><div class="controls_hr8V"><div class="control_Br1p"><label class="controlLabel_J5tp" for="_R_idmldeh_"><span>Threshold τ</span><span class="controlValue_cYgn">0.500</span></label><input id="_R_idmldeh_" class="range_qGHz" type="range" min="0" max="1" step="0.005" aria-label="Decision threshold tau" aria-valuetext="0.500" aria-describedby="_R_idmldeh_-hint" value="0.5"><span class="controlHint_ilRY" id="_R_idmldeh_-hint">Flag a request as unsafe when score ≥ τ.</span></div><div class="control_Br1p"><label class="controlLabel_J5tp" for="_R_12dmldeh_"><span>Cost ratio c_FN / c_FP</span><span class="controlValue_cYgn">10 : 1</span></label><input id="_R_12dmldeh_" class="range_qGHz" type="range" min="1" max="100" step="1" aria-label="Ratio of false negative cost to false positive cost" aria-valuetext="10 to 1" aria-describedby="_R_12dmldeh_-hint" value="10"><span class="controlHint_ilRY" id="_R_12dmldeh_-hint">How much worse is letting an unsafe request through than blocking a safe one?</span></div><div class="control_Br1p"><label class="controlLabel_J5tp" for="_R_1idmldeh_"><span>Deployment base rate P(unsafe)</span><span class="controlValue_cYgn">1.0%</span></label><input id="_R_1idmldeh_" class="range_qGHz" type="range" min="0.001" max="0.5" step="0.001" aria-label="Proportion of live traffic that is genuinely unsafe" aria-valuetext="1.0%" aria-describedby="_R_1idmldeh_-hint" value="0.01"><span class="controlHint_ilRY" id="_R_1idmldeh_-hint">The evaluation set is 34.3% unsafe. Real traffic is usually far cleaner.</span></div><div class="control_Br1p"><span class="controlLabel_J5tp">Optimal τ<span class="controlValue_cYgn">0.595</span></span><button type="button" class="button_ioxi buttonPrimary_sfPF">Jump to minimum cost</button></div></div><div class="svgWrap_mSxx"><svg class="svg_pLEH histogram_iOmc" viewBox="0 0 720 150" preserveAspectRatio="none" role="img" aria-label="Score distribution of safe and unsafe examples with a draggable threshold line."><rect x="0" y="110.46511627906976" width="15.863636363636363" height="19.534883720930235" class="histSafe_nrmu"></rect><rect x="16.363636363636363" y="15.581395348837205" width="15.863636363636363" height="114.4186046511628" class="histSafe_nrmu"></rect><rect x="32.72727272727273" y="18.372093023255815" width="15.863636363636363" height="111.62790697674419" class="histSafe_nrmu"></rect><rect x="49.09090909090909" y="10" width="15.863636363636363" height="120" class="histSafe_nrmu"></rect><rect x="65.45454545454545" y="12.79069767441861" width="15.863636363636363" height="117.20930232558139" class="histSafe_nrmu"></rect><rect x="81.81818181818181" y="15.581395348837205" width="15.863636363636363" height="114.4186046511628" class="histSafe_nrmu"></rect><rect x="98.18181818181819" y="21.162790697674424" width="15.863636363636363" height="108.83720930232558" class="histSafe_nrmu"></rect><rect x="114.54545454545455" y="54.65116279069767" width="15.863636363636363" height="75.34883720930233" class="histSafe_nrmu"></rect><rect x="130.9090909090909" y="65.81395348837209" width="15.863636363636363" height="64.18604651162791" class="histSafe_nrmu"></rect><rect x="147.27272727272728" y="88.13953488372093" width="15.863636363636363" height="41.86046511627907" class="histSafe_nrmu"></rect><rect x="163.63636363636363" y="82.55813953488372" width="15.863636363636363" height="47.44186046511628" class="histSafe_nrmu"></rect><rect x="180" y="65.81395348837209" width="15.863636363636363" height="64.18604651162791" class="histSafe_nrmu"></rect><rect x="196.36363636363637" y="90.93023255813952" width="15.863636363636363" height="39.06976744186047" class="histSafe_nrmu"></rect><rect x="212.72727272727272" y="102.09302325581396" width="15.863636363636363" height="27.906976744186046" class="histSafe_nrmu"></rect><rect x="229.0909090909091" y="96.51162790697674" width="15.863636363636363" height="33.48837209302326" class="histSafe_nrmu"></rect><rect x="245.45454545454544" y="107.67441860465117" width="15.863636363636363" height="22.325581395348838" class="histSafe_nrmu"></rect><rect x="261.8181818181818" y="113.25581395348837" width="15.863636363636363" height="16.74418604651163" class="histSafe_nrmu"></rect><rect x="278.1818181818182" y="104.88372093023256" width="15.863636363636363" height="25.116279069767444" class="histSafe_nrmu"></rect><rect x="294.54545454545456" y="104.88372093023256" width="15.863636363636363" height="25.116279069767444" class="histSafe_nrmu"></rect><rect x="310.9090909090909" y="121.62790697674419" width="15.863636363636363" height="8.372093023255815" class="histSafe_nrmu"></rect><rect x="327.27272727272725" y="110.46511627906976" width="15.863636363636363" height="19.534883720930235" class="histSafe_nrmu"></rect><rect x="343.6363636363636" y="124.4186046511628" width="15.863636363636363" height="5.5813953488372094" class="histSafe_nrmu"></rect><rect x="360" y="121.62790697674419" width="15.863636363636363" height="8.372093023255815" class="histSafe_nrmu"></rect><rect x="376.3636363636364" y="124.4186046511628" width="15.863636363636363" height="5.5813953488372094" class="histSafe_nrmu"></rect><rect x="392.72727272727275" y="116.04651162790698" width="15.863636363636363" height="13.953488372093023" class="histSafe_nrmu"></rect><rect x="409.09090909090907" y="116.04651162790698" width="15.863636363636363" height="13.953488372093023" class="histSafe_nrmu"></rect><rect x="425.45454545454544" y="124.4186046511628" width="15.863636363636363" height="5.5813953488372094" class="histSafe_nrmu"></rect><rect x="441.8181818181818" y="127.20930232558139" width="15.863636363636363" height="2.7906976744186047" class="histSafe_nrmu"></rect><rect x="458.1818181818182" y="127.20930232558139" width="15.863636363636363" height="2.7906976744186047" class="histSafe_nrmu"></rect><rect x="474.54545454545456" y="127.20930232558139" width="15.863636363636363" height="2.7906976744186047" class="histSafe_nrmu"></rect><rect x="490.9090909090909" y="130" width="15.863636363636363" height="0" class="histSafe_nrmu"></rect><rect x="507.27272727272725" y="130" width="15.863636363636363" height="0" class="histSafe_nrmu"></rect><rect x="523.6363636363636" y="124.4186046511628" width="15.863636363636363" height="5.5813953488372094" class="histSafe_nrmu"></rect><rect x="540" y="130" width="15.863636363636363" height="0" class="histSafe_nrmu"></rect><rect x="556.3636363636364" y="130" width="15.863636363636363" height="0" class="histSafe_nrmu"></rect><rect x="572.7272727272727" y="130" width="15.863636363636363" height="0" class="histSafe_nrmu"></rect><rect x="589.0909090909091" y="130" width="15.863636363636363" height="0" class="histSafe_nrmu"></rect><rect x="605.4545454545455" y="130" width="15.863636363636363" height="0" class="histSafe_nrmu"></rect><rect x="621.8181818181818" y="130" width="15.863636363636363" height="0" class="histSafe_nrmu"></rect><rect x="638.1818181818181" y="130" width="15.863636363636363" height="0" class="histSafe_nrmu"></rect><rect x="654.5454545454545" y="130" width="15.863636363636363" height="0" class="histSafe_nrmu"></rect><rect x="670.9090909090909" y="130" width="15.863636363636363" height="0" class="histSafe_nrmu"></rect><rect x="687.2727272727273" y="130" width="15.863636363636363" height="0" class="histSafe_nrmu"></rect><rect x="703.6363636363636" y="130" width="15.863636363636363" height="0" class="histSafe_nrmu"></rect><rect x="0" y="130" width="15.863636363636363" height="0" class="histUnsafe_Wv1M"></rect><rect x="16.363636363636363" y="130" width="15.863636363636363" height="0" class="histUnsafe_Wv1M"></rect><rect x="32.72727272727273" y="130" width="15.863636363636363" height="0" class="histUnsafe_Wv1M"></rect><rect x="49.09090909090909" y="130" width="15.863636363636363" height="0" class="histUnsafe_Wv1M"></rect><rect x="65.45454545454545" y="130" width="15.863636363636363" height="0" class="histUnsafe_Wv1M"></rect><rect x="81.81818181818181" y="130" width="15.863636363636363" height="0" class="histUnsafe_Wv1M"></rect><rect x="98.18181818181819" y="130" width="15.863636363636363" height="0" class="histUnsafe_Wv1M"></rect><rect x="114.54545454545455" y="130" width="15.863636363636363" height="0" class="histUnsafe_Wv1M"></rect><rect x="130.9090909090909" y="130" width="15.863636363636363" height="0" class="histUnsafe_Wv1M"></rect><rect x="147.27272727272728" y="130" width="15.863636363636363" height="0" class="histUnsafe_Wv1M"></rect><rect x="163.63636363636363" y="130" width="15.863636363636363" height="0" class="histUnsafe_Wv1M"></rect><rect x="180" y="124.4186046511628" width="15.863636363636363" height="5.5813953488372094" class="histUnsafe_Wv1M"></rect><rect x="196.36363636363637" y="127.20930232558139" width="15.863636363636363" height="2.7906976744186047" class="histUnsafe_Wv1M"></rect><rect x="212.72727272727272" y="124.4186046511628" width="15.863636363636363" height="5.5813953488372094" class="histUnsafe_Wv1M"></rect><rect x="229.0909090909091" y="130" width="15.863636363636363" height="0" class="histUnsafe_Wv1M"></rect><rect x="245.45454545454544" y="118.83720930232558" width="15.863636363636363" height="11.162790697674419" class="histUnsafe_Wv1M"></rect><rect x="261.8181818181818" y="130" width="15.863636363636363" height="0" class="histUnsafe_Wv1M"></rect><rect x="278.1818181818182" y="127.20930232558139" width="15.863636363636363" height="2.7906976744186047" class="histUnsafe_Wv1M"></rect><rect x="294.54545454545456" y="124.4186046511628" width="15.863636363636363" height="5.5813953488372094" class="histUnsafe_Wv1M"></rect><rect x="310.9090909090909" y="121.62790697674419" width="15.863636363636363" height="8.372093023255815" class="histUnsafe_Wv1M"></rect><rect x="327.27272727272725" y="124.4186046511628" width="15.863636363636363" height="5.5813953488372094" class="histUnsafe_Wv1M"></rect><rect x="343.6363636363636" y="121.62790697674419" width="15.863636363636363" height="8.372093023255815" class="histUnsafe_Wv1M"></rect><rect x="360" y="118.83720930232558" width="15.863636363636363" height="11.162790697674419" class="histUnsafe_Wv1M"></rect><rect x="376.3636363636364" y="127.20930232558139" width="15.863636363636363" height="2.7906976744186047" class="histUnsafe_Wv1M"></rect><rect x="392.72727272727275" y="124.4186046511628" width="15.863636363636363" height="5.5813953488372094" class="histUnsafe_Wv1M"></rect><rect x="409.09090909090907" y="127.20930232558139" width="15.863636363636363" height="2.7906976744186047" class="histUnsafe_Wv1M"></rect><rect x="425.45454545454544" y="118.83720930232558" width="15.863636363636363" height="11.162790697674419" class="histUnsafe_Wv1M"></rect><rect x="441.8181818181818" y="110.46511627906976" width="15.863636363636363" height="19.534883720930235" class="histUnsafe_Wv1M"></rect><rect x="458.1818181818182" y="104.88372093023256" width="15.863636363636363" height="25.116279069767444" class="histUnsafe_Wv1M"></rect><rect x="474.54545454545456" y="102.09302325581396" width="15.863636363636363" height="27.906976744186046" class="histUnsafe_Wv1M"></rect><rect x="490.9090909090909" y="110.46511627906976" width="15.863636363636363" height="19.534883720930235" class="histUnsafe_Wv1M"></rect><rect x="507.27272727272725" y="102.09302325581396" width="15.863636363636363" height="27.906976744186046" class="histUnsafe_Wv1M"></rect><rect x="523.6363636363636" y="110.46511627906976" width="15.863636363636363" height="19.534883720930235" class="histUnsafe_Wv1M"></rect><rect x="540" y="104.88372093023256" width="15.863636363636363" height="25.116279069767444" class="histUnsafe_Wv1M"></rect><rect x="556.3636363636364" y="93.72093023255815" width="15.863636363636363" height="36.27906976744186" class="histUnsafe_Wv1M"></rect><rect x="572.7272727272727" y="88.13953488372093" width="15.863636363636363" height="41.86046511627907" class="histUnsafe_Wv1M"></rect><rect x="589.0909090909091" y="88.13953488372093" width="15.863636363636363" height="41.86046511627907" class="histUnsafe_Wv1M"></rect><rect x="605.4545454545455" y="65.81395348837209" width="15.863636363636363" height="64.18604651162791" class="histUnsafe_Wv1M"></rect><rect x="621.8181818181818" y="88.13953488372093" width="15.863636363636363" height="41.86046511627907" class="histUnsafe_Wv1M"></rect><rect x="638.1818181818181" y="82.55813953488372" width="15.863636363636363" height="47.44186046511628" class="histUnsafe_Wv1M"></rect><rect x="654.5454545454545" y="82.55813953488372" width="15.863636363636363" height="47.44186046511628" class="histUnsafe_Wv1M"></rect><rect x="670.9090909090909" y="93.72093023255815" width="15.863636363636363" height="36.27906976744186" class="histUnsafe_Wv1M"></rect><rect x="687.2727272727273" y="82.55813953488372" width="15.863636363636363" height="47.44186046511628" class="histUnsafe_Wv1M"></rect><rect x="703.6363636363636" y="118.83720930232558" width="15.863636363636363" height="11.162790697674419" class="histUnsafe_Wv1M"></rect><line x1="360" y1="0" x2="360" y2="140" class="tauLine_yF3p"></line></svg></div><p class="legendRow_om72"><span class="legendItem_fx92"><span class="legendSwatch_ly4K histSafe_nrmu"></span>safe</span><span class="legendItem_fx92"><span class="legendSwatch_ly4K histUnsafe_Wv1M"></span>unsafe</span><span class="legendHint_pdp0">Drag the line, or use the τ slider.</span></p><div class="charts_UBTr"><figure class="chart__OlR"><figcaption class="chartTitle_JK0P">ROC<span class="chartSubtitle_pHig">AUC = 0.987</span></figcaption><svg class="svg_pLEH" viewBox="0 0 300 300" role="img" aria-label="FPR vs TPR"><rect x="38" y="14" width="248" height="248" class="plotArea_QhBk"></rect><line x1="38" y1="262" x2="286" y2="14" class="chance_eT6F"></line><path d="M38.00,94.60 L38.00,98.73 L38.00,98.73 L38.00,98.73 L38.00,103.90 L38.00,107.00 L38.00,109.07 L38.00,113.20 L38.00,114.23 L38.00,117.33 L38.00,121.47 L38.00,125.60 L38.00,128.70 L38.00,130.77 L38.00,134.90 L38.00,136.97 L38.00,142.13 L38.00,145.23 L38.00,149.37 L38.00,152.47 L38.00,157.63 L38.00,161.77 L38.00,165.90 L38.00,175.20 L38.00,178.30 L38.00,181.40 L38.00,185.53 L38.00,188.63 L38.00,191.73 L38.00,193.80 L38.00,195.87 L38.00,200.00 L38.00,206.20 L38.00,210.33 L38.00,211.37 L38.00,213.43 L38.00,217.57 L38.00,224.80 L38.00,228.93 L38.00,231.00 L38.00,235.13 L38.00,237.20 L38.00,240.30 L38.00,246.50 L38.00,250.63 L38.00,252.70 L38.00,256.83 L38.00,257.87 L38.00,260.97 L38.00,260.97 L38.00,262.00 L38.00,262.00 L38.54,92.53 L38.54,92.53 L38.54,93.57 L39.08,72.90 L39.08,73.93 L39.08,74.97 L39.08,76.00 L39.08,77.03 L39.08,82.20 L39.08,84.27 L39.08,86.33 L39.08,89.43 L39.08,91.50 L39.62,56.37 L39.62,59.47 L39.62,61.53 L39.62,63.60 L39.62,67.73 L39.62,69.80 L39.62,71.87 L40.16,54.30 L40.16,55.33 L40.70,42.93 L40.70,46.03 L40.70,47.07 L40.70,47.07 L40.70,49.13 L40.70,51.20 L40.70,53.27 L40.70,54.30 L41.77,42.93 L42.85,42.93 L42.85,42.93 L43.93,42.93 L44.47,39.83 L44.47,40.87 L44.47,40.87 L44.47,41.90 L46.09,39.83 L47.17,39.83 L48.24,38.80 L48.24,38.80 L48.24,38.80 L48.24,38.80 L48.24,39.83 L49.32,38.80 L49.86,32.60 L49.86,34.67 L49.86,34.67 L49.86,35.70 L50.40,32.60 L50.94,31.57 L50.94,31.57 L50.94,32.60 L52.02,30.53 L52.02,30.53 L53.10,29.50 L54.71,29.50 L55.25,29.50 L55.79,28.47 L56.33,27.43 L56.33,27.43 L56.87,26.40 L57.95,24.33 L60.10,24.33 L61.18,24.33 L61.18,24.33 L62.26,24.33 L62.26,24.33 L62.80,24.33 L63.34,23.30 L66.03,23.30 L67.65,23.30 L68.19,23.30 L68.19,23.30 L69.27,23.30 L69.81,23.30 L71.43,20.20 L71.43,21.23 L73.04,19.17 L74.12,19.17 L75.20,19.17 L75.20,19.17 L77.36,19.17 L80.05,19.17 L80.59,18.13 L80.59,18.13 L81.13,18.13 L82.75,17.10 L85.44,17.10 L87.60,17.10 L88.68,17.10 L91.37,16.07 L92.45,16.07 L93.53,16.07 L96.23,14.00 L100.00,14.00 L102.16,14.00 L105.39,14.00 L107.01,14.00 L109.17,14.00 L110.78,14.00 L113.48,14.00 L116.17,14.00 L117.79,14.00 L118.87,14.00 L119.95,14.00 L122.64,14.00 L125.34,14.00 L128.03,14.00 L132.35,14.00 L133.43,14.00 L135.58,14.00 L136.66,14.00 L139.36,14.00 L143.67,14.00 L147.98,14.00 L151.76,14.00 L154.99,14.00 L163.62,14.00 L165.77,14.00 L170.63,14.00 L176.02,14.00 L182.49,14.00 L188.42,14.00 L192.73,14.00 L196.50,14.00 L201.90,14.00 L205.67,14.00 L212.68,14.00 L215.91,14.00 L219.69,14.00 L224.00,14.00 L231.01,14.00 L236.94,14.00 L240.71,14.00 L246.64,14.00 L250.42,14.00 L255.81,14.00 L261.20,14.00 L268.75,14.00 L270.37,14.00 L275.22,14.00 L281.15,14.00 L283.30,14.00 L284.92,14.00 L286.00,14.00 L286.00,14.00 L286.00,14.00" class="curve_MZbm"></path><circle cx="49.86086956521739" cy="34.666666666666686" r="5" class="marker_Thvy"></circle><line x1="38" y1="262" x2="286" y2="262" class="axis_vyjV"></line><line x1="38" y1="262" x2="38" y2="14" class="axis_vyjV"></line><text x="38" y="277" text-anchor="middle" class="tickLabel_B3jM">0</text><text x="162" y="277" text-anchor="middle" class="tickLabel_B3jM">0.5</text><text x="286" y="277" text-anchor="middle" class="tickLabel_B3jM">1</text><text x="32" y="266" text-anchor="end" class="tickLabel_B3jM">0</text><text x="32" y="142" text-anchor="end" class="tickLabel_B3jM">0.5</text><text x="32" y="18" text-anchor="end" class="tickLabel_B3jM">1</text><text x="162" y="296" text-anchor="middle" class="axisLabel_Yazw">FPR</text><text x="10" y="138" text-anchor="middle" transform="rotate(-90 10 138)" class="axisLabel_Yazw">TPR</text></svg></figure><figure class="chart__OlR"><figcaption class="chartTitle_JK0P">Precision-Recall<span class="chartSubtitle_pHig">at the eval base rate 34.3%</span></figcaption><svg class="svg_pLEH" viewBox="0 0 300 300" role="img" aria-label="recall vs precision"><rect x="38" y="14" width="248" height="248" class="plotArea_QhBk"></rect><line x1="38" y1="176.9714285714286" x2="286" y2="176.9714285714286" class="chance_eT6F"></line><path d="M38.00,14.00 L38.00,14.00 L39.03,14.00 L39.03,14.00 L42.13,14.00 L43.17,14.00 L47.30,14.00 L49.37,14.00 L53.50,14.00 L59.70,14.00 L62.80,14.00 L64.87,14.00 L69.00,14.00 L71.07,14.00 L75.20,14.00 L82.43,14.00 L86.57,14.00 L88.63,14.00 L89.67,14.00 L93.80,14.00 L100.00,14.00 L104.13,14.00 L106.20,14.00 L108.27,14.00 L111.37,14.00 L114.47,14.00 L118.60,14.00 L121.70,14.00 L124.80,14.00 L134.10,14.00 L138.23,14.00 L142.37,14.00 L147.53,14.00 L150.63,14.00 L154.77,14.00 L157.87,14.00 L163.03,14.00 L165.10,14.00 L169.23,14.00 L171.30,14.00 L174.40,14.00 L178.53,14.00 L182.67,14.00 L185.77,14.00 L186.80,14.00 L190.93,14.00 L193.00,14.00 L196.10,14.00 L201.27,14.00 L201.27,14.00 L201.27,14.00 L205.40,14.00 L206.43,15.51 L207.47,15.50 L207.47,15.50 L208.50,16.97 L210.57,16.93 L213.67,16.88 L215.73,16.85 L217.80,16.82 L222.97,16.74 L224.00,16.73 L225.03,16.71 L226.07,16.70 L227.10,16.68 L228.13,17.98 L230.20,17.94 L232.27,17.90 L236.40,17.82 L238.47,17.78 L240.53,17.74 L243.63,17.68 L244.67,18.86 L245.70,20.02 L245.70,18.84 L246.73,19.99 L248.80,19.93 L250.87,19.88 L252.93,19.82 L252.93,19.82 L253.97,19.79 L257.07,26.23 L257.07,24.10 L257.07,24.10 L257.07,21.93 L257.07,19.71 L258.10,27.23 L259.13,27.17 L259.13,27.17 L260.17,34.14 L260.17,32.17 L260.17,30.17 L260.17,27.11 L261.20,35.97 L261.20,34.05 L261.20,34.05 L261.20,34.05 L261.20,34.05 L264.30,36.64 L265.33,36.55 L265.33,36.55 L267.40,38.20 L267.40,37.28 L267.40,36.36 L268.43,38.10 L268.43,38.10 L269.47,39.79 L269.47,39.79 L270.50,44.88 L270.50,44.03 L270.50,41.45 L271.53,45.60 L272.57,46.31 L272.57,46.31 L273.60,47.00 L275.67,55.33 L275.67,54.58 L275.67,54.58 L275.67,53.06 L275.67,53.06 L275.67,51.52 L275.67,48.37 L276.70,64.46 L276.70,63.77 L276.70,62.39 L276.70,62.39 L276.70,61.69 L276.70,59.57 L276.70,55.93 L278.77,66.12 L279.80,65.95 L280.83,75.80 L280.83,72.78 L280.83,70.29 L280.83,70.29 L280.83,69.02 L280.83,67.73 L281.87,76.78 L281.87,76.20 L281.87,76.20 L282.90,84.43 L282.90,83.35 L282.90,81.15 L282.90,78.33 L283.93,88.91 L283.93,87.89 L283.93,86.85 L286.00,176.97 L286.00,176.97 L286.00,176.97 L286.00,176.73 L286.00,176.36 L286.00,175.86 L286.00,174.47 L286.00,173.30 L286.00,172.90 L286.00,170.99 L286.00,169.58 L286.00,168.12 L286.00,167.07 L286.00,165.38 L286.00,164.27 L286.00,162.47 L286.00,160.26 L286.00,158.85 L286.00,157.58 L286.00,156.47 L286.00,153.98 L286.00,152.59 L286.00,150.54 L286.00,149.06 L286.00,147.32 L286.00,144.83 L286.00,142.00 L286.00,139.53 L286.00,137.22 L286.00,136.16 L286.00,131.76 L286.00,130.03 L286.00,127.95 L286.00,125.49 L286.00,122.93 L286.00,121.29 L286.00,120.62 L286.00,119.27 L286.00,118.58 L286.00,115.76 L286.00,113.94 L286.00,112.08 L286.00,110.16 L286.00,109.38 L286.00,108.60 L286.00,107.40 L286.00,105.37 L286.00,103.28 L286.00,102.00 L286.00,100.26 L286.00,98.93 L286.00,96.21 L286.00,94.34 L286.00,90.97" class="curve_MZbm"></path><circle cx="265.3333333333333" cy="36.54545454545456" r="5" class="marker_Thvy"></circle><line x1="38" y1="262" x2="286" y2="262" class="axis_vyjV"></line><line x1="38" y1="262" x2="38" y2="14" class="axis_vyjV"></line><text x="38" y="277" text-anchor="middle" class="tickLabel_B3jM">0</text><text x="162" y="277" text-anchor="middle" class="tickLabel_B3jM">0.5</text><text x="286" y="277" text-anchor="middle" class="tickLabel_B3jM">1</text><text x="32" y="266" text-anchor="end" class="tickLabel_B3jM">0</text><text x="32" y="142" text-anchor="end" class="tickLabel_B3jM">0.5</text><text x="32" y="18" text-anchor="end" class="tickLabel_B3jM">1</text><text x="162" y="296" text-anchor="middle" class="axisLabel_Yazw">recall</text><text x="10" y="138" text-anchor="middle" transform="rotate(-90 10 138)" class="axisLabel_Yazw">precision</text></svg></figure><figure class="chart__OlR"><figcaption class="chartTitle_JK0P">Expected cost<span class="chartSubtitle_pHig">C(τ) = 10·π·FNR + (1−π)·FPR</span></figcaption><svg class="svg_pLEH" viewBox="0 0 300 300" role="img" aria-label="τ vs cost"><rect x="38" y="14" width="248" height="248" class="plotArea_QhBk"></rect><path d="M38.00,14.00 L39.24,14.00 L40.48,14.00 L41.72,15.08 L42.96,16.70 L44.20,18.85 L45.44,24.78 L46.68,29.63 L47.92,31.25 L49.16,38.80 L50.40,44.19 L51.64,49.58 L52.88,53.36 L54.12,59.29 L55.36,63.06 L56.60,68.99 L57.84,76.00 L59.08,80.31 L60.32,84.09 L61.56,87.32 L62.80,94.33 L64.04,98.10 L65.28,103.50 L66.52,107.27 L67.76,111.58 L69.00,117.51 L70.24,123.98 L71.48,129.37 L72.72,134.23 L73.96,136.38 L75.20,145.01 L76.44,148.24 L77.68,152.02 L78.92,156.33 L80.16,160.64 L81.40,163.34 L82.64,164.42 L83.88,166.57 L85.12,167.65 L86.36,171.97 L87.60,174.66 L88.84,177.36 L90.08,180.05 L91.32,181.13 L92.56,182.21 L93.80,183.83 L95.04,186.52 L96.28,189.22 L97.52,190.83 L98.76,192.99 L100.00,194.61 L101.24,197.84 L102.48,200.00 L103.72,203.77 L104.96,206.26 L106.20,207.34 L107.44,208.42 L108.68,211.01 L109.92,212.09 L111.16,214.24 L112.40,216.94 L113.64,218.45 L114.88,218.99 L116.12,218.99 L117.36,219.43 L118.60,222.12 L119.84,224.28 L121.08,224.28 L122.32,225.36 L123.56,226.43 L124.80,227.95 L126.04,227.84 L127.28,229.25 L128.52,229.79 L129.76,230.87 L131.00,230.87 L132.24,231.41 L133.48,233.03 L134.72,235.72 L135.96,236.16 L137.20,236.70 L138.44,236.70 L139.68,237.77 L140.92,237.77 L142.16,238.85 L143.40,241.01 L144.64,241.88 L145.88,242.31 L147.12,242.31 L148.36,242.75 L149.60,243.18 L150.84,243.72 L152.08,245.34 L153.32,246.31 L154.56,246.31 L155.80,247.29 L157.04,247.29 L158.28,247.18 L159.52,247.72 L160.76,248.26 L162.00,248.05 L163.24,248.05 L164.48,247.95 L165.72,248.17 L166.96,249.25 L168.20,249.25 L169.44,249.25 L170.68,249.25 L171.92,249.15 L173.16,250.23 L174.40,251.30 L175.64,252.92 L176.88,252.82 L178.12,252.82 L179.36,252.71 L180.60,253.15 L181.84,254.23 L183.08,254.23 L184.32,255.30 L185.56,256.38 L186.80,256.07 L188.04,255.96 L189.28,255.96 L190.52,255.76 L191.76,255.55 L193.00,255.34 L194.24,255.23 L195.48,255.77 L196.72,255.67 L197.96,256.10 L199.20,255.79 L200.44,255.58 L201.68,255.37 L202.92,254.95 L204.16,254.75 L205.40,254.54 L206.64,254.97 L207.88,254.87 L209.12,254.76 L210.36,254.66 L211.60,254.55 L212.84,254.03 L214.08,253.82 L215.32,253.62 L216.56,253.30 L217.80,253.09 L219.04,253.53 L220.28,253.53 L221.52,253.42 L222.76,253.86 L224.00,253.44 L225.24,253.44 L226.48,253.44 L227.72,252.92 L228.96,252.61 L230.20,252.40 L231.44,251.98 L232.68,251.88 L233.92,251.56 L235.16,251.14 L236.40,250.73 L237.64,250.41 L238.88,250.21 L240.12,249.79 L241.36,249.58 L242.60,249.06 L243.84,248.74 L245.08,248.33 L246.32,248.01 L247.56,247.49 L248.80,247.07 L250.04,246.66 L251.28,245.72 L252.52,245.40 L253.76,245.09 L255.00,244.67 L256.24,244.36 L257.48,244.05 L258.72,243.84 L259.96,243.63 L261.20,243.21 L262.44,242.59 L263.68,242.17 L264.92,242.06 L266.16,241.86 L267.40,241.44 L268.64,240.71 L269.88,240.29 L271.12,240.08 L272.36,239.66 L273.60,239.45 L274.84,239.14 L276.08,238.52 L277.32,238.10 L278.56,237.89 L279.80,237.47 L281.04,237.37 L282.28,237.05 L283.52,237.05 L284.76,236.95 L286.00,236.95" class="curve_MZbm"></path><line x1="185.56" y1="262" x2="185.56" y2="14" class="optimalLine_RA1O"></line><circle cx="162" cy="248.0515883472405" r="5" class="marker_Thvy"></circle><line x1="38" y1="262" x2="286" y2="262" class="axis_vyjV"></line><line x1="38" y1="262" x2="38" y2="14" class="axis_vyjV"></line><text x="38" y="277" text-anchor="middle" class="tickLabel_B3jM">0</text><text x="162" y="277" text-anchor="middle" class="tickLabel_B3jM">0.5</text><text x="286" y="277" text-anchor="middle" class="tickLabel_B3jM">1</text><text x="32" y="266" text-anchor="end" class="tickLabel_B3jM">0</text><text x="32" y="142" text-anchor="end" class="tickLabel_B3jM">0.5</text><text x="32" y="18" text-anchor="end" class="tickLabel_B3jM">1</text><text x="162" y="296" text-anchor="middle" class="axisLabel_Yazw">τ</text><text x="10" y="138" text-anchor="middle" transform="rotate(-90 10 138)" class="axisLabel_Yazw">cost</text></svg></figure></div><div class="matrixWrap_OApT"><table class="matrix_BqPq"><caption class="matrixCaption_qy8z">Measured on the held-out set at τ = 0.500 (n = 700)</caption><thead><tr><td></td><th scope="col">predicted unsafe</th><th scope="col">predicted safe</th></tr></thead><tbody><tr><th scope="row">actually unsafe</th><td class="cellGood_rnN1"><span class="cellValue_fzwa">220</span><span class="cellTag_CetO">TP</span></td><td class="cellBad_afq5"><span class="cellValue_fzwa">20</span><span class="cellTag_CetO">FN</span></td></tr><tr><th scope="row">actually safe</th><td class="cellBad_afq5"><span class="cellValue_fzwa">22</span><span class="cellTag_CetO">FP</span></td><td class="cellGood_rnN1"><span class="cellValue_fzwa">438</span><span class="cellTag_CetO">TN</span></td></tr></tbody></table></div><div class="readouts__tjv"><div class="readout_D9ns"><span class="readoutLabel_EsIV">Recall (TPR)</span><span class="readoutValue_VS6z">91.7%</span><span class="readoutSub_DoT9">95% CI 87.5%–94.5%</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">FPR</span><span class="readoutValue_VS6z">4.8%</span><span class="readoutSub_DoT9">95% CI 3.2%–7.1%</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">Precision on eval set</span><span class="readoutValue_VS6z">90.9%</span><span class="readoutSub_DoT9">95% CI 86.6%–93.9%</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">Precision at live base rate</span><span class="readoutValue_VS6z">16.2%</span><span class="readoutSub_DoT9">95% CI 11.0%–23.1%</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">Expected cost</span><span class="readoutValue_VS6z">0.0557</span><span class="readoutSub_DoT9">minimised at τ = 0.595</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">Flagged per 10,000</span><span class="readoutValue_VS6z">565</span><span class="readoutSub_DoT9">473 of them false alarms</span></div></div><p class="callout_aEDz calloutDanger_TZRT" role="status"><strong class="calloutTitle_nx3s">Precision has collapsed.</strong>The classifier looks excellent on the evaluation set — 90.9% precision — but at a live base rate of 1.0% it drops to 16.2%. Nothing about the model changed; TPR and FPR are identical. There are simply so many more safe requests than unsafe ones that an FPR of 4.8% produces more false alarms than the classifier finds true positives. This is why a guardrail benchmarked on a balanced set falls apart in production, and why FPR, not accuracy, is the number to negotiate over.</p><p class="status_mfC7">Showing a synthetic held-out set.</p></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-setting-up-the-environment">5. Setting Up the Environment<a href="https://kobkrit.com/en/blog/llm-09-benchmarking#5-setting-up-the-environment" class="hash-link" aria-label="Direct link to 5. Setting Up the Environment" title="Direct link to 5. Setting Up the Environment" translate="no">​</a></h2>
<p>Open Colab and pick <strong>Runtime → Change runtime type → T4 GPU</strong> (the free tier is enough).
This chapter is <strong>pure inference</strong> — no optimizer, no gradients — sweeping every 0.6B-sized checkpoint
takes about <strong>15 minutes</strong> in total, more relaxed than any chapter so far.</p>
<div class="theme-admonition theme-admonition-danger admonition_xJq3 alert alert--danger"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"></path></svg></span>The series-wide warning worth re-reading every chapter</div><div class="admonitionContent_BuS1"><p>The Colab T4 is Turing architecture (SM 7.5), which <strong>does not support bfloat16</strong> and <strong>does not support FlashAttention-2</strong>.</p><p>But Qwen3-0.6B's <code>config.json</code> declares <code>torch_dtype: bfloat16</code>.
So <code>torch_dtype="auto"</code> is <strong>a trap</strong> — your code will crash or run bizarrely slowly without telling you why.</p><div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">torch_dtype</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">float16      </span><span class="token comment" style="color:#999988;font-style:italic"># not bfloat16</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">attn_implementation</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"sdpa"</span><span class="token plain">     </span><span class="token comment" style="color:#999988;font-style:italic"># not flash_attention_2</span><br></span></code></pre></div></div><p>This chapter doesn't train, so there's no <code>fp16=True</code> in TrainingArguments to worry about — load the model in fp16 and measure away.</p></div></div>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">cap </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_device_capability</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"compute capability:"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cap</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                    </span><span class="token comment" style="color:#999988;font-style:italic"># T4 = (7, 5)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"native bf16:"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cap</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&gt;=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                   </span><span class="token comment" style="color:#999988;font-style:italic"># T4 -&gt; False</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"torch says   :"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">is_bf16_supported</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># T4 -&gt; True (counts emulation!)</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span><code>is_bf16_supported()</code> lies on a T4</div><div class="admonitionContent_BuS1"><p>Recent torch returns <code>True</code> on a T4 because it counts <strong>emulation</strong> as support — which is far slower than fp16.
Gate on <strong>compute capability ≥ 8.0</strong> (Ampere and up) instead. This was a real bug, caught only by running the notebook on Colab.</p></div></div>
<p>Every checkpoint in the series builds on the same Qwen3-0.6B base, and most are LoRA adapters,
so we load the base weights <strong>once</strong> and swap adapters in and out one at a time — VRAM doesn't grow with the checkpoint count:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">CHECKPOINTS </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"base"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">        </span><span class="token boolean" style="color:#36acaa">None</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">                                   </span><span class="token comment" style="color:#999988;font-style:italic"># plain Qwen3-0.6B-Base</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"01-cpt"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">      </span><span class="token string" style="color:#e3116c">"kobkrit/qwen3-0.6b-th-cpt"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">            </span><span class="token comment" style="color:#999988;font-style:italic"># full weights (chapter 1)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"02-sft"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">      </span><span class="token string" style="color:#e3116c">"kobkrit/qwen3-0.6b-th-sft-lora"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">       </span><span class="token comment" style="color:#999988;font-style:italic"># LoRA (chapter 2)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"03-ppo"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">      </span><span class="token string" style="color:#e3116c">"kobkrit/qwen3-0.6b-th-ppo-lora"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">       </span><span class="token comment" style="color:#999988;font-style:italic"># LoRA (chapter 3)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"04-dpo"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">      </span><span class="token string" style="color:#e3116c">"kobkrit/qwen3-0.6b-th-dpo-lora"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">       </span><span class="token comment" style="color:#999988;font-style:italic"># LoRA (chapter 4)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"05-grpo"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">     </span><span class="token string" style="color:#e3116c">"kobkrit/qwen3-0.6b-th-grpo-lora"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># LoRA (chapter 5)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"06-ctx-dist"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"kobkrit/qwen3-0.6b-th-ctxdist-lora"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># LoRA (chapter 6)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"07-distill"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">  </span><span class="token string" style="color:#e3116c">"kobkrit/qwen3-0.6b-th-distill"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># the student from chapter 7</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"08-guarded"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">  </span><span class="token string" style="color:#e3116c">"kobkrit/qwen3-0.6b-th-sft-lora+guard"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># model + filter from chapter 8</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-preparing-the-data">6. Preparing the Data<a href="https://kobkrit.com/en/blog/llm-09-benchmarking#6-preparing-the-data" class="hash-link" aria-label="Direct link to 6. Preparing the Data" title="Direct link to 6. Preparing the Data" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="61-scb10xthai_exam--real-thai-standardized-exams">6.1 <code>scb10x/thai_exam</code> — real Thai standardized exams<a href="https://kobkrit.com/en/blog/llm-09-benchmarking#61-scb10xthai_exam--real-thai-standardized-exams" class="hash-link" aria-label="Direct link to 61-scb10xthai_exam--real-thai-standardized-exams" title="Direct link to 61-scb10xthai_exam--real-thai-standardized-exams" translate="no">​</a></h3>
<p>A multiple-choice set drawn from Thailand's actual national exams (O-NET, IC, TGAT, TPAT-1, A-Level —
the university-entrance and professional-certification exams Thai students sit).
This is the Thai benchmark public leaderboards use most, and it is the backbone of this chapter.</p>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>Check the license on the dataset card before use — the notebook enforces this step</div><div class="admonitionContent_BuS1"><p>Real exams have owners, so datasets derived from real exams carry terms of use you <strong>must read yourself</strong> on
the Hugging Face dataset card before loading — don't guess, don't copy code past this step.
The notebook pulls up the card's metadata, prints it, and stops to wait for your confirmation before continuing:</p><div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> huggingface_hub </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> DatasetCard</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">card </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> DatasetCard</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">load</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"scb10x/thai_exam"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"license:"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> card</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">data</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"license"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">card</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">text</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">:</span><span class="token number" style="color:#36acaa">1500</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># read the card's terms with your own eyes</span><br></span></code></pre></div></div><p>If the license doesn't permit your use (say, commercial), stop right there.
An evaluation that begins by violating the data's terms can never be called rigorous.</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="62-visai-aigsm8k-thai--generative-math-problems">6.2 <code>VISAI-AI/gsm8k-thai</code> — generative math problems<a href="https://kobkrit.com/en/blog/llm-09-benchmarking#62-visai-aigsm8k-thai--generative-math-problems" class="hash-link" aria-label="Direct link to 62-visai-aigsm8k-thai--generative-math-problems" title="Direct link to 62-visai-aigsm8k-thai--generative-math-problems" translate="no">​</a></h3>
<p>GSM8K translated into Thai: math word problems where the model must <strong>produce the answer itself</strong>, with no choices to compare log-likelihoods over —
the dedicated proving ground for generative exact-match mode (check the license on the card here too).</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="63-kobeval-th--the-series-own-100-question-set">6.3 KobEval-TH — the series' own 100-question set<a href="https://kobkrit.com/en/blog/llm-09-benchmarking#63-kobeval-th--the-series-own-100-question-set" class="hash-link" aria-label="Direct link to 6.3 KobEval-TH — the series' own 100-question set" title="Direct link to 6.3 KobEval-TH — the series' own 100-question set" translate="no">​</a></h3>
<p>The benchmark used since chapter 1 (TH-KNOW Thai knowledge, instruction following, <code>th_ratio</code> —
the fraction of Thai characters in a model's output, this series' standing metric for catching silent drift into English).
Its virtue is not size — 100 questions gives ±10-point CIs, per figure 9.1 —
but <strong>constancy</strong>: every chapter measures with the same set, the same way, so cross-chapter comparison is real.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="64-thai-normalization--where-thai-exact-match-dies-most-often">6.4 Thai normalization — where Thai exact-match dies most often<a href="https://kobkrit.com/en/blog/llm-09-benchmarking#64-thai-normalization--where-thai-exact-match-dies-most-often" class="hash-link" aria-label="Direct link to 6.4 Thai normalization — where Thai exact-match dies most often" title="Direct link to 6.4 Thai normalization — where Thai exact-match dies most often" translate="no">​</a></h3>
<p>Thai has its own digit characters (๐๑๒๓๔๕๖๗๘๙ for 0–9), still common in official documents.
So "๕๐", "50", and " 50 " are all the same answer — but Python's <code>==</code> doesn't think so:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> re</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">THAI_DIGITS </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">str</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">maketrans</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"๐๑๒๓๔๕๖๗๘๙"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"0123456789"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">normalize_thai</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">s</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">str</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token builtin">str</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    s </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> s</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">strip</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">translate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">THAI_DIGITS</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># Thai digits ๐-๙ → Arabic</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    s </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> s</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">replace</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">","</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                 </span><span class="token comment" style="color:#999988;font-style:italic"># 1,000 → 1000</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    s </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> re</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sub</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">r"\s+"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> s</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">              </span><span class="token comment" style="color:#999988;font-style:italic"># Thai doesn't separate words with spaces — drop them all</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> s</span><br></span></code></pre></div></div>
<p>The notebook ships a small unit test for this function, because <strong>a bug in the grader is contamination in reverse</strong>:
it makes the model look systematically worse than it is, and no error will ever tell you.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-the-main-code">7. The Main Code<a href="https://kobkrit.com/en/blog/llm-09-benchmarking#7-the-main-code" class="hash-link" aria-label="Direct link to 7. The Main Code" title="Direct link to 7. The Main Code" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="71-mode-1--log-likelihood-multiple-choice-equation-32-literally">7.1 Mode 1 — log-likelihood multiple-choice (equation 3.2, literally)<a href="https://kobkrit.com/en/blog/llm-09-benchmarking#71-mode-1--log-likelihood-multiple-choice-equation-32-literally" class="hash-link" aria-label="Direct link to 7.1 Mode 1 — log-likelihood multiple-choice (equation 3.2, literally)" title="Direct link to 7.1 Mode 1 — log-likelihood multiple-choice (equation 3.2, literally)" translate="no">​</a></h3>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> torch</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token decorator annotation punctuation" style="color:#393A34">@torch</span><span class="token decorator annotation punctuation" style="color:#393A34">.</span><span class="token decorator annotation punctuation" style="color:#393A34">no_grad</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">score_mc</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">model</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> tok</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> question</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> choices</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> gamma</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    scores </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> c </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> choices</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        q_ids    </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> tok</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">question</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> return_tensors</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"pt"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">input_ids</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        full_ids </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> tok</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">question </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> c</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> return_tensors</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"pt"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">input_ids</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        logits   </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> model</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">full_ids</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">logits</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">:</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">:</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        targets  </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> full_ids</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">:</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">:</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        logp </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">log_softmax</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">logits</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">gather</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> targets</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">unsqueeze</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">squeeze</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        ans </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> logp</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> q_ids</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">shape</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">:</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain">            </span><span class="token comment" style="color:#999988;font-style:italic"># the choice's tokens only</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        scores</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">append</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ans</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">sum</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">item</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> </span><span class="token builtin">len</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ans</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">**</span><span class="token plain"> gamma</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">tensor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">scores</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">argmax</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">         </span><span class="token comment" style="color:#999988;font-style:italic"># not a single token is generated</span><br></span></code></pre></div></div>
<p>Strengths: 100% deterministic, fast, works even on base models that can't yet answer in sentences.
Limits: multiple-choice only, and the score depends on <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>γ</mi></mrow><annotation encoding="application/x-tex">\gamma</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0556em">γ</span></span></span></span>, as section 9 shows.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="72-mode-2--generative-exact-match">7.2 Mode 2 — generative exact-match<a href="https://kobkrit.com/en/blog/llm-09-benchmarking#72-mode-2--generative-exact-match" class="hash-link" aria-label="Direct link to 7.2 Mode 2 — generative exact-match" title="Direct link to 7.2 Mode 2 — generative exact-match" translate="no">​</a></h3>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token decorator annotation punctuation" style="color:#393A34">@torch</span><span class="token decorator annotation punctuation" style="color:#393A34">.</span><span class="token decorator annotation punctuation" style="color:#393A34">no_grad</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">score_generative</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">model</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> tok</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> question</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> gold</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    msgs </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"role"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"user"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"content"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> question</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ids </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> tok</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">apply_chat_template</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">msgs</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> add_generation_prompt</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                                  enable_thinking</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">False</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># the series' standing contract</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                                  return_tensors</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"pt"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    out </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> model</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">generate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ids</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> max_new_tokens</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">256</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> do_sample</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">False</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># greedy</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    pred </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> tok</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">decode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">out</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ids</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">shape</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">:</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> skip_special_tokens</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">normalize_thai</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">extract_answer</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">pred</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> normalize_thai</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">gold</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p><code>extract_answer</code> pulls the final answer out of the text (the last number for GSM8K-TH,
the choice letter for multiple-choice) — this function is itself a score-affecting decision, and must be reported.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="73-mode-3--llm-as-judge-with-a-written-rubric">7.3 Mode 3 — LLM-as-judge with a written rubric<a href="https://kobkrit.com/en/blog/llm-09-benchmarking#73-mode-3--llm-as-judge-with-a-written-rubric" class="hash-link" aria-label="Direct link to 7.3 Mode 3 — LLM-as-judge with a written rubric" title="Direct link to 7.3 Mode 3 — LLM-as-judge with a written rubric" translate="no">​</a></h3>
<p>The notebook's rubric is written in Thai, since it grades Thai answers; in English it reads:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">JUDGE_RUBRIC </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token triple-quoted-string string" style="color:#e3116c">"""You are an exam grader. Judge by this rubric only:</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">1 = the substance matches the gold answer (accept spelling variants,</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">    Thai/Arabic numerals, and different phrasings with the same meaning)</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">0 = wrong, off-topic, or no answer</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">Do not award points for elegant language. Do not award points for length.</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">Answer in JSON only: {"score": 0 or 1, "reason": "brief"}</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="display:inline-block;color:#e3116c"></span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">Question: {q}</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">Gold answer: {gold}</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">Model's answer: {pred}"""</span><br></span></code></pre></div></div>
<p>The judge must be a model far stronger than the one being judged. The notebook is designed to plug in
any OpenAI-compatible endpoint, and it <strong>always records the rubric + the judge's model name into <code>results.json</code></strong>
— judge results that don't say who the judge was and what rubric it used are just another kind of rumor.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="74-what-the-professionals-use--lm-evaluation-harness">7.4 What the professionals use — lm-evaluation-harness<a href="https://kobkrit.com/en/blog/llm-09-benchmarking#74-what-the-professionals-use--lm-evaluation-harness" class="hash-link" aria-label="Direct link to 7.4 What the professionals use — lm-evaluation-harness" title="Direct link to 7.4 What the professionals use — lm-evaluation-harness" translate="no">​</a></h3>
<p>We wrote the three modes ourselves to see the internals, but real work should stand on tooling the community has vetted:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">pip install lm-eval</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">lm_eval --tasks list | grep -i thai        # first check which task names actually exist in your version</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">lm_eval --model hf \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    --model_args pretrained=Qwen/Qwen3-0.6B,dtype=float16 \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    --tasks thai_exam \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    --num_fewshot 5 \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    --batch_size 8 \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    --seed 42 \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    --log_samples --output_path results/harness</span><br></span></code></pre></div></div>
<p><code>--log_samples</code> is the most important flag on those lines: it records per-question answers,
which lets us (1) compute Wilson CIs ourselves, (2) pair questions for McNemar, and (3) trace exactly why each wrong answer was wrong.
Notice that the harness reports both <code>acc</code> and <code>acc_norm</code> — those are <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>γ</mi><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">\gamma=0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0556em">γ</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0</span></span></span></span> and the length-normalised form of equation 3.2.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="8-results">8. Results<a href="https://kobkrit.com/en/blog/llm-09-benchmarking#8-results" class="hash-link" aria-label="Direct link to 8. Results" title="Direct link to 8. Results" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-whole-series-summary-figure--every-checkpoint-on-one-axis-with-error-bars">The whole-series summary figure — every checkpoint on one axis, with error bars<a href="https://kobkrit.com/en/blog/llm-09-benchmarking#the-whole-series-summary-figure--every-checkpoint-on-one-axis-with-error-bars" class="hash-link" aria-label="Direct link to The whole-series summary figure — every checkpoint on one axis, with error bars" title="Direct link to The whole-series summary figure — every checkpoint on one axis, with error bars" translate="no">​</a></h3>
<p>The notebook ends with the figure this series has walked 8 chapters to draw:
every checkpoint from chapters 1–8 on a single axis, measured under a single contract, with a Wilson 95% CI on every bar
(the real numbers live in the notebook's <code>results.json</code> — so this table stays <code>?</code> until you run it yourself):</p>
<table><thead><tr><th>checkpoint</th><th>ThaiExam (95% CI)</th><th>GSM8K-TH (95% CI)</th><th>KobEval-TH (95% CI)</th><th><code>th_ratio</code></th></tr></thead><tbody><tr><td>Qwen3-0.6B-Base</td><td>?</td><td>?</td><td>?</td><td>?</td></tr><tr><td>Chapter 1 — CPT</td><td>?</td><td>?</td><td>?</td><td>?</td></tr><tr><td>Chapter 2 — SFT-LoRA</td><td>?</td><td>?</td><td>?</td><td>?</td></tr><tr><td>Chapter 3 — PPO</td><td>?</td><td>?</td><td>?</td><td>?</td></tr><tr><td>Chapter 4 — DPO</td><td>?</td><td>?</td><td>?</td><td>?</td></tr><tr><td>Chapter 5 — GRPO</td><td>?</td><td>?</td><td>?</td><td>?</td></tr><tr><td>Chapter 6 — Context distillation</td><td>?</td><td>?</td><td>?</td><td>?</td></tr><tr><td>Chapter 7 — Model distillation</td><td>?</td><td>?</td><td>?</td><td>?</td></tr><tr><td>Chapter 8 — SFT + guardrail</td><td>?</td><td>?</td><td>?</td><td>?</td></tr></tbody></table>
<p>What to expect before running (always write the hypothesis down before looking — the best habit this chapter can teach):
most checkpoints' CIs will <strong>overlap</strong> on ThaiExam, because our training runs are minuscule next to pretraining.
The differences that should survive their error bars are <code>th_ratio</code> (chapter 4 attacked it head-on)
and instruction-format compliance on KobEval-TH (the work of chapter 2's SFT).
If the real figure comes out differently, that is something to be excited about, not something to hide.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="one-answer-three-judges-three-scores">One answer, three judges, three scores<a href="https://kobkrit.com/en/blog/llm-09-benchmarking#one-answer-three-judges-three-scores" class="hash-link" aria-label="Direct link to One answer, three judges, three scores" title="Direct link to One answer, three judges, three scores" translate="no">​</a></h3>
<div class="root_IS5b"><div class="picker_cO8e"><span class="pickerLabel_sE2x" id="llmcourse-bac-picker">Prompt</span><div class="pickerButtons_j7L1" role="tablist" aria-labelledby="llmcourse-bac-picker"><button type="button" role="tab" id="llmcourse-bac-tab-0" aria-selected="true" aria-controls="llmcourse-bac-panel-0" tabindex="0" class="pickerButton_gFO3 pickerButtonActive_xIUp">1</button><button type="button" role="tab" id="llmcourse-bac-tab-1" aria-selected="false" aria-controls="llmcourse-bac-panel-1" tabindex="-1" class="pickerButton_gFO3">2</button></div></div><blockquote class="prompt_O4Wp" lang="th"><span class="promptLabel_h2F6">Prompt</span>อธิบายว่าทำไมท้องฟ้าถึงเป็นสีฟ้า แบบสั้น ๆ</blockquote><div class="grid_h_9T" id="llmcourse-bac-panel-0" role="tabpanel" aria-labelledby="llmcourse-bac-tab-0" style="grid-template-columns:repeat(auto-fit, minmax(min(100%, 260px), 1fr))"><article class="card_S27b"><header class="cardHeader_w7wJ"><h4 class="cardTitle_NUQN">base</h4><div class="badges_pXcS"><span class="badge_wUaQ badgeBad_WFwi" title="Share of non-whitespace characters that are Thai script">Thai 18%</span><span class="badge_wUaQ">41 tokens</span></div></header><div class="output_VSGg" lang="th">The sky appears blue because of Rayleigh scattering. ท้องฟ้า is blue เพราะ light scatter ครับ. Shorter wavelengths scatter more than longer ones.</div></article><article class="card_S27b"><header class="cardHeader_w7wJ"><h4 class="cardTitle_NUQN">sft</h4><div class="badges_pXcS"><span class="badge_wUaQ badgeGood_MHH_" title="Share of non-whitespace characters that are Thai script">Thai 99%</span><span class="badge_wUaQ">78 tokens</span></div></header><div class="output_VSGg" lang="th">ท้องฟ้าเป็นสีฟ้าเพราะแสงอาทิตย์กระทบกับโมเลกุลของอากาศแล้วเกิดการกระเจิงแบบเรย์ลี ซึ่งแสงสีน้ำเงินที่มีความยาวคลื่นสั้นกว่าจะกระเจิงได้มากกว่าแสงสีแดง เราจึงมองเห็นท้องฟ้าเป็นสีฟ้าครับ</div></article></div><p class="status_mfC7">Showing the built-in sample.</p></div>
<p>The samples above are real model answers where <strong>the same question earns different scores under the three modes</strong> —
for instance, answering "๕๐ บาท" ("50 baht", with the amount in Thai numerals): exact-match without normalization gives 0, with normalization gives 1, and the judge gives 1.
Or an answer whose reasoning is right all the way but botches the final number, which the judge sometimes grades more kindly than the answer key.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-honesty-exercise-reproduce-a-leaderboard-number--or-find-out-exactly-why-you-cant">The honesty exercise: reproduce a leaderboard number — or find out exactly why you can't<a href="https://kobkrit.com/en/blog/llm-09-benchmarking#the-honesty-exercise-reproduce-a-leaderboard-number--or-find-out-exactly-why-you-cant" class="hash-link" aria-label="Direct link to The honesty exercise: reproduce a leaderboard number — or find out exactly why you can't" title="Direct link to The honesty exercise: reproduce a leaderboard number — or find out exactly why you can't" translate="no">​</a></h3>
<p>The notebook closes with the task that teaches more than every other cell combined:
open a public leaderboard that reports Qwen3-0.6B on ThaiExam, write down the number they announce,
then try to produce <strong>that same number</strong> from your own machine.</p>
<p>If it doesn't match (and on the first attempt it usually doesn't), <strong>do not stop at "close enough"</strong> — walk the variables one at a time:</p>
<ol>
<li class=""><strong>The prompt template</strong> — does it match theirs? (figure 9.3 already told you this alone is more than enough)</li>
<li class=""><strong>The scoring mode</strong> — did they use log-likelihood or generative, <code>acc</code> or <code>acc_norm</code> (<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>γ</mi></mrow><annotation encoding="application/x-tex">\gamma</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0556em">γ</span></span></span></span>!)</li>
<li class=""><strong>Shot count</strong> — 0-shot and 5-shot are different worlds</li>
<li class=""><strong><code>enable_thinking</code></strong> — Qwen3 has a private thinking mode; on/off changes both score and runtime</li>
<li class=""><strong>Dataset version and subset</strong> — thai_exam has five subjects; how did they average?</li>
<li class=""><strong>Batch size during generation</strong> — different padding genuinely can change greedy outputs</li>
</ol>
<p>A report saying <em>"we got 41.8 while the leaderboard reports 43.5, and the cause is that they used 5-shot with acc_norm while we used 0-shot with acc"</em>
is worth more than a report whose number matches exactly but can't explain why —
because the first proves you <strong>control your own measuring instrument</strong>. The second may just be lucky.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="9-comparison">9. Comparison<a href="https://kobkrit.com/en/blog/llm-09-benchmarking#9-comparison" class="hash-link" aria-label="Direct link to 9. Comparison" title="Direct link to 9. Comparison" translate="no">​</a></h2>
<p>Every previous chapter compared "many models, one measurement." This chapter flips it:
<strong>one model (the SFT checkpoint from chapter 2), many measurements</strong>, watching how far the score swings
under decisions that normally go unreported:</p>
<table><thead><tr><th>Setting (each row differs from the contract in exactly one place)</th><th>ThaiExam</th><th>GSM8K-TH</th><th>KobEval-TH</th></tr></thead><tbody><tr><td>The series contract (loglik <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>γ</mi><mo>=</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">\gamma=1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0556em">γ</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">1</span></span></span></span>, 0-shot, thinking off)</td><td>?</td><td>—</td><td>?</td></tr><tr><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>γ</mi><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">\gamma = 0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0556em">γ</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0</span></span></span></span> instead of <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>γ</mi><mo>=</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">\gamma = 1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0556em">γ</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">1</span></span></span></span></td><td>?</td><td>—</td><td>?</td></tr><tr><td>generative exact-match instead of loglik</td><td>?</td><td>?</td><td>?</td></tr><tr><td>5-shot instead of 0-shot</td><td>?</td><td>?</td><td>?</td></tr><tr><td>LLM-as-judge instead of exact-match</td><td>?</td><td>?</td><td>?</td></tr><tr><td><code>enable_thinking=True</code></td><td>?</td><td>?</td><td>?</td></tr></tbody></table>
<p>(GSM8K-TH has no choices to compare log-likelihoods over, so only generative mode applies — that cell is blank on purpose.)</p>
<p>Every row is <strong>the same model, the same weights to the byte</strong>. What you should see is the score swinging by several points —
more than the gap between models on a typical leaderboard. And that is the final answer to the question this chapter opened with:
when different groups report numbers that disagree, most of the time <strong>nobody is lying</strong> — they just never measured with the same instrument.</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>The series' measurement contract — which every previous chapter has honored</div><div class="admonitionContent_BuS1"><p>Every number in chapters 1–8 was measured under exactly the same conditions:</p><ul>
<li class=""><strong>greedy decoding</strong> (<code>do_sample=False</code>)</li>
<li class=""><strong><code>max_new_tokens=256</code></strong></li>
<li class=""><strong><code>enable_thinking=False</code></strong></li>
<li class=""><strong><code>seed=42</code></strong></li>
<li class="">KobEval-TH — <strong>the same 100 questions, never edited along the way</strong> + <strong>a Wilson 95% CI on every number</strong></li>
</ul><p>When chapter 1 declared these conditions, it looked like fussiness. By this line you know why:
without this contract, the table in section 8 couldn't be compared across chapters at all — it would be 9 rows measured with 9 different rulers.</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="traps-to-watch-for">Traps to watch for<a href="https://kobkrit.com/en/blog/llm-09-benchmarking#traps-to-watch-for" class="hash-link" aria-label="Direct link to Traps to watch for" title="Direct link to Traps to watch for" translate="no">​</a></h3>
<p><strong>1. Contamination — the exam leaked into our own training data</strong>
The notebook runs the equation 3.5 check (20-char-grams) between the ThaiExam / KobEval-TH questions
and the corpora we trained on in chapters 1–2 (<code>thaigov-v2</code> and the synthetic instruct set).
The place to watch: thaigov is Thai government documents, and ThaiExam includes questions on government rules, regulations, and civic knowledge —
real overlap is plausible. If you find hits, <strong>report them per-question and exclude them from the summary</strong>, don't look away.
(The actual scan output prints in the notebook — the hit count is <code>?</code> until you run it.)</p>
<p><strong>2. Comparing numbers across papers with different templates</strong>
"Our model got 45 on ThaiExam and that paper reports 43" means nothing at all
if the template, the shot count, and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>γ</mi></mrow><annotation encoding="application/x-tex">\gamma</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0556em">γ</span></span></span></span> differ — only numbers measured <strong>under the same harness, same config</strong> are comparable.</p>
<p><strong>3. Giving one model the chat template and not the other</strong>
Scoring a base model through a chat template = handicapping it for free. Scoring an instruct model without its template = the same handicap.
The section 8 table contains both kinds (CPT is base, the rest are chat),
so the notebook prints the actual first prompt for every model for you to eyeball — the one line that keeps the whole table fair.</p>
<p><strong>4. The judge favors its own family (self-enhancement bias)</strong>
Multiple studies find LLM judges score answers written in their own family's style higher than they should.
If the judge and the judged are relatives, the numbers come out suspiciously sweet —
the correct fix isn't to find a "neutral" judge (there isn't one) but to <strong>always report the judge's name</strong>, and cross-check with a judge from another family when results are close.</p>
<p><strong>5. Thai exact-match breaking on Thai numerals</strong>
The model answers "๕๐", the answer key says "50" — instant zero if you forgot <code>normalize_thai</code>.
And this bug doesn't spread evenly: a model CPT-trained on government documents (which use Thai numerals heavily) loses more points than its peers.
It becomes a systematic bias that picks on specific models — the grader must be fair before anyone talks about rankings.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="10-summary">10. Summary<a href="https://kobkrit.com/en/blog/llm-09-benchmarking#10-summary" class="hash-link" aria-label="Direct link to 10. Summary" title="Direct link to 10. Summary" translate="no">​</a></h2>
<ul>
<li class=""><strong>Accuracy without a CI is a rumor</strong> — n=100 gives ±10 points, and most leaderboard gaps are smaller than that</li>
<li class=""><strong>Wilson is not a luxury</strong> — the normal approximation gives a zero-width CI at <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>p</mi><mo>^</mo></mover><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">\hat p = 0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal">p</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.1667em"><span class="mord">^</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0</span></span></span></span>, exactly where we need it most</li>
<li class=""><strong>A score is a property of (model × method × exam × n)</strong> — scoring mode, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>γ</mi></mrow><annotation encoding="application/x-tex">\gamma</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord mathnormal" style="margin-right:0.0556em">γ</span></span></span></span>, shots, and template can move a score more than models actually differ</li>
<li class=""><strong>Two models on the same exam: use McNemar</strong> — the questions they agree on are not evidence</li>
<li class=""><strong>pass@k needs the binomial estimator</strong> — the plug-in estimator is biased, by Jensen</li>
<li class=""><strong>Scan for contamination before believing a score</strong>, especially when training data and exams come from neighboring domains</li>
<li class=""><strong>Declare a measurement contract on day one and never touch it again</strong> — this series': greedy, 256 tokens, thinking off, seed 42</li>
<li class=""><strong>Reproducing someone's number and explaining the difference</strong> beats matching it without knowing why</li>
</ul>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>Limitations of this experiment</div><div class="admonitionContent_BuS1"><p><strong>Benchmarks measure what is easy to measure, not what matters.</strong> Auto-gradable multiple-choice became the standard,
but real users don't arrive with four labeled options — they arrive with long questions, personal context, and expectations no accuracy score captures.</p><p><strong>A high ThaiExam score does not mean useful to Thai users.</strong> A model that aces A-Level exam questions
may draft official correspondence poorly, answer customers unnaturally, or be too stiff for anyone to want to use.
The link between exam scores and real-world value is far looser than leaderboards make it feel.</p><p>And the point worth pinning to the wall: <strong>30 human-evaluated questions on your product's real traffic
usually tell you more than 10,000 multiple-choice questions</strong> — yes, the CI is wider (equation 3.1 already told us how much),
but it measures <strong>the right thing</strong> coarsely, which always beats measuring the wrong thing precisely.
This chapter hands you statistical tools for both kinds of measurement — don't spend them only on the convenient kind.</p></div></div>
<p><strong>Next chapter:</strong> <a class="" href="https://kobkrit.com/en/blog/llm-10-deployment">Deployment</a> — a model that has been measured must go out and meet real users.
What quantization trades away, what machines can serve it, and how this chapter's numbers become production regression tests.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="references">References<a href="https://kobkrit.com/en/blog/llm-09-benchmarking#references" class="hash-link" aria-label="Direct link to References" title="Direct link to References" translate="no">​</a></h2>
<ol>
<li class="">Liang et al. (2022). <a href="https://arxiv.org/abs/2211.09110" target="_blank" rel="noopener noreferrer" class="">Holistic Evaluation of Language Models</a> — HELM: multi-dimensional evaluation instead of one number</li>
<li class="">Biderman et al. (2024). <a href="https://arxiv.org/abs/2405.14782" target="_blank" rel="noopener noreferrer" class="">Lessons from the Trenches on Reproducible Evaluation of Language Models</a> — lessons from lm-evaluation-harness on reproducible evaluation</li>
<li class="">Miller (2024). <a href="https://arxiv.org/abs/2411.00640" target="_blank" rel="noopener noreferrer" class="">Adding Error Bars to Evals: A Statistical Approach to Language Model Evaluations</a> — why every accuracy number needs an error bar</li>
<li class="">Zheng et al. (2023). <a href="https://arxiv.org/abs/2306.05685" target="_blank" rel="noopener noreferrer" class="">Judging LLM-as-a-Judge with MT-Bench and Chatbot Arena</a> — LLM-as-a-judge and its biases</li>
<li class="">Chiang et al. (2024). <a href="https://arxiv.org/abs/2403.04132" target="_blank" rel="noopener noreferrer" class="">Chatbot Arena: An Open Platform for Evaluating LLMs by Human Preference</a> — ranking by real human preference</li>
<li class="">Hendrycks et al. (2020). <a href="https://arxiv.org/abs/2009.03300" target="_blank" rel="noopener noreferrer" class="">Measuring Massive Multitask Language Understanding</a> — MMLU: the template for multiple-choice benchmarks</li>
<li class="">Chen et al. (2021). <a href="https://arxiv.org/abs/2107.03374" target="_blank" rel="noopener noreferrer" class="">Evaluating Large Language Models Trained on Code</a> — the unbiased pass@k estimator used in section 9</li>
<li class="">Wilson (1927). <a href="https://doi.org/10.1080/01621459.1927.10502953" target="_blank" rel="noopener noreferrer" class="">Probable Inference, the Law of Succession, and Statistical Inference</a> — the Wilson interval used on every number in this series</li>
<li class="">McNemar (1947). <a href="https://doi.org/10.1007/BF02295996" target="_blank" rel="noopener noreferrer" class="">Note on the Sampling Error of the Difference Between Correlated Proportions or Percentages</a> — the paired test for two models on the same items</li>
</ol>
<hr>
<p><em>The writing, code and notebooks in this series are licensed under <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank" rel="noopener noreferrer" class="">CC BY-NC-SA 4.0</a> — reuse and adapt them freely with attribution, for non-commercial purposes, and share your adaptations under the same terms. Third-party models and datasets referenced here keep their own licences.</em></p>
<nav class="nav_RfLT" aria-label="Thai LLM tutorial series navigation"><p class="heading_XRWm">Thai LLM series<span class="progress_f8e8">Part 9 of 10</span></p><ol class="list_U31a"><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-01-continue-pretraining"><span class="number_u3BE" aria-hidden="true">1</span><span class="title_BPvL">Continue Pretraining</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-02-sft-lora"><span class="number_u3BE" aria-hidden="true">2</span><span class="title_BPvL">SFT and LoRA</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo"><span class="number_u3BE" aria-hidden="true">3</span><span class="title_BPvL">RLHF and PPO</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-04-dpo"><span class="number_u3BE" aria-hidden="true">4</span><span class="title_BPvL">DPO: Direct Preference Optimization</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-05-grpo"><span class="number_u3BE" aria-hidden="true">5</span><span class="title_BPvL">GRPO</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-06-context-distillation"><span class="number_u3BE" aria-hidden="true">6</span><span class="title_BPvL">Context Distillation</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-07-model-distillation"><span class="number_u3BE" aria-hidden="true">7</span><span class="title_BPvL">Model Distillation</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-08-guardrails"><span class="number_u3BE" aria-hidden="true">8</span><span class="title_BPvL">Guardrails</span></a></li><li class="item_Y10l"><span class="chip_DDpP chipCurrent_BGpo" aria-current="step"><span class="number_u3BE" aria-hidden="true">9</span><span class="title_BPvL">Benchmarking</span><span class="srOnly_owtF">(you are here)</span></span></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-10-deployment"><span class="number_u3BE" aria-hidden="true">10</span><span class="title_BPvL">Deployment</span></a></li></ol></nav>]]></content>
        <author>
            <name>Kobkrit Viriyayudhakorn</name>
            <uri>https://github.com/kobkrit</uri>
        </author>
        <category label="ai" term="ai"/>
        <category label="llm" term="llm"/>
        <category label="thai" term="thai"/>
        <category label="tutorial" term="tutorial"/>
        <category label="evaluation" term="evaluation"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[LLM 10/10] Deployment: You're Not Waiting on Compute, You're Waiting on Weights to Travel]]></title>
        <id>https://kobkrit.com/en/blog/llm-10-deployment</id>
        <link href="https://kobkrit.com/en/blog/llm-10-deployment"/>
        <updated>2026-07-20T12:00:00.000Z</updated>
        <summary type="html"><![CDATA[Computing the ceiling on LLM serving speed straight from the GPU datasheet, then measuring the real thing on a Colab T4 — KV cache, batching, quantisation, and why every optimisation is an attack on the same bottleneck]]></summary>
        <content type="html"><![CDATA[<p>Across nine chapters we've injected knowledge, taught format, arranged preferences, distilled models, kept models from breaking, and measured honestly.
But even our best model is still just a checkpoint file that nobody can call.
This final chapter puts it into real service, and proves the single sentence that governs every decision in LLM serving:
<strong>decoding one token at a time is not limited by compute power, it is limited by memory bandwidth</strong> —
we'll compute the speed ceiling from the GPU's datasheet before writing a single line of code, then measure the real thing against it.</p>
<a class="badge_rUYD" href="https://colab.research.google.com/github/kobkrit/thai-llm-tutorials/blob/main/notebooks/10_deployment.ipynb" target="_blank" rel="noopener noreferrer" aria-label="Open the notebook 10_deployment.ipynb in Google Colab (opens in a new tab)"><svg class="mark_NB8U" viewBox="0 0 24 24" width="20" height="20" aria-hidden="true" focusable="false"><mask id="llmcourse-colab-cut"><rect x="0" y="0" width="24" height="24" fill="#fff"></rect><circle cx="16.2" cy="12" r="6.1" fill="#000"></circle></mask><circle cx="8.4" cy="12" r="4.6" fill="none" stroke="#F9AB00" stroke-width="3.1" mask="url(#llmcourse-colab-cut)"></circle><circle cx="16.2" cy="12" r="4.6" fill="none" stroke="#E8710A" stroke-width="3.1"></circle></svg><span class="text_QXpz">Open in Colab</span><code class="notebook_ntO0">10_deployment.ipynb</code></a>
<nav class="nav_RfLT" aria-label="Thai LLM tutorial series navigation"><p class="heading_XRWm">Thai LLM series<span class="progress_f8e8">Part 10 of 10</span></p><ol class="list_U31a"><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-01-continue-pretraining"><span class="number_u3BE" aria-hidden="true">1</span><span class="title_BPvL">Continue Pretraining</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-02-sft-lora"><span class="number_u3BE" aria-hidden="true">2</span><span class="title_BPvL">SFT and LoRA</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo"><span class="number_u3BE" aria-hidden="true">3</span><span class="title_BPvL">RLHF and PPO</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-04-dpo"><span class="number_u3BE" aria-hidden="true">4</span><span class="title_BPvL">DPO: Direct Preference Optimization</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-05-grpo"><span class="number_u3BE" aria-hidden="true">5</span><span class="title_BPvL">GRPO</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-06-context-distillation"><span class="number_u3BE" aria-hidden="true">6</span><span class="title_BPvL">Context Distillation</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-07-model-distillation"><span class="number_u3BE" aria-hidden="true">7</span><span class="title_BPvL">Model Distillation</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-08-guardrails"><span class="number_u3BE" aria-hidden="true">8</span><span class="title_BPvL">Guardrails</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-09-benchmarking"><span class="number_u3BE" aria-hidden="true">9</span><span class="title_BPvL">Benchmarking</span></a></li><li class="item_Y10l"><span class="chip_DDpP chipCurrent_BGpo" aria-current="step"><span class="number_u3BE" aria-hidden="true">10</span><span class="title_BPvL">Deployment</span><span class="srOnly_owtF">(you are here)</span></span></li></ol></nav>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-the-problem">1. The Problem<a href="https://kobkrit.com/en/blog/llm-10-deployment#1-the-problem" class="hash-link" aria-label="Direct link to 1. The Problem" title="Direct link to 1. The Problem" translate="no">​</a></h2>
<p>You have a model trained and selected by the sweep in chapter 9. The next questions aren't machine learning questions at all:</p>
<table><thead><tr><th>The question from the person paying</th><th>The number that answers it</th></tr></thead><tbody><tr><td>How long does one user wait</td><td>p50 / p99 latency</td></tr><tr><td>How many concurrent users can we take</td><td>concurrency (Little's law)</td></tr><tr><td>How many GPUs do we need</td><td>throughput (tok/s)</td></tr><tr><td>How long a context can we allow</td><td>KV cache budget</td></tr></tbody></table>
<p>Most people answer these with "I tried <code>generate</code> and it felt fast," which isn't engineering.
And the way most benchmark articles answer them is usually meaningless, for very specific reasons:</p>
<ul>
<li class="">Reporting tok/s <strong>without stating batch size</strong> — 27 tok/s at batch 1 and 400 tok/s at batch 32 can be the exact same machine</li>
<li class="">Averaging <strong>prefill</strong> speed (reading the prompt) together with <strong>decode</strong> (producing the answer), when the two hit entirely different bottlenecks</li>
<li class="">Reporting post-quantisation speed <strong>without reporting quality</strong> — this is the original sin of the genre, and we'll come back to it several times in this chapter</li>
</ul>
<p>This chapter answers every question above with numbers we measure ourselves, on the same free machine we've used all series.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-what-were-going-to-do">2. What We're Going to Do<a href="https://kobkrit.com/en/blog/llm-10-deployment#2-what-were-going-to-do" class="hash-link" aria-label="Direct link to 2. What We're Going to Do" title="Direct link to 2. What We're Going to Do" translate="no">​</a></h2>
<p>We start from one physical fact and let everything flow out of it.</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>The core idea of this chapter</div><div class="admonitionContent_BuS1"><p>When decoding one token at a time at batch = 1, producing a single token requires <strong>reading every weight of the model out of HBM, one full pass</strong>,
while doing only ~2 FLOP of compute per weight — so the GPU sits idle waiting for data to arrive.
<strong>You aren't waiting on compute, you're waiting on weights to travel from memory to the chip.</strong></p><p>Every serving optimisation that means anything — batching, quantisation, paged KV cache —
attacks this same bottleneck from a different angle: <em>reduce the bytes that must travel, or get more out of each trip.</em></p></div></div>
<p>The plan for this chapter is straightforward, and I think it's the most self-proving experiment in the series:</p>
<ol>
<li class=""><strong>Compute the ceiling</strong> on decode speed from the T4's datasheet — without running anything yet</li>
<li class=""><strong>Measure reality</strong> with a server deliberately written badly first, and see how many times off the ceiling it lands</li>
<li class=""><strong>Close the gap</strong> step by step — static KV cache, <code>torch.compile</code>, continuous batching written by hand in about 60 lines — measuring again at every step so we know exactly how much each one bought</li>
<li class=""><strong>What it costs</strong> — quantise to int8 and nf4, then measure speed <em>and</em> quality on KobEval-TH, always together</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-the-equations">3. The Equations<a href="https://kobkrit.com/en/blog/llm-10-deployment#3-the-equations" class="hash-link" aria-label="Direct link to 3. The Equations" title="Direct link to 3. The Equations" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="31-the-memory-budget-at-serving-time">3.1 The memory budget at serving time<a href="https://kobkrit.com/en/blog/llm-10-deployment#31-the-memory-budget-at-serving-time" class="hash-link" aria-label="Direct link to 3.1 The memory budget at serving time" title="Direct link to 3.1 The memory budget at serving time" translate="no">​</a></h3>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>M</mi><mtext>  </mtext><mo>=</mo><mtext>  </mtext><munder><munder><mrow><mi>P</mi><mtext> </mtext><msub><mi>b</mi><mi>w</mi></msub></mrow><mo stretchy="true">⏟</mo></munder><mtext>weights</mtext></munder><mtext>  </mtext><mo>+</mo><mtext>  </mtext><munder><munder><mrow><mn>2</mn><mtext> </mtext><mi>L</mi><mtext> </mtext><msub><mi>n</mi><mrow><mi>k</mi><mi>v</mi></mrow></msub><mtext> </mtext><msub><mi>d</mi><mi>h</mi></msub><mtext> </mtext><mi>s</mi><mtext> </mtext><mi>B</mi><mtext> </mtext><msub><mi>b</mi><mrow><mi>k</mi><mi>v</mi></mrow></msub></mrow><mo stretchy="true">⏟</mo></munder><mtext>KV&nbsp;cache</mtext></munder><mtext>  </mtext><mo>+</mo><mtext>  </mtext><msub><mi>M</mi><mtext>act</mtext></msub></mrow><annotation encoding="application/x-tex">M \;=\; \underbrace{P\,b_w}_{\text{weights}} \;+\; \underbrace{2\,L\,n_{kv}\,d_h\,s\,B\,b_{kv}}_{\text{KV cache}} \;+\; M_{\text{act}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal" style="margin-right:0.109em">M</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.3147em;vertical-align:-1.6202em"></span><span class="minner munder"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-1.5159em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">weights</span></span></span></span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="minner munder"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span class="svg-align" style="top:-2.202em"><span class="pstrut" style="height:3em"></span><span class="stretchy" style="height:0.548em;min-width:1.6em"><span class="brace-left" style="height:0.548em"><svg xmlns="http://www.w3.org/2000/svg" width="400em" height="0.548em" viewBox="0 0 400000 548" preserveAspectRatio="xMinYMin slice"><path d="M0 6l6-6h17c12.688 0 19.313.3 20 1 4 4 7.313 8.3 10 13
 35.313 51.3 80.813 93.8 136.5 127.5 55.688 33.7 117.188 55.8 184.5 66.5.688
 0 2 .3 4 1 18.688 2.7 76 4.3 172 5h399450v120H429l-6-1c-124.688-8-235-61.7
-331-161C60.687 138.7 32.312 99.3 7 54L0 41V6z"></path></svg></span><span class="brace-center" style="height:0.548em"><svg xmlns="http://www.w3.org/2000/svg" width="400em" height="0.548em" viewBox="0 0 400000 548" preserveAspectRatio="xMidYMin slice"><path d="M199572 214
c100.7 8.3 195.3 44 280 108 55.3 42 101.7 93 139 153l9 14c2.7-4 5.7-8.7 9-14
 53.3-86.7 123.7-153 211-199 66.7-36 137.3-56.3 212-62h199568v120H200432c-178.3
 11.7-311.7 78.3-403 201-6 8-9.7 12-11 12-.7.7-6.7 1-18 1s-17.3-.3-18-1c-1.3 0
-5-4-11-12-44.7-59.3-101.3-106.3-170-141s-145.3-54.3-229-60H0V214z"></path></svg></span><span class="brace-right" style="height:0.548em"><svg xmlns="http://www.w3.org/2000/svg" width="400em" height="0.548em" viewBox="0 0 400000 548" preserveAspectRatio="xMaxYMin slice"><path d="M399994 0l6 6v35l-6 11c-56 104-135.3 181.3-238 232-57.3
 28.7-117 45-179 50H-300V214h399897c43.3-7 81-15 113-26 100.7-33 179.7-91 237
-174 2.7-5 6-9 10-13 .7-1 7.3-1 20-1h17z"></path></svg></span></span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1389em">P</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0269em">w</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.798em"><span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.6202em"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:2.1785em;vertical-align:-1.4841em"></span><span class="minner munder"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-1.5159em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">KV&nbsp;cache</span></span></span></span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="minner munder"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span class="svg-align" style="top:-2.202em"><span class="pstrut" style="height:3em"></span><span class="stretchy" style="height:0.548em;min-width:1.6em"><span class="brace-left" style="height:0.548em"><svg xmlns="http://www.w3.org/2000/svg" width="400em" height="0.548em" viewBox="0 0 400000 548" preserveAspectRatio="xMinYMin slice"><path d="M0 6l6-6h17c12.688 0 19.313.3 20 1 4 4 7.313 8.3 10 13
 35.313 51.3 80.813 93.8 136.5 127.5 55.688 33.7 117.188 55.8 184.5 66.5.688
 0 2 .3 4 1 18.688 2.7 76 4.3 172 5h399450v120H429l-6-1c-124.688-8-235-61.7
-331-161C60.687 138.7 32.312 99.3 7 54L0 41V6z"></path></svg></span><span class="brace-center" style="height:0.548em"><svg xmlns="http://www.w3.org/2000/svg" width="400em" height="0.548em" viewBox="0 0 400000 548" preserveAspectRatio="xMidYMin slice"><path d="M199572 214
c100.7 8.3 195.3 44 280 108 55.3 42 101.7 93 139 153l9 14c2.7-4 5.7-8.7 9-14
 53.3-86.7 123.7-153 211-199 66.7-36 137.3-56.3 212-62h199568v120H200432c-178.3
 11.7-311.7 78.3-403 201-6 8-9.7 12-11 12-.7.7-6.7 1-18 1s-17.3-.3-18-1c-1.3 0
-5-4-11-12-44.7-59.3-101.3-106.3-170-141s-145.3-54.3-229-60H0V214z"></path></svg></span><span class="brace-right" style="height:0.548em"><svg xmlns="http://www.w3.org/2000/svg" width="400em" height="0.548em" viewBox="0 0 400000 548" preserveAspectRatio="xMaxYMin slice"><path d="M399994 0l6 6v35l-6 11c-56 104-135.3 181.3-238 232-57.3
 28.7-117 45-179 50H-300V214h399897c43.3-7 81-15 113-26 100.7-33 179.7-91 237
-174 2.7-5 6-9 10-13 .7-1 7.3-1 20-1h17z"></path></svg></span></span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord">2</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal">L</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal">n</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0315em">k</span><span class="mord mathnormal mtight" style="margin-right:0.0359em">v</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">h</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal">s</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0502em">B</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0315em">k</span><span class="mord mathnormal mtight" style="margin-right:0.0359em">v</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.798em"><span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.4841em"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.109em">M</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:-0.109em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">act</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span></span>
<ul>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>P</mi></mrow><annotation encoding="application/x-tex">P</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal" style="margin-right:0.1389em">P</span></span></span></span> = parameter count, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>b</mi><mi>w</mi></msub></mrow><annotation encoding="application/x-tex">b_w</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0269em">w</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> = bytes per weight (fp16 = 2)</li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi></mrow><annotation encoding="application/x-tex">L</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal">L</span></span></span></span> = number of layers, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>n</mi><mrow><mi>k</mi><mi>v</mi></mrow></msub></mrow><annotation encoding="application/x-tex">n_{kv}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal">n</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0315em">k</span><span class="mord mathnormal mtight" style="margin-right:0.0359em">v</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> = number of <strong>key-value heads</strong>, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>d</mi><mi>h</mi></msub></mrow><annotation encoding="application/x-tex">d_h</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">h</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> = dimension per head, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>b</mi><mrow><mi>k</mi><mi>v</mi></mrow></msub></mrow><annotation encoding="application/x-tex">b_{kv}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0315em">k</span><span class="mord mathnormal mtight" style="margin-right:0.0359em">v</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> = bytes per value in the cache</li>
<li class=""><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>s</mi></mrow><annotation encoding="application/x-tex">s</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">s</span></span></span></span> = context length, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>B</mi></mrow><annotation encoding="application/x-tex">B</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal" style="margin-right:0.0502em">B</span></span></span></span> = number of sequences held at once</li>
<li class="">The leading 2 is one set each for K and V, while <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>M</mi><mtext>act</mtext></msub></mrow><annotation encoding="application/x-tex">M_{\text{act}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.109em">M</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:-0.109em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">act</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> at inference time is so small it's nearly droppable</li>
</ul>
<p>Substituting the real values from Qwen3-0.6B's <code>config.json</code> (<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi><mo>=</mo><mn>28</mn></mrow><annotation encoding="application/x-tex">L=28</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal">L</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">28</span></span></span></span>, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>n</mi><mrow><mi>k</mi><mi>v</mi></mrow></msub><mo>=</mo><mn>8</mn></mrow><annotation encoding="application/x-tex">n_{kv}=8</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal">n</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0315em">k</span><span class="mord mathnormal mtight" style="margin-right:0.0359em">v</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">8</span></span></span></span>, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>d</mi><mi>h</mi></msub><mo>=</mo><mn>128</mn></mrow><annotation encoding="application/x-tex">d_h=128</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">h</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">128</span></span></span></span>, fp16):</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mtext>KV/token</mtext><mtext>  </mtext><mo>=</mo><mtext>  </mtext><mn>2</mn><mo>×</mo><mn>28</mn><mo>×</mo><mn>8</mn><mo>×</mo><mn>128</mn><mo>×</mo><mn>2</mn><mtext>  </mtext><mo>=</mo><mtext>  </mtext><mn>114,688</mn><mtext>&nbsp;bytes</mtext><mtext>  </mtext><mo>=</mo><mtext>  </mtext><mn>112</mn><mtext>&nbsp;KiB&nbsp;exactly</mtext></mrow><annotation encoding="application/x-tex">\text{KV/token} \;=\; 2 \times 28 \times 8 \times 128 \times 2 \;=\; 114{,}688 \text{ bytes} \;=\; 112\ \text{KiB exactly}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord text"><span class="mord">KV/token</span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em"></span><span class="mord">2</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em"></span><span class="mord">28</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em"></span><span class="mord">8</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em"></span><span class="mord">128</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">2</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord">114</span><span class="mord"><span class="mpunct">,</span></span><span class="mord">688</span><span class="mord text"><span class="mord">&nbsp;bytes</span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord">112</span><span class="mspace">&nbsp;</span><span class="mord text"><span class="mord">KiB&nbsp;exactly</span></span></span></span></span></span>
<p>Beware the trap people fall into most often right here: Qwen3 uses grouped-query attention, so you must use <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>n</mi><mrow><mi>k</mi><mi>v</mi></mrow></msub><mo>=</mo><mn>8</mn></mrow><annotation encoding="application/x-tex">n_{kv}=8</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal">n</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0315em">k</span><span class="mord mathnormal mtight" style="margin-right:0.0359em">v</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">8</span></span></span></span>,
not the number of attention heads (16) — get that one value wrong and your answer doubles instantly.
And this 112 KiB figure isn't a number floating in an article. It's <code>assert</code>ed in the test suite of this site's widget
(<code>memoryMath.test.ts</code>) — the series' code and its prose are forced to agree.</p>
<p>Now multiply by the model's full context ceiling (<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>s</mi><mo>=</mo><mn>40,960</mn></mrow><annotation encoding="application/x-tex">s = 40{,}960</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">s</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.8389em;vertical-align:-0.1944em"></span><span class="mord">40</span><span class="mord"><span class="mpunct">,</span></span><span class="mord">960</span></span></span></span>, per the real <code>max_position_embeddings</code>):</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mn>114,688</mn><mo>×</mo><mn>40,960</mn><mtext>  </mtext><mo>=</mo><mtext>  </mtext><mn>4,697,620,480</mn><mtext>&nbsp;bytes</mtext><mtext>  </mtext><mo>≈</mo><mtext>  </mtext><mn>4.7</mn><mtext>&nbsp;GB</mtext><mtext>  </mtext><mo stretchy="false">(</mo><mo>≈</mo><mn>4.4</mn><mtext>&nbsp;GiB</mtext><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">114{,}688 \times 40{,}960 \;=\; 4{,}697{,}620{,}480 \text{ bytes} \;\approx\; 4.7\ \text{GB} \;(\approx 4.4\ \text{GiB})</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8389em;vertical-align:-0.1944em"></span><span class="mord">114</span><span class="mord"><span class="mpunct">,</span></span><span class="mord">688</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.8389em;vertical-align:-0.1944em"></span><span class="mord">40</span><span class="mord"><span class="mpunct">,</span></span><span class="mord">960</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord">4</span><span class="mord"><span class="mpunct">,</span></span><span class="mord">697</span><span class="mord"><span class="mpunct">,</span></span><span class="mord">620</span><span class="mord"><span class="mpunct">,</span></span><span class="mord">480</span><span class="mord text"><span class="mord">&nbsp;bytes</span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">≈</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord">4.7</span><span class="mspace">&nbsp;</span><span class="mord text"><span class="mord">GB</span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mopen">(</span><span class="mrel">≈</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord">4.4</span><span class="mspace">&nbsp;</span><span class="mord text"><span class="mord">GiB</span></span><span class="mclose">)</span></span></span></span></span>
<p><strong>For a single sequence</strong> — roughly <strong>3.9 times</strong> the entire model's weights (596M parameters × 2 bytes ≈ 1.19 GB).
This is the arithmetic reason long context is expensive: the budget isn't eaten by the model, it's eaten by <strong>the conversation's memory</strong>.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="32-the-most-important-equation-in-this-chapter--the-decode-ceiling">3.2 The most important equation in this chapter — the decode ceiling<a href="https://kobkrit.com/en/blog/llm-10-deployment#32-the-most-important-equation-in-this-chapter--the-decode-ceiling" class="hash-link" aria-label="Direct link to 3.2 The most important equation in this chapter — the decode ceiling" title="Direct link to 3.2 The most important equation in this chapter — the decode ceiling" translate="no">​</a></h3>
<p>Producing one token requires reading every weight once, plus the KV cache accumulated so far, so</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>t</mi><mtext>token</mtext></msub><mtext>  </mtext><mo>≳</mo><mtext>  </mtext><mfrac><mrow><msub><mi>M</mi><mtext>weights</mtext></msub><mo>+</mo><msub><mi>M</mi><mtext>KV</mtext></msub></mrow><mtext>BW</mtext></mfrac><mspace width="2em"></mspace><mo>⟹</mo><mspace width="2em"></mspace><mtext>tok/s</mtext><mtext>  </mtext><mo>≲</mo><mtext>  </mtext><mfrac><mrow><mn>320</mn><mtext>&nbsp;GB/s</mtext></mrow><mrow><mn>1.2</mn><mtext>&nbsp;GB</mtext></mrow></mfrac><mtext>  </mtext><mo>≈</mo><mtext>  </mtext><mn>266</mn></mrow><annotation encoding="application/x-tex">t_{\text{token}} \;\gtrsim\; \frac{M_{\text{weights}} + M_{\text{KV}}}{\text{BW}}
\qquad\Longrightarrow\qquad
\text{tok/s} \;\lesssim\; \frac{320\ \text{GB/s}}{1.2\ \text{GB}} \;\approx\; 266</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9592em;vertical-align:-0.2296em"></span><span class="mord"><span class="mord mathnormal">t</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">token</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel amsrm">≳</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.0463em;vertical-align:-0.686em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3603em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord text"><span class="mord">BW</span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.109em">M</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.109em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">weights</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.109em">M</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-left:-0.109em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">KV</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:2em"></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">⟹</span><span class="mspace" style="margin-right:2em"></span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord text"><span class="mord">tok/s</span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel amsrm">≲</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.113em;vertical-align:-0.686em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.427em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord">1.2</span><span class="mspace">&nbsp;</span><span class="mord text"><span class="mord">GB</span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord">320</span><span class="mspace">&nbsp;</span><span class="mord text"><span class="mord">GB/s</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">≈</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">266</span></span></span></span></span>
<p>320 GB/s is the T4's GDDR6 bandwidth straight off the datasheet — <strong>~266 tok/s is the theoretical ceiling at batch = 1</strong>.
No code on earth makes a T4 decode this model single-stream faster than that, because it's a limit of the wiring, not of the software.</p>
<p>Let's check that bandwidth really is the bottleneck: at 266 tok/s the compute load is <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>2</mn><mi>P</mi><mo>≈</mo><mn>1.19</mn></mrow><annotation encoding="application/x-tex">2P \approx 1.19</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord">2</span><span class="mord mathnormal" style="margin-right:0.1389em">P</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">≈</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">1.19</span></span></span></span> GFLOP/token,
totalling ~0.32 TFLOPS, or <strong>about 0.5%</strong> of the 65 TFLOPS (fp16) a T4 can do — the chip is 99.5% idle.
The notebook will measure the real number (far below the ceiling), and section 8 will explain and close that gap layer by layer.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="33-littles-law--sizing-the-system-you-must-support">3.3 Little's Law — sizing the system you must support<a href="https://kobkrit.com/en/blog/llm-10-deployment#33-littles-law--sizing-the-system-you-must-support" class="hash-link" aria-label="Direct link to 3.3 Little's Law — sizing the system you must support" title="Direct link to 3.3 Little's Law — sizing the system you must support" translate="no">​</a></h3>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>L</mi><mtext>  </mtext><mo>=</mo><mtext>  </mtext><mi>λ</mi><mtext> </mtext><mi>W</mi></mrow><annotation encoding="application/x-tex">L \;=\; \lambda\,W</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal">L</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal">λ</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.1389em">W</span></span></span></span></span>
<p>The number of jobs in the system (<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi></mrow><annotation encoding="application/x-tex">L</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal">L</span></span></span></span>) equals the arrival rate (<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>λ</mi></mrow><annotation encoding="application/x-tex">\lambda</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal">λ</span></span></span></span>) times the mean time per job (<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>W</mi></mrow><annotation encoding="application/x-tex">W</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal" style="margin-right:0.1389em">W</span></span></span></span>) — always true, with no assumptions about the distribution.
You can size a system with it immediately: if users arrive at <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>λ</mi><mo>=</mo><mn>5</mn></mrow><annotation encoding="application/x-tex">\lambda = 5</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal">λ</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">5</span></span></span></span> requests/second and each answer takes <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>W</mi><mo>=</mo><mn>2</mn></mrow><annotation encoding="application/x-tex">W = 2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal" style="margin-right:0.1389em">W</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">2</span></span></span></span> seconds,
the system must hold <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi><mo>=</mo><mn>10</mn></mrow><annotation encoding="application/x-tex">L = 10</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal">L</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">10</span></span></span></span> requests <strong>at once</strong> — at an average context of 1,024 tokens that's a KV cache of
<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>10</mn><mo>×</mo><mn>1,024</mn><mo>×</mo><mn>112</mn><mtext>&nbsp;KiB</mtext><mo>≈</mo><mn>1.2</mn></mrow><annotation encoding="application/x-tex">10 \times 1{,}024 \times 112\ \text{KiB} \approx 1.2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em"></span><span class="mord">10</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.8389em;vertical-align:-0.1944em"></span><span class="mord">1</span><span class="mord"><span class="mpunct">,</span></span><span class="mord">024</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord">112</span><span class="mspace">&nbsp;</span><span class="mord text"><span class="mord">KiB</span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">≈</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">1.2</span></span></span></span> GB reserved at all times. Equations 3.1 and 3.3 are one equation seen from two angles.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="34-int8-symmetric-quantisation">3.4 INT8 symmetric quantisation<a href="https://kobkrit.com/en/blog/llm-10-deployment#34-int8-symmetric-quantisation" class="hash-link" aria-label="Direct link to 3.4 INT8 symmetric quantisation" title="Direct link to 3.4 INT8 symmetric quantisation" translate="no">​</a></h3>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>s</mi><mtext>  </mtext><mo>=</mo><mtext>  </mtext><mfrac><mrow><mi>max</mi><mo>⁡</mo><mi mathvariant="normal">∣</mi><mi>x</mi><mi mathvariant="normal">∣</mi></mrow><mn>127</mn></mfrac><mo separator="true">,</mo><mspace width="2em"></mspace><msub><mi>x</mi><mi>q</mi></msub><mtext>  </mtext><mo>=</mo><mtext>  </mtext><mrow><mi mathvariant="normal">r</mi><mi mathvariant="normal">o</mi><mi mathvariant="normal">u</mi><mi mathvariant="normal">n</mi><mi mathvariant="normal">d</mi></mrow><mtext> ⁣</mtext><mrow><mo fence="true">(</mo><mfrac><mi>x</mi><mi>s</mi></mfrac><mo fence="true">)</mo></mrow><mo separator="true">,</mo><mspace width="2em"></mspace><mover accent="true"><mi>x</mi><mo>^</mo></mover><mtext>  </mtext><mo>=</mo><mtext>  </mtext><mi>s</mi><mtext> </mtext><msub><mi>x</mi><mi>q</mi></msub></mrow><annotation encoding="application/x-tex">s \;=\; \frac{\max|x|}{127},\qquad x_q \;=\; \mathrm{round}\!\left(\frac{x}{s}\right),\qquad \hat{x} \;=\; s\,x_q</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">s</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.113em;vertical-align:-0.686em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.427em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord">127</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mop">max</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord">∣</span><span class="mord mathnormal">x</span><span class="mord">∣</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:2em"></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">q</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1.836em;vertical-align:-0.686em"></span><span class="mord"><span class="mord mathrm">round</span></span><span class="mspace" style="margin-right:-0.1667em"></span><span class="mspace" style="margin-right:0.1667em"></span><span class="minner"><span class="mopen delimcenter" style="top:0em"><span class="delimsizing size2">(</span></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.1076em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord mathnormal">s</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord mathnormal">x</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mclose delimcenter" style="top:0em"><span class="delimsizing size2">)</span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mpunct">,</span><span class="mspace" style="margin-right:2em"></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal">x</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.2222em"><span class="mord">^</span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.7167em;vertical-align:-0.2861em"></span><span class="mord mathnormal">s</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">q</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span></span></span></span></span>
<p>Store weights as 8-bit integers (<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>x</mi><mi>q</mi></msub><mo>∈</mo><mo stretchy="false">[</mo><mo>−</mo><mn>127</mn><mo separator="true">,</mo><mn>127</mn><mo stretchy="false">]</mo></mrow><annotation encoding="application/x-tex">x_q \in [-127, 127]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8252em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">q</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mopen">[</span><span class="mord">−</span><span class="mord">127</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord">127</span><span class="mclose">]</span></span></span></span>) with one scale factor <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>s</mi></mrow><annotation encoding="application/x-tex">s</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">s</span></span></span></span> per group, and multiply back when you use them.
The error per value is at most <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>s</mi><mi mathvariant="normal">/</mi><mn>2</mn></mrow><annotation encoding="application/x-tex">s/2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal">s</span><span class="mord">/2</span></span></span></span>. What you gain is halving the bytes per weight — and since equation 3.2 says time per token
scales with bytes read, <strong>in theory decode gets 2× faster</strong>. In practice the kernel that has to dequantise
can eat that profit entirely or worse (especially bitsandbytes' LLM.int8() on a T4) — measure, never guess.
And don't forget: bitsandbytes touches <strong>weights</strong> only. The KV cache is still fp16 at the same 112 KiB/token.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="35-prefill-vs-decode--two-regimes-you-must-never-mix">3.5 Prefill vs Decode — two regimes you must never mix<a href="https://kobkrit.com/en/blog/llm-10-deployment#35-prefill-vs-decode--two-regimes-you-must-never-mix" class="hash-link" aria-label="Direct link to 3.5 Prefill vs Decode — two regimes you must never mix" title="Direct link to 3.5 Prefill vs Decode — two regimes you must never mix" translate="no">​</a></h3>
<p>Define arithmetic intensity <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>I</mi></mrow><annotation encoding="application/x-tex">I</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal" style="margin-right:0.0785em">I</span></span></span></span> = FLOPs done per byte read, and compare it against the GPU's "ridge point":</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>I</mi><mtext>ridge</mtext></msub><mtext>  </mtext><mo>=</mo><mtext>  </mtext><mfrac><mrow><mn>65</mn><mtext>&nbsp;TFLOPS</mtext></mrow><mrow><mn>320</mn><mtext>&nbsp;GB/s</mtext></mrow></mfrac><mtext>  </mtext><mo>≈</mo><mtext>  </mtext><mn>203</mn><mtext>&nbsp;FLOP/byte</mtext></mrow><annotation encoding="application/x-tex">I_{\text{ridge}} \;=\; \frac{65\ \text{TFLOPS}}{320\ \text{GB/s}} \;\approx\; 203\ \text{FLOP/byte}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9694em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0785em">I</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0785em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">ridge</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.2963em;vertical-align:-0.936em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3603em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord">320</span><span class="mspace">&nbsp;</span><span class="mord text"><span class="mord">GB/s</span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord">65</span><span class="mspace">&nbsp;</span><span class="mord text"><span class="mord">TFLOPS</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.936em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">≈</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord">203</span><span class="mspace">&nbsp;</span><span class="mord text"><span class="mord">FLOP/byte</span></span></span></span></span></span>
<ul>
<li class=""><strong>Decode (batch 1):</strong> read 2 bytes of weight, do 2 FLOP → <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>I</mi><mo>≈</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">I \approx 1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal" style="margin-right:0.0785em">I</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">≈</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">1</span></span></span></span> — about 200× below the ridge → <strong>bandwidth-bound</strong></li>
<li class=""><strong>Prefill:</strong> a prompt of <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>s</mi></mrow><annotation encoding="application/x-tex">s</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">s</span></span></span></span> tokens is processed at once, so each weight is reused <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>s</mi></mrow><annotation encoding="application/x-tex">s</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">s</span></span></span></span> times per read → <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>I</mi><mo>≈</mo><mi>s</mi></mrow><annotation encoding="application/x-tex">I \approx s</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal" style="margin-right:0.0785em">I</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">≈</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">s</span></span></span></span> — a prompt beyond ~200 tokens is already <strong>compute-bound</strong></li>
</ul>
<p>These two phases are entirely different worlds: prefill can push thousands of tokens per second, decode gets tens to hundreds.
Anyone who averages the two into a single "tok/s" is reporting a number that tells you almost nothing —
which is why our notebook always reports <strong>TTFT</strong> (time to first token — measuring prefill) and <strong>ITL</strong> (inter-token latency — measuring decode) separately.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-seeing-the-equations">4. Seeing the Equations<a href="https://kobkrit.com/en/blog/llm-10-deployment#4-seeing-the-equations" class="hash-link" aria-label="Direct link to 4. Seeing the Equations" title="Direct link to 4. Seeing the Equations" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="context-is-what-eats-the-budget-not-the-model">Context is what eats the budget, not the model<a href="https://kobkrit.com/en/blog/llm-10-deployment#context-is-what-eats-the-budget-not-the-model" class="hash-link" aria-label="Direct link to Context is what eats the budget, not the model" title="Direct link to Context is what eats the budget, not the model" translate="no">​</a></h3>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-10-deployment/kv-cache-growth.light.svg" alt="Total serving memory plotted against context length at batch 1, 4 and 16, with the T4's 16 GB ceiling line and the 1.19 GB model-weights line" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-10-deployment/kv-cache-growth.dark.svg" alt="Total serving memory plotted against context length at batch 1, 4 and 16, with the T4's 16 GB ceiling line and the 1.19 GB model-weights line" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 10.1</span>The serving memory budget from equation 3.1 — computed end to end from Qwen3-0.6B's real config values: KV grows 112 KiB per token per sequence, and at batch 16 the card is full from a context of ~8,000 tokens onward</p><div class="captionFooter_w00v"></div></figcaption></figure>
<p>Note the square marker at the lower right: a single sequence at the full 40,960-token context uses 4.7 GB of KV — nearly four times the model's own weights.
And the batch-16 line hits the 16 GB ceiling from ~8,070 tokens. This is why LLM providers bill by context length.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="batching-one-read-of-the-weights-redeemed-for-many-tokens">Batching: one read of the weights, redeemed for many tokens<a href="https://kobkrit.com/en/blog/llm-10-deployment#batching-one-read-of-the-weights-redeemed-for-many-tokens" class="hash-link" aria-label="Direct link to Batching: one read of the weights, redeemed for many tokens" title="Direct link to Batching: one read of the weights, redeemed for many tokens" translate="no">​</a></h3>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-10-deployment/roofline.light.svg" alt="Aggregate throughput rising with batch size and converging toward the 266 tok/s bandwidth ceiling, while per-request latency gets worse" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-10-deployment/roofline.dark.svg" alt="Aggregate throughput rising with batch size and converging toward the 266 tok/s bandwidth ceiling, while per-request latency gets worse" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 10.2</span>The 266 tok/s ceiling from equation 3.2 (red dashed line — computed from the real datasheet) against a model of how batching amortises fixed cost (solid line — an illustration of the mechanism; the real numbers come from the notebook)</p><div class="captionFooter_w00v"></div></figcaption></figure>
<p>Read this graph on both axes: the blue line rises (good) but the orange line rises too (bad).
Batching isn't free — it is <strong>selling each individual user's latency to buy the system's throughput</strong>.
Where you should sit on this graph is a business decision, not a technical one.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-queue-explodes-before-the-server-fills-up">The queue explodes before the server fills up<a href="https://kobkrit.com/en/blog/llm-10-deployment#the-queue-explodes-before-the-server-fills-up" class="hash-link" aria-label="Direct link to The queue explodes before the server fills up" title="Direct link to The queue explodes before the server fills up" translate="no">​</a></h3>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-10-deployment/littles-law.light.svg" alt="Latency at the 50th and 99th percentile plotted against request arrival rate, showing p99 exploding as saturation approaches" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-10-deployment/littles-law.dark.svg" alt="Latency at the 50th and 99th percentile plotted against request arrival rate, showing p99 exploding as saturation approaches" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 10.3</span>p50/p99 of an M/M/1 queue computed from the formula -ln(1-q)/(μ-λ) — a mathematical model, not measured results, but this 'knee' shape will show up in the real measurements in section 8</p><div class="captionFooter_w00v"></div></figcaption></figure>
<p>The point to remember: at 80% utilisation p99 has already blown past 11 seconds, even though the server is "still 20% free."
Real systems must always leave headroom — anyone who sizes a system to exactly 100% of measured throughput is designing a system whose p99 is infinite.</p>
<p>Play with the serving budget yourself — switch to Serving mode and adjust context and concurrent requests, and watch when you hit the 16 GB ceiling:</p>
<div class="root_EEmQ"><div class="controls_hr8V"><div class="control_Br1p"><label class="controlLabel_J5tp" for="llmcourse-mbc-model"><span>Model</span></label><select id="llmcourse-mbc-model" class="select_AyHE"><option value="Qwen3-0.6B" selected="">Qwen3-0.6B</option><option value="Qwen3-1.7B">Qwen3-1.7B</option><option value="Qwen3-4B">Qwen3-4B</option><option value="Qwen3-8B">Qwen3-8B</option></select><span class="controlHint_ilRY">596.0M parameters, derived from config.json</span></div><div class="control_Br1p"><label class="controlLabel_J5tp" for="llmcourse-mbc-params"><span>Parameters (millions)</span></label><input id="llmcourse-mbc-params" class="numberInput_P4fE" type="number" min="1" max="1000000" step="1" value="596"></div><fieldset class="control_Br1p" style="border:0;padding:0;margin:0"><legend class="segmentedLegend_oU13">Weight dtype</legend><div class="segmented_Klsm"><span class="segment_AC25"><input type="radio" id="_R_pculdeh_-fp32" name="llmcourse-mbc-dtype-_R_pculdeh_" value="fp32"><label class="segmentLabel_wkEZ" for="_R_pculdeh_-fp32">fp32 (4B)</label></span><span class="segment_AC25"><input type="radio" id="_R_pculdeh_-fp16" name="llmcourse-mbc-dtype-_R_pculdeh_" checked="" value="fp16"><label class="segmentLabel_wkEZ" for="_R_pculdeh_-fp16">fp16 (2B)</label></span><span class="segment_AC25"><input type="radio" id="_R_pculdeh_-int8" name="llmcourse-mbc-dtype-_R_pculdeh_" value="int8"><label class="segmentLabel_wkEZ" for="_R_pculdeh_-int8">int8 (1B)</label></span><span class="segment_AC25"><input type="radio" id="_R_pculdeh_-nf4" name="llmcourse-mbc-dtype-_R_pculdeh_" value="nf4"><label class="segmentLabel_wkEZ" for="_R_pculdeh_-nf4">nf4 (0.5B)</label></span></div></fieldset><fieldset class="control_Br1p" style="border:0;padding:0;margin:0"><legend class="segmentedLegend_oU13">Run mode</legend><div class="segmented_Klsm"><span class="segment_AC25"><input type="radio" id="_R_11culdeh_-train" name="llmcourse-mbc-mode-_R_11culdeh_" value="train"><label class="segmentLabel_wkEZ" for="_R_11culdeh_-train">Training</label></span><span class="segment_AC25"><input type="radio" id="_R_11culdeh_-inference" name="llmcourse-mbc-mode-_R_11culdeh_" checked="" value="inference"><label class="segmentLabel_wkEZ" for="_R_11culdeh_-inference">Serving</label></span></div></fieldset><div class="control_Br1p"><label class="controlLabel_J5tp" for="_R_19culdeh_"><span>LoRA rank</span><span class="controlValue_cYgn">r = 16</span></label><input id="_R_19culdeh_" class="range_qGHz" type="range" min="0" max="7" step="1" disabled="" aria-label="LoRA rank" aria-valuetext="r = 16" value="3"></div><div class="control_Br1p"><label class="controlLabel_J5tp" for="_R_1hculdeh_"><span>Batch size</span><span class="controlValue_cYgn">1</span></label><input id="_R_1hculdeh_" class="range_qGHz" type="range" min="0" max="6" step="1" disabled="" aria-label="Batch size" aria-valuetext="1" value="0"></div><div class="control_Br1p"><label class="controlLabel_J5tp" for="_R_1pculdeh_"><span>Sequence length</span><span class="controlValue_cYgn">1024 tok</span></label><input id="_R_1pculdeh_" class="range_qGHz" type="range" min="0" max="7" step="1" aria-label="Sequence length in tokens" aria-valuetext="1024 tokens" value="2"></div><div class="control_Br1p"><label class="controlLabel_J5tp" for="_R_21culdeh_"><span>Concurrent requests</span><span class="controlValue_cYgn">1</span></label><input id="_R_21culdeh_" class="range_qGHz" type="range" min="0" max="8" step="1" aria-label="Concurrent requests held in the KV cache" aria-valuetext="1" value="0"></div><div class="control_Br1p"><label class="checkboxRow_XXA4" for="llmcourse-mbc-ckpt"><input id="llmcourse-mbc-ckpt" type="checkbox" disabled="" checked=""><span>Gradient checkpointing</span></label><span class="controlHint_ilRY">Trades about 30% more compute for a large drop in activation memory.</span></div></div><div class="svgWrap_mSxx"><svg class="svg_pLEH chart_YWLW" viewBox="0 0 720 118" role="img" aria-label="Stacked VRAM usage totalling 1.22 GiB against a 16 GiB ceiling. Verdict: fits."><rect x="0" y="26" width="720" height="44" rx="6" class="barTrack_ylwk"></rect><rect x="0" y="26" width="46.259562174479164" height="44" class="barSegment_eSn9 seriesWeights_xyK5"><title>weights: 1.11 GiB</title></rect><rect x="46.259562174479164" y="26" width="1" height="44" class="barSegment_eSn9 seriesActivations_mq5k"><title>activations: 68.00 KiB</title></rect><rect x="46.26226425170898" y="26" width="4.557291666666666" height="44" class="barSegment_eSn9 seriesKv_dhmF"><title>kvCache: 112.00 MiB</title></rect><line x1="666.6666666666666" y1="14" x2="666.6666666666666" y2="82" class="ceilingLine_Gd0g"></line><text x="666.6666666666666" y="10" text-anchor="end" class="ceilingLabel_huNs">16 GB — Colab T4</text><g><line x1="0" y1="70" x2="0" y2="75" class="tick_YNak"></line><text x="0" y="88" text-anchor="middle" class="tickLabel_B3jM">0</text></g><g><line x1="166.66666666666666" y1="70" x2="166.66666666666666" y2="75" class="tick_YNak"></line><text x="166.66666666666666" y="88" text-anchor="middle" class="tickLabel_B3jM">4</text></g><g><line x1="333.3333333333333" y1="70" x2="333.3333333333333" y2="75" class="tick_YNak"></line><text x="333.3333333333333" y="88" text-anchor="middle" class="tickLabel_B3jM">8</text></g><g><line x1="500" y1="70" x2="500" y2="75" class="tick_YNak"></line><text x="500" y="88" text-anchor="middle" class="tickLabel_B3jM">12</text></g><g><line x1="666.6666666666666" y1="70" x2="666.6666666666666" y2="75" class="tick_YNak"></line><text x="666.6666666666666" y="88" text-anchor="middle" class="tickLabel_B3jM">16</text></g><text x="720" y="116" text-anchor="end" class="axisLabel_Yazw">GiB</text></svg></div><ul class="legend_BTbY"><li class="legendItem_ApeG"><span class="swatch_vsP4 seriesWeights_xyK5" aria-hidden="true"></span><span class="legendLabel_rxKN">Weights</span><span class="legendValue_wTen">1.11 GiB</span></li><li class="legendItem_ApeG"><span class="swatch_vsP4 seriesGradients_Yy9k" aria-hidden="true"></span><span class="legendLabel_rxKN">Gradients</span><span class="legendValue_wTen">—</span></li><li class="legendItem_ApeG"><span class="swatch_vsP4 seriesOptimizer_Sr99" aria-hidden="true"></span><span class="legendLabel_rxKN">Optimizer state</span><span class="legendValue_wTen">—</span></li><li class="legendItem_ApeG"><span class="swatch_vsP4 seriesActivations_mq5k" aria-hidden="true"></span><span class="legendLabel_rxKN">Activations</span><span class="legendValue_wTen">68.00 KiB</span></li><li class="legendItem_ApeG"><span class="swatch_vsP4 seriesKv_dhmF" aria-hidden="true"></span><span class="legendLabel_rxKN">KV cache</span><span class="legendValue_wTen">112.00 MiB</span></li></ul><div class="readouts__tjv"><div class="readout_D9ns"><span class="readoutLabel_EsIV">Total VRAM</span><span class="readoutValue_VS6z">1.22 GiB</span><span class="readoutSub_DoT9">14.78 GiB to spare</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">Trainable params</span><span class="readoutValue_VS6z">0</span><span class="readoutSub_DoT9">0.00%</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">KV cache per token</span><span class="readoutValue_VS6z">112 KiB</span><span class="readoutSub_DoT9">2 x 28 x 8 x 128</span></div><div class="readout_D9ns"><span class="readoutLabel_EsIV">Full context KV</span><span class="readoutValue_VS6z">4.38 GiB</span><span class="readoutSub_DoT9">41.0K tok</span></div></div><p class="callout_aEDz calloutSuccess_oTZ4" role="status"><strong class="calloutTitle_nx3s">It fits.</strong>This run needs 1.22 GiB and leaves 14.78 GiB of headroom on a free Colab T4.</p></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-setting-up-the-environment">5. Setting Up the Environment<a href="https://kobkrit.com/en/blog/llm-10-deployment#5-setting-up-the-environment" class="hash-link" aria-label="Direct link to 5. Setting Up the Environment" title="Direct link to 5. Setting Up the Environment" translate="no">​</a></h2>
<p>Open Colab and pick <strong>Runtime → Change runtime type → T4 GPU</strong> (the free tier suffices — for the last time in this series).</p>
<div class="theme-admonition theme-admonition-danger admonition_xJq3 alert alert--danger"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"></path></svg></span>The series-wide warning — final round, and you should be able to recite it by now</div><div class="admonitionContent_BuS1"><p>If you've followed all nine chapters you know it by heart: the T4 is Turing (SM 7.5), <strong>no bfloat16, no FlashAttention-2</strong>.
This series' running joke was never only a joke — in this chapter it bites twice more:</p><div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">torch_dtype</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">float16      </span><span class="token comment" style="color:#999988;font-style:italic"># not bfloat16 — the last time you'll read this line from me</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">attn_implementation</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"sdpa"</span><span class="token plain">     </span><span class="token comment" style="color:#999988;font-style:italic"># not flash_attention_2</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># and calling it in advance: vLLM needs dtype="half" — leave it on auto and it finds bf16 in the config and refuses on the spot</span><br></span></code></pre></div></div></div></div>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">cap </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_device_capability</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"compute capability:"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cap</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                    </span><span class="token comment" style="color:#999988;font-style:italic"># T4 = (7, 5)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"native bf16:"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cap</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&gt;=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                   </span><span class="token comment" style="color:#999988;font-style:italic"># T4 -&gt; False</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"torch says   :"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">is_bf16_supported</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># T4 -&gt; True (counts emulation!)</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span><code>is_bf16_supported()</code> lies on a T4</div><div class="admonitionContent_BuS1"><p>Recent torch returns <code>True</code> on a T4 because it counts <strong>emulation</strong> as support — which is far slower than fp16.
Gate on <strong>compute capability ≥ 8.0</strong> (Ampere and up) instead. This was a real bug, caught only by running the notebook on Colab.</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="stage-1--merge-the-adapter-and-export-2-min">Stage 1 — Merge the adapter and export (~2 min)<a href="https://kobkrit.com/en/blog/llm-10-deployment#stage-1--merge-the-adapter-and-export-2-min" class="hash-link" aria-label="Direct link to Stage 1 — Merge the adapter and export (~2 min)" title="Direct link to Stage 1 — Merge the adapter and export (~2 min)" translate="no">​</a></h3>
<p>All series long we trained with LoRA, which becomes a burden at serving time: every forward pass has to compute an extra <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>B</mi><mi>A</mi><mi>x</mi></mrow><annotation encoding="application/x-tex">BAx</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal" style="margin-right:0.0502em">B</span><span class="mord mathnormal">A</span><span class="mord mathnormal">x</span></span></span></span>.
The good news is that LoRA merges back into the base weights in closed form: <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>W</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo>=</mo><mi>W</mi><mo>+</mo><mstyle scriptlevel="0" displaystyle="false"><mfrac><mi>α</mi><mi>r</mi></mfrac></mstyle><mi>B</mi><mi>A</mi></mrow><annotation encoding="application/x-tex">W' = W + \tfrac{\alpha}{r}BA</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7519em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1389em">W</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7519em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.7667em;vertical-align:-0.0833em"></span><span class="mord mathnormal" style="margin-right:0.1389em">W</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.0404em;vertical-align:-0.345em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6954em"><span style="top:-2.655em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">r</span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.394em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0037em">α</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.345em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mord mathnormal" style="margin-right:0.0502em">B</span><span class="mord mathnormal">A</span></span></span></span> — once served, it costs exactly what the base model costs.</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> torch</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> transformers </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> AutoModelForCausalLM</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> AutoTokenizer</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> peft </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> PeftModel</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">tok </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> AutoTokenizer</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">from_pretrained</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Qwen/Qwen3-0.6B"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">base </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> AutoModelForCausalLM</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">from_pretrained</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"Qwen/Qwen3-0.6B"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    torch_dtype</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">float16</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    attn_implementation</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"sdpa"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cuda</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">policy </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> PeftModel</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">from_pretrained</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">base</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"qwen3-th-lora-best"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># the sweep winner from chapter 9</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">merged </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> policy</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">merge_and_unload</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                               </span><span class="token comment" style="color:#999988;font-style:italic"># W' = W + (α/r)·B·A</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">merged</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">save_pretrained</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"qwen3-th-serve"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">tok</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">save_pretrained</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"qwen3-th-serve"</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>Don't take it on faith that merging gives you the same model — <strong>prove it</strong> by comparing logits:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># Thai prompt: "What are the duties of the Provincial Electricity Authority?"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">x </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> tok</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"การไฟฟ้าส่วนภูมิภาคมีหน้าที่อะไร"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> return_tensors</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"pt"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">to</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"cuda"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">with</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">no_grad</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    d </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">policy</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">**</span><span class="token plain">x</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">logits </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> merged</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">**</span><span class="token plain">x</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">logits</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">abs</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">max</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">item</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-interpolation string" style="color:#e3116c">f"max |Δlogit| = </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">d</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">:</span><span class="token string-interpolation interpolation format-spec">.4f</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># around ~1e-3 — near zero, but not exactly zero</span><br></span></code></pre></div></div>
<p>It isn't exactly zero because adding <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mstyle scriptlevel="0" displaystyle="false"><mfrac><mi>α</mi><mi>r</mi></mfrac></mstyle><mi>B</mi><mi>A</mi></mrow><annotation encoding="application/x-tex">\tfrac{\alpha}{r}BA</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0404em;vertical-align:-0.345em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6954em"><span style="top:-2.655em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">r</span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.394em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0037em">α</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.345em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mord mathnormal" style="margin-right:0.0502em">B</span><span class="mord mathnormal">A</span></span></span></span> into <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>W</mi></mrow><annotation encoding="application/x-tex">W</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal" style="margin-right:0.1389em">W</span></span></span></span> in fp16 involves rounding — if you see the 1e-3 range, that's normal.
If you see the 1.0 range, you loaded the wrong adapter or the dtypes don't match.</p>
<p>The price you pay: a ~20 MB adapter file (10.1M parameters at r = 16) becomes ~1.2 GB of full weights,
a ~60× increase, in exchange for every serving tool (vLLM included) seeing it as one ordinary model.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-preparing-the-data">6. Preparing the Data<a href="https://kobkrit.com/en/blog/llm-10-deployment#6-preparing-the-data" class="hash-link" aria-label="Direct link to 6. Preparing the Data" title="Direct link to 6. Preparing the Data" translate="no">​</a></h2>
<p>The "data" in this chapter isn't a training set, it's a <strong>workload</strong> — and a workload measured the wrong way always yields a p99 that's too pretty to be true.</p>
<ul>
<li class=""><strong>60 Thai prompts</strong> from the same pool as KobEval-TH — a mix of short, medium and long, so prefill has the variety of real work</li>
<li class=""><strong>The TH-KNOW quality set</strong> from chapter 9 — reused for every configuration whose speed we measure</li>
<li class=""><strong>An open-loop load generator</strong>: request arrival times drawn from a Poisson process, then fired <em>on schedule</em> whether or not the server is ready</li>
</ul>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> numpy </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> np</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">rng </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">random</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">default_rng</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">42</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">gaps </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> rng</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">exponential</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1.0</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> LAM</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> size</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">N_REQUESTS</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># Poisson process: gaps ~ Exp(λ)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">arrivals </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cumsum</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">gaps</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                           </span><span class="token comment" style="color:#999988;font-style:italic"># the firing schedule — followed strictly</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>Why open-loop — the trap named coordinated omission</div><div class="admonitionContent_BuS1"><p>If your load generator fires one request and <em>waits for the answer</em> before firing the next (closed-loop), a slow server automatically makes you fire more slowly.
The queue then never accumulates, and the p99 you measure is deceptively pretty, because the instrument is "being polite" to the system it's measuring.
Firing on a schedule randomised in advance — even when the previous request hasn't finished — is the only way the knee in figure 10.3 ever appears for real.</p></div></div>
<p>Every request records three values: <strong>TTFT</strong>, <strong>mean ITL</strong>, and token count — then we summarise as p50/p99 per configuration.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-the-main-code">7. The Main Code<a href="https://kobkrit.com/en/blog/llm-10-deployment#7-the-main-code" class="hash-link" aria-label="Direct link to 7. The Main Code" title="Direct link to 7. The Main Code" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="71-stage-2--a-baseline-deliberately-made-bad-4-min">7.1 Stage 2 — A baseline deliberately made bad (~4 min)<a href="https://kobkrit.com/en/blog/llm-10-deployment#71-stage-2--a-baseline-deliberately-made-bad-4-min" class="hash-link" aria-label="Direct link to 7.1 Stage 2 — A baseline deliberately made bad (~4 min)" title="Direct link to 7.1 Stage 2 — A baseline deliberately made bad (~4 min)" translate="no">​</a></h3>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> fastapi </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> FastAPI</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> threading</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> uvicorn</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">app </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> FastAPI</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token decorator annotation punctuation" style="color:#393A34">@app</span><span class="token decorator annotation punctuation" style="color:#393A34">.</span><span class="token decorator annotation punctuation" style="color:#393A34">post</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"/generate"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">generate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">body</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">dict</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ids </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> tok</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">body</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"prompt"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> return_tensors</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"pt"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">to</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"cuda"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    out </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> merged</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">generate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">**</span><span class="token plain">ids</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> max_new_tokens</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">128</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> do_sample</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">False</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"text"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> tok</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">decode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">out</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ids</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">input_ids</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">shape</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">:</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                               skip_special_tokens</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">threading</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Thread</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    target</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">uvicorn</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">run</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> args</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">app</span><span class="token punctuation" style="color:#393A34">,</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    kwargs</span><span class="token operator" style="color:#393A34">=</span><span class="token builtin">dict</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">host</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"127.0.0.1"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> port</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">8000</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> log_level</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"warning"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    daemon</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">start</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>This server violates everything this chapter teaches: one request at a time, no batching, the KV cache reallocated every call,
a Python loop per token — <strong>and that is its job</strong>. It's the baseline every improvement gets measured back against.
If you don't measure the starting point, "5× faster" is just advertising.</p>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>Colab kills background servers when the connection drops</div><div class="admonitionContent_BuS1"><p>The thread running uvicorn lives only as long as the session — close the tab, let the screen sleep, or get your runtime reclaimed, and the server vanishes silently.
So the notebook measures in short complete bursts and writes results to <code>results.json</code> immediately after each one. Don't design a measurement that has to run for hours on free Colab.</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="72-stage-3a--static-kv-cache--torchcompile">7.2 Stage 3a — Static KV cache + <code>torch.compile</code><a href="https://kobkrit.com/en/blog/llm-10-deployment#72-stage-3a--static-kv-cache--torchcompile" class="hash-link" aria-label="Direct link to 72-stage-3a--static-kv-cache--torchcompile" title="Direct link to 72-stage-3a--static-kv-cache--torchcompile" translate="no">​</a></h3>
<p>Ordinary <code>generate</code> grows the KV cache one token at a time, so shapes change constantly and it can't be compiled.
Reserve the whole cache up front (static) and shapes stay fixed enough for <code>torch.compile</code> to capture the entire graph into a CUDA graph —
eliminating the baseline's single biggest overhead, launching kernels one at a time from Python.</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">merged</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">generation_config</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cache_implementation </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"static"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">fast </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">compile</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">merged</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> mode</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"reduce-overhead"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">warm </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> tok</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"อุ่นเครื่อง"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> return_tensors</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"pt"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">to</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"cuda"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># Thai for "warm up"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> _ </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token builtin">range</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">                                   </span><span class="token comment" style="color:#999988;font-style:italic"># always warm up before timing</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    fast</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">generate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">**</span><span class="token plain">warm</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> max_new_tokens</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">8</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-danger admonition_xJq3 alert alert--danger"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"></path></svg></span>No warmup before measuring = the entire number set is garbage</div><div class="admonitionContent_BuS1"><p>The first <code>torch.compile</code> call spends <strong>tens of seconds to minutes</strong> tracing and compiling.
If that time leaks into your timing you'll conclude that compiling "made it slower," which is the exact inverse of the truth.
The notebook's rule: throw away at least 3 runs before starting the clock, for every configuration, no exceptions.</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="73-stage-3b--continuous-batching-60-lines-written-by-hand">7.3 Stage 3b — Continuous batching, ~60 lines written by hand<a href="https://kobkrit.com/en/blog/llm-10-deployment#73-stage-3b--continuous-batching-60-lines-written-by-hand" class="hash-link" aria-label="Direct link to 7.3 Stage 3b — Continuous batching, ~60 lines written by hand" title="Direct link to 7.3 Stage 3b — Continuous batching, ~60 lines written by hand" translate="no">​</a></h3>
<p>Equation 3.2 says reading the weights once is the big cost — batching is dividing that one cost across many tokens.
But static batching (wait for <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>B</mi></mrow><annotation encoding="application/x-tex">B</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal" style="margin-right:0.0502em">B</span></span></span></span> to accumulate, start, then wait for the longest one to finish) wastes enormous room,
so <strong>continuous batching</strong> admits new requests into the batch the moment a slot frees up. The heart of it is this loop:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> collections </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> deque</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">queue</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> running</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> MAX_BATCH </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> deque</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">16</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">while</span><span class="token plain"> queue </span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> running</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">while</span><span class="token plain"> queue </span><span class="token keyword" style="color:#00009f">and</span><span class="token plain"> </span><span class="token builtin">len</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">running</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain"> MAX_BATCH</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        running</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">append</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">Sequence</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">queue</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">popleft</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># admitted mid-flight, without waiting for the old batch</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    step</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">running</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">          </span><span class="token comment" style="color:#999988;font-style:italic"># one forward step for every sequence — 1 read of the weights, B tokens out</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    running </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">s </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> s </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> running </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> s</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">done</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># finished ones leave immediately, returning slots to the queue</span><br></span></code></pre></div></div>
<p>The full version (~60 lines, including per-sequence position ids and masks) is in the notebook.
It is not vLLM — no paged memory, no prefix cache — but it proves the mechanism with code you can read in one screen,
and <strong>every percent</strong> of its measured improvement can be accounted for, which matters more than sophistication in this lesson.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="74-quantisation--int8-and-nf4-and-the-real-price">7.4 Quantisation — int8 and nf4, and the real price<a href="https://kobkrit.com/en/blog/llm-10-deployment#74-quantisation--int8-and-nf4-and-the-real-price" class="hash-link" aria-label="Direct link to 7.4 Quantisation — int8 and nf4, and the real price" title="Direct link to 7.4 Quantisation — int8 and nf4, and the real price" translate="no">​</a></h3>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> transformers </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> BitsAndBytesConfig</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">int8 </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> AutoModelForCausalLM</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">from_pretrained</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"qwen3-th-serve"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    quantization_config</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">BitsAndBytesConfig</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">load_in_8bit</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    device_map</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">nf4 </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> AutoModelForCausalLM</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">from_pretrained</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"qwen3-th-serve"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    quantization_config</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">BitsAndBytesConfig</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        load_in_4bit</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bnb_4bit_quant_type</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"nf4"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bnb_4bit_compute_dtype</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">torch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">float16</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    device_map</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<figure class="frame_n6Ig"><div class="body_N_CB"><img src="https://kobkrit.com/img/blog/llm-10-deployment/quant-tradeoff.light.svg" alt="Bar charts comparing VRAM and decode speed for fp16, int8 and nf4, with a gap left for the TH-KNOW accuracy difference" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://kobkrit.com/img/blog/llm-10-deployment/quant-tradeoff.dark.svg" alt="Bar charts comparing VRAM and decode speed for fp16, int8 and nf4, with a gap left for the TH-KNOW accuracy difference" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><figcaption class="caption_Cn5s"><p class="captionText_Wb4P"><span class="figureLabel_QVk8">Figure 10.4</span>Left: VRAM of the weights, computed for real from the config (bitsandbytes always keeps embeddings in fp16) — Right: approximate speed scaling on a T4; the ΔTH-KNOW slot is deliberately left as ? until the notebook fills it in</p><div class="captionFooter_w00v"></div></figcaption></figure>
<div class="theme-admonition theme-admonition-danger admonition_xJq3 alert alert--danger"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"></path></svg></span>The original sin of this genre: reporting speed without reporting quality</div><div class="admonitionContent_BuS1"><p>The number "nf4 saves 2.2× the VRAM!" with no quality score attached <strong>is not an experimental result, it's an advertisement</strong>,
because squeezing weights down to 4 bits always costs something. The only meaningful question is "how much."
So our notebook runs <strong>TH-KNOW on KobEval-TH for every configuration</strong> that has a row in the section 9 table —
fp16, int8 and nf4 sit the same exam, with Wilson CIs as this series always does.</p></div></div>
<p>And a prediction stated plainly in advance: on a T4, <strong>bitsandbytes' int8 is often <em>slower</em> than fp16</strong> —
LLM.int8() splits outliers off to be multiplied in fp16, so you pay overhead twice. It is a VRAM-saving tool, not a speed tool.
If your measurements come out that way, that isn't a bug on your side, it's the truth review articles rarely print.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="75-stage-4--vllm-optional-and-the-most-fragile-thing-in-the-series">7.5 Stage 4 — vLLM (optional, and the most fragile thing in the series)<a href="https://kobkrit.com/en/blog/llm-10-deployment#75-stage-4--vllm-optional-and-the-most-fragile-thing-in-the-series" class="hash-link" aria-label="Direct link to 7.5 Stage 4 — vLLM (optional, and the most fragile thing in the series)" title="Direct link to 7.5 Stage 4 — vLLM (optional, and the most fragile thing in the series)" translate="no">​</a></h3>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>This cell has the highest chance of breaking of any across all 10 notebooks — for reasons that can be explained</div><div class="admonitionContent_BuS1"><p>vLLM does support SM 7.5, true, but on Colab's free T4 three conditions stack up:
(1) you must specify <code>dtype="half"</code> — it finds bf16 in the config and refuses on the spot (by now you should have predicted this before reading it),
(2) many versions require <code>enforce_eager=True</code>, because the CUDA graph path has problems on older cards,
(3) some recent versions drop or break the sm_75 build entirely — so the notebook <strong>pins a fixed version</strong>. Don't upgrade to latest.</p></div></div>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">try</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> vllm </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> LLM</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> SamplingParams          </span><span class="token comment" style="color:#999988;font-style:italic"># version pinned in the notebook's install cell</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    llm </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> LLM</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        model</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"qwen3-th-serve"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        dtype</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"half"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">                 </span><span class="token comment" style="color:#999988;font-style:italic"># the T4 has no bf16 — must be stated explicitly</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        enforce_eager</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">           </span><span class="token comment" style="color:#999988;font-style:italic"># skip the CUDA graph that misbehaves on sm_75</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        gpu_memory_utilization</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.85</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        max_model_len</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">4096</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    VLLM_OK </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">True</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">except</span><span class="token plain"> Exception </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> e</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    VLLM_OK </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">False</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"vLLM unavailable on this runtime — safe to skip:"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> e</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>This structure is deliberate: if vLLM fails to install or crashes at init, everything in stages 1–3 is <strong>still intact</strong>.
This chapter's main conclusions don't depend on vLLM at all — it's merely evidence of what paged KV plus well-fused kernels can add
compared to our 60-line scheduler.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="8-results">8. Results<a href="https://kobkrit.com/en/blog/llm-10-deployment#8-results" class="hash-link" aria-label="Direct link to 8. Results" title="Direct link to 8. Results" translate="no">​</a></h2>
<p>The notebook writes every number to <code>results.json</code> — the frame is <strong>theory from the datasheet against measured reality</strong>:</p>
<table><thead><tr><th>Quantity</th><th>Theory (section 3)</th><th>Measured (notebook)</th></tr></thead><tbody><tr><td>single decode, naive <code>generate</code></td><td>ceiling ≤ 266 tok/s</td><td>?</td></tr><tr><td>single decode, static cache + compile</td><td>ceiling ≤ 266 tok/s</td><td>?</td></tr><tr><td>aggregate at batch 16 (continuous batching)</td><td>several times higher than batch 1</td><td>?</td></tr><tr><td>TTFT (prompt ~512 tokens)</td><td>tens of ms (compute-bound)</td><td>?</td></tr></tbody></table>
<p><strong>What to expect and how to read it:</strong> the naive number will land roughly <strong>10×</strong> below the ceiling — don't panic, and don't blame the T4.
That gap has traceable, layered origins: a Python loop per token, dozens of kernel launches per step,
dynamic KV allocation, syncing back to the CPU during sampling — stage 3 removes these layers one at a time and <strong>measures again every time</strong>.
The total creeping closer and closer to the ceiling (but never touching it, because the ceiling excludes KV, activations and the remaining overhead)
is the empirical evidence that equation 3.2 really does describe the actual machine — which is why I call it the series' most solid experiment.</p>
<p>Under load, the second table captures the shape of figure 10.3:</p>
<table><thead><tr><th>λ (req/s)</th><th>p50</th><th>p99</th></tr></thead><tbody><tr><td>low (~30% of capacity)</td><td>?</td><td>?</td></tr><tr><td>medium (~60%)</td><td>?</td><td>?</td></tr><tr><td>near saturation (~90%)</td><td>?</td><td>? — should explode into the knee from figure 10.3</td></tr></tbody></table>
<p>And the eyeball quality check no number can substitute for — the same prompt, answered in fp16 versus nf4:</p>
<div class="root_IS5b"><div class="picker_cO8e"><span class="pickerLabel_sE2x" id="llmcourse-bac-picker">Prompt</span><div class="pickerButtons_j7L1" role="tablist" aria-labelledby="llmcourse-bac-picker"><button type="button" role="tab" id="llmcourse-bac-tab-0" aria-selected="true" aria-controls="llmcourse-bac-panel-0" tabindex="0" class="pickerButton_gFO3 pickerButtonActive_xIUp">1</button><button type="button" role="tab" id="llmcourse-bac-tab-1" aria-selected="false" aria-controls="llmcourse-bac-panel-1" tabindex="-1" class="pickerButton_gFO3">2</button></div></div><blockquote class="prompt_O4Wp" lang="th"><span class="promptLabel_h2F6">Prompt</span>อธิบายว่าทำไมท้องฟ้าถึงเป็นสีฟ้า แบบสั้น ๆ</blockquote><div class="grid_h_9T" id="llmcourse-bac-panel-0" role="tabpanel" aria-labelledby="llmcourse-bac-tab-0" style="grid-template-columns:repeat(auto-fit, minmax(min(100%, 260px), 1fr))"><article class="card_S27b"><header class="cardHeader_w7wJ"><h4 class="cardTitle_NUQN">base</h4><div class="badges_pXcS"><span class="badge_wUaQ badgeBad_WFwi" title="Share of non-whitespace characters that are Thai script">Thai 18%</span><span class="badge_wUaQ">41 tokens</span></div></header><div class="output_VSGg" lang="th">The sky appears blue because of Rayleigh scattering. ท้องฟ้า is blue เพราะ light scatter ครับ. Shorter wavelengths scatter more than longer ones.</div></article><article class="card_S27b"><header class="cardHeader_w7wJ"><h4 class="cardTitle_NUQN">sft</h4><div class="badges_pXcS"><span class="badge_wUaQ badgeGood_MHH_" title="Share of non-whitespace characters that are Thai script">Thai 99%</span><span class="badge_wUaQ">78 tokens</span></div></header><div class="output_VSGg" lang="th">ท้องฟ้าเป็นสีฟ้าเพราะแสงอาทิตย์กระทบกับโมเลกุลของอากาศแล้วเกิดการกระเจิงแบบเรย์ลี ซึ่งแสงสีน้ำเงินที่มีความยาวคลื่นสั้นกว่าจะกระเจิงได้มากกว่าแสงสีแดง เราจึงมองเห็นท้องฟ้าเป็นสีฟ้าครับ</div></article></div><p class="status_mfC7">Showing the built-in sample.</p></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="9-comparison">9. Comparison<a href="https://kobkrit.com/en/blog/llm-10-deployment#9-comparison" class="hash-link" aria-label="Direct link to 9. Comparison" title="Direct link to 9. Comparison" translate="no">​</a></h2>
<p>The summary table for the whole chapter — every bit of speed gained has to show what it cost, on the same row:</p>
<table><thead><tr><th>Configuration</th><th>tok/s @B=1</th><th>tok/s @B=16</th><th>p99</th><th>Peak VRAM</th><th>Max concurrency*</th><th>TH-KNOW</th></tr></thead><tbody><tr><td>fp16 + naive <code>generate</code></td><td>?</td><td>—</td><td>?</td><td>~1.5 GB</td><td>1</td><td>baseline</td></tr><tr><td>+ static cache + <code>torch.compile</code></td><td>?</td><td>—</td><td>?</td><td>~1.7 GB</td><td>1</td><td>= baseline (identical weights)</td></tr><tr><td>+ continuous batching</td><td>?</td><td>?</td><td>?</td><td>?</td><td>16 (per MAX_BATCH)</td><td>= baseline</td></tr><tr><td>int8 (bitsandbytes)</td><td>?</td><td>?</td><td>?</td><td>~0.9 GB</td><td>?</td><td>?</td></tr><tr><td>nf4 (bitsandbytes)</td><td>?</td><td>?</td><td>?</td><td>~0.7 GB</td><td>?</td><td>?</td></tr><tr><td>vLLM <code>dtype="half"</code> (if it runs)</td><td>?</td><td>?</td><td>?</td><td>per <code>gpu_memory_utilization</code></td><td>?</td><td>= fp16</td></tr></tbody></table>
<p>* At a context of 1,024 tokens, the KV budget from equation 3.1 supports <strong>hundreds</strong> of sequences (~14.8 GB ÷ 112 MiB ≈ 125)
— the real limiter is the scheduler and prefill compute, not VRAM, which is a lesson in itself.</p>
<p>The pattern you <strong>should expect</strong>:</p>
<ul>
<li class="">compile helps batch 1 the most (it kills per-step overhead, which is the single-stream bottleneck)</li>
<li class="">batching barely helps tok/s per request but multiplies the aggregate — and makes p99 worse under high load</li>
<li class="">int8 lowers VRAM but is <strong>slower</strong> on a T4; nf4 lowers VRAM more and is faster than int8 — for quality you must look at the rightmost column yourself, never conclude it from the speed columns</li>
<li class="">vLLM, if it survives, should clearly beat our hand-written scheduler at high concurrency — if it doesn't, <code>enforce_eager</code> is eating its profit</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="traps-to-watch-for">Traps to watch for<a href="https://kobkrit.com/en/blog/llm-10-deployment#traps-to-watch-for" class="hash-link" aria-label="Direct link to Traps to watch for" title="Direct link to Traps to watch for" translate="no">​</a></h3>
<p><strong>1. The server vanishing silently because Colab dropped the connection</strong> —
measure in short bursts, write results immediately (section 7.1). Don't plan a measurement longer than the session's lifetime.</p>
<p><strong>2. bf16 on a T4</strong> — by chapter ten you should be able to predict this before the error appears: <code>torch_dtype=torch.float16</code> in transformers
and <code>dtype="half"</code> in vLLM. The series' running joke ends with this chapter, but Turing cards live on in the world.</p>
<p><strong>3. No warmup before measuring</strong> — the first compile takes minutes; if it leaks into your timing, your conclusion inverts instantly (section 7.2)</p>
<p><strong>4. Reporting tok/s without stating batch size</strong> — the same number can mean a system where users wait 40 ms or 500 ms per token.
Every number in this chapter therefore carries its @B with it.</p>
<p><strong>5. Mixing prefill into decode</strong> — long prompts make "average tok/s" deceptively high, because prefill is compute-bound
and consumes thousands of tokens per second (section 3.5) — report TTFT and ITL separately, and only separately.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="10-summary">10. Summary<a href="https://kobkrit.com/en/blog/llm-10-deployment#10-summary" class="hash-link" aria-label="Direct link to 10. Summary" title="Direct link to 10. Summary" translate="no">​</a></h2>
<ul>
<li class=""><strong>Decode at batch 1 is waiting on weights to travel, not on compute</strong> — the chip is ~99.5% idle while "working flat out"</li>
<li class=""><strong>The speed ceiling is computable from the datasheet</strong>: 320 GB/s ÷ 1.2 GB ≈ 266 tok/s, before running a single line of code</li>
<li class=""><strong>KV cache is 112 KiB/token</strong> (the same number this site's test suite asserts) — at the full 40,960-token context
that's ~4.7 GB for one single sequence, nearly four times the model's weights — <strong>context is what eats the budget</strong></li>
<li class=""><strong>Batching = selling latency to buy throughput</strong>, and continuous batching is the way of selling that loses the least</li>
<li class=""><strong>Quantisation reduces the bytes that must travel</strong> — twice as fast in theory, but in practice you must measure, and always measure quality alongside</li>
<li class=""><strong>Little's law ties it all together</strong>: <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi><mo>=</mo><mi>λ</mi><mi>W</mi></mrow><annotation encoding="application/x-tex">L = \lambda W</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal">L</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal" style="margin-right:0.1389em">λW</span></span></span></span> gives the number of sequences you must hold, which loops right back into the KV budget</li>
<li class=""><strong>p99 explodes before the server fills up</strong> — a system with no headroom is a system designed to fail exactly when the most people are using it</li>
</ul>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>Limitations of this experiment</div><div class="admonitionContent_BuS1"><p><strong>The T4 is a 2018 card.</strong> 320 GB/s against an H100's ~3.35 TB/s — an order of magnitude apart.
Every absolute number in this chapter therefore <strong>does not transfer</strong> to other machines. What does transfer is <em>the ratios and the way of thinking</em>:
equations 3.1–3.5 hold for any card, you just swap in the constants from a newer datasheet.</p><p>And real production serving needs several more layers this chapter <strong>never touches at all</strong>:
autoscaling, health checks and readiness probes, observability (metrics/logging/tracing),
multi-tenancy and user isolation, rate limiting, authentication, per-request cost accounting,
model version management and rollback — not discussing them doesn't mean they don't matter.
It means one article can't say everything, and we chose to talk about the thing that underlies all of it: the physics of the bottleneck.</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="closing-the-series">Closing the series<a href="https://kobkrit.com/en/blog/llm-10-deployment#closing-the-series" class="hash-link" aria-label="Direct link to Closing the series" title="Direct link to Closing the series" translate="no">​</a></h3>
<p>The ten chapters built a single path: inject knowledge (ch. 1) → teach format (ch. 2) → arrange preferences three ways
(ch. 3–5) → distil it smaller (ch. 6–7) → keep it from breaking (ch. 8) → measure honestly (ch. 9) → put it into service and measure against the ceiling physics sets (this one).
But what I really want you to carry away isn't any one technique. It's the <strong>habits</strong> every chapter repeated:
write the equation before writing the code, measure everything with a confidence interval, and always publish your own limitations in the yellow box at the end.
Models will change, libraries will change, cards will get ten times faster — those three habits will still hold on the day everything in this series is obsolete.</p>
<p>All ten notebooks run to completion on free Colab — don't take my word for it, <strong>go run them</strong> and see where your numbers differ from mine.
If you wandered in and this is the first chapter you've read: <a class="" href="https://kobkrit.com/en/blog/llm-01-continue-pretraining">start at chapter 1 — Continue Pretraining</a>
and walk the path all the way to here. See you around.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="references">References<a href="https://kobkrit.com/en/blog/llm-10-deployment#references" class="hash-link" aria-label="Direct link to References" title="Direct link to References" translate="no">​</a></h2>
<ol>
<li class="">Kwon et al. (2023). <a href="https://arxiv.org/abs/2309.06180" target="_blank" rel="noopener noreferrer" class="">Efficient Memory Management for Large Language Model Serving with PagedAttention</a> — PagedAttention: the KV-cache management behind vLLM</li>
<li class="">Yu et al. (2022). <a href="https://www.usenix.org/conference/osdi22/presentation/yu" target="_blank" rel="noopener noreferrer" class="">Orca: A Distributed Serving System for Transformer-Based Generative Models</a> (OSDI '22) — the original continuous batching, hand-rolled in miniature in section 7</li>
<li class="">Dao et al. (2022). <a href="https://arxiv.org/abs/2205.14135" target="_blank" rel="noopener noreferrer" class="">FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness</a> — FlashAttention -- and why a T4 cannot use it</li>
<li class="">Frantar et al. (2022). <a href="https://arxiv.org/abs/2210.17323" target="_blank" rel="noopener noreferrer" class="">GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers</a> — GPTQ: accurate post-training quantization</li>
<li class="">Dettmers et al. (2022). <a href="https://arxiv.org/abs/2208.07339" target="_blank" rel="noopener noreferrer" class="">LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale</a> — LLM.int8(): the basis of bitsandbytes' 8-bit mode</li>
<li class="">Pope et al. (2022). <a href="https://arxiv.org/abs/2211.05102" target="_blank" rel="noopener noreferrer" class="">Efficiently Scaling Transformer Inference</a> — a systems-level analysis of inference bottlenecks</li>
<li class="">Williams et al. (2009). <a href="https://doi.org/10.1145/1498765.1498785" target="_blank" rel="noopener noreferrer" class="">Roofline: An Insightful Visual Performance Model for Multicore Architectures</a> — the roofline model behind section 3's 266 tok/s ceiling</li>
<li class="">Pipatanakul et al. (2023). <a href="https://arxiv.org/abs/2312.13951" target="_blank" rel="noopener noreferrer" class="">Typhoon: Thai Large Language Models</a> — Typhoon: another line of Thai LLMs</li>
<li class="">Nguyen et al. (2023). <a href="https://arxiv.org/abs/2312.00738" target="_blank" rel="noopener noreferrer" class="">SeaLLMs -- Large Language Models for Southeast Asia</a> — SeaLLMs: models for Southeast Asian languages</li>
<li class="">Pairatsuppawat et al. (2025). <a href="https://arxiv.org/abs/2512.19455" target="_blank" rel="noopener noreferrer" class="">SiamGPT: Quality-First Fine-Tuning for Stable Thai Text Generation</a> — SiamGPT: quality-first Thai fine-tuning</li>
</ol>
<hr>
<p><em>The writing, code and notebooks in this series are licensed under <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank" rel="noopener noreferrer" class="">CC BY-NC-SA 4.0</a> — reuse and adapt them freely with attribution, for non-commercial purposes, and share your adaptations under the same terms. Third-party models and datasets referenced here keep their own licences.</em></p>
<nav class="nav_RfLT" aria-label="Thai LLM tutorial series navigation"><p class="heading_XRWm">Thai LLM series<span class="progress_f8e8">Part 10 of 10</span></p><ol class="list_U31a"><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-01-continue-pretraining"><span class="number_u3BE" aria-hidden="true">1</span><span class="title_BPvL">Continue Pretraining</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-02-sft-lora"><span class="number_u3BE" aria-hidden="true">2</span><span class="title_BPvL">SFT and LoRA</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-03-rlhf-ppo"><span class="number_u3BE" aria-hidden="true">3</span><span class="title_BPvL">RLHF and PPO</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-04-dpo"><span class="number_u3BE" aria-hidden="true">4</span><span class="title_BPvL">DPO: Direct Preference Optimization</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-05-grpo"><span class="number_u3BE" aria-hidden="true">5</span><span class="title_BPvL">GRPO</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-06-context-distillation"><span class="number_u3BE" aria-hidden="true">6</span><span class="title_BPvL">Context Distillation</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-07-model-distillation"><span class="number_u3BE" aria-hidden="true">7</span><span class="title_BPvL">Model Distillation</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-08-guardrails"><span class="number_u3BE" aria-hidden="true">8</span><span class="title_BPvL">Guardrails</span></a></li><li class="item_Y10l"><a class="chip_DDpP" href="https://kobkrit.com/en/blog/llm-09-benchmarking"><span class="number_u3BE" aria-hidden="true">9</span><span class="title_BPvL">Benchmarking</span></a></li><li class="item_Y10l"><span class="chip_DDpP chipCurrent_BGpo" aria-current="step"><span class="number_u3BE" aria-hidden="true">10</span><span class="title_BPvL">Deployment</span><span class="srOnly_owtF">(you are here)</span></span></li></ol></nav>]]></content>
        <author>
            <name>Kobkrit Viriyayudhakorn</name>
            <uri>https://github.com/kobkrit</uri>
        </author>
        <category label="ai" term="ai"/>
        <category label="llm" term="llm"/>
        <category label="thai" term="thai"/>
        <category label="tutorial" term="tutorial"/>
        <category label="deployment" term="deployment"/>
        <category label="inference" term="inference"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[รวบรวม Link สำหรับเรียนรู้ Transformer สำหรับ SuperAIEngineer Season 3]]></title>
        <id>https://kobkrit.com/en/blog/link-transformer-superaiengineer-season-3</id>
        <link href="https://kobkrit.com/en/blog/link-transformer-superaiengineer-season-3"/>
        <updated>2021-09-01T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Schedule]]></summary>
        <content type="html"><![CDATA[<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="schedule">Schedule<a href="https://kobkrit.com/en/blog/link-transformer-superaiengineer-season-3#schedule" class="hash-link" aria-label="Direct link to Schedule" title="Direct link to Schedule" translate="no">​</a></h3>
<p>27 Feb 2023</p>
<p>13:00–13:05 แนะนำว่าโจทย์ QA คืออะไร และ Transformer เอาไปอะไรได้บ้าง<br>
13:05–14:00 NLP Core Knowledge<br>
14:00–14:05 — — Brain Break — —<br>
14:05–15:00 Transformer Core Knowledge<br>
15:00–15:05 — — Brain Break — —<br>
15:05–16:00 Colab 1 (Preprocessing + Text Class) + Colab สร้าง QA<br>
16:00–16:15 Q/A Session</p>
<p>— —</p>
<p>28 Feb 2023<br>
09:00–10:00 Colab 2 (NE + POS+ WS + SS)<br>
10:00–10:05 — — Brain Break — —<br>
10:05–11:00 How ChatGPT Build and Works?<br>
11:00–11:05 — — Brain Break — —<br>
11:05–12:00 Colab: Making Your Own ChatGPT (As the way we did on OpenThaiGPT 0.0.1) + Colab: Reinforcement Learning with Human Feedback (RLHF)<br>
12:00–12:15 Q/A Session + OpenThaiGPT Open for Volunteers.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="โจทย์-qa">โจทย์ QA<a href="https://kobkrit.com/en/blog/link-transformer-superaiengineer-season-3#%E0%B9%82%E0%B8%88%E0%B8%97%E0%B8%A2%E0%B9%8C-qa" class="hash-link" aria-label="Direct link to โจทย์ QA" title="Direct link to โจทย์ QA" translate="no">​</a></h3>
<p>อะไรคือ QA: <a href="https://ai.iapp.co.th/product/thai_automatic_qa" target="_blank" rel="noopener noreferrer" class="">https://ai.iapp.co.th/product/thai_automatic_qa</a><br>
Colab สร้าง QA: <a href="https://colab.research.google.com/drive/1inDOJzCh-iG3_aAU-73tq3FzlCwM8nvY" target="_blank" rel="noopener noreferrer" class="">https://colab.research.google.com/drive/1inDOJzCh-iG3_aAU-73tq3FzlCwM8nvY</a></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="nlp-core-knowledge">NLP Core Knowledge<a href="https://kobkrit.com/en/blog/link-transformer-superaiengineer-season-3#nlp-core-knowledge" class="hash-link" aria-label="Direct link to NLP Core Knowledge" title="Direct link to NLP Core Knowledge" translate="no">​</a></h3>
<p>Slide (Basic NLP -&gt; Word Embbeding -&gt; LSTM): <a href="https://drive.google.com/file/d/14AVefnJvgaNXWw6wo-kpmHQyjLAikMgp/view?usp=sharing" target="_blank" rel="noopener noreferrer" class="">https://drive.google.com/file/d/14AVefnJvgaNXWw6wo-kpmHQyjLAikMgp/view?usp=sharing</a></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="transformer-core-knowledge">Transformer Core Knowledge<a href="https://kobkrit.com/en/blog/link-transformer-superaiengineer-season-3#transformer-core-knowledge" class="hash-link" aria-label="Direct link to Transformer Core Knowledge" title="Direct link to Transformer Core Knowledge" translate="no">​</a></h3>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="slide">Slide<a href="https://kobkrit.com/en/blog/link-transformer-superaiengineer-season-3#slide" class="hash-link" aria-label="Direct link to Slide" title="Direct link to Slide" translate="no">​</a></h4>
<p>Slide (Thai NLP in Transformers Era): <a href="https://drive.google.com/file/d/1-V-Gy45c7vHQ4GejvWDBHk0w9oJ4z18I/view?usp=sharing" target="_blank" rel="noopener noreferrer" class="">https://drive.google.com/file/d/1-V-Gy45c7vHQ4GejvWDBHk0w9oJ4z18I/view?usp=sharing</a></p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="colab">Colab<a href="https://kobkrit.com/en/blog/link-transformer-superaiengineer-season-3#colab" class="hash-link" aria-label="Direct link to Colab" title="Direct link to Colab" translate="no">​</a></h4>
<p>Colab 1 (Preprocessing + Text Class): <a href="https://colab.research.google.com/drive/1fGKoS1WH6dbw3mYffOgTtdmPN2Wi9doF?usp=sharing" target="_blank" rel="noopener noreferrer" class="">https://colab.research.google.com/drive/1fGKoS1WH6dbw3mYffOgTtdmPN2Wi9doF?usp=sharing</a><br>
Colab 2 (NE + POS+ WS + SS): <a href="https://colab.research.google.com/drive/1CWamaQH1Lgd7mSZ0UZ4jx2AUMAGDpsfq?usp=sharing#scrollTo=cvrnEG4mOm1p" target="_blank" rel="noopener noreferrer" class="">https://colab.research.google.com/drive/1CWamaQH1Lgd7mSZ0UZ4jx2AUMAGDpsfq?usp=sharing#scrollTo=cvrnEG4mOm1p</a></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="chatgpt-core-knowledge">ChatGPT Core Knowledge<a href="https://kobkrit.com/en/blog/link-transformer-superaiengineer-season-3#chatgpt-core-knowledge" class="hash-link" aria-label="Direct link to ChatGPT Core Knowledge" title="Direct link to ChatGPT Core Knowledge" translate="no">​</a></h3>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="website">Website<a href="https://kobkrit.com/en/blog/link-transformer-superaiengineer-season-3#website" class="hash-link" aria-label="Direct link to Website" title="Direct link to Website" translate="no">​</a></h4>
<p><a href="https://openthaigpt.aieat.or.th/" target="_blank" rel="noopener noreferrer" class="">https://openthaigpt.aieat.or.th/</a></p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="slide-1">Slide<a href="https://kobkrit.com/en/blog/link-transformer-superaiengineer-season-3#slide-1" class="hash-link" aria-label="Direct link to Slide" title="Direct link to Slide" translate="no">​</a></h4>
<p>Slide (ChatGPT: How it works?): <a href="https://docs.google.com/presentation/d/1Q6_S_GDWHuNC0DfprNMn9EH0kyqK_y-RbdK5fJB6EUk/edit#slide=id.g1f3418062d6_0_156" target="_blank" rel="noopener noreferrer" class="">https://docs.google.com/presentation/d/1Q6_S_GDWHuNC0DfprNMn9EH0kyqK_y-RbdK5fJB6EUk/edit#slide=id.g1f3418062d6_0_156</a></p>
<p>Slide (OpenThaiGPT):<br>
<a href="https://docs.google.com/presentation/d/1JJxtwo1pCJC3u6aSfslSp1FJSb5ZS5xBKIiQ-6Kip_g/edit?usp=sharing" target="_blank" rel="noopener noreferrer" class="">https://docs.google.com/presentation/d/1JJxtwo1pCJC3u6aSfslSp1FJSb5ZS5xBKIiQ-6Kip_g/edit?usp=sharing</a></p>
<p><strong>Colab</strong><br>
Finetuning OpenThaiGPT version POC 0.0.1:<br>
<a href="https://colab.research.google.com/drive/1MA1FHwknrs6mVstOHcSyFTnDNWrus-G-?usp=sharing" target="_blank" rel="noopener noreferrer" class="">https://colab.research.google.com/drive/1MA1FHwknrs6mVstOHcSyFTnDNWrus-G-?usp=sharing</a></p>
<p>RLHF: สอนให้ Model Generate ข้อความเชิงบวก (Positive Sentiment) ได้มากขึ้นด้วย PPO <a href="https://colab.research.google.com/drive/1qce78Q00SY7CKXLVtiSGFbP5C1V_nypn?usp=sharing" target="_blank" rel="noopener noreferrer" class="">https://colab.research.google.com/drive/1qce78Q00SY7CKXLVtiSGFbP5C1V_nypn?usp=sharing</a></p>
<hr>
<p><a href="https://kobkrit.com/%E0%B8%A3%E0%B8%A7%E0%B8%9A%E0%B8%A3%E0%B8%A7%E0%B8%A1-link-%E0%B8%AA%E0%B8%B3%E0%B8%AB%E0%B8%A3%E0%B8%B1%E0%B8%9A%E0%B9%80%E0%B8%A3%E0%B8%B5%E0%B8%A2%E0%B8%99%E0%B8%A3%E0%B8%B9%E0%B9%89-transformer-%E0%B8%AA%E0%B8%B3%E0%B8%AB%E0%B8%A3%E0%B8%B1%E0%B8%9A-superaiengineer-season-3-782feb422f32" target="_blank" rel="noopener noreferrer" class="">รวบรวม Link สำหรับเรียนรู้ Transformer สำหรับ SuperAIEngineer Season 3</a> was originally published in <a href="https://kobkrit.com/" target="_blank" rel="noopener noreferrer" class="">Kobkrit</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content>
        <author>
            <name>Kobkrit Viriyayudhakorn</name>
            <uri>https://github.com/kobkrit</uri>
        </author>
        <category label="ai" term="ai"/>
        <category label="nlp" term="nlp"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[แจกวิธี Train Thai Question Answering AI ใช้ Wangchanberta บน Dataset iApp QA โดย Simple Transformer]]></title>
        <id>https://kobkrit.com/en/blog/train-thai-question-answering-system-wangchanberta-iapp-qa-s</id>
        <link href="https://kobkrit.com/en/blog/train-thai-question-answering-system-wangchanberta-iapp-qa-s"/>
        <updated>2021-06-15T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[แจกวิธี Train Thai Question Answering AI ใช้ Wangchanberta บน Dataset iApp QA โดย Simple Transformer]]></summary>
        <content type="html"><![CDATA[<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="แจกวิธี-train-thai-question-answering-ai-ใช้-wangchanberta-บน-dataset-iapp-qa-โดย-simple-transformer">แจกวิธี Train Thai Question Answering AI ใช้ Wangchanberta บน Dataset iApp QA โดย Simple Transformer<a href="https://kobkrit.com/en/blog/train-thai-question-answering-system-wangchanberta-iapp-qa-s#%E0%B9%81%E0%B8%88%E0%B8%81%E0%B8%A7%E0%B8%B4%E0%B8%98%E0%B8%B5-train-thai-question-answering-ai-%E0%B9%83%E0%B8%8A%E0%B9%89-wangchanberta-%E0%B8%9A%E0%B8%99-dataset-iapp-qa-%E0%B9%82%E0%B8%94%E0%B8%A2-simple-transformer" class="hash-link" aria-label="Direct link to แจกวิธี Train Thai Question Answering AI ใช้ Wangchanberta บน Dataset iApp QA โดย Simple Transformer" title="Direct link to แจกวิธี Train Thai Question Answering AI ใช้ Wangchanberta บน Dataset iApp QA โดย Simple Transformer" translate="no">​</a></h3>
<p><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/a7d65532d1d3_b9d193aa9ee6-1f548b63375c48984eae0c8c59beb22b.png" width="1024" height="423" class="img_ev3q"></p>
<p>How to make Thai QA System using SimpleTransformer</p>
<ul>
<li class="">Pretrain Model: <a href="https://medium.com/airesearch-in-th/wangchanberta-%E0%B9%82%E0%B8%A1%E0%B9%80%E0%B8%94%E0%B8%A5%E0%B8%9B%E0%B8%A3%E0%B8%B0%E0%B8%A1%E0%B8%A7%E0%B8%A5%E0%B8%9C%E0%B8%A5%E0%B8%A0%E0%B8%B2%E0%B8%A9%E0%B8%B2%E0%B9%84%E0%B8%97%E0%B8%A2%E0%B8%97%E0%B8%B5%E0%B9%88%E0%B9%83%E0%B8%AB%E0%B8%8D%E0%B9%88%E0%B9%81%E0%B8%A5%E0%B8%B0%E0%B8%81%E0%B9%89%E0%B8%B2%E0%B8%A7%E0%B8%AB%E0%B8%99%E0%B9%89%E0%B8%B2%E0%B8%97%E0%B8%B5%E0%B9%88%E0%B8%AA%E0%B8%B8%E0%B8%94%E0%B9%83%E0%B8%99%E0%B8%82%E0%B8%93%E0%B8%B0%E0%B8%99%E0%B8%B5%E0%B9%89-d920c27cd433" target="_blank" rel="noopener noreferrer" class="">Wangchanberta</a></li>
<li class="">Dataset: <a href="https://huggingface.co/datasets/iapp_wiki_qa_squad" target="_blank" rel="noopener noreferrer" class="">iApp Thai Wikipedia QA</a></li>
<li class="">Training: <a href="https://simpletransformers.ai/docs/qa-minimal-start/" target="_blank" rel="noopener noreferrer" class="">Simple Transformer QA</a></li>
<li class="">Author: Kobkrit Viriyayudhakorn <a href="mailto:kobkrit@iapp.co.th" target="_blank" rel="noopener noreferrer" class="">kobkrit@iapp.co.th</a></li>
<li class="">Written on 14 Apr 2022</li>
</ul>
<p>Colab:</p>
<p><a href="https://colab.research.google.com/drive/1inDOJzCh-iG3_aAU-73tq3FzlCwM8nvY#scrollTo=vLChKnukd3gC" target="_blank" rel="noopener noreferrer" class="">https://colab.research.google.com/drive/1inDOJzCh-iG3_aAU-73tq3FzlCwM8nvY#scrollTo=vLChKnukd3gC</a></p>
<hr>
<p><a href="https://kobkrit.com/%E0%B9%81%E0%B8%88%E0%B8%81%E0%B8%A7%E0%B8%B4%E0%B8%98%E0%B8%B5-train-thai-question-answering-system-%E0%B9%83%E0%B8%8A%E0%B9%89-wangchanberta-%E0%B8%9A%E0%B8%99-iapp-qa-%E0%B9%82%E0%B8%94%E0%B8%A2-simple-transformer-a7d65532d1d3" target="_blank" rel="noopener noreferrer" class="">แจกวิธี Train Thai Question Answering AI ใช้ Wangchanberta บน Dataset iApp QA โดย Simple…</a> was originally published in <a href="https://kobkrit.com/" target="_blank" rel="noopener noreferrer" class="">Kobkrit</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content>
        <author>
            <name>Kobkrit Viriyayudhakorn</name>
            <uri>https://github.com/kobkrit</uri>
        </author>
        <category label="ai" term="ai"/>
        <category label="nlp" term="nlp"/>
        <category label="thai" term="thai"/>
        <category label="tutorial" term="tutorial"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[แจกไบเบิ้ล วิธีการ Finetune BERT, Roberta, Wangchanberta สำหรับงาน NLP ภาษาไทยแบบง่าย พร้อมแจกโค้ดบน Colab!!]]></title>
        <id>https://kobkrit.com/en/blog/finetune-bert-roberta-wangchanberta-nlp</id>
        <link href="https://kobkrit.com/en/blog/finetune-bert-roberta-wangchanberta-nlp"/>
        <updated>2021-03-10T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[แจกไบเบิ้ล วิธีการ Finetune BERT, Roberta, Wangchanberta สำหรับงาน NLP ภาษาไทยแบบง่าย พร้อมแจกโค้ดบน Colab!!]]></summary>
        <content type="html"><![CDATA[<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="แจกไบเบิ้ล-วิธีการ-finetune-bert-roberta-wangchanberta-สำหรับงาน-nlp-ภาษาไทยแบบง่าย-พร้อมแจกโค้ดบน-colab">แจกไบเบิ้ล วิธีการ Finetune BERT, Roberta, Wangchanberta สำหรับงาน NLP ภาษาไทยแบบง่าย พร้อมแจกโค้ดบน Colab!!<a href="https://kobkrit.com/en/blog/finetune-bert-roberta-wangchanberta-nlp#%E0%B9%81%E0%B8%88%E0%B8%81%E0%B9%84%E0%B8%9A%E0%B9%80%E0%B8%9A%E0%B8%B4%E0%B9%89%E0%B8%A5-%E0%B8%A7%E0%B8%B4%E0%B8%98%E0%B8%B5%E0%B8%81%E0%B8%B2%E0%B8%A3-finetune-bert-roberta-wangchanberta-%E0%B8%AA%E0%B8%B3%E0%B8%AB%E0%B8%A3%E0%B8%B1%E0%B8%9A%E0%B8%87%E0%B8%B2%E0%B8%99-nlp-%E0%B8%A0%E0%B8%B2%E0%B8%A9%E0%B8%B2%E0%B9%84%E0%B8%97%E0%B8%A2%E0%B9%81%E0%B8%9A%E0%B8%9A%E0%B8%87%E0%B9%88%E0%B8%B2%E0%B8%A2-%E0%B8%9E%E0%B8%A3%E0%B9%89%E0%B8%AD%E0%B8%A1%E0%B9%81%E0%B8%88%E0%B8%81%E0%B9%82%E0%B8%84%E0%B9%89%E0%B8%94%E0%B8%9A%E0%B8%99-colab" class="hash-link" aria-label="Direct link to แจกไบเบิ้ล วิธีการ Finetune BERT, Roberta, Wangchanberta สำหรับงาน NLP ภาษาไทยแบบง่าย พร้อมแจกโค้ดบน Colab!!" title="Direct link to แจกไบเบิ้ล วิธีการ Finetune BERT, Roberta, Wangchanberta สำหรับงาน NLP ภาษาไทยแบบง่าย พร้อมแจกโค้ดบน Colab!!" translate="no">​</a></h3>
<p>ชุดซอฟต์แวร์ Transformer จาก Huggingface (<a href="https://huggingface.co/" target="_blank" rel="noopener noreferrer" class="">https://huggingface.co/</a>) เป็นศูนย์รวม Software, Model และ Datasets ในการใช้ Transformer ทางด้าน NLP ที่ยอดนิยมที่สุดในโลก สนับสนุนทางภาษาไทยและภาษาอังกฤษ และทุกภาษาทั่วโลก</p>
<p>การใช้งานชุดซอฟต์แวร์ Transformer จาก Huggingface นี้ ต้องมีความรู้เฉพาะทางของแต่ละ Model ในตระกูล Transformer และต้องเรียนรู้ API ของ Huggingface ต่างๆ อาทิเช่น Datasets, Trainer, Tokenizer, Inference API ที่ต้องใช้เวลาและการเรียนรู้ค่อนข้างนาน (แต่ก็ดีกว่าไป Clone GIT Repo ของ Transformer แต่ละตัวมาแล้วมาเรียนรู้และเล่นเอง ไปหลายขุมแล้ว)</p>
<p>เพื่อที่จะให้ผู้ที่ทำการเรียนรู้ สามารถนำ Model Transformer นำไปใช้งานได้อย้างรวดเร็ว โดยที่เข้าใจถึงพัฒนาการของงาน NLP จาก One-hot Encoding, Word2Vec, LSTM, Encoder &amp; Decoder และ Transformers ได้ด้วยนั้น</p>
<p><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/1fbbaac7c905_2d2e674cce7a-ef9b911876d6958c07999543a9154576.jpeg" width="1024" height="682" class="img_ev3q">สอน NLP Processing + Toolkits ที่ค่าย Super AI Engineer Season 2 ระหว่างวันที่ 14–15 กุมภาพันธ์ 2565</p>
<p>ทางผมได้รับเชิญ ให้สอนวิธีการพัฒนา AI เหล่านี้ในค่าย Super AI Engineer Season 2 ผู้เข้าร่วมประมาณ 130 ท่าน จัดโดยสมาคมปัญญาประดิษฐ์ประเทศไทย (AIAT) ระหว่างวันที่ 14–15 กุมภาพันธ์ 2565โดยสอนวิธีการสร้าง Model AI ด้าน NLP ตั้งแต่วิธี Basic จนถึงการใช้ Word2Vec, LSTM, BERT, Roberta และสอน Finetune โมโดลโดยการใช้ Tensorflow Keras, Pytorch และสุดท้าย Finetune บนซอฟต์แวร์ชุด Simple Transformer (<a href="https://simpletransformers.ai/" target="_blank" rel="noopener noreferrer" class="">https://simpletransformers.ai)/</a>ซึ่งเป็นชุดซอฟต์แวร์ที่ที่ทำให้เรา Finetune Model บนข้อมูลบน Pandas ได้โดยง่าย โดยไม่ต้องเขียน Data Class หรือใช้ Data loader บนงาน NLP ภาษาไทยและอังกฤษได้ ซึ่งใน Colab มีตัวอย่างตั้งแต่</p>
<ol>
<li class="">Text Cleaning</li>
<li class="">Text Classification</li>
<li class="">Text Similarity</li>
<li class="">Word Segmentation</li>
<li class="">Name Entity Recognition (NER)</li>
<li class="">Part of Speech Tagging (POS)</li>
<li class="">Sentence Segmentation</li>
</ol>
<p>พร้อมตัวอย่างใช้งานได้จริงผ่าน Notebook บน Google Colaboratory ใน 2 Links นี้</p>
<p>NLP Preprocessing + Text Classification (Monday 14 Feburary)<br>
<a href="https://bit.ly/sai2-nlp1" target="_blank" rel="noopener noreferrer" class="">https://bit.ly/sai2-nlp1</a></p>
<p>NE + POS + WS + SS (Tuesday 15 Feburary)<a href="https://bit.ly/sai2-nlp2" target="_blank" rel="noopener noreferrer" class=""><br>
https://bit.ly/sai2-nlp2</a></p>
<p>และสุดท้ายสอนการ Upload Model และ Tokenizer ขึ้นที่หน้าเว็บไซด์ของ Huggingface อีกด้วย ในท้ายของวันที่ 15 Faburary</p>
<p>ใครสนใจลองเข้าไปเรียนรู้ดูได้ หากเจอข้อผิดพลาดอะไร สามารถแจ้งมาที่ได้เลย มาจะทำการ Update แก้ไขให้ครับ</p>
<p>Colab ตัวนี้เป็นแบบ MIT license สามารถใช้ในการแจกจ่าย ดัดแปลง ไปใช้ในทางธุรกิจ อะไรได้หมดเลยครับ ทางผมยินดีครับ ขอบคุณครับ</p>
<hr>
<p><a href="https://kobkrit.com/%E0%B9%81%E0%B8%88%E0%B8%81%E0%B9%84%E0%B8%9A%E0%B9%80%E0%B8%9A%E0%B8%B4%E0%B9%89%E0%B8%A5-%E0%B8%A7%E0%B8%B4%E0%B8%98%E0%B8%B5%E0%B8%81%E0%B8%B2%E0%B8%A3-finetune-bert-roberta-wangchanberta-%E0%B8%AA%E0%B8%B3%E0%B8%AB%E0%B8%A3%E0%B8%B1%E0%B8%9A%E0%B8%87%E0%B8%B2%E0%B8%99-nlp-%E0%B8%A0%E0%B8%B2%E0%B8%A9%E0%B8%B2%E0%B9%84%E0%B8%97%E0%B8%A2%E0%B9%81%E0%B8%9A%E0%B8%9A%E0%B8%87%E0%B9%88%E0%B8%B2%E0%B8%A2-1fbbaac7c905" target="_blank" rel="noopener noreferrer" class="">แจกไบเบิ้ล วิธีการ Finetune BERT, Roberta, Wangchanberta สำหรับงาน NLP ภาษาไทยแบบง่าย…</a> was originally published in <a href="https://kobkrit.com/" target="_blank" rel="noopener noreferrer" class="">Kobkrit</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content>
        <author>
            <name>Kobkrit Viriyayudhakorn</name>
            <uri>https://github.com/kobkrit</uri>
        </author>
        <category label="nlp" term="nlp"/>
        <category label="thai" term="thai"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[ศักยภาพของ AI สู่โอกาสใหม่แห่งการแข่งขันและความสำเร็จ]]></title>
        <id>https://kobkrit.com/en/blog/ai-ai-2019</id>
        <link href="https://kobkrit.com/en/blog/ai-ai-2019"/>
        <updated>2019-12-15T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[https://medium.com/media/f8ead5836ef79253f028959f246dd628/href]]></summary>
        <content type="html"><![CDATA[<p><a href="https://medium.com/media/f8ead5836ef79253f028959f246dd628/href" target="_blank" rel="noopener noreferrer" class="">https://medium.com/media/f8ead5836ef79253f028959f246dd628/href</a></p>
<p>สวัสดีครับ เจอกันอีกแล้วนะครับ ผม กอบกฤตย์ นะครับ</p>
<p>เนื่องจากทางผมมีโอกาสได้ไปพูดที่งาน Metalex 2019 เป็นครั้งที่สองแล้วนะครับ ในฐานะกรรมการสมาคมปัญญาประดิษฐ์ประเทศไทย (AIAT) ในหัวข้อเรื่อง <strong><em>ศักยภาพของ AI สู่โอกาสใหม่แห่งการแข่งขันและความสำเร็จ</em></strong> ซึ่งเป็นแนวที่ค่อนข้าง Abstract มาก ผมเลยคิดว่า มันก็เป็นโอกาสอันดีเหมือนกัน ที่ได้สรุปข่าว AI ที่สำคัญๆในปี 2019 มารวบรวมให้กับผู้อ่านทุกท่าน และให้ทุกท่านได้เตรียมตัวปรับตัวกับกระแส AI Disruption ที่จะรุนแรงขึ้นเรื่อยๆในปี 2020 นะครับ โดยหัวข้อที่ผมพูดแบ่งเป็น 3 หัวข้อหลักดังนี้นะครับ</p>
<p><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/588fdf26e410_b00103258000-520ca05205051cf18f2321010ceed4e8.jpeg" width="1024" height="483" class="img_ev3q"></p>
<p><strong>หัวข้อที่ 1:</strong> เรื่องความก้าวหน้าด้าน AI ของทั้งโลกในปี 2019 ต้องยอมรับว่า สิ่งที่ก้าวหน้าที่สุดในปี 2019 นี่ ผมต้องยกให้กับเรื่อง Transfer Learning เลยครับ Transfer Learning คือการที่ AI เรียนรู้ข้อมูลจากแหล่งอื่นมาก่อน (มักจะเป็น Unsupervised Learning) แล้วสามารถนำมาสร้าง Model ใหม่ตามที่เราต้องการได้โดยใช้ข้อมูลน้อยลงมากๆ และ ไม่ว่าจะเป็น Domain ที่ชัดเจนมากๆ อาทิเช่น Natural Language Processing โดยการสร้าง Langauge Model จาก Corpus บทความขนาดใหญ่ ด้วยวิธี Pre-training แล้วค่อยมาปรับ Fine-tune กับงานที่เราต้องการใช้ภายหลังนะครับ ตัวอย่างที่ชัดเจนคือ Google BERT (<a href="https://github.com/google-research/bert" target="_blank" rel="noopener noreferrer" class="">https://github.com/google-research/bert</a>)</p>
<p>หนึ่งในนั้นที่น่าสนใจก็คือเรื่อง Machine Translation นะครับ ที่ไม่ต้องใช้เนื้อหา Translation Pair ในการสอน AI แล้ว เพียงแค่นำ Pre-training ของแต่ละภาษามาแล้ว AI จะหาคู่คำแปลได้เองอัตโนมัติด้วยเทคนิคการ Map ให้ Word-Embedding ตรงกันนะครับ ทำให้เราสามารถทำ Machine Translation ได้ด้วยต้นทุนที่ถูกลงมากเลยครับ หากใครสนใจไปลองอ่าน Facebook XLM ดูนะครับ (<a href="https://github.com/facebookresearch/XLM/commit/0650071bb97b7176edc5acbec75ebce18e071738" target="_blank" rel="noopener noreferrer" class="">https://github.com/facebookresearch/XLM</a>)</p>
<p>เรื่อง Speech นะครับ ตอนนี้เค้าสามารถสร้าง Speech Model ด้วยเทคนิค Pre-training โดยให้ AI ฟังเสียงตัวอย่างการพูดจากคนหลายๆพันคนนะครับ โดยที่ไม่ต้องมี Text Script ที่ควบคู่กับเสียงแล้วนะครับ และสามารถสร้างตัว Text-to-Speech (TTS) เป็นเสียงของใครก็ได้ ขอเพียงแค่มีตัวอย่างเสียงของผู้พูดที่เราต้องการเพียงแค่ 5 วินาทีเท่านั้น สามารถลองไปดูรายละเอียดเพิ่มเติมที่ <a href="https://github.com/CorentinJ/Real-Time-Voice-Cloning" target="_blank" rel="noopener noreferrer" class="">https://github.com/CorentinJ/Real-Time-Voice-Cloning</a> ได้เลยนะครับ</p>
<p>ห<strong>ัวข้อที่ 2</strong> โอกาสใหม่ๆครับ ในปี 2020 เราจะเริ่มเห็นหุ่นยนต์มากขึ้นเรื่อยๆ ในปี 2025 เราจะเห็นหุ่นยนต์จนชินตา และคาดว่าจำนวนประเภทหุ่นยนต์จะมากกว่าจำนวน Species ของสัตว์ทั้งหมดในช่วงปี 2030 เราจะเห็นบริษัททางด้านหุ่นยนต์เติบโตขึ้นมหาศาลครับ อาทิเช่น หุ่นยนต์ทำความสะอาด,​หุ่นยนต์ประจำบ้าน, Smart Speaker และโอกาสต่างๆจากหุ่นยนต์ก็มหาศาลเช่นกัน อาทิเช่น</p>
<ol>
<li class="">งานการเขียนโปรแกรม AI เพิ่มความสามารถหุ่นยนต์ในด้านต่างๆ ความต้องการ</li>
<li class="">ระบบศุนย์รวมข้อมูลและ Control ผ่านระบบ IOT ที่สามารถเชื่อมโยงหุ่นยนต์เข้าไว้ด้วยกัน</li>
<li class="">ระบบ Big Data ที่ทำให้ AI เข้าใจข้อมูลมากยิ่งขึ้น จนกระทั่งรู้ใจลูกค้ามากยิ่งกว่าตัวลูกค้าเอง เราจะสามารถสร้าง Personalize Marketing ซึ่งจะช่วยสร้างเม็ดเงินมหาศาลจากความสามารถเหล่านี้ครับ</li>
</ol>
<p>ส่วนประเทศไทยต้องเริ่ม Focus จากฐานการผลิตชิ้นส่วนรถยนต์เครื่องยนต์ที่ใช้นำมัน มาเป็นการผลิตชิ้นส่วนหุ่นยนต์ หรือตัวหุ่นยนต์เองได้แล้วนะครับ (รถยนต์ไฟฟ้าใช้ชิ้นส่วนไม่กี่ชี้นเอง และมักจะผลิตเป็นเนื้อเดียวกันแต่แรก) จะและต้องเร่งให้บริษัททางด้าน IT และ Software House ต้องสามารถใช้งาน AI ได้เป็น พร้อมตอบรับความต้องการของลูกค้าที่มีมากขึ้นในปี 2020 นะครับ</p>
<p>ห<strong>ัวข้อที่ 3</strong> คือเรื่องการแข่งขันนะครับ ก็ค่อนข้างชัดเจนว่า งานที่ถูกสร้างเพราะการมาถึงของ AI จะเพิ่มตำแหน่งงานมากถึงประมาณ 133 ล้านตำแหน่งนะครับ แต่ก็จะทำลายตำแหน่งงานเก่าๆ ที่มาถูก AI ด้วยประมาณ 75 ล้านตำแหน่งเช่นกัน ในประเทศไทยจะมีปัญหาใหญ่มากๆ อันนึงก็คือปัญหา Skill Gap ครับ คือคนที่ทำ AI ได้ จะถูกแย่งตัวกันมาก และคนที่ทำ AI ไม่ได้ จะใช้เวลานานพอสมควร (3 เดือน — 1 ปี) กว่าจะสามารถมาเรียนรู้จนมาทำ AI ได้ คนที่ทำไม่ได้ จะหางานยากขึ้นกว่าเดิมมาก (เพราะตำแหน่งลดลงไปถึง 33%) ส่วนคนที่ทำได้แล้ว จะมีความต้องการเพิ่มขึ้นเป็น 2 เท่าในปี คศ. 2022</p>
<p>คนไทยมีปัญหาในเรื่องพื้นฐานความรู้สำหรับงาน AI ค่อนข้างมาก (ขาดทักษะ STEM) จะส่งผลให้คนจำนวนมากตกที่นั่งลำบากในอนาคตอันใกล้นี้ครับ วิธีการแก้ไขก็คือกลับไปทบทวนวิชาคณิตศาสตร์และคอมพิวเตอร์ครับ เพราะ AI คือใช้คณิตศาสตร์เป็นหลัก จำพวก Linear Algebra, Differiential Equation และทักษะทางด้าน Programming โดยเฉพาะภาษา Python ที่สามารถนำมาเขียน AI ได้ดีที่สุดนะครับ</p>
<p>รายละเอียด Slide ทั้งหมดสามารถดูได้ที่ Slideshare ด้านบนนะครับ</p>
<p><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/588fdf26e410_305898db0264-f25565a513b093ea2dbd547b3006361e.jpeg" width="1024" height="576" class="img_ev3q"><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/588fdf26e410_d39ed050e889-fed05ef65b776f94e991d36fc2f2de84.jpeg" width="640" height="1355" class="img_ev3q"></p>
<p>หากท่านชอบ Blog ความรู้แบบนี้ฝากกดรูปตบมือ หรือ ช่วยแชร์บทความลง Social Network ที่ท่านชื่นชอบได้เลยครับ</p>
<p>ขอบคุณครับ</p>
<p>หากใครสนใจอยากจะพัฒนาหรือต้องการที่ปรึกษาด้าน AI สามารถเข้าไปดูผลงานของบริษัทเรา iApp Technology ได้ที่ <a href="https://iapp.co.th/" target="_blank" rel="noopener noreferrer" class="">https://iapp.co.th</a> และ <a href="https://ai.iapp.co.th/" target="_blank" rel="noopener noreferrer" class="">https://ai.iapp.co.th</a> หรือติดต่อได้ที่ <a href="mailto:kobkrit@iapp.co.th" target="_blank" rel="noopener noreferrer" class="">kobkrit@iapp.co.th</a> ได้เลยนะครับ #Ai #iApp</p>
<p><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/588fdf26e410_cec39426438f-a84af37f4cec9ad0496e13663fd061ec.png" width="1024" height="190" class="img_ev3q"><a href="https://ai.iapp.co.th/" target="_blank" rel="noopener noreferrer" class="">https://ai.iapp.co.th</a></p>
<p>ดูเพิ่มเติมได้ที่ <a href="https://iapp.co.th/" target="_blank" rel="noopener noreferrer" class="">https://iapp.co.th</a> และ <a href="https://ai.iapp.co.th/" target="_blank" rel="noopener noreferrer" class="">https://ai.iapp.co.th</a> :D</p>
<hr>
<p><a href="https://kobkrit.com/%E0%B8%A8%E0%B8%B1%E0%B8%81%E0%B8%A2%E0%B8%A0%E0%B8%B2%E0%B8%9E%E0%B8%82%E0%B8%AD%E0%B8%87-ai-%E0%B8%AA%E0%B8%B9%E0%B9%88%E0%B9%82%E0%B8%AD%E0%B8%81%E0%B8%B2%E0%B8%AA%E0%B9%83%E0%B8%AB%E0%B8%A1%E0%B9%88%E0%B9%81%E0%B8%AB%E0%B9%88%E0%B8%87%E0%B8%81%E0%B8%B2%E0%B8%A3%E0%B9%81%E0%B8%82%E0%B9%88%E0%B8%87%E0%B8%82%E0%B8%B1%E0%B8%99%E0%B9%81%E0%B8%A5%E0%B8%B0%E0%B8%84%E0%B8%A7%E0%B8%B2%E0%B8%A1%E0%B8%AA%E0%B8%B3%E0%B9%80%E0%B8%A3%E0%B9%87%E0%B8%88-%E0%B8%A3%E0%B8%B2%E0%B8%A2%E0%B8%87%E0%B8%B2%E0%B8%99%E0%B8%84%E0%B8%A7%E0%B8%B2%E0%B8%A1%E0%B8%81%E0%B9%89%E0%B8%B2%E0%B8%A7%E0%B8%AB%E0%B8%99%E0%B9%89%E0%B8%B2%E0%B8%82%E0%B8%AD%E0%B8%87-ai-%E0%B9%83%E0%B8%99%E0%B8%8A%E0%B9%88%E0%B8%A7%E0%B8%87%E0%B8%9B%E0%B8%B5-2019-588fdf26e410" target="_blank" rel="noopener noreferrer" class="">ศักยภาพของ AI สู่โอกาสใหม่แห่งการแข่งขันและความสำเร็จ (รายงานความก้าวหน้าของ AI ในช่วงปี 2019)</a> was originally published in <a href="https://kobkrit.com/" target="_blank" rel="noopener noreferrer" class="">Kobkrit</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content>
        <author>
            <name>Kobkrit Viriyayudhakorn</name>
            <uri>https://github.com/kobkrit</uri>
        </author>
        <category label="ai" term="ai"/>
        <category label="thai" term="thai"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[การประมวลภาษาไทย NLP แบบ Transfer Learning ด้วย BERT]]></title>
        <id>https://kobkrit.com/en/blog/nlp-transfer-learning-bert</id>
        <link href="https://kobkrit.com/en/blog/nlp-transfer-learning-bert"/>
        <updated>2019-06-10T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[สวัสดีครับ ไม่ได้ Post กันมาซักพักเลยครับ สบายดีไหมครับ ผม กอบกฤตย์ นะครับ เนื่องจากทางผม ได้รับการเชื้อเชิญโดยคุณ Charin  มาพูดในงาน Data Science BKK #9  เลยมาพูดเรื่อง BERT ซักหน่อยครับ]]></summary>
        <content type="html"><![CDATA[<p>สวัสดีครับ ไม่ได้ Post กันมาซักพักเลยครับ สบายดีไหมครับ ผม กอบกฤตย์ นะครับ เนื่องจากทางผม ได้รับการเชื้อเชิญโดยคุณ Charin <a href="https://www.facebook.com/charin.lin.5" target="_blank" rel="noopener noreferrer" class="">https://www.facebook.com/charin.lin.5</a> มาพูดในงาน Data Science BKK #9 <a href="https://www.facebook.com/groups/dsbkkgroup/" target="_blank" rel="noopener noreferrer" class="">https://www.facebook.com/groups/dsbkkgroup/</a> เลยมาพูดเรื่อง BERT ซักหน่อยครับ</p>
<p>BERT เนี่ย มันย่อมาจาก Bidirectional Encoder Representations from Transformers พัฒนาโดย Google ครับ มันเป็น AI Deep Learning แบบ Transformer สำหรับงาน NLP (Natural Language Processing) โดยเฉพาะ ซึ่งเอาชนะ State-of-the-Art ในงาน NLP ได้กระจุยหลายตัวครับ</p>
<p>เวลาเทรน BERT เนี่ย ต้องทำการ Train 2 รอบไม่เหมือนกับ Deep Learning ทั่วๆไปแบบ LSTM หรือ RNN นะครับ มันจะแบ่งเป็น Pre-training เรียนรู้เข้าใจภาษาจากเนื้อหาข้อความภาษาจำนวนมาก(อาทิเช่นมาจาก Wikipedia, Toronto Book Corpus) เป็นการเรียนรู้แบบ Unsupervised Learning ก่อนนะครับ ซึ่ง Data ที่ใช้ไม่คต้องมีการ Label Class อะไร เพื่อให้ BERT เข้าใจ Language Model (LM) ของภาษานั้นๆเสียก่อนครับ</p>
<p>หลังจากที่ BERT เข้าใจ Language Model เรียบร้อยแล้วเราก็จะเอา BERT มาใช้งานในด้านต่างๆอาทิเช่น ทำ Sentimental Analysis ก็ต้องทำการ Train รอบที่สอง เรียกว่า Fine Tune นะครับ เป็นแบบ Supervised Learning โดยที่เราต้องเอา Data ที่เราต้องสอนมัน แบบมี Class อาทิเช่น Wisesight Sentimetal Data set (<a href="https://www.kaggle.com/c/wisesight-sentiment" target="_blank" rel="noopener noreferrer" class="">https://www.kaggle.com/c/wisesight-sentiment</a>) มาปรับ Weight ที่ Layer ท้ายๆของ BERT ครับ ให้มันเรียนรู้ให้แยกแยะ ข้อความอารมณ์ดี หรือข้อความอารมณ์เสียได้</p>
<p>เนื้อหาฉบับเต็มจะอยู่ใน Link Youtube ข้างล่างนะครับ โดยเนื้อหาประกอบไปด้วย</p>
<ol>
<li class="">ฺฺBERT ดีอย่างไร</li>
<li class="">การทำ NLP ในยุค Deep Learning จาก One-hot encoding ถึง BERT</li>
<li class="">วิธีการทำงานของ BERT</li>
<li class="">การ Pre-training, Fine-tuning และการใช้งาน BERT ของจริง</li>
<li class="">มีอะไรจะมาเจ๋งกว่า BERT มาอีกไหม</li>
</ol>
<p><strong>ดู Video ได้เลยครับ</strong></p>
<p><a href="https://medium.com/media/6ba726cc23e3064c9ac7058aca02de25/href" target="_blank" rel="noopener noreferrer" class="">https://medium.com/media/6ba726cc23e3064c9ac7058aca02de25/href</a></p>
<p><strong>Slide shares:</strong></p>
<p><a href="https://medium.com/media/dcf1748492673092fc07faa4a3fcabff/href" target="_blank" rel="noopener noreferrer" class="">https://medium.com/media/dcf1748492673092fc07faa4a3fcabff/href</a><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/1e8abaa578dc_fad777dd3d24-db1c18a8f9607296fc14142ffdb325e5.jpeg" width="960" height="720" class="img_ev3q"><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/1e8abaa578dc_6a8b80f4b20d-0d3c6cc9e2a5b102c622ce526de6bf03.jpeg" width="960" height="720" class="img_ev3q"><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/1e8abaa578dc_eb881d2ed785-f3607e8978258e6a8b7930193e7ffd18.jpeg" width="960" height="720" class="img_ev3q"><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/1e8abaa578dc_b18494ec04fa-ce4f57919d2bf97afd0e340e0b7aa770.jpeg" width="960" height="720" class="img_ev3q"></p>
<p>หากมีคำถามอะไร สามารถทิ้ง Comment ไว้ได้เลยนะครับ จะรีบมาตอบให้เร็วที่สุด หากชอบใจบทความนี้ฝากกดปุ่มตบมือ (Clap) ให้หน่อยนะครับ</p>
<p>หากใครสนใจอยากจะพัฒนาหรือต้องการที่ปรึกษาด้าน AI สามารถเข้าไปดูผลงานของบริษัทเรา iApp Technology ได้ที่ <a href="https://iapp.co.th/" target="_blank" rel="noopener noreferrer" class="">https://iapp.co.th</a> และ <a href="https://ai.iapp.co.th/" target="_blank" rel="noopener noreferrer" class="">https://ai.iapp.co.th</a> หรือติดต่อได้ที่ <a href="mailto:kobkrit@iapp.co.th" target="_blank" rel="noopener noreferrer" class="">kobkrit@iapp.co.th</a> ได้เลยนะครับ #Ai #iApp</p>
<p><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/588fdf26e410_cec39426438f-a84af37f4cec9ad0496e13663fd061ec.png" width="1024" height="190" class="img_ev3q"><a href="https://ai.iapp.co.th/" target="_blank" rel="noopener noreferrer" class="">https://ai.iapp.co.th</a></p>
<p>ดูเพิ่มเติมได้ที่ <a href="https://iapp.co.th/" target="_blank" rel="noopener noreferrer" class="">https://iapp.co.th</a> และ <a href="https://ai.iapp.co.th/" target="_blank" rel="noopener noreferrer" class="">https://ai.iapp.co.th</a> :D</p>
<hr>
<p><a href="https://kobkrit.com/%E0%B8%81%E0%B8%B2%E0%B8%A3%E0%B8%9B%E0%B8%A3%E0%B8%B0%E0%B8%A1%E0%B8%A7%E0%B8%A5%E0%B8%A0%E0%B8%B2%E0%B8%A9%E0%B8%B2%E0%B9%84%E0%B8%97%E0%B8%A2-nlp-%E0%B9%81%E0%B8%9A%E0%B8%9A-transfer-learning-%E0%B8%94%E0%B9%89%E0%B8%A7%E0%B8%A2-bert-1e8abaa578dc" target="_blank" rel="noopener noreferrer" class="">การประมวลภาษาไทย NLP แบบ Transfer Learning ด้วย BERT</a> was originally published in <a href="https://kobkrit.com/" target="_blank" rel="noopener noreferrer" class="">Kobkrit</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content>
        <author>
            <name>Kobkrit Viriyayudhakorn</name>
            <uri>https://github.com/kobkrit</uri>
        </author>
        <category label="ai" term="ai"/>
        <category label="nlp" term="nlp"/>
        <category label="thai" term="thai"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[จำนวนบริษัทด้าน AI ของไทย เมื่อเทียบกับเพื่อนบ้าน]]></title>
        <id>https://kobkrit.com/en/blog/ai</id>
        <link href="https://kobkrit.com/en/blog/ai"/>
        <updated>2019-04-20T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[ผล Government Artificial Intelligence Readiness Index ประจำปี 2019 จัดอันดับโดย Oxford Insights]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/a229d5bc123a_fa61e7758f13-7b597445ea446a67dec1d5d193c761c4.png" width="1024" height="594" class="img_ev3q">ผล Government Artificial Intelligence Readiness Index ประจำปี 2019 จัดอันดับโดย Oxford Insights</p>
<p>เร็วๆนี้มีข่าวว่า หนึ่งในตัวชี้วัดความพร้อมด้าน AI ของแต่ละประเทศคือ จำนวนบริษัท AI Startups โดยจากการจัดอันดับ Government AI Readiness 2019 [<a href="https://www.oxfordinsights.com/ai-readiness2019" target="_blank" rel="noopener noreferrer" class="">1</a>],[<a href="https://www.bangkokpost.com/tech/1719147/thailand-56th-in-ai-readiness-index" target="_blank" rel="noopener noreferrer" class="">2</a>] โดยทาง Oxford Insights ผู้จัดอันดับนั้น ไปเอาข้อมูลจากเว็ปไซด์ <a href="https://www.crunchbase.com/hub/artificial-intelligence-startups?fbclid=IwAR0yPQI0SfvObBEJ01BsEwYBVTO3fFFTHG94MaZ9xLS5YQBTWEgdVNAQpeo#section-overview" target="_blank" rel="noopener noreferrer" class="">https://www.crunchbase.com</a> มาพิจารณา โชคดีที่ผมได้กรอกข้อมูลของบริษัทผมไว้ บริษัท ไอแอพพ์เทคโนโลยี จำกัด (iApp Technology Co., Ltd. — <a href="https://iapp.co.th/" target="_blank" rel="noopener noreferrer" class="">https://iapp.co.th</a>) จึงเป็นหนึ่งในบริษัท AI ของไทยที่ได้ถูก Index ด้วย (มีอารมณ์ภาคภูมิใจนิดๆ)</p>
<p>เว็บไซด์ Crunchbase.com ถือว่าเป็น Website ฐานข้อมูล Startup ที่ใหญ่ที่สุดในโลก ผมรู้จักเว็บไซด์นี้ครั้งแรก ตอนได้ไปร่วมกับ JFDI.Asia Startup Accelerator ที่สิงคโปร์ในปี 2014 โดยคุณเจ้าของ JFDI.Asia Startup Accelerator คุณ Meng Wong แนะนำให้กรอกข้อมูล Startup ของตัวเองลงเว็บไซด์นี้ ภายในสัปดาห์แรกของการเริ่มเข้าค่ายเลยทีเดียว นักลงทุนทั่วโลกเค้าจะได้รู้จัก เพราะนักลงทุนเค้าจะอ้างอิงฐานข้อมูลนี้เป็นมาตรฐานเสมอๆ</p>
<p>ผมจึงอยากเชิญชวนทุกท่านที่ทำ Startup ทุกท่านว่า หากใครต้องการได้รับ Invest จากนักลงทุนทั่วโลก ควรเสียเวลาเล็กน้อยกรอกข้อมูลของท่านลงในเว็บไซด์นี้ แล้วคนทั้งโลกจะได้รู้จักบริษัทของท่าน</p>
<p>คราวนี้ผมสงสัยจริงๆว่า จำนวนบริษัทในไทย ที่เป็น Artificial Intelligence นั้นมีทั้งหมดกี่บริษัทกันแน่ และเราเป็นอันดับที่เท่าไร่ใน Southeast Asian นี้</p>
<p>เพื่อหาคำตอบ ผมเลยใช้หน้า Search ของ Crunchbase ซึ่งสามารถระบุได้ 2 เงื่อนไข (Package ฟรี) ทางผมเลยใช้ Search Condition ง่ายๆดังรูป ที่ Website ของ Crunchbase</p>
<ol>
<li class="">Location = ประเทศนั้นๆ</li>
<li class="">Category = Artificial Intelligence</li>
</ol>
<p><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/a229d5bc123a_d9ec2ff386cc-b4daaa01b0134dea970f35af278bfe5b.png" width="1024" height="234" class="img_ev3q"></p>
<p>(เนื่องจากผมไม่ได้ซื้อ Package Crunchbase Pro ไว้นะครับ เลยแสดงผลแค่ 5 อันดับแรกของแต่ละประเทศเท่านั้น)</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="ประเทศไทย">ประเทศไทย<a href="https://kobkrit.com/en/blog/ai#%E0%B8%9B%E0%B8%A3%E0%B8%B0%E0%B9%80%E0%B8%97%E0%B8%A8%E0%B9%84%E0%B8%97%E0%B8%A2" class="hash-link" aria-label="Direct link to ประเทศไทย" title="Direct link to ประเทศไทย" translate="no">​</a></h3>
<p>ประเทศไทยมี 11 บริษัทครับ บริษัทที่ Rank ดีที่สุดคือ Wongnai นั้นเอง และบริษัทของผมเอง iApp Technology อยู่ที่ 2</p>
<p>บริษัทในประเทศไทยเป็นบริษัท AI Application แนวจับ Domain ต่างๆ หลากหลาย อาทิเช่น อาหาร, NLP ภาษาไทย (AI Consulting), อสังหา, รถยนต์, SEO Digital Marketing…</p>
<p><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/a229d5bc123a_495494caeb04-eb9b9b889233a1d28ea052e2ff7fb8db.png" width="1024" height="569" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="สิงคโปร์">สิงคโปร์<a href="https://kobkrit.com/en/blog/ai#%E0%B8%AA%E0%B8%B4%E0%B8%87%E0%B8%84%E0%B9%82%E0%B8%9B%E0%B8%A3%E0%B9%8C" class="hash-link" aria-label="Direct link to สิงคโปร์" title="Direct link to สิงคโปร์" translate="no">​</a></h3>
<p>165 บริษัท หรือประมาณ 15 เท่าของเมืองไทย ส่วนมากเป็นแนว B2B ทั้งนั้น</p>
<p><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/a229d5bc123a_d4a0663c7962-d54a25a33e34ce59e75eed1f62aced1d.png" width="1024" height="532" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="มาเลเซีย">มาเลเซีย<a href="https://kobkrit.com/en/blog/ai#%E0%B8%A1%E0%B8%B2%E0%B9%80%E0%B8%A5%E0%B9%80%E0%B8%8B%E0%B8%B5%E0%B8%A2" class="hash-link" aria-label="Direct link to มาเลเซีย" title="Direct link to มาเลเซีย" translate="no">​</a></h3>
<p>25 บริษัท ประมาณ 2 เท่ากว่าๆของเมืองไทย ส่วนมากเน้น Digital Marketing AI (B2B) 3 บริษัท และ อาหาร 2 บริษัท</p>
<p><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/a229d5bc123a_9c3a1b3e354e-ef475051c5ce90cfcfd2cea78722e17b.png" width="1024" height="488" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="พม่า">พม่า<a href="https://kobkrit.com/en/blog/ai#%E0%B8%9E%E0%B8%A1%E0%B9%88%E0%B8%B2" class="hash-link" aria-label="Direct link to พม่า" title="Direct link to พม่า" translate="no">​</a></h3>
<p>1 บริษัท ด้าน Digital Marketing ที่มี AI ช่วยด้วย</p>
<p><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/a229d5bc123a_82beaa547e61-8bd6ea5f7c4f3c447ffa71c1535d2248.png" width="1024" height="288" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="ลาว">ลาว<a href="https://kobkrit.com/en/blog/ai#%E0%B8%A5%E0%B8%B2%E0%B8%A7" class="hash-link" aria-label="Direct link to ลาว" title="Direct link to ลาว" translate="no">​</a></h3>
<p>0 บริษัท</p>
<p><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/a229d5bc123a_62a4196f431b-7aa0d32f15d44d0e582b3e843df09fce.png" width="1024" height="280" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="กัมพูชา">กัมพูชา<a href="https://kobkrit.com/en/blog/ai#%E0%B8%81%E0%B8%B1%E0%B8%A1%E0%B8%9E%E0%B8%B9%E0%B8%8A%E0%B8%B2" class="hash-link" aria-label="Direct link to กัมพูชา" title="Direct link to กัมพูชา" translate="no">​</a></h3>
<p>1 บริษัท เรื่อง Solution โรงแรมและร้านอาหาร B2B</p>
<p><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/a229d5bc123a_520a3d63596e-e63c97901212f69f516b4f55df9fee5b.png" width="1024" height="304" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="เวียดนาม">เวียดนาม<a href="https://kobkrit.com/en/blog/ai#%E0%B9%80%E0%B8%A7%E0%B8%B5%E0%B8%A2%E0%B8%94%E0%B8%99%E0%B8%B2%E0%B8%A1" class="hash-link" aria-label="Direct link to เวียดนาม" title="Direct link to เวียดนาม" translate="no">​</a></h3>
<p>8 บริษัท เป็นแนว Software Development AI และ Real Estate</p>
<p><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/a229d5bc123a_6a78a78ef4a4-758b50cd6f553f0c043f2e15c1a4e7f8.png" width="1024" height="431" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="อินโดนิเซีย">อินโดนิเซีย<a href="https://kobkrit.com/en/blog/ai#%E0%B8%AD%E0%B8%B4%E0%B8%99%E0%B9%82%E0%B8%94%E0%B8%99%E0%B8%B4%E0%B9%80%E0%B8%8B%E0%B8%B5%E0%B8%A2" class="hash-link" aria-label="Direct link to อินโดนิเซีย" title="Direct link to อินโดนิเซีย" translate="no">​</a></h3>
<p>20 บริษัท เป็นแนว Chatbot และ Pure AI ( Image, Neuro Science) ดูเป็น Deep Tech มากๆ</p>
<p><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/a229d5bc123a_8e2ecc6a1f53-213c774571dea2af8ddcbff52020f85a.png" width="1024" height="534" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="ฟิลิปปินส์">ฟิลิปปินส์<a href="https://kobkrit.com/en/blog/ai#%E0%B8%9F%E0%B8%B4%E0%B8%A5%E0%B8%B4%E0%B8%9B%E0%B8%9B%E0%B8%B4%E0%B8%99%E0%B8%AA%E0%B9%8C" class="hash-link" aria-label="Direct link to ฟิลิปปินส์" title="Direct link to ฟิลิปปินส์" translate="no">​</a></h3>
<p>8 บริษัท Chatbot, NLP, Digital Maketing, AI Consulting</p>
<p><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/a229d5bc123a_69b6032aeec1-539d5eb76728bcf702bc358d4832de23.png" width="1024" height="536" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="บรูไน">บรูไน<a href="https://kobkrit.com/en/blog/ai#%E0%B8%9A%E0%B8%A3%E0%B8%B9%E0%B9%84%E0%B8%99" class="hash-link" aria-label="Direct link to บรูไน" title="Direct link to บรูไน" translate="no">​</a></h3>
<p>0 บริษัท</p>
<p><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/a229d5bc123a_29453cc183b4-81438e611147c6f370c3786fa282c3d6.png" width="1024" height="280" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="ติมอร์">ติมอร์<a href="https://kobkrit.com/en/blog/ai#%E0%B8%95%E0%B8%B4%E0%B8%A1%E0%B8%AD%E0%B8%A3%E0%B9%8C" class="hash-link" aria-label="Direct link to ติมอร์" title="Direct link to ติมอร์" translate="no">​</a></h3>
<p>0 บริษัท</p>
<p><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/a229d5bc123a_ed0b341810a6-1e2f5077cf116d39dcb49a29ef68400b.png" width="1024" height="291" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="สรุป">สรุป<a href="https://kobkrit.com/en/blog/ai#%E0%B8%AA%E0%B8%A3%E0%B8%B8%E0%B8%9B" class="hash-link" aria-label="Direct link to สรุป" title="Direct link to สรุป" translate="no">​</a></h3>
<p><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/a229d5bc123a_32ff1c6fb0c4-2ef3a3c0cb368f3c054715f912907a60.png" width="496" height="453" class="img_ev3q"></p>
<p>ประเทศไทยอันดับที่ 4 ผมว่าจริงๆ ผมสัมผัสได้ว่าในไทยน่าจะมีมากกว่านี้ อยู่ที่ประมาณ 20–30 บริษัท แต่ละบริษัทเจ๋งๆทั้งนั้น แต่ว่าเค้าไม่ได้เข้ามาใส่ข้อมูลเลยทำให้ข้อมูลมันดูน้อยๆไปหน่อย เลยจะอยากจะเชิญชวนบริษัท AI ในไทยที่ยังไม่ได้มาใส่ข้อมูล มาใส่ใน Crunchbase นะครับ จะได้ช่วยเป็นส่วนนึงให้ประเทศไทยจะได้ Rank Government AI Readiness 2019 สูงกว่านี้อีกนิด ในปีถัดไปครับ</p>
<p>ขอบคุณครับ</p>
<p>เขียนเมื่อ 28 July 2019</p>
<p>หากใครสนใจอยากจะพัฒนาหรือต้องการที่ปรึกษาด้าน AI สามารถเข้าไปดูผลงานของบริษัทเรา iApp Technology ได้ที่ <a href="https://iapp.co.th/" target="_blank" rel="noopener noreferrer" class="">https://iapp.co.th</a> และ <a href="https://ai.iapp.co.th/" target="_blank" rel="noopener noreferrer" class="">https://ai.iapp.co.th</a> หรือติดต่อได้ที่ <a href="mailto:kobkrit@iapp.co.th" target="_blank" rel="noopener noreferrer" class="">kobkrit@iapp.co.th</a> ได้เลยนะครับ #Ai #iApp</p>
<p><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/588fdf26e410_cec39426438f-a84af37f4cec9ad0496e13663fd061ec.png" width="1024" height="190" class="img_ev3q"><a href="https://ai.iapp.co.th/" target="_blank" rel="noopener noreferrer" class="">https://ai.iapp.co.th</a></p>
<p>ดูเพิ่มเติมได้ที่ <a href="https://iapp.co.th/" target="_blank" rel="noopener noreferrer" class="">https://iapp.co.th</a> และ <a href="https://ai.iapp.co.th/" target="_blank" rel="noopener noreferrer" class="">https://ai.iapp.co.th</a> :D</p>
<hr>
<p><a href="https://kobkrit.com/%E0%B8%88%E0%B8%B3%E0%B8%99%E0%B8%A7%E0%B8%99%E0%B8%9A%E0%B8%A3%E0%B8%B4%E0%B8%A9%E0%B8%B1%E0%B8%97%E0%B8%94%E0%B9%89%E0%B8%B2%E0%B8%99-ai-%E0%B8%82%E0%B8%AD%E0%B8%87%E0%B9%84%E0%B8%97%E0%B8%A2-%E0%B9%80%E0%B8%A1%E0%B8%B7%E0%B9%88%E0%B8%AD%E0%B9%80%E0%B8%97%E0%B8%B5%E0%B8%A2%E0%B8%9A%E0%B8%81%E0%B8%B1%E0%B8%9A%E0%B9%80%E0%B8%9E%E0%B8%B7%E0%B9%88%E0%B8%AD%E0%B8%99%E0%B8%9A%E0%B9%89%E0%B8%B2%E0%B8%99-a229d5bc123a" target="_blank" rel="noopener noreferrer" class="">จำนวนบริษัทด้าน AI ของไทย เมื่อเทียบกับเพื่อนบ้าน</a> was originally published in <a href="https://kobkrit.com/" target="_blank" rel="noopener noreferrer" class="">Kobkrit</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content>
        <author>
            <name>Kobkrit Viriyayudhakorn</name>
            <uri>https://github.com/kobkrit</uri>
        </author>
        <category label="ai" term="ai"/>
        <category label="thai" term="thai"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Jupyter Lab Terminal non-UTF8 Fix Encoding]]></title>
        <id>https://kobkrit.com/en/blog/jupyter-lab-terminal-non-utf8fix-encoding</id>
        <link href="https://kobkrit.com/en/blog/jupyter-lab-terminal-non-utf8fix-encoding"/>
        <updated>2019-01-15T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Jupyter Lab Terminal non-UTF8 Fix Encoding]]></summary>
        <content type="html"><![CDATA[<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="jupyter-lab-terminal-non-utf8-fix-encoding">Jupyter Lab Terminal non-UTF8 Fix Encoding<a href="https://kobkrit.com/en/blog/jupyter-lab-terminal-non-utf8fix-encoding#jupyter-lab-terminal-non-utf8-fix-encoding" class="hash-link" aria-label="Direct link to Jupyter Lab Terminal non-UTF8 Fix Encoding" title="Direct link to Jupyter Lab Terminal non-UTF8 Fix Encoding" translate="no">​</a></h3>
<p><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/b8ab7905e573_eceaf84a55d1-0514514908d7fdc0e941b6152644fad8.png" width="1024" height="346" class="img_ev3q"></p>
<p>We have non-ASIIC filename (Thai UTF-8 filename), which is displayed incorrectly in the Jupyter lab terminal by default. It is display as ??????????.txt.</p>
<p>To solve with this, just type</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">export LANG=C.UTF-8</span><br></span></code></pre></div></div>
<p>into the terminal.</p>
<p>The problem is resolved immediately.</p>
<p>For those whoever want to develop and get consult on creating your own AI model, please getting more information at our company website “iApp Technology” (<a href="https://iapp.co.th/" target="_blank" rel="noopener noreferrer" class="">https://iapp.co.th</a>) and testing our AI demoes (<a href="https://ai.iapp.co.th/" target="_blank" rel="noopener noreferrer" class="">https://ai.iapp.co.th</a>). You can contact me directly at <a href="mailto:kobkrit@iapp.co.th" target="_blank" rel="noopener noreferrer" class="">kobkrit@iapp.co.th</a>. Thank you very much. #iApp #Ai</p>
<p><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/573168195011_5e2e2b4c1014-c396d6c26d1586d2f663f5c587bf3dd1.png" width="1024" height="186" class="img_ev3q"></p>
<p>See more at <a href="https://iapp.co.th/" target="_blank" rel="noopener noreferrer" class="">https://iapp.co.th</a> and <a href="https://ai.iapp.co.th/" target="_blank" rel="noopener noreferrer" class="">https://ai.iapp.co.th</a></p>
<hr>
<p><a href="https://kobkrit.com/jupyter-lab-terminal-non-utf8fix-encoding-b8ab7905e573" target="_blank" rel="noopener noreferrer" class="">Jupyter Lab Terminal non-UTF8Fix Encoding</a> was originally published in <a href="https://kobkrit.com/" target="_blank" rel="noopener noreferrer" class="">Kobkrit</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content>
        <author>
            <name>Kobkrit Viriyayudhakorn</name>
            <uri>https://github.com/kobkrit</uri>
        </author>
        <category label="ai" term="ai"/>
        <category label="thai" term="thai"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[How to directly download files from Dropbox, or Google drive using wget in Terminal or in Google Colaboratory.]]></title>
        <id>https://kobkrit.com/en/blog/how-to-directly-download-files-from-dropbox-or-google-drive</id>
        <link href="https://kobkrit.com/en/blog/how-to-directly-download-files-from-dropbox-or-google-drive"/>
        <updated>2018-12-17T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[How to directly download files from Dropbox, or Google drive using wget in Terminal or in Google Colaboratory.]]></summary>
        <content type="html"><![CDATA[<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-to-directly-download-files-from-dropbox-or-google-drive-using-wget-in-terminal-or-in-google-colaboratory">How to directly download files from Dropbox, or Google drive using wget in Terminal or in Google Colaboratory.<a href="https://kobkrit.com/en/blog/how-to-directly-download-files-from-dropbox-or-google-drive#how-to-directly-download-files-from-dropbox-or-google-drive-using-wget-in-terminal-or-in-google-colaboratory" class="hash-link" aria-label="Direct link to How to directly download files from Dropbox, or Google drive using wget in Terminal or in Google Colaboratory." title="Direct link to How to directly download files from Dropbox, or Google drive using wget in Terminal or in Google Colaboratory." translate="no">​</a></h3>
<p><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/573168195011_427e5823a699-c072a4af65e02f9d050e870f036c1826.png" width="1024" height="680" class="img_ev3q"></p>
<p>Google Colaboratory is a great tool for data science and machine learning practitioners nowsday. Since a Google Colaboratory is a GPU-enable remote compute instance running on Google Cloud. It does not locally run on our machine. It is quite difficult to upload the dataset or any CSV files into the remote instance.</p>
<p>The easiest way to do is, we upload our files to the Public folder in the Dropbox. We copy the public link and download it using command as follow.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-dropbox"><strong># Dropbox</strong><a href="https://kobkrit.com/en/blog/how-to-directly-download-files-from-dropbox-or-google-drive#-dropbox" class="hash-link" aria-label="Direct link to -dropbox" title="Direct link to -dropbox" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># Dropbox</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">## Google Colaboratory</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">!wget -O news.csv &lt;https://www.dropbox.com/s/XXXXXXX/news.csv?dl=0&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">## Terminal, Command Line</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ wget -O news.csv &lt;https://www.dropbox.com/s/XXXXXXX/news.csv?dl=0&gt;</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-google-drive"># Google Drive<a href="https://kobkrit.com/en/blog/how-to-directly-download-files-from-dropbox-or-google-drive#-google-drive" class="hash-link" aria-label="Direct link to # Google Drive" title="Direct link to # Google Drive" translate="no">​</a></h3>
<p>Unfortunately, in Google drive is not easy like in the Dropbox, the Google drive does not provide direct public link that allow us to fetch the file directly. When you turn on the Link Sharing, They usually provide us the virtual path like this.</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[https://drive.google.com/open?id=XXXXXXXXXXXXX](&lt;https://drive.google.com/open?id=1opkctEFmJ8E08PRzaiqNrEyUZcbXegsJ&gt;)XXXXXXXXXXX</span><br></span></code></pre></div></div>
<p>Since our team using Google drive as the primary source of file sharing, we need to think the solution for it.</p>
<p>Luckily there is a tool called <strong>Gdown</strong> (<a href="https://github.com/circulosmeos/gdown.pl" target="_blank" rel="noopener noreferrer" class="">https://github.com/circulosmeos/gdown.pl</a>). You can install via <strong>pip</strong>. It can directly download file from the Google drive virtual path for us, we can use in the command as follows.</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># Google Drive</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">## Google Colabratory</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">!gdown --id XXXXXXXXXXXXXXXXX</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">## Terminal, Command Line</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ pip install gdown</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ gdown --id XXXXXXXXXXXXXXXXX</span><br></span></code></pre></div></div>
<p><strong>Note that</strong> you need to extract the “XXXXXXXXXXXX” part from the virtual link provided from Google drive by yourself.</p>
<p>Does this blog article helpful?? If yes, please help us <strong>press a Clap hand button</strong> and <strong>press a purple Follow button</strong> for getting helpful tips on Artificial Intelligence, Data Science, Machine Learning and Computer Science from <a href="https://ai.iapp.co.th/" target="_blank" rel="noopener noreferrer" class=""><strong>iApp Technology</strong></a>and <a href="http://kobkrit.com/" target="_blank" rel="noopener noreferrer" class=""><strong>kobkrit.com</strong></a></p>
<p>For those whoever want to develop and get consult on creating your own AI model, please getting more information at our company website “iApp Technology” (<a href="https://iapp.co.th/" target="_blank" rel="noopener noreferrer" class="">https://iapp.co.th</a>) and testing our AI demoes (<a href="https://ai.iapp.co.th/" target="_blank" rel="noopener noreferrer" class="">https://ai.iapp.co.th</a>). You can contact me directly at <a href="mailto:kobkrit@iapp.co.th" target="_blank" rel="noopener noreferrer" class="">kobkrit@iapp.co.th</a>. Thank you very much. #iApp #Ai</p>
<p><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/573168195011_5e2e2b4c1014-c396d6c26d1586d2f663f5c587bf3dd1.png" width="1024" height="186" class="img_ev3q"></p>
<p>See more at <a href="https://iapp.co.th/" target="_blank" rel="noopener noreferrer" class="">https://iapp.co.th</a> and <a href="https://ai.iapp.co.th/" target="_blank" rel="noopener noreferrer" class="">https://ai.iapp.co.th</a></p>
<hr>
<p><a href="https://kobkrit.com/how-to-directly-download-files-from-dropbox-or-google-drive-using-wget-in-terminal-or-in-google-573168195011" target="_blank" rel="noopener noreferrer" class="">How to directly download files from Dropbox, or Google drive using wget in Terminal or in Google…</a> was originally published in <a href="https://kobkrit.com/" target="_blank" rel="noopener noreferrer" class="">Kobkrit</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content>
        <author>
            <name>Kobkrit Viriyayudhakorn</name>
            <uri>https://github.com/kobkrit</uri>
        </author>
        <category label="cloud" term="cloud"/>
        <category label="machine-learning" term="machine-learning"/>
        <category label="tutorial" term="tutorial"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Using allow_growth memory option in Tensorflow and Keras]]></title>
        <id>https://kobkrit.com/en/blog/using-allow-growth-memory-option-in-tensorflow-and-keras</id>
        <link href="https://kobkrit.com/en/blog/using-allow-growth-memory-option-in-tensorflow-and-keras"/>
        <updated>2018-10-14T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[1]]></summary>
        <content type="html"><![CDATA[<p>1</p>
<p>We faced a problem when we have a GPU computer that shared with multiple users. Most users run their GPU process without the “allow_growth” option in their Tensorflow or Keras environments. It causes the memory of a graphics card will be fully allocated to that process. In reality, it is might need only the fraction of memory for operating. It prevents any new GPU process which consumes a GPU memory to be run on the same machine.</p>
<p>Example of three processes which can shared in two graphic cards enabled by “allow_growth” option.</p>
<p>To cove with this, They just enable the “allow_growth” setting in Tensorflow or Keras. The following code for setting allow_growth memory option in Tensorflow.</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># Tensorflow  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">import tensorflow as tf  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">config = tf.ConfigProto()  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">config.gpu_options.allow_growth = True  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">session = tf.Session(config=config, ...)</span><br></span></code></pre></div></div>
<p>And for Keras</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">#For Kerasfrom keras.callbacks import ModelCheckpoint  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">from keras.models import Model, load_model, save_model, Sequential  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">from keras.layers import Dense, Activation, Dropout, Input, Masking, TimeDistributed, LSTM, Conv1D  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">from keras.layers import GRU, Bidirectional, BatchNormalization, Reshape  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">from keras.optimizers import Adamfrom keras.backend.tensorflow_backend import set_session  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">import tensorflow as tf  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">config = tf.ConfigProto()  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">config.gpu_options.allow_growth = True  # dynamically grow the memory used on the GPU  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">config.log_device_placement = True  # to log device placement (on which device the operation ran)sess = tf.Session(config=config)set_session(sess)  # set this TensorFlow session as the default session for Keras</span><br></span></code></pre></div></div>
<p>This increase the graphics cards utilization, not limited the number of process to the amount of card that host machine have. :)</p>
<p>For those whoever want to develop and get consult on creating your own AI model, please getting more information at our company website “iApp Technology” (<a href="https://iapp.co.th/" target="_blank" rel="noopener noreferrer" class="">https://iapp.co.th</a>) and testing our AI demoes (<a href="https://ai.iapp.co.th/" target="_blank" rel="noopener noreferrer" class="">https://ai.iapp.co.th</a>). You can contact me directly at <a href="mailto:kobkrit@iapp.co.th" target="_blank" rel="noopener noreferrer" class="">kobkrit@iapp.co.th</a>. Thank you very much. #iApp #Ai</p>
<p>See more at <a href="https://iapp.co.th/" target="_blank" rel="noopener noreferrer" class="">https://iapp.co.th</a> and <a href="https://ai.iapp.co.th/" target="_blank" rel="noopener noreferrer" class="">https://ai.iapp.co.th</a></p>]]></content>
        <author>
            <name>Kobkrit Viriyayudhakorn</name>
            <uri>https://github.com/kobkrit</uri>
        </author>
        <category label="cloud" term="cloud"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Shrink Disk in Google Cloud Platform on Ubuntu With The Smallest Effort Possible]]></title>
        <id>https://kobkrit.com/en/blog/shrink-disk-in-google-cloud-platform-on-ubuntu-with-the-smal</id>
        <link href="https://kobkrit.com/en/blog/shrink-disk-in-google-cloud-platform-on-ubuntu-with-the-smal"/>
        <updated>2018-10-01T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Like everyone else, when you creating a disk for an instance, we usually allocate the size of disk much much higher than we actually need. We have a very pessimistic view on a disk space we need, and finally, we end up wasting money on unnecessary matters.]]></summary>
        <content type="html"><![CDATA[<p>Like everyone else, when you creating a disk for an instance, we usually allocate the size of disk much much higher than we actually need. We have a very pessimistic view on a disk space we need, and finally, we end up wasting money on unnecessary matters.</p>
<p>I created an instance on GCP, aimed for running several docker containers. I create an extra 500GB drive located in /dev/sdb (to be mounted on /var/lib/docker) attached to my instance, but actually, an only 60GB drive is needed. The following steps are for shrinking a disk for the unmountable partition.</p>
<ol>
<li class="">Make the snapshot of a disk for backup in GCP (Actually docker-1-var-lib-docker is originally 500GB)</li>
</ol>
<p><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/e25efe31f68d_ac3ffa7f410c-08d00d5c68c2b86d740586b9a6a9aef6.png" width="1024" height="297" class="img_ev3q"></p>
<p>2. Down your docker in Ubuntu, <code>$ sudo service docker stop</code></p>
<p>3. Umount the old disk, <code>$ sudo umount /dev/sdb</code></p>
<p>4. Resize it, <code>$ sudo resize2fs /dev/sdb 60G</code> You need to wait a while.</p>
<p>5. Edit its partition table, <code>$ sudo cfdisk /dev/sdb</code> will give you a text-based gui to inspect your partition table. I would recommend you to print the partition table to a file or screen at that point, and take note of the current configuration as backup. You can then select /dev/sdb and delete the partition. In its place, free space will be displayed. Use new to create a new partition with 60 GB in its place, and set the type to ext4. Then, move to the trailing free space and create the 440GB swap partition with type swap.</p>
<p><img decoding="async" loading="lazy" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyEAAAErCAMAAAD+NHyOAAABZVBMVEX9WErUST7GxsadNi7XSz/bS0CnOjHlT0P2VUi/QjiPMSrJRjvLy8uZNS309PSqOzLrUkX5Vknc3NzMRzzTST71VUj8V0r/WUsbCQfm5uY9FREGAgFOGxYXCAYlDAo2Eg9wJyCBLSVRHBcaCQcoDQtiIhwrDwxpJB7w8PACAADk5ORDFxMMBANKGRUTBgWVNCucNi1lIx08FBFGGBQPBQS+QjcWBwbFRDkgCwmYNSzWSj6fNy6mOTAxEQ5vJiDu7u44ExDnUEMBAACwPTMIAgLi4uLxVEZJGRWKMCgZCAfIRTqRMipaHxoqDgzz8/PqUUQEAQGzPjQLAwNFGBT7V0nERDlTHBhdIBtkIh0tDw23t7fmUEMAAACvPTMHAgK9QTf3VkiGLidPGxf+WEpZHxopDgzfTUFqJR+oOjHx8fE6FBHpUUSyPjTwU0bzVUf6V0lSHBgiCwrbTEDiTkJ7KiS1PzXzVEcMRWU2AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAtGVYSWZJSSoACAAAAAYAEgEDAAEAAAABAAAAGgEFAAEAAABWAAAAGwEFAAEAAABeAAAAKAEDAAEAAAACAAAAEwIDAAEAAAABAAAAaYcEAAEAAABmAAAAAAAAAEgAAAABAAAASAAAAAEAAAAGAACQBwAEAAAAMDIxMAGRBwAEAAAAAQIDAACgBwAEAAAAMDEwMAGgAwABAAAA//8AAAKgBAABAAAAIQMAAAOgBAABAAAAKwEAAAAAAACs0n5SAAAPZElEQVR4nO2dCbvkRBVAB3cdjeKIzx13RBzFXVwAR3BDAXEDjSiOouOOor/fbzqd5N66VTeVdHW6U33OfPNevaS2JHWSqqzXWgBIc82ZBwAYAuCBIQAeGALggSEAHhgC4IEhi2iapumD4SwvshNrosCMhJGSF5QEGgxZhNP0jtNOMeRUYMgids2umdFOD2yoY+p0PpE5GHI4GLKIodGL3tY+aFvlaEizjz+k6n5184ZpiaYu+2uirHGmjtKH+jJhGRiyiET73NsRNEhhyDDTGDKKFWnPIrayUOYlZVNxOJAcBIYsIrUH7/bZychhqw7aeqodRyRMZajHK5M5wyQYUsiQYdefOG8VNFRjyA6vNN21M106Uw01G0MWgyGlDOkb50JDpgqzRw31l0gvhigYcjgYUs6QvkX6vSwxrQtMNmHHkKE0ld6MVzBkORiyCNFO80bq5ngzBuKHIp18zMeYpifEB+kIshwMOfia+ngCNraTV5GlBurIIGMlDdkl0oXFunrihHA/aDlwaS8aDAHwwBAADwwB8MAQAA8MAfDAEAAPDAHwwBAADwyJXc+blSq4UK7uQQwuJobPbkTi2Kc5Dr3eF61qMvf+qRIuNO65eEOWNQN5q9XY1M1NJcFNhOaPSJzIJfnDah2talC6zZi7uXow5LCk4i4s6YqaYgzRNzuqOIn7EA+tdfyGMSuNvb/r4sGQISB7QGGPJ9rjMLcuBrda9VM8Q8I4+96YLkY+cxsoFN4V5i+l6VIlj1cY0nPxhsTutw17PX4PftIQM2vX5oNZ+0D3XxuiOl/DIMH2y9INOqiYnI4hE2DI2Pr1rn2qlYQj7dhBRY9HxhcwxEYdw8Sw2dpxii7JjlwmqqqmpwxhpL4HQ2J9kLHnMvVkbMwm0YjNMUS3chtnf8e6KUmPU0arxtNkk4sX18FOGl/90KTzvBwwJNa5V7+cFGOwa6vyvSLjFN3aVL/KxAmfkApVs4epIdq0y6EOY51NWRjSgyHWkFSv3SQIw4Fo8d6aOWEVOUYkDQmqlugiZVQ1MQlDDBjSRjv3YSjSg4/Eis9Trc0OHew4I9LzCcqK/h4OXpNVtVduwsIwpOfiDRH9DHHlOzyHagwZulSyo5IwJH5Nvc2+pm6uv4tYNmtjSKSqaUO4ph5y8YZsirw2S8suCYZsCQxZHwzZDLndHgQpCoYAeGAIgAeGAHhgCIAHhgBUYMjUJd6p8zfywpq4fJaZ6vjYO6RikSYykL+niwv+dgtpprNMlWCfAd7YpcjLMCRxq0dOoQtLnM90hrnLP5nT5O1Z8+ZmrXD1kvstKXIZhrSVG5K+Eys3n8KG7FAPuph7/bfC9gyx7wuJ3fZU2JBIoeOsPnaqoyO+RjumTr7SZJwVLmCTdXfiDENskxW3mEXXc5tY0tQKt8e28L6ws2dzhjTJgLIolUXmc90iiSmrTy4fCtyXaeKNtQnqnPhirpxlC43enNvNMW8gyl2scKcum3xjqqEqn7HCbcX2U9vNsBVD1ONJTiCdgw1lKCKaT1DEsK+NimqL1k0qElG2o/hypRuWfNPDtPl6VSQMMXVuMpbULrVUXOxRMKQ045ZJq5L5JOosRcYjj3x8UKTV7aab1ExWPlZyeAiIGJKsr27lzRxD5HLElqtxKj+9wlXF9omn5TorNmdIfIrsGyTTmz9ytpMtdMqQ+ChBt4ujGjK1XGop8g1pY+knVnjEkNQe4lypy5DUqo+02HTkww2J/VHCkGSFlxtijjvzDGknVnjsGJJRw3Nia4aodT+e4QlU8R6anSdIUkBRD73R47pMt+GYIU3mSD3psbNM8VKjhjTxlZCzwmXFxnWwIUG2Z4g8t9uaQL99ww0mL6WrXNxCI2+itg/S6kdwY1mrJ2SzxyH2y7eibdmxzpxzdJG3eZtHkccfTfgB3siSJle4qtiwDdoNsRFDNs0ZN4gzrtq5gCEX3QzPuGrnAoZcbjPcWHfnRGAIgAeGAHhgCIAHhgB4YAiAB4YAeGAIgAeGAHhgCIAHhgB4YAiAB4YAeGAIgAeGAHhgCIAHhgB4YAiAB4YAeGAIgAeGAHhgCIAHhgB4YAiAB4bAXJpFL9pa77Op2fgvCo8aknqn7Mqc5BWvOR/HVXFL1E2937Y5ShHlaUwgOUFvwWMvjX0dsR9d/5SvaU4bch4v5l5xpQYlZe1VElMWF5rK7vQb4nBD1tyYM7OXL+VWn8bTWWGIKglDLsaQpnUMEXmFvSzx4cou1d134w9T2nWIrVTny7cFCw0/A2C+fBtWbEgV+eBtbqHxAkoVUZ7GBMbvN9gansAQ9VGLVOs91JD9waTzQ05Zg8hKHUrvA8WHTGOG9qOvkSGBab6mhtmFBqHCRaxhSOPUcMUOQXxtxteYqHv3L2hRzYQhfezxy6XFW6SDVF/U97gNRC9kejOL6DKOrWFWoXInd5wiytOEgcTWWd2QyEfnzDZ1DFG7waQh8lul2pAVT63YlRpZ9aUro6UMVqlrSKDUrJNiyT+KFXEaQ3bo2KtILduLCIn6HHoMSRvSrkeWIaUd0esm25D++5vLDm46cnOMIk53DGlPYEh44DLGmLqoVp5jiO5LnrUhZSu12JDgyHMMQw4p4qINadIl6yN0/kg9+MBszK5TGZL88m3hQoci8gwR28R0K/ILjaUsU8RKhjTD1tFxTmpIzgYcEmQbsv/RncIShqx424BcqeJknazYkc72BmXZesSi9xGGtbtgHGL6ycWKKE8zBPpq9GssVkNR0yNXWaxDcUUg+ZFgZVEwUm82fl/WiXsZGrO72mQRc2hWT3gcmqw5WzPkDLrhARdoSHO5dy5ugPNaz9Hj8uaKAIfNGQKwKhgC4IEhAB4YAuCBIQAeGALggSEAHhgC4IEhAB4YAuBxbXePe38PJAECBBoV4BgC4IEhAB4YAuCBIQAeGALggSEAHhgC4IEhAB4YAuBxbX8BEQBiYAiAB4YAeGAIgAeGGHbjs3BaGG4LlpWd15y4F00r3tk7TuznjHfvplLJWRhimDKgqCFHi3zJtOLH2OYDV9QUnUrNwpC5BuzfEV6wrKNEvnja7v94vAiPD2LKOLnfuhiSuwfq35sbzBZrUDxxM78PJPdr4v3+Q4bxyGPcsVAZmFuLKmnDX0HfSkwR0/sfGOIg1tSwquwRWvw9RjnEkD6jIR+boXr6zUZeXo0KaZu5hqhNiSEOYxvTIRVQv207zS5rGDj2ebgZ9ocJE0fXD0OaYIOJdSR+hetJrF4MyTsRMk47liEmNDgTP83VThky6+RYtbQ6JDdOsJPRiSJbEkMMES+MGEc0xM4yKaaOIRdPGwT7e6yEJ+MUHdPMwhADhmydNhoO+stmZaktyDFkmSHxbtfYTgsYMtXY7XhFu4kizVxDwtGdjoYheYYE7VBceJfnZksYEjmBG5YpLxn3hSZTXSCtHsvZAUgfr48+JgtmYUhhVmqal27AqmBIIdRoYIWyYCUwpBQrdm4wZEUwBMCD59QBPDAEwANDADwwBMADQwA8MATAA0MAPDAEwANDADwwBMADQwA8MATAA0MAPDAEwANDADwwBMADQwA8MATAA0MAPDAEwANDADwwBMADQwA8MATAA0MAPDAEwANDADwwBMADQwA8MATAA0MAPDAEwANDADwwBMADQwA8MATAA0MAPDAEwANDADwwBMADQwA8MATAA0MAPDAEwANDADwwBMADQwA8MATAA0MAPDAEwANDADwwBMADQwA8MATAA0MAPDAEwANDADwwBMADQwA8MATAA0MAPDAEwANDADwwBMADQwA8MATAA0MAPDAEwANDADwwBMADQwA8MATAA0MAPDAEwANDADwwBMADQwA8MATAA0MAPDAEwANDADwwBGANQ27e+N+Nm4XyAqjOkJtXd0ERqI5ChtzYGXKjTGYAtRly6587Q35XJDOA6gz5zE6QK4b9UB1lGvUHO0P+XiQzgOoMeaUz5OUimQFUZ8gLnSH3FMkMoDpDHu8M+WGRzACqM+ShzpDfFskMoDpDft8Z8rcimQFUZ8iPOkPeUCQzgOoM+XxnyMNFMgOozpAPd4Y8VSQzgOoMebUz5KUimQFUZ8hLnSGvFskMoDpDnuoMebRIZgDVGfJwZ8jzRTIDqM6QP3eG/KFIZgDVGfKTzpAPFMkMoDpDvtwZ8lCRzACqM+R6Z8jjRTIDqM6QezpDXiiSGUB1hrzcGfJKkcwAqjPkmc6QrxTJDKA6Q+7rDHmuSGYAlRjyRB+41Qly+1aZOgHUYchz3/ykehnQfcOcBw+vGMD2e1mP/uwtXxIvA3pmP/mLj7xYpG4AGzfkxf9c/fUTt/TLgG599Mbt9xWrHsCmR+rPX11dXb9fvAzo2V9+/erqbcVqB7BtQ579990B+mu/1xly/YkH7l4Y+fWu5wVQBddKvMLhH50hr//27tcvStUNYPOGPPiuq5DvlKoaQAVXDPdvyhr57nsK1Qygimvq+9t6B/5Upl4AlRjyUy3IV7muDlVx+H1Zb5aC3PvjIrUCqMeQv0hDPlKkUgA13dv731GQ172/RJ0AqjLkW7d7QW5/oUSVACp7PuTtvSGPFMgMoDpDPv7uTpA3crsJVEeRZwy/0RnyrxJ5AdRnyNfuvSvIb0pkBVDjc+qfu3u7yceKZAVQoSE/eNPV1QNFcgKo0ZD2+1c/53YTqJFChnz2HX8skxFAlYa0HyqUD0CdhgBckiHNPJ5+71t/9Zon78xKs/qSApzMkOZTn37nk4/NS7KotgDbNKS589idpzEELsiQ7ucx2OccL/ZY5jg5n6RQ2L4hclefbOyOB64kKUOm6ytqNTdl/vQZTXsiJobUb8hkc1/AIYbMmpwTZZFuubExZPNgSHNgq8aQuplhiOrjqL/74Bgws9TIpoQhIpM+98yUU9PFhH5ZImWNU5YUChUa0ppAH6lrLjJgZ4lUhQzpmu7+x3EM6XPWZalZGFI72SN1a8mowbxZZUbqWo6jGGKLsLMwpHayjyH9jFMbogKnM0TsPpYUChUaIofZGJJZaQzZPBiCIVBupD427yWGFD6XFQ6lc1NOTu+nRAbpEUUwpG5mXFMPT38OJ3WjhgSzRKrCI/Wu4IyU+dPF0oaHKbVO+sD8QqEaQxJMzJ5IdYAhy5lhyPELhYrvXFSDkllyDEeXpCHu/vgQnJxPUijUfPf71Py8VKsvKcAJnw9ZZAzA+cNz6gAeGALggSEAHhgC4IEhAB4YAuCBIQAeGALggSEAHhgC4IEhAK3D/wHbkmUmeGrlBwAAAABJRU5ErkJggg==" width="801" height="299" class="img_ev3q"></p>
<p>6. Create a new disk in GCP with the size of 60GB attached with the instance. It will be on /dev/sdc, you can view it by <code>$ lsblk</code> (The image is taken after the process is done.)</p>
<p><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/e25efe31f68d_07e282736e52-ecb8c82fb76985c3dc61f5d656923c9f.png" width="697" height="525" class="img_ev3q"></p>
<p>7. Finally clone the disk, <code>dd if=/dev/sdb of=/dev/sdc</code> (It will take a while)</p>
<p>8. Try to mount /dev/sdc on /var/lib/docker instead of the old disk <code>mount /dev/sdc /var/lib/docker</code></p>
<p>9. Start the docker service <code>$ sudo service docker start</code></p>
<p>10. Hooray!, Now everything works with the smaller disk need.</p>
<p><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/e25efe31f68d_44a52ac5b113-f7ea81e1f2b51cd95370da45cfc27199.png" width="767" height="653" class="img_ev3q"></p>
<p>11. Get rid of the old disk on GCP. We do not need to pay from them anymore.</p>
<p>In summary, we unmount a disk, shrink the disk, edit the partition table, and then using dd to clone disk from the old to the new. Finally, mount the new on the old’s mount point and finally, we can get rid of the old disk.</p>
<p>Hope this guide saves your time.</p>
<p>Thank you.</p>
<p>For those whoever want to develop and get consult on creating your own AI model, please getting more information at our company website “iApp Technology” (<a href="https://iapp.co.th/" target="_blank" rel="noopener noreferrer" class="">https://iapp.co.th</a>) and testing our AI demoes (<a href="https://ai.iapp.co.th/" target="_blank" rel="noopener noreferrer" class="">https://ai.iapp.co.th</a>). You can contact me directly at <a href="mailto:kobkrit@iapp.co.th" target="_blank" rel="noopener noreferrer" class="">kobkrit@iapp.co.th</a>. Thank you very much. #iApp #Ai</p>
<p><img decoding="async" loading="lazy" src="https://kobkrit.com/en/assets/images/573168195011_5e2e2b4c1014-c396d6c26d1586d2f663f5c587bf3dd1.png" width="1024" height="186" class="img_ev3q"></p>
<p>See more at <a href="https://iapp.co.th/" target="_blank" rel="noopener noreferrer" class="">https://iapp.co.th</a> and <a href="https://ai.iapp.co.th/" target="_blank" rel="noopener noreferrer" class="">https://ai.iapp.co.th</a></p>
<hr>
<p><a href="https://kobkrit.com/shrink-disk-in-google-cloud-platform-on-ubuntu-with-the-smallest-effort-possible-e25efe31f68d" target="_blank" rel="noopener noreferrer" class="">Shrink Disk in Google Cloud Platform on Ubuntu With The Smallest Effort Possible</a> was originally published in <a href="https://kobkrit.com/" target="_blank" rel="noopener noreferrer" class="">Kobkrit</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content>
        <author>
            <name>Kobkrit Viriyayudhakorn</name>
            <uri>https://github.com/kobkrit</uri>
        </author>
        <category label="ai" term="ai"/>
        <category label="cloud" term="cloud"/>
        <category label="docker" term="docker"/>
    </entry>
</feed>