<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Vitor Sousa</title><description>Writing on production ML systems: recommendation engines, contextual bandits, reinforcement learning for LLM alignment, and evaluation frameworks that keep deployed models honest.</description><link>https://www.vitorsousa.com/</link><image><url>https://www.vitorsousa.com/favicon.svg</url><title>Vitor Sousa Blog</title><link>https://www.vitorsousa.com/</link></image><item><title>A Transformer from Raw Tensors: What 112 Tests Taught Me</title><link>https://www.vitorsousa.com/blog/transformer-from-raw-tensors/</link><guid isPermaLink="true">https://www.vitorsousa.com/blog/transformer-from-raw-tensors/</guid><description>The model-foundations close-out of Learning with Verifiable Rewards: a decoder-only transformer built from raw tensors — attention with RoPE, RMSNorm, SwiGLU, an incremental KV-cache, four sampling modes — and the three properties that are easiest to get silently wrong. A case study in verification, not assembly.</description><pubDate>Fri, 24 Jul 2026 08:00:00 GMT</pubDate><content:encoded><![CDATA[<h1 id="after-gdpo-a-map-of-the-grpo-family">After GDPO: A Map of the GRPO Family</h1>
<p><strong>Part 5 of 5:</strong> The Map</p>
<blockquote>
<p><strong>TL;DR:</strong> DAPO, Dr. GRPO, GSPO, GDPO, CISPO, VAPO, GiGPO, GMPO, REINFORCE++… it looks like GRPO fragmented into a zoo. It didn’t. Every post-GRPO method keeps the same skeleton — drop the critic, compute a group-relative advantage — and varies along a <em>small set of design axes</em>: token vs. sequence granularity, clip width, length and standard-deviation normalization, KL on or off, how advantages are normalized, and how groups are sampled. Hold the axes and the zoo collapses into a handful of deliberate trade-offs. This post maps the four variants worth treating in depth, gives you a decision guide for when to reach for each, and equips you to reason about the <em>next</em> variant you meet — not just the ones listed here.</p>
</blockquote>
<p><strong>Reading time:</strong> ~18 minutes</p>
<p><strong>Prerequisites:</strong> This is the capstone of the series. It assumes you already know GRPO. If not, start with <a href="/blog/grpo/">Part 3: GRPO</a> — and ideally <a href="/blog/ppo-deep-dive/">Part 2: PPO</a> and <a href="/blog/gdpo/">Part 4: GDPO</a> — then come back. This post sits <em>on top</em> of those; it does not re-derive them.</p>
<hr>
<h2 id="the-zoo-is-real">The Zoo Is Real</h2>
<p>Open any RL-for-LLMs reading list from the last year and you will drown in acronyms. DAPO. Dr. GRPO. GSPO. GDPO. CISPO. GMPO. VAPO. GiGPO. REINFORCE++. There is even a <a href="https://github.com/hscspring/rl-llm-nlp">curated index</a> whose entire job is to keep up with the post-R1 GRPO family. The natural reaction is the one everyone has: <em>the field fragmented, and I now have ten algorithms to learn instead of one.</em></p>
<p>That reaction is wrong, and it is wrong in a useful way. These are not ten algorithms. They are one algorithm — <a href="/blog/grpo/">GRPO</a> — being paid down along a handful of engineering debts that GRPO left on the table. Once you can name those debts, every “new” method announces itself as <em>which debt it pays and how</em>. You stop memorizing the zoo and start reading coordinates on a map.</p>
<p>This post is that map. It is deliberately a synthesis, not a listicle: the value is in the axes and the judgement, not in summaries you could get from each paper’s abstract.</p>
<hr>
<h2 id="the-shared-skeleton-what-nobody-changes">The Shared Skeleton (What Nobody Changes)</h2>
<p>Start with what every member of the family keeps. Strip away the branding and they all make GRPO’s two core moves:</p>
<ol>
<li><strong>Drop the critic.</strong> No learned 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.22222em;">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.03588em;">ψ</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>. PPO’s four-model architecture loses a model, and with it roughly a third of the memory.</li>
<li><strong>Compute a group-relative advantage.</strong> For a prompt <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.03588em;">q</span></span></span></span>, sample a group of <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> completions <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">{</mo><msub><mi>o</mi><mn>1</mn></msub><mo separator="true">,</mo><mo>…</mo><mo separator="true">,</mo><msub><mi>o</mi><mi>G</mi></msub><mo stretchy="false">}</mo></mrow><annotation encoding="application/x-tex">\{o_1, \ldots, o_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="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.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">o</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 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> from the old policy, score them, and use the group statistics as the baseline:</li>
</ol>
<p><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><mrow><mi>i</mi><mo separator="true">,</mo><mi>t</mi></mrow></msub><mo>=</mo><msub><mover accent="true"><mi>r</mi><mo>~</mo></mover><mi>i</mi></msub><mo>=</mo><mfrac><mrow><msub><mi>r</mi><mi>i</mi></msub><mo>−</mo><mtext>mean</mtext><mo stretchy="false">(</mo><mi mathvariant="bold">r</mi><mo stretchy="false">)</mo></mrow><mrow><mtext>std</mtext><mo stretchy="false">(</mo><mi mathvariant="bold">r</mi><mo stretchy="false">)</mo></mrow></mfrac><mo separator="true">,</mo><mspace width="2em"></mspace><mi mathvariant="bold">r</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><mi mathvariant="normal">.</mi></mrow><annotation encoding="application/x-tex">\hat{A}_{i,t} = \tilde{r}_i = \frac{r_i - \text{mean}(\mathbf{r})}{\text{std}(\mathbf{r})}, \qquad \mathbf{r} = \{r_1, \ldots, r_G\}.</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.2329em;vertical-align:-0.2861em;"></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 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:0.8179em;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.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span></span><span style="top:-3.35em;"><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.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.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="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 text mtight"><span class="mord mtight">std</span></span><span class="mopen mtight">(</span><span class="mord mathbf mtight">r</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.02778em;">r</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.0278em;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 text mtight"><span class="mord mtight">mean</span></span><span class="mopen mtight">(</span><span class="mord mathbf mtight">r</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="mpunct">,</span><span class="mspace" style="margin-right:2em;"></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathbf">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="mopen">{</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">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.02778em;">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 class="mord">.</span></span></span></span></p>
<p>That is the whole skeleton. At heart it is REINFORCE with a baseline — the mean reward of the group <em>is</em> the baseline — which is exactly the lineage <a href="https://arxiv.org/abs/2402.14740">Ahmadian et al. (2024)</a> argued we should return to for RLHF. Everything is then plugged into a PPO-style clipped surrogate:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="script">L</mi><mrow><mi>i</mi><mo separator="true">,</mo><mi>t</mi></mrow></msub><mo>=</mo><mi>min</mi><mo>⁡</mo><mrow><mo fence="true">[</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><mrow><mi>i</mi><mo separator="true">,</mo><mi>t</mi></mrow></msub><mo separator="true">,</mo><mtext>  </mtext><mtext>clip</mtext><mo stretchy="false">(</mo><msub><mi>ρ</mi><mrow><mi>i</mi><mo separator="true">,</mo><mi>t</mi></mrow></msub><mo separator="true">,</mo><mn>1</mn><mo>−</mo><mi>ε</mi><mo separator="true">,</mo><mn>1</mn><mo>+</mo><mi>ε</mi><mo stretchy="false">)</mo><mtext> </mtext><msub><mover accent="true"><mi>A</mi><mo>^</mo></mover><mrow><mi>i</mi><mo separator="true">,</mo><mi>t</mi></mrow></msub><mo fence="true">]</mo></mrow><mo separator="true">,</mo><mspace width="2em"></mspace><msub><mi>ρ</mi><mrow><mi>i</mi><mo separator="true">,</mo><mi>t</mi></mrow></msub><mo>=</mo><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><mtext> </mtext><mi mathvariant="normal">∣</mi><mtext> </mtext><mi>q</mi><mo separator="true">,</mo><msub><mi>o</mi><mrow><mi>i</mi><mo separator="true">,</mo><mo>&#x3C;</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><mtext> </mtext><mi mathvariant="normal">∣</mi><mtext> </mtext><mi>q</mi><mo separator="true">,</mo><msub><mi>o</mi><mrow><mi>i</mi><mo separator="true">,</mo><mo>&#x3C;</mo><mi>t</mi></mrow></msub><mo stretchy="false">)</mo></mrow></mfrac><mi mathvariant="normal">.</mi></mrow><annotation encoding="application/x-tex">\mathcal{L}_{i,t} = \min\left[\rho_{i,t}\, \hat{A}_{i,t}, \; \text{clip}(\rho_{i,t}, 1-\varepsilon, 1+\varepsilon)\, \hat{A}_{i,t}\right], \qquad \rho_{i,t} = \frac{\pi_\theta(o_{i,t}\,|\,q, o_{i,&#x3C;t})}{\pi_{\theta_{\text{old}}}(o_{i,t}\,|\,q, o_{i,&#x3C;t})}.</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.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:1.8em;vertical-align:-0.65em;"></span><span class="mop">min</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="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 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.2778em;"></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.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="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="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 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 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"><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:1.658em;vertical-align:-0.6257em;"></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.0323em;"><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.03588em;">π</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 mtight"><span class="mord mathnormal mtight" style="margin-right:0.02778em;">θ</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.3448em;margin-left:-0.0278em;margin-right:0.1em;"><span class="pstrut" style="height:2.6944em;"></span><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">old</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.3496em;"><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.401em;"><span></span></span></span></span></span></span><span class="mopen 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 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.2819em;"><span></span></span></span></span></span></span><span class="mspace mtight" style="margin-right:0.1952em;"></span><span class="mord mtight">∣</span><span class="mspace mtight" style="margin-right:0.1952em;"></span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">q</span><span class="mpunct 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 mtight"><span class="mord mathnormal mtight">i</span><span class="mpunct mtight">,</span><span class="mrel mtight">&#x3C;</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.2819em;"><span></span></span></span></span></span></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.5073em;"><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.03588em;">π</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.02778em;">θ</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">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 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.2819em;"><span></span></span></span></span></span></span><span class="mspace mtight" style="margin-right:0.1952em;"></span><span class="mord mtight">∣</span><span class="mspace mtight" style="margin-right:0.1952em;"></span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">q</span><span class="mpunct 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 mtight"><span class="mord mathnormal mtight">i</span><span class="mpunct mtight">,</span><span class="mrel mtight">&#x3C;</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.2819em;"><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.6257em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mord">.</span></span></span></span></p>
<p>Every variant in this post inherits these two equations. What they argue about is the <em>fine print</em> — and the fine print turns out to be a short list.</p>
<hr>
<h2 id="the-axes-of-variation">The Axes of Variation</h2>
<p>This is the intellectual core of the post. Spend time here; the variants below are just coordinates in the space these axes define.</p>
<p><strong>Granularity.</strong> Is the importance 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">\rho</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">ρ</span></span></span></span> and its clipping computed per <em>token</em> or per <em>sequence</em>? GRPO is token-level. A single rare token can produce a wildly large or small ratio, and that noise compounds — badly in mixture-of-experts models, where the active experts can change between the sampling policy and the training policy. Sequence-level methods average the signal over the whole completion so the unit of optimization matches the unit of reward.</p>
<p><strong>Clip width.</strong> PPO/GRPO clip symmetrically, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">[</mo><mn>1</mn><mo>−</mo><mi>ε</mi><mo separator="true">,</mo><mn>1</mn><mo>+</mo><mi>ε</mi><mo stretchy="false">]</mo></mrow><annotation encoding="application/x-tex">[1-\varepsilon, 1+\varepsilon]</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">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="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">ε</span><span class="mclose">]</span></span></span></span>. But the clip ceiling quietly caps <em>upside</em>: it limits how much probability mass a good-but-currently-unlikely token can gain in one step. Those low-probability tokens are often the interesting ones — the “wait,” “let me reconsider,” fork-and-reflect tokens that long chain-of-thought reasoning depends on. Widening or de-symmetrizing the clip (“clip-higher”) controls the explore/exploit edge and is the main lever against <em>entropy collapse</em>, where the policy sharpens into a narrow, repetitive mode and stops learning.</p>
<p><strong>Length and std normalization.</strong> GRPO divides the per-response loss by response length and divides the advantage by the group’s standard deviation. Both are “harmless-looking” conveniences that bias the gradient. Length normalization makes the optimizer prefer longer wrong answers (more tokens over which to spread a negative advantage means a smaller per-token penalty); std normalization over-weights easy prompts where rewards barely vary. These biases are why responses balloon during training without getting better.</p>
<p><strong>KL term.</strong> Keep the KL penalty to the reference model, or drop it? KL anchors the policy and prevents drift, but it costs a reference model in memory and can hold the policy back when you <em>want</em> it to move far from the base model (as in R1-Zero-style training from a base checkpoint). Many recent recipes drop it entirely.</p>
<p><strong>Advantage normalization.</strong> Per-group versus global (batch-wide) normalization, and — crucially in multi-objective settings — single-reward versus multi-reward. When several rewards are summed and then normalized together, distinct reward combinations can collapse to identical advantages, blinding the policy to one objective. This is the failure <a href="/blog/gdpo/">Part 4</a> is built around.</p>
<p><strong>Sampling.</strong> Static groups versus dynamic sampling. A group where every completion is correct (or every one is wrong) has zero advantage variance and therefore contributes <em>no gradient</em> — it is wasted compute. Dynamically filtering or resampling degenerate groups keeps every batch informative.</p>
<p>Six axes. That is the entire design space the family lives in. Here is the same idea as a picture — each axis, and the variant that pushes hardest along it:</p>
<figure style="margin:2rem 0;">
<svg viewBox="0 0 760 500" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Six design axes, each shown as a track from GRPO&#x27;s default position to the variant that moves along it" style="width:100%;height:auto;display:block;background:var(--bg-secondary);border:1px solid var(--border);border-radius:10px;">
  <text x="30" y="36" font-family="inherit" font-size="17" font-weight="700" fill="var(--text-primary)">The six axes — and the variant that moves along each</text>
  <text x="30" y="58" font-family="inherit" font-size="12.5" fill="var(--text-muted)">Each track runs from GRPO's default (amber) to the variant that pushes hardest along it (green).</text>
  <!-- Row template -->
  <!-- 1 Granularity -->
</svg><p><text x="30" y="116" font-family="inherit" font-size="14.5" font-weight="600" fill="var(--text-primary)">Granularity</text>
<text x="250" y="98" font-family="inherit" font-size="11" fill="var(--text-muted)">token-level</text>
<text x="712" y="98" font-family="inherit" font-size="11" fill="var(--text-muted)" text-anchor="end">sequence-level</text>
<line x1="250" y1="112" x2="712" y2="112" stroke="var(--border)" stroke-width="3" stroke-linecap="round">
<circle cx="280" cy="112" r="7" fill="var(--accent)">
<text x="280" y="134" font-family="inherit" font-size="11" fill="var(--text-secondary)" text-anchor="middle">GRPO</text>
<circle cx="686" cy="112" r="7" fill="var(--link-color)">
<text x="686" y="134" font-family="inherit" font-size="11" fill="var(--text-secondary)" text-anchor="middle">GSPO</text></circle></circle></line></p>
  <!-- 2 Clip width -->
<p><text x="30" y="186" font-family="inherit" font-size="14.5" font-weight="600" fill="var(--text-primary)">Clip width</text>
<text x="250" y="168" font-family="inherit" font-size="11" fill="var(--text-muted)">tight, symmetric</text>
<text x="712" y="168" font-family="inherit" font-size="11" fill="var(--text-muted)" text-anchor="end">wide (clip-higher)</text>
<line x1="250" y1="182" x2="712" y2="182" stroke="var(--border)" stroke-width="3" stroke-linecap="round">
<circle cx="280" cy="182" r="7" fill="var(--accent)">
<text x="280" y="204" font-family="inherit" font-size="11" fill="var(--text-secondary)" text-anchor="middle">GRPO</text>
<circle cx="686" cy="182" r="7" fill="var(--link-color)">
<text x="686" y="204" font-family="inherit" font-size="11" fill="var(--text-secondary)" text-anchor="middle">DAPO</text></circle></circle></line></p>
  <!-- 3 Length / std norm -->
<p><text x="30" y="256" font-family="inherit" font-size="14.5" font-weight="600" fill="var(--text-primary)">Length + std norm</text>
<text x="250" y="238" font-family="inherit" font-size="11" fill="var(--text-muted)">on (biased)</text>
<text x="712" y="238" font-family="inherit" font-size="11" fill="var(--text-muted)" text-anchor="end">removed (unbiased)</text>
<line x1="250" y1="252" x2="712" y2="252" stroke="var(--border)" stroke-width="3" stroke-linecap="round">
<circle cx="280" cy="252" r="7" fill="var(--accent)">
<text x="280" y="274" font-family="inherit" font-size="11" fill="var(--text-secondary)" text-anchor="middle">GRPO</text>
<circle cx="686" cy="252" r="7" fill="var(--link-color)">
<text x="686" y="274" font-family="inherit" font-size="11" fill="var(--text-secondary)" text-anchor="middle">Dr. GRPO</text></circle></circle></line></p>
  <!-- 4 Advantage norm -->
<p><text x="30" y="326" font-family="inherit" font-size="14.5" font-weight="600" fill="var(--text-primary)">Advantage norm</text>
<text x="250" y="308" font-family="inherit" font-size="11" fill="var(--text-muted)">pooled rewards</text>
<text x="712" y="308" font-family="inherit" font-size="11" fill="var(--text-muted)" text-anchor="end">decoupled per reward</text>
<line x1="250" y1="322" x2="712" y2="322" stroke="var(--border)" stroke-width="3" stroke-linecap="round">
<circle cx="280" cy="322" r="7" fill="var(--accent)">
<text x="280" y="344" font-family="inherit" font-size="11" fill="var(--text-secondary)" text-anchor="middle">GRPO</text>
<circle cx="686" cy="322" r="7" fill="var(--link-color)">
<text x="686" y="344" font-family="inherit" font-size="11" fill="var(--text-secondary)" text-anchor="middle">GDPO</text></circle></circle></line></p>
  <!-- 5 KL term -->
<p><text x="30" y="396" font-family="inherit" font-size="14.5" font-weight="600" fill="var(--text-primary)">KL term</text>
<text x="250" y="378" font-family="inherit" font-size="11" fill="var(--text-muted)">kept</text>
<text x="712" y="378" font-family="inherit" font-size="11" fill="var(--text-muted)" text-anchor="end">dropped</text>
<line x1="250" y1="392" x2="712" y2="392" stroke="var(--border)" stroke-width="3" stroke-linecap="round">
<circle cx="280" cy="392" r="7" fill="var(--accent)">
<text x="280" y="414" font-family="inherit" font-size="11" fill="var(--text-secondary)" text-anchor="middle">GRPO</text>
<circle cx="686" cy="392" r="7" fill="var(--link-color)">
<text x="686" y="414" font-family="inherit" font-size="10" fill="var(--text-secondary)" text-anchor="middle">DAPO / Dr. GRPO</text></circle></circle></line></p>
  <!-- 6 Sampling -->
<p><text x="30" y="466" font-family="inherit" font-size="14.5" font-weight="600" fill="var(--text-primary)">Sampling</text>
<text x="250" y="448" font-family="inherit" font-size="11" fill="var(--text-muted)">static groups</text>
<text x="712" y="448" font-family="inherit" font-size="11" fill="var(--text-muted)" text-anchor="end">dynamic sampling</text>
<line x1="250" y1="462" x2="712" y2="462" stroke="var(--border)" stroke-width="3" stroke-linecap="round">
<circle cx="280" cy="462" r="7" fill="var(--accent)">
<text x="280" y="484" font-family="inherit" font-size="11" fill="var(--text-secondary)" text-anchor="middle">GRPO</text>
<circle cx="686" cy="462" r="7" fill="var(--link-color)">
<text x="686" y="484" font-family="inherit" font-size="11" fill="var(--text-secondary)" text-anchor="middle">DAPO</text>
</circle></circle></line></p>
<figcaption style="font-size:0.85rem;color:var(--text-muted);text-align:center;margin-top:0.5rem;">Note that DAPO appears on three axes — it is not one trick but a <em>bundle</em> of axis-moves shipped together.</figcaption>
</figure>
<p>Two patterns jump out of this picture. First, most variants move along <em>one</em> axis — they are surgical. Second, DAPO moves along <em>several at once</em>; it is less a single idea than a curated bundle. Keep both observations in mind as we walk the variants.</p>
<p>What do these axes look like in a training run? The two biases that motivate Dr. GRPO and DAPO show up as characteristic curve shapes:</p>
<figure style="margin:2rem 0;">
<svg viewBox="0 0 760 300" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Two schematic training curves: response length inflating under GRPO but flat under Dr. GRPO, and policy entropy collapsing under standard clipping but holding under clip-higher" style="width:100%;height:auto;display:block;background:var(--bg-secondary);border:1px solid var(--border);border-radius:10px;">
  <!-- Panel A: response length -->
  <text x="60" y="34" font-family="inherit" font-size="14" font-weight="600" fill="var(--text-primary)">Response length</text>
  <line x1="60" y1="60" x2="60" y2="230" stroke="var(--border)" stroke-width="1.5"></line>
  <line x1="60" y1="230" x2="330" y2="230" stroke="var(--border)" stroke-width="1.5"></line>
  <text x="195" y="256" font-family="inherit" font-size="11" fill="var(--text-muted)" text-anchor="middle">training steps →</text>
  <path d="M60 195 C150 175, 240 120, 330 78" fill="none" stroke="var(--accent)" stroke-width="3"></path>
  <path d="M60 196 C150 193, 240 194, 330 190" fill="none" stroke="var(--link-color)" stroke-width="3"></path>
  <text x="336" y="80" font-family="inherit" font-size="11" fill="var(--accent)">GRPO (inflates)</text>
  <text x="336" y="190" font-family="inherit" font-size="11" fill="var(--link-color)">Dr. GRPO (flat)</text>
  <!-- Panel B: entropy -->
</svg><p><text x="430" y="34" font-family="inherit" font-size="14" font-weight="600" fill="var(--text-primary)">Policy entropy</text>
<line x1="430" y1="60" x2="430" y2="230" stroke="var(--border)" stroke-width="1.5">
<line x1="430" y1="230" x2="700" y2="230" stroke="var(--border)" stroke-width="1.5">
<text x="565" y="256" font-family="inherit" font-size="11" fill="var(--text-muted)" text-anchor="middle">training steps →</text>
<path d="M430 72 C520 92, 560 200, 700 214" fill="none" stroke="var(--accent)" stroke-width="3">
<path d="M430 78 C540 96, 600 110, 700 124" fill="none" stroke="var(--link-color)" stroke-width="3">
<text x="500" y="208" font-family="inherit" font-size="11" fill="var(--accent)">symmetric clip (collapses)</text>
<text x="520" y="118" font-family="inherit" font-size="11" fill="var(--link-color)">clip-higher (holds)</text>
</path></path></line></line></p>
<figcaption style="font-size:0.85rem;color:var(--text-muted);text-align:center;margin-top:0.5rem;">Schematic, not from any single paper — redrawn to show the <em>shapes</em>. Length inflation and entropy collapse are the two failure curves the family was built to flatten.</figcaption>
</figure>
<hr>
<h2 id="the-variants-problem--change--when">The Variants: Problem → Change → When</h2>
<p>With the axes in hand, here are the four worth knowing in depth, plus the frontier cluster. Read each as a triple: the problem it noticed, the change it made, and the situation that should make you reach for it.</p>















































<table><thead><tr><th>Variant</th><th>Core change vs. GRPO</th><th>Problem it fixes</th><th>Reach for it when…</th></tr></thead><tbody><tr><td><strong>GRPO</strong></td><td>— (the baseline)</td><td>PPO’s critic cost</td><td>you want a sane critic-free default</td></tr><tr><td><strong>Dr. GRPO</strong></td><td>removes length + std normalization → unbiased gradient</td><td>response-length inflation; token waste</td><td>responses balloon; you want token efficiency</td></tr><tr><td><strong>DAPO</strong></td><td>clip-higher + dynamic sampling + token-level loss + overlong reward shaping</td><td>entropy collapse, reward noise, long-CoT instability</td><td>scaling up; long chains-of-thought; training diverging</td></tr><tr><td><strong>GSPO</strong></td><td>sequence-level importance ratio + sequence-level clipping</td><td>noisy token ratios; MoE instability</td><td>MoE models; large-scale stability</td></tr><tr><td><strong>GDPO</strong></td><td>normalize each reward independently before combining</td><td>advantage collapse under multiple rewards</td><td>multi-objective alignment (tool use, multi-criteria)</td></tr><tr><td><strong>CISPO</strong></td><td>clips importance-sampling weights, not token updates</td><td>low-prob “fork”/reflection tokens being clipped out</td><td>you’re losing reflective “aha” tokens</td></tr></tbody></table>
<h3 id="dr-grpo--debias-the-gradient">Dr. GRPO — debias the gradient</h3>
<p><a href="https://arxiv.org/abs/2503.20783"><em>Understanding R1-Zero-Like Training: A Critical Perspective</em></a> (Liu et al., 2025) noticed that GRPO’s length and standard-deviation normalization are not neutral conveniences — they are biases. The length term systematically rewards <em>longer wrong answers</em>, which is exactly the runaway-verbosity pathology practitioners kept seeing. The fix is almost embarrassingly small: delete both normalizers. No division by response length, no division by group std. The gradient becomes unbiased, responses stop inflating, and the model is markedly more token-efficient. Built on the lightweight <a href="https://github.com/sail-sg/oat">Oat</a> framework, with a clean <a href="https://github.com/sail-sg/understand-r1-zero">reference implementation</a>. Reach for it the moment you see length climbing without accuracy following.</p>
<h3 id="dapo--the-production-bundle">DAPO — the production bundle</h3>
<p><a href="https://arxiv.org/abs/2503.14476"><em>DAPO: An Open-Source LLM Reinforcement Learning System at Scale</em></a> (Yu et al., 2025) is the one that is really four things at once — Decoupled Clip and Dynamic sAmpling Policy Optimization. It ships <strong>clip-higher</strong> (de-symmetrize the clip ceiling to keep low-probability exploratory tokens alive and fight entropy collapse), <strong>dynamic sampling</strong> (drop degenerate all-correct/all-wrong groups so every batch carries gradient), <strong>token-level policy-gradient loss</strong> (so long completions are not under-weighted in long-CoT training), and <strong>overlong reward shaping</strong> (soften the penalty for responses that run past the length budget, reducing reward noise). Together they reach 50 points on AIME 2024 with a Qwen2.5-32B base, with code and data fully open on top of <a href="https://github.com/volcengine/verl">verl</a> (<a href="https://github.com/BytedTsinghua-SIA/DAPO">repo</a>). DAPO is what you reach for when you are scaling up real long-CoT training and instability — diverging loss, collapsing entropy — is the thing standing between you and a good model.</p>
<h3 id="gspo--match-the-unit-of-optimization-to-the-unit-of-reward">GSPO — match the unit of optimization to the unit of reward</h3>
<p><a href="https://arxiv.org/abs/2507.18071"><em>Group Sequence Policy Optimization</em></a> (Zheng et al., 2025, the Qwen team) made a single sharp observation: the reward is assigned to a <em>sequence</em>, so why is the importance ratio computed per <em>token</em>? Token-level ratios are noisy, and that noise is catastrophic for mixture-of-experts models, where the set of active experts can differ between the sampling and training passes. GSPO defines the importance ratio on the sequence likelihood and clips at the sequence level. The result stabilizes MoE RL training — enough that it underpins the latest Qwen3 models. Reach for it when you are training MoE models or chasing large-scale stability, and when token-level ratios are giving you grief. (Implementations live in <a href="https://github.com/volcengine/verl">verl</a> and TRL; the <a href="https://qwenlm.github.io/blog/gspo/">Qwen write-up</a> is the readable intro.)</p>
<h3 id="gdpo--keep-multiple-rewards-from-colliding">GDPO — keep multiple rewards from colliding</h3>
<p>This is <a href="/blog/gdpo/">Part 4</a>, so I will be brief. <a href="https://arxiv.org/abs/2601.05242"><em>GDPO: Group reward-Decoupled Normalization Policy Optimization</em></a> (Liu et al., 2026, NVIDIA) attacks the multi-reward case. Sum several rewards and normalize the total, and different reward combinations can map to the <em>same</em> advantage — the policy goes blind to one objective. GDPO normalizes each reward independently <em>before</em> combining, preserving the resolution of every signal. Reach for it whenever you are optimizing more than one reward at once — tool calling with format-plus-correctness, math with format-plus-answer-plus-integer, coding with execution-plus-tests-plus-style. (If you suspect this is already happening to you, the <a href="/bits/">advantage-collapse checklist</a> is a quick diagnostic.)</p>
<h3 id="the-frontier-cluster">The frontier cluster</h3>
<p>Beyond the four, a frontier worth knowing as <em>coordinates</em>, not as separate disciplines:</p>
<ul>
<li><strong>CISPO</strong>, from the <a href="https://arxiv.org/abs/2506.13585">MiniMax-M1 technical report</a> (2025), clips the <em>importance-sampling weights</em> rather than the token updates. The motivation is precisely the clip-width axis above: standard clipping silently discards the low-probability fork-and-reflect tokens, so CISPO keeps every token contributing and reports a 2× speedup over DAPO in their setting. Reach for it when you suspect your clip is eating your model’s “aha” moments.</li>
<li><strong>SPO</strong> (<a href="https://arxiv.org/abs/2509.13232"><em>Single-stream Policy Optimization</em></a>, Xu &#x26; Ding, 2025) goes group-<em>free</em>: it replaces the on-the-fly group baseline with a persistent, KL-adaptive value tracker and normalizes advantages globally across the batch. It is a glimpse of where the family heads once you decide the group itself is the bottleneck.</li>
</ul>
<p>You can now place the rest of the zoo — VAPO, GMPO, GiGPO, REINFORCE++ — without my help: each one moves along one or two of the six axes. That is the entire point of the map.</p>
<hr>
<h2 id="a-decision-guide">A Decision Guide</h2>
<p>The variants reduce to a flowchart. Start from the symptom, not the acronym.</p>
<figure style="margin:2rem 0;">
<svg viewBox="0 0 760 470" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Decision flowchart mapping a training symptom to the variant that addresses it" style="width:100%;height:auto;display:block;background:var(--bg-secondary);border:1px solid var(--border);border-radius:10px;">
  <rect x="170" y="24" width="420" height="48" rx="9" fill="var(--bg-tertiary)" stroke="var(--accent)" stroke-width="2"></rect>
  <text x="380" y="46" font-family="inherit" font-size="14.5" font-weight="700" fill="var(--text-primary)" text-anchor="middle">Running GRPO and hitting a wall?</text>
  <text x="380" y="64" font-family="inherit" font-size="12" fill="var(--text-muted)" text-anchor="middle">Diagnose the symptom, then read across.</text>
  <!-- rows -->
  <!-- row1 -->
  <rect x="30" y="104" width="380" height="44" rx="8" fill="var(--bg-tertiary)" stroke="var(--border)" stroke-width="1.5"></rect>
  <text x="46" y="131" font-family="inherit" font-size="13" fill="var(--text-secondary)">Responses ballooning, token waste</text>
  <line x1="410" y1="126" x2="470" y2="126" stroke="var(--border)" stroke-width="2"></line>
  <polygon points="470,126 462,122 462,130" fill="var(--border)"></polygon>
  <rect x="470" y="104" width="260" height="44" rx="8" fill="var(--bg-tertiary)" stroke="var(--link-color)" stroke-width="2"></rect>
  <text x="600" y="125" font-family="inherit" font-size="13.5" font-weight="700" fill="var(--text-primary)" text-anchor="middle">Dr. GRPO</text>
  <text x="600" y="140" font-family="inherit" font-size="10.5" fill="var(--text-muted)" text-anchor="middle">debias length + std</text>
  <!-- row2 -->
  <rect x="30" y="162" width="380" height="44" rx="8" fill="var(--bg-tertiary)" stroke="var(--border)" stroke-width="1.5"></rect>
  <text x="46" y="189" font-family="inherit" font-size="13" fill="var(--text-secondary)">Long-CoT at scale, entropy collapsing</text>
  <line x1="410" y1="184" x2="470" y2="184" stroke="var(--border)" stroke-width="2"></line>
  <polygon points="470,184 462,180 462,188" fill="var(--border)"></polygon>
  <rect x="470" y="162" width="260" height="44" rx="8" fill="var(--bg-tertiary)" stroke="var(--link-color)" stroke-width="2"></rect>
  <text x="600" y="183" font-family="inherit" font-size="13.5" font-weight="700" fill="var(--text-primary)" text-anchor="middle">DAPO</text>
  <text x="600" y="198" font-family="inherit" font-size="10.5" fill="var(--text-muted)" text-anchor="middle">clip-higher + dynamic sampling</text>
  <!-- row3 -->
  <rect x="30" y="220" width="380" height="44" rx="8" fill="var(--bg-tertiary)" stroke="var(--border)" stroke-width="1.5"></rect>
  <text x="46" y="247" font-family="inherit" font-size="13" fill="var(--text-secondary)">MoE or large-scale instability</text>
  <line x1="410" y1="242" x2="470" y2="242" stroke="var(--border)" stroke-width="2"></line>
  <polygon points="470,242 462,238 462,246" fill="var(--border)"></polygon>
  <rect x="470" y="220" width="260" height="44" rx="8" fill="var(--bg-tertiary)" stroke="var(--link-color)" stroke-width="2"></rect>
  <text x="600" y="241" font-family="inherit" font-size="13.5" font-weight="700" fill="var(--text-primary)" text-anchor="middle">GSPO</text>
  <text x="600" y="256" font-family="inherit" font-size="10.5" fill="var(--text-muted)" text-anchor="middle">sequence-level ratio + clip</text>
  <!-- row4 -->
  <rect x="30" y="278" width="380" height="44" rx="8" fill="var(--bg-tertiary)" stroke="var(--border)" stroke-width="1.5"></rect>
  <text x="46" y="305" font-family="inherit" font-size="13" fill="var(--text-secondary)">Multiple rewards colliding</text>
  <line x1="410" y1="300" x2="470" y2="300" stroke="var(--border)" stroke-width="2"></line>
  <polygon points="470,300 462,296 462,304" fill="var(--border)"></polygon>
  <rect x="470" y="278" width="260" height="44" rx="8" fill="var(--bg-tertiary)" stroke="var(--link-color)" stroke-width="2"></rect>
  <text x="600" y="299" font-family="inherit" font-size="13.5" font-weight="700" fill="var(--text-primary)" text-anchor="middle">GDPO</text>
  <text x="600" y="314" font-family="inherit" font-size="10.5" fill="var(--text-muted)" text-anchor="middle">decouple reward normalization</text>
  <!-- row5 -->
  <rect x="30" y="336" width="380" height="44" rx="8" fill="var(--bg-tertiary)" stroke="var(--border)" stroke-width="1.5"></rect>
  <text x="46" y="363" font-family="inherit" font-size="13" fill="var(--text-secondary)">Losing reflection / "aha" tokens</text>
  <line x1="410" y1="358" x2="470" y2="358" stroke="var(--border)" stroke-width="2"></line>
  <polygon points="470,358 462,354 462,362" fill="var(--border)"></polygon>
  <rect x="470" y="336" width="260" height="44" rx="8" fill="var(--bg-tertiary)" stroke="var(--link-color)" stroke-width="2"></rect>
  <text x="600" y="357" font-family="inherit" font-size="13.5" font-weight="700" fill="var(--text-primary)" text-anchor="middle">CISPO</text>
  <text x="600" y="372" font-family="inherit" font-size="10.5" fill="var(--text-muted)" text-anchor="middle">clip the IS weights, not tokens</text>
  <!-- default -->
  <rect x="30" y="406" width="700" height="44" rx="8" fill="none" stroke="var(--border)" stroke-width="1.5" stroke-dasharray="5 4"></rect>
  <text x="380" y="433" font-family="inherit" font-size="13" fill="var(--text-muted)" text-anchor="middle">No specific failure? Plain GRPO (or REINFORCE-with-baseline / RLOO) is a strong, simple default.</text>
</svg>
<figcaption style="font-size:0.85rem;color:var(--text-muted);text-align:center;margin-top:0.5rem;">These are not mutually exclusive — DAPO already bundles several of these moves, and verl lets you compose them.</figcaption>
</figure>
<p>The honest version of this flowchart has a caveat: the moves compose. You can run Dr. GRPO’s debiasing <em>and</em> clip-higher <em>and</em> drop KL at the same time; that is roughly what the minimal from-scratch repos do by default. The flowchart tells you which lever a symptom points to, not that you may pull only one.</p>
<hr>
<h2 id="is-this-just-ppo-with-extra-steps">”Is This Just PPO With Extra Steps?”</h2>
<p>A fair skeptic will push back: if everything is REINFORCE-with-a-baseline wearing a PPO surrogate, did we really invent anything?</p>
<p>Two uncomfortable findings say <em>mostly, the skeptic is right</em> — and that is worth internalizing.</p>
<p>First, the on-policy story is a myth. <a href="https://arxiv.org/abs/2509.24203"><em>Group-Relative REINFORCE Is Secretly an Off-Policy Algorithm</em></a> (Yao et al., 2025) derives group-relative REINFORCE from first principles and shows it admits a <em>native off-policy</em> interpretation — the within-group mean baseline does not require the on-policy assumption everyone attaches to it. Where this lands is sobering: the convergence point of the whole family is something close to “PPO with a global advantage and no critic.” The variants are different routes to the same neighborhood, not different destinations.</p>
<p>Second, and more deflating for algorithm enthusiasts: <a href="https://arxiv.org/abs/2510.13786"><em>The Art of Scaling RL Compute for LLMs</em></a> (Khatri, Madaan et al., 2025) — a 400,000+ GPU-hour study — finds that loss aggregation, normalization, curriculum, and the choice of off-policy algorithm primarily move <em>compute efficiency</em>, not the <em>asymptotic</em> performance ceiling. The ceiling is set elsewhere: by the data and the reward. Algorithms get you to the ceiling faster; they rarely raise it.</p>
<p>That is the judgement this map is really teaching. The axes are real and worth tuning — they decide whether your run is stable, efficient, and reaches the ceiling at all. But if you are choosing a variant hoping it will out-<em>level</em> your data, you have the wrong lever in your hand.</p>
<hr>
<h2 id="the-production-lens">The Production Lens</h2>
<p>Which points straight at the thing that actually decides outcomes: every variant on this map optimizes against a <em>reward</em>, and every reward is a <em>proxy</em>.</p>
<p>In math and code you get lucky — a verifier exists. The answer is right or it is not; the unit tests pass or they do not. Reinforcement learning with verifiable rewards (RLVR) works there precisely because the proxy is nearly the real thing. Step outside that world — helpfulness, faithfulness, tone, “did the agent actually accomplish the task” — and there is no clean verifier. You build one (a reward model, an LLM judge, a heuristic), and the optimizer’s job becomes finding its seams. Whatever gap exists between your proxy and what you actually want, a good optimizer will widen it. That is reward hacking, and a <em>better</em> optimizer hacks a flawed reward <em>faster</em>.</p>
<p>So the practical ranking is the opposite of the one the zoo implies. Outside math and code, the bottleneck is almost never the optimizer; it is the verifier. Picking GSPO over DAPO is a second-order decision. Building a reward your optimizer cannot game is the first-order one — and it is the genuinely hard, under-glamorous engineering that decides whether any of these algorithms help you. Hold this as a principle: <em>spend your scrutiny on the reward, then let the axes tune the run.</em></p>
<hr>
<h2 id="feeling-the-differences-on-one-gpu">Feeling the Differences on One GPU</h2>
<p>You do not need a cluster to build intuition for the axes — you need a small model and an afternoon. The deltas between these methods are legible at tiny scale, and a handful of repos are built to make them legible:</p>
<ul>
<li><strong><a href="https://github.com/policy-gradient/GRPO-Zero">GRPO-Zero</a></strong> implements GRPO from scratch <em>with</em> DAPO’s improvements (token-level loss, no KL, overlong filtering) on the Countdown task. It is the single best repo to dissect if you want to <em>see</em> the axis-moves in code rather than read about them.</li>
<li><strong><a href="https://github.com/lsdefine/simple_GRPO">simple_GRPO</a></strong> is ~200 lines across two files and trains in under an hour on a single A800 — teaching-oriented, nothing hidden.</li>
<li><strong><a href="https://github.com/Jiayi-Pan/TinyZero">TinyZero</a></strong> reproduces R1-Zero-style training on verl for under $30, a clean baseline to fork.</li>
<li><strong><a href="https://github.com/unslothai/unsloth">Unsloth’s GRPO notebooks</a></strong> get the R1-Zero “aha” on 5–7 GB of VRAM via QLoRA — about as close to a laptop as this gets.</li>
</ul>
<p>For the research framing of why small-model RL is even worth your time, <a href="https://arxiv.org/abs/2503.16219"><em>Reinforcement Learning for Reasoning in Small LLMs</em></a> (Dang &#x26; Ngo, 2025) trained a 1.5B model to 46.7% on AIME 2024 for roughly $42 on 4×A40 in 24 hours — and was candid about what broke. If you would rather run the full family under one roof, <a href="https://github.com/volcengine/verl"><strong>verl</strong></a> implements almost all of it (PPO, GRPO, GSPO, DAPO, Dr. GRPO, RLOO, REINFORCE++, PRIME) with verifiable rewards; <a href="https://github.com/huggingface/trl"><strong>TRL</strong></a> is the gentlest on-ramp via its <code>GRPOTrainer</code>. This is exactly the territory my own <a href="https://github.com/vitorhcsousa/verifiable-reward-lab">verifiable-reward-lab</a> project lives in.</p>
<hr>
<h2 id="where-the-family-is-heading">Where the Family Is Heading</h2>
<p>The axes will keep being the right lens, even as the methods drift past the ones named here. Three directions are already visible. <strong>Group-free</strong> methods like <a href="https://arxiv.org/abs/2509.13232">SPO</a> ask whether the group baseline — the move that <em>defined</em> GRPO — is itself the next bottleneck, replacing it with a persistent value tracker. <strong>Agentic and hierarchical advantages</strong> push group-relative credit assignment down to steps and sub-trajectories for tool-using, multi-turn agents. And <strong>verifier-free RL</strong> tries to escape the proxy problem from the other side — learning a reward signal without a hand-built verifier at all, which, if it works, attacks the actual ceiling rather than the path to it.</p>
<p>None of these will look like a clean break. Each will arrive as another coordinate: a different granularity, a different clip, a different normalization, a different way to sample. GRPO never fragmented into a zoo. It exposed a design space — and once you can read the axes, you are not learning the next algorithm so much as recognizing where on the map it sits.</p>
<p>That is the whole series in one sentence: from PPO’s critic, to GRPO’s group baseline, to GDPO’s decoupled rewards, the field has been walking a small set of axes the entire time. You can see the full arc on the <a href="/series/rl-for-llms/">series page</a>.</p>
<hr>
<h2 id="references">References</h2>
<p><strong>Foundations (linked, not re-derived here):</strong></p>
<ul>
<li>Schulman et al. (2017), <em>Proximal Policy Optimization Algorithms</em> — <a href="https://arxiv.org/abs/1707.06347">arXiv:1707.06347</a></li>
<li>Ahmadian et al. (2024), <em>Back to Basics: Revisiting REINFORCE Style Optimization for Learning from Human Feedback in LLMs</em> — <a href="https://arxiv.org/abs/2402.14740">arXiv:2402.14740</a></li>
<li>Shao et al. (2024), <em>DeepSeekMath</em> (GRPO origin) — <a href="https://arxiv.org/abs/2402.03300">arXiv:2402.03300</a></li>
<li>DeepSeek-AI / Guo et al. (2025), <em>DeepSeek-R1</em> — <a href="https://arxiv.org/abs/2501.12948">arXiv:2501.12948</a></li>
</ul>
<p><strong>The four core variants:</strong></p>
<ul>
<li>Liu et al. (2025), <em>Understanding R1-Zero-Like Training: A Critical Perspective</em> (Dr. GRPO) — <a href="https://arxiv.org/abs/2503.20783">arXiv:2503.20783</a></li>
<li>Yu et al. (2025), <em>DAPO: An Open-Source LLM Reinforcement Learning System at Scale</em> — <a href="https://arxiv.org/abs/2503.14476">arXiv:2503.14476</a></li>
<li>Zheng et al. (2025), <em>Group Sequence Policy Optimization</em> (GSPO) — <a href="https://arxiv.org/abs/2507.18071">arXiv:2507.18071</a></li>
<li>Liu et al. (2026), <em>GDPO: Group reward-Decoupled Normalization Policy Optimization</em> — <a href="https://arxiv.org/abs/2601.05242">arXiv:2601.05242</a></li>
</ul>
<p><strong>Frontier and critical perspective:</strong></p>
<ul>
<li>MiniMax (2025), <em>MiniMax-M1</em> (introduces CISPO) — <a href="https://arxiv.org/abs/2506.13585">arXiv:2506.13585</a></li>
<li>Yao et al. (2025), <em>Group-Relative REINFORCE Is Secretly an Off-Policy Algorithm</em> — <a href="https://arxiv.org/abs/2509.24203">arXiv:2509.24203</a></li>
<li>Khatri, Madaan et al. (2025), <em>The Art of Scaling RL Compute for LLMs</em> — <a href="https://arxiv.org/abs/2510.13786">arXiv:2510.13786</a></li>
<li>Xu &#x26; Ding (2025), <em>Single-stream Policy Optimization</em> (SPO) — <a href="https://arxiv.org/abs/2509.13232">arXiv:2509.13232</a></li>
</ul>
<p><strong>Small-model / single-GPU:</strong></p>
<ul>
<li>Dang &#x26; Ngo (2025), <em>Reinforcement Learning for Reasoning in Small LLMs</em> (Open-RS) — <a href="https://arxiv.org/abs/2503.16219">arXiv:2503.16219</a></li>
<li>Frameworks and minimal repos: <a href="https://github.com/volcengine/verl">verl</a>, <a href="https://github.com/huggingface/trl">TRL</a>, <a href="https://github.com/sail-sg/oat">Oat</a>, <a href="https://github.com/policy-gradient/GRPO-Zero">GRPO-Zero</a>, <a href="https://github.com/lsdefine/simple_GRPO">simple_GRPO</a>, <a href="https://github.com/Jiayi-Pan/TinyZero">TinyZero</a>, <a href="https://github.com/unslothai/unsloth">Unsloth</a>, and the curated <a href="https://github.com/hscspring/rl-llm-nlp">rl-llm-nlp index</a>.</li>
</ul>]]></content:encoded><enclosure url="https://www.vitorsousa.com/_astro/building-a-transformer.BvwHo2r3.png" length="756000" type="image/png"/></item><item><title>After GDPO: A Map of the GRPO Family</title><link>https://www.vitorsousa.com/blog/grpo-family-map/</link><guid isPermaLink="true">https://www.vitorsousa.com/blog/grpo-family-map/</guid><description>GRPO didn&apos;t fragment into a zoo. It exposed a handful of design axes — token vs. sequence granularity, clip width, length and std normalization, KL, advantage normalization, sampling — and every &apos;new&apos; algorithm is a different point in that space. A practitioner&apos;s map of Dr. GRPO, DAPO, GSPO, GDPO, and the frontier, with a decision guide for when to reach for each.</description><pubDate>Mon, 22 Jun 2026 10:00:00 GMT</pubDate><content:encoded><![CDATA[<h1 id="exploring-openelm-the-intersection-of-open-source-and-high-efficiency-in-ai">Exploring OpenELM The Intersection of Open Source and High Efficiency in AI</h1>
<p><img src="/images/openelm.png" alt="Cover illustration representing the OpenELM research paper"></p>
<hr>
<p><a href="https://arxiv.org/abs/2404.14619"><img src="https://img.shields.io/badge/apple-FFF?style=for-the-badge&#x26;logo=apple&#x26;logoColor=000" alt="Apple badge linking to the OpenELM announcement"></a> <a href="https://arxiv.org/abs/2404.14619"><img src="https://img.shields.io/badge/24_APR_2024-FFF?style=for-the-badge&#x26;logo=rocket&#x26;logoColor=000" alt="Publication date badge for 24 April 2024"></a> <a href="https://arxiv.org/abs/2404.14619"><img src="https://img.shields.io/badge/arxiv-FFF?style=for-the-badge&#x26;logo=arxiv&#x26;logoColor=000" alt="arXiv badge linking to the OpenELM paper"></a></p>
<hr>
<h2 id="introduction"><strong>Introduction</strong></h2>
<p>OpenELM is a state-of-the-art open language model launched by Apple.<br>
The researchers introduced <em><strong>OpenELM</strong></em>, a family of <strong>O</strong>pen-source <strong>E</strong>fficient <strong>L</strong>anguage <strong>M</strong>odels. OpenELM uses a layer-wise scaling strategy to efficiently allocate parameters within each layer of the transformer model, leading to enhanced accuracy. The OpenELM models were pretrained using the <a href="https://github.com/apple/corenet/tree/main/projects/openelm">CoreNet</a> library. They release both pretrained and instruction-tuned models with 270M, 450M, 1.1B and 3B parameters.</p>
<p>They also release the complete framework, encompassing data preparation, training, fine-tuning, and evaluation procedures, alongside multiple pre-trained checkpoints and training logs, to facilitate open research.</p>
<blockquote>
<p>OpenELM outperforms existing open LLMs that are pre-trained using publicly available datasets. OpenELM with 1.1 billion parameters outperforms OLMo, which has 1.2 billion parameters, by 2.36% while requiring 2× fewer pre-training tokens.</p>
</blockquote>
<h2 id="innovations-in-architecture">Innovations in Architecture</h2>
<p>OpenELM distinguishes itself with a layer-wise scaling strategy within its transformer architecture, enabling more efficient parameter allocation across layers. Traditional transformers allocate parameters uniformly across all layers, the models are isotropic. In contrast, OpenELM adjusts the configuration of each layer to optimize parameter usage effectively. This approach not only enhances the model’s learning efficiency but also significantly boosts its performance with less training data.</p>
<p>Key architectural points:</p>
<ul>
<li>They don’t use learnable bias parameters in any fully connected(linear) layers.</li>
<li>Apply pre-normalization using <strong>RMSNorm</strong></li>
<li>Use  <strong>Rotatory Positional Embedding (ROPE)</strong>: Enhances the model’s understanding of word order, critical for generating coherent and contextually aware language outputs.</li>
<li>Use <strong>Grouped Query Attention (GQA)</strong>: An alternative to the typical multi-head attention that allows more nuanced attention mechanisms, improving the model’s interpretative capabilities.</li>
<li>Use <strong>SwiGLU Activation</strong>: A novel activation function that provides a more dynamic range for model training, leading to faster convergence and improved accuracy (replacing the FFN).</li>
<li>Use <strong>Flash Attention</strong> to compute the dot-product attention</li>
<li>Use the same <strong>tokenizer</strong> as <strong>LLama</strong>.
<ul>
<li>They filter and tokenize data <em>on the fly</em>. This facilitates experimentation with different tokenizers.</li>
</ul>
</li>
</ul>
<h3 id="layer-wise-scaling">Layer-wise scaling</h3>
<p>For the non-uniform allocation of parameters in the transformer layer, they adjust the number of attention heads and the FFN multiplier in each transformer layer.</p>
<p>Assuming that the standard transformer model with uniform parameter allocation has <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.6833em;"></span><span class="mord mathnormal" style="margin-right:0.10903em;">N</span></span></span></span> transformer layers and the dimensionality of the input of each layer is <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>d</mi><mrow><mi>m</mi><mi>o</mi><mi>d</mi><mi>e</mi><mi>l</mi></mrow></msub></mrow><annotation encoding="application/x-tex">d_{model}</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 mtight"><span class="mord mathnormal mtight">m</span><span class="mord mathnormal mtight">o</span><span class="mord mathnormal mtight">d</span><span class="mord mathnormal mtight">e</span><span class="mord mathnormal mtight" style="margin-right:0.01968em;">l</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 MHA has <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>n</mi><mi>h</mi></msub></mrow><annotation encoding="application/x-tex">n_h</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 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>​ heads and a dimension o</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>n</mi><msub><mi>i</mi><mi>h</mi></msub></msub><mo>=</mo><mfrac><mrow><msub><mi>α</mi><mi>i</mi></msub><mo>⋅</mo><msub><mi>d</mi><mtext>model</mtext></msub></mrow><msub><mi>d</mi><mi>h</mi></msub></mfrac><mo separator="true">,</mo><mspace width="1em"></mspace><msub><mi>m</mi><mi>i</mi></msub><mo>=</mo><msub><mi>β</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">n_{i_h} = \frac{\alpha_i \cdot d_{\text{model}}}{d_h}, \quad m_i = \beta_i</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">n</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 mtight"><span class="mord mathnormal mtight">i</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: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">h</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.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"><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 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.0037em;">α</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.0037em;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="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 mtight"><span class="mord text mtight"><span class="mord mtight">model</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 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">m</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:0.8889em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.05278em;">β</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.0528em;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></span>
<p>Where:</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>i</mi></msub><mo>=</mo><msub><mi>α</mi><mi>min</mi><mo>⁡</mo></msub><mo>+</mo><mfrac><mrow><mo stretchy="false">(</mo><msub><mi>α</mi><mi>max</mi><mo>⁡</mo></msub><mo>−</mo><msub><mi>α</mi><mi>min</mi><mo>⁡</mo></msub><mo stretchy="false">)</mo><mo>⋅</mo><mi>i</mi></mrow><mrow><mi>N</mi><mo>−</mo><mn>1</mn></mrow></mfrac><mo separator="true">,</mo></mrow><annotation encoding="application/x-tex">\alpha_i = \alpha_{\min} + \frac{(\alpha_{\max} - \alpha_{\min}) \cdot i}{N-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" style="margin-right:0.0037em;">α</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.0037em;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:0.7333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0037em;">α</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.0037em;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="mop mtight"><span class="mtight">m</span><span class="mtight">i</span><span class="mtight">n</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:2.1963em;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.427em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.10903em;">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">1</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.0037em;">α</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.0037em;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="mop mtight"><span class="mtight">m</span><span class="mtight">a</span><span class="mtight">x</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 mathnormal" style="margin-right:0.0037em;">α</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.0037em;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="mop mtight"><span class="mtight">m</span><span class="mtight">i</span><span class="mtight">n</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 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">i</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="mpunct">,</span></span></span></span></span>
<p>and</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>i</mi></msub><mo>=</mo><msub><mi>β</mi><mi>min</mi><mo>⁡</mo></msub><mo>+</mo><mfrac><mrow><mo stretchy="false">(</mo><msub><mi>β</mi><mi>max</mi><mo>⁡</mo></msub><mo>−</mo><msub><mi>β</mi><mi>min</mi><mo>⁡</mo></msub><mo stretchy="false">)</mo><mo>⋅</mo><mi>i</mi></mrow><mrow><mi>N</mi><mo>−</mo><mn>1</mn></mrow></mfrac><mo separator="true">,</mo><mspace width="1em"></mspace><mn>0</mn><mo>≤</mo><mi>i</mi><mo>&#x3C;</mo><mi>N</mi><mi mathvariant="normal">.</mi></mrow><annotation encoding="application/x-tex">\beta_i = \beta_{\min} + \frac{(\beta_{\max} - \beta_{\min}) \cdot i}{N-1}, \quad 0 \leq i &#x3C; 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"><span class="mord mathnormal" style="margin-right:0.05278em;">β</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.0528em;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:0.8889em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.05278em;">β</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.0528em;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="mop mtight"><span class="mtight">m</span><span class="mtight">i</span><span class="mtight">n</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:2.1963em;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.427em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.10903em;">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">1</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.05278em;">β</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.0528em;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="mop mtight"><span class="mtight">m</span><span class="mtight">a</span><span class="mtight">x</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 mathnormal" style="margin-right:0.05278em;">β</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.0528em;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="mop mtight"><span class="mtight">m</span><span class="mtight">i</span><span class="mtight">n</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 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">i</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="mpunct">,</span><span class="mspace" style="margin-right:1em;"></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">0</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.6986em;vertical-align:-0.0391em;"></span><span class="mord mathnormal">i</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&#x3C;</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.10903em;">N</span><span class="mord">.</span></span></span></span></span>
<p>The <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>a</mi><mrow><mi>m</mi><mi>i</mi><mi>n</mi></mrow></msub><mo separator="true">,</mo><msub><mi>a</mi><mrow><mi>m</mi><mi>a</mi><mi>x</mi></mrow></msub></mrow><annotation encoding="application/x-tex">a_{min}, a_{max}</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">a</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">min</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">a</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 mathnormal mtight">ma</span><span class="mord mathnormal mtight">x</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> are the hyperparameters that allow them to scal teh attention heads. Similarly <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>b</mi><mrow><mi>m</mi><mi>i</mi><mi>n</mi></mrow></msub><mo separator="true">,</mo><msub><mi>b</mi><mrow><mi>m</mi><mi>a</mi><mi>x</mi></mrow></msub></mrow><annotation encoding="application/x-tex">b_{min},b_{max}</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 class="mord mathnormal">b</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">min</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">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 mtight"><span class="mord mathnormal mtight">ma</span><span class="mord mathnormal mtight">x</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> allow them to vary the with of the FFN layers.
Note, setting <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>a</mi><mrow><mi>m</mi><mi>i</mi><mi>n</mi></mrow></msub><mo>=</mo><msub><mi>a</mi><mrow><mi>m</mi><mi>a</mi><mi>x</mi></mrow></msub><mo>=</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">a_{min}= a_{max}=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">a</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">min</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">a</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 mathnormal mtight">ma</span><span class="mord mathnormal mtight">x</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">1</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>m</mi><mi>i</mi></msub><mo>=</mo><mi>m</mi></mrow><annotation encoding="application/x-tex">m_i = m</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.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:0.4306em;"></span><span class="mord mathnormal">m</span></span></span></span>  produces the standard uniform transformer model.</p>
<h3 id="pretraining">Pretraining</h3>
<p>The used pre-training dataset contains RefinedWeb, deduplicated PILE, a subset of RedPajama, and a subset of Dolma v1.6, totalling approximately 1.8 trillion tokens.</p>
<p>They filter and tokenize data <em>on the fly</em>. This facilitates experimentation with different tokenizers.</p>
<p>To filter out low-length sequences, they apply two filtering methods. The first method operates at the characterlevel, checking if the number of characters in the sequence is below a specified threshold. The second method operates at the token-level, where it examines whether the sequence contains fewer tokens than a specified threshold. Sequences that are shorter than either of these thresholds are skipped.
In  experiments, were used <strong>200 characters</strong> and <strong>256 tokens</strong> as character and token-level filtering thresholds.</p>
<h2 id="performance-and-benchmarking">Performance and Benchmarking</h2>
<blockquote>
<p>OpenELM’s performance is outstanding, especially when benchmarked against similar-sized models like OLMo. With 1.1 billion parameters, OpenELM achieves a 2.36% higher accuracy while requiring only half the pre-training tokens. This efficiency is illustrated through various benchmarks on tasks such as ARC-c, BoolQ, and HellaSwag, where OpenELM consistently outperforms its peers.</p>
</blockquote>
<h3 id="evaluation">Evaluation</h3>
<p>They evaluate the performance across different tasks using LM Evaluation Harness:</p>
<ul>
<li>
<h4 id="standard-zero-shot-tasks7">Standard zero-shot tasks(7):</h4>
<ul>
<li>ARC easy and challenge,</li>
<li>BoolQ,</li>
<li>HellaSwag,</li>
<li>PIQA,</li>
<li>SciQ, and</li>
<li>WinoGrande</li>
</ul>








































































































<table><thead><tr><th><strong>Model Size</strong></th><th><strong>ARC-c</strong></th><th><strong>ARC-e</strong></th><th><strong>BoolQ</strong></th><th><strong>HellaSwag</strong></th><th><strong>PIQA</strong></th><th><strong>SciQ</strong></th><th><strong>WinoGrande</strong></th><th><strong>Average</strong></th></tr></thead><tbody><tr><td><a href="https://huggingface.co/apple/OpenELM-270M">OpenELM-270M</a></td><td>26.45</td><td>45.08</td><td><strong>53.98</strong></td><td>46.71</td><td>69.75</td><td><strong>84.70</strong></td><td><strong>53.91</strong></td><td>54.37</td></tr><tr><td><a href="https://huggingface.co/apple/OpenELM-270M-Instruct">OpenELM-270M-Instruct</a></td><td><strong>30.55</strong></td><td><strong>46.68</strong></td><td>48.56</td><td><strong>52.07</strong></td><td><strong>70.78</strong></td><td>84.40</td><td>52.72</td><td><strong>55.11</strong></td></tr><tr><td><a href="https://huggingface.co/apple/OpenELM-450M">OpenELM-450M</a></td><td>27.56</td><td>48.06</td><td>55.78</td><td>53.97</td><td>72.31</td><td>87.20</td><td>58.01</td><td>57.56</td></tr><tr><td><a href="https://huggingface.co/apple/OpenELM-450M-Instruct">OpenELM-450M-Instruct</a></td><td><strong>30.38</strong></td><td><strong>50.00</strong></td><td><strong>60.37</strong></td><td><strong>59.34</strong></td><td><strong>72.63</strong></td><td><strong>88.00</strong></td><td><strong>58.96</strong></td><td><strong>59.95</strong></td></tr><tr><td><a href="https://huggingface.co/apple/OpenELM-1_1B">OpenELM-1_1B</a></td><td>32.34</td><td><strong>55.43</strong></td><td>63.58</td><td>64.81</td><td><strong>75.57</strong></td><td><strong>90.60</strong></td><td>61.72</td><td>63.44</td></tr><tr><td><a href="https://huggingface.co/apple/OpenELM-1_1B-Instruct">OpenELM-1_1B-Instruct</a></td><td><strong>37.97</strong></td><td>52.23</td><td><strong>70.00</strong></td><td><strong>71.20</strong></td><td>75.03</td><td>89.30</td><td><strong>62.75</strong></td><td><strong>65.50</strong></td></tr><tr><td><a href="https://huggingface.co/apple/OpenELM-3B">OpenELM-3B</a></td><td>35.58</td><td>59.89</td><td>67.40</td><td>72.44</td><td>78.24</td><td><strong>92.70</strong></td><td>65.51</td><td>67.39</td></tr><tr><td><a href="https://huggingface.co/apple/OpenELM-3B-Instruct">OpenELM-3B-Instruct</a></td><td><strong>39.42</strong></td><td><strong>61.74</strong></td><td><strong>68.17</strong></td><td><strong>76.36</strong></td><td><strong>79.00</strong></td><td>92.50</td><td><strong>66.85</strong></td><td><strong>69.15</strong></td></tr></tbody></table>
</li>
<li>
<h4 id="openllm-5">OpenLLM (5)</h4>
<ul>
<li>ARC challenge,</li>
<li>HellaSwag,</li>
<li>MMLU,</li>
<li>TruthfulQA, and</li>
<li>WinoGrande.</li>
</ul>






















































































<table><thead><tr><th><strong>Model Size</strong></th><th><strong>ARC-c</strong></th><th><strong>HellaSwag</strong></th><th><strong>MMLU</strong></th><th><strong>TruthfulQA</strong></th><th><strong>WinoGrande</strong></th><th><strong>Average</strong></th></tr></thead><tbody><tr><td><a href="https://huggingface.co/apple/OpenELM-270M">OpenELM-270M</a></td><td>27.65</td><td>47.15</td><td>25.72</td><td><strong>39.24</strong></td><td><strong>53.83</strong></td><td>38.72</td></tr><tr><td><a href="https://huggingface.co/apple/OpenELM-270M-Instruct">OpenELM-270M-Instruct</a></td><td><strong>32.51</strong></td><td><strong>51.58</strong></td><td><strong>26.70</strong></td><td>38.72</td><td>53.20</td><td><strong>40.54</strong></td></tr><tr><td><a href="https://huggingface.co/apple/OpenELM-450M">OpenELM-450M</a></td><td>30.20</td><td>53.86</td><td><strong>26.01</strong></td><td>40.18</td><td>57.22</td><td>41.50</td></tr><tr><td><a href="https://huggingface.co/apple/OpenELM-450M-Instruct">OpenELM-450M-Instruct</a></td><td><strong>33.53</strong></td><td><strong>59.31</strong></td><td>25.41</td><td><strong>40.48</strong></td><td><strong>58.33</strong></td><td><strong>43.41</strong></td></tr><tr><td><a href="https://huggingface.co/apple/OpenELM-1_1B">OpenELM-1_1B</a></td><td>36.69</td><td>65.71</td><td><strong>27.05</strong></td><td>36.98</td><td>63.22</td><td>45.93</td></tr><tr><td><a href="https://huggingface.co/apple/OpenELM-1_1B-Instruct">OpenELM-1_1B-Instruct</a></td><td><strong>41.55</strong></td><td><strong>71.83</strong></td><td>25.65</td><td><strong>45.95</strong></td><td><strong>64.72</strong></td><td><strong>49.94</strong></td></tr><tr><td><a href="https://huggingface.co/apple/OpenELM-3B">OpenELM-3B</a></td><td>42.24</td><td>73.28</td><td><strong>26.76</strong></td><td>34.98</td><td>67.25</td><td>48.90</td></tr><tr><td><a href="https://huggingface.co/apple/OpenELM-3B-Instruct">OpenELM-3B-Instruct</a></td><td><strong>47.70</strong></td><td><strong>76.87</strong></td><td>24.80</td><td><strong>38.76</strong></td><td><strong>67.96</strong></td><td><strong>51.22</strong></td></tr></tbody></table>
</li>
<li>
<h4 id="llm360-leaderboard-tasks-7">LLM360 leaderboard tasks (7):</h4>
<ul>
<li>ARC challenge,</li>
<li>CrowS-Pairs (English version),</li>
<li>HellaSwag,</li>
<li>WinoGrande,</li>
<li>MMLU,</li>
<li>PIQA, and</li>
<li>RACE</li>
</ul>

















































































































<table><thead><tr><th><strong>Model Size</strong></th><th><strong>ARC-c</strong></th><th><strong>CrowS-Pairs</strong></th><th><strong>HellaSwag</strong></th><th><strong>MMLU</strong></th><th><strong>PIQA</strong></th><th><strong>RACE</strong></th><th><strong>TruthfulQA</strong></th><th><strong>WinoGrande</strong></th><th><strong>Average</strong></th></tr></thead><tbody><tr><td><a href="https://huggingface.co/apple/OpenELM-270M">OpenELM-270M</a></td><td>27.65</td><td><strong>66.79</strong></td><td>47.15</td><td>25.72</td><td>69.75</td><td>30.91</td><td><strong>39.24</strong></td><td><strong>53.83</strong></td><td>45.13</td></tr><tr><td><a href="https://huggingface.co/apple/OpenELM-270M-Instruct">OpenELM-270M-Instruct</a></td><td><strong>32.51</strong></td><td>66.01</td><td><strong>51.58</strong></td><td><strong>26.70</strong></td><td><strong>70.78</strong></td><td>33.78</td><td>38.72</td><td>53.20</td><td><strong>46.66</strong></td></tr><tr><td><a href="https://huggingface.co/apple/OpenELM-450M">OpenELM-450M</a></td><td>30.20</td><td><strong>68.63</strong></td><td>53.86</td><td><strong>26.01</strong></td><td>72.31</td><td>33.11</td><td>40.18</td><td>57.22</td><td>47.69</td></tr><tr><td><a href="https://huggingface.co/apple/OpenELM-450M-Instruct">OpenELM-450M-Instruct</a></td><td><strong>33.53</strong></td><td>67.44</td><td><strong>59.31</strong></td><td>25.41</td><td><strong>72.63</strong></td><td><strong>36.84</strong></td><td><strong>40.48</strong></td><td><strong>58.33</strong></td><td><strong>49.25</strong></td></tr><tr><td><a href="https://huggingface.co/apple/OpenELM-1_1B">OpenELM-1_1B</a></td><td>36.69</td><td><strong>71.74</strong></td><td>65.71</td><td><strong>27.05</strong></td><td><strong>75.57</strong></td><td>36.46</td><td>36.98</td><td>63.22</td><td>51.68</td></tr><tr><td><a href="https://huggingface.co/apple/OpenELM-1_1B-Instruct">OpenELM-1_1B-Instruct</a></td><td><strong>41.55</strong></td><td>71.02</td><td><strong>71.83</strong></td><td>25.65</td><td>75.03</td><td><strong>39.43</strong></td><td><strong>45.95</strong></td><td><strong>64.72</strong></td><td><strong>54.40</strong></td></tr><tr><td><a href="https://huggingface.co/apple/OpenELM-3B">OpenELM-3B</a></td><td>42.24</td><td><strong>73.29</strong></td><td>73.28</td><td><strong>26.76</strong></td><td>78.24</td><td><strong>38.76</strong></td><td>34.98</td><td>67.25</td><td>54.35</td></tr><tr><td><a href="https://huggingface.co/apple/OpenELM-3B-Instruct">OpenELM-3B-Instruct</a></td><td><strong>47.70</strong></td><td>72.33</td><td><strong>76.87</strong></td><td>24.80</td><td><strong>79.00</strong></td><td>38.47</td><td><strong>38.76</strong></td><td><strong>67.96</strong></td><td><strong>55.73</strong></td></tr></tbody></table>
</li>
</ul>
<blockquote>
<p>OpenELM outperforms existing open LLMs that are pre-trained using publicly available datasets. OpenELM with 1.1 billion parameters outperforms OLMo, which has 1.2 billion parameters, by 2.36% while requiring 2× fewer pre-training tokens.</p>
</blockquote>
<h3 id="instruction-tuning-results">Instruction tuning results</h3>
<p>They also check the impact of inctruct tuning, that consistently improves OpenELM’s average accuracy by 1-2% across different evaluation frameworks.</p>
<h3 id="parameter-efficient-fine-tuning-peft-results">Parameter-efficient fine-tuning (PEFT) results</h3>
<p>They used <em><strong>CommonSense</strong></em> reasoning training and evaluation setup that provides 170k training samples across 8 multiple-choice datasets for PEFT studies with different methods, including <em><strong>LoRA</strong></em>  and <em><strong>DoRA</strong></em></p>
<h3 id="decrease-on-throughput">Decrease on throughput</h3>
<p>In one of the lastest section they show an analysis that reveals tha OpenELM  is a bit slower thatn OLMo. They attribute significant portion of OpenELM’s processing time  to the naive implementation of RMSNorm. Naive RMSNorm implementation results in many individual kernel launches each of which processes a small input, rather than a launch of a single, fused kernel, as would be the case with e.g. LayerNorm. By replacing the naive RMSNorm with Apex’s RMSNorm  we observe a notable increase in OpenELM’s throughput.</p>
<h2 id="conclusion"><strong>Conclusion</strong></h2>
<p>The major point was the usage of a layer-wise scaling method for efficient parameter allocation within the transformer model, resulting in improved accuracy compared to existing models.</p>
<p>OpenELM is more than just another entry in the growing list of language models. Its innovative architecture, superior performance, and unprecedented commitment to openness set a new standard in the field.  It’s really interesting see a company like Apple to release the complete framework, encompassing data preparation, training, fine-tuning, and evaluation procedures, alongside multiple pre-trained checkpoints and training logs,  this could really help in further developments in research.</p>
<p>In future work, one of the point identified to improve the inference efficiency of OpenELM.</p>
<h2 id="explore-more"><strong>Explore More</strong></h2>
<p>For those interested in diving deeper into OpenELM or even experimenting with the model, check out the resources provided by the researchers <a href="https://github.com/apple/corenet/tree/main/projects/openelm"><img src="https://img.shields.io/badge/github-FFF?style=for-the-badge&#x26;logo=github&#x26;logoColor=000" alt="github"></a> <a href="https://huggingface.co/apple/OpenELM"><img src="https://img.shields.io/badge/🤗_Hugging_Face-FFF?style=for-the-badge" alt="date"></a>.</p>]]></content:encoded><enclosure url="https://www.vitorsousa.com/_astro/grpo-family-map.DS_JBgQg.svg" length="756000" type="image/svg"/></item><item><title>Building a Transformer: The Complete Forward Pass</title><link>https://www.vitorsousa.com/foundations/building-a-transformer/</link><guid isPermaLink="true">https://www.vitorsousa.com/foundations/building-a-transformer/</guid><description>Layer normalization, residual connections, feed-forward networks, and the full decoder-only transformer assembled piece by piece with tested PyTorch code.</description><pubDate>Fri, 24 Apr 2026 19:00:00 GMT</pubDate></item><item><title>Positional Encoding: Teaching Transformers to Count</title><link>https://www.vitorsousa.com/foundations/positional-encoding/</link><guid isPermaLink="true">https://www.vitorsousa.com/foundations/positional-encoding/</guid><description>Sinusoidal, learned, and rotary position embeddings — the math of why position information is necessary and how each approach encodes it.</description><pubDate>Thu, 02 Apr 2026 19:00:00 GMT</pubDate></item><item><title>Attention Is All You Need to Implement</title><link>https://www.vitorsousa.com/foundations/attention-from-scratch/</link><guid isPermaLink="true">https://www.vitorsousa.com/foundations/attention-from-scratch/</guid><description>The complete guide to attention mechanisms — from the matrix math of scaled dot-product attention through multi-head projections to a tested PyTorch implementation. Every equation derived, every dimension explained.</description><pubDate>Wed, 01 Apr 2026 19:00:00 GMT</pubDate></item><item><title>GDPO: Multi-Reward RL Done Right</title><link>https://www.vitorsousa.com/blog/gdpo/</link><guid isPermaLink="true">https://www.vitorsousa.com/blog/gdpo/</guid><description>When GRPO meets multiple rewards, advantages collapse. GDPO fixes this by normalizing each reward independently before combining. Learn why this matters for tool calling, math reasoning, and any multi-objective LLM alignment.</description><pubDate>Wed, 11 Feb 2026 19:00:00 GMT</pubDate><content:encoded><![CDATA[<h1 id="reinforcement-learning-foundations-for-llm-alignment">Reinforcement Learning Foundations for LLM Alignment</h1>
<p><strong>Part 1 of 4:</strong> The Mathematical Foundations</p>
<blockquote>
<p><strong>TL;DR:</strong> Before understanding PPO, GRPO, or any modern LLM alignment algorithm, you need solid RL foundations. This article covers MDPs, policy gradients, the variance problem, value functions, Bellman equations, and advantage estimation—all through the lens of language model fine-tuning. By the end, you’ll understand <em>why</em> these algorithms are designed the way they are.</p>
</blockquote>
<p><strong>Reading time:</strong> ~35 minutes</p>
<hr>
<h2 id="why-rl-for-language-models">Why RL for Language Models?</h2>
<p><em><strong>You’ve trained a language model on terabytes of text. It can complete sentences, answer questions, even write code. But it also happily generates harmful content, confidently states falsehoods, and ignores user preferences. Supervised fine-tuning on curated examples helps, but you can’t enumerate every possible good response.</strong></em></p>
<p><em><strong>What you need is a way to optimize for “goodness” directly—to tell the model “responses like this are better than responses like that” and have it learn the pattern. This is exactly what reinforcement learning provides.</strong></em></p>
<p>The insight behind RLHF (Reinforcement Learning from Human Feedback) is powerful: instead of showing the model correct outputs, we show it a <em>reward signal</em> indicating how good its outputs are. The model then learns to maximize this reward.</p>
<p>But RL for LLMs isn’t straightforward. The action space is enormous (vocabulary size per token), episodes are variable length, rewards are sparse (often only at generation end), and we need stable training for billion-parameter models.</p>
<p>Understanding the foundations deeply will help you see why algorithms like PPO and GRPO make specific design choices—and when those choices might not be right for your problem.</p>
<hr>
<h2 id="table-of-contents">Table of Contents</h2>
<ol>
<li><a href="#the-mdp-framework">The MDP Framework</a></li>
<li><a href="#policies-and-the-objective">Policies and the Objective</a></li>
<li><a href="#policy-gradients-learning-without-a-model">Policy Gradients: Learning Without a Model</a></li>
<li><a href="#the-variance-problem">The Variance Problem</a></li>
<li><a href="#value-functions-and-bellman-equations">Value Functions and Bellman Equations</a></li>
<li><a href="#advantage-functions-the-key-insight">Advantage Functions: The Key Insight</a></li>
<li><a href="#actor-critic-methods">Actor-Critic Methods</a></li>
<li><a href="#from-general-rl-to-llm-fine-tuning">From General RL to LLM Fine-Tuning</a></li>
<li><a href="#key-takeaways">Key Takeaways</a></li>
</ol>
<hr>
<h2 id="the-mdp-framework">The MDP Framework</h2>
<p>Reinforcement learning formalizes sequential decision-making through <strong>Markov Decision Processes (MDPs)</strong>. Understanding this framework precisely will help you see how LLM generation maps onto RL concepts.</p>
<h3 id="definition">Definition</h3>
<p>An MDP is defined by the tuple <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi mathvariant="script">S</mi><mo separator="true">,</mo><mi mathvariant="script">A</mi><mo separator="true">,</mo><mi>P</mi><mo separator="true">,</mo><mi>R</mi><mo separator="true">,</mo><mi>γ</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">(\mathcal{S}, \mathcal{A}, P, R, \gamma)</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 mathcal" style="margin-right:0.075em;">S</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathcal">A</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.00773em;">R</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.05556em;">γ</span><span class="mclose">)</span></span></span></span>:</p>



































<table><thead><tr><th>Component</th><th>Symbol</th><th>Description</th></tr></thead><tbody><tr><td><strong>State space</strong></td><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="script">S</mi></mrow><annotation encoding="application/x-tex">\mathcal{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 mathcal" style="margin-right:0.075em;">S</span></span></span></span></td><td>All possible situations the agent can be in</td></tr><tr><td><strong>Action space</strong></td><td><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></td><td>All possible actions the agent can take</td></tr><tr><td><strong>Transition dynamics</strong></td><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>P</mi><mo stretchy="false">(</mo><msup><mi>s</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mi mathvariant="normal">∥</mi><mi>s</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">P(s' \| s, a)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0019em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">s</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="mord">∥</span><span class="mord mathnormal">s</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</span><span class="mclose">)</span></span></span></span></td><td>Probability of reaching state <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>s</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup></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.7519em;"></span><span class="mord"><span class="mord mathnormal">s</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></span></span> from state <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> after action <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.4306em;"></span><span class="mord mathnormal">a</span></span></span></span></td></tr><tr><td><strong>Reward function</strong></td><td><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>s</mi><mo separator="true">,</mo><mi>a</mi><mo separator="true">,</mo><msup><mi>s</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">R(s, a, s')</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0019em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.00773em;">R</span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal">s</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="mclose">)</span></span></span></span></td><td>Immediate reward for transition <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>s</mi><mo separator="true">,</mo><mi>a</mi><mo separator="true">,</mo><msup><mi>s</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">(s, a, s')</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0019em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal">s</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="mclose">)</span></span></span></span></td></tr><tr><td><strong>Discount factor</strong></td><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>γ</mi><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">\gamma \in [0, 1]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7335em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.05556em;">γ</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></td><td>How much to weight future vs. immediate rewards</td></tr></tbody></table>
<h3 id="the-markov-property">The Markov Property</h3>
<p>The “Markov” in MDP refers to the <strong>Markov property</strong>: the future depends only on the current state, not on how we got there.</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>P</mi><mo stretchy="false">(</mo><msub><mi>s</mi><mrow><mi>t</mi><mo>+</mo><mn>1</mn></mrow></msub><mi mathvariant="normal">∣</mi><msub><mi>s</mi><mi>t</mi></msub><mo separator="true">,</mo><msub><mi>a</mi><mi>t</mi></msub><mo separator="true">,</mo><msub><mi>s</mi><mrow><mi>t</mi><mo>−</mo><mn>1</mn></mrow></msub><mo separator="true">,</mo><msub><mi>a</mi><mrow><mi>t</mi><mo>−</mo><mn>1</mn></mrow></msub><mo separator="true">,</mo><mo>…</mo><mo stretchy="false">)</mo><mo>=</mo><mi>P</mi><mo stretchy="false">(</mo><msub><mi>s</mi><mrow><mi>t</mi><mo>+</mo><mn>1</mn></mrow></msub><mi mathvariant="normal">∣</mi><msub><mi>s</mi><mi>t</mi></msub><mo separator="true">,</mo><msub><mi>a</mi><mi>t</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">P(s_{t+1} | s_t, a_t, s_{t-1}, a_{t-1}, \ldots) = P(s_{t+1} | s_t, a_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.13889em;">P</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="mord">∣</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="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></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="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></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="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></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.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="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner">…</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.13889em;">P</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="mord">∣</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="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></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="mclose">)</span></span></span></span></p>
<p>This is crucial for tractability—we don’t need to remember the entire history, just the current state.</p>
<h3 id="the-agent-environment-loop">The Agent-Environment Loop</h3>
<p>At each timestep <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>:</p>
<ol>
<li>Agent observes state <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></li>
<li>Agent selects action <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> according to its policy</li>
<li>Environment transitions to <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>s</mi><mrow><mi>t</mi><mo>+</mo><mn>1</mn></mrow></msub><mo>∼</mo><mi>P</mi><mo stretchy="false">(</mo><mo>⋅</mo><mi mathvariant="normal">∣</mi><msub><mi>s</mi><mi>t</mi></msub><mo separator="true">,</mo><msub><mi>a</mi><mi>t</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">s_{t+1} \sim P(\cdot | s_t, a_t)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6389em;vertical-align:-0.2083em;"></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="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.13889em;">P</span><span class="mopen">(</span><span class="mord">⋅</span><span class="mord">∣</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="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></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="mclose">)</span></span></span></span></li>
<li>Agent receives 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><mi>R</mi><mo stretchy="false">(</mo><msub><mi>s</mi><mi>t</mi></msub><mo separator="true">,</mo><msub><mi>a</mi><mi>t</mi></msub><mo separator="true">,</mo><msub><mi>s</mi><mrow><mi>t</mi><mo>+</mo><mn>1</mn></mrow></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">r_t = R(s_t, a_t, s_{t+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" style="margin-right:0.02778em;">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:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.00773em;">R</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="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></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="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></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></span></span></li>
<li>Repeat until terminal state or horizon</li>
</ol>
<div class="mermaid">
graph LR
    subgraph Loop["&#x3C;b>The RL Interaction Loop&#x3C;/b>"]
        direction LR
        
        A["&#x3C;b>Agent&#x3C;/b>&#x3C;br/>━━━━━━━━━━&#x3C;br/>Policy π(a|s)&#x3C;br/>Selects actions&#x3C;br/>Learns from rewards"]
        
        ActionNode["&#x3C;b>Action&#x3C;/b>&#x3C;br/>&#x3C;i>a_t&#x3C;/i>"]
        
        E["&#x3C;b>Environment&#x3C;/b>&#x3C;br/>━━━━━━━━━━&#x3C;br/>Dynamics P(s'|s,a)&#x3C;br/>Generates rewards&#x3C;br/>Transitions states"]
        
        FeedbackNode["&#x3C;b>Feedback&#x3C;/b>&#x3C;br/>&#x3C;i>s_t+1 , r_t&#x3C;/i>"]
        
        A --> ActionNode
        ActionNode --> E
        E --> FeedbackNode
        FeedbackNode --> A
    end
    
    style A stroke:#10b981,stroke-width:2.5px
    style E stroke:#06b6d4,stroke-width:2.5px
    style ActionNode stroke:#10b981,stroke-width:1.5px
    style FeedbackNode stroke:#f59e0b,stroke-width:1.5px
    style Loop stroke:#334155,stroke-width:1px</div>
<h3 id="llm-generation-as-an-mdp">LLM Generation as an MDP</h3>
<p>For language model fine-tuning, we can map concepts as follows:</p>





























<table><thead><tr><th>RL Concept</th><th>LLM Equivalent</th></tr></thead><tbody><tr><td><strong>State <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></strong></td><td>Prompt + tokens generated so far: <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>q</mi><mo separator="true">,</mo><msub><mi>o</mi><mn>1</mn></msub><mo separator="true">,</mo><mo>…</mo><mo separator="true">,</mo><msub><mi>o</mi><mrow><mi>t</mi><mo>−</mo><mn>1</mn></mrow></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">(q, o_1, \ldots, o_{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="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">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.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">o</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></span></span></td></tr><tr><td><strong>Action <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></strong></td><td>Next token <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>o</mi><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">o_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">o</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> from 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.08222em;">V</span></span></span></span></td></tr><tr><td><strong>Transition</strong></td><td>Deterministic: append chosen token to sequence</td></tr><tr><td><strong>Reward</strong></td><td>Often sparse: <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><mn>0</mn></mrow><annotation encoding="application/x-tex">r_t = 0</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.02778em;">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.6444em;"></span><span class="mord">0</span></span></span></span> for <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi><mo>&#x3C;</mo><mi>T</mi></mrow><annotation encoding="application/x-tex">t &#x3C; T</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6542em;vertical-align:-0.0391em;"></span><span class="mord mathnormal">t</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&#x3C;</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">T</span></span></span></span>, <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><mi>R</mi><mo stretchy="false">(</mo><mi>q</mi><mo separator="true">,</mo><mi>o</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">r_T = R(q, o)</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.02778em;">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" style="margin-right:0.13889em;">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="mord mathnormal" style="margin-right:0.00773em;">R</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">o</span><span class="mclose">)</span></span></span></span> at end</td></tr><tr><td><strong>Episode</strong></td><td>One complete generation from prompt to EOS token</td></tr></tbody></table>
<p>The state space is enormous (all possible token sequences up to context length), and the action space is the vocabulary size (typically 32K-128K tokens). This scale motivates many of the algorithmic choices we’ll see.</p>
<blockquote>
<p><strong>Key Insight:</strong> The Markov property technically holds for LLM generation—the next token distribution depends only on the current sequence, not on how we generated it. However, the state itself encodes the full history, so we’re not actually discarding information.</p>
</blockquote>
<hr>
<h2 id="policies-and-the-objective">Policies and the Objective</h2>
<h3 id="what-is-a-policy">What is a Policy?</h3>
<p>A <strong>policy</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">\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.03588em;">π</span></span></span></span> maps states to actions. It can be:</p>
<ul>
<li><strong>Deterministic:</strong> <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>a</mi><mo>=</mo><mi>π</mi><mo stretchy="false">(</mo><mi>s</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">a = \pi(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">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:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">π</span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mclose">)</span></span></span></span> — always the same action in state <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></li>
<li><strong>Stochastic:</strong> <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>a</mi><mo>∼</mo><mi>π</mi><mo stretchy="false">(</mo><mi>a</mi><mi mathvariant="normal">∣</mi><mi>s</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">a \sim \pi(a|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">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:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">π</span><span class="mopen">(</span><span class="mord mathnormal">a</span><span class="mord">∣</span><span class="mord mathnormal">s</span><span class="mclose">)</span></span></span></span> — a probability distribution over actions</li>
</ul>
<p>For LLMs, the policy is inherently stochastic: <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>o</mi><mi>t</mi></msub><mi mathvariant="normal">∣</mi><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo><mo>=</mo><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>o</mi><mi>t</mi></msub><mi mathvariant="normal">∣</mi><mi>q</mi><mo separator="true">,</mo><msub><mi>o</mi><mrow><mo>&#x3C;</mo><mi>t</mi></mrow></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\pi_\theta(o_t | s_t) = \pi_\theta(o_t | q, o_{&#x3C;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"><span class="mord mathnormal" style="margin-right:0.03588em;">π</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.02778em;">θ</span></span></span></span><span 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.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><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.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.03588em;">π</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.02778em;">θ</span></span></span></span><span 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.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><span class="mord mathnormal" style="margin-right:0.03588em;">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.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">&#x3C;</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> is the probability of token <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>o</mi><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">o_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">o</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> given the context. The 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.02778em;">θ</span></span></span></span> are the model weights.</p>
<h3 id="the-return">The Return</h3>
<p>The <strong>return</strong> <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>G</mi><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">G_t</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">G</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 the cumulative (discounted) reward from time <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> onwards:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>G</mi><mi>t</mi></msub><mo>=</mo><msubsup><mo>∑</mo><mrow><mi>k</mi><mo>=</mo><mn>0</mn></mrow><mi mathvariant="normal">∞</mi></msubsup><msup><mi>γ</mi><mi>k</mi></msup><msub><mi>r</mi><mrow><mi>t</mi><mo>+</mo><mi>k</mi></mrow></msub><mo>=</mo><msub><mi>r</mi><mi>t</mi></msub><mo>+</mo><mi>γ</mi><msub><mi>r</mi><mrow><mi>t</mi><mo>+</mo><mn>1</mn></mrow></msub><mo>+</mo><msup><mi>γ</mi><mn>2</mn></msup><msub><mi>r</mi><mrow><mi>t</mi><mo>+</mo><mn>2</mn></mrow></msub><mo>+</mo><mo>⋯</mo></mrow><annotation encoding="application/x-tex">G_t = \sum_{k=0}^{\infty} \gamma^k r_{t+k} = r_t + \gamma r_{t+1} + \gamma^2 r_{t+2} + \cdots</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">G</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:1.1488em;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.8043em;"><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.03148em;">k</span><span class="mrel mtight">=</span><span class="mord mtight">0</span></span></span></span><span style="top:-3.2029em;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 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 mathnormal" style="margin-right:0.05556em;">γ</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.03148em;">k</span></span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">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 mtight"><span class="mord mathnormal mtight">t</span><span class="mbin mtight">+</span><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</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="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.02778em;">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:0.7917em;vertical-align:-0.2083em;"></span><span class="mord mathnormal" style="margin-right:0.05556em;">γ</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">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"><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="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.0224em;vertical-align:-0.2083em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.05556em;">γ</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"><span class="mord mathnormal" style="margin-right:0.02778em;">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"><span class="mord mathnormal mtight">t</span><span class="mbin mtight">+</span><span class="mord mtight">2</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="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.313em;"></span><span class="minner">⋯</span></span></span></span></p>
<p>The discount factor <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.05556em;">γ</span></span></span></span> serves multiple purposes:</p>
<ul>
<li><strong>Mathematical:</strong> Ensures the sum converges for infinite horizons</li>
<li><strong>Practical:</strong> Encodes preference for immediate vs. delayed rewards</li>
<li><strong>LLM setting:</strong> Often <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.05556em;">γ</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> since episodes are finite</li>
</ul>
<h3 id="the-rl-objective">The RL Objective</h3>
<p>The goal of RL is to find a policy that maximizes <strong>expected return</strong>:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>J</mi><mo stretchy="false">(</mo><mi>θ</mi><mo stretchy="false">)</mo><mo>=</mo><msub><mi mathvariant="double-struck">E</mi><mrow><mi>τ</mi><mo>∼</mo><msub><mi>π</mi><mi>θ</mi></msub></mrow></msub><mrow><mo fence="true">[</mo><msubsup><mo>∑</mo><mrow><mi>t</mi><mo>=</mo><mn>0</mn></mrow><mi>T</mi></msubsup><msup><mi>γ</mi><mi>t</mi></msup><msub><mi>r</mi><mi>t</mi></msub><mo fence="true">]</mo></mrow><mo>=</mo><msub><mi mathvariant="double-struck">E</mi><mrow><mi>τ</mi><mo>∼</mo><msub><mi>π</mi><mi>θ</mi></msub></mrow></msub><mo stretchy="false">[</mo><msub><mi>G</mi><mn>0</mn></msub><mo stretchy="false">]</mo></mrow><annotation encoding="application/x-tex">J(\theta) = \mathbb{E}_{\tau \sim \pi_\theta}\left[\sum_{t=0}^{T} \gamma^t r_t\right] = \mathbb{E}_{\tau \sim \pi_\theta}[G_0]</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.09618em;">J</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.02778em;">θ</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.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 mathnormal mtight" style="margin-right:0.1132em;">τ</span><span class="mrel mtight">∼</span><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.03588em;">π</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.02778em;">θ</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 delimcenter" style="top:0em;"><span class="delimsizing size2">[</span></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.9812em;"><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">t</span><span class="mrel mtight">=</span><span class="mord mtight">0</span></span></span></span><span style="top:-3.2029em;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.13889em;">T</span></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 mathnormal" style="margin-right:0.05556em;">γ</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7936em;"><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">t</span></span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">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="mclose delimcenter" style="top:0em;"><span class="delimsizing size2">]</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.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 mathnormal mtight" style="margin-right:0.1132em;">τ</span><span class="mrel mtight">∼</span><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.03588em;">π</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.02778em;">θ</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">G</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">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="mclose">]</span></span></span></span></p>
<p>where <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>τ</mi><mo>=</mo><mo stretchy="false">(</mo><msub><mi>s</mi><mn>0</mn></msub><mo separator="true">,</mo><msub><mi>a</mi><mn>0</mn></msub><mo separator="true">,</mo><msub><mi>r</mi><mn>0</mn></msub><mo separator="true">,</mo><msub><mi>s</mi><mn>1</mn></msub><mo separator="true">,</mo><msub><mi>a</mi><mn>1</mn></msub><mo separator="true">,</mo><msub><mi>r</mi><mn>1</mn></msub><mo separator="true">,</mo><mo>…</mo><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\tau = (s_0, a_0, r_0, s_1, a_1, r_1, \ldots)</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 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 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">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="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></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.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">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="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">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">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="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></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">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">a</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" style="margin-right:0.02778em;">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="mclose">)</span></span></span></span> is a <strong>trajectory</strong> sampled by following 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.03588em;">π</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.02778em;">θ</span></span></span></span><span 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>The expectation is over:</p>
<ul>
<li>Initial state distribution <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>s</mi><mn>0</mn></msub><mo>∼</mo><msub><mi>ρ</mi><mn>0</mn></msub></mrow><annotation encoding="application/x-tex">s_0 \sim \rho_0</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.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">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.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.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">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></li>
<li>Actions from policy <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><mo>∼</mo><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><mi>a</mi><mi mathvariant="normal">∣</mi><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">a_t \sim \pi_\theta(a|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">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><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.03588em;">π</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.02778em;">θ</span></span></span></span><span 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">a</span><span class="mord">∣</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></li>
<li>Transitions from dynamics <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>s</mi><mrow><mi>t</mi><mo>+</mo><mn>1</mn></mrow></msub><mo>∼</mo><mi>P</mi><mo stretchy="false">(</mo><msup><mi>s</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mi mathvariant="normal">∣</mi><msub><mi>s</mi><mi>t</mi></msub><mo separator="true">,</mo><msub><mi>a</mi><mi>t</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">s_{t+1} \sim P(s'|s_t, a_t)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6389em;vertical-align:-0.2083em;"></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="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.0019em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">s</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="mord">∣</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="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></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="mclose">)</span></span></span></span></li>
</ul>
<h3 id="for-llms-specifically">For LLMs Specifically</h3>
<p>In the LLM setting, the objective simplifies to:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>J</mi><mo stretchy="false">(</mo><mi>θ</mi><mo stretchy="false">)</mo><mo>=</mo><msub><mi mathvariant="double-struck">E</mi><mrow><mi>q</mi><mo>∼</mo><mi>P</mi><mo stretchy="false">(</mo><mi>Q</mi><mo stretchy="false">)</mo><mo separator="true">,</mo><mtext> </mtext><mi>o</mi><mo>∼</mo><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><mi>O</mi><mi mathvariant="normal">∣</mi><mi>q</mi><mo stretchy="false">)</mo></mrow></msub><mo stretchy="false">[</mo><mi>R</mi><mo stretchy="false">(</mo><mi>q</mi><mo separator="true">,</mo><mi>o</mi><mo stretchy="false">)</mo><mo stretchy="false">]</mo></mrow><annotation encoding="application/x-tex">J(\theta) = \mathbb{E}_{q \sim P(Q), \, o \sim \pi_\theta(O|q)}[R(q, o)]</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.09618em;">J</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.02778em;">θ</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.1052em;vertical-align:-0.3552em;"></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.03588em;">q</span><span class="mrel mtight">∼</span><span class="mord mathnormal mtight" style="margin-right:0.13889em;">P</span><span class="mopen mtight">(</span><span class="mord mathnormal mtight">Q</span><span class="mclose mtight">)</span><span class="mpunct mtight">,</span><span class="mspace mtight" style="margin-right:0.1952em;"></span><span class="mord mathnormal mtight">o</span><span class="mrel mtight">∼</span><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.03588em;">π</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.02778em;">θ</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 mathnormal mtight" style="margin-right:0.02778em;">O</span><span class="mord mtight">∣</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">q</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="mopen">[</span><span class="mord mathnormal" style="margin-right:0.00773em;">R</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">o</span><span class="mclose">)]</span></span></span></span></p>
<p>where:</p>
<ul>
<li><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.03588em;">q</span></span></span></span> is a prompt from the prompt distribution <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>P</mi><mo stretchy="false">(</mo><mi>Q</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">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 mathnormal" style="margin-right:0.13889em;">P</span><span class="mopen">(</span><span class="mord mathnormal">Q</span><span class="mclose">)</span></span></span></span></li>
<li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>o</mi><mo>=</mo><mo stretchy="false">(</mo><msub><mi>o</mi><mn>1</mn></msub><mo separator="true">,</mo><mo>…</mo><mo separator="true">,</mo><msub><mi>o</mi><mi>T</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">o = (o_1, \ldots, o_T)</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">o</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 class="mord mathnormal">o</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">o</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 mathnormal mtight" style="margin-right:0.13889em;">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> is a complete response sampled from the model</li>
<li><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>q</mi><mo separator="true">,</mo><mi>o</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">R(q, o)</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.00773em;">R</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">o</span><span class="mclose">)</span></span></span></span> is the reward (typically from a reward model)</li>
</ul>
<p>With sparse rewards (only at episode end), there’s no discounting to worry about within an episode.</p>
<hr>
<h2 id="policy-gradients-learning-without-a-model">Policy Gradients: Learning Without a Model</h2>
<p>How do we optimize <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>J</mi><mo stretchy="false">(</mo><mi>θ</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">J(\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 mathnormal" style="margin-right:0.09618em;">J</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.02778em;">θ</span><span class="mclose">)</span></span></span></span>? We can’t compute it analytically—it requires integrating over all possible trajectories. But we can estimate its gradient and use gradient ascent.</p>
<h3 id="the-policy-gradient-theorem">The Policy Gradient Theorem</h3>
<p>The <strong>policy gradient theorem</strong> (Sutton et al., 1999) provides a remarkable result: we can estimate <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="normal">∇</mi><mi>θ</mi></msub><mi>J</mi><mo stretchy="false">(</mo><mi>θ</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\nabla_\theta J(\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">∇</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.02778em;">θ</span></span></span></span><span 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 mathnormal" style="margin-right:0.09618em;">J</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.02778em;">θ</span><span class="mclose">)</span></span></span></span> using only samples, without knowing the transition dynamics <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.13889em;">P</span></span></span></span>.</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="normal">∇</mi><mi>θ</mi></msub><mi>J</mi><mo stretchy="false">(</mo><mi>θ</mi><mo stretchy="false">)</mo><mo>=</mo><msub><mi mathvariant="double-struck">E</mi><mrow><mi>τ</mi><mo>∼</mo><msub><mi>π</mi><mi>θ</mi></msub></mrow></msub><mrow><mo fence="true">[</mo><msubsup><mo>∑</mo><mrow><mi>t</mi><mo>=</mo><mn>0</mn></mrow><mi>T</mi></msubsup><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>a</mi><mi>t</mi></msub><mi mathvariant="normal">∣</mi><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo><mo>⋅</mo><msub><mi>G</mi><mi>t</mi></msub><mo fence="true">]</mo></mrow></mrow><annotation encoding="application/x-tex">\nabla_\theta J(\theta) = \mathbb{E}_{\tau \sim \pi_\theta}\left[\sum_{t=0}^{T} \nabla_\theta \log \pi_\theta(a_t|s_t) \cdot G_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">∇</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.02778em;">θ</span></span></span></span><span 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 mathnormal" style="margin-right:0.09618em;">J</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.02778em;">θ</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.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 mathnormal mtight" style="margin-right:0.1132em;">τ</span><span class="mrel mtight">∼</span><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.03588em;">π</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.02778em;">θ</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 delimcenter" style="top:0em;"><span class="delimsizing size2">[</span></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.9812em;"><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">t</span><span class="mrel mtight">=</span><span class="mord mtight">0</span></span></span></span><span style="top:-3.2029em;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.13889em;">T</span></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">∇</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.02778em;">θ</span></span></span></span><span 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.01389em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">π</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.02778em;">θ</span></span></span></span><span 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="mord">∣</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 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.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 delimcenter" style="top:0em;"><span class="delimsizing size2">]</span></span></span></span></span></span></p>
<p>Let’s unpack this:</p>
<ul>
<li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><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>a</mi><mi>t</mi></msub><mi mathvariant="normal">∣</mi><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\nabla_\theta \log \pi_\theta(a_t|s_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"><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.02778em;">θ</span></span></span></span><span 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.01389em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">π</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.02778em;">θ</span></span></span></span><span 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="mord">∣</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> is the <strong>score function</strong>—the direction that increases the probability of action <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></li>
<li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>G</mi><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">G_t</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">G</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 the return from time <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>—how good the outcome was</li>
<li>The product says: “increase probability of actions that led to high returns”</li>
</ul>
<h3 id="intuition-credit-assignment">Intuition: Credit Assignment</h3>
<p>The gradient has an elegant interpretation:</p>
<ol>
<li>Sample a trajectory by running the policy</li>
<li>For each action taken, compute how much total reward followed</li>
<li>If reward was high, increase that action’s probability</li>
<li>If reward was low, decrease that action’s probability</li>
</ol>
<p>This is the core of <strong>policy gradient methods</strong>: reinforce good actions, discourage bad ones.</p>
<h3 id="the-reinforce-algorithm">The REINFORCE Algorithm</h3>
<p>The simplest policy gradient algorithm is <strong>REINFORCE</strong> (Williams, 1992):</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>Algorithm: REINFORCE</span></span>
<span class="line"><span></span></span>
<span class="line"><span>For each episode:</span></span>
<span class="line"><span>    1. Sample trajectory τ = (s₀, a₀, r₀, ..., s_T, a_T, r_T) using π_θ</span></span>
<span class="line"><span>    </span></span>
<span class="line"><span>    2. For t = 0 to T:</span></span>
<span class="line"><span>        Compute return: G_t = Σₖ γᵏ r_{t+k}</span></span>
<span class="line"><span>    </span></span>
<span class="line"><span>    3. Compute gradient estimate:</span></span>
<span class="line"><span>        ∇̂ = Σₜ ∇_θ log π_θ(aₜ|sₜ) · Gₜ</span></span>
<span class="line"><span>    </span></span>
<span class="line"><span>    4. Update: θ ← θ + α∇̂</span></span>
<span class="line"><span></span></span></code></pre>
<h3 id="why-this-works-the-log-derivative-trick">Why This Works: The Log-Derivative Trick</h3>
<p>The derivation uses the <strong>log-derivative trick</strong>:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="normal">∇</mi><mi>θ</mi></msub><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><mi>a</mi><mi mathvariant="normal">∣</mi><mi>s</mi><mo stretchy="false">)</mo><mo>=</mo><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><mi>a</mi><mi mathvariant="normal">∣</mi><mi>s</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><mi>a</mi><mi mathvariant="normal">∣</mi><mi>s</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\nabla_\theta \pi_\theta(a|s) = \pi_\theta(a|s) \cdot \nabla_\theta \log \pi_\theta(a|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"><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.02778em;">θ</span></span></span></span><span 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 mathnormal" style="margin-right:0.03588em;">π</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.02778em;">θ</span></span></span></span><span 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">a</span><span class="mord">∣</span><span class="mord mathnormal">s</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" style="margin-right:0.03588em;">π</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.02778em;">θ</span></span></span></span><span 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">a</span><span class="mord">∣</span><span class="mord mathnormal">s</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="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.02778em;">θ</span></span></span></span><span 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.01389em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">π</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.02778em;">θ</span></span></span></span><span 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">a</span><span class="mord">∣</span><span class="mord mathnormal">s</span><span class="mclose">)</span></span></span></span></p>
<p>This converts a gradient of a probability into an expectation we can sample:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="normal">∇</mi><mi>θ</mi></msub><msub><mi mathvariant="double-struck">E</mi><mrow><mi>a</mi><mo>∼</mo><msub><mi>π</mi><mi>θ</mi></msub></mrow></msub><mo stretchy="false">[</mo><mi>f</mi><mo stretchy="false">(</mo><mi>a</mi><mo stretchy="false">)</mo><mo stretchy="false">]</mo><mo>=</mo><msub><mi mathvariant="double-struck">E</mi><mrow><mi>a</mi><mo>∼</mo><msub><mi>π</mi><mi>θ</mi></msub></mrow></msub><mo stretchy="false">[</mo><mi>f</mi><mo stretchy="false">(</mo><mi>a</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><mi>a</mi><mi mathvariant="normal">∣</mi><mi>s</mi><mo stretchy="false">)</mo><mo stretchy="false">]</mo></mrow><annotation encoding="application/x-tex">\nabla_\theta \mathbb{E}_{a \sim \pi_\theta}[f(a)] = \mathbb{E}_{a \sim \pi_\theta}[f(a) \cdot \nabla_\theta \log \pi_\theta(a|s)]</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">∇</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.02778em;">θ</span></span></span></span><span 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 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 mathnormal mtight">a</span><span class="mrel mtight">∼</span><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.03588em;">π</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.02778em;">θ</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" style="margin-right:0.10764em;">f</span><span class="mopen">(</span><span class="mord mathnormal">a</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.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 mathnormal mtight">a</span><span class="mrel mtight">∼</span><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.03588em;">π</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.02778em;">θ</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" style="margin-right:0.10764em;">f</span><span class="mopen">(</span><span class="mord mathnormal">a</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="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.02778em;">θ</span></span></span></span><span 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.01389em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">π</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.02778em;">θ</span></span></span></span><span 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">a</span><span class="mord">∣</span><span class="mord mathnormal">s</span><span class="mclose">)]</span></span></span></span></p>
<p>The full derivation for trajectories is more involved but follows the same principle.</p>
<blockquote>
<p><strong>Key Insight:</strong> Policy gradients are <strong>model-free</strong>—we don’t need to know <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>P</mi><mo stretchy="false">(</mo><msup><mi>s</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mi mathvariant="normal">∣</mi><mi>s</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">P(s'|s,a)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0019em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">s</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="mord">∣</span><span class="mord mathnormal">s</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</span><span class="mclose">)</span></span></span></span>. We only need to sample from the environment and observe rewards. This is crucial for LLMs where the “environment” is just string concatenation.</p>
</blockquote>
<hr>
<h2 id="the-variance-problem">The Variance Problem</h2>
<p>REINFORCE is elegant but has a critical flaw: <strong>high variance</strong>. The gradient estimates are so noisy that learning is impractically slow.</p>
<h3 id="sources-of-variance">Sources of Variance</h3>
<p>Consider what affects the gradient estimate <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><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>a</mi><mi>t</mi></msub><mi mathvariant="normal">∣</mi><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo><mo>⋅</mo><msub><mi>G</mi><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">\nabla_\theta \log \pi_\theta(a_t|s_t) \cdot G_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"><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.02778em;">θ</span></span></span></span><span 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.01389em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">π</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.02778em;">θ</span></span></span></span><span 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="mord">∣</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:0.8333em;vertical-align:-0.15em;"></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.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>:</p>
<ol>
<li><strong>Stochastic policy:</strong> Different action samples give different gradients</li>
<li><strong>Stochastic transitions:</strong> Same action can lead to different states</li>
<li><strong>Long horizons:</strong> <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>G</mi><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">G_t</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">G</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> includes all future rewards, accumulating randomness</li>
<li><strong>Sparse rewards:</strong> Most of the signal comes from rare events</li>
</ol>
<h3 id="a-concrete-example">A Concrete Example</h3>
<p>Suppose you’re training an LLM to solve math problems. The reward is +1 for correct, 0 for incorrect.</p>
<p><strong>Episode 1:</strong> Model generates a correct solution. Every token gets gradient proportional to +1.</p>
<p><strong>Episode 2:</strong> Model generates an incorrect solution. Every token gets gradient proportional to 0.</p>
<p><strong>The problem:</strong> In episode 1, <em>all</em> tokens get positive reinforcement—including lucky guesses, unnecessary steps, and tokens that happened to work but weren’t essential. The gradient doesn’t distinguish “this token was crucial” from “this token was irrelevant.”</p>
<h3 id="variance-reduction-with-baselines">Variance Reduction with Baselines</h3>
<p>A key insight: we can subtract any <strong>baseline</strong> <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>b</mi><mo stretchy="false">(</mo><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">b(s_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">b</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> that doesn’t depend on actions without changing the expected gradient:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="normal">∇</mi><mi>θ</mi></msub><mi>J</mi><mo stretchy="false">(</mo><mi>θ</mi><mo stretchy="false">)</mo><mo>=</mo><mi mathvariant="double-struck">E</mi><mrow><mo fence="true">[</mo><msub><mo>∑</mo><mi>t</mi></msub><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>a</mi><mi>t</mi></msub><mi mathvariant="normal">∣</mi><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo><mo>⋅</mo><mo stretchy="false">(</mo><msub><mi>G</mi><mi>t</mi></msub><mo>−</mo><mi>b</mi><mo stretchy="false">(</mo><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo><mo stretchy="false">)</mo><mo fence="true">]</mo></mrow></mrow><annotation encoding="application/x-tex">\nabla_\theta J(\theta) = \mathbb{E}\left[\sum_{t} \nabla_\theta \log \pi_\theta(a_t|s_t) \cdot (G_t - b(s_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">∇</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.02778em;">θ</span></span></span></span><span 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 mathnormal" style="margin-right:0.09618em;">J</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.02778em;">θ</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.0497em;vertical-align:-0.2997em;"></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="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.1308em;"><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">t</span></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">∇</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.02778em;">θ</span></span></span></span><span 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.01389em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">π</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.02778em;">θ</span></span></span></span><span 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="mord">∣</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 class="mopen">(</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.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.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal">b</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="mclose delimcenter" style="top:0em;">]</span></span></span></span></span></p>
<p><strong>Why doesn’t this change the expectation?</strong></p>
<p><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><mrow><mi>a</mi><mo>∼</mo><mi>π</mi></mrow></msub><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><mi>a</mi><mi mathvariant="normal">∣</mi><mi>s</mi><mo stretchy="false">)</mo><mo>⋅</mo><mi>b</mi><mo stretchy="false">(</mo><mi>s</mi><mo stretchy="false">)</mo><mo fence="true">]</mo></mrow><mo>=</mo><mi>b</mi><mo stretchy="false">(</mo><mi>s</mi><mo stretchy="false">)</mo><mo>⋅</mo><msub><mi mathvariant="double-struck">E</mi><mrow><mi>a</mi><mo>∼</mo><mi>π</mi></mrow></msub><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><mi>a</mi><mi mathvariant="normal">∣</mi><mi>s</mi><mo stretchy="false">)</mo><mo fence="true">]</mo></mrow><mo>=</mo><mi>b</mi><mo stretchy="false">(</mo><mi>s</mi><mo stretchy="false">)</mo><mo>⋅</mo><msub><mi mathvariant="normal">∇</mi><mi>θ</mi></msub><msub><mo>∑</mo><mi>a</mi></msub><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><mi>a</mi><mi mathvariant="normal">∣</mi><mi>s</mi><mo stretchy="false">)</mo><mo>=</mo><mi>b</mi><mo stretchy="false">(</mo><mi>s</mi><mo stretchy="false">)</mo><mo>⋅</mo><msub><mi mathvariant="normal">∇</mi><mi>θ</mi></msub><mn>1</mn><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">\mathbb{E}_{a \sim \pi}\left[\nabla_\theta \log \pi_\theta(a|s) \cdot b(s)\right] = b(s) \cdot \mathbb{E}_{a \sim \pi}\left[\nabla_\theta \log \pi_\theta(a|s)\right] = b(s) \cdot \nabla_\theta \sum_a \pi_\theta(a|s) = b(s) \cdot \nabla_\theta 1 = 0</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">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 mathnormal mtight">a</span><span class="mrel mtight">∼</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">π</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="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.02778em;">θ</span></span></span></span><span 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.01389em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">π</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.02778em;">θ</span></span></span></span><span 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">a</span><span class="mord">∣</span><span class="mord mathnormal">s</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 mathnormal">b</span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mclose">)</span><span class="mclose delimcenter" style="top:0em;">]</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">b</span><span class="mopen">(</span><span class="mord mathnormal">s</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="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 mathnormal mtight">a</span><span class="mrel mtight">∼</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">π</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="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.02778em;">θ</span></span></span></span><span 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.01389em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">π</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.02778em;">θ</span></span></span></span><span 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">a</span><span class="mord">∣</span><span class="mord mathnormal">s</span><span class="mclose">)</span><span class="mclose delimcenter" style="top:0em;">]</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">b</span><span class="mopen">(</span><span class="mord mathnormal">s</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.0497em;vertical-align:-0.2997em;"></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.02778em;">θ</span></span></span></span><span 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"><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 mathnormal mtight">a</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 mathnormal" style="margin-right:0.03588em;">π</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.02778em;">θ</span></span></span></span><span 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">a</span><span class="mord">∣</span><span class="mord mathnormal">s</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">b</span><span class="mopen">(</span><span class="mord mathnormal">s</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.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.02778em;">θ</span></span></span></span><span 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">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.6444em;"></span><span class="mord">0</span></span></span></span></p>
<p>The baseline can be anything that doesn’t depend on the action—but choosing it well dramatically reduces variance.</p>
<h3 id="optimal-baseline">Optimal Baseline</h3>
<p>The variance-minimizing baseline is approximately the <strong>expected return from state <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></strong>:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>b</mi><mo>∗</mo></msup><mo stretchy="false">(</mo><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo><mo>≈</mo><mi mathvariant="double-struck">E</mi><mo stretchy="false">[</mo><msub><mi>G</mi><mi>t</mi></msub><mi mathvariant="normal">∣</mi><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">]</mo></mrow><annotation encoding="application/x-tex">b^*(s_t) \approx \mathbb{E}[G_t | s_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"><span class="mord mathnormal">b</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"><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.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 mathbb">E</span><span class="mopen">[</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.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><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></p>
<p>This is the <strong>value function</strong> <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><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">V(s_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.22222em;">V</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>, which we’ll explore next.</p>
<h3 id="intuition-relative-performance">Intuition: Relative Performance</h3>
<p>With a good baseline, the gradient update becomes:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><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>a</mi><mi>t</mi></msub><mi mathvariant="normal">∣</mi><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo><mo>⋅</mo><mo stretchy="false">(</mo><msub><mi>G</mi><mi>t</mi></msub><mo>−</mo><mi>V</mi><mo stretchy="false">(</mo><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\nabla_\theta \log \pi_\theta(a_t|s_t) \cdot (G_t - V(s_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"><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.02778em;">θ</span></span></span></span><span 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.01389em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">π</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.02778em;">θ</span></span></span></span><span 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="mord">∣</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:1em;vertical-align:-0.25em;"></span><span class="mopen">(</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.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.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.22222em;">V</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></p>
<p>This says:</p>
<ul>
<li>If <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>G</mi><mi>t</mi></msub><mo>></mo><mi>V</mi><mo stretchy="false">(</mo><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">G_t > V(s_t)</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">G</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="mord mathnormal" style="margin-right:0.22222em;">V</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>: Action was <strong>better than expected</strong> → increase probability</li>
<li>If <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>G</mi><mi>t</mi></msub><mo>&#x3C;</mo><mi>V</mi><mo stretchy="false">(</mo><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">G_t &#x3C; V(s_t)</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">G</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">&#x3C;</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.22222em;">V</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>: Action was <strong>worse than expected</strong> → decrease probability</li>
<li>If <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>G</mi><mi>t</mi></msub><mo>=</mo><mi>V</mi><mo stretchy="false">(</mo><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">G_t = V(s_t)</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">G</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="mord mathnormal" style="margin-right:0.22222em;">V</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>: Action was <strong>exactly as expected</strong> → no change</li>
</ul>
<p>This <strong>relative</strong> signal is far more informative than absolute returns.</p>
<div class="mermaid">
graph LR
    subgraph Absolute["&#x3C;b>Without Baseline&#x3C;/b>&#x3C;br/>REINFORCE"]
        direction TB
        A1["&#x3C;b>G = 100&#x3C;/b>&#x3C;br/>━━━━━━━━&#x3C;br/>Big positive ∇&#x3C;br/>Reinforce strongly"]
        A2["&#x3C;b>G = 95&#x3C;/b>&#x3C;br/>━━━━━━━━&#x3C;br/>Big positive ∇&#x3C;br/>Reinforce strongly"]
        A3["&#x3C;b>G = 0&#x3C;/b>&#x3C;br/>━━━━━━━━&#x3C;br/>Zero ∇&#x3C;br/>No learning"]
        
        A1 ~~~ A2 ~~~ A3
    end
    
    Arrow["&#x3C;b>Add baseline&#x3C;/b>&#x3C;br/>V(s) = 97&#x3C;br/>━━━━━━━━&#x3C;br/>Relative signal&#x3C;br/>reduces variance"]
    
    subgraph Relative["&#x3C;b>With Baseline&#x3C;/b>&#x3C;br/>Advantage = G − V(s)"]
        direction TB
        B1["&#x3C;b>A = +3&#x3C;/b>&#x3C;br/>━━━━━━━━&#x3C;br/>Small positive ∇&#x3C;br/>Slightly better"]
        B2["&#x3C;b>A = −2&#x3C;/b>&#x3C;br/>━━━━━━━━&#x3C;br/>Small negative ∇&#x3C;br/>Slightly worse"]
        B3["&#x3C;b>A = −97&#x3C;/b>&#x3C;br/>━━━━━━━━&#x3C;br/>Large negative ∇&#x3C;br/>Much worse"]
        
        B1 ~~~ B2 ~~~ B3
    end
    
    Absolute --> Arrow --> Relative
    
    style A1 stroke:#f59e0b,stroke-width:2px
    style A2 stroke:#f59e0b,stroke-width:2px
    style A3 stroke:#64748b,stroke-width:2px
    
    style B1 stroke:#10b981,stroke-width:2px
    style B2 stroke:#ef4444,stroke-width:2px
    style B3 stroke:#ef4444,stroke-width:2.5px
    
    style Arrow stroke:#06b6d4,stroke-width:2px
    
    style Absolute stroke:#f59e0b,stroke-width:1.5px
    style Relative stroke:#10b981,stroke-width:1.5px</div>
<blockquote>
<p><strong>Key Insight:</strong> The baseline transforms absolute returns into relative performance measures. This is the foundation of advantage-based methods—and explains why GRPO’s group normalization works.</p>
</blockquote>
<hr>
<h2 id="value-functions-and-bellman-equations">Value Functions and Bellman Equations</h2>
<p>To use <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.22222em;">V</span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mclose">)</span></span></span></span> as a baseline, we need to estimate it. Value functions are central to RL and worth understanding deeply.</p>
<h3 id="state-value-function">State Value Function</h3>
<p>The <strong>state value function</strong> <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>V</mi><mi>π</mi></msup><mo stretchy="false">(</mo><mi>s</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">V^\pi(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"><span class="mord mathnormal" style="margin-right:0.22222em;">V</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><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.03588em;">π</span></span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mclose">)</span></span></span></span> is the expected return starting from state <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> and following policy <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.03588em;">π</span></span></span></span>:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>V</mi><mi>π</mi></msup><mo stretchy="false">(</mo><mi>s</mi><mo stretchy="false">)</mo><mo>=</mo><msub><mi mathvariant="double-struck">E</mi><mi>π</mi></msub><mrow><mo fence="true">[</mo><msubsup><mo>∑</mo><mrow><mi>k</mi><mo>=</mo><mn>0</mn></mrow><mi mathvariant="normal">∞</mi></msubsup><msup><mi>γ</mi><mi>k</mi></msup><msub><mi>r</mi><mrow><mi>t</mi><mo>+</mo><mi>k</mi></mrow></msub><mtext> </mtext><mo fence="false" stretchy="true" minsize="1.8em" maxsize="1.8em">∣</mo><mtext> </mtext><msub><mi>s</mi><mi>t</mi></msub><mo>=</mo><mi>s</mi><mo fence="true">]</mo></mrow></mrow><annotation encoding="application/x-tex">V^\pi(s) = \mathbb{E}_\pi\left[\sum_{k=0}^{\infty} \gamma^k r_{t+k} \,\Big|\, s_t = s\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.22222em;">V</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><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.03588em;">π</span></span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">s</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.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.03588em;">π</span></span></span></span><span 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="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size2">[</span></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.8043em;"><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.03148em;">k</span><span class="mrel mtight">=</span><span class="mord mtight">0</span></span></span></span><span style="top:-3.2029em;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 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 mathnormal" style="margin-right:0.05556em;">γ</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.03148em;">k</span></span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">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 mtight"><span class="mord mathnormal mtight">t</span><span class="mbin mtight">+</span><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</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="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.333em;height:1.800em;"><svg xmlns="http://www.w3.org/2000/svg" width="0.333em" height="1.800em" viewBox="0 0 333 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"></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">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="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal">s</span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size2">]</span></span></span></span></span></span></p>
<p>This answers: “How good is it to be in state <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>?”</p>
<h3 id="action-value-function">Action Value Function</h3>
<p>The <strong>action value function</strong> (or Q-function) <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>Q</mi><mi>π</mi></msup><mo stretchy="false">(</mo><mi>s</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">Q^\pi(s, a)</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">Q</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><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.03588em;">π</span></span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</span><span class="mclose">)</span></span></span></span> is the expected return starting from state <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>, taking action <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.4306em;"></span><span class="mord mathnormal">a</span></span></span></span>, then following policy <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.03588em;">π</span></span></span></span>:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>Q</mi><mi>π</mi></msup><mo stretchy="false">(</mo><mi>s</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo><mo>=</mo><msub><mi mathvariant="double-struck">E</mi><mi>π</mi></msub><mrow><mo fence="true">[</mo><msubsup><mo>∑</mo><mrow><mi>k</mi><mo>=</mo><mn>0</mn></mrow><mi mathvariant="normal">∞</mi></msubsup><msup><mi>γ</mi><mi>k</mi></msup><msub><mi>r</mi><mrow><mi>t</mi><mo>+</mo><mi>k</mi></mrow></msub><mtext> </mtext><mo fence="false" stretchy="true" minsize="1.8em" maxsize="1.8em">∣</mo><mtext> </mtext><msub><mi>s</mi><mi>t</mi></msub><mo>=</mo><mi>s</mi><mo separator="true">,</mo><msub><mi>a</mi><mi>t</mi></msub><mo>=</mo><mi>a</mi><mo fence="true">]</mo></mrow></mrow><annotation encoding="application/x-tex">Q^\pi(s, a) = \mathbb{E}_\pi\left[\sum_{k=0}^{\infty} \gamma^k r_{t+k} \,\Big|\, s_t = s, a_t = a\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">Q</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><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.03588em;">π</span></span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</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.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.03588em;">π</span></span></span></span><span 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="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size2">[</span></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.8043em;"><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.03148em;">k</span><span class="mrel mtight">=</span><span class="mord mtight">0</span></span></span></span><span style="top:-3.2029em;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 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 mathnormal" style="margin-right:0.05556em;">γ</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.03148em;">k</span></span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">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 mtight"><span class="mord mathnormal mtight">t</span><span class="mbin mtight">+</span><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</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="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.333em;height:1.800em;"><svg xmlns="http://www.w3.org/2000/svg" width="0.333em" height="1.800em" viewBox="0 0 333 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"></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">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="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal">s</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></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 mathnormal">a</span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size2">]</span></span></span></span></span></span></p>
<p>This answers: “How good is it to take action <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.4306em;"></span><span class="mord mathnormal">a</span></span></span></span> in state <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>?”</p>
<h3 id="relationship-between-v-and-q">Relationship Between V and Q</h3>
<p>The value function is the expected Q-value under the policy:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>V</mi><mi>π</mi></msup><mo stretchy="false">(</mo><mi>s</mi><mo stretchy="false">)</mo><mo>=</mo><msub><mi mathvariant="double-struck">E</mi><mrow><mi>a</mi><mo>∼</mo><mi>π</mi></mrow></msub><mo stretchy="false">[</mo><msup><mi>Q</mi><mi>π</mi></msup><mo stretchy="false">(</mo><mi>s</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo><mo stretchy="false">]</mo><mo>=</mo><msub><mo>∑</mo><mi>a</mi></msub><mi>π</mi><mo stretchy="false">(</mo><mi>a</mi><mi mathvariant="normal">∣</mi><mi>s</mi><mo stretchy="false">)</mo><msup><mi>Q</mi><mi>π</mi></msup><mo stretchy="false">(</mo><mi>s</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">V^\pi(s) = \mathbb{E}_{a \sim \pi}[Q^\pi(s, a)] = \sum_a \pi(a|s) Q^\pi(s, a)</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.22222em;">V</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><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.03588em;">π</span></span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">s</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 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 mathnormal mtight">a</span><span class="mrel mtight">∼</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">π</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">Q</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><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.03588em;">π</span></span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</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.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.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 mathnormal mtight">a</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 mathnormal" style="margin-right:0.03588em;">π</span><span class="mopen">(</span><span class="mord mathnormal">a</span><span class="mord">∣</span><span class="mord mathnormal">s</span><span class="mclose">)</span><span class="mord"><span class="mord mathnormal">Q</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><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.03588em;">π</span></span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</span><span class="mclose">)</span></span></span></span></p>
<h3 id="the-bellman-equation">The Bellman Equation</h3>
<p>Value functions satisfy a recursive relationship called the <strong>Bellman equation</strong>:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>V</mi><mi>π</mi></msup><mo stretchy="false">(</mo><mi>s</mi><mo stretchy="false">)</mo><mo>=</mo><msub><mi mathvariant="double-struck">E</mi><mrow><mi>a</mi><mo>∼</mo><mi>π</mi><mo separator="true">,</mo><msup><mi>s</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo>∼</mo><mi>P</mi></mrow></msub><mrow><mo fence="true">[</mo><mi>r</mi><mo stretchy="false">(</mo><mi>s</mi><mo separator="true">,</mo><mi>a</mi><mo separator="true">,</mo><msup><mi>s</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>+</mo><mi>γ</mi><msup><mi>V</mi><mi>π</mi></msup><mo stretchy="false">(</mo><msup><mi>s</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo fence="true">]</mo></mrow></mrow><annotation encoding="application/x-tex">V^\pi(s) = \mathbb{E}_{a \sim \pi, s' \sim P}\left[r(s, a, s') + \gamma V^\pi(s')\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.22222em;">V</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><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.03588em;">π</span></span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">s</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.038em;vertical-align:-0.2861em;"></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">a</span><span class="mrel mtight">∼</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">π</span><span class="mpunct mtight">,</span><span class="mord mtight"><span class="mord mathnormal mtight">s</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 mathnormal mtight" style="margin-right:0.13889em;">P</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><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal">s</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="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 mathnormal" style="margin-right:0.05556em;">γ</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.22222em;">V</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><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.03588em;">π</span></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"><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="mclose">)</span><span class="mclose delimcenter" style="top:0em;">]</span></span></span></span></span></p>
<p>This says: “The value of a state equals the expected immediate reward plus the discounted value of the next state.”</p>
<p>For Q-functions:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>Q</mi><mi>π</mi></msup><mo stretchy="false">(</mo><mi>s</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo><mo>=</mo><msub><mi mathvariant="double-struck">E</mi><mrow><msup><mi>s</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo>∼</mo><mi>P</mi></mrow></msub><mrow><mo fence="true">[</mo><mi>r</mi><mo stretchy="false">(</mo><mi>s</mi><mo separator="true">,</mo><mi>a</mi><mo separator="true">,</mo><msup><mi>s</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>+</mo><mi>γ</mi><msub><mo>∑</mo><msup><mi>a</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup></msub><mi>π</mi><mo stretchy="false">(</mo><msup><mi>a</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mi mathvariant="normal">∣</mi><msup><mi>s</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><msup><mi>Q</mi><mi>π</mi></msup><mo stretchy="false">(</mo><msup><mi>s</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo separator="true">,</mo><msup><mi>a</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo fence="true">]</mo></mrow></mrow><annotation encoding="application/x-tex">Q^\pi(s, a) = \mathbb{E}_{s' \sim P}\left[r(s, a, s') + \gamma \sum_{a'} \pi(a'|s') Q^\pi(s', a')\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">Q</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><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.03588em;">π</span></span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</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.0516em;vertical-align:-0.2997em;"></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 mtight"><span class="mord mathnormal mtight">s</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 mathnormal mtight" style="margin-right:0.13889em;">P</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="minner"><span class="mopen delimcenter" style="top:0em;">[</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal">s</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="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 mathnormal" style="margin-right:0.05556em;">γ</span><span class="mspace" style="margin-right:0.1667em;"></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.1783em;"><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">a</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></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 mathnormal" style="margin-right:0.03588em;">π</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">a</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="mord">∣</span><span class="mord"><span class="mord mathnormal">s</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="mclose">)</span><span class="mord"><span class="mord mathnormal">Q</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><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.03588em;">π</span></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"><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="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal">a</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="mclose">)</span><span class="mclose delimcenter" style="top:0em;">]</span></span></span></span></span></p>
<h3 id="why-bellman-equations-matter">Why Bellman Equations Matter</h3>
<p>The Bellman equation enables <strong>bootstrapping</strong>: we can estimate <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.22222em;">V</span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mclose">)</span></span></span></span> using our current estimate of <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><msup><mi>s</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><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:1.0019em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.22222em;">V</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">s</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="mclose">)</span></span></span></span>, without waiting for the episode to end.</p>
<p><strong>Monte Carlo:</strong> Wait for episode end, compute <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>G</mi><mi>t</mi></msub><mo>=</mo><msub><mo>∑</mo><mi>k</mi></msub><msup><mi>γ</mi><mi>k</mi></msup><msub><mi>r</mi><mrow><mi>t</mi><mo>+</mo><mi>k</mi></mrow></msub></mrow><annotation encoding="application/x-tex">G_t = \sum_k \gamma^k r_{t+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">G</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:1.1488em;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.03148em;">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 mathnormal" style="margin-right:0.05556em;">γ</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.03148em;">k</span></span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">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 mtight"><span class="mord mathnormal mtight">t</span><span class="mbin mtight">+</span><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</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>, update <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><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo><mo>←</mo><mi>V</mi><mo stretchy="false">(</mo><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo><mo>+</mo><mi>α</mi><mo stretchy="false">(</mo><msub><mi>G</mi><mi>t</mi></msub><mo>−</mo><mi>V</mi><mo stretchy="false">(</mo><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">V(s_t) \leftarrow V(s_t) + \alpha(G_t - V(s_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.22222em;">V</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.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.22222em;">V</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:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0037em;">α</span><span class="mopen">(</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.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.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.22222em;">V</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></p>
<p><strong>Temporal Difference (TD):</strong> After one step, update using <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><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo><mo>←</mo><mi>V</mi><mo stretchy="false">(</mo><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo><mo>+</mo><mi>α</mi><mo stretchy="false">(</mo><msub><mi>r</mi><mi>t</mi></msub><mo>+</mo><mi>γ</mi><mi>V</mi><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><mi>V</mi><mo stretchy="false">(</mo><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">V(s_t) \leftarrow V(s_t) + \alpha(r_t + \gamma V(s_{t+1}) - V(s_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.22222em;">V</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.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.22222em;">V</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:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0037em;">α</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">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.22222em;">γV</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:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.22222em;">V</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></p>
<p>TD learning has lower variance (one-step update vs. full episode) but introduces bias (using an estimate <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><msub><mi>s</mi><mrow><mi>t</mi><mo>+</mo><mn>1</mn></mrow></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">V(s_{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 mathnormal" style="margin-right:0.22222em;">V</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></span></span> instead of the true expected return).</p>
<h3 id="td-error">TD Error</h3>
<p>The <strong>TD error</strong> is the difference between our estimate and the bootstrapped target:</p>
<p><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><msub><mi>r</mi><mi>t</mi></msub><mo>+</mo><mi>γ</mi><mi>V</mi><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><mi>V</mi><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(s_{t+1}) - V(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.03785em;">δ</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.02778em;">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.22222em;">γV</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:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.22222em;">V</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></p>
<p>If our value function is accurate, <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><msub><mi>δ</mi><mi>t</mi></msub><mo stretchy="false">]</mo><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">\mathbb{E}[\delta_t] = 0</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"><span class="mord mathnormal" style="margin-right:0.03785em;">δ</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="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.6444em;"></span><span class="mord">0</span></span></span></span>. The TD error measures how “surprised” we are by what happened.</p>
<blockquote>
<p><strong>Key Insight:</strong> Bellman equations let us learn value functions incrementally, updating after each step rather than waiting for episodes to complete. This is especially valuable for long episodes like LLM generation.</p>
</blockquote>
<hr>
<h2 id="advantage-functions-the-key-insight">Advantage Functions: The Key Insight</h2>
<p>The <strong>advantage function</strong> combines V and Q to measure how much better an action is than average:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>A</mi><mi>π</mi></msup><mo stretchy="false">(</mo><mi>s</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo><mo>=</mo><msup><mi>Q</mi><mi>π</mi></msup><mo stretchy="false">(</mo><mi>s</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo><mo>−</mo><msup><mi>V</mi><mi>π</mi></msup><mo stretchy="false">(</mo><mi>s</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">A^\pi(s, a) = Q^\pi(s, a) - V^\pi(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"><span class="mord mathnormal">A</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><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.03588em;">π</span></span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</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">Q</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><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.03588em;">π</span></span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</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="mord"><span class="mord mathnormal" style="margin-right:0.22222em;">V</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><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.03588em;">π</span></span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mclose">)</span></span></span></span></p>
<h3 id="interpretation">Interpretation</h3>
<ul>
<li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi><mo stretchy="false">(</mo><mi>s</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo><mo>></mo><mn>0</mn></mrow><annotation encoding="application/x-tex">A(s, a) > 0</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">A</span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</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.6444em;"></span><span class="mord">0</span></span></span></span>: Action <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.4306em;"></span><span class="mord mathnormal">a</span></span></span></span> is better than the average action in state <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></li>
<li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi><mo stretchy="false">(</mo><mi>s</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo><mo>&#x3C;</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">A(s, a) &#x3C; 0</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">A</span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&#x3C;</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>: Action <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.4306em;"></span><span class="mord mathnormal">a</span></span></span></span> is worse than average</li>
<li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi><mo stretchy="false">(</mo><mi>s</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">A(s, a) = 0</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">A</span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</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.6444em;"></span><span class="mord">0</span></span></span></span>: Action <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.4306em;"></span><span class="mord mathnormal">a</span></span></span></span> is exactly average</li>
</ul>
<p>The advantage answers the crucial question: “Is this action better or worse than what I’d typically do?”</p>
<h3 id="why-advantages-for-policy-gradients">Why Advantages for Policy Gradients?</h3>
<p>Using advantages in the policy gradient gives:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="normal">∇</mi><mi>θ</mi></msub><mi>J</mi><mo stretchy="false">(</mo><mi>θ</mi><mo stretchy="false">)</mo><mo>=</mo><mi mathvariant="double-struck">E</mi><mrow><mo fence="true">[</mo><msub><mo>∑</mo><mi>t</mi></msub><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>a</mi><mi>t</mi></msub><mi mathvariant="normal">∣</mi><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo><mo>⋅</mo><msup><mi>A</mi><mi>π</mi></msup><mo stretchy="false">(</mo><msub><mi>s</mi><mi>t</mi></msub><mo separator="true">,</mo><msub><mi>a</mi><mi>t</mi></msub><mo stretchy="false">)</mo><mo fence="true">]</mo></mrow></mrow><annotation encoding="application/x-tex">\nabla_\theta J(\theta) = \mathbb{E}\left[\sum_t \nabla_\theta \log \pi_\theta(a_t|s_t) \cdot A^\pi(s_t, a_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">∇</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.02778em;">θ</span></span></span></span><span 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 mathnormal" style="margin-right:0.09618em;">J</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.02778em;">θ</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.0497em;vertical-align:-0.2997em;"></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="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.1308em;"><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">t</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">∇</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.02778em;">θ</span></span></span></span><span 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.01389em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">π</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.02778em;">θ</span></span></span></span><span 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="mord">∣</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 class="mord"><span class="mord mathnormal">A</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><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.03588em;">π</span></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="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></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="mclose">)</span><span class="mclose delimcenter" style="top:0em;">]</span></span></span></span></span></p>
<p>This is optimal for variance reduction because:</p>
<ol>
<li><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><mi>a</mi></msub><mo stretchy="false">[</mo><mi>A</mi><mo stretchy="false">(</mo><mi>s</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo><mo stretchy="false">]</mo><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">\mathbb{E}_a[A(s, a)] = 0</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">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 mathnormal mtight">a</span></span></span></span><span 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">A</span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</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.6444em;"></span><span class="mord">0</span></span></span></span> by construction (advantages are centered)</li>
<li>The signal focuses on <strong>relative</strong> action quality</li>
<li>It separates “was the action good?” from “was the state good?”</li>
</ol>
<h3 id="estimating-advantages">Estimating Advantages</h3>
<p>We rarely know <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>A</mi><mi>π</mi></msup></mrow><annotation encoding="application/x-tex">A^\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"><span class="mord mathnormal">A</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><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.03588em;">π</span></span></span></span></span></span></span></span></span></span></span> exactly. Common estimators:</p>
<p><strong>1. Monte Carlo Advantage:</strong>
<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><mo>=</mo><msub><mi>G</mi><mi>t</mi></msub><mo>−</mo><mi>V</mi><mo stretchy="false">(</mo><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\hat{A}_t = G_t - V(s_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 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">G</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.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.22222em;">V</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>
Use actual returns minus estimated value. Unbiased but high variance.</p>
<p><strong>2. TD Advantage:</strong>
<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><mo>=</mo><msub><mi>r</mi><mi>t</mi></msub><mo>+</mo><mi>γ</mi><mi>V</mi><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><mi>V</mi><mo stretchy="false">(</mo><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo><mo>=</mo><msub><mi>δ</mi><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">\hat{A}_t = r_t + \gamma V(s_{t+1}) - V(s_t) = \delta_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 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.02778em;">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.22222em;">γV</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:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.22222em;">V</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.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"><span class="mord mathnormal" style="margin-right:0.03785em;">δ</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></span></span>
One-step TD error. Low variance but biased.</p>
<p><strong>3. Generalized Advantage Estimation (GAE):</strong>
<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mover accent="true"><mi>A</mi><mo>^</mo></mover><mi>t</mi><mrow><mtext>GAE</mtext><mo stretchy="false">(</mo><mi>γ</mi><mo separator="true">,</mo><mi>λ</mi><mo stretchy="false">)</mo></mrow></msubsup><mo>=</mo><msubsup><mo>∑</mo><mrow><mi>k</mi><mo>=</mo><mn>0</mn></mrow><mi mathvariant="normal">∞</mi></msubsup><mo stretchy="false">(</mo><mi>γ</mi><mi>λ</mi><msup><mo stretchy="false">)</mo><mi>k</mi></msup><msub><mi>δ</mi><mrow><mi>t</mi><mo>+</mo><mi>k</mi></mrow></msub></mrow><annotation encoding="application/x-tex">\hat{A}_t^{\text{GAE}(\gamma, \lambda)} = \sum_{k=0}^{\infty} (\gamma\lambda)^k \delta_{t+k}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.2906em;vertical-align:-0.2458em;"></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:1.0448em;"><span style="top:-2.4542em;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 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="mord text mtight"><span class="mord mtight">GAE</span></span><span class="mopen mtight">(</span><span class="mord mathnormal mtight" style="margin-right:0.05556em;">γ</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">λ</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.2458em;"><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.1488em;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.8043em;"><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.03148em;">k</span><span class="mrel mtight">=</span><span class="mord mtight">0</span></span></span></span><span style="top:-3.2029em;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 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="mopen">(</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.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.03148em;">k</span></span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03785em;">δ</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.03148em;">k</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>
Interpolates between MC (<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>) and 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">\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>). We’ll explore GAE deeply in Part 2.</p>
<h3 id="properties-of-good-advantage-estimators">Properties of Good Advantage Estimators</h3>





















<table><thead><tr><th>Property</th><th>Description</th></tr></thead><tbody><tr><td><strong>Low bias</strong></td><td><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><mover accent="true"><mi>A</mi><mo>^</mo></mover><mo stretchy="false">]</mo><mo>≈</mo><mi>A</mi></mrow><annotation encoding="application/x-tex">\mathbb{E}[\hat{A}] \approx A</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.1968em;vertical-align:-0.25em;"></span><span class="mord mathbb">E</span><span class="mopen">[</span><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="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.6833em;"></span><span class="mord mathnormal">A</span></span></span></span> — estimates are accurate on average</td></tr><tr><td><strong>Low variance</strong></td><td>Estimates don’t fluctuate wildly between samples</td></tr><tr><td><strong>Computational efficiency</strong></td><td>Can be computed without excessive overhead</td></tr></tbody></table>
<p>There’s typically a bias-variance tradeoff: lower variance estimators introduce more bias (by relying on learned value functions).</p>
<blockquote>
<p><strong>Key Insight:</strong> The advantage function is the “right” quantity for policy gradients. It tells us exactly what we want to know: was this action better or worse than average? All modern policy optimization methods (PPO, GRPO, etc.) are fundamentally about estimating advantages well.</p>
</blockquote>
<hr>
<h2 id="actor-critic-methods">Actor-Critic Methods</h2>
<p><strong>Actor-critic</strong> methods combine policy gradients (the “actor”) with learned value functions (the “critic”). This architecture underlies PPO and most modern RL algorithms.</p>
<h3 id="the-two-components">The Two Components</h3>
<p><strong>Actor:</strong> 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><mo stretchy="false">(</mo><mi>a</mi><mi mathvariant="normal">∣</mi><mi>s</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\pi_\theta(a|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"><span class="mord mathnormal" style="margin-right:0.03588em;">π</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.02778em;">θ</span></span></span></span><span 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">a</span><span class="mord">∣</span><span class="mord mathnormal">s</span><span class="mclose">)</span></span></span></span> that selects actions</p>
<ul>
<li>Parametrized by <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.02778em;">θ</span></span></span></span></li>
<li>Optimized via policy gradients</li>
<li>Goal: maximize expected return</li>
</ul>
<p><strong>Critic:</strong> The value function <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><mi>s</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">V_\psi(s)</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.22222em;">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.03588em;">ψ</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">s</span><span class="mclose">)</span></span></span></span> or <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>Q</mi><mi>ψ</mi></msub><mo stretchy="false">(</mo><mi>s</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">Q_\psi(s, a)</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">Q</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.03588em;">ψ</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">s</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</span><span class="mclose">)</span></span></span></span></p>
<ul>
<li>Parametrized by <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">\psi</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.03588em;">ψ</span></span></span></span></li>
<li>Optimized via Bellman equation (TD learning)</li>
<li>Goal: accurately estimate expected returns</li>
</ul>
<h3 id="why-two-networks">Why Two Networks?</h3>
<p>Each component benefits the other:</p>
<ol>
<li><strong>Critic helps actor:</strong> Provides baseline/advantage estimates for lower-variance policy gradients</li>
<li><strong>Actor helps critic:</strong> Generates on-policy data for value function training</li>
</ol>
<div class="mermaid">
graph TB
    subgraph AC["&#x3C;b>Actor-Critic Architecture&#x3C;/b>"]
        direction TB
        
        S["&#x3C;b>State s_t&#x3C;/b>"]
        
        subgraph Networks["Neural Networks"]
            direction LR
            Actor["&#x3C;b>Actor π_θ&#x3C;/b>&#x3C;br/>━━━━━━━━&#x3C;br/>Policy network&#x3C;br/>Outputs P(a|s)"]
            Critic["&#x3C;b>Critic V_ψ&#x3C;/b>&#x3C;br/>━━━━━━━━&#x3C;br/>Value network&#x3C;br/>Outputs V(s)"]
        end
        
        subgraph Environment["Environment Interaction"]
            direction LR
            Action["Action&#x3C;br/>&#x3C;i>a_t ~ π(·|s)&#x3C;/i>"]
            Env["Environment"]
            Reward["Reward&#x3C;br/>&#x3C;i>r_t&#x3C;/i>"]
            NextS["Next State&#x3C;br/>&#x3C;i>s_t+1&#x3C;/i>"]
        end
        
        Adv["&#x3C;b>Advantage&#x3C;/b>&#x3C;br/>━━━━━━━━&#x3C;br/>A = r_t + γV(s_t+1) − V(s_t)"]
        
        subgraph Updates["Parameter Updates"]
            direction LR
            ActorUpdate["&#x3C;b>Actor Update&#x3C;/b>&#x3C;br/>━━━━━━━━&#x3C;br/>θ ← θ + α∇_θ log π · A"]
            CriticUpdate["&#x3C;b>Critic Update&#x3C;/b>&#x3C;br/>━━━━━━━━&#x3C;br/>ψ ← ψ − α∇_ψ (V − target)²"]
        end
    end
    
    S --> Actor
    S --> Critic
    
    Actor --> Action
    Action --> Env
    Env --> Reward
    Env --> NextS
    
    Critic --> Adv
    Reward --> Adv
    NextS -.->|"V(s_t+1)"| Adv
    
    Adv --> ActorUpdate
    Adv --> CriticUpdate
    
    ActorUpdate -.->|"update θ"| Actor
    CriticUpdate -.->|"update ψ"| Critic
    
    style S stroke:#8b5cf6,stroke-width:2px
    
    style Actor stroke:#10b981,stroke-width:2.5px
    style Critic stroke:#06b6d4,stroke-width:2.5px
    
    style Action stroke:#64748b,stroke-width:1.5px
    style Env stroke:#64748b,stroke-width:1.5px
    style Reward stroke:#64748b,stroke-width:1.5px
    style NextS stroke:#64748b,stroke-width:1.5px
    
    style Adv stroke:#f59e0b,stroke-width:2.5px
    
    style ActorUpdate stroke:#10b981,stroke-width:2px
    style CriticUpdate stroke:#06b6d4,stroke-width:2px
    
    style Networks stroke:#475569,stroke-width:1px
    style Environment stroke:#475569,stroke-width:1px
    style Updates stroke:#475569,stroke-width:1px
    style AC stroke:#334155,stroke-width:1px</div>
<h3 id="the-a2c-algorithm">The A2C Algorithm</h3>
<p><strong>Advantage Actor-Critic (A2C)</strong> is a foundational actor-critic algorithm:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>Algorithm: A2C</span></span>
<span class="line"><span></span></span>
<span class="line"><span>For each batch of experience:</span></span>
<span class="line"><span>    1. Collect trajectories using current policy π_θ</span></span>
<span class="line"><span>    </span></span>
<span class="line"><span>    2. Compute advantages:</span></span>
<span class="line"><span>        For each timestep t:</span></span>
<span class="line"><span>            δ_t = r_t + γV_ψ(s_{t+1}) - V_ψ(s_t)  # TD error</span></span>
<span class="line"><span>            (Or use GAE for multi-step advantages)</span></span>
<span class="line"><span>    </span></span>
<span class="line"><span>    3. Update critic (minimize value loss):</span></span>
<span class="line"><span>        L_critic = Σ_t (V_ψ(s_t) - G_t)²  # Or TD targets</span></span>
<span class="line"><span>        ψ ← ψ - α_critic ∇_ψ L_critic</span></span>
<span class="line"><span>    </span></span>
<span class="line"><span>    4. Update actor (maximize policy objective):</span></span>
<span class="line"><span>        L_actor = Σ_t log π_θ(a_t|s_t) · Â_t</span></span>
<span class="line"><span>        θ ← θ + α_actor ∇_θ L_actor</span></span>
<span class="line"><span></span></span></code></pre>
<h3 id="on-policy-vs-off-policy">On-Policy vs. Off-Policy</h3>
<p><strong>On-policy</strong> methods (A2C, PPO, GRPO) require data from the current policy:</p>
<ul>
<li>Samples 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.03588em;">π</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.02778em;">θ</span></span></span></span><span 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> used to update <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.03588em;">π</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.02778em;">θ</span></span></span></span><span 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>
<li>Must discard data after each update</li>
<li>Simpler theory but less sample efficient</li>
</ul>
<p><strong>Off-policy</strong> methods (DQN, SAC) can reuse old data:</p>
<ul>
<li>Samples from any policy can update <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.03588em;">π</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.02778em;">θ</span></span></span></span><span 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>
<li>Requires importance sampling or replay buffers</li>
<li>More sample efficient but more complex</li>
</ul>
<p>LLM fine-tuning typically uses on-policy methods because:</p>
<ol>
<li>The state space (text) is too large for replay buffers</li>
<li>On-policy methods are more stable for large models</li>
<li>Sample efficiency matters less when each “sample” is a full text generation</li>
</ol>
<h3 id="challenges-in-actor-critic">Challenges in Actor-Critic</h3>
<ol>
<li><strong>Training instability:</strong> Actor and critic must improve together; if one diverges, both fail</li>
<li><strong>Sample efficiency:</strong> On-policy methods need fresh samples after each update</li>
<li><strong>Hyperparameter sensitivity:</strong> Learning rates, advantage estimation, entropy bonuses all interact</li>
<li><strong>Scale:</strong> For LLMs, the critic is another massive network to train</li>
</ol>
<blockquote>
<p><strong>Key Insight:</strong> Actor-critic methods are powerful but complex. The critic provides essential variance reduction, but at the cost of doubled model capacity and potential instability. This motivates GRPO’s approach of eliminating the critic entirely.</p>
</blockquote>
<hr>
<h2 id="from-general-rl-to-llm-fine-tuning">From General RL to LLM Fine-Tuning</h2>
<p>Let’s map everything we’ve learned to the specific setting of language model alignment.</p>
<h3 id="the-llm-rl-correspondence">The LLM-RL Correspondence</h3>





































<table><thead><tr><th>General RL</th><th>LLM Fine-Tuning</th></tr></thead><tbody><tr><td>State <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></td><td>Prompt + generated tokens <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>q</mi><mo separator="true">,</mo><msub><mi>o</mi><mrow><mo>&#x3C;</mo><mi>t</mi></mrow></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">(q, o_{&#x3C;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="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">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.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">&#x3C;</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></td></tr><tr><td>Action <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.4306em;"></span><span class="mord mathnormal">a</span></span></span></span></td><td>Next token <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>o</mi><mi>t</mi></msub><mo>∈</mo><mi mathvariant="script">V</mi></mrow><annotation encoding="application/x-tex">o_t \in \mathcal{V}</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">o</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.6833em;"></span><span class="mord mathcal" style="margin-right:0.08222em;">V</span></span></span></span></td></tr><tr><td>Policy $\pi(a</td><td>s)$</td></tr><tr><td>Transition $P(s’</td><td>s,a)$</td></tr><tr><td>Reward <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.6833em;"></span><span class="mord mathnormal" style="margin-right:0.00773em;">R</span></span></span></span></td><td>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><mo stretchy="false">(</mo><mi>q</mi><mo separator="true">,</mo><mi>o</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">r_\phi(q, o)</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.02778em;">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" style="margin-right:0.03588em;">q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">o</span><span class="mclose">)</span></span></span></span> (often sparse)</td></tr><tr><td>Episode</td><td>One complete generation</td></tr><tr><td>Value function <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.22222em;">V</span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mclose">)</span></span></span></span></td><td>Expected reward from partial generation</td></tr></tbody></table>
<h3 id="unique-challenges-for-llms">Unique Challenges for LLMs</h3>
<p><strong>1. Enormous Action Space</strong></p>
<p>Vocabulary sizes of 32K-128K tokens mean:</p>
<ul>
<li>Can’t enumerate all actions</li>
<li>Must use function approximation (the LLM itself)</li>
<li>Exploration is implicit in stochastic sampling</li>
</ul>
<p><strong>2. Sparse Rewards</strong></p>
<p>Typically, reward comes only at generation end:</p>
<ul>
<li><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><mn>0</mn></mrow><annotation encoding="application/x-tex">r_t = 0</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.02778em;">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.6444em;"></span><span class="mord">0</span></span></span></span> for <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi><mo>&#x3C;</mo><mi>T</mi></mrow><annotation encoding="application/x-tex">t &#x3C; T</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6542em;vertical-align:-0.0391em;"></span><span class="mord mathnormal">t</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&#x3C;</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">T</span></span></span></span></li>
<li><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><mi>R</mi><mo stretchy="false">(</mo><mi>q</mi><mo separator="true">,</mo><mi>o</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">r_T = R(q, o)</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.02778em;">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" style="margin-right:0.13889em;">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="mord mathnormal" style="margin-right:0.00773em;">R</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">o</span><span class="mclose">)</span></span></span></span> — the reward model score</li>
</ul>
<p>This makes credit assignment hard: which tokens were responsible for the reward?</p>
<p><strong>3. Variable-Length Episodes</strong></p>
<p>Generations can be 10 tokens or 1000 tokens:</p>
<ul>
<li>Can’t use fixed-horizon methods</li>
<li>Must handle EOS token properly</li>
<li>Padding and masking complexities</li>
</ul>
<p><strong>4. The Reference Model Constraint</strong></p>
<p>Unlike standard RL, we don’t want to maximize reward unconditionally. We want to improve while staying close to a reference model:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mrow><mi>max</mi><mo>⁡</mo></mrow><mi>θ</mi></msub><mi mathvariant="double-struck">E</mi><mo stretchy="false">[</mo><mi>R</mi><mo stretchy="false">(</mo><mi>q</mi><mo separator="true">,</mo><mi>o</mi><mo stretchy="false">)</mo><mo stretchy="false">]</mo><mo>−</mo><mi>β</mi><mo>⋅</mo><mtext>KL</mtext><mo stretchy="false">(</mo><msub><mi>π</mi><mi>θ</mi></msub><mi mathvariant="normal">∣</mi><mi mathvariant="normal">∣</mi><msub><mi>π</mi><mtext>ref</mtext></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\max_\theta \mathbb{E}[R(q, o)] - \beta \cdot \text{KL}(\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"><span class="mop">max</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.02778em;">θ</span></span></span></span><span 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 mathbb">E</span><span class="mopen">[</span><span class="mord mathnormal" style="margin-right:0.00773em;">R</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">o</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.05278em;">β</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 text"><span class="mord">KL</span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">π</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.02778em;">θ</span></span></span></span><span 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.03588em;">π</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></p>
<p>This prevents:</p>
<ul>
<li><strong>Reward hacking:</strong> Finding exploits in the reward model</li>
<li><strong>Mode collapse:</strong> Generating the same high-reward response always</li>
<li><strong>Capability loss:</strong> Forgetting useful behaviors from pretraining</li>
</ul>
<p><strong>5. Scale</strong></p>
<p>Training billion-parameter models requires:</p>
<ul>
<li>Massive memory for model weights, gradients, optimizer states</li>
<li>Distributed training across many GPUs</li>
<li>Careful numerical stability</li>
</ul>
<h3 id="the-rlhf-pipeline">The RLHF Pipeline</h3>
<div class="mermaid">
graph TB
    subgraph RLHF["RLHF Training Pipeline"]
        direction TB
        
        SFT[SFT Model&#x3C;br/>━━━━━━━━&#x3C;br/>Supervised fine-tuned&#x3C;br/>on demonstrations] 
        
        RM[Reward Model&#x3C;br/>━━━━━━━━&#x3C;br/>Trained on human&#x3C;br/>preference comparisons]
        
        RL[RL Training&#x3C;br/>━━━━━━━━&#x3C;br/>PPO / GRPO / etc.&#x3C;br/>Maximize reward]
        
        Final[Aligned Model&#x3C;br/>━━━━━━━━&#x3C;br/>Helpful, harmless,&#x3C;br/>honest]
        
        SFT -->|"Initialize&#x3C;br/>policy &#x26; reference"| RL
        RM -->|"Provides&#x3C;br/>reward signal"| RL
        RL -->|"Optimized&#x3C;br/>policy"| Final
    end
    
    style SFT stroke:#64748b,stroke-width:2px
    style RM stroke:#8b5cf6,stroke-width:2px
    style RL stroke:#10b981,stroke-width:2.5px
    style Final stroke:#06b6d4,stroke-width:2.5px
    
    style RLHF stroke:none</div>
<h3 id="whats-coming-next">What’s Coming Next</h3>
<p>With these foundations, we’re ready to understand <strong>PPO</strong> (Part 2):</p>
<ul>
<li>How clipping provides stable updates</li>
<li>How GAE estimates advantages</li>
<li>Why it requires four models</li>
<li>And why that’s a problem</li>
</ul>
<p>Then <strong>GRPO</strong> (Part 3) will show how to eliminate the critic by clever use of group statistics.</p>
<p>Finally, <strong>GDPO</strong> (Part 4) will address multi-reward settings where GRPO falls short.</p>
<hr>
<h2 id="key-takeaways">Key Takeaways</h2>
<h3 id="the-mdp-framework-1">The MDP Framework</h3>
<ul>
<li>RL formalizes sequential decisions as states, actions, rewards</li>
<li>Markov property: future depends only on current state</li>
<li>LLM generation maps naturally to this framework</li>
</ul>
<h3 id="policy-gradients">Policy Gradients</h3>
<ul>
<li>We can optimize expected reward using gradient ascent</li>
<li>The policy gradient theorem enables learning without knowing dynamics</li>
<li>REINFORCE is simple but high-variance</li>
</ul>
<h3 id="the-variance-problem-1">The Variance Problem</h3>
<ul>
<li>Raw policy gradients are too noisy for practical use</li>
<li>Baselines reduce variance without changing expected gradient</li>
<li>The optimal baseline is approximately the value function</li>
</ul>
<h3 id="value-functions">Value Functions</h3>
<ul>
<li><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.22222em;">V</span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mclose">)</span></span></span></span> = expected return from state <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></li>
<li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>Q</mi><mo stretchy="false">(</mo><mi>s</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">Q(s,a)</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">Q</span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</span><span class="mclose">)</span></span></span></span> = expected return from state <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> taking action <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.4306em;"></span><span class="mord mathnormal">a</span></span></span></span></li>
<li>Bellman equations enable bootstrapped learning</li>
</ul>
<h3 id="advantage-functions">Advantage Functions</h3>
<ul>
<li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi><mo stretchy="false">(</mo><mi>s</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo><mo>=</mo><mi>Q</mi><mo stretchy="false">(</mo><mi>s</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo><mo>−</mo><mi>V</mi><mo stretchy="false">(</mo><mi>s</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">A(s,a) = Q(s,a) - 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">A</span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</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">Q</span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</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="mord mathnormal" style="margin-right:0.22222em;">V</span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mclose">)</span></span></span></span> measures relative action quality</li>
<li>Advantages are centered: <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><mi>a</mi></msub><mo stretchy="false">[</mo><mi>A</mi><mo stretchy="false">(</mo><mi>s</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo><mo stretchy="false">]</mo><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">\mathbb{E}_a[A(s,a)] = 0</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">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 mathnormal mtight">a</span></span></span></span><span 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">A</span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</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.6444em;"></span><span class="mord">0</span></span></span></span></li>
<li><strong>This is the key quantity for policy optimization</strong></li>
</ul>
<h3 id="actor-critic">Actor-Critic</h3>
<ul>
<li>Actor (policy) + Critic (value function) work together</li>
<li>Critic provides variance reduction for actor updates</li>
<li>Doubles the model capacity required</li>
</ul>
<h3 id="llm-specific-considerations">LLM-Specific Considerations</h3>
<ul>
<li>Sparse rewards make credit assignment hard</li>
<li>Reference model constraint prevents reward hacking</li>
<li>Scale demands memory-efficient algorithms</li>
</ul>
<hr>
<h2 id="whats-next">What’s Next</h2>
<p>In <strong>Part 2: PPO for Language Models</strong>, we’ll see how Proximal Policy Optimization addresses many challenges:</p>
<ul>
<li>Trust regions for stable updates</li>
<li>GAE for flexible advantage estimation</li>
<li>Clipping for simplicity</li>
</ul>
<p>But we’ll also see PPO’s costs: the four-model architecture that strains GPU memory, and the complexity that makes implementation tricky. This sets the stage for GRPO’s elegant simplification.</p>
<hr>
<h2 id="further-reading">Further Reading</h2>
<p><strong>Foundational Papers:</strong></p>
<ul>
<li><a href="https://papers.nips.cc/paper/1713-policy-gradient-methods-for-reinforcement-learning-with-function-approximation">Policy Gradient Methods for Reinforcement Learning with Function Approximation</a> (Sutton et al., 1999)</li>
<li><a href="https://link.springer.com/article/10.1007/BF00992696">Simple Statistical Gradient-Following Algorithms for Connectionist Reinforcement Learning</a> (Williams, 1992 — REINFORCE)</li>
</ul>
<p><strong>Textbooks:</strong></p>
<ul>
<li><a href="http://incompleteideas.net/book/the-book-2nd.html">Reinforcement Learning: An Introduction</a> (Sutton &#x26; Barto, 2018) — The definitive reference</li>
<li><a href="https://arxiv.org/abs/1810.06339">Deep Reinforcement Learning</a> (Foundations and Trends survey)</li>
</ul>
<p><strong>LLM-Specific:</strong></p>
<ul>
<li><a href="https://arxiv.org/abs/2203.02155">Training language models to follow instructions with human feedback</a> (InstructGPT, Ouyang et al., 2022)</li>
<li><a href="https://arxiv.org/abs/2009.01325">Learning to summarize from human feedback</a> (Stiennon et al., 2020)</li>
</ul>]]></content:encoded><enclosure url="https://www.vitorsousa.com/_astro/gdpo-advantage-collapse.BHoPiJHq.png" length="756000" type="image/png"/></item><item><title>GRPO: Eliminating the Value Network</title><link>https://www.vitorsousa.com/blog/grpo/</link><guid isPermaLink="true">https://www.vitorsousa.com/blog/grpo/</guid><description>Group Relative Policy Optimization replaces PPO&apos;s learned value function with a simple insight: sample multiple outputs and use their relative rewards as advantages. 33% memory savings, simpler implementation, and the algorithm powering DeepSeek-R1.</description><pubDate>Tue, 03 Feb 2026 19:00:00 GMT</pubDate><content:encoded><![CDATA[<h1 id="contextual-bandit-theory-regret-bounds-and-exploration">Contextual Bandit Theory: Regret Bounds and Exploration</h1>
<p><strong>Part 2 of 5:</strong> Theory That Informs Practice</p>
<blockquote>
<p><strong>TL;DR:</strong> Contextual bandits minimize regret—cumulative opportunity cost versus the optimal policy. Optimal algorithms achieve O(√(dT log T)) regret for d-dimensional contexts. The exploration-exploitation tradeoff is fundamental: confidence bounds (UCB) and posterior sampling (Thompson) provide principled solutions. This math directly guides algorithm selection and hyperparameter tuning.</p>
</blockquote>
<p><strong>Reading time:</strong> ~18 minutes</p>
<hr>
<h2 id="introduction-why-math-matters-for-practice">Introduction: Why Math Matters for Practice</h2>
<p>You don’t need to derive regret bounds to deploy bandits in production. But understanding the mathematics directly informs practical decisions:</p>
<ul>
<li><strong>Algorithm selection:</strong> Why LinUCB for linear rewards? Why Thompson Sampling often wins empirically?</li>
<li><strong>Hyperparameter tuning:</strong> What does α control in LinUCB? How to set it?</li>
<li><strong>Debugging:</strong> Why is my bandit not converging? (Check if regret is sublinear)</li>
<li><strong>Feature engineering:</strong> Why does dimensionality hurt? (√d penalty in regret bounds)</li>
</ul>
<p>This post builds intuition for the theory that makes contextual bandits work.</p>
<hr>
<h2 id="the-contextual-bandit-problem-formal">The Contextual Bandit Problem (Formal)</h2>
<h3 id="setup">Setup</h3>
<p>At each round t = 1, 2, …, T:</p>
<ol>
<li><strong>Environment reveals context</strong> <strong>x</strong>ₜ ∈ <strong>X</strong> (context space)</li>
<li><strong>Agent chooses action</strong> <strong>a</strong>ₜ ∈ <strong>A</strong> from available action set</li>
<li><strong>Environment generates reward</strong> <strong>r</strong>ₜ ~ P(r | <strong>x</strong>ₜ, <strong>a</strong>ₜ) from unknown distribution</li>
<li><strong>Agent observes reward</strong> <strong>r</strong>ₜ for chosen action (but not rewards for unchosen actions)</li>
</ol>
<h3 id="goal">Goal</h3>
<p>Learn policy π: <strong>X</strong> → <strong>A</strong> that maximizes expected cumulative reward:</p>
<p><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>π</mi><mo stretchy="false">)</mo><mo>=</mo><mi mathvariant="double-struck">E</mi><mrow><mo fence="true">[</mo><msubsup><mo>∑</mo><mrow><mi>t</mi><mo>=</mo><mn>1</mn></mrow><mi>T</mi></msubsup><msub><mi>r</mi><mi>t</mi></msub><mo>∣</mo><mi>π</mi><mo fence="true">]</mo></mrow></mrow><annotation encoding="application/x-tex">V(\pi) = \mathbb{E}\left[\sum_{t=1}^{T} r_t \mid \pi \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 mathnormal" style="margin-right:0.22222em;">V</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">π</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 mathbb">E</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="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.9812em;"><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">t</span><span class="mrel mtight">=</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.2029em;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.13889em;">T</span></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 mathnormal" style="margin-right:0.02778em;">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 class="mord mathnormal" style="margin-right:0.03588em;">π</span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size2">]</span></span></span></span></span></span></p>
<h3 id="key-insight-partial-feedback">Key Insight: Partial Feedback</h3>
<p>We only observe rewards for actions we take. This is the <strong>partial feedback</strong> problem—we never know what would have happened if we’d chosen differently (the counterfactual outcome).</p>
<p><strong>Example:</strong></p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>Context: User is 25-year-old in San Francisco</span></span>
<span class="line"><span>Action chosen: Recommend Product A</span></span>
<span class="line"><span>Reward observed: Click (reward = 1)</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Unknown counterfactuals:</span></span>
<span class="line"><span>- What if we'd shown Product B? Maybe reward = 0 (worse)</span></span>
<span class="line"><span>- What if we'd shown Product C? Maybe reward = 1 (equally good)</span></span>
<span class="line"><span></span></span>
<span class="line"><span>We'll never know unless we try those actions in similar contexts.</span></span>
<span class="line"><span></span></span></code></pre>
<p>This partial observability is why exploration is necessary—we must try actions to learn their value.</p>
<hr>
<h2 id="regret-the-fundamental-performance-metric">Regret: The Fundamental Performance Metric</h2>
<p>Since we don’t know the true reward distributions, we can’t compute V(π) directly. Instead, we measure <strong>regret</strong>—how much worse we do compared to the optimal policy.</p>
<h3 id="definition">Definition</h3>
<p>Let π* be the optimal policy that always chooses the best action for each context. The <strong>regret</strong> after T rounds is:</p>
<p><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>T</mi><mo stretchy="false">)</mo><mo>=</mo><msubsup><mo>∑</mo><mrow><mi>t</mi><mo>=</mo><mn>1</mn></mrow><mi>T</mi></msubsup><msubsup><mi>r</mi><mi>t</mi><mo>∗</mo></msubsup><mo>−</mo><msubsup><mo>∑</mo><mrow><mi>t</mi><mo>=</mo><mn>1</mn></mrow><mi>T</mi></msubsup><msub><mi>r</mi><mi>t</mi></msub><mo>=</mo><mi>T</mi><mo>⋅</mo><mi>V</mi><mo stretchy="false">(</mo><msup><mi>π</mi><mo>∗</mo></msup><mo stretchy="false">)</mo><mo>−</mo><mi>V</mi><mo stretchy="false">(</mo><mi>π</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">R(T) = \sum_{t=1}^{T} r_t^* - \sum_{t=1}^{T} r_t = T \cdot V(\pi^*) - V(\pi)</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.00773em;">R</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.13889em;">T</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.2809em;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.9812em;"><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">t</span><span class="mrel mtight">=</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.2029em;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.13889em;">T</span></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 mathnormal" style="margin-right:0.02778em;">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6887em;"><span style="top:-2.453em;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 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 class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><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.2809em;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.9812em;"><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">t</span><span class="mrel mtight">=</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.2029em;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.13889em;">T</span></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 mathnormal" style="margin-right:0.02778em;">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.6833em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">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:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.22222em;">V</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">π</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="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="mord mathnormal" style="margin-right:0.22222em;">V</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">π</span><span class="mclose">)</span></span></span></span></p>
<p>where rₜ* is the reward we would have gotten from the optimal action at time t.</p>
<h3 id="interpretation">Interpretation</h3>
<p>Regret measures <strong>cumulative opportunity cost</strong>. If R(T) = 100 after 1000 rounds, we earned 100 less total reward than optimal.</p>
<p><strong>Important:</strong> We want algorithms that minimize regret growth.</p>
<h3 id="sublinear-regret">Sublinear Regret</h3>
<p>An algorithm is good if regret grows <strong>sublinearly</strong> in T:</p>
<p><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>T</mi><mo stretchy="false">)</mo><mo>=</mo><mi>o</mi><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">R(T) = o(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.00773em;">R</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.13889em;">T</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">o</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.13889em;">T</span><span class="mclose">)</span></span></span></span></p>
<p>Equivalently, average regret goes to zero:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mfrac><mrow><mi>R</mi><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo></mrow><mi>T</mi></mfrac><mo>→</mo><mn>0</mn><mtext> as </mtext><mi>T</mi><mo>→</mo><mi mathvariant="normal">∞</mi></mrow><annotation encoding="application/x-tex">\frac{R(T)}{T} \to 0 \text{ as } T \to \infty</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.355em;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: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 mathnormal mtight" style="margin-right:0.13889em;">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.485em;"><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.00773em;">R</span><span class="mopen mtight">(</span><span class="mord mathnormal mtight" style="margin-right:0.13889em;">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.345em;"><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.6833em;"></span><span class="mord">0</span><span class="mord text"><span class="mord"> as </span></span><span class="mord mathnormal" style="margin-right:0.13889em;">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:0.4306em;"></span><span class="mord">∞</span></span></span></span></p>
<p>This means we’re <strong>converging to optimal performance</strong>. Linear regret R(T) = Θ(T) would mean we’re making a constant mistake forever—we never learn.</p>
<h3 id="typical-regret-bounds">Typical Regret Bounds</h3>






























<table><thead><tr><th>Algorithm</th><th>Regret Bound</th><th>Interpretation</th></tr></thead><tbody><tr><td><strong>ε-greedy</strong></td><td>O(T^(2/3))</td><td>Suboptimal. Explores forever at fixed rate.</td></tr><tr><td><strong>UCB</strong></td><td>O(√(T log T))</td><td>Near-optimal for MAB. Confidence-based exploration.</td></tr><tr><td><strong>Thompson Sampling</strong></td><td>O(√T)</td><td>Optimal for many problems. Posterior sampling.</td></tr><tr><td><strong>LinUCB</strong></td><td>O(d√(T log T))</td><td>Linear contextual bandit. Pays √d factor for d dimensions.</td></tr></tbody></table>
<p>The √T bound is essentially <strong>optimal</strong>—you can’t do better in the worst case. This comes from a fundamental information-theoretic tradeoff.</p>
<h3 id="visual-comparison-of-regret-growth">Visual Comparison of Regret Growth</h3>
<div class="mermaid">graph TB
    subgraph Regret["Cumulative Regret Over Time"]
        direction LR
        
        Bad["Linear O&#x26;#40;T&#x26;#41;&#x3C;br/>━━━━━━━━&#x3C;br/>Never learns&#x3C;br/>Constant mistakes&#x3C;br/>❌ Unacceptable"]
        
        Subopt["Sublinear O&#x26;#40;T^2/3&#x26;#41;&#x3C;br/>━━━━━━━━&#x3C;br/>ε-greedy&#x3C;br/>Learning but slow&#x3C;br/>⚠️ Suboptimal"]
        
        Good["O&#x26;#40;√T log T&#x26;#41;&#x3C;br/>━━━━━━━━&#x3C;br/>UCB, LinUCB&#x3C;br/>Near-optimal&#x3C;br/>✓ Production-ready"]
        
        Optimal["O&#x26;#40;√T&#x26;#41;&#x3C;br/>━━━━━━━━&#x3C;br/>Thompson Sampling&#x3C;br/>Optimal bound&#x3C;br/>✓✓ Best theoretical"]
    end
    
    Bad -.worse.-> Subopt
    Subopt -.worse.-> Good
    Good -.worse.-> Optimal
    
    style Bad stroke:#ef4444,stroke-width:2px
    style Subopt stroke:#f59e0b,stroke-width:2px
    style Good stroke:#10b981,stroke-width:2px
    style Optimal stroke:#06b6d4,stroke-width:2.5px
    style Regret stroke:#64748b,stroke-width:2px
    
    linkStyle 0,1,2 stroke:#64748b,stroke-width:1px,stroke-dasharray:3</div>
<p><strong>Practical interpretation:</strong></p>
<p>After 10,000 rounds:</p>
<ul>
<li><strong>Linear O(T):</strong> ~10,000 cumulative regret (never learned)</li>
<li><strong>ε-greedy O(T^(2/3)):</strong> ~464 cumulative regret</li>
<li><strong>UCB O(√T log T):</strong> ~130 cumulative regret</li>
<li><strong>Thompson O(√T):</strong> ~100 cumulative regret</li>
</ul>
<p>The difference compounds over time. In production with millions of decisions, UCB/Thompson save significant value vs ε-greedy.</p>
<hr>
<h2 id="reward-models-assumptions-about-structure">Reward Models: Assumptions About Structure</h2>
<p>Real-world reward functions are complex. We make modeling assumptions to make learning tractable.</p>
<h3 id="linear-reward-model">Linear Reward Model</h3>
<p><strong>Assumption:</strong> Reward is a linear function of context features:</p>
<p><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>a</mi><mo stretchy="false">)</mo><mo>=</mo><msubsup><mi>θ</mi><mi>a</mi><mi>T</mi></msubsup><mi>x</mi><mo>+</mo><mi>ϵ</mi></mrow><annotation encoding="application/x-tex">r(x, a) = \theta_a^T x + \epsilon</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.02778em;">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">a</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.0883em;vertical-align:-0.247em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8413em;"><span style="top:-2.453em;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">a</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 mathnormal mtight" style="margin-right:0.13889em;">T</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></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.4306em;"></span><span class="mord mathnormal">ϵ</span></span></span></span></p>
<p>where:</p>
<ul>
<li><strong>θ</strong>ₐ ∈ ℝᵈ is a weight vector for action a</li>
<li><strong>x</strong> ∈ ℝᵈ is the context vector</li>
<li>ε ~ N(0, σ²) is noise</li>
</ul>
<p><strong>When this is reasonable:</strong></p>
<p>✅ Features are well-engineered (embeddings, hand-crafted)<br>
✅ Relationships are approximately linear in feature space<br>
✅ Interpretability is important (coefficients show feature importance)</p>
<p><strong>Limitations:</strong></p>
<p>❌ Misses complex nonlinear interactions<br>
❌ Requires good feature engineering upfront<br>
❌ Can underfit when true function is highly nonlinear</p>
<p><strong>Example: Content Recommendation</strong></p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>Context x: [user_age, user_tenure, content_freshness, </span></span>
<span class="line"><span>            content_popularity, topic_match]</span></span>
<span class="line"><span>Action a: show_content (binary)</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Linear model: </span></span>
<span class="line"><span>r(x, show) = θ₁·age + θ₂·tenure + θ₃·freshness + </span></span>
<span class="line"><span>             θ₄·popularity + θ₅·topic_match</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Interpretation: If θ₃ = 0.8, each unit increase in freshness </span></span>
<span class="line"><span>                adds 0.8 to expected reward</span></span>
<span class="line"><span></span></span></code></pre>
<h3 id="generalized-linear-models-glms">Generalized Linear Models (GLMs)</h3>
<p><strong>Assumption:</strong> Reward follows an exponential family distribution with linear predictor:</p>
<p><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>r</mi><mo>∣</mo><mi>x</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">]</mo><mo>=</mo><msup><mi>g</mi><mrow><mo>−</mo><mn>1</mn></mrow></msup><mo stretchy="false">(</mo><msubsup><mi>θ</mi><mi>a</mi><mi>T</mi></msubsup><mi>x</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\mathbb{E}[r \mid x, a] = g^{-1}(\theta_a^T 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 mathbb">E</span><span class="mopen">[</span><span class="mord mathnormal" style="margin-right:0.02778em;">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="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</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.0913em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">g</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"><span class="mord mtight">−</span><span class="mord mtight">1</span></span></span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8413em;"><span style="top:-2.453em;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">a</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 mathnormal mtight" style="margin-right:0.13889em;">T</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></span><span class="mord mathnormal">x</span><span class="mclose">)</span></span></span></span></p>
<p>where g is a link function.</p>
<p><strong>Common cases:</strong></p>
<p><strong>Logistic regression (binary rewards):</strong></p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="double-struck">P</mi><mo stretchy="false">(</mo><mi>r</mi><mo>=</mo><mn>1</mn><mo>∣</mo><mi>x</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo><mo>=</mo><mi>σ</mi><mo stretchy="false">(</mo><msubsup><mi>θ</mi><mi>a</mi><mi>T</mi></msubsup><mi>x</mi><mo stretchy="false">)</mo><mo>=</mo><mfrac><mn>1</mn><mrow><mn>1</mn><mo>+</mo><msup><mi>e</mi><mrow><mo>−</mo><msubsup><mi>θ</mi><mi>a</mi><mi>T</mi></msubsup><mi>x</mi></mrow></msup></mrow></mfrac></mrow><annotation encoding="application/x-tex">\mathbb{P}(r = 1 \mid x, a) = \sigma(\theta_a^T x) = \frac{1}{1 + e^{-\theta_a^T 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 mathbb">P</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.02778em;">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="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:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</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.0913em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">σ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8413em;"><span style="top:-2.453em;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">a</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 mathnormal mtight" style="margin-right:0.13889em;">T</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></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.4775em;vertical-align:-0.6324em;"></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.4259em;"><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 class="mbin mtight">+</span><span class="mord mtight"><span class="mord mathnormal mtight">e</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:1.0916em;"><span style="top:-3.0916em;margin-right:0.0714em;"><span class="pstrut" style="height:2.6981em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight"><span class="mord mtight">−</span><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.02778em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.9773em;"><span style="top:-2.2833em;margin-left:-0.0278em;margin-right:0.1em;"><span class="pstrut" style="height:2.6833em;"></span><span class="mord mathnormal mtight">a</span></span><span style="top:-2.9773em;margin-right:0.1em;"><span class="pstrut" style="height:2.6833em;"></span><span class="mord mathnormal mtight" style="margin-right:0.13889em;">T</span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.4em;"><span></span></span></span></span></span></span><span class="mord mathnormal mtight">x</span></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.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.6324em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></p>
<p>Use when reward is click/no-click, conversion/no-conversion.</p>
<p><strong>Poisson regression (count rewards):</strong></p>
<p><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>r</mi><mo>∣</mo><mi>x</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">]</mo><mo>=</mo><msup><mi>e</mi><mrow><msubsup><mi>θ</mi><mi>a</mi><mi>T</mi></msubsup><mi>x</mi></mrow></msup></mrow><annotation encoding="application/x-tex">\mathbb{E}[r \mid x, a] = e^{\theta_a^T 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 mathbb">E</span><span class="mopen">[</span><span class="mord mathnormal" style="margin-right:0.02778em;">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="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</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.0064em;"></span><span class="mord"><span class="mord mathnormal">e</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:1.0064em;"><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 class="mord mathnormal mtight" style="margin-right:0.02778em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.9191em;"><span style="top:-2.214em;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 mathnormal mtight">a</span></span></span><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 mathnormal mtight" style="margin-right:0.13889em;">T</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.286em;"><span></span></span></span></span></span></span><span class="mord mathnormal mtight">x</span></span></span></span></span></span></span></span></span></span></span></span></p>
<p>Use when reward is number of purchases, page views, etc.</p>
<p><strong>Advantage:</strong> Captures bounded or discrete reward structures while maintaining interpretability.</p>
<h3 id="nonlinear-models-neural-bandits">Nonlinear Models (Neural Bandits)</h3>
<p><strong>Assumption:</strong> Reward is a complex nonlinear function:</p>
<p><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>a</mi><mo stretchy="false">)</mo><mo>=</mo><msub><mi>f</mi><msub><mi>θ</mi><mi>a</mi></msub></msub><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo>+</mo><mi>ϵ</mi></mrow><annotation encoding="application/x-tex">r(x, a) = f_{\theta_a}(x) + \epsilon</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.02778em;">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">a</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.0001em;vertical-align:-0.2501em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.10764em;">f</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.1076em;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.02778em;">θ</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.0278em;margin-right:0.0714em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mathnormal mtight">a</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></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2501em;"><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><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">ϵ</span></span></span></span></p>
<p>where f is a neural network with parameters θₐ.</p>
<p><strong>When this is necessary:</strong></p>
<p>✅ Complex feature interactions that linear models can’t capture<br>
✅ High-dimensional raw inputs (images, text)<br>
✅ Sufficient data to train deep models</p>
<p><strong>Challenges:</strong></p>
<p>❌ Requires much more data<br>
❌ Less interpretable (black box)<br>
❌ Harder to quantify uncertainty (needed for exploration)<br>
❌ Computationally expensive</p>
<p><strong>When to use each:</strong></p>



































<table><thead><tr><th>Context Dim</th><th>Reward Structure</th><th>Recommended Model</th></tr></thead><tbody><tr><td>d &#x3C; 50</td><td>Linear or mildly nonlinear</td><td>Linear</td></tr><tr><td>d = 50-500</td><td>Linear with interactions</td><td>Linear + feature engineering</td></tr><tr><td>d > 500</td><td>Linear</td><td>Linear with regularization (large λ)</td></tr><tr><td>Any d</td><td>Highly nonlinear</td><td>Neural network</td></tr><tr><td>Images, text</td><td>Complex patterns</td><td>Neural (CNN, Transformer)</td></tr></tbody></table>
<hr>
<h2 id="the-exploration-exploitation-tradeoff">The Exploration-Exploitation Tradeoff</h2>
<p>This is the <strong>fundamental dilemma</strong> in bandit problems.</p>
<h3 id="the-tension">The Tension</h3>
<p><strong>Exploitation:</strong> Choose the action that has performed best so far. Maximize immediate reward based on current knowledge.</p>
<p><strong>Exploration:</strong> Try actions with uncertain rewards. Gather information that might lead to better decisions later.</p>
<p><strong>The conflict:</strong> Exploration sacrifices short-term reward for long-term learning. Exploitation maximizes current reward but risks missing better alternatives.</p>
<h3 id="toy-example">Toy Example</h3>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>Two actions: A and B</span></span>
<span class="line"><span>Current estimates: </span></span>
<span class="line"><span>  μ̂_A = 0.8 (based on 100 trials)</span></span>
<span class="line"><span>  μ̂_B = 0.6 (based on 10 trials)</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Pure exploitation: Always choose A (highest estimate)</span></span>
<span class="line"><span>Problem: B's estimate is uncertain. True μ_B might be 0.9, </span></span>
<span class="line"><span>         but we have sparse data.</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Pure exploration: Choose randomly to gather data</span></span>
<span class="line"><span>Problem: While gathering data, we're not maximizing reward</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Optimal balance: Choose A most of the time, occasionally try B </span></span>
<span class="line"><span>                 until confident about its value</span></span>
<span class="line"><span></span></span></code></pre>
<h3 id="mathematical-formalization">Mathematical Formalization</h3>
<p>Define the <strong>exploitation gap</strong> as the difference between estimated best and true best:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="normal">Δ</mi><mi>t</mi></msub><mo>=</mo><msub><mrow><mi>max</mi><mo>⁡</mo></mrow><mi>a</mi></msub><mi mathvariant="double-struck">E</mi><mo stretchy="false">[</mo><mi>r</mi><mo>∣</mo><msub><mi>x</mi><mi>t</mi></msub><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">]</mo><mo>−</mo><mi mathvariant="double-struck">E</mi><mo stretchy="false">[</mo><mi>r</mi><mo>∣</mo><msub><mi>x</mi><mi>t</mi></msub><mo separator="true">,</mo><msub><mi>a</mi><mi>t</mi></msub><mo stretchy="false">]</mo></mrow><annotation encoding="application/x-tex">\Delta_t = \max_a \mathbb{E}[r \mid x_t, a] - \mathbb{E}[r \mid x_t, a_t]</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.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="mop"><span class="mop">max</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 mathnormal mtight">a</span></span></span></span><span 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 mathbb">E</span><span class="mopen">[</span><span class="mord mathnormal" style="margin-right:0.02778em;">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="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="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</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="mord mathbb">E</span><span class="mopen">[</span><span class="mord mathnormal" style="margin-right:0.02778em;">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="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="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></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="mclose">]</span></span></span></span></p>
<p>And the <strong>exploration benefit</strong> as information gain:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>I</mi><mi>t</mi></msub><mo>=</mo><mtext>Reduction in uncertainty about </mtext><mi>θ</mi></mrow><annotation encoding="application/x-tex">I_t = \text{Reduction in uncertainty about } \theta</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.07847em;">I</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.0785em;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.8889em;vertical-align:-0.1944em;"></span><span class="mord text"><span class="mord">Reduction in uncertainty about </span></span><span class="mord mathnormal" style="margin-right:0.02778em;">θ</span></span></span></span></p>
<p>Optimal policies balance these:</p>
<p><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><mo>=</mo><mi>arg</mi><mo>⁡</mo><msub><mrow><mi>max</mi><mo>⁡</mo></mrow><mi>a</mi></msub><mrow><mo fence="true">{</mo><mi mathvariant="double-struck">E</mi><mo stretchy="false">[</mo><mi>r</mi><mo>∣</mo><msub><mi>x</mi><mi>t</mi></msub><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">]</mo><mo>+</mo><mi>λ</mi><mo>⋅</mo><mi>I</mi><mo stretchy="false">(</mo><mi>a</mi><mo stretchy="false">)</mo><mo fence="true">}</mo></mrow></mrow><annotation encoding="application/x-tex">a_t = \arg\max_a \left\{ \mathbb{E}[r \mid x_t, a] + \lambda \cdot I(a) \right\}</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 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">ar<span style="margin-right:0.01389em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mop"><span class="mop">max</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 mathnormal mtight">a</span></span></span></span><span 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="minner"><span class="mopen delimcenter" style="top:0em;">{</span><span class="mord mathbb">E</span><span class="mopen">[</span><span class="mord mathnormal" style="margin-right:0.02778em;">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 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="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</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 mathnormal">λ</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.07847em;">I</span><span class="mopen">(</span><span class="mord mathnormal">a</span><span class="mclose">)</span><span class="mclose delimcenter" style="top:0em;">}</span></span></span></span></span></p>
<p>where λ controls exploration intensity.</p>
<h3 id="visual-representation">Visual Representation</h3>
<div class="mermaid">
graph LR
    Exploit[Exploitation&#x3C;br/>━━━━━━━━&#x3C;br/>Use current knowledge&#x3C;br/>Maximize immediate reward&#x3C;br/>Risk: Missing better options]
    
    Explore[Exploration&#x3C;br/>━━━━━━━━&#x3C;br/>Gather information&#x3C;br/>Reduce uncertainty&#x3C;br/>Cost: Lower immediate reward]
    
    Balance[Optimal Balance&#x3C;br/>━━━━━━━━&#x3C;br/>Exploit high-confidence actions&#x3C;br/>Explore uncertain alternatives&#x3C;br/>Minimize regret over time]
    
    Exploit -.tradeoff.-> Balance
    Explore -.tradeoff.-> Balance
    
    style Exploit stroke:#10b981,stroke-width:2.5px
    style Explore stroke:#06b6d4,stroke-width:2.5px
    style Balance stroke:#f59e0b,stroke-width:2.5px
    
    linkStyle 0,1 stroke:#64748b,stroke-width:2px,stroke-dasharray:5</div>
<h3 id="why-this-matters-in-practice">Why This Matters in Practice</h3>
<p><strong>If you over-exploit (ε = 0.01):</strong></p>
<ul>
<li>Converge quickly to locally optimal solution</li>
<li>Miss potentially better alternatives</li>
<li>Regret grows linearly if you lock onto suboptimal action</li>
</ul>
<p><strong>If you over-explore (ε = 0.5):</strong></p>
<ul>
<li>Waste traffic on clearly inferior actions</li>
<li>Slow convergence to optimal policy</li>
<li>Users see more bad experiences</li>
</ul>
<p><strong>Optimal strategy:</strong> Exploration rate should decrease over time. Early on, uncertainty is high—explore more. Later, confidence grows—exploit more.</p>
<hr>
<h2 id="the-confidence-bound-principle">The Confidence-Bound Principle</h2>
<p>Many algorithms use <strong>optimism under uncertainty</strong>: when unsure about an action’s value, assume it might be great.</p>
<h3 id="upper-confidence-bound-ucb">Upper Confidence Bound (UCB)</h3>
<p><strong>Intuition:</strong> Add a confidence bonus to each action’s estimate. Choose the action with the highest <strong>upper confidence bound</strong>.</p>
<p><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><mo>=</mo><mi>arg</mi><mo>⁡</mo><msub><mrow><mi>max</mi><mo>⁡</mo></mrow><mi>a</mi></msub><mrow><mo fence="true">{</mo><msub><mover accent="true"><mi>μ</mi><mo>^</mo></mover><mi>a</mi></msub><mo>+</mo><msqrt><mfrac><mrow><mn>2</mn><mi>log</mi><mo>⁡</mo><mi>t</mi></mrow><msub><mi>n</mi><mi>a</mi></msub></mfrac></msqrt><mo fence="true">}</mo></mrow></mrow><annotation encoding="application/x-tex">a_t = \arg\max_a \left\{ \hat{\mu}_a + \sqrt{\frac{2 \log t}{n_a}} \right\}</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 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.8786em;vertical-align:-0.65em;"></span><span class="mop">ar<span style="margin-right:0.01389em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mop"><span class="mop">max</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 mathnormal mtight">a</span></span></span></span><span 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="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size2">{</span></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">μ</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 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.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">a</span></span></span></span><span 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 sqrt"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.2286em;"><span class="svg-align" style="top:-3.8em;"><span class="pstrut" style="height:3.8em;"></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:0.9322em;"><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">n</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: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">a</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></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.4461em;"><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="mspace mtight" style="margin-right:0.1952em;"></span><span class="mop mtight"><span class="mtight">l</span><span class="mtight">o</span><span class="mtight" style="margin-right:0.01389em;">g</span></span><span class="mspace mtight" style="margin-right:0.1952em;"></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.4451em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span><span style="top:-3.1886em;"><span class="pstrut" style="height:3.8em;"></span><span class="hide-tail" style="min-width:1.02em;height:1.88em;"><svg xmlns="http://www.w3.org/2000/svg" width="400em" height="1.88em" viewBox="0 0 400000 1944" preserveAspectRatio="xMinYMin slice"><path d="M983 90
l0 -0
c4,-6.7,10,-10,18,-10 H400000v40
H1013.1s-83.4,268,-264.1,840c-180.7,572,-277,876.3,-289,913c-4.7,4.7,-12.7,7,-24,7
s-12,0,-12,0c-1.3,-3.3,-3.7,-11.7,-7,-25c-35.3,-125.3,-106.7,-373.3,-214,-744
c-10,12,-21,25,-33,39s-32,39,-32,39c-6,-5.3,-15,-14,-27,-26s25,-30,25,-30
c26.7,-32.7,52,-63,76,-91s52,-60,52,-60s208,722,208,722
c56,-175.3,126.3,-397.3,211,-666c84.7,-268.7,153.8,-488.2,207.5,-658.5
c53.7,-170.3,84.5,-266.8,92.5,-289.5z
M1001 80h400000v40h-400000z"></path></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.6114em;"><span></span></span></span></span></span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size2">}</span></span></span></span></span></span></p>
<p>where:</p>
<ul>
<li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mover accent="true"><mi>μ</mi><mo>^</mo></mover><mi>a</mi></msub></mrow><annotation encoding="application/x-tex">\hat{\mu}_a</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 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">μ</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 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.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">a</span></span></span></span><span 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> = estimated mean reward for action a</li>
<li>nₐ = number of times action a has been tried</li>
<li>The √ term = confidence radius</li>
</ul>
<h3 id="why-this-works">Why This Works</h3>
<ul>
<li><strong>Well-explored actions</strong> (large nₐ): Confidence radius is small. Trust the estimate <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mover accent="true"><mi>μ</mi><mo>^</mo></mover><mi>a</mi></msub></mrow><annotation encoding="application/x-tex">\hat{\mu}_a</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 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">μ</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 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.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">a</span></span></span></span><span 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>
<li><strong>Under-explored actions</strong> (small nₐ): Confidence radius is large. UCB is optimistic.</li>
<li>This creates natural exploration: uncertain actions get inflated values and get tried.</li>
</ul>
<h3 id="the-math-behind-the-confidence-radius">The Math Behind the Confidence Radius</h3>
<p>The √(2 log t / nₐ) comes from Hoeffding’s inequality:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="double-struck">P</mi><mrow><mo fence="true">(</mo><mi mathvariant="normal">∣</mi><msub><mover accent="true"><mi>μ</mi><mo>^</mo></mover><mi>a</mi></msub><mo>−</mo><msub><mi>μ</mi><mi>a</mi></msub><mi mathvariant="normal">∣</mi><mo>></mo><msqrt><mfrac><mrow><mn>2</mn><mi>log</mi><mo>⁡</mo><mi>t</mi></mrow><msub><mi>n</mi><mi>a</mi></msub></mfrac></msqrt><mo fence="true">)</mo></mrow><mo>≤</mo><mfrac><mn>1</mn><msup><mi>t</mi><mn>4</mn></msup></mfrac></mrow><annotation encoding="application/x-tex">\mathbb{P}\left(|\hat{\mu}_a - \mu_a| > \sqrt{\frac{2 \log t}{n_a}}\right) \leq \frac{1}{t^4}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.8786em;vertical-align:-0.65em;"></span><span class="mord mathbb">P</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><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">μ</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 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.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">a</span></span></span></span><span 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 mathnormal">μ</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">a</span></span></span></span><span 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="mspace" style="margin-right:0.2778em;"></span><span class="mrel">></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord sqrt"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.2286em;"><span class="svg-align" style="top:-3.8em;"><span class="pstrut" style="height:3.8em;"></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:0.9322em;"><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">n</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: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">a</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></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.4461em;"><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="mspace mtight" style="margin-right:0.1952em;"></span><span class="mop mtight"><span class="mtight">l</span><span class="mtight">o</span><span class="mtight" style="margin-right:0.01389em;">g</span></span><span class="mspace mtight" style="margin-right:0.1952em;"></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.4451em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span><span style="top:-3.1886em;"><span class="pstrut" style="height:3.8em;"></span><span class="hide-tail" style="min-width:1.02em;height:1.88em;"><svg xmlns="http://www.w3.org/2000/svg" width="400em" height="1.88em" viewBox="0 0 400000 1944" preserveAspectRatio="xMinYMin slice"><path d="M983 90
l0 -0
c4,-6.7,10,-10,18,-10 H400000v40
H1013.1s-83.4,268,-264.1,840c-180.7,572,-277,876.3,-289,913c-4.7,4.7,-12.7,7,-24,7
s-12,0,-12,0c-1.3,-3.3,-3.7,-11.7,-7,-25c-35.3,-125.3,-106.7,-373.3,-214,-744
c-10,12,-21,25,-33,39s-32,39,-32,39c-6,-5.3,-15,-14,-27,-26s25,-30,25,-30
c26.7,-32.7,52,-63,76,-91s52,-60,52,-60s208,722,208,722
c56,-175.3,126.3,-397.3,211,-666c84.7,-268.7,153.8,-488.2,207.5,-658.5
c53.7,-170.3,84.5,-266.8,92.5,-289.5z
M1001 80h400000v40h-400000z"></path></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.6114em;"><span></span></span></span></span></span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size2">)</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 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.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">4</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.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></span></span></p>
<p>With high probability (1 - 1/t⁴), the true mean is within the confidence radius. By using the <strong>upper bound</strong>, we ensure we don’t prematurely dismiss good actions.</p>
<h3 id="visual-intuition-for-ucb">Visual Intuition for UCB</h3>
<div class="mermaid">
graph LR
    subgraph Action_A[" "]
        A_Est[Estimate: 0.5&#x3C;br/>Trials: 100&#x3C;br/>Confidence: ±0.1]
        A_UCB[UCB: 0.6&#x3C;br/>✓ Well-explored&#x3C;br/>Small bonus]
    end
    
    subgraph Action_B[" "]
        B_Est[Estimate: 0.4&#x3C;br/>Trials: 10&#x3C;br/>Confidence: ±0.3]
        B_UCB[UCB: 0.7&#x3C;br/>⚡ Uncertain&#x3C;br/>Large bonus]
    end
    
    A_Est --> A_UCB
    B_Est --> B_UCB
    
    B_UCB -.selected.-> Winner[Action B&#x3C;br/>chosen despite&#x3C;br/>lower estimate]
    
    style Action_A stroke:none
    style Action_B stroke:none
    
    style A_Est stroke:#10b981,stroke-width:2px
    style A_UCB stroke:#10b981,stroke-width:2px
    style B_Est stroke:#f59e0b,stroke-width:2px
    style B_UCB stroke:#f59e0b,stroke-width:2.5px
    style Winner stroke:#06b6d4,stroke-width:2px
    
    linkStyle 2 stroke:#06b6d4,stroke-width:2px,stroke-dasharray:5</div>
<p><strong>Key insight:</strong> Action B has lower estimate (0.4 vs 0.5) but higher UCB (0.7 vs 0.6) due to uncertainty. UCB chooses B to reduce uncertainty—exactly what we want.</p>
<h3 id="linucb-extending-to-contextual-bandits">LinUCB: Extending to Contextual Bandits</h3>
<p>For linear contextual bandits, UCB extends to:</p>
<p><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><mo>=</mo><mi>arg</mi><mo>⁡</mo><msub><mrow><mi>max</mi><mo>⁡</mo></mrow><mi>a</mi></msub><mrow><mo fence="true">{</mo><msubsup><mi>θ</mi><mi>a</mi><mi>T</mi></msubsup><msub><mi>x</mi><mi>t</mi></msub><mo>+</mo><mi>α</mi><msqrt><mrow><msubsup><mi>x</mi><mi>t</mi><mi>T</mi></msubsup><msubsup><mi>A</mi><mi>a</mi><mrow><mo>−</mo><mn>1</mn></mrow></msubsup><msub><mi>x</mi><mi>t</mi></msub></mrow></msqrt><mo fence="true">}</mo></mrow></mrow><annotation encoding="application/x-tex">a_t = \arg\max_a \left\{ \theta_a^T x_t + \alpha \sqrt{x_t^T A_a^{-1} x_t} \right\}</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 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="mop">ar<span style="margin-right:0.01389em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mop"><span class="mop">max</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 mathnormal mtight">a</span></span></span></span><span 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="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size2">{</span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8413em;"><span style="top:-2.453em;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">a</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 mathnormal mtight" style="margin-right:0.13889em;">T</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></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.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal" style="margin-right:0.0037em;">α</span><span class="mord sqrt"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.9731em;"><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"><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.8231em;"><span style="top:-2.4542em;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 style="top:-3.0448em;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.13889em;">T</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2458em;"><span></span></span></span></span></span></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.7401em;"><span style="top:-2.453em;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">a</span></span></span><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 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.247em;"><span></span></span></span></span></span></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 style="top:-2.9331em;"><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.2669em;"><span></span></span></span></span></span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size2">}</span></span></span></span></span></span></p>
<p>where:</p>
<ul>
<li>Aₐ = XₐᵀXₐ + λI (regularized Gram matrix)</li>
<li>α = exploration parameter (typically 0.1-2.0)</li>
<li>λ = regularization parameter (typically 1.0)</li>
</ul>
<p>The confidence ellipsoid <strong>√(xᵀA⁻¹x)</strong> captures uncertainty in the parameter estimate <strong>θ</strong>ₐ.</p>
<p><strong>Novel contexts</strong> (far from previously seen data) have large confidence radii, encouraging exploration in new regions of the feature space.</p>
<hr>
<h2 id="thompson-sampling-bayesian-perspective">Thompson Sampling: Bayesian Perspective</h2>
<p>Instead of confidence bounds, Thompson Sampling uses <strong>probability matching</strong>.</p>
<h3 id="algorithm">Algorithm</h3>
<ol>
<li>Maintain posterior distribution P(θₐ | data) for each action’s parameters</li>
<li>Sample θ̃ₐ ~ P(θₐ | data) for each action</li>
<li>Choose action with highest sampled reward: aₜ = argmaxₐ θ̃ₐᵀxₜ</li>
<li>Observe reward, update posteriors</li>
</ol>
<h3 id="probability-matching">Probability Matching</h3>
<p>The probability of choosing action a equals the probability that a is optimal given current knowledge:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="double-struck">P</mi><mo stretchy="false">(</mo><mtext>choose </mtext><mi>a</mi><mo stretchy="false">)</mo><mo>=</mo><mi mathvariant="double-struck">P</mi><mo stretchy="false">(</mo><mi>a</mi><mtext> is optimal</mtext><mo>∣</mo><mtext>data</mtext><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\mathbb{P}(\text{choose } a) = \mathbb{P}(a \text{ is optimal} \mid \text{data})</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">P</span><span class="mopen">(</span><span class="mord text"><span class="mord">choose </span></span><span class="mord mathnormal">a</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 mathbb">P</span><span class="mopen">(</span><span class="mord mathnormal">a</span><span class="mord text"><span class="mord"> is optimal</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">data</span></span><span class="mclose">)</span></span></span></span></p>
<p>This naturally balances exploration and exploitation through Bayesian updating:</p>
<ul>
<li><strong>Early:</strong> Posteriors are diffuse → high probability of sampling various actions → <strong>exploration</strong></li>
<li><strong>Late:</strong> Posteriors concentrate → high probability of sampling best action → <strong>exploitation</strong></li>
</ul>
<p>No manual tuning of exploration rate needed. The uncertainty drives exploration organically.</p>
<h3 id="why-it-works">Why It Works</h3>
<p>Thompson Sampling achieves optimal O(√T) regret bounds for many problems while being simpler to implement than UCB variants. The Bayesian framework naturally handles uncertainty.</p>
<h3 id="example-bernoulli-bandits">Example: Bernoulli Bandits</h3>
<p>For binary rewards (click/no-click), use Beta-Bernoulli conjugacy:</p>
<p><strong>Prior:</strong> θₐ ~ Beta(1, 1) (uniform)</p>
<p><strong>Likelihood:</strong> r | θₐ ~ Bernoulli(θₐ)</p>
<p><strong>Posterior after observing data:</strong></p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>θ</mi><mi>a</mi></msub><mo>∣</mo><mtext>data</mtext><mo>∼</mo><mtext>Beta</mtext><mo stretchy="false">(</mo><msub><mi>α</mi><mi>a</mi></msub><mo separator="true">,</mo><msub><mi>β</mi><mi>a</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\theta_a \mid \text{data} \sim \text{Beta}(\alpha_a, \beta_a)</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.02778em;">θ</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">a</span></span></span></span><span 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.6944em;"></span><span class="mord text"><span class="mord">data</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">Beta</span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0037em;">α</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.0037em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">a</span></span></span></span><span 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.05278em;">β</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.0528em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">a</span></span></span></span><span 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></p>
<p>where:</p>
<ul>
<li>αₐ = 1 + number of successes (clicks)</li>
<li>βₐ = 1 + number of failures (no clicks)</li>
</ul>
<p><strong>To choose action:</strong> Sample θ̃ₐ ~ Beta(αₐ, βₐ) for each action, pick argmax θ̃ₐ.</p>
<h3 id="for-linear-contextual-bandits">For Linear Contextual Bandits</h3>
<p>Assume Gaussian prior and noise:</p>
<p><strong>Prior:</strong> θₐ ~ N(0, σ²I)<br>
<strong>Noise:</strong> ε ~ N(0, σ²)</p>
<p><strong>Posterior:</strong></p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>θ</mi><mi>a</mi></msub><mo>∣</mo><msub><mi>D</mi><mi>a</mi></msub><mo>∼</mo><mi mathvariant="script">N</mi><mo stretchy="false">(</mo><msub><mover accent="true"><mi>θ</mi><mo>^</mo></mover><mi>a</mi></msub><mo separator="true">,</mo><msub><mi mathvariant="normal">Σ</mi><mi>a</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\theta_a \mid D_a \sim \mathcal{N}(\hat{\theta}_a, \Sigma_a)</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.02778em;">θ</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">a</span></span></span></span><span 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.8333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">D</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">a</span></span></span></span><span 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.2079em;vertical-align:-0.25em;"></span><span class="mord mathcal" style="margin-right:0.14736em;">N</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.9579em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">θ</span></span><span style="top:-3.2634em;"><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.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">a</span></span></span></span><span 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">Σ</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">a</span></span></span></span><span 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></p>
<p>where:</p>
<ul>
<li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mover accent="true"><mi>θ</mi><mo>^</mo></mover><mi>a</mi></msub><mo>=</mo><mo stretchy="false">(</mo><msup><mi>X</mi><mi>T</mi></msup><mi>X</mi><mo>+</mo><mi>λ</mi><mi>I</mi><msup><mo stretchy="false">)</mo><mrow><mo>−</mo><mn>1</mn></mrow></msup><msup><mi>X</mi><mi>T</mi></msup><mi>r</mi></mrow><annotation encoding="application/x-tex">\hat{\theta}_a = (X^T X + \lambda I)^{-1} X^T r</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.1079em;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.9579em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">θ</span></span><span style="top:-3.2634em;"><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.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">a</span></span></span></span><span 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.0913em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.07847em;">X</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8413em;"><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.13889em;">T</span></span></span></span></span></span></span></span><span class="mord mathnormal" style="margin-right:0.07847em;">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:1.0913em;vertical-align:-0.25em;"></span><span class="mord mathnormal">λ</span><span class="mord mathnormal" style="margin-right:0.07847em;">I</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"><span class="mord mtight">−</span><span class="mord mtight">1</span></span></span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.07847em;">X</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8413em;"><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.13889em;">T</span></span></span></span></span></span></span></span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span></span></span></span> (ridge regression estimate)</li>
<li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="normal">Σ</mi><mi>a</mi></msub><mo>=</mo><msup><mi>σ</mi><mn>2</mn></msup><mo stretchy="false">(</mo><msup><mi>X</mi><mi>T</mi></msup><mi>X</mi><mo>+</mo><mi>λ</mi><mi>I</mi><msup><mo stretchy="false">)</mo><mrow><mo>−</mo><mn>1</mn></mrow></msup></mrow><annotation encoding="application/x-tex">\Sigma_a = \sigma^2(X^T X + \lambda I)^{-1}</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.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">a</span></span></span></span><span 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.0913em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">σ</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="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.07847em;">X</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8413em;"><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.13889em;">T</span></span></span></span></span></span></span></span><span class="mord mathnormal" style="margin-right:0.07847em;">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:1.0641em;vertical-align:-0.25em;"></span><span class="mord mathnormal">λ</span><span class="mord mathnormal" style="margin-right:0.07847em;">I</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"><span class="mord mtight">−</span><span class="mord mtight">1</span></span></span></span></span></span></span></span></span></span></span></span> (posterior covariance)</li>
</ul>
<p><strong>To choose action:</strong> Sample <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mover accent="true"><mi>θ</mi><mo>~</mo></mover><mi>a</mi></msub><mo>∼</mo><mi mathvariant="script">N</mi><mo stretchy="false">(</mo><msub><mover accent="true"><mi>θ</mi><mo>^</mo></mover><mi>a</mi></msub><mo separator="true">,</mo><msub><mi mathvariant="normal">Σ</mi><mi>a</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\tilde{\theta}_a \sim \mathcal{N}(\hat{\theta}_a, \Sigma_a)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0813em;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.9313em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">θ</span></span><span style="top:-3.6134em;"><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.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">a</span></span></span></span><span 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.2079em;vertical-align:-0.25em;"></span><span class="mord mathcal" style="margin-right:0.14736em;">N</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.9579em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">θ</span></span><span style="top:-3.2634em;"><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.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">a</span></span></span></span><span 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">Σ</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">a</span></span></span></span><span 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>, compute <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>a</mi></msub><mo>=</mo><msubsup><mover accent="true"><mi>θ</mi><mo>~</mo></mover><mi>a</mi><mi>T</mi></msubsup><msub><mi>x</mi><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">\tilde{r}_a = \tilde{\theta}_a^T x_t</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8179em;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.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span></span><span style="top:-3.35em;"><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">a</span></span></span></span><span 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.1783em;vertical-align:-0.247em;"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9313em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">θ</span></span><span style="top:-3.6134em;"><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.8413em;"><span style="top:-2.453em;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">a</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 mathnormal mtight" style="margin-right:0.13889em;">T</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></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>, pick <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><mo>=</mo><mi>arg</mi><mo>⁡</mo><msub><mrow><mi>max</mi><mo>⁡</mo></mrow><mi>a</mi></msub><msub><mover accent="true"><mi>r</mi><mo>~</mo></mover><mi>a</mi></msub></mrow><annotation encoding="application/x-tex">a_t = \arg\max_a \tilde{r}_a</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 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.8623em;vertical-align:-0.1944em;"></span><span class="mop">ar<span style="margin-right:0.01389em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mop"><span class="mop">max</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 mathnormal mtight">a</span></span></span></span><span 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.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span></span><span style="top:-3.35em;"><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">a</span></span></span></span><span 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>
<hr>
<h2 id="the-fundamental-limits-lower-bounds">The Fundamental Limits: Lower Bounds</h2>
<p><strong>Can we do better than O(√T) regret?</strong> No (in the worst case).</p>
<h3 id="theorem-lai--robbins-1985">Theorem (Lai &#x26; Robbins, 1985)</h3>
<p>For any algorithm, there exist problem instances where regret is at least:</p>
<p><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>T</mi><mo stretchy="false">)</mo><mo>=</mo><mi mathvariant="normal">Ω</mi><mo stretchy="false">(</mo><mi>log</mi><mo>⁡</mo><mi>T</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">R(T) = \Omega(\log 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.00773em;">R</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.13889em;">T</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><span class="mopen">(</span><span class="mop">lo<span style="margin-right:0.01389em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">T</span><span class="mclose">)</span></span></span></span></p>
<p>for stochastic bandits (MAB).</p>
<p>For contextual bandits with d-dimensional context and linear rewards:</p>
<p><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>T</mi><mo stretchy="false">)</mo><mo>=</mo><mi mathvariant="normal">Ω</mi><mo stretchy="false">(</mo><msqrt><mrow><mi>d</mi><mi>T</mi></mrow></msqrt><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">R(T) = \Omega(\sqrt{dT})</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.00773em;">R</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.13889em;">T</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.1822em;vertical-align:-0.25em;"></span><span class="mord">Ω</span><span class="mopen">(</span><span class="mord sqrt"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.9322em;"><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">d</span><span class="mord mathnormal" style="margin-right:0.13889em;">T</span></span></span><span style="top:-2.8922em;"><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.1078em;"><span></span></span></span></span></span><span class="mclose">)</span></span></span></span></p>
<h3 id="implication">Implication</h3>
<p>Algorithms achieving O(√(dT log T)) are <strong>near-optimal</strong>. The log T factor is unavoidable slack, but the √(dT) matches the lower bound up to logarithmic factors.</p>
<h3 id="intuition-for-the-t-bound">Intuition for the √T Bound</h3>
<p>You need to explore each action enough times to distinguish it from the optimal one. If the gap between rewards is Δ, you need roughly O(1/Δ²) samples per action. With K actions, total exploration cost is O(K/Δ²).</p>
<p>If you don’t know Δ in advance and must be robust to small gaps, worst-case regret is O(√(KT)).</p>
<p><strong>This isn’t just theoretical</strong>—it explains why bandit algorithms can’t magically achieve zero regret. There’s a fundamental cost to learning under uncertainty.</p>
<hr>
<h2 id="key-takeaways">Key Takeaways</h2>
<p><strong>Essential concepts:</strong></p>
<p><strong>Regret R(T) = cumulative opportunity cost</strong> vs optimal policy</p>
<ul>
<li>Goal: Achieve sublinear regret R(T) = o(T)</li>
<li>Means average regret → 0 as T → ∞ (we’re learning)</li>
</ul>
<p><strong>Optimal algorithms achieve O(√(dT log T))</strong> for d-dimensional contexts</p>
<ul>
<li>The √T term is unavoidable (information-theoretic limit)</li>
<li>The √d factor is the cost of personalization</li>
<li>Linear regret O(T) means algorithm never learns (failure)</li>
</ul>
<p><strong>Exploration-exploitation is fundamental</strong>—no free lunch</p>
<ul>
<li>Explore: Try uncertain actions to gather information</li>
<li>Exploit: Choose known best action to maximize reward</li>
<li>Tradeoff: Short-term reward vs long-term learning</li>
</ul>
<p><strong>Two principled approaches to balance:</strong></p>
<ul>
<li><strong>UCB:</strong> Optimism under uncertainty (confidence-based)</li>
<li><strong>Thompson:</strong> Posterior sampling (Bayesian)</li>
<li>Both achieve near-optimal regret bounds</li>
</ul>
<p><strong>Reward model matters:</strong></p>
<ul>
<li>Linear: Fast, interpretable, works for d &#x3C; 500</li>
<li>GLM: Handles bounded/discrete rewards</li>
<li>Neural: Required for high-dim or complex nonlinear</li>
</ul>
<p><strong>Practical implications:</strong></p>






























<table><thead><tr><th>If You See…</th><th>It Means…</th><th>Action</th></tr></thead><tbody><tr><td>Regret growing linearly</td><td>Algorithm not learning</td><td>Increase exploration (ε or α)</td></tr><tr><td>High variance in rewards</td><td>Unstable estimates</td><td>Increase regularization (λ)</td></tr><tr><td>Slow convergence</td><td>Under-exploration</td><td>Check confidence bounds</td></tr><tr><td>Many dimensions (d > 100)</td><td>√d penalty hurts</td><td>Feature selection or neural</td></tr></tbody></table>
<p><strong>When to worry about math:</strong></p>
<p>✅ <strong>Do worry:</strong> Algorithm selection, tuning α/λ, understanding regret curves<br>
❌ <strong>Don’t over-optimize:</strong> Chasing last 1% theoretical gain—empirical performance matters more</p>
<hr>
<h2 id="further-reading">Further Reading</h2>
<p><strong>Foundational papers:</strong></p>
<ul>
<li><a href="https://link.springer.com/article/10.1023/A:1013689704352">Finite-time Analysis of the Multiarmed Bandit Problem</a> (Auer et al., 2002) - UCB1 algorithm and regret bounds</li>
<li><a href="http://proceedings.mlr.press/v23/agrawal12.html">Analysis of Thompson Sampling</a> (Agrawal &#x26; Goyal, 2012) - Theoretical analysis</li>
<li><a href="http://proceedings.mlr.press/v28/agrawal13.html">Thompson Sampling for Contextual Bandits</a> (Agrawal &#x26; Goyal, 2013) - Extension to contextual case</li>
</ul>
<p><strong>Textbooks:</strong></p>
<ul>
<li><a href="https://banditalgs.com/">Bandit Algorithms</a> (Lattimore &#x26; Szepesvári, 2020) - Comprehensive theoretical treatment (free online)</li>
<li><a href="http://incompleteideas.net/book/the-book-2nd.html">Reinforcement Learning: An Introduction</a> (Sutton &#x26; Barto) - Includes bandit chapters</li>
</ul>
<p><strong>Blog posts:</strong></p>
<ul>
<li><a href="https://lilianweng.github.io/posts/2018-01-23-multi-armed-bandit/">Lil’Log: Multi-Armed Bandits</a> - Excellent visual tutorial</li>
<li><a href="https://jeremykun.com/tag/multi-armed-bandit/">Jeremy Kun: Bandits Series</a> - Mathematical blog series</li>
</ul>
<hr>]]></content:encoded><enclosure url="https://www.vitorsousa.com/_astro/grpo-architecture.MjZ2Lnx7.png" length="756000" type="image/png"/></item><item><title>PPO for Language Models: The RLHF Workhorse</title><link>https://www.vitorsousa.com/blog/ppo-deep-dive/</link><guid isPermaLink="true">https://www.vitorsousa.com/blog/ppo-deep-dive/</guid><description>Deep dive into Proximal Policy Optimization—the algorithm behind most LLM alignment. Understand trust regions, the clipped objective, GAE, and why PPO&apos;s four-model architecture creates problems at scale.</description><pubDate>Sun, 18 Jan 2026 10:00:00 GMT</pubDate><content:encoded><![CDATA[<h1 id="when-to-use-contextual-bandits-the-decision-framework">When to Use Contextual Bandits: The Decision Framework</h1>
<p><strong>Part 1 of 5:</strong> Understanding the Landscape</p>
<blockquote>
<p><strong>TL;DR:</strong> Contextual bandits bridge the gap between static A/B testing and full reinforcement learning. Use them when you need personalized, adaptive optimization with immediate feedback—but not for sequential decision-making or when you need clean causal inference. This post helps you decide if bandits are right for your problem.</p>
</blockquote>
<p><strong>Reading time:</strong> ~20 minutes</p>
<hr>
<h2 id="the-adaptive-optimization-gap">The Adaptive Optimization Gap</h2>
<p><em><strong>You’re running an A/B test to optimize your recommendation system. Variant A gets 50% of traffic, variant B gets 50%. You wait three weeks for statistical significance. Finally, results are in: B wins with 5.2% lift. You ship it to 100% of users.</strong></em></p>
<p><em><strong>But here’s what you missed: During those three weeks, 50% of your users got the inferior experience. And variant B might work great for power users but poorly for newcomers—your aggregate metric doesn’t capture this. You just left significant value on the table.</strong></em></p>
<blockquote>
<p>This is the adaptive optimization gap that contextual bandits solve.</p>
</blockquote>
<h3 id="the-contextual-bandit-loop">The contextual bandit loop</h3>
<p>Before diving deeper, understand the core interaction pattern:</p>
<div class="mermaid">
graph LR
    S[1 Observe Context&#x3C;br/>━━━━━━━━&#x3C;br/>User features&#x3C;br/>Situation state&#x3C;br/>Available actions] --> A[2 Select Action&#x3C;br/>━━━━━━━━&#x3C;br/>Use policy π&#x3C;br/>Balance explore/exploit&#x3C;br/>Log decision]
    
    A --> R[3 Receive Reward&#x3C;br/>━━━━━━━━&#x3C;br/>Immediate feedback&#x3C;br/>Click, purchase, engage&#x3C;br/>Measurable outcome]
    
    R --> U[4 Update Policy&#x3C;br/>━━━━━━━━&#x3C;br/>Learn from feedback&#x3C;br/>Adjust parameters&#x3C;br/>Reduce uncertainty]
    
    U -.continuous learning.-> S
    
    style S stroke:#10b981,stroke-width:2.5px
    style A stroke:#06b6d4,stroke-width:2.5px
    style R stroke:#f59e0b,stroke-width:2.5px
    style U stroke:#8b5cf6,stroke-width:2.5px
    
    linkStyle 3 stroke:#64748b,stroke-width:2px,stroke-dasharray:5</div>
<p><strong>The key difference from A/B testing:</strong> Step 4 happens immediately after each interaction. The policy learns and adapts continuously, not after weeks of fixed allocation.</p>
<h3 id="quick-start-is-this-guide-for-you">Quick Start: Is This Guide For You?</h3>
<p><strong>✅ This series is for you if:</strong></p>
<ul>
<li>You’re optimizing recommendations, rankings, or content selection</li>
<li>You have >1000 decisions/day with measurable outcomes</li>
<li>User preferences vary (personalization matters)</li>
<li>You need faster learning than A/B testing provides</li>
</ul>
<p><strong>❌ Skip to alternatives if:</strong></p>
<ul>
<li>Decisions are highly sequential (use RL instead)</li>
<li>Feedback is delayed >1 week (use delayed RL)</li>
<li>You have &#x3C;100 decisions/day (use A/B testing)</li>
<li>Context doesn’t matter (use multi-armed bandit)</li>
</ul>
<hr>
<h2 id="what-are-contextual-bandits">What Are Contextual Bandits?</h2>
<h3 id="starting-simple-the-casino-analogy">Starting Simple: The Casino Analogy</h3>
<p><strong>Multi-Armed Bandit (MAB):</strong>
You walk into a casino with K slot machines. Each has an unknown payout rate. Your goal: maximize winnings over 1000 plays.</p>
<p>The challenge: explore (try machines to learn rates) vs. exploit (play the best one). Every player faces the same problem—machine 3 is either best for everyone or no one. <strong>No personalization.</strong></p>
<p><strong>The Real World Problem:</strong>
The “best” choice depends on <strong>who you are</strong> and <strong>the situation</strong>.</p>
<p>News articles example: {Tech, Sports, Politics}</p>
<ul>
<li><strong>MAB thinking:</strong> “Sports gets 15% CTR on average → show Sports to everyone”</li>
<li><strong>Reality:</strong> Tech enthusiasts click Tech 40% vs Sports 5%. Sports fans reverse this pattern.</li>
<li><strong>The insight:</strong> Optimal action changes based on context (user type).</li>
</ul>
<h3 id="technical-definition">Technical Definition</h3>
<p><strong>Contextual Bandit:</strong> An online learning algorithm for personalized decision-making under uncertainty.</p>
<p><strong>Mathematical Formulation:</strong></p>
<p>At each timestep t = 1, 2, …, T:</p>
<ol>
<li><strong>Observe context</strong> <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><mo>∈</mo><msup><mi mathvariant="double-struck">R</mi><mi>d</mi></msup></mrow><annotation encoding="application/x-tex">x_t \in \mathbb{R}^d</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">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><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 mathnormal mtight">d</span></span></span></span></span></span></span></span></span></span></span>
Context vector with d features (user demographics, item attributes, environment)</li>
<li><strong>Choose action</strong> <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><mo>∈</mo><mi mathvariant="script">A</mi><mo>=</mo><mrow><mn>1</mn><mo separator="true">,</mo><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mo separator="true">,</mo><mi>K</mi></mrow></mrow><annotation encoding="application/x-tex">a_t \in \mathcal{A} = {1, ..., K}</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">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><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathcal">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.8778em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord">1</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">...</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.07153em;">K</span></span></span></span></span>
Select from K available actions based on context</li>
<li><strong>Receive reward</strong> <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><mi>P</mi><mo stretchy="false">(</mo><mi>r</mi><mo>∣</mo><msub><mi>x</mi><mi>t</mi></msub><mo separator="true">,</mo><msub><mi>a</mi><mi>t</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">r_t \sim P(r \mid x_t, 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" style="margin-right:0.02778em;">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:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.02778em;">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="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="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></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="mclose">)</span></span></span></span>
Observe stochastic reward from unknown distribution</li>
<li><strong>Update model</strong> <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>f</mi><mo>^</mo></mover><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo><mo>≈</mo><mi mathvariant="double-struck">E</mi><mo stretchy="false">[</mo><mi>r</mi><mo>∣</mo><mi>x</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">]</mo></mrow><annotation encoding="application/x-tex">\hat{f}(x, a) \approx \mathbb{E}[r \mid x, a]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.2079em;vertical-align:-0.25em;"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.9579em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.10764em;">f</span></span><span style="top:-3.2634em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.0833em;"><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 mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</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 mathbb">E</span><span class="mopen">[</span><span class="mord mathnormal" style="margin-right:0.02778em;">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="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</span><span class="mclose">]</span></span></span></span>
Learn to predict expected reward for any (context, action) pair</li>
<li><strong>Repeat</strong> with continuous adaptation</li>
</ol>
<p><strong>Goal:</strong> Learn policy <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>π</mi><mo>:</mo><mi mathvariant="script">X</mi><mo>→</mo><mi mathvariant="script">A</mi></mrow><annotation encoding="application/x-tex">\pi: \mathcal{X} \rightarrow \mathcal{A}</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.03588em;">π</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.6833em;"></span><span class="mord mathcal" style="margin-right:0.14643em;">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.6833em;"></span><span class="mord mathcal">A</span></span></span></span> that maximizes cumulative reward:</p>
<p><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>π</mi><mo stretchy="false">)</mo><mo>=</mo><mi mathvariant="double-struck">E</mi><mrow><mo fence="true">[</mo><msubsup><mo>∑</mo><mrow><mi>t</mi><mo>=</mo><mn>1</mn></mrow><mi>T</mi></msubsup><msub><mi>r</mi><mi>t</mi></msub><mo>∣</mo><mi>π</mi><mo fence="true">]</mo></mrow></mrow><annotation encoding="application/x-tex">V(\pi) = \mathbb{E}\left[\sum_{t=1}^{T} r_t \mid \pi\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 mathnormal" style="margin-right:0.22222em;">V</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">π</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 mathbb">E</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="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.9812em;"><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">t</span><span class="mrel mtight">=</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.2029em;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.13889em;">T</span></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 mathnormal" style="margin-right:0.02778em;">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 class="mord mathnormal" style="margin-right:0.03588em;">π</span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size2">]</span></span></span></span></span></span></p>
<p><strong>Key Differences from MAB:</strong></p>






























<table><thead><tr><th>Aspect</th><th>Multi-Armed Bandit</th><th>Contextual Bandit</th></tr></thead><tbody><tr><td><strong>Learning task</strong></td><td>K numbers: <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>μ</mi><mi>a</mi></msub><mo>=</mo><mi mathvariant="double-struck">E</mi><mo stretchy="false">[</mo><mi>r</mi><mo>∣</mo><mi>a</mi><mo stretchy="false">]</mo></mrow><annotation encoding="application/x-tex">\mu_a = \mathbb{E}[r \mid a]</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.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">a</span></span></span></span><span 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 mathbb">E</span><span class="mopen">[</span><span class="mord mathnormal" style="margin-right:0.02778em;">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="mord mathnormal">a</span><span class="mclose">]</span></span></span></span></td><td>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>x</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo><mo>=</mo><mi mathvariant="double-struck">E</mi><mo stretchy="false">[</mo><mi>r</mi><mo>∣</mo><mi>x</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">]</mo></mrow><annotation encoding="application/x-tex">f(x,a) = \mathbb{E}[r \mid x, a]</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.10764em;">f</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">a</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 mathbb">E</span><span class="mopen">[</span><span class="mord mathnormal" style="margin-right:0.02778em;">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="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</span><span class="mclose">]</span></span></span></span></td></tr><tr><td><strong>Personalization</strong></td><td>None (global optimum)</td><td>Per-context optimization</td></tr><tr><td><strong>Complexity</strong></td><td>Simple</td><td>Moderate (function approximation)</td></tr><tr><td><strong>Sample efficiency</strong></td><td>Good for homogeneous</td><td>Better for heterogeneous</td></tr></tbody></table>
<h3 id="concrete-example-content-recommendation">Concrete Example: Content Recommendation</h3>
<p><strong>Setup:</strong></p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>Context x ∈ ℝ⁵: </span></span>
<span class="line"><span>  x = [user_age, user_tenure_days, is_mobile, time_of_day, topic_affinity]</span></span>
<span class="line"><span>  Example: x₁ = [28, 450, 1, 14, 0.8]  </span></span>
<span class="line"><span>  (28yo, 450 days tenure, mobile, 2pm, 80% tech affinity)</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Actions a ∈ {1, 2, 3}:</span></span>
<span class="line"><span>  a=1: article_A (tech content)</span></span>
<span class="line"><span>  a=2: article_B (sports content)  </span></span>
<span class="line"><span>  a=3: article_C (politics content)</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Reward r ∈ {0, 1}:</span></span>
<span class="line"><span>  r = 1 if user clicks, r = 0 otherwise</span></span>
<span class="line"><span></span></span></code></pre>
<p><strong>How MAB Fails:</strong></p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>After 1000 uniform trials:</span></span>
<span class="line"><span>  article_A: 120/333 = 36% CTR</span></span>
<span class="line"><span>  article_B: 133/333 = 40% CTR ← Highest average</span></span>
<span class="line"><span>  article_C: 97/334 = 29% CTR</span></span>
<span class="line"><span></span></span>
<span class="line"><span>MAB decision: Always show article_B</span></span>
<span class="line"><span>Problem: Misses that tech users prefer A, sports fans prefer B</span></span>
<span class="line"><span></span></span></code></pre>
<p><strong>How Contextual Bandit Succeeds:</strong></p>
<p>Learn function <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>P</mi><mo stretchy="false">(</mo><mtext>click</mtext><mo>∣</mo><mi>x</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo><mo>=</mo><mi>f</mi><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">P(\text{click} \mid x, a) = f(x, a)</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.13889em;">P</span><span class="mopen">(</span><span class="mord text"><span class="mord">click</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="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</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.10764em;">f</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">a</span><span class="mclose">)</span></span></span></span>:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>Tech enthusiast: x₁ = [28, 450, 1, 14, 0.8]</span></span>
<span class="line"><span>  f(x₁, article_A) = 0.75 ← Optimal for this user</span></span>
<span class="line"><span>  f(x₁, article_B) = 0.15</span></span>
<span class="line"><span>  f(x₁, article_C) = 0.10</span></span>
<span class="line"><span>  → Recommend article_A</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Sports fan: x₂ = [45, 1200, 0, 19, 0.2]</span></span>
<span class="line"><span>  f(x₂, article_A) = 0.12</span></span>
<span class="line"><span>  f(x₂, article_B) = 0.68 ← Optimal for this user</span></span>
<span class="line"><span>  f(x₂, article_C) = 0.20</span></span>
<span class="line"><span>  → Recommend article_B</span></span>
<span class="line"><span></span></span></code></pre>
<p><strong>Result:</strong> Personalized CTR ~55% vs MAB’s global 40%.</p>
<h3 id="the-learning-challenge">The Learning Challenge</h3>
<p><strong>What makes this hard:</strong></p>
<ol>
<li><strong>Partial feedback:</strong> Only observe reward for chosen action
<ul>
<li>Chose A, saw reward 1 → What would B have given? Unknown (counterfactual)</li>
<li>Must explore alternatives to learn their value</li>
</ul>
</li>
<li><strong>Function approximation:</strong> Learning <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>x</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">f(x,a)</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.10764em;">f</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">a</span><span class="mclose">)</span></span></span></span> across continuous context space
<ul>
<li>Can’t try every possible x (infinite contexts)</li>
<li>Must generalize from observed data to unseen contexts</li>
</ul>
</li>
<li><strong>Exploration-exploitation tradeoff:</strong>
<ul>
<li>Explore: Try uncertain actions to gather information (short-term cost)</li>
<li>Exploit: Choose known best action to maximize reward (long-term requires learning)</li>
<li>Balance: Neither pure exploration nor pure exploitation is optimal</li>
</ul>
</li>
</ol>
<p><strong>Optimal algorithms</strong> (LinUCB, Thompson Sampling) achieve <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><msqrt><mrow><mi>d</mi><mi>T</mi><mi>log</mi><mo>⁡</mo><mi>T</mi></mrow></msqrt><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(\sqrt{dT \log T})</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.085em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord sqrt"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.835em;"><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">d</span><span class="mord mathnormal" style="margin-right:0.13889em;">T</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mop">lo<span style="margin-right:0.01389em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">T</span></span></span><span style="top:-2.795em;"><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.205em;"><span></span></span></span></span></span><span class="mclose">)</span></span></span></span> regret—near-optimal convergence to best policy.</p>
<h3 id="where-contextual-bandits-fit">Where Contextual Bandits Fit</h3>















































<table><thead><tr><th>Method</th><th>Context?</th><th>Online Learning?</th><th>Explores?</th><th>Use Case</th></tr></thead><tbody><tr><td><strong>A/B Test</strong></td><td>❌</td><td>❌</td><td>Yes (forced)</td><td>One-time experiments</td></tr><tr><td><strong>Supervised ML</strong></td><td>✅</td><td>❌</td><td>❌</td><td>Offline labeled data</td></tr><tr><td><strong>MAB</strong></td><td>❌</td><td>✅</td><td>✅</td><td>Homogeneous population</td></tr><tr><td><strong>Contextual Bandit</strong></td><td>✅</td><td>✅</td><td>✅</td><td><strong>Personalized + adaptive</strong></td></tr><tr><td><strong>Full RL</strong></td><td>✅</td><td>✅</td><td>✅</td><td>Sequential decisions</td></tr></tbody></table>
<p><strong>Critical distinction from RL:</strong></p>
<p>Contextual bandits are <strong>stateless</strong>. Each decision is independent.</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>Contextual Bandit (memoryless):</span></span>
<span class="line"><span>  t=1: x₁ → a₁ → r₁</span></span>
<span class="line"><span>  t=2: x₂ → a₂ → r₂  (x₂ independent of a₁)</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Full RL (stateful):</span></span>
<span class="line"><span>  t=1: s₁ → a₁ → r₁ → s₂  </span></span>
<span class="line"><span>  t=2: s₂ → a₂ → r₂ → s₃  (s₂ caused by a₁)</span></span>
<span class="line"><span></span></span></code></pre>
<p><strong>Example:</strong> Recommending an article doesn’t change tomorrow’s user → bandit. Playing chess (moves change board state) → RL.</p>
<h3 id="why-contextual-bandits-matter">Why Contextual Bandits Matter</h3>
<p><strong>1. Automatic Personalization</strong></p>
<p>Traditional:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>Run A/B test → Analyst finds "B wins for 18-25, A for 40+" </span></span>
<span class="line"><span>→ Engineer 2-variant system → Repeat for each feature</span></span>
<span class="line"><span></span></span></code></pre>
<p>Contextual bandit:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>Deploy with features → Learns f(age, location, ...) → best action</span></span>
<span class="line"><span>→ Discovers "A wins for 18-25 + mobile + evening" automatically</span></span>
<span class="line"><span></span></span></code></pre>
<p><strong>2. Sample Efficiency</strong></p>
<p>Over 10,000 decisions:</p>
<ul>
<li><strong>A/B test (50/50 split):</strong> ~5,000 suboptimal → 2,500 lost conversions</li>
<li><strong>Contextual bandit:</strong> ~500 exploratory → 100 lost conversions</li>
<li><strong>Savings:</strong> 2,400 conversions (48% improvement)</li>
</ul>
<p><strong>3. Continuous Adaptation</strong></p>
<p>Handles non-stationarity:</p>
<ul>
<li>User preferences shift (trends)</li>
<li>Seasonality (December ≠ July)</li>
<li>Competition (dynamic pricing)</li>
<li>External shocks (COVID)</li>
</ul>
<p>Bandit automatically detects changes and adapts. A/B test remains static until next experiment.</p>
<p><strong>Real-world impact:</strong></p>
<ul>
<li>Netflix: 20-30% engagement lift (personalized artwork)</li>
<li>E-commerce: 5-15% revenue increase (dynamic pricing)</li>
<li>Healthcare: 10-25% outcome improvement (treatment personalization)</li>
</ul>
<h3 id="quick-decision-guide">Quick Decision Guide</h3>
<p><strong>✅ Use contextual bandits when:</strong></p>
<ol>
<li>Context predicts optimal action (heterogeneous users/situations)</li>
<li>Decisions are independent (stateless)</li>
<li>Feedback is immediate (seconds to days)</li>
<li>Scale justifies complexity (1000+ decisions/day)</li>
<li>Reward is measurable and well-defined</li>
</ol>
<p><strong>❌ Use alternatives when:</strong></p>
<ul>
<li>No context → MAB</li>
<li>Sequential dependencies → Full RL</li>
<li>Delayed rewards (>1 week) → Delayed RL</li>
<li>Need causal estimates → A/B testing</li>
<li>Low scale (&#x3C;100/day) → A/B testing</li>
</ul>
<hr>
<h2 id="the-problem-with-static-experimentation">The Problem with Static Experimentation</h2>
<p>A/B testing is the gold standard for causal inference, but has fundamental limitations for adaptive optimization:</p>
<h3 id="core-limitations">Core Limitations</h3>
<p><strong>1. Fixed allocation wastes traffic</strong>
50/50 split for entire experiment. Bad variants get equal traffic until significance.</p>
<p><strong>2. No personalization</strong>
Aggregate metrics hide segment-level differences. Manual segmentation requires pre-defining groups and exponentially splits traffic.</p>
<p><strong>3. Slow adaptation</strong>
Wait weeks for statistical power. System doesn’t learn during experiment.</p>
<p><strong>4. Sequential testing overhead</strong>
Testing 5 variants requires either multiple rounds (slow) or 5-way split (massive sample size needed).</p>
<p><strong>Reality:</strong> Production systems need faster, adaptive, personalized optimization.</p>
<hr>
<h2 id="understanding-the-decision-making-spectrum">Understanding the Decision-Making Spectrum</h2>
<div class="mermaid">
graph TB
    subgraph Complexity[" "]
        direction TB
        
        AB[A/B Testing&#x3C;br/>━━━━━━━━&#x3C;br/>Static allocation&#x3C;br/>Clean inference&#x3C;br/>Slow learning]
        
        MAB[Multi-Armed Bandit&#x3C;br/>━━━━━━━━&#x3C;br/>No context&#x3C;br/>Adaptive allocation&#x3C;br/>Global optimum]
        
        CB[Contextual Bandit&#x3C;br/>━━━━━━━━&#x3C;br/>Context-aware&#x3C;br/>Personalized&#x3C;br/>One-step decisions]
        
        RL[Full RL&#x3C;br/>━━━━━━━━&#x3C;br/>Sequential planning&#x3C;br/>Long-term rewards&#x3C;br/>Complex dynamics]
    end
    
    AB --> MAB
    MAB --> CB
    CB --> RL
    
    style AB stroke:#8b5cf6,stroke-width:2px
    style MAB stroke:#06b6d4,stroke-width:2px
    style CB stroke:#10b981,stroke-width:2.5px
    style RL stroke:#ef4444,stroke-width:2px
    style Complexity stroke:none
    
    linkStyle 0,1,2 stroke:#64748b,stroke-width:2px</div>















































<table><thead><tr><th>Dimension</th><th>A/B Testing</th><th>MAB</th><th>Contextual Bandit</th><th>Full RL</th></tr></thead><tbody><tr><td><strong>Adaptation</strong></td><td>None</td><td>Global</td><td>Personalized</td><td>Sequential</td></tr><tr><td><strong>Context use</strong></td><td>Post-hoc</td><td>None</td><td>Core feature</td><td>State</td></tr><tr><td><strong>Feedback</strong></td><td>Delayed (weeks)</td><td>Immediate</td><td>Immediate</td><td>Delayed + sequential</td></tr><tr><td><strong>Sample efficiency</strong></td><td>Low</td><td>Medium</td><td>High</td><td>Very low</td></tr><tr><td><strong>Complexity</strong></td><td>Simple</td><td>Simple</td><td>Moderate</td><td>High</td></tr></tbody></table>
<hr>
<h2 id="contextual-bandit-vs-multi-armed-bandit">Contextual Bandit vs Multi-Armed Bandit</h2>
<p><strong>The distinction:</strong> Do you have side information about the decision context?</p>
<p><strong>MAB:</strong> Learn global optimum. Same best action for everyone.</p>
<p><strong>When MAB suffices:</strong></p>
<ul>
<li>Homepage layout (users identical)</li>
<li>Global search ranking (uniform population)</li>
<li>Fixed-time promotions (time is only context)</li>
</ul>
<p><strong>Example MAB failure:</strong></p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>Notification timing for fitness app:</span></span>
<span class="line"><span></span></span>
<span class="line"><span>MAB: Learns 7 PM is optimal on average (60% prefer evening, 40% prefer morning)</span></span>
<span class="line"><span>→ Converges to 7 PM (majority wins)</span></span>
<span class="line"><span>→ 40% of users get suboptimal forever</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Contextual bandit: Uses activity patterns</span></span>
<span class="line"><span>→ Morning people → 6 AM, evening people → 7 PM</span></span>
<span class="line"><span>→ Personalized optimal for all</span></span>
<span class="line"><span></span></span></code></pre>
<p><strong>When CB is necessary:</strong></p>
<ul>
<li>Product recommendations (preferences vary by user)</li>
<li>Ad selection (relevance is contextual)</li>
<li>Content ranking (interests heterogeneous)</li>
</ul>
<p><strong>Critical question:</strong> Is personalization lift > complexity cost? If users homogeneous → MAB. If diverse → CB.</p>
<hr>
<h2 id="contextual-bandit-vs-ab-testing">Contextual Bandit vs A/B Testing</h2>
<h3 id="the-fundamental-tradeoff">The Fundamental Tradeoff</h3>



































<table><thead><tr><th>Dimension</th><th>A/B Testing</th><th>Contextual Bandit</th></tr></thead><tbody><tr><td><strong>Causal clarity</strong></td><td>✅ High (randomization)</td><td>⚠️ Moderate (needs careful analysis)</td></tr><tr><td><strong>Sample efficiency</strong></td><td>❌ Low (fixed allocation)</td><td>✅ High (adaptive)</td></tr><tr><td><strong>Personalization</strong></td><td>❌ None (aggregate)</td><td>✅ Automatic</td></tr><tr><td><strong>Speed to optimal</strong></td><td>❌ Slow (weeks)</td><td>✅ Fast (days)</td></tr><tr><td><strong>Organizational fit</strong></td><td>✅ Mature</td><td>⚠️ Requires ML culture</td></tr></tbody></table>
<h3 id="when-to-use-each">When to Use Each</h3>
<p><strong>A/B testing:</strong></p>
<ul>
<li>Irreversible platform decisions (pricing, core UX)</li>
<li>Need clean causal estimates (reporting/legal)</li>
<li>Testing hypotheses, not optimizing</li>
<li>Lack ML infrastructure</li>
<li>Abundant sample size</li>
</ul>
<p><strong>Contextual bandits:</strong></p>
<ul>
<li>Optimizing recommendations/rankings at scale</li>
<li>High user heterogeneity</li>
<li>Quick iteration on many variants</li>
<li>Significant opportunity cost</li>
<li>ML engineering resources available</li>
</ul>
<p><strong>Hybrid approach:</strong></p>
<ol>
<li>Week 1-2: A/B test (validate personalization beats baseline)</li>
<li>Week 3+: Deploy bandit (continuous optimization)</li>
<li>Periodic A/B audits (validate effectiveness)</li>
</ol>
<hr>
<h2 id="contextual-bandit-vs-full-reinforcement-learning">Contextual Bandit vs Full Reinforcement Learning</h2>
<p><strong>The boundary:</strong> Do current actions affect future rewards beyond immediate impact?</p>
<h3 id="contextual-bandits">Contextual Bandits</h3>
<p><strong>One-step decisions.</strong> Each round:</p>
<ol>
<li>Observe context xₜ</li>
<li>Choose action aₜ</li>
<li>Receive reward rₜ</li>
<li>Episode ends (next context independent)</li>
</ol>
<p><strong>Valid when:</strong></p>
<ul>
<li>Recommendations (showing A doesn’t prevent showing B later)</li>
<li>Ad serving (A doesn’t change future ad inventory)</li>
<li>Email timing (7 PM today doesn’t affect tomorrow’s options)</li>
</ul>
<h3 id="full-rl">Full RL</h3>
<p><strong>Sequential decisions.</strong> Markov Decision Process:</p>
<ul>
<li>States sₜ, actions aₜ</li>
<li>Transitions P(sₜ₊₁|sₜ, aₜ) ← Actions affect future states</li>
<li>Learn policy π(s) maximizing discounted cumulative reward</li>
</ul>
<p><strong>Required when:</strong></p>
<ul>
<li>Game playing (moves change board)</li>
<li>Inventory management (purchases deplete stock)</li>
<li>Dialogue systems (responses change conversation)</li>
</ul>
<h3 id="the-mdp-structure-test">The MDP Structure Test</h3>
<p><strong>Ask:</strong> “If I choose action A, does that change future actions available or desirable?”</p>
<ul>
<li><strong>No</strong> → Bandit (ad A doesn’t change ad inventory)</li>
<li><strong>Weakly</strong> → Borderline (recommendation A slightly shifts interests)</li>
<li><strong>Strongly</strong> → RL (chess move A fundamentally changes options)</li>
</ul>
<h3 id="sample-complexity-comparison">Sample Complexity Comparison</h3>
<p><strong>Sample complexity</strong> answers: “How many interactions needed to learn a near-optimal policy?”</p>
<p>Different from regret (cumulative loss), it measures the <strong>learning time</strong> itself.</p>





























<table><thead><tr><th>Problem Type</th><th>Regret Bound</th><th>Sample Complexity</th><th>What You’re Learning</th></tr></thead><tbody><tr><td><strong>Multi-armed bandit</strong></td><td>O(√T)</td><td>O(K log T / Δ²)</td><td>K numbers (mean rewards)</td></tr><tr><td><strong>Contextual bandit</strong></td><td>O(√(dT log T))</td><td>O(d log T / Δ²)</td><td>d-dimensional function</td></tr><tr><td><strong>Tabular MDP</strong></td><td>O(√(|S||A|T))</td><td>O(|S|²|A| / (1-γ)³Δ²)</td><td>|S|²|A| transitions</td></tr></tbody></table>
<p><strong>Where these come from:</strong></p>
<ul>
<li>
<p><strong>Δ²</strong> in denominator: From hypothesis testing (Hoeffding’s inequality). To distinguish options with gap Δ requires O(1/Δ²) samples. Smaller gaps need exponentially more data.</p>
</li>
<li>
<p><strong>d factor</strong> (contextual): Linear regression in d dimensions needs O(d/ε²) samples to achieve accuracy ε. With gap Δ, setting ε ~ Δ gives O(d/Δ²).</p>
</li>
<li>
<p><strong>|S|²</strong> (MDP): Must learn P(s’|s,a) for all state pairs. Need to visit each (s,a) multiple times and estimate |S| transition probabilities per pair = O(|S|²).</p>
</li>
<li>
<p><strong>(1-γ)³</strong> (MDP): Discount γ determines effective horizon 1/(1-γ). Longer horizons need more samples to propagate value information. Cube comes from value error + mixing time + Bellman error propagation.</p>
</li>
</ul>
<p><strong>Sources:</strong></p>
<ul>
<li>MAB: <a href="https://link.springer.com/article/10.1023/A:1013689704352">Auer et al. (2002)</a>, Lai &#x26; Robbins (1985)</li>
<li>Contextual: <a href="https://arxiv.org/abs/1003.0146">Li et al. (2010)</a>, <a href="https://papers.nips.cc/paper/2011/hash/e1d5be1c7f2f456670de3d53c7b54f4a-Abstract.html">Abbasi-Yadkori et al. (2011)</a></li>
<li>MDP: Kakade (2003), <a href="https://www.jmlr.org/papers/volume9/strehl08a/strehl08a.pdf">Strehl &#x26; Littman (2008)</a></li>
</ul>
<p><strong>Concrete example</strong> (Δ = 0.1 gap, 10K decisions):</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>MAB with K=10 arms:</span></span>
<span class="line"><span>  Sample complexity: 10 × log(10,000) / 0.1² ≈ 9,200 samples</span></span>
<span class="line"><span>  </span></span>
<span class="line"><span>Contextual with d=20 features:</span></span>
<span class="line"><span>  Sample complexity: 20 × log(10,000) / 0.1² ≈ 18,400 samples</span></span>
<span class="line"><span>  </span></span>
<span class="line"><span>Tabular MDP with |S|=100 states, |A|=10, γ=0.9:</span></span>
<span class="line"><span>  Sample complexity: 100² × 10 / (0.1)³ × 0.001 ≈ 10,000,000 samples</span></span>
<span class="line"><span></span></span></code></pre>
<p><strong>Why this matters:</strong> RL sample complexity is 100-1000× higher than contextual bandits for equivalent problems. This isn’t just theory—it explains why recommendation systems learn in days (bandits) while game AI needs millions of episodes (RL).</p>
<p><strong>RL sample complexity explodes</strong> with state space size and planning horizon. For large state spaces or long horizons, you need millions of samples.</p>
<hr>
<h2 id="real-world-application-domains">Real-World Application Domains</h2>
<h3 id="1-recommendation-systems">1. Recommendation Systems</h3>
<p><strong>Context:</strong> User demographics, history, session behavior, item attributes
<strong>Actions:</strong> Which items to recommend
<strong>Reward:</strong> Clicks, purchases, watch time</p>
<p><strong>Example:</strong> Netflix artwork personalization</p>
<h3 id="2-online-advertising">2. Online Advertising</h3>
<p><strong>Context:</strong> User profile, page content, ad attributes
<strong>Actions:</strong> Which ad to display
<strong>Reward:</strong> Clicks (CTR) or conversions (CPA)</p>
<p><strong>Challenges:</strong> Delayed conversion, budget constraints, dynamic bids</p>
<h3 id="3-content-ranking">3. Content Ranking</h3>
<p><strong>Context:</strong> User preferences, content recency, feed position
<strong>Actions:</strong> Order of content items
<strong>Reward:</strong> Engagement (clicks, dwell time, shares)</p>
<p><strong>Example:</strong> LinkedIn feed ranking</p>
<h3 id="4-personalized-notifications">4. Personalized Notifications</h3>
<p><strong>Context:</strong> User timezone, engagement history, content urgency
<strong>Actions:</strong> Send time (discrete slots)
<strong>Reward:</strong> Open rate, click-through</p>
<p><strong>Challenges:</strong> Delayed feedback, notification fatigue</p>
<h3 id="5-clinical-trials">5. Clinical Trials</h3>
<p><strong>Context:</strong> Patient demographics, medical history, biomarkers
<strong>Actions:</strong> Treatment assignment
<strong>Reward:</strong> Health outcomes</p>
<p><strong>Challenges:</strong> Safety constraints, small samples, delayed outcomes, ethics</p>
<hr>
<h2 id="decision-framework-is-your-problem-a-contextual-bandit">Decision Framework: Is Your Problem a Contextual Bandit?</h2>
<h3 id="-requirements-checklist">✅ Requirements Checklist</h3>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" disabled> <strong>Independent decisions:</strong> Choices don’t affect future options/rewards</li>
<li class="task-list-item"><input type="checkbox" disabled> <strong>Immediate feedback:</strong> Observe reward within seconds to days</li>
<li class="task-list-item"><input type="checkbox" disabled> <strong>Heterogeneous contexts:</strong> Different situations favor different actions</li>
<li class="task-list-item"><input type="checkbox" disabled> <strong>Multiple options:</strong> At least 2-3 actions to choose between</li>
<li class="task-list-item"><input type="checkbox" disabled> <strong>Sufficient scale:</strong> 1000+ decisions/day for worthwhile learning</li>
<li class="task-list-item"><input type="checkbox" disabled> <strong>Measurable reward:</strong> Clear metric capturing value</li>
</ul>
<h3 id="-red-flags">❌ Red Flags</h3>
<ul>
<li>Sequential dependencies → Full RL</li>
<li>Delayed feedback (>1 week) → Delayed RL</li>
<li>Homogeneous population → MAB</li>
<li>Only 1-2 total decisions → A/B testing</li>
<li>Ambiguous reward → Fix measurement first</li>
</ul>
<h3 id="decision-tree">Decision Tree</h3>
<div class="mermaid">
graph TD
    Start{Is your problem&#x3C;br/>a contextual bandit?}
    
    Start --> Q1{Decisions&#x3C;br/>independent?}
    Q1 -->|No| RL[Use Full RL&#x3C;br/>━━━━━━━━&#x3C;br/>Sequential dependencies&#x3C;br/>require planning]
    
    Q1 -->|Yes| Q2{Immediate&#x3C;br/>feedback?}
    Q2 -->|No| Delay[Use Delayed RL&#x3C;br/>━━━━━━━━&#x3C;br/>or wait for better&#x3C;br/>reward signal]
    
    Q2 -->|Yes| Q3{Context&#x3C;br/>matters?}
    Q3 -->|No| MAB[Use MAB&#x3C;br/>━━━━━━━━&#x3C;br/>Homogeneous population&#x3C;br/>No personalization]
    
    Q3 -->|Yes| Q4{Multiple&#x3C;br/>options?}
    Q4 -->|No| NoOpt[No optimization&#x3C;br/>━━━━━━━━&#x3C;br/>Nothing to choose&#x3C;br/>between]
    
    Q4 -->|Yes| Q5{Sufficient&#x3C;br/>scale?}
    Q5 -->|No| AB[Use A/B Testing&#x3C;br/>━━━━━━━━&#x3C;br/>Small sample&#x3C;br/>Statistical approach]
    
    Q5 -->|Yes| Q6{Measurable&#x3C;br/>reward?}
    Q6 -->|No| Fix[Fix measurement&#x3C;br/>━━━━━━━━&#x3C;br/>Define clear metric&#x3C;br/>first]
    
    Q6 -->|Yes| CB[✅ Use Contextual Bandit&#x3C;br/>━━━━━━━━&#x3C;br/>All criteria met&#x3C;br/>Ready to proceed]
    
    style Start stroke:#64748b,stroke-width:2.5px
    style CB stroke:#10b981,stroke-width:2.5px
    
    style Q1 stroke:#64748b,stroke-width:2px
    style Q2 stroke:#64748b,stroke-width:2px
    style Q3 stroke:#64748b,stroke-width:2px
    style Q4 stroke:#64748b,stroke-width:2px
    style Q5 stroke:#64748b,stroke-width:2px
    style Q6 stroke:#64748b,stroke-width:2px
    
    style RL stroke:#ef4444,stroke-width:2px
    style Delay stroke:#f59e0b,stroke-width:2px
    style MAB stroke:#06b6d4,stroke-width:2px
    style NoOpt stroke:#64748b,stroke-width:2px
    style AB stroke:#8b5cf6,stroke-width:2px
    style Fix stroke:#f59e0b,stroke-width:2px</div>
<hr>
<h2 id="key-takeaways">Key Takeaways</h2>
<p><strong>Use contextual bandits when:</strong></p>
<ul>
<li>Decisions independent (today doesn’t constrain tomorrow)</li>
<li>Feedback immediate (seconds to days)</li>
<li>Personalization matters (users/contexts differ)</li>
<li>Scale sufficient (1000+ decisions/day)</li>
</ul>
<p><strong>Choose alternatives when:</strong></p>
<ul>
<li>A/B testing: Need causal inference, low traffic, risk-averse</li>
<li>MAB: Homogeneous population</li>
<li>Full RL: Sequential dependencies, long horizons</li>
</ul>
<p><strong>Fundamental tradeoffs:</strong></p>
<ul>
<li>A/B: Clean inference ↔ Sample efficiency</li>
<li>Bandits: Adaptation ↔ Biased data</li>
<li>RL: Sequential planning ↔ Sample complexity</li>
</ul>
<p><strong>Quick validation:</strong>
✅ Independent? ✅ Immediate? ✅ Heterogeneous? ✅ Multiple options? ✅ Scale? ✅ Measurable?</p>
<p>All ✅ → Contextual bandit likely right choice</p>
<hr>
<h2 id="further-reading">Further Reading</h2>
<p><strong>Alternatives:</strong></p>
<ul>
<li>A/B Testing: <a href="https://experimentguide.com/">Trustworthy Online Controlled Experiments</a> (Kohavi et al.)</li>
<li>MAB: <a href="https://arxiv.org/abs/1904.07272">Introduction to Multi-Armed Bandits</a> (Lattimore &#x26; Szepesvári)</li>
<li>RL: <a href="http://incompleteideas.net/book/the-book-2nd.html">Sutton &#x26; Barto’s RL Book</a></li>
</ul>
<p><strong>Contextual bandits:</strong></p>
<ul>
<li><a href="https://arxiv.org/abs/1003.0146">LinUCB paper</a> (Li et al., 2010)</li>
<li><a href="https://lilianweng.github.io/posts/2018-01-23-multi-armed-bandit/">Lil’Log: Multi-Armed Bandits</a></li>
</ul>
<p><strong>Production:</strong></p>
<ul>
<li><a href="https://netflixtechblog.com/artwork-personalization-c589f074ad76">Netflix: Artwork Personalization</a></li>
</ul>]]></content:encoded><enclosure url="https://www.vitorsousa.com/_astro/ppo-llm-architecture.DgAorA6w.png" length="756000" type="image/png"/></item><item><title>Reinforcement Learning Foundations for LLM Alignment</title><link>https://www.vitorsousa.com/blog/rl-foundations/</link><guid isPermaLink="true">https://www.vitorsousa.com/blog/rl-foundations/</guid><description>Master the RL fundamentals powering modern LLM training: from MDPs and policy gradients through value functions and actor-critic methods. The mathematical foundations you need before diving into PPO, GRPO, and beyond.</description><pubDate>Sun, 11 Jan 2026 10:00:00 GMT</pubDate><content:encoded><![CDATA[<h1 id="implementing-contextual-bandits-complete-algorithm-guide">Implementing Contextual Bandits: Complete Algorithm Guide</h1>
<p><strong>Part 3 of 5:</strong> From Theory to Code</p>
<blockquote>
<p><strong>TL;DR:</strong> This post provides complete, production-ready implementations of the essential contextual bandit algorithms: ε-greedy (baseline), UCB/LinUCB (confidence-based), and Thompson Sampling (Bayesian). Includes algorithm selection guide with default hyperparameters, when to use each method, and practical tuning strategies.</p>
</blockquote>
<p><strong>Reading time:</strong> ~25 minutes</p>
<hr>
<h2 id="introduction-from-theory-to-practice">Introduction: From Theory to Practice</h2>
<p>Parts 1 and 2 covered when to use contextual bandits and the mathematical foundations. Now it’s time to implement.</p>
<p>This post provides <strong>complete, working code</strong> for the core algorithms you’ll actually deploy:</p>
<ol>
<li><strong>ε-greedy:</strong> Simplest baseline (start here)</li>
<li><strong>UCB/LinUCB:</strong> Confidence-based exploration (production workhorse)</li>
<li><strong>Thompson Sampling:</strong> Bayesian posterior sampling (often best empirically)</li>
</ol>
<p>Each algorithm includes:</p>
<ul>
<li>Full Python implementation</li>
<li>When to use it</li>
<li>Hyperparameter defaults and tuning</li>
<li>Strengths and limitations</li>
</ul>
<p>By the end, you’ll know which algorithm fits your problem and have code ready to run.</p>
<hr>
<h2 id="algorithm-selection-quick-start-guide">Algorithm Selection: Quick Start Guide</h2>
<p>Before diving into implementations, use this table to pick your starting point:</p>





















































<table><thead><tr><th><strong>Your Situation</strong></th><th><strong>Recommended Algorithm</strong></th><th><strong>Default Hyperparameters</strong></th><th><strong>Why This Works</strong></th></tr></thead><tbody><tr><td><strong>Few actions (K &#x3C; 20), no context</strong></td><td>ε-greedy or UCB1</td><td>ε = 0.1 with decay 1/t</td><td>Simple, interpretable, proven</td></tr><tr><td><strong>Few actions, simple context (d &#x3C; 50)</strong></td><td>LinUCB</td><td>α = 1.0, λ = 1.0</td><td>Efficient, confidence-based exploration</td></tr><tr><td><strong>Medium context (d = 50-500), linear</strong></td><td>LinUCB or Linear Thompson</td><td>α = 1.0, λ = 1.0, σ = 1.0</td><td>Balance of efficiency and exploration</td></tr><tr><td><strong>High-dim context (d > 500), linear</strong></td><td>LinUCB with feature selection</td><td>α = 0.5, λ = 10.0</td><td>Regularization prevents overfitting</td></tr><tr><td><strong>Non-stationary environment</strong></td><td>ε-greedy (constant) or Discounted TS</td><td>ε = 0.15, γ = 0.9999</td><td>Continuous exploration adapts to drift</td></tr><tr><td><strong>Need interpretability</strong></td><td>LinUCB</td><td>α = 1.0, λ = 1.0</td><td>Can examine learned weights</td></tr><tr><td><strong>Maximum performance</strong></td><td>Thompson Sampling</td><td>Use defaults from algorithm</td><td>Best empirical results</td></tr></tbody></table>
<p><strong>Start simple:</strong> Begin with ε-greedy to validate your setup. Upgrade to LinUCB or Thompson Sampling once you’ve confirmed the basics work.</p>
<hr>
<h2 id="ε-greedy-the-simplest-baseline">ε-greedy: The Simplest Baseline</h2>
<p>ε-greedy is the go-to baseline. It’s easy to implement, easy to explain to stakeholders, and works reasonably well.</p>
<h3 id="algorithm">Algorithm</h3>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#E1E4E8">At each </span><span style="color:#79B8FF">round</span><span style="color:#E1E4E8"> t:</span></span>
<span class="line"><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">. With probability ε: Choose random action (explore)</span></span>
<span class="line"><span style="color:#79B8FF">2</span><span style="color:#E1E4E8">. With probability </span><span style="color:#79B8FF">1</span><span style="color:#F97583">-</span><span style="color:#E1E4E8">ε: Choose action </span><span style="color:#F97583">with</span><span style="color:#E1E4E8"> highest estimated reward (exploit)</span></span>
<span class="line"><span style="color:#79B8FF">3</span><span style="color:#E1E4E8">. Observe reward, update estimates</span></span>
<span class="line"></span></code></pre>
<h3 id="mathematical-formulation">Mathematical Formulation</h3>
<span class="katex-error" title="ParseError: KaTeX parse error: Expected &#x27;EOF&#x27;, got &#x27;&#x26;&#x27; at position 33: …t{\mu}_{a&#x27;}(x) &#x26;̲ \text{with pro…" style="color:#cc0000">\arg\max_{a'} \hat{\mu}_{a'}(x) &#x26; \text{with probability } 1-\varepsilon \\
\text{uniform}(A) &#x26; \text{with probability } \varepsilon
\end{cases}$

### Complete Implementation

```python
import numpy as np

class EpsilonGreedy:
    """
    ε-greedy bandit for multi-armed bandit (no context).
    
    Simple baseline: explores randomly with probability ε,
    exploits best action with probability 1-ε.
    """
    def __init__(self, n_actions, epsilon=0.1):
        """
        Args:
            n_actions: Number of available actions
            epsilon: Exploration probability (0 = pure exploit, 1 = pure explore)
        """
        self.n_actions = n_actions
        self.epsilon = epsilon
        
        # Track statistics for each action
        self.counts = np.zeros(n_actions)      # Number of times each action tried
        self.values = np.zeros(n_actions)      # Estimated mean reward for each action
    
    def select_action(self, context=None):
        """
        Select action using ε-greedy strategy.
        
        Args:
            context: Ignored for basic ε-greedy (non-contextual)
            
        Returns:
            action: Index of chosen action (0 to n_actions-1)
        """
        if np.random.random() &#x3C; self.epsilon:
            # Explore: choose random action
            return np.random.randint(self.n_actions)
        else:
            # Exploit: choose action with highest estimated value
            return np.argmax(self.values)
    
    def update(self, action, reward):
        """
        Update estimates based on observed reward.
        
        Args:
            action: Action that was taken
            reward: Observed reward
        """
        self.counts[action] += 1
        n = self.counts[action]
        
        # Incremental mean update: μ_new = μ_old + (reward - μ_old) / n
        self.values[action] += (reward - self.values[action]) / n
    
    def get_state(self):
        """Return current state for logging/debugging."""
        return {
            'counts': self.counts.copy(),
            'values': self.values.copy(),
            'epsilon': self.epsilon
        }


# Example usage
if __name__ == "__main__":
    # Simulate 3 actions with different true rewards
    true_rewards = [0.3, 0.5, 0.7]  # Action 2 is best
    n_rounds = 1000
    
    bandit = EpsilonGreedy(n_actions=3, epsilon=0.1)
    
    total_reward = 0
    for t in range(n_rounds):
        # Select action
        action = bandit.select_action()
        
        # Simulate reward (Bernoulli with true probability)
        reward = 1 if np.random.random() &#x3C; true_rewards[action] else 0
        
        # Update bandit
        bandit.update(action, reward)
        total_reward += reward
    
    print(f"Total reward: {total_reward}/{n_rounds}")
    print(f"Average reward: {total_reward/n_rounds:.3f}")
    print(f"Optimal average: {max(true_rewards):.3f}")
    print(f"\nLearned values: {bandit.values}")
    print(f"Action counts: {bandit.counts}")
```

### Decaying Exploration

Fixed ε wastes exploration forever. Better to decay over time:

```python
class DecayingEpsilonGreedy(EpsilonGreedy):
    """
    ε-greedy with decaying exploration rate.
    
    Starts with high exploration, gradually increases exploitation.
    """
    def __init__(self, n_actions, epsilon_0=1.0, decay_rate=0.01, min_epsilon=0.01):
        """
        Args:
            epsilon_0: Initial exploration probability
            decay_rate: How fast to decay (higher = faster decay)
            min_epsilon: Minimum exploration probability
        """
        super().__init__(n_actions, epsilon=epsilon_0)
        self.epsilon_0 = epsilon_0
        self.decay_rate = decay_rate
        self.min_epsilon = min_epsilon
        self.t = 0
    
    def select_action(self, context=None):
        """Select action with decaying ε."""
        # Update epsilon: ε(t) = max(min_ε, ε₀ / (1 + decay_rate * t))
        self.t += 1
        self.epsilon = max(
            self.min_epsilon,
            self.epsilon_0 / (1 + self.decay_rate * self.t)
        )
        
        return super().select_action(context)
```

### When to Use ε-greedy

**✅ Use when:**
- You need a simple, interpretable baseline
- Explaining the algorithm to stakeholders
- Testing your infrastructure before deploying complex methods
- Environment is non-stationary (constant ε helps track drift)
- Many actions (simpler than maintaining confidence bounds for thousands of actions)

**❌ Limitations:**
- Explores uniformly (wastes trials on clearly bad actions)
- Suboptimal regret: O(T^(2/3)) vs O(√T) for UCB/Thompson
- Requires tuning ε (too high wastes traffic, too low risks convergence)

### Hyperparameter Tuning

| Parameter | Typical Range | Too Low → Problem | Too High → Problem |
|-----------|---------------|-------------------|-------------------|
| **ε (exploration)** | 0.05 - 0.2 | Premature convergence | Wasted traffic on bad actions |
| **decay_rate** | 0.001 - 0.1 | Too slow adaptation | Stops exploring too quickly |
| **min_epsilon** | 0.01 - 0.05 | Eventually stops learning | Never fully exploits |

**Rule of thumb:**
- **Stationary environment:** Start ε = 0.1, decay to min = 0.01
- **Non-stationary:** Use constant ε = 0.15 (no decay)
- **Quick testing:** ε = 0.2 (more exploration, faster learning signal)

---

## UCB: Confidence-Based Exploration

UCB (Upper Confidence Bound) improves on ε-greedy by directing exploration toward **uncertain** actions, not random ones.

### Algorithm (UCB1 for MAB)

```python
At each round t:
1. Compute UCB for each action: UCB(a) = μ̂_a + sqrt(2 log t / n_a)
2. Choose action with highest UCB
3. Observe reward, update estimates
```

### Implementation

```python
class UCB1:
    """
    UCB1 algorithm for multi-armed bandits.
    
    Uses optimism under uncertainty: adds confidence bonus to each
    action's estimate, naturally balancing exploration and exploitation.
    """
    def __init__(self, n_actions):
        """
        Args:
            n_actions: Number of available actions
        """
        self.n_actions = n_actions
        self.counts = np.zeros(n_actions)
        self.values = np.zeros(n_actions)
        self.t = 0  # Total rounds
    
    def select_action(self, context=None):
        """
        Select action using UCB strategy.
        
        Returns:
            action: Index of action with highest UCB
        """
        self.t += 1
        
        # Phase 1: Try each action once (initialization)
        for a in range(self.n_actions):
            if self.counts[a] == 0:
                return a
        
        # Phase 2: Compute UCB for each action
        ucb_values = np.zeros(self.n_actions)
        for a in range(self.n_actions):
            # UCB = estimate + confidence bonus
            confidence_bonus = np.sqrt(2 * np.log(self.t) / self.counts[a])
            ucb_values[a] = self.values[a] + confidence_bonus
        
        return np.argmax(ucb_values)
    
    def update(self, action, reward):
        """Update estimates based on observed reward."""
        self.counts[action] += 1
        n = self.counts[action]
        self.values[action] += (reward - self.values[action]) / n
    
    def get_ucb_values(self):
        """Return current UCB values for debugging."""
        if self.t == 0:
            return np.zeros(self.n_actions)
        
        ucb_values = np.zeros(self.n_actions)
        for a in range(self.n_actions):
            if self.counts[a] > 0:
                confidence_bonus = np.sqrt(2 * np.log(self.t) / self.counts[a])
                ucb_values[a] = self.values[a] + confidence_bonus
        return ucb_values
```

### Visual Intuition

Remember from Part 2: UCB gives higher bonuses to under-explored actions.

```mermaid

graph LR
    subgraph Action_A[" "]
        A_Est[Estimate: 0.5&#x3C;br/>Trials: 100&#x3C;br/>Bonus: +0.1]
        A_UCB[UCB: 0.6&#x3C;br/>Well-explored]
    end
    
    subgraph Action_B[" "]
        B_Est[Estimate: 0.4&#x3C;br/>Trials: 10&#x3C;br/>Bonus: +0.3]
        B_UCB[UCB: 0.7&#x3C;br/>⚡ Selected]
    end
    
    A_Est --> A_UCB
    B_Est --> B_UCB
    
    B_UCB -.chosen.-> Winner[Action B wins&#x3C;br/>despite lower estimate]
    
    style Action_A stroke:none
    style Action_B stroke:none
    
    style A_Est stroke:#10b981,stroke-width:2px
    style A_UCB stroke:#10b981,stroke-width:2px
    style B_Est stroke:#f59e0b,stroke-width:2px
    style B_UCB stroke:#f59e0b,stroke-width:2.5px
    style Winner stroke:#06b6d4,stroke-width:2px
    
    linkStyle 2 stroke:#06b6d4,stroke-width:2px,stroke-dasharray:5
```

### When to Use UCB1

**✅ Use when:**
- Stochastic rewards (i.i.d. from fixed distributions)
- Moderate number of actions (K = 2-100)
- Want theoretical guarantees (provable regret bounds)
- No context available (pure MAB problem)

**❌ Limitations:**
- No context (doesn't use side information)
- Assumes stationarity (struggles when distributions drift)
- Can over-explore compared to Thompson Sampling

---

## LinUCB: UCB for Contextual Bandits

LinUCB extends UCB to contextual bandits with linear reward models. This is the **production workhorse** for most applications.

### Algorithm

```python
At each round t:
1. Observe context x_t
2. For each action a:
   - Estimate θ̂_a via ridge regression
   - Compute confidence radius: σ_a(x) = α · sqrt(x^T A_a^-1 x)
   - Compute UCB: UCB_a(x) = θ̂_a^T x + σ_a(x)
3. Choose a_t = argmax_a UCB_a(x_t)
4. Observe reward, update estimates
```

### Complete Implementation

```python
class LinUCB:
    """
    Linear Upper Confidence Bound algorithm.
    
    Assumes linear reward model: r(x,a) = θ_a^T x + ε
    Uses ridge regression to estimate parameters and confidence ellipsoids
    to guide exploration.
    """
    def __init__(self, n_actions, n_features, alpha=1.0, lambda_=1.0):
        """
        Args:
            n_actions: Number of available actions
            n_features: Dimensionality of context features
            alpha: Exploration parameter (higher = more exploration)
            lambda_: Regularization parameter (higher = more regularization)
        """
        self.n_actions = n_actions
        self.n_features = n_features
        self.alpha = alpha
        self.lambda_ = lambda_
        
        # Initialize for each action
        # A_a = X_a^T X_a + λI (regularized Gram matrix)
        # b_a = X_a^T r_a (feature-reward products)
        self.A = [lambda_ * np.eye(n_features) for _ in range(n_actions)]
        self.b = [np.zeros(n_features) for _ in range(n_actions)]
    
    def select_action(self, context):
        """
        Select action with highest UCB.
        
        Args:
            context: Feature vector (array of shape [n_features])
            
        Returns:
            action: Index of chosen action
        """
        context = np.array(context).reshape(-1, 1)  # Column vector
        ucb_values = np.zeros(self.n_actions)
        
        for a in range(self.n_actions):
            # Compute parameter estimate: θ̂_a = A_a^-1 b_a
            A_inv = np.linalg.inv(self.A[a])
            theta = A_inv @ self.b[a]
            
            # Predicted reward
            predicted_reward = theta.T @ context
            
            # Confidence bonus: α * sqrt(x^T A_a^-1 x)
            confidence_bonus = self.alpha * np.sqrt(
                context.T @ A_inv @ context
            )
            
            # UCB = predicted reward + confidence bonus
            ucb_values[a] = predicted_reward + confidence_bonus
        
        return np.argmax(ucb_values)
    
    def update(self, action, context, reward):
        """
        Update estimates for chosen action.
        
        Args:
            action: Action that was taken
            context: Feature vector that was observed
            reward: Reward that was received
        """
        context = np.array(context).reshape(-1, 1)
        
        # Update A_a = A_a + x x^T
        self.A[action] += context @ context.T
        
        # Update b_a = b_a + r x
        self.b[action] += reward * context.squeeze()
    
    def get_theta(self, action):
        """Get learned parameter vector for an action."""
        A_inv = np.linalg.inv(self.A[action])
        return A_inv @ self.b[action]
    
    def get_confidence_radius(self, action, context):
        """Get confidence radius for action in given context."""
        context = np.array(context).reshape(-1, 1)
        A_inv = np.linalg.inv(self.A[action])
        return self.alpha * np.sqrt(context.T @ A_inv @ context)[0, 0]


# Example usage
if __name__ == "__main__":
    # Simulate contextual bandit with 3 actions, 5 features
    n_actions = 3
    n_features = 5
    n_rounds = 1000
    
    # True parameters (unknown to algorithm)
    true_theta = [
        np.array([0.1, 0.2, -0.1, 0.3, 0.1]),  # Action 0
        np.array([0.3, 0.1, 0.2, -0.1, 0.2]),  # Action 1
        np.array([0.2, 0.3, 0.1, 0.2, 0.3]),   # Action 2
    ]
    
    bandit = LinUCB(n_actions, n_features, alpha=1.0, lambda_=1.0)
    
    total_reward = 0
    for t in range(n_rounds):
        # Generate random context
        context = np.random.randn(n_features)
        
        # Select action
        action = bandit.select_action(context)
        
        # Simulate reward: r = θ_a^T x + noise
        true_reward = true_theta[action] @ context
        noise = np.random.normal(0, 0.1)
        reward = true_reward + noise
        
        # Update bandit
        bandit.update(action, context, reward)
        total_reward += reward
    
    print(f"Average reward: {total_reward/n_rounds:.3f}")
    print("\nLearned parameters vs True parameters:")
    for a in range(n_actions):
        learned = bandit.get_theta(a)
        print(f"Action {a}:")
        print(f"  Learned: {learned}")
        print(f"  True:    {true_theta[a]}")
        print(f"  Error:   {np.linalg.norm(learned - true_theta[a]):.3f}")
```

### When to Use LinUCB

**✅ Use when:**
- Have context features (user, item, situation)
- Rewards are approximately linear in features
- Need interpretable model (can examine learned θ weights)
- Context dimensionality is moderate (d = 10-1000)
- Want provable regret guarantees: O(d√(T log T))

**❌ Limitations:**
- Assumes linear reward structure (misspecification if nonlinear)
- Computational cost grows with d² (matrix inversion)
- May underperform Thompson Sampling empirically

### Hyperparameter Tuning

| Parameter | Typical Range | Effect | Tuning Strategy |
|-----------|---------------|--------|-----------------|
| **α (exploration)** | 0.1 - 2.0 | Controls exploration intensity | Start 1.0, increase if under-exploring |
| **λ (regularization)** | 0.1 - 10.0 | Prevents overfitting, stabilizes estimates | Increase if high variance or correlated features |

**Quick tuning:**
1. Start with α = 1.0, λ = 1.0 (defaults)
2. If regret grows linearly → increase α (more exploration)
3. If high variance in estimates → increase λ (more regularization)
4. If many features (d > 100) → use λ = 5-10

---

## Thompson Sampling: Bayesian Posterior Sampling

Thompson Sampling takes a Bayesian approach: maintain probability distributions over parameters, sample from posteriors to choose actions.

### Algorithm

```python
At each round t:
1. For each action a:
   - Sample θ̃_a ~ P(θ_a | data)
2. Choose a_t = argmax_a θ̃_a^T x_t (using sampled parameters)
3. Observe reward, update posteriors
```

### Bernoulli Thompson Sampling (for binary rewards)

```python
class BernoulliThompsonSampling:
    """
    Thompson Sampling for Bernoulli bandits (binary rewards).
    
    Uses Beta-Bernoulli conjugacy for efficient posterior updates.
    Natural exploration-exploitation balance through posterior sampling.
    """
    def __init__(self, n_actions):
        """
        Args:
            n_actions: Number of available actions
        """
        self.n_actions = n_actions
        
        # Beta prior parameters: Beta(α, β)
        # Start with uniform prior: Beta(1, 1)
        self.alpha = np.ones(n_actions)  # Successes + 1
        self.beta = np.ones(n_actions)   # Failures + 1
    
    def select_action(self, context=None):
        """
        Sample from posterior and choose action with highest sample.
        
        Returns:
            action: Index of chosen action
        """
        # Sample θ̃_a ~ Beta(α_a, β_a) for each action
        samples = np.random.beta(self.alpha, self.beta)
        
        # Choose action with highest sampled value
        return np.argmax(samples)
    
    def update(self, action, reward):
        """
        Update posterior based on observed reward.
        
        Args:
            action: Action that was taken
            reward: Binary reward (0 or 1)
        """
        # Bayesian update for Beta-Bernoulli
        if reward == 1:
            self.alpha[action] += 1  # Observed success
        else:
            self.beta[action] += 1   # Observed failure
    
    def get_posterior_stats(self):
        """Return posterior means and standard deviations."""
        means = self.alpha / (self.alpha + self.beta)
        variances = (self.alpha * self.beta) / (
            (self.alpha + self.beta)**2 * (self.alpha + self.beta + 1)
        )
        return means, np.sqrt(variances)
```

### Gaussian Thompson Sampling (for continuous rewards)

```python
class GaussianThompsonSampling:
    """
    Thompson Sampling for Gaussian bandits (continuous rewards).
    
    Assumes rewards are normally distributed. Uses Bayesian updating
    for Gaussian with known variance.
    """
    def __init__(self, n_actions, prior_mean=0.0, prior_var=1.0, noise_var=1.0):
        """
        Args:
            n_actions: Number of available actions
            prior_mean: Prior mean for each action's reward
            prior_var: Prior variance for each action's reward
            noise_var: Known noise variance in rewards
        """
        self.n_actions = n_actions
        self.noise_var = noise_var
        
        # Posterior parameters (start with prior)
        self.means = np.ones(n_actions) * prior_mean
        self.variances = np.ones(n_actions) * prior_var
        self.counts = np.zeros(n_actions)
    
    def select_action(self, context=None):
        """
        Sample from Gaussian posterior for each action.
        
        Returns:
            action: Index of action with highest sample
        """
        # Sample θ̃_a ~ N(μ_a, σ²_a) for each action
        samples = np.random.normal(self.means, np.sqrt(self.variances))
        
        return np.argmax(samples)
    
    def update(self, action, reward):
        """
        Bayesian update for Gaussian with known variance.
        
        Args:
            action: Action that was taken
            reward: Observed continuous reward
        """
        # Bayesian update using precision (inverse variance)
        precision_prior = 1 / self.variances[action]
        precision_data = 1 / self.noise_var
        
        # Posterior precision = prior precision + data precision
        posterior_precision = precision_prior + precision_data
        
        # Posterior mean is weighted average
        posterior_mean = (
            (precision_prior * self.means[action] + precision_data * reward)
            / posterior_precision
        )
        
        # Update
        self.means[action] = posterior_mean
        self.variances[action] = 1 / posterior_precision
        self.counts[action] += 1
```

### Linear Thompson Sampling

```python
class LinearThompsonSampling:
    """
    Thompson Sampling for linear contextual bandits.
    
    Assumes linear reward model with Gaussian noise.
    Samples parameters from posterior, chooses action with highest
    predicted reward under sampled parameters.
    """
    def __init__(self, n_actions, n_features, lambda_=1.0, noise_var=1.0):
        """
        Args:
            n_actions: Number of available actions
            n_features: Dimensionality of context features
            lambda_: Regularization parameter (prior precision)
            noise_var: Noise variance in rewards
        """
        self.n_actions = n_actions
        self.n_features = n_features
        self.lambda_ = lambda_
        self.noise_var = noise_var
        
        # Sufficient statistics for each action
        # Same as LinUCB but interpret as posterior parameters
        self.A = [lambda_ * np.eye(n_features) for _ in range(n_actions)]
        self.b = [np.zeros(n_features) for _ in range(n_actions)]
    
    def select_action(self, context):
        """
        Sample θ from posterior, choose action with highest θ^T x.
        
        Args:
            context: Feature vector
            
        Returns:
            action: Index of chosen action
        """
        context = np.array(context).reshape(-1, 1)
        sampled_rewards = np.zeros(self.n_actions)
        
        for a in range(self.n_actions):
            # Posterior distribution: θ_a | data ~ N(μ_a, Σ_a)
            A_inv = np.linalg.inv(self.A[a])
            theta_mean = A_inv @ self.b[a]
            theta_cov = self.noise_var * A_inv
            
            # Sample θ̃_a ~ N(μ_a, Σ_a)
            theta_sample = np.random.multivariate_normal(theta_mean, theta_cov)
            
            # Compute predicted reward: r̃ = θ̃_a^T x
            sampled_rewards[a] = theta_sample.T @ context.squeeze()
        
        return np.argmax(sampled_rewards)
    
    def update(self, action, context, reward):
        """
        Update posterior for chosen action.
        
        Args:
            action: Action that was taken
            context: Feature vector
            reward: Observed reward
        """
        context = np.array(context).reshape(-1, 1)
        
        # Same update as LinUCB (Bayesian interpretation)
        self.A[action] += context @ context.T
        self.b[action] += reward * context.squeeze()
    
    def get_posterior_params(self, action):
        """Get posterior mean and covariance for an action."""
        A_inv = np.linalg.inv(self.A[action])
        mean = A_inv @ self.b[action]
        cov = self.noise_var * A_inv
        return mean, cov
```

### When to Use Thompson Sampling

**✅ Use when:**
- Want best empirical performance (often beats UCB)
- Don't want to tune exploration parameters (self-tuning)
- Can afford computational cost of sampling
- Environment may be non-stationary (posterior naturally adapts)

**❌ Limitations:**
- More computationally expensive (sampling from posteriors)
- Less interpretable than UCB confidence bounds
- Theoretical guarantees weaker (though still optimal)

### Why Thompson Sampling Works So Well

**Probability matching:** The probability of choosing action a equals the probability that a is optimal.

**Natural exploration schedule:**
- Early: Posteriors are diffuse → high variance in samples → exploration
- Late: Posteriors concentrate → low variance → exploitation

No manual decay schedule needed. The Bayesian framework handles it automatically.

---

## Algorithm Comparison

### Performance Comparison

```mermaid

graph TB
    subgraph Comparison["Algorithm Performance Spectrum"]
        direction LR
        
        EG[ε-greedy&#x3C;br/>━━━━━━━━&#x3C;br/>Regret: O(T^2/3)&#x3C;br/>Simple, suboptimal]
        
        UCB[UCB / LinUCB&#x3C;br/>━━━━━━━━&#x3C;br/>Regret: O(√T log T)&#x3C;br/>Provable, interpretable]
        
        TS[Thompson Sampling&#x3C;br/>━━━━━━━━&#x3C;br/>Regret: O(√T)&#x3C;br/>Optimal, self-tuning]
    end
    
    EG -.worse.-> UCB
    UCB -.comparable.-> TS
    
    style EG stroke:#f59e0b,stroke-width:2px
    style UCB stroke:#10b981,stroke-width:2px
    style TS stroke:#06b6d4,stroke-width:2.5px
    style Comparison stroke:#64748b,stroke-width:2px
    
    linkStyle 0 stroke:#ef4444,stroke-width:2px,stroke-dasharray:3
    linkStyle 1 stroke:#10b981,stroke-width:2px,stroke-dasharray:3
```

### Detailed Comparison Table

| Algorithm | Regret | Exploration | Tuning | Computational Cost | Use When |
|-----------|--------|-------------|--------|-------------------|----------|
| **ε-greedy** | O(T^(2/3)) | Random | Tune ε | O(1) per round | Simple baseline, non-stationary |
| **UCB1** | O(√(KT log T)) | Confidence-based | None | O(K) per round | MAB, theoretical guarantees |
| **LinUCB** | O(d√(T log T)) | Confidence ellipsoid | Tune α, λ | O(d²) per round | Linear rewards, interpretability |
| **Thompson (Bernoulli)** | O(√T) | Posterior sampling | None | O(K) per round | Binary rewards, best empirical |
| **Thompson (Linear)** | O(√T) | Posterior sampling | Tune λ | O(d³) per round | Linear rewards, self-tuning |

### Practical Recommendation

**For most production use cases:**

1. **Start:** ε-greedy with ε = 0.1 (validate infrastructure)
2. **Upgrade:** LinUCB with α = 1.0, λ = 1.0 (production baseline)
3. **Optimize:** Linear Thompson Sampling (often best performance)

**Thompson Sampling often wins empirically** despite similar theoretical bounds to UCB. The self-tuning exploration is powerful in practice.

---

## Hyperparameter Tuning Guide

### General Tuning Strategy

```python
def tune_hyperparameters(algorithm_class, param_grid, data, metric='reward'):
    """
    Simple hyperparameter tuning via grid search.
    
    Args:
        algorithm_class: Bandit algorithm class
        param_grid: Dict of parameter ranges to try
        data: Historical data (context, action, reward) tuples
        metric: What to optimize ('reward', 'regret', etc.)
    
    Returns:
        best_params: Best hyperparameter configuration
        results: Performance for all configurations
    """
    from itertools import product
    
    # Generate all combinations
    param_names = list(param_grid.keys())
    param_values = list(param_grid.values())
    combinations = list(product(*param_values))
    
    results = []
    for combo in combinations:
        params = dict(zip(param_names, combo))
        
        # Initialize algorithm with these params
        bandit = algorithm_class(**params)
        
        # Replay historical data
        total_reward = 0
        for context, action, reward in data:
            # Only update if this is what bandit would have chosen
            chosen_action = bandit.select_action(context)
            if chosen_action == action:
                bandit.update(action, context, reward)
                total_reward += reward
        
        results.append({
            'params': params,
            'reward': total_reward,
            'coverage': sum(1 for c, a, r in data 
                           if bandit.select_action(c) == a) / len(data)
        })
    
    # Find best
    best = max(results, key=lambda x: x[metric])
    return best['params'], results


# Example usage
param_grid = {
    'n_actions': [10],  # Fixed
    'n_features': [20],  # Fixed
    'alpha': [0.5, 1.0, 2.0],
    'lambda_': [0.1, 1.0, 10.0]
}

# best_params, all_results = tune_hyperparameters(LinUCB, param_grid, historical_data)
```

### Parameter Guidelines

| Algorithm | Parameter | Default | When to Increase | When to Decrease |
|-----------|-----------|---------|------------------|------------------|
| **ε-greedy** | ε | 0.1 | Regret growing linearly | Too many suboptimal actions |
| **LinUCB** | α | 1.0 | Under-exploration (linear regret) | Over-exploration (slow convergence) |
| **LinUCB** | λ | 1.0 | High variance, correlated features | Underfitting, slow learning |
| **Thompson** | λ | 1.0 | High variance | Underfitting |
| **Thompson** | noise_var | 1.0 | Rewards more noisy | Rewards less noisy |

### Quick Diagnostic Checklist

| Symptom | Likely Cause | Fix |
|---------|--------------|-----|
| **Regret grows linearly** | Under-exploration | Increase ε or α |
| **High variance in estimates** | Need more regularization | Increase λ |
| **Slow convergence** | Over-exploration | Decrease ε or α |
| **All traffic on one action** | Premature convergence | Increase exploration |
| **Random action selection** | Over-exploration | Decrease ε |

---

## Complete Working Example

Here's a full example comparing all algorithms on the same problem:

```python
import numpy as np
import matplotlib.pyplot as plt

# Simulate contextual bandit environment
class SimulatedEnvironment:
    """Simulated environment for testing bandit algorithms."""
    def __init__(self, n_actions=3, n_features=5):
        self.n_actions = n_actions
        self.n_features = n_features
        
        # True parameters (unknown to algorithms)
        self.true_theta = [
            np.random.randn(n_features) for _ in range(n_actions)
        ]
        
        # Compute optimal expected reward for regret calculation
        self.optimal_expected_reward = None
    
    def get_context(self):
        """Generate random context."""
        return np.random.randn(self.n_features)
    
    def get_reward(self, context, action):
        """Generate reward for context-action pair."""
        true_reward = self.true_theta[action] @ context
        noise = np.random.normal(0, 0.1)
        return true_reward + noise
    
    def get_optimal_action(self, context):
        """Return optimal action for context (for regret calculation)."""
        expected_rewards = [theta @ context for theta in self.true_theta]
        return np.argmax(expected_rewards)


def run_experiment(bandit, env, n_rounds=1000):
    """
    Run bandit algorithm on environment.
    
    Returns:
        rewards: List of rewards per round
        regrets: List of regret per round
        actions: List of actions chosen per round
    """
    rewards = []
    regrets = []
    actions_chosen = []
    
    for t in range(n_rounds):
        # Get context
        context = env.get_context()
        
        # Bandit chooses action
        action = bandit.select_action(context)
        
        # Observe reward
        reward = env.get_reward(context, action)
        
        # Compute regret
        optimal_action = env.get_optimal_action(context)
        optimal_reward = env.get_reward(context, optimal_action)
        regret = optimal_reward - reward
        
        # Update bandit
        bandit.update(action, context, reward)
        
        # Log
        rewards.append(reward)
        regrets.append(regret)
        actions_chosen.append(action)
    
    return rewards, regrets, actions_chosen


# Run comparison
if __name__ == "__main__":
    n_rounds = 1000
    n_actions = 3
    n_features = 5
    
    # Create environment
    env = SimulatedEnvironment(n_actions, n_features)
    
    # Test algorithms
    algorithms = {
        'ε-greedy (ε=0.1)': EpsilonGreedy(n_actions, epsilon=0.1),
        'LinUCB (α=1.0)': LinUCB(n_actions, n_features, alpha=1.0),
        'Linear Thompson': LinearThompsonSampling(n_actions, n_features)
    }
    
    results = {}
    for name, bandit in algorithms.items():
        rewards, regrets, actions = run_experiment(bandit, env, n_rounds)
        results[name] = {
            'rewards': rewards,
            'regrets': regrets,
            'cumulative_regret': np.cumsum(regrets)
        }
        
        print(f"\n{name}:")
        print(f"  Average reward: {np.mean(rewards):.3f}")
        print(f"  Cumulative regret: {np.sum(regrets):.1f}")
    
    # Plot cumulative regret
    plt.figure(figsize=(10, 6))
    for name, data in results.items():
        plt.plot(data['cumulative_regret'], label=name, linewidth=2)
    
    plt.xlabel('Round')
    plt.ylabel('Cumulative Regret')
    plt.title('Algorithm Comparison: Cumulative Regret Over Time')
    plt.legend()
    plt.grid(True, alpha=0.3)
    plt.tight_layout()
    plt.savefig('bandit_comparison.png', dpi=150)
    print("\nPlot saved as 'bandit_comparison.png'")
```

---

## Key Takeaways

**Essential concepts:**

 **Start simple, upgrade deliberately**
   - Begin with ε-greedy to validate infrastructure
   - Upgrade to LinUCB for production baseline
   - Use Thompson Sampling for best empirical performance

 **Algorithm selection depends on your problem:**
   - No context → ε-greedy or UCB1
   - Linear context (d &#x3C; 500) → LinUCB or Linear Thompson
   - Need interpretability → LinUCB (examine θ weights)
   - Maximum performance → Thompson Sampling

 **Default hyperparameters work well:**
   - ε-greedy: ε = 0.1 with decay 1/t
   - LinUCB: α = 1.0, λ = 1.0
   - Thompson: Use defaults, tune only if needed

 **Tuning based on symptoms:**
   - Linear regret → Increase exploration (ε or α)
   - High variance → Increase regularization (λ)
   - Slow convergence → Decrease exploration

 **Thompson Sampling often wins in practice**
   - Self-tuning exploration (no manual decay)
   - Best empirical results in many domains
   - Slight computational overhead worth it

**Practical workflow:**

1. **Week 1:** Implement ε-greedy, validate logging
2. **Week 2:** Upgrade to LinUCB or Thompson
3. **Week 3:** Tune hyperparameters on historical data
4. **Week 4:** Deploy to production with monitoring

---

## Further Reading

**Code repositories:**

- **[Vowpal Wabbit](https://github.com/VowpalWabbit/vowpal_wabbit)**: Production-grade bandit library from Microsoft
- **[Contextualbandits](https://github.com/david-cortes/contextualbandits)**: Python library with scikit-learn integration
- **[PyMC Bandits](https://github.com/pymc-devs/pymc-experimental)**: Bayesian bandit implementations

**Papers on these algorithms:**

- **ε-greedy:** Classic exploration strategy, no single paper
- **UCB1:** [Auer et al., 2002](https://link.springer.com/article/10.1023/A:1013689704352)
- **LinUCB:** [Li et al., 2010](https://arxiv.org/abs/1003.0146)
- **Thompson Sampling:** [Agrawal &#x26; Goyal, 2013](http://proceedings.mlr.press/v28/agrawal13.html)

**Blog posts:**

- [Lil'Log: Multi-Armed Bandits](https://lilianweng.github.io/posts/2018-01-23-multi-armed-bandit/)
- [Jeremy Kun: Bandits Series](https://jeremykun.com/tag/multi-armed-bandit/)

---</span>]]></content:encoded><enclosure url="https://www.vitorsousa.com/_astro/rl-foundations-llm.D3imlyjx.png" length="756000" type="image/png"/></item><item><title>Deploying Contextual Bandits: Production Guide and Offline Evaluation</title><link>https://www.vitorsousa.com/blog/contextual-bandits-prod/</link><guid isPermaLink="true">https://www.vitorsousa.com/blog/contextual-bandits-prod/</guid><description>Systems design, offline evaluation, and monitoring strategies for running contextual bandits safely in production.</description><pubDate>Fri, 21 Nov 2025 09:00:00 GMT</pubDate><content:encoded><![CDATA[<h1 id="ppo-for-language-models-the-rlhf-workhorse">PPO for Language Models: The RLHF Workhorse</h1>
<p><strong>Part 2 of 4:</strong> The Industry Standard</p>
<blockquote>
<p><strong>TL;DR:</strong> PPO (Proximal Policy Optimization) is the backbone of RLHF, powering ChatGPT, Claude, and most aligned LLMs. It provides stable training through clipped objectives and flexible advantage estimation via GAE. But this comes at a cost: four models (policy, reference, value, reward) straining GPU memory. Understanding PPO deeply reveals why simpler alternatives like GRPO emerged.</p>
</blockquote>
<p><strong>Reading time:</strong> ~28 minutes</p>
<p><strong>Prerequisites:</strong> <a href="/blog/rl-foundations-llm/">Part 1: RL Foundations</a> covers policy gradients, value functions, and advantage estimation.</p>
<hr>
<h2 id="from-reinforce-to-ppo">From REINFORCE to PPO</h2>
<p>In Part 1, we saw that policy gradients are powerful but suffer from high variance. Actor-critic methods add a value function to reduce variance, but introduce their own challenges: training two networks that must improve together without destabilizing each other.</p>
<p><strong>PPO</strong> (Proximal Policy Optimization) addresses stability through a deceptively simple idea: <strong>limit how much the policy can change in a single update</strong>. If updates are small enough, the value function doesn’t become stale, and training remains stable.</p>
<p>But “small updates” is vague. How do we formalize it? And how do we enforce it efficiently?</p>
<hr>
<hr>
<h2 id="the-trust-region-idea">The Trust Region Idea</h2>
<h3 id="the-problem-with-large-updates">The Problem with Large Updates</h3>
<p>Consider what happens when we take a policy gradient step:</p>
<ol>
<li>Collect trajectories using current policy <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.03588em;">π</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.02778em;">θ</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></li>
<li>Compute advantages using value function <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.22222em;">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.03588em;">ψ</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></li>
<li>Update: <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>θ</mi><mo>←</mo><mi>θ</mi><mo>+</mo><mi>α</mi><msub><mi mathvariant="normal">∇</mi><mi>θ</mi></msub><mi>J</mi><mo stretchy="false">(</mo><mi>θ</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\theta \leftarrow \theta + \alpha \nabla_\theta J(\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.02778em;">θ</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.7778em;vertical-align:-0.0833em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">θ</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.0037em;">α</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.02778em;">θ</span></span></span></span><span 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 mathnormal" style="margin-right:0.09618em;">J</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.02778em;">θ</span><span class="mclose">)</span></span></span></span></li>
</ol>
<p>The problem: our advantages were computed for <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.03588em;">π</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.02778em;">θ</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>, but we’re evaluating actions under <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.03588em;">π</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.02778em;">θ</span></span></span></span><span 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>. If <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.02778em;">θ</span></span></span></span> changes too much, the advantages become stale—they no longer reflect the true quality of actions under the new policy.</p>
<p>This can cause:</p>
<ul>
<li><strong>Overshooting:</strong> A good action gets reinforced too strongly</li>
<li><strong>Policy collapse:</strong> All probability mass shifts to one action</li>
<li><strong>Training instability:</strong> Performance oscillates wildly</li>
</ul>
<h3 id="trust-regions-the-concept">Trust Regions: The Concept</h3>
<p>A <strong>trust region</strong> is a constraint on how much the policy can change:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mrow><mi>max</mi><mo>⁡</mo></mrow><mi>θ</mi></msub><mi>J</mi><mo stretchy="false">(</mo><mi>θ</mi><mo stretchy="false">)</mo><mspace width="1em"></mspace><mtext>subject to</mtext><mspace width="1em"></mspace><mi>D</mi><mo stretchy="false">(</mo><msub><mi>π</mi><mi>θ</mi></msub><mo separator="true">,</mo><msub><mi>π</mi><msub><mi>θ</mi><mtext>old</mtext></msub></msub><mo stretchy="false">)</mo><mo>≤</mo><mi>δ</mi></mrow><annotation encoding="application/x-tex">\max_\theta J(\theta) \quad \text{subject to} \quad D(\pi_\theta, \pi_{\theta_{\text{old}}}) \leq \delta</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="mop"><span class="mop">max</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.02778em;">θ</span></span></span></span><span 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.09618em;">J</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.02778em;">θ</span><span class="mclose">)</span><span class="mspace" style="margin-right:1em;"></span><span class="mord text"><span class="mord">subject to</span></span><span class="mspace" style="margin-right:1em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">D</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">π</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.02778em;">θ</span></span></span></span><span 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.03588em;">π</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.02778em;">θ</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="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.6944em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span></span></span></span></p>
<p>where <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>D</mi></mrow><annotation encoding="application/x-tex">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 mathnormal" style="margin-right:0.02778em;">D</span></span></span></span> is some distance measure between policies, 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">\delta</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.03785em;">δ</span></span></span></span> is a small threshold.</p>
<p>Within the trust region, our local approximation (using old advantages) is valid. Outside it, we can’t trust our gradient estimates.</p>
<h3 id="trpo-the-principled-approach">TRPO: The Principled Approach</h3>
<p><strong>Trust Region Policy Optimization (TRPO)</strong> uses KL divergence as the distance:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mrow><mi>max</mi><mo>⁡</mo></mrow><mi>θ</mi></msub><mi mathvariant="double-struck">E</mi><mrow><mo fence="true">[</mo><mfrac><mrow><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><mi>a</mi><mi mathvariant="normal">∣</mi><mi>s</mi><mo stretchy="false">)</mo></mrow><mrow><msub><mi>π</mi><msub><mi>θ</mi><mtext>old</mtext></msub></msub><mo stretchy="false">(</mo><mi>a</mi><mi mathvariant="normal">∣</mi><mi>s</mi><mo stretchy="false">)</mo></mrow></mfrac><msup><mi>A</mi><msub><mi>π</mi><mtext>old</mtext></msub></msup><mo stretchy="false">(</mo><mi>s</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo><mo fence="true">]</mo></mrow><mspace width="1em"></mspace><mtext>s.t.</mtext><mspace width="1em"></mspace><mi mathvariant="double-struck">E</mi><mrow><mo fence="true">[</mo><mtext>KL</mtext><mo stretchy="false">(</mo><msub><mi>π</mi><msub><mi>θ</mi><mtext>old</mtext></msub></msub><mi mathvariant="normal">∥</mi><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">)</mo><mo fence="true">]</mo></mrow><mo>≤</mo><mi>δ</mi></mrow><annotation encoding="application/x-tex">\max_\theta \mathbb{E}\left[\frac{\pi_\theta(a|s)}{\pi_{\theta_{\text{old}}}(a|s)} A^{\pi_{\text{old}}}(s, a)\right] \quad \text{s.t.} \quad \mathbb{E}\left[\text{KL}(\pi_{\theta_{\text{old}}} \| \pi_\theta)\right] \leq \delta</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="mop"><span class="mop">max</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.02778em;">θ</span></span></span></span><span 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 mathbb">E</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.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.03588em;">π</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 mtight"><span class="mord mathnormal mtight" style="margin-right:0.02778em;">θ</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.3448em;margin-left:-0.0278em;margin-right:0.1em;"><span class="pstrut" style="height:2.6944em;"></span><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">old</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.3496em;"><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.401em;"><span></span></span></span></span></span></span><span class="mopen mtight">(</span><span class="mord mathnormal mtight">a</span><span class="mord mtight">∣</span><span class="mord mathnormal mtight">s</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.03588em;">π</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.02778em;">θ</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 mathnormal mtight">a</span><span class="mord mtight">∣</span><span class="mord mathnormal mtight">s</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.6257em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mord"><span class="mord mathnormal">A</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><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 class="mord mathnormal mtight" style="margin-right:0.03588em;">π</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">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></span></span></span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</span><span class="mclose">)</span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size2">]</span></span></span><span class="mspace" style="margin-right:1em;"></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord text"><span class="mord">s.t.</span></span><span class="mspace" style="margin-right:1em;"></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 text"><span class="mord">KL</span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">π</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.02778em;">θ</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="mord">∥</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">π</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.02778em;">θ</span></span></span></span><span 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="mclose delimcenter" style="top:0em;">]</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.6944em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span></span></span></span></p>
<p>TRPO solves this constrained optimization using second-order methods (conjugate gradient + line search). It’s theoretically elegant but:</p>
<ul>
<li>Computationally expensive</li>
<li>Complex to implement</li>
<li>Hard to parallelize</li>
</ul>
<p>This motivated PPO: <strong>can we get trust region benefits with first-order methods?</strong></p>
<hr>
<h2 id="from-trpo-to-ppo-the-clipped-objective">From TRPO to PPO: The Clipped Objective</h2>
<p>PPO’s insight: instead of enforcing a hard constraint, <strong>clip the objective</strong> so that updates beyond the trust region give no additional benefit.</p>
<h3 id="the-importance-sampling-ratio">The Importance Sampling Ratio</h3>
<p>First, we rewrite the policy gradient using importance sampling:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>J</mi><mo stretchy="false">(</mo><mi>θ</mi><mo stretchy="false">)</mo><mo>=</mo><msub><mi mathvariant="double-struck">E</mi><mrow><mi>s</mi><mo separator="true">,</mo><mi>a</mi><mo>∼</mo><msub><mi>π</mi><msub><mi>θ</mi><mtext>old</mtext></msub></msub></mrow></msub><mrow><mo fence="true">[</mo><mfrac><mrow><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><mi>a</mi><mi mathvariant="normal">∣</mi><mi>s</mi><mo stretchy="false">)</mo></mrow><mrow><msub><mi>π</mi><msub><mi>θ</mi><mtext>old</mtext></msub></msub><mo stretchy="false">(</mo><mi>a</mi><mi mathvariant="normal">∣</mi><mi>s</mi><mo stretchy="false">)</mo></mrow></mfrac><msup><mi>A</mi><msub><mi>π</mi><mtext>old</mtext></msub></msup><mo stretchy="false">(</mo><mi>s</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo><mo fence="true">]</mo></mrow></mrow><annotation encoding="application/x-tex">J(\theta) = \mathbb{E}_{s, a \sim \pi_{\theta_{\text{old}}}}\left[\frac{\pi_\theta(a|s)}{\pi_{\theta_{\text{old}}}(a|s)} A^{\pi_{\text{old}}}(s, a)\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 mathnormal" style="margin-right:0.09618em;">J</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.02778em;">θ</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.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 mathnormal mtight">s</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">a</span><span class="mrel mtight">∼</span><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.03588em;">π</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 mtight"><span class="mord mathnormal mtight" style="margin-right:0.02778em;">θ</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.3448em;margin-left:-0.0278em;margin-right:0.1em;"><span class="pstrut" style="height:2.6944em;"></span><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">old</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.3496em;"><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.401em;"><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.4307em;"><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 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.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.03588em;">π</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 mtight"><span class="mord mathnormal mtight" style="margin-right:0.02778em;">θ</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.3448em;margin-left:-0.0278em;margin-right:0.1em;"><span class="pstrut" style="height:2.6944em;"></span><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">old</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.3496em;"><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.401em;"><span></span></span></span></span></span></span><span class="mopen mtight">(</span><span class="mord mathnormal mtight">a</span><span class="mord mtight">∣</span><span class="mord mathnormal mtight">s</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.03588em;">π</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.02778em;">θ</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 mathnormal mtight">a</span><span class="mord mtight">∣</span><span class="mord mathnormal mtight">s</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.6257em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mord"><span class="mord mathnormal">A</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><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 class="mord mathnormal mtight" style="margin-right:0.03588em;">π</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">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></span></span></span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</span><span class="mclose">)</span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size2">]</span></span></span></span></span></span></p>
<p>Define the <strong>importance ratio</strong>:</p>
<p><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 stretchy="false">(</mo><mi>θ</mi><mo stretchy="false">)</mo><mo>=</mo><mfrac><mrow><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>a</mi><mi>t</mi></msub><mi mathvariant="normal">∣</mi><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><mi mathvariant="normal">∣</mi><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo></mrow></mfrac></mrow><annotation encoding="application/x-tex">\rho_t(\theta) = \frac{\pi_\theta(a_t|s_t)}{\pi_{\theta_{\text{old}}}(a_t|s_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"><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="mopen">(</span><span class="mord mathnormal" style="margin-right:0.02778em;">θ</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.6357em;vertical-align:-0.6257em;"></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.03588em;">π</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 mtight"><span class="mord mathnormal mtight" style="margin-right:0.02778em;">θ</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.3448em;margin-left:-0.0278em;margin-right:0.1em;"><span class="pstrut" style="height:2.6944em;"></span><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">old</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.3496em;"><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.401em;"><span></span></span></span></span></span></span><span class="mopen mtight">(</span><span class="mord mtight"><span class="mord mathnormal mtight">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2963em;"><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">t</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 mtight"><span class="mord mathnormal mtight">s</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2963em;"><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">t</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="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.03588em;">π</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.02778em;">θ</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">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2963em;"><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">t</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 mtight"><span class="mord mathnormal mtight">s</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2963em;"><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">t</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="mclose mtight">)</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.6257em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></p>
<p>When <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>θ</mi><mo>=</mo><msub><mi>θ</mi><mtext>old</mtext></msub></mrow><annotation encoding="application/x-tex">\theta = \theta_{\text{old}}</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.02778em;">θ</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"><span class="mord mathnormal" style="margin-right:0.02778em;">θ</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">old</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>, we have <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">\rho = 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">ρ</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>. As <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.02778em;">θ</span></span></span></span> diverges from <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>θ</mi><mtext>old</mtext></msub></mrow><annotation encoding="application/x-tex">\theta_{\text{old}}</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.02778em;">θ</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">old</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="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ρ</mi></mrow><annotation encoding="application/x-tex">\rho</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">ρ</span></span></span></span> moves away from 1.</p>
<h3 id="the-clipped-surrogate-objective">The Clipped Surrogate Objective</h3>
<p>PPO’s objective clips the ratio to prevent large updates:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>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><mrow><mo fence="true">[</mo><mi>min</mi><mo>⁡</mo><mrow><mo fence="true">(</mo><msub><mi>ρ</mi><mi>t</mi></msub><mo stretchy="false">(</mo><mi>θ</mi><mo stretchy="false">)</mo><msub><mi>A</mi><mi>t</mi></msub><mo separator="true">,</mo><mtext> </mtext><mtext>clip</mtext><mo stretchy="false">(</mo><msub><mi>ρ</mi><mi>t</mi></msub><mo stretchy="false">(</mo><mi>θ</mi><mo stretchy="false">)</mo><mo separator="true">,</mo><mn>1</mn><mo>−</mo><mi>ε</mi><mo separator="true">,</mo><mn>1</mn><mo>+</mo><mi>ε</mi><mo stretchy="false">)</mo><msub><mi>A</mi><mi>t</mi></msub><mo fence="true">)</mo></mrow><mo fence="true">]</mo></mrow></mrow><annotation encoding="application/x-tex">L^{\text{CLIP}}(\theta) = \mathbb{E}_t\left[\min\left(\rho_t(\theta) A_t, \, \text{clip}(\rho_t(\theta), 1-\varepsilon, 1+\varepsilon) A_t\right)\right]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0913em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathnormal">L</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8413em;"><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 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.02778em;">θ</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 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="mspace" style="margin-right:0.1667em;"></span><span class="minner"><span class="mopen delimcenter" style="top:0em;">[</span><span class="mop">min</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 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="mopen">(</span><span class="mord mathnormal" style="margin-right:0.02778em;">θ</span><span class="mclose">)</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="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></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="mopen">(</span><span class="mord mathnormal" style="margin-right:0.02778em;">θ</span><span class="mclose">)</span><span class="mpunct">,</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="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="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="mclose delimcenter" style="top:0em;">)</span></span><span class="mclose delimcenter" style="top:0em;">]</span></span></span></span></span></p>
<p>Let’s unpack this carefully.</p>
<h3 id="how-clipping-works">How Clipping Works</h3>
<p><strong>Case 1: Positive advantage (<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><mo>></mo><mn>0</mn></mrow><annotation encoding="application/x-tex">A_t > 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">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><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">0</span></span></span></span>) — the action was good</strong></p>
<p>We want to increase <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>a</mi><mi>t</mi></msub><mi mathvariant="normal">∣</mi><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\pi_\theta(a_t|s_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"><span class="mord mathnormal" style="margin-right:0.03588em;">π</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.02778em;">θ</span></span></span></span><span 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="mord">∣</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>, which increases <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>.</p>
<ul>
<li>If <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>&#x3C;</mo><mn>1</mn><mo>+</mo><mi>ε</mi></mrow><annotation encoding="application/x-tex">\rho_t &#x3C; 1 + \varepsilon</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7335em;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">&#x3C;</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:0.4306em;"></span><span class="mord mathnormal">ε</span></span></span></span>: The objective is <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><msub><mi>A</mi><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">\rho_t A_t</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"><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="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>, so gradients push <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> higher</li>
<li>If <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><mo>+</mo><mi>ε</mi></mrow><annotation encoding="application/x-tex">\rho_t > 1 + \varepsilon</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7335em;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.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>: The clipped term becomes <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mn>1</mn><mo>+</mo><mi>ε</mi><mo stretchy="false">)</mo><msub><mi>A</mi><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">(1+\varepsilon) A_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="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">ε</span><span class="mclose">)</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>, which is constant. <strong>No gradient</strong> — we’ve increased the action probability enough</li>
</ul>
<p><strong>Case 2: Negative advantage (<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><mo>&#x3C;</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">A_t &#x3C; 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">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">&#x3C;</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>) — the action was bad</strong></p>
<p>We want to decrease <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>a</mi><mi>t</mi></msub><mi mathvariant="normal">∣</mi><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\pi_\theta(a_t|s_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"><span class="mord mathnormal" style="margin-right:0.03588em;">π</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.02778em;">θ</span></span></span></span><span 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="mord">∣</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>, which decreases <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>.</p>
<ul>
<li>If <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><mo>−</mo><mi>ε</mi></mrow><annotation encoding="application/x-tex">\rho_t > 1 - \varepsilon</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7335em;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.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>: The objective is <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><msub><mi>A</mi><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">\rho_t A_t</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"><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="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>, so gradients push <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> lower</li>
<li>If <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>&#x3C;</mo><mn>1</mn><mo>−</mo><mi>ε</mi></mrow><annotation encoding="application/x-tex">\rho_t &#x3C; 1 - \varepsilon</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7335em;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">&#x3C;</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:0.4306em;"></span><span class="mord mathnormal">ε</span></span></span></span>: The clipped term becomes <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><mi>ε</mi><mo stretchy="false">)</mo><msub><mi>A</mi><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">(1-\varepsilon) A_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="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">ε</span><span class="mclose">)</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>, which is constant. <strong>No gradient</strong> — we’ve decreased the action probability enough</li>
</ul>
<div class="mermaid">
graph TB
    subgraph Clipping["PPO Clipping Mechanism"]
        direction TB
        
        subgraph PosAdv["A > 0 (Good Action)"]
            P1[ρ &#x3C; 1+ε&#x3C;br/>━━━━━━━━&#x3C;br/>Gradient active&#x3C;br/>Keep increasing π]
            P2[ρ > 1+ε&#x3C;br/>━━━━━━━━&#x3C;br/>Gradient clipped&#x3C;br/>Enough increase]
        end
        
        subgraph NegAdv["A &#x3C; 0 (Bad Action)"]
            N1[ρ > 1-ε&#x3C;br/>━━━━━━━━&#x3C;br/>Gradient active&#x3C;br/>Keep decreasing π]
            N2[ρ &#x3C; 1-ε&#x3C;br/>━━━━━━━━&#x3C;br/>Gradient clipped&#x3C;br/>Enough decrease]
        end
    end
    
    style P1 stroke:#10b981,stroke-width:2px
    style P2 stroke:#64748b,stroke-width:2px
    style N1 stroke:#ef4444,stroke-width:2px
    style N2 stroke:#64748b,stroke-width:2px
    
    style PosAdv stroke:#10b981
    style NegAdv stroke:#ef4444
    style Clipping stroke:none</div>
<h3 id="the-pessimistic-bound">The “Pessimistic” Bound</h3>
<p>The <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>min</mi><mo>⁡</mo></mrow><annotation encoding="application/x-tex">\min</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6679em;"></span><span class="mop">min</span></span></span></span> in the objective creates a <strong>pessimistic bound</strong>: we take the worse of (unclipped, clipped) for each term.</p>
<ul>
<li>For good actions: Can’t get more than <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mn>1</mn><mo>+</mo><mi>ε</mi><mo stretchy="false">)</mo><msub><mi>A</mi><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">(1+\varepsilon)A_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="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">ε</span><span class="mclose">)</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> benefit</li>
<li>For bad actions: Can’t get less than <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><mi>ε</mi><mo stretchy="false">)</mo><msub><mi>A</mi><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">(1-\varepsilon)A_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="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">ε</span><span class="mclose">)</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> penalty</li>
</ul>
<p>This is conservative by design—it prevents the optimizer from exploiting stale advantages.</p>
<h3 id="why-ε02varepsilon--02ε02">Why <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">\varepsilon = 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>?</h3>
<p>The canonical choice is <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">\varepsilon = 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>, allowing <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">\rho</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">ρ</span></span></span></span> to range from 0.8 to 1.2. This means:</p>
<ul>
<li>Action probabilities can at most increase by 20% (relative)</li>
<li>Action probabilities can at most decrease by 20% (relative)</li>
</ul>
<p>per update step. Empirically, this balances stability and learning speed. Some implementations use 0.1 for more conservative updates.</p>
<blockquote>
<p><strong>Key Insight:</strong> PPO’s clipping creates a “soft trust region” using only first-order optimization. The policy can change, but changes beyond <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">\varepsilon</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> provide no additional gradient signal. It’s not a hard constraint, but it works remarkably well in practice.</p>
</blockquote>
<hr>
<h2 id="generalized-advantage-estimation-gae">Generalized Advantage Estimation (GAE)</h2>
<p>PPO pairs the clipped objective with <strong>Generalized Advantage Estimation (GAE)</strong>, a flexible way to estimate advantages.</p>
<h3 id="the-bias-variance-tradeoff">The Bias-Variance Tradeoff</h3>
<p>Recall from Part 1 that we can estimate advantages in different ways:</p>
<p><strong>Monte Carlo (MC):</strong> <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mover accent="true"><mi>A</mi><mo>^</mo></mover><mi>t</mi><mtext>MC</mtext></msubsup><mo>=</mo><msub><mi>G</mi><mi>t</mi></msub><mo>−</mo><mi>V</mi><mo stretchy="false">(</mo><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo><mo>=</mo><msubsup><mo>∑</mo><mrow><mi>k</mi><mo>=</mo><mn>0</mn></mrow><mrow><mi>T</mi><mo>−</mo><mi>t</mi></mrow></msubsup><msup><mi>γ</mi><mi>k</mi></msup><msub><mi>r</mi><mrow><mi>t</mi><mo>+</mo><mi>k</mi></mrow></msub><mo>−</mo><mi>V</mi><mo stretchy="false">(</mo><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\hat{A}_t^{\text{MC}} = G_t - V(s_t) = \sum_{k=0}^{T-t} \gamma^k r_{t+k} - V(s_t)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.1938em;vertical-align:-0.247em;"></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.8413em;"><span style="top:-2.453em;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 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 text mtight"><span class="mord mtight">MC</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><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">G</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.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.22222em;">V</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.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.2809em;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.9812em;"><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.03148em;">k</span><span class="mrel mtight">=</span><span class="mord mtight">0</span></span></span></span><span style="top:-3.2029em;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.13889em;">T</span><span class="mbin 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.2997em;"><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.05556em;">γ</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.03148em;">k</span></span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">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 mtight"><span class="mord mathnormal mtight">t</span><span class="mbin mtight">+</span><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</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="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.22222em;">V</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></p>
<ul>
<li><strong>Unbiased:</strong> Uses actual returns</li>
<li><strong>High variance:</strong> Includes all future randomness</li>
</ul>
<p><strong>Temporal Difference (TD):</strong> <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mover accent="true"><mi>A</mi><mo>^</mo></mover><mi>t</mi><mtext>TD</mtext></msubsup><mo>=</mo><msub><mi>r</mi><mi>t</mi></msub><mo>+</mo><mi>γ</mi><mi>V</mi><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><mi>V</mi><mo stretchy="false">(</mo><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo><mo>=</mo><msub><mi>δ</mi><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">\hat{A}_t^{\text{TD}} = r_t + \gamma V(s_{t+1}) - V(s_t) = \delta_t</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.1938em;vertical-align:-0.247em;"></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.8413em;"><span style="top:-2.453em;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 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 text mtight"><span class="mord mtight">TD</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><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.02778em;">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.22222em;">γV</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:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.22222em;">V</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.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"><span class="mord mathnormal" style="margin-right:0.03785em;">δ</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></span></span></p>
<ul>
<li><strong>Biased:</strong> Uses estimated <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><msub><mi>s</mi><mrow><mi>t</mi><mo>+</mo><mn>1</mn></mrow></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">V(s_{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 mathnormal" style="margin-right:0.22222em;">V</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></span></span> instead of true expected return</li>
<li><strong>Low variance:</strong> Only one step of randomness</li>
</ul>
<h3 id="gae-interpolating-between-mc-and-td">GAE: Interpolating Between MC and TD</h3>
<p>GAE introduces parameter <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>λ</mi><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">\lambda \in [0, 1]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7335em;vertical-align:-0.0391em;"></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: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> to interpolate:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mover accent="true"><mi>A</mi><mo>^</mo></mover><mi>t</mi><mrow><mtext>GAE</mtext><mo stretchy="false">(</mo><mi>γ</mi><mo separator="true">,</mo><mi>λ</mi><mo stretchy="false">)</mo></mrow></msubsup><mo>=</mo><msubsup><mo>∑</mo><mrow><mi>k</mi><mo>=</mo><mn>0</mn></mrow><mi mathvariant="normal">∞</mi></msubsup><mo stretchy="false">(</mo><mi>γ</mi><mi>λ</mi><msup><mo stretchy="false">)</mo><mi>k</mi></msup><msub><mi>δ</mi><mrow><mi>t</mi><mo>+</mo><mi>k</mi></mrow></msub></mrow><annotation encoding="application/x-tex">\hat{A}_t^{\text{GAE}(\gamma, \lambda)} = \sum_{k=0}^{\infty} (\gamma\lambda)^k \delta_{t+k}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.2906em;vertical-align:-0.2458em;"></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:1.0448em;"><span style="top:-2.4542em;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 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="mord text mtight"><span class="mord mtight">GAE</span></span><span class="mopen mtight">(</span><span class="mord mathnormal mtight" style="margin-right:0.05556em;">γ</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">λ</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.2458em;"><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.1488em;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.8043em;"><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.03148em;">k</span><span class="mrel mtight">=</span><span class="mord mtight">0</span></span></span></span><span style="top:-3.2029em;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 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="mopen">(</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.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.03148em;">k</span></span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03785em;">δ</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.03148em;">k</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></p>
<p>where <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><msub><mi>r</mi><mi>t</mi></msub><mo>+</mo><mi>γ</mi><mi>V</mi><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><mi>V</mi><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(s_{t+1}) - V(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.03785em;">δ</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.02778em;">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.22222em;">γV</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:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.22222em;">V</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> is the TD error.</p>
<p><strong>Special cases:</strong></p>
<ul>
<li><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>: <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><mo>=</mo><msub><mi>δ</mi><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">\hat{A}_t = \delta_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 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"><span class="mord mathnormal" style="margin-right:0.03785em;">δ</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></span></span> (TD, low variance, high bias)</li>
<li><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>: <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><mo>=</mo><msub><mo>∑</mo><mi>k</mi></msub><msup><mi>γ</mi><mi>k</mi></msup><msub><mi>δ</mi><mrow><mi>t</mi><mo>+</mo><mi>k</mi></mrow></msub><mo>=</mo><msub><mi>G</mi><mi>t</mi></msub><mo>−</mo><mi>V</mi><mo stretchy="false">(</mo><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\hat{A}_t = \sum_k \gamma^k \delta_{t+k} = G_t - V(s_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 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.1488em;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.03148em;">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 mathnormal" style="margin-right:0.05556em;">γ</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.03148em;">k</span></span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03785em;">δ</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.03148em;">k</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="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">G</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.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.22222em;">V</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> (MC, high variance, low bias)</li>
</ul>
<h3 id="practical-computation">Practical Computation</h3>
<p>GAE can be computed efficiently backwards through the trajectory:</p>
<p><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><mo>=</mo><msub><mi>δ</mi><mi>t</mi></msub><mo>+</mo><mi>γ</mi><mi>λ</mi><msub><mover accent="true"><mi>A</mi><mo>^</mo></mover><mrow><mi>t</mi><mo>+</mo><mn>1</mn></mrow></msub></mrow><annotation encoding="application/x-tex">\hat{A}_t = \delta_t + \gamma\lambda \hat{A}_{t+1}</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:0.8444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03785em;">δ</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.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.1551em;vertical-align:-0.2083em;"></span><span class="mord mathnormal">γλ</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.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></span></span></p>
<p>Starting from the end of the episode (<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><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">\hat{A}_T = 0</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.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 mathnormal mtight" style="margin-right:0.13889em;">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">0</span></span></span></span> or <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><mo>=</mo><msub><mi>δ</mi><mi>T</mi></msub></mrow><annotation encoding="application/x-tex">\hat{A}_T = \delta_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.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 mathnormal mtight" style="margin-right:0.13889em;">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.8444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03785em;">δ</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.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" style="margin-right:0.13889em;">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>), we recurse backwards.</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#F97583">def</span><span style="color:#B392F0"> compute_gae</span><span style="color:#E1E4E8">(rewards, values, gamma</span><span style="color:#F97583">=</span><span style="color:#79B8FF">0.99</span><span style="color:#E1E4E8">, lam</span><span style="color:#F97583">=</span><span style="color:#79B8FF">0.95</span><span style="color:#E1E4E8">):</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#9ECBFF">    Compute GAE advantages.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Args:</span></span>
<span class="line"><span style="color:#9ECBFF">        rewards: [T] rewards at each timestep</span></span>
<span class="line"><span style="color:#9ECBFF">        values: [T+1] value estimates (includes bootstrap V(s_T+1))</span></span>
<span class="line"><span style="color:#9ECBFF">        gamma: Discount factor</span></span>
<span class="line"><span style="color:#9ECBFF">        lam: GAE lambda parameter</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">        advantages: [T] GAE advantage estimates</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#E1E4E8">    T </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> len</span><span style="color:#E1E4E8">(rewards)</span></span>
<span class="line"><span style="color:#E1E4E8">    advantages </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.zeros(T)</span></span>
<span class="line"><span style="color:#E1E4E8">    last_adv </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 0</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    for</span><span style="color:#E1E4E8"> t </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> reversed</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">range</span><span style="color:#E1E4E8">(T)):</span></span>
<span class="line"><span style="color:#6A737D">        # TD error: δ_t = r_t + γV(s_{t+1}) - V(s_t)</span></span>
<span class="line"><span style="color:#E1E4E8">        delta </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> rewards[t] </span><span style="color:#F97583">+</span><span style="color:#E1E4E8"> gamma </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> values[t </span><span style="color:#F97583">+</span><span style="color:#79B8FF"> 1</span><span style="color:#E1E4E8">] </span><span style="color:#F97583">-</span><span style="color:#E1E4E8"> values[t]</span></span>
<span class="line"><span style="color:#6A737D">        # GAE recursion: A_t = δ_t + γλ A_{t+1}</span></span>
<span class="line"><span style="color:#E1E4E8">        advantages[t] </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> delta </span><span style="color:#F97583">+</span><span style="color:#E1E4E8"> gamma </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> lam </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> last_adv</span></span>
<span class="line"><span style="color:#E1E4E8">        last_adv </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> advantages[t]</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    return</span><span style="color:#E1E4E8"> advantages</span></span>
<span class="line"></span></code></pre>
<h3 id="choosing-λlambdaλ">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">\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></h3>



































<table><thead><tr><th><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> Value</th><th>Bias</th><th>Variance</th><th>Best For</th></tr></thead><tbody><tr><td>0.0</td><td>High</td><td>Low</td><td>When value function is very accurate</td></tr><tr><td>0.9</td><td>Medium</td><td>Medium</td><td>Typical choice</td></tr><tr><td>0.95</td><td>Low-Medium</td><td>Medium-High</td><td>Default in most PPO implementations</td></tr><tr><td>1.0</td><td>None</td><td>High</td><td>When value function is poor</td></tr></tbody></table>
<p>The default <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> works well across many domains, including LLM fine-tuning.</p>
<blockquote>
<p><strong>Key Insight:</strong> GAE lets us tune the bias-variance tradeoff. High <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> trusts actual returns more; low <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> trusts the value function more. For LLMs with sparse rewards, 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">\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 often better since the value function may be inaccurate at intermediate states.</p>
</blockquote>
<hr>
<h2 id="the-complete-ppo-algorithm">The Complete PPO Algorithm</h2>
<p>Now let’s put it all together.</p>
<h3 id="ppo-clip-algorithm">PPO-Clip Algorithm</h3>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>Algorithm: PPO-Clip</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Hyperparameters: </span></span>
<span class="line"><span>    ε (clip ratio, typically 0.2)</span></span>
<span class="line"><span>    γ (discount, typically 0.99 or 1.0)</span></span>
<span class="line"><span>    λ (GAE lambda, typically 0.95)</span></span>
<span class="line"><span>    K (epochs per batch, typically 3-10)</span></span>
<span class="line"><span>    M (minibatch size)</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Initialize: Policy π_θ, Value function V_ψ</span></span>
<span class="line"><span></span></span>
<span class="line"><span>For iteration = 1, 2, ...:</span></span>
<span class="line"><span>    ┌─────────────────────────────────────────────────────┐</span></span>
<span class="line"><span>    │ Phase 1: Collect Experience                         │</span></span>
<span class="line"><span>    │ ─────────────────────────────────────────────────── │</span></span>
<span class="line"><span>    │ Run π_θ for T timesteps, collecting:                │</span></span>
<span class="line"><span>    │   {s_t, a_t, r_t, s_{t+1}} for t = 1...T           │</span></span>
<span class="line"><span>    │                                                     │</span></span>
<span class="line"><span>    │ Compute value estimates V_ψ(s_t) for all t          │</span></span>
<span class="line"><span>    └─────────────────────────────────────────────────────┘</span></span>
<span class="line"><span>    </span></span>
<span class="line"><span>    ┌─────────────────────────────────────────────────────┐</span></span>
<span class="line"><span>    │ Phase 2: Compute Advantages                         │</span></span>
<span class="line"><span>    │ ─────────────────────────────────────────────────── │</span></span>
<span class="line"><span>    │ For t = T-1 down to 0:                              │</span></span>
<span class="line"><span>    │     δ_t = r_t + γV_ψ(s_{t+1}) - V_ψ(s_t)           │</span></span>
<span class="line"><span>    │     Â_t = δ_t + γλ Â_{t+1}    (GAE)                │</span></span>
<span class="line"><span>    │                                                     │</span></span>
<span class="line"><span>    │ Compute returns: R̂_t = Â_t + V_ψ(s_t)              │</span></span>
<span class="line"><span>    └─────────────────────────────────────────────────────┘</span></span>
<span class="line"><span>    </span></span>
<span class="line"><span>    ┌─────────────────────────────────────────────────────┐</span></span>
<span class="line"><span>    │ Phase 3: Optimization (K epochs)                    │</span></span>
<span class="line"><span>    │ ─────────────────────────────────────────────────── │</span></span>
<span class="line"><span>    │ For epoch = 1 to K:                                 │</span></span>
<span class="line"><span>    │     For each minibatch of size M:                   │</span></span>
<span class="line"><span>    │                                                     │</span></span>
<span class="line"><span>    │         # Policy loss (clipped)                     │</span></span>
<span class="line"><span>    │         ρ = π_θ(a|s) / π_θ_old(a|s)                │</span></span>
<span class="line"><span>    │         L_clip = min(ρÂ, clip(ρ, 1-ε, 1+ε)Â)       │</span></span>
<span class="line"><span>    │         L_policy = -mean(L_clip)                    │</span></span>
<span class="line"><span>    │                                                     │</span></span>
<span class="line"><span>    │         # Value loss                                │</span></span>
<span class="line"><span>    │         L_value = mean((V_ψ(s) - R̂)²)              │</span></span>
<span class="line"><span>    │                                                     │</span></span>
<span class="line"><span>    │         # Entropy bonus (optional)                  │</span></span>
<span class="line"><span>    │         L_entropy = -mean(H[π_θ(·|s)])             │</span></span>
<span class="line"><span>    │                                                     │</span></span>
<span class="line"><span>    │         # Combined loss                             │</span></span>
<span class="line"><span>    │         L = L_policy + c_v·L_value + c_e·L_entropy  │</span></span>
<span class="line"><span>    │                                                     │</span></span>
<span class="line"><span>    │         Update θ, ψ using gradient of L             │</span></span>
<span class="line"><span>    │                                                     │</span></span>
<span class="line"><span>    │     Update π_θ_old ← π_θ                           │</span></span>
<span class="line"><span>    └─────────────────────────────────────────────────────┘</span></span>
<span class="line"><span></span></span></code></pre>
<h3 id="the-three-loss-components">The Three Loss Components</h3>
<p><strong>1. Policy Loss (maximize)</strong>
<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>L</mi><mtext>CLIP</mtext></msup><mo>=</mo><msub><mi mathvariant="double-struck">E</mi><mi>t</mi></msub><mrow><mo fence="true">[</mo><mi>min</mi><mo>⁡</mo><mo stretchy="false">(</mo><msub><mi>ρ</mi><mi>t</mi></msub><msub><mover accent="true"><mi>A</mi><mo>^</mo></mover><mi>t</mi></msub><mo separator="true">,</mo><mtext>clip</mtext><mo stretchy="false">(</mo><msub><mi>ρ</mi><mi>t</mi></msub><mo separator="true">,</mo><mn>1</mn><mo>−</mo><mi>ε</mi><mo separator="true">,</mo><mn>1</mn><mo>+</mo><mi>ε</mi><mo stretchy="false">)</mo><msub><mover accent="true"><mi>A</mi><mo>^</mo></mover><mi>t</mi></msub><mo stretchy="false">)</mo><mo fence="true">]</mo></mrow></mrow><annotation encoding="application/x-tex">L^{\text{CLIP}} = \mathbb{E}_t\left[\min(\rho_t \hat{A}_t, \text{clip}(\rho_t, 1-\varepsilon, 1+\varepsilon)\hat{A}_t)\right]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8413em;"></span><span class="mord"><span class="mord mathnormal">L</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8413em;"><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 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="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="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="mop">min</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="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" 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="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="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="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="mclose">)</span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size2">]</span></span></span></span></span></span></p>
<p><strong>2. Value Loss (minimize)</strong>
<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>L</mi><mtext>VF</mtext></msup><mo>=</mo><msub><mi mathvariant="double-struck">E</mi><mi>t</mi></msub><mrow><mo fence="true">[</mo><mo stretchy="false">(</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 stretchy="false">)</mo><mn>2</mn></msup><mo fence="true">]</mo></mrow></mrow><annotation encoding="application/x-tex">L^{\text{VF}} = \mathbb{E}_t\left[(V_\psi(s_t) - \hat{R}_t)^2\right]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8413em;"></span><span class="mord"><span class="mord mathnormal">L</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8413em;"><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 text mtight"><span class="mord mtight">VF</span></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.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="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="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.22222em;">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.03588em;">ψ</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 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.00773em;">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="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 class="mclose delimcenter" style="top:0em;"><span class="delimsizing size2">]</span></span></span></span></span></span></p>
<p>where <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><mo>=</mo><msub><mover accent="true"><mi>A</mi><mo>^</mo></mover><mi>t</mi></msub><mo>+</mo><msubsup><mi>V</mi><mi>ψ</mi><mtext>old</mtext></msubsup><mo stretchy="false">(</mo><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\hat{R}_t = \hat{A}_t + V_\psi^{\text{old}}(s_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.00773em;">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="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.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.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.2683em;vertical-align:-0.4192em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.22222em;">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8491em;"><span style="top:-2.4169em;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.03588em;">ψ</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"><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.4192em;"><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> is the target return.</p>
<p><strong>3. Entropy Bonus (maximize)</strong>
<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>L</mi><mtext>ENT</mtext></msup><mo>=</mo><msub><mi mathvariant="double-struck">E</mi><mi>t</mi></msub><mrow><mo fence="true">[</mo><mi>H</mi><mo stretchy="false">[</mo><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><mo>⋅</mo><mi mathvariant="normal">∣</mi><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo><mo stretchy="false">]</mo><mo fence="true">]</mo></mrow></mrow><annotation encoding="application/x-tex">L^{\text{ENT}} = \mathbb{E}_t\left[H[\pi_\theta(\cdot|s_t)]\right]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8413em;"></span><span class="mord"><span class="mord mathnormal">L</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8413em;"><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 text mtight"><span class="mord mtight">ENT</span></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"><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="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.08125em;">H</span><span class="mopen">[</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">π</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.02778em;">θ</span></span></span></span><span 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"><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="mclose delimcenter" style="top:0em;">]</span></span></span></span></span></p>
<p>Encourages exploration by preventing premature convergence to deterministic policies.</p>
<h3 id="why-multiple-epochs">Why Multiple Epochs?</h3>
<p>PPO reuses collected data for <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.07153em;">K</span></span></span></span> epochs (typically 3-10). This improves sample efficiency—we extract more learning from each batch of experience.</p>
<p>The clipping ensures this is safe: even with multiple passes, the policy can’t change too drastically because updates beyond <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">\varepsilon</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> provide no gradient.</p>
<hr>
<h2 id="ppo-for-llms-the-four-model-problem">PPO for LLMs: The Four-Model Problem</h2>
<p>When applying PPO to LLM alignment, we need <strong>four separate models</strong>:</p>
<div class="mermaid">
graph TB
    subgraph Models["&#x3C;b>PPO for LLMs: Four-Model Architecture&#x3C;/b>"]
        direction LR
        
        subgraph Trainable["Trainable"]
            Policy["&#x3C;b>Policy π_θ&#x3C;/b>&#x3C;br/>━━━━━━━━━━&#x3C;br/>LLM being trained&#x3C;br/>Generates responses&#x3C;br/>&#x3C;i>~28 GB&#x3C;/i>"]
            Value["&#x3C;b>Value V_ψ&#x3C;/b>&#x3C;br/>━━━━━━━━━━&#x3C;br/>Same size as policy&#x3C;br/>Estimates V(s) for GAE&#x3C;br/>&#x3C;i>~28 GB&#x3C;/i>"]
        end
        
        subgraph Frozen["Frozen"]
            Reference["&#x3C;b>Reference π_ref&#x3C;/b>&#x3C;br/>━━━━━━━━━━&#x3C;br/>Initial policy copy&#x3C;br/>KL penalty anchor&#x3C;br/>&#x3C;i>~14 GB&#x3C;/i>"]
            Reward["&#x3C;b>Reward r_φ&#x3C;/b>&#x3C;br/>━━━━━━━━━━&#x3C;br/>Scores responses&#x3C;br/>Training signal&#x3C;br/>&#x3C;i>~14 GB&#x3C;/i>"]
        end
    end
    
    subgraph Flow["&#x3C;b>Training Loop&#x3C;/b>"]
        direction LR
        Response["Response&#x3C;br/>&#x3C;i>o&#x3C;/i>"]
        KL["KL&#x3C;br/>Penalty"]
        Advantage["Advantage&#x3C;br/>&#x3C;i>Â&#x3C;/i>"]
        Update["Policy&#x3C;br/>Update"]
    end
    
    Policy -->|"sample"| Response
    Response -->|"score"| Reward
    Reward -->|"r"| Advantage
    
    Response -->|"states"| Value
    Value -->|"V(s)"| Advantage
    
    Policy -->|"π_θ"| KL
    Reference -->|"π_ref"| KL
    KL -->|"β·D_KL"| Advantage
    
    Advantage -->|"∇L_clip"| Update
    Update -->|"θ ← θ + α∇"| Policy
    
    style Policy stroke:#10b981,stroke-width:2.5px
    style Value stroke:#f59e0b,stroke-width:2.5px
    style Reference stroke:#64748b,stroke-width:2px
    style Reward stroke:#8b5cf6,stroke-width:2px
    
    style Response stroke:#475569,stroke-width:1.5px
    style KL stroke:#475569,stroke-width:1.5px
    style Advantage stroke:#06b6d4,stroke-width:2px
    style Update stroke:#10b981,stroke-width:2px
    
    style Trainable stroke:#10b981,stroke-width:1.5px,stroke-dasharray:5
    style Frozen stroke:#64748b,stroke-width:1.5px,stroke-dasharray:5
    style Models stroke:#334155,stroke-width:1px
    style Flow stroke:#334155,stroke-width:1px
</div>
<h3 id="memory-requirements-7b-model-example">Memory Requirements (7B Model Example)</h3>



































<table><thead><tr><th>Model</th><th>Training?</th><th>Memory</th></tr></thead><tbody><tr><td>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.03588em;">π</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.02778em;">θ</span></span></span></span><span 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></td><td>✅ Yes</td><td>~28 GB (weights + gradients + optimizer)</td></tr><tr><td>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.03588em;">π</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></td><td>❌ Frozen</td><td>~14 GB</td></tr><tr><td>Value <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.22222em;">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.03588em;">ψ</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></td><td>✅ Yes</td><td>~28 GB</td></tr><tr><td>Reward <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.02778em;">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></td><td>❌ Frozen</td><td>~14 GB</td></tr><tr><td><strong>Total</strong></td><td></td><td><strong>~84 GB</strong></td></tr></tbody></table>
<p>This is <em>before</em> accounting for activations, KV cache, and batch processing. For a 7B model, you need multiple A100-80GB GPUs just for the model weights.</p>
<h3 id="why-each-model-is-necessary">Why Each Model is “Necessary”</h3>
<p><strong>Policy:</strong> Obviously needed—this is what we’re training.</p>
<p><strong>Reference:</strong> Needed for KL penalty to prevent reward hacking. Without it, the policy might find exploits in the reward model.</p>
<p><strong>Value:</strong> Needed for GAE advantage estimation. This is where PPO’s memory cost comes from.</p>
<p><strong>Reward:</strong> Needed to provide training signal. Could potentially be called as an API, but latency becomes an issue.</p>
<h3 id="the-value-networks-burden">The Value Network’s Burden</h3>
<p>The value network is particularly problematic:</p>
<ol>
<li><strong>Same size as policy:</strong> For accurate value estimates, the value network should have similar capacity</li>
<li><strong>Learns a different task:</strong> Predicting expected reward vs. generating text</li>
<li><strong>Sparse reward signal:</strong> With rewards only at episode end, value learning is difficult</li>
<li><strong>Coupled training:</strong> Value must improve alongside policy; either failing destabilizes both</li>
</ol>
<blockquote>
<p><strong>The key question GRPO asks:</strong> Do we actually need a learned value network? Can we estimate advantages some other way?</p>
</blockquote>
<hr>
<h2 id="kl-penalties-and-reward-shaping">KL Penalties and Reward Shaping</h2>
<h3 id="the-kl-divergence-constraint">The KL Divergence Constraint</h3>
<p>RLHF adds a KL penalty to prevent the policy from diverging too far from the reference:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mi>r</mi><mi>t</mi><mtext>total</mtext></msubsup><mo>=</mo><msub><mi>r</mi><mi>t</mi></msub><mo>−</mo><mi>β</mi><mo>⋅</mo><mi>log</mi><mo>⁡</mo><mfrac><mrow><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>a</mi><mi>t</mi></msub><mi mathvariant="normal">∣</mi><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo></mrow><mrow><msub><mi>π</mi><mtext>ref</mtext></msub><mo stretchy="false">(</mo><msub><mi>a</mi><mi>t</mi></msub><mi mathvariant="normal">∣</mi><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo></mrow></mfrac></mrow><annotation encoding="application/x-tex">r_t^{\text{total}} = r_t - \beta \cdot \log\frac{\pi_\theta(a_t|s_t)}{\pi_{\text{ref}}(a_t|s_t)}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0961em;vertical-align:-0.247em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8491em;"><span style="top:-2.453em;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 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 text mtight"><span class="mord mtight">total</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><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.02778em;">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:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.05278em;">β</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.01389em;">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.03588em;">π</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">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2963em;"><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">t</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 mtight"><span class="mord mathnormal mtight">s</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2963em;"><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">t</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="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.03588em;">π</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.02778em;">θ</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">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2963em;"><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">t</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 mtight"><span class="mord mathnormal mtight">s</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2963em;"><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">t</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="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></p>
<p>The coefficient <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.05278em;">β</span></span></span></span> controls the penalty strength:</p>
<ul>
<li>High <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.05278em;">β</span></span></span></span>: Policy stays close to reference, may under-optimize reward</li>
<li>Low <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.05278em;">β</span></span></span></span>: Policy can diverge more, risk of reward hacking</li>
</ul>
<h3 id="why-kl-penalty-matters">Why KL Penalty Matters</h3>
<p>Without KL penalty, several failure modes emerge:</p>
<p><strong>1. Reward Hacking</strong>
The policy finds patterns that score high on the reward model but don’t represent genuine quality. For example, excessive hedging phrases like “I think” that reward models might prefer.</p>
<p><strong>2. Mode Collapse</strong>
The policy converges to generating the same high-reward response regardless of prompt, losing diversity.</p>
<p><strong>3. Capability Loss</strong>
The policy forgets useful behaviors from pretraining that weren’t represented in the reward model.</p>
<h3 id="kl-in-the-reward-vs-kl-in-the-loss">KL in the Reward vs. KL in the Loss</h3>
<p>There are two ways to incorporate KL:</p>
<p><strong>In the reward (standard PPO-RLHF):</strong>
<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>ϕ</mi></msub><mo stretchy="false">(</mo><mi>q</mi><mo separator="true">,</mo><mi>o</mi><mo stretchy="false">)</mo><mo>−</mo><mi>β</mi><mi>log</mi><mo>⁡</mo><mfrac><mrow><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>o</mi><mi>t</mi></msub><mi mathvariant="normal">∣</mi><mi>q</mi><mo separator="true">,</mo><msub><mi>o</mi><mrow><mo>&#x3C;</mo><mi>t</mi></mrow></msub><mo stretchy="false">)</mo></mrow><mrow><msub><mi>π</mi><mtext>ref</mtext></msub><mo stretchy="false">(</mo><msub><mi>o</mi><mi>t</mi></msub><mi mathvariant="normal">∣</mi><mi>q</mi><mo separator="true">,</mo><msub><mi>o</mi><mrow><mo>&#x3C;</mo><mi>t</mi></mrow></msub><mo stretchy="false">)</mo></mrow></mfrac></mrow><annotation encoding="application/x-tex">r_t = r_\phi(q, o) - \beta \log\frac{\pi_\theta(o_t|q, o_{&#x3C;t})}{\pi_{\text{ref}}(o_t|q, o_{&#x3C;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.02778em;">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:1.0361em;vertical-align:-0.2861em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">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" style="margin-right:0.03588em;">q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">o</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.53em;vertical-align:-0.52em;"></span><span class="mord mathnormal" style="margin-right:0.05278em;">β</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mop">lo<span style="margin-right:0.01389em;">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.03588em;">π</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">o</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2963em;"><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">t</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" style="margin-right:0.03588em;">q</span><span class="mpunct 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.2963em;"><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 mtight"><span class="mrel mtight">&#x3C;</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.1709em;"><span></span></span></span></span></span></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.03588em;">π</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.02778em;">θ</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">o</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2963em;"><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">t</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" style="margin-right:0.03588em;">q</span><span class="mpunct 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.2963em;"><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 mtight"><span class="mrel mtight">&#x3C;</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.1709em;"><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.52em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></p>
<p>This couples KL with the reward, affecting advantage estimation.</p>
<p><strong>In the loss directly (GRPO’s approach):</strong>
<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi><mo>=</mo><msup><mi>L</mi><mtext>CLIP</mtext></msup><mo>+</mo><mi>β</mi><mo>⋅</mo><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>ref</mtext></msub><mo stretchy="false">]</mo></mrow><annotation encoding="application/x-tex">L = L^{\text{CLIP}} + \beta \cdot \mathbb{D}_{\text{KL}}[\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.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.9247em;vertical-align:-0.0833em;"></span><span class="mord"><span class="mord mathnormal">L</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8413em;"><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 text mtight"><span class="mord mtight">CLIP</span></span></span></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.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.05278em;">β</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 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.03588em;">π</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.02778em;">θ</span></span></span></span><span 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.03588em;">π</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></p>
<p>This decouples KL from advantages, simplifying the optimization.</p>
<hr>
<h2 id="pytorch-implementation">PyTorch Implementation</h2>
<p>Here’s a complete, educational PPO implementation for LLM fine-tuning:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#9ECBFF">"""</span></span>
<span class="line"><span style="color:#9ECBFF">Proximal Policy Optimization (PPO) for LLM Fine-Tuning</span></span>
<span class="line"><span style="color:#9ECBFF">======================================================</span></span>
<span class="line"></span>
<span class="line"><span style="color:#9ECBFF">Educational implementation emphasizing clarity over optimization.</span></span>
<span class="line"><span style="color:#9ECBFF">For production, use libraries like TRL or VERL.</span></span>
<span class="line"><span style="color:#9ECBFF">"""</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> torch</span></span>
<span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> torch.nn.functional </span><span style="color:#F97583">as</span><span style="color:#E1E4E8"> F</span></span>
<span class="line"><span style="color:#F97583">from</span><span style="color:#E1E4E8"> torch </span><span style="color:#F97583">import</span><span style="color:#E1E4E8"> Tensor</span></span>
<span class="line"><span style="color:#F97583">from</span><span style="color:#E1E4E8"> dataclasses </span><span style="color:#F97583">import</span><span style="color:#E1E4E8"> dataclass</span></span>
<span class="line"><span style="color:#F97583">from</span><span style="color:#E1E4E8"> typing </span><span style="color:#F97583">import</span><span style="color:#E1E4E8"> Tuple, Optional</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#B392F0">@dataclass</span></span>
<span class="line"><span style="color:#F97583">class</span><span style="color:#B392F0"> PPOConfig</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#9ECBFF">    """PPO hyperparameters."""</span></span>
<span class="line"><span style="color:#6A737D">    # Core PPO</span></span>
<span class="line"><span style="color:#E1E4E8">    clip_epsilon: </span><span style="color:#79B8FF">float</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 0.2</span><span style="color:#6A737D">      # ε: clipping parameter</span></span>
<span class="line"><span style="color:#E1E4E8">    gamma: </span><span style="color:#79B8FF">float</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 1.0</span><span style="color:#6A737D">             # Discount (1.0 for finite episodes)</span></span>
<span class="line"><span style="color:#E1E4E8">    gae_lambda: </span><span style="color:#79B8FF">float</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 0.95</span><span style="color:#6A737D">       # λ: GAE parameter</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Loss coefficients</span></span>
<span class="line"><span style="color:#E1E4E8">    vf_coef: </span><span style="color:#79B8FF">float</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 0.5</span><span style="color:#6A737D">           # Value function loss weight</span></span>
<span class="line"><span style="color:#E1E4E8">    entropy_coef: </span><span style="color:#79B8FF">float</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 0.01</span><span style="color:#6A737D">     # Entropy bonus weight</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Training</span></span>
<span class="line"><span style="color:#E1E4E8">    ppo_epochs: </span><span style="color:#79B8FF">int</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 4</span><span style="color:#6A737D">            # K: epochs per batch</span></span>
<span class="line"><span style="color:#E1E4E8">    max_grad_norm: </span><span style="color:#79B8FF">float</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 0.5</span><span style="color:#6A737D">     # Gradient clipping</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # KL penalty</span></span>
<span class="line"><span style="color:#E1E4E8">    kl_coef: </span><span style="color:#79B8FF">float</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 0.1</span><span style="color:#6A737D">           # β: KL penalty coefficient</span></span>
<span class="line"><span style="color:#E1E4E8">    target_kl: </span><span style="color:#79B8FF">float</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 0.01</span><span style="color:#6A737D">        # Target KL for early stopping</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">def</span><span style="color:#B392F0"> compute_gae</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#E1E4E8">    rewards: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">    values: Tensor, </span></span>
<span class="line"><span style="color:#E1E4E8">    dones: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">    gamma: </span><span style="color:#79B8FF">float</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 1.0</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">    lam: </span><span style="color:#79B8FF">float</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 0.95</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">) -> Tuple[Tensor, Tensor]:</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#9ECBFF">    Compute Generalized Advantage Estimation.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Args:</span></span>
<span class="line"><span style="color:#9ECBFF">        rewards: [batch, seq_len] rewards at each step</span></span>
<span class="line"><span style="color:#9ECBFF">        values: [batch, seq_len + 1] value estimates</span></span>
<span class="line"><span style="color:#9ECBFF">        dones: [batch, seq_len] episode termination flags</span></span>
<span class="line"><span style="color:#9ECBFF">        gamma: Discount factor</span></span>
<span class="line"><span style="color:#9ECBFF">        lam: GAE lambda</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">    Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">        advantages: [batch, seq_len] GAE advantages</span></span>
<span class="line"><span style="color:#9ECBFF">        returns: [batch, seq_len] discounted returns</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#E1E4E8">    batch_size, seq_len </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> rewards.shape</span></span>
<span class="line"><span style="color:#E1E4E8">    advantages </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.zeros_like(rewards)</span></span>
<span class="line"><span style="color:#E1E4E8">    last_adv </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.zeros(batch_size, </span><span style="color:#FFAB70">device</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">rewards.device)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Backward pass through sequence</span></span>
<span class="line"><span style="color:#F97583">    for</span><span style="color:#E1E4E8"> t </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> reversed</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">range</span><span style="color:#E1E4E8">(seq_len)):</span></span>
<span class="line"><span style="color:#6A737D">        # Mask for non-terminal states</span></span>
<span class="line"><span style="color:#E1E4E8">        non_terminal </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 1.0</span><span style="color:#F97583"> -</span><span style="color:#E1E4E8"> dones[:, t]</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # TD error: δ_t = r_t + γV(s_{t+1}) - V(s_t)</span></span>
<span class="line"><span style="color:#E1E4E8">        delta </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> rewards[:, t] </span><span style="color:#F97583">+</span><span style="color:#E1E4E8"> gamma </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> values[:, t </span><span style="color:#F97583">+</span><span style="color:#79B8FF"> 1</span><span style="color:#E1E4E8">] </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> non_terminal </span><span style="color:#F97583">-</span><span style="color:#E1E4E8"> values[:, t]</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # GAE: A_t = δ_t + γλ A_{t+1}</span></span>
<span class="line"><span style="color:#E1E4E8">        advantages[:, t] </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> delta </span><span style="color:#F97583">+</span><span style="color:#E1E4E8"> gamma </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> lam </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> non_terminal </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> last_adv</span></span>
<span class="line"><span style="color:#E1E4E8">        last_adv </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> advantages[:, t]</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Returns = advantages + values</span></span>
<span class="line"><span style="color:#E1E4E8">    returns </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> advantages </span><span style="color:#F97583">+</span><span style="color:#E1E4E8"> values[:, :</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">]</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    return</span><span style="color:#E1E4E8"> advantages, returns</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">def</span><span style="color:#B392F0"> compute_policy_loss</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#E1E4E8">    log_probs: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">    old_log_probs: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">    advantages: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">    clip_epsilon: </span><span style="color:#79B8FF">float</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 0.2</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">) -> Tuple[Tensor, Tensor]:</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#9ECBFF">    Compute PPO clipped policy loss.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Args:</span></span>
<span class="line"><span style="color:#9ECBFF">        log_probs: [batch, seq_len] current policy log probs</span></span>
<span class="line"><span style="color:#9ECBFF">        old_log_probs: [batch, seq_len] old policy log probs</span></span>
<span class="line"><span style="color:#9ECBFF">        advantages: [batch, seq_len] advantage estimates</span></span>
<span class="line"><span style="color:#9ECBFF">        clip_epsilon: Clipping parameter ε</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">    Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">        policy_loss: Scalar loss (to minimize)</span></span>
<span class="line"><span style="color:#9ECBFF">        clip_fraction: Fraction of samples clipped</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#6A737D">    # Importance ratio: ρ = π_θ / π_old</span></span>
<span class="line"><span style="color:#E1E4E8">    ratio </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.exp(log_probs </span><span style="color:#F97583">-</span><span style="color:#E1E4E8"> old_log_probs)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Clipped ratio</span></span>
<span class="line"><span style="color:#E1E4E8">    clipped_ratio </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.clamp(ratio, </span><span style="color:#79B8FF">1</span><span style="color:#F97583"> -</span><span style="color:#E1E4E8"> clip_epsilon, </span><span style="color:#79B8FF">1</span><span style="color:#F97583"> +</span><span style="color:#E1E4E8"> clip_epsilon)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # PPO objective: min(ρA, clip(ρ)A)</span></span>
<span class="line"><span style="color:#E1E4E8">    policy_loss_1 </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> ratio </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> advantages</span></span>
<span class="line"><span style="color:#E1E4E8">    policy_loss_2 </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> clipped_ratio </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> advantages</span></span>
<span class="line"><span style="color:#E1E4E8">    policy_loss </span><span style="color:#F97583">=</span><span style="color:#F97583"> -</span><span style="color:#E1E4E8">torch.min(policy_loss_1, policy_loss_2).mean()</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Metrics</span></span>
<span class="line"><span style="color:#E1E4E8">    clip_fraction </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> ((ratio </span><span style="color:#F97583">-</span><span style="color:#79B8FF"> 1</span><span style="color:#E1E4E8">).abs() </span><span style="color:#F97583">></span><span style="color:#E1E4E8"> clip_epsilon).float().mean()</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    return</span><span style="color:#E1E4E8"> policy_loss, clip_fraction</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">def</span><span style="color:#B392F0"> compute_value_loss</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#E1E4E8">    values: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">    returns: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">    old_values: Optional[Tensor] </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> None</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">    clip_epsilon: </span><span style="color:#79B8FF">float</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 0.2</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">    clip_value: </span><span style="color:#79B8FF">bool</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> True</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">) -> Tensor:</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#9ECBFF">    Compute value function loss, optionally with clipping.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Args:</span></span>
<span class="line"><span style="color:#9ECBFF">        values: [batch, seq_len] current value estimates</span></span>
<span class="line"><span style="color:#9ECBFF">        returns: [batch, seq_len] target returns</span></span>
<span class="line"><span style="color:#9ECBFF">        old_values: [batch, seq_len] old value estimates (for clipping)</span></span>
<span class="line"><span style="color:#9ECBFF">        clip_epsilon: Clipping parameter</span></span>
<span class="line"><span style="color:#9ECBFF">        clip_value: Whether to clip value updates</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">    Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">        value_loss: Scalar loss</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#F97583">    if</span><span style="color:#E1E4E8"> clip_value </span><span style="color:#F97583">and</span><span style="color:#E1E4E8"> old_values </span><span style="color:#F97583">is</span><span style="color:#F97583"> not</span><span style="color:#79B8FF"> None</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#6A737D">        # Clipped value loss (prevents large value updates)</span></span>
<span class="line"><span style="color:#E1E4E8">        values_clipped </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> old_values </span><span style="color:#F97583">+</span><span style="color:#E1E4E8"> torch.clamp(</span></span>
<span class="line"><span style="color:#E1E4E8">            values </span><span style="color:#F97583">-</span><span style="color:#E1E4E8"> old_values, </span><span style="color:#F97583">-</span><span style="color:#E1E4E8">clip_epsilon, clip_epsilon</span></span>
<span class="line"><span style="color:#E1E4E8">        )</span></span>
<span class="line"><span style="color:#E1E4E8">        value_loss_1 </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> F.mse_loss(values, returns, </span><span style="color:#FFAB70">reduction</span><span style="color:#F97583">=</span><span style="color:#9ECBFF">'none'</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        value_loss_2 </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> F.mse_loss(values_clipped, returns, </span><span style="color:#FFAB70">reduction</span><span style="color:#F97583">=</span><span style="color:#9ECBFF">'none'</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        value_loss </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.max(value_loss_1, value_loss_2).mean()</span></span>
<span class="line"><span style="color:#F97583">    else</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#E1E4E8">        value_loss </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> F.mse_loss(values, returns)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    return</span><span style="color:#E1E4E8"> value_loss</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">def</span><span style="color:#B392F0"> compute_entropy_bonus</span><span style="color:#E1E4E8">(log_probs: Tensor, probs: Tensor) -> Tensor:</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#9ECBFF">    Compute entropy bonus for exploration.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    H[π] = -Σ π(a) log π(a)</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Args:</span></span>
<span class="line"><span style="color:#9ECBFF">        log_probs: Log probabilities</span></span>
<span class="line"><span style="color:#9ECBFF">        probs: Probabilities</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">    Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">        entropy: Scalar entropy (to maximize)</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#E1E4E8">    entropy </span><span style="color:#F97583">=</span><span style="color:#F97583"> -</span><span style="color:#E1E4E8">(probs </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> log_probs).sum(</span><span style="color:#FFAB70">dim</span><span style="color:#F97583">=-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">).mean()</span></span>
<span class="line"><span style="color:#F97583">    return</span><span style="color:#E1E4E8"> entropy</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">def</span><span style="color:#B392F0"> compute_kl_penalty</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#E1E4E8">    log_probs: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">    ref_log_probs: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">) -> Tensor:</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#9ECBFF">    Compute KL divergence from reference policy.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Uses the approximation: KL ≈ (log π_θ - log π_ref)</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Args:</span></span>
<span class="line"><span style="color:#9ECBFF">        log_probs: [batch, seq_len] current policy</span></span>
<span class="line"><span style="color:#9ECBFF">        ref_log_probs: [batch, seq_len] reference policy</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">    Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">        kl: Scalar KL divergence estimate</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#E1E4E8">    kl </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> (log_probs </span><span style="color:#F97583">-</span><span style="color:#E1E4E8"> ref_log_probs).mean()</span></span>
<span class="line"><span style="color:#F97583">    return</span><span style="color:#E1E4E8"> kl</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">class</span><span style="color:#B392F0"> PPOTrainer</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#9ECBFF">    PPO trainer for LLM fine-tuning.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Requires four models:</span></span>
<span class="line"><span style="color:#9ECBFF">    - policy_model: The LLM being trained</span></span>
<span class="line"><span style="color:#9ECBFF">    - value_model: Estimates V(s) for advantage computation</span></span>
<span class="line"><span style="color:#9ECBFF">    - reference_model: Frozen copy for KL penalty</span></span>
<span class="line"><span style="color:#9ECBFF">    - reward_model: Scores complete responses</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#79B8FF"> __init__</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#E1E4E8">        self,</span></span>
<span class="line"><span style="color:#E1E4E8">        policy_model: torch.nn.Module,</span></span>
<span class="line"><span style="color:#E1E4E8">        value_model: torch.nn.Module,</span></span>
<span class="line"><span style="color:#E1E4E8">        reference_model: torch.nn.Module,</span></span>
<span class="line"><span style="color:#E1E4E8">        reward_model: torch.nn.Module,</span></span>
<span class="line"><span style="color:#E1E4E8">        tokenizer,</span></span>
<span class="line"><span style="color:#E1E4E8">        config: PPOConfig,</span></span>
<span class="line"><span style="color:#E1E4E8">    ):</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.policy </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> policy_model</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.value </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> value_model</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.reference </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> reference_model</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.reward_fn </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> reward_model</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.tokenizer </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> tokenizer</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.config </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> config</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Freeze reference</span></span>
<span class="line"><span style="color:#F97583">        for</span><span style="color:#E1E4E8"> param </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.reference.parameters():</span></span>
<span class="line"><span style="color:#E1E4E8">            param.requires_grad </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> False</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Optimizers (separate for policy and value)</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.policy_optimizer </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.optim.AdamW(</span></span>
<span class="line"><span style="color:#79B8FF">            self</span><span style="color:#E1E4E8">.policy.parameters(), </span><span style="color:#FFAB70">lr</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1e-6</span></span>
<span class="line"><span style="color:#E1E4E8">        )</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.value_optimizer </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.optim.AdamW(</span></span>
<span class="line"><span style="color:#79B8FF">            self</span><span style="color:#E1E4E8">.value.parameters(), </span><span style="color:#FFAB70">lr</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1e-5</span></span>
<span class="line"><span style="color:#E1E4E8">        )</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#B392F0">    @torch.no_grad</span><span style="color:#E1E4E8">()</span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> generate_experience</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#E1E4E8">        self,</span></span>
<span class="line"><span style="color:#E1E4E8">        prompts: list[</span><span style="color:#79B8FF">str</span><span style="color:#E1E4E8">],</span></span>
<span class="line"><span style="color:#E1E4E8">        max_length: </span><span style="color:#79B8FF">int</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 512</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">    ) -> </span><span style="color:#79B8FF">dict</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Generate responses and compute rewards.</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">        Returns dict with all data needed for PPO update.</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#6A737D">        # Tokenize prompts</span></span>
<span class="line"><span style="color:#E1E4E8">        prompt_encodings </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.tokenizer(</span></span>
<span class="line"><span style="color:#E1E4E8">            prompts, </span><span style="color:#FFAB70">return_tensors</span><span style="color:#F97583">=</span><span style="color:#9ECBFF">"pt"</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">padding</span><span style="color:#F97583">=</span><span style="color:#79B8FF">True</span></span>
<span class="line"><span style="color:#E1E4E8">        )</span></span>
<span class="line"><span style="color:#E1E4E8">        prompt_ids </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> prompt_encodings[</span><span style="color:#9ECBFF">"input_ids"</span><span style="color:#E1E4E8">]</span></span>
<span class="line"><span style="color:#E1E4E8">        prompt_mask </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> prompt_encodings[</span><span style="color:#9ECBFF">"attention_mask"</span><span style="color:#E1E4E8">]</span></span>
<span class="line"><span style="color:#E1E4E8">        prompt_len </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> prompt_ids.shape[</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">]</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Generate responses</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.policy.eval()</span></span>
<span class="line"><span style="color:#E1E4E8">        outputs </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.policy.generate(</span></span>
<span class="line"><span style="color:#FFAB70">            input_ids</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">prompt_ids,</span></span>
<span class="line"><span style="color:#FFAB70">            attention_mask</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">prompt_mask,</span></span>
<span class="line"><span style="color:#FFAB70">            max_new_tokens</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">max_length,</span></span>
<span class="line"><span style="color:#FFAB70">            do_sample</span><span style="color:#F97583">=</span><span style="color:#79B8FF">True</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#FFAB70">            temperature</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1.0</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#FFAB70">            return_dict_in_generate</span><span style="color:#F97583">=</span><span style="color:#79B8FF">True</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#FFAB70">            output_scores</span><span style="color:#F97583">=</span><span style="color:#79B8FF">True</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">        )</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#E1E4E8">        sequences </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> outputs.sequences</span></span>
<span class="line"><span style="color:#E1E4E8">        response_ids </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> sequences[:, prompt_len:]</span></span>
<span class="line"><span style="color:#E1E4E8">        batch_size, response_len </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> response_ids.shape</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Get log probs from policy</span></span>
<span class="line"><span style="color:#E1E4E8">        policy_outputs </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.policy(sequences, </span><span style="color:#FFAB70">return_dict</span><span style="color:#F97583">=</span><span style="color:#79B8FF">True</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        policy_logits </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> policy_outputs.logits[:, prompt_len</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">:</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">, :]</span></span>
<span class="line"><span style="color:#E1E4E8">        policy_log_probs </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> F.log_softmax(policy_logits, </span><span style="color:#FFAB70">dim</span><span style="color:#F97583">=-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        policy_log_probs </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> policy_log_probs.gather(</span></span>
<span class="line"><span style="color:#FFAB70">            dim</span><span style="color:#F97583">=-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">index</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">response_ids.unsqueeze(</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        ).squeeze(</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Get log probs from reference</span></span>
<span class="line"><span style="color:#E1E4E8">        ref_outputs </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.reference(sequences, </span><span style="color:#FFAB70">return_dict</span><span style="color:#F97583">=</span><span style="color:#79B8FF">True</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        ref_logits </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> ref_outputs.logits[:, prompt_len</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">:</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">, :]</span></span>
<span class="line"><span style="color:#E1E4E8">        ref_log_probs </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> F.log_softmax(ref_logits, </span><span style="color:#FFAB70">dim</span><span style="color:#F97583">=-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        ref_log_probs </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> ref_log_probs.gather(</span></span>
<span class="line"><span style="color:#FFAB70">            dim</span><span style="color:#F97583">=-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">index</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">response_ids.unsqueeze(</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        ).squeeze(</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Get value estimates</span></span>
<span class="line"><span style="color:#E1E4E8">        value_outputs </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.value(sequences, </span><span style="color:#FFAB70">return_dict</span><span style="color:#F97583">=</span><span style="color:#79B8FF">True</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#6A737D">        # Assuming value head outputs scalar per position</span></span>
<span class="line"><span style="color:#E1E4E8">        values </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> value_outputs.logits[:, prompt_len</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">:, </span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">]  </span><span style="color:#6A737D"># [batch, response_len + 1]</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Compute rewards (sparse: only at end)</span></span>
<span class="line"><span style="color:#E1E4E8">        rewards </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.zeros(batch_size, response_len)</span></span>
<span class="line"><span style="color:#F97583">        for</span><span style="color:#E1E4E8"> i </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> range</span><span style="color:#E1E4E8">(batch_size):</span></span>
<span class="line"><span style="color:#E1E4E8">            response_text </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.tokenizer.decode(response_ids[i])</span></span>
<span class="line"><span style="color:#E1E4E8">            prompt_text </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> prompts[i]</span></span>
<span class="line"><span style="color:#E1E4E8">            rewards[i, </span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">] </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.reward_fn(prompt_text, response_text)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Add KL penalty to rewards</span></span>
<span class="line"><span style="color:#E1E4E8">        kl_penalty </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.config.kl_coef </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> (policy_log_probs </span><span style="color:#F97583">-</span><span style="color:#E1E4E8"> ref_log_probs)</span></span>
<span class="line"><span style="color:#E1E4E8">        rewards </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> rewards </span><span style="color:#F97583">-</span><span style="color:#E1E4E8"> kl_penalty</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Create done mask (only last token is "done")</span></span>
<span class="line"><span style="color:#E1E4E8">        dones </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.zeros(batch_size, response_len)</span></span>
<span class="line"><span style="color:#E1E4E8">        dones[:, </span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">] </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 1.0</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Compute GAE</span></span>
<span class="line"><span style="color:#E1E4E8">        advantages, returns </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> compute_gae(</span></span>
<span class="line"><span style="color:#E1E4E8">            rewards, values,</span></span>
<span class="line"><span style="color:#E1E4E8">            dones,</span></span>
<span class="line"><span style="color:#FFAB70">            gamma</span><span style="color:#F97583">=</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.config.gamma,</span></span>
<span class="line"><span style="color:#FFAB70">            lam</span><span style="color:#F97583">=</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.config.gae_lambda,</span></span>
<span class="line"><span style="color:#E1E4E8">        )</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Normalize advantages</span></span>
<span class="line"><span style="color:#E1E4E8">        advantages </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> (advantages </span><span style="color:#F97583">-</span><span style="color:#E1E4E8"> advantages.mean()) </span><span style="color:#F97583">/</span><span style="color:#E1E4E8"> (advantages.std() </span><span style="color:#F97583">+</span><span style="color:#79B8FF"> 1e-8</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        return</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#9ECBFF">            "sequences"</span><span style="color:#E1E4E8">: sequences,</span></span>
<span class="line"><span style="color:#9ECBFF">            "response_ids"</span><span style="color:#E1E4E8">: response_ids,</span></span>
<span class="line"><span style="color:#9ECBFF">            "old_log_probs"</span><span style="color:#E1E4E8">: policy_log_probs.detach(),</span></span>
<span class="line"><span style="color:#9ECBFF">            "old_values"</span><span style="color:#E1E4E8">: values[:, :</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">].detach(),</span></span>
<span class="line"><span style="color:#9ECBFF">            "advantages"</span><span style="color:#E1E4E8">: advantages,</span></span>
<span class="line"><span style="color:#9ECBFF">            "returns"</span><span style="color:#E1E4E8">: returns,</span></span>
<span class="line"><span style="color:#9ECBFF">            "ref_log_probs"</span><span style="color:#E1E4E8">: ref_log_probs,</span></span>
<span class="line"><span style="color:#E1E4E8">        }</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> train_step</span><span style="color:#E1E4E8">(self, experience: </span><span style="color:#79B8FF">dict</span><span style="color:#E1E4E8">) -> </span><span style="color:#79B8FF">dict</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Perform PPO update on collected experience.</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">        Args:</span></span>
<span class="line"><span style="color:#9ECBFF">            experience: Dict from generate_experience()</span></span>
<span class="line"><span style="color:#9ECBFF">            </span></span>
<span class="line"><span style="color:#9ECBFF">        Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">            metrics: Training metrics</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#E1E4E8">        sequences </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> experience[</span><span style="color:#9ECBFF">"sequences"</span><span style="color:#E1E4E8">]</span></span>
<span class="line"><span style="color:#E1E4E8">        response_ids </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> experience[</span><span style="color:#9ECBFF">"response_ids"</span><span style="color:#E1E4E8">]</span></span>
<span class="line"><span style="color:#E1E4E8">        old_log_probs </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> experience[</span><span style="color:#9ECBFF">"old_log_probs"</span><span style="color:#E1E4E8">]</span></span>
<span class="line"><span style="color:#E1E4E8">        old_values </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> experience[</span><span style="color:#9ECBFF">"old_values"</span><span style="color:#E1E4E8">]</span></span>
<span class="line"><span style="color:#E1E4E8">        advantages </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> experience[</span><span style="color:#9ECBFF">"advantages"</span><span style="color:#E1E4E8">]</span></span>
<span class="line"><span style="color:#E1E4E8">        returns </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> experience[</span><span style="color:#9ECBFF">"returns"</span><span style="color:#E1E4E8">]</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#E1E4E8">        prompt_len </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> sequences.shape[</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">] </span><span style="color:#F97583">-</span><span style="color:#E1E4E8"> response_ids.shape[</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">]</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#E1E4E8">        metrics </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#9ECBFF">            "policy_loss"</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#9ECBFF">            "value_loss"</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#9ECBFF">            "entropy"</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#9ECBFF">            "kl"</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#9ECBFF">            "clip_fraction"</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">        }</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Multiple epochs over the same data</span></span>
<span class="line"><span style="color:#F97583">        for</span><span style="color:#E1E4E8"> epoch </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> range</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.config.ppo_epochs):</span></span>
<span class="line"><span style="color:#79B8FF">            self</span><span style="color:#E1E4E8">.policy.train()</span></span>
<span class="line"><span style="color:#79B8FF">            self</span><span style="color:#E1E4E8">.value.train()</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#6A737D">            # Forward pass - policy</span></span>
<span class="line"><span style="color:#E1E4E8">            policy_outputs </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.policy(sequences, </span><span style="color:#FFAB70">return_dict</span><span style="color:#F97583">=</span><span style="color:#79B8FF">True</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">            policy_logits </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> policy_outputs.logits[:, prompt_len</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">:</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">, :]</span></span>
<span class="line"><span style="color:#E1E4E8">            policy_log_probs </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> F.log_softmax(policy_logits, </span><span style="color:#FFAB70">dim</span><span style="color:#F97583">=-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">            policy_log_probs </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> policy_log_probs.gather(</span></span>
<span class="line"><span style="color:#FFAB70">                dim</span><span style="color:#F97583">=-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">index</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">response_ids.unsqueeze(</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">            ).squeeze(</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#6A737D">            # Forward pass - value</span></span>
<span class="line"><span style="color:#E1E4E8">            value_outputs </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.value(sequences, </span><span style="color:#FFAB70">return_dict</span><span style="color:#F97583">=</span><span style="color:#79B8FF">True</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">            values </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> value_outputs.logits[:, prompt_len</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">:</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">]</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#6A737D">            # Compute losses</span></span>
<span class="line"><span style="color:#E1E4E8">            policy_loss, clip_frac </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> compute_policy_loss(</span></span>
<span class="line"><span style="color:#E1E4E8">                policy_log_probs, old_log_probs, advantages,</span></span>
<span class="line"><span style="color:#79B8FF">                self</span><span style="color:#E1E4E8">.config.clip_epsilon</span></span>
<span class="line"><span style="color:#E1E4E8">            )</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#E1E4E8">            value_loss </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> compute_value_loss(</span></span>
<span class="line"><span style="color:#E1E4E8">                values, returns, old_values,</span></span>
<span class="line"><span style="color:#79B8FF">                self</span><span style="color:#E1E4E8">.config.clip_epsilon</span></span>
<span class="line"><span style="color:#E1E4E8">            )</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#6A737D">            # KL for early stopping</span></span>
<span class="line"><span style="color:#E1E4E8">            kl </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> compute_kl_penalty(policy_log_probs, old_log_probs)</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#6A737D">            # Combined loss</span></span>
<span class="line"><span style="color:#E1E4E8">            loss </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> (</span></span>
<span class="line"><span style="color:#E1E4E8">                policy_loss </span></span>
<span class="line"><span style="color:#F97583">                +</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.config.vf_coef </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> value_loss</span></span>
<span class="line"><span style="color:#E1E4E8">            )</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#6A737D">            # Backward and update</span></span>
<span class="line"><span style="color:#79B8FF">            self</span><span style="color:#E1E4E8">.policy_optimizer.zero_grad()</span></span>
<span class="line"><span style="color:#79B8FF">            self</span><span style="color:#E1E4E8">.value_optimizer.zero_grad()</span></span>
<span class="line"><span style="color:#E1E4E8">            loss.backward()</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#E1E4E8">            torch.nn.utils.clip_grad_norm_(</span></span>
<span class="line"><span style="color:#79B8FF">                self</span><span style="color:#E1E4E8">.policy.parameters(), </span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.config.max_grad_norm</span></span>
<span class="line"><span style="color:#E1E4E8">            )</span></span>
<span class="line"><span style="color:#E1E4E8">            torch.nn.utils.clip_grad_norm_(</span></span>
<span class="line"><span style="color:#79B8FF">                self</span><span style="color:#E1E4E8">.value.parameters(), </span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.config.max_grad_norm</span></span>
<span class="line"><span style="color:#E1E4E8">            )</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#79B8FF">            self</span><span style="color:#E1E4E8">.policy_optimizer.step()</span></span>
<span class="line"><span style="color:#79B8FF">            self</span><span style="color:#E1E4E8">.value_optimizer.step()</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#6A737D">            # Track metrics</span></span>
<span class="line"><span style="color:#E1E4E8">            metrics[</span><span style="color:#9ECBFF">"policy_loss"</span><span style="color:#E1E4E8">] </span><span style="color:#F97583">+=</span><span style="color:#E1E4E8"> policy_loss.item()</span></span>
<span class="line"><span style="color:#E1E4E8">            metrics[</span><span style="color:#9ECBFF">"value_loss"</span><span style="color:#E1E4E8">] </span><span style="color:#F97583">+=</span><span style="color:#E1E4E8"> value_loss.item()</span></span>
<span class="line"><span style="color:#E1E4E8">            metrics[</span><span style="color:#9ECBFF">"kl"</span><span style="color:#E1E4E8">] </span><span style="color:#F97583">+=</span><span style="color:#E1E4E8"> kl.item()</span></span>
<span class="line"><span style="color:#E1E4E8">            metrics[</span><span style="color:#9ECBFF">"clip_fraction"</span><span style="color:#E1E4E8">] </span><span style="color:#F97583">+=</span><span style="color:#E1E4E8"> clip_frac.item()</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#6A737D">            # Early stopping on KL</span></span>
<span class="line"><span style="color:#F97583">            if</span><span style="color:#E1E4E8"> kl.item() </span><span style="color:#F97583">></span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.config.target_kl </span><span style="color:#F97583">*</span><span style="color:#79B8FF"> 1.5</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#F97583">                break</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Average metrics</span></span>
<span class="line"><span style="color:#F97583">        for</span><span style="color:#E1E4E8"> k </span><span style="color:#F97583">in</span><span style="color:#E1E4E8"> metrics:</span></span>
<span class="line"><span style="color:#E1E4E8">            metrics[k] </span><span style="color:#F97583">/=</span><span style="color:#E1E4E8"> (epoch </span><span style="color:#F97583">+</span><span style="color:#79B8FF"> 1</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        return</span><span style="color:#E1E4E8"> metrics</span></span>
<span class="line"></span></code></pre>
<hr>
<h2 id="ppos-limitations-for-llms">PPO’s Limitations for LLMs</h2>
<p>Despite its success, PPO has significant drawbacks for LLM alignment:</p>
<h3 id="1-memory-overhead">1. Memory Overhead</h3>
<p>The four-model architecture is expensive:</p>
<ul>
<li>For 7B models: ~84 GB just for weights</li>
<li>Doesn’t include activations, optimizer states, or gradients</li>
<li>Often requires model parallelism even on high-end GPUs</li>
</ul>
<h3 id="2-value-function-challenges">2. Value Function Challenges</h3>
<p>The value network faces unique difficulties in LLM settings:</p>
<p><strong>Sparse rewards:</strong> With rewards only at episode end, the value function must predict final reward from intermediate states. Early in generation, this prediction is extremely noisy.</p>
<p><strong>State space complexity:</strong> The “state” is all possible text prefixes—an enormous, structured space. Learning accurate values across this space is hard.</p>
<p><strong>Representation mismatch:</strong> The value network uses the same architecture as a language model, but predicts scalars instead of generating text. This may not be optimal.</p>
<h3 id="3-implementation-complexity">3. Implementation Complexity</h3>
<p>PPO has many moving parts:</p>
<ul>
<li>GAE computation with proper masking</li>
<li>Clipping with correct sign handling</li>
<li>Value function clipping (optional but common)</li>
<li>Entropy bonus tuning</li>
<li>KL penalty in rewards</li>
<li>Multiple epochs with early stopping</li>
</ul>
<p>Each component requires careful implementation and debugging.</p>
<h3 id="4-hyperparameter-sensitivity">4. Hyperparameter Sensitivity</h3>
<p>PPO performance depends on many hyperparameters:</p>
<ul>
<li>Clip epsilon (ε)</li>
<li>GAE lambda (λ)</li>
<li>Value function coefficient</li>
<li>Entropy coefficient</li>
<li>KL penalty coefficient</li>
<li>Learning rates for policy and value</li>
<li>Number of PPO epochs</li>
</ul>
<p>Tuning these for each new task and model size is time-consuming.</p>
<h3 id="5-sample-efficiency">5. Sample Efficiency</h3>
<p>PPO is on-policy: data from old policies can’t be reused directly. Each batch of experience trains for K epochs, then is discarded. For expensive LLM generation, this can be wasteful.</p>
<blockquote>
<p><strong>These limitations motivate GRPO:</strong> What if we could eliminate the value network entirely? What if advantages could be computed without learning V(s)? Part 3 explores this elegant alternative.</p>
</blockquote>
<hr>
<h2 id="key-takeaways">Key Takeaways</h2>
<h3 id="trust-regions">Trust Regions</h3>
<ul>
<li>Large policy updates destabilize training</li>
<li>TRPO uses hard KL constraints (expensive)</li>
<li>PPO uses clipping (simple, effective)</li>
</ul>
<h3 id="the-clipped-objective">The Clipped Objective</h3>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>L</mi><mtext>CLIP</mtext></msup><mo>=</mo><mi>min</mi><mo>⁡</mo><mo stretchy="false">(</mo><msub><mi>ρ</mi><mi>t</mi></msub><msub><mi>A</mi><mi>t</mi></msub><mo separator="true">,</mo><mtext>clip</mtext><mo stretchy="false">(</mo><msub><mi>ρ</mi><mi>t</mi></msub><mo separator="true">,</mo><mn>1</mn><mo>−</mo><mi>ε</mi><mo separator="true">,</mo><mn>1</mn><mo>+</mo><mi>ε</mi><mo stretchy="false">)</mo><msub><mi>A</mi><mi>t</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">L^{\text{CLIP}} = \min(\rho_t A_t, \text{clip}(\rho_t, 1-\varepsilon, 1+\varepsilon) A_t)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8413em;"></span><span class="mord"><span class="mord mathnormal">L</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8413em;"><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 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="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"><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="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="mpunct">,</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="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="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">ε</span><span class="mclose">)</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="mclose">)</span></span></span></span></p>
<ul>
<li>Limits how much action probabilities can change</li>
<li>Provides “soft” trust region with first-order optimization</li>
<li>Default <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">\varepsilon = 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></li>
</ul>
<h3 id="generalized-advantage-estimation">Generalized Advantage Estimation</h3>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mover accent="true"><mi>A</mi><mo>^</mo></mover><mi>t</mi><mtext>GAE</mtext></msubsup><mo>=</mo><msubsup><mo>∑</mo><mrow><mi>k</mi><mo>=</mo><mn>0</mn></mrow><mi mathvariant="normal">∞</mi></msubsup><mo stretchy="false">(</mo><mi>γ</mi><mi>λ</mi><msup><mo stretchy="false">)</mo><mi>k</mi></msup><msub><mi>δ</mi><mrow><mi>t</mi><mo>+</mo><mi>k</mi></mrow></msub></mrow><annotation encoding="application/x-tex">\hat{A}_t^{\text{GAE}} = \sum_{k=0}^{\infty} (\gamma\lambda)^k \delta_{t+k}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.1938em;vertical-align:-0.247em;"></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.8413em;"><span style="top:-2.453em;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 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 text mtight"><span class="mord mtight">GAE</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><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.1488em;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.8043em;"><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.03148em;">k</span><span class="mrel mtight">=</span><span class="mord mtight">0</span></span></span></span><span style="top:-3.2029em;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 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="mopen">(</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.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.03148em;">k</span></span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03785em;">δ</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.03148em;">k</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></p>
<ul>
<li>Interpolates between TD (low variance) and MC (low bias)</li>
<li>Default <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></li>
<li>Computed efficiently with backward recursion</li>
</ul>
<h3 id="the-four-model-problem">The Four-Model Problem</h3>
<ul>
<li>Policy, Reference, Value, Reward</li>
<li>~84 GB for 7B model (weights only)</li>
<li>Value network is the memory bottleneck</li>
</ul>
<h3 id="ppos-challenges-for-llms">PPO’s Challenges for LLMs</h3>
<ul>
<li>Memory overhead from value network</li>
<li>Sparse rewards make value learning hard</li>
<li>Implementation complexity</li>
<li>Hyperparameter sensitivity</li>
</ul>
<hr>
<h2 id="whats-next">What’s Next</h2>
<p>In <strong>Part 3: GRPO</strong>, we’ll see how DeepSeek eliminates the value network entirely:</p>
<ul>
<li>Group-relative advantages from sampled outputs</li>
<li>33% memory reduction</li>
<li>Simpler implementation</li>
<li>Equal or better performance</li>
</ul>
<p>The insight is elegant: instead of learning to predict expected reward, just sample multiple outputs and compare them directly.</p>
<hr>
<h2 id="further-reading">Further Reading</h2>
<p><strong>Original Papers:</strong></p>
<ul>
<li><a href="https://arxiv.org/abs/1707.06347">Proximal Policy Optimization Algorithms</a> (Schulman et al., 2017)</li>
<li><a href="https://arxiv.org/abs/1502.05477">Trust Region Policy Optimization</a> (Schulman et al., 2015)</li>
<li><a href="https://arxiv.org/abs/1506.02438">High-Dimensional Continuous Control Using Generalized Advantage Estimation</a> (Schulman et al., 2015 — GAE)</li>
</ul>
<p><strong>LLM Applications:</strong></p>
<ul>
<li><a href="https://arxiv.org/abs/2203.02155">Training language models to follow instructions with human feedback</a> (InstructGPT)</li>
<li><a href="https://arxiv.org/abs/2212.08073">Constitutional AI: Harmlessness from AI Feedback</a> (Anthropic)</li>
</ul>
<p><strong>Implementations:</strong></p>
<ul>
<li><a href="https://github.com/huggingface/trl">TRL: Transformer Reinforcement Learning</a></li>
<li><a href="https://github.com/OpenRLHF/OpenRLHF">OpenRLHF</a></li>
<li><a href="https://github.com/volcengine/verl">VERL</a></li>
</ul>]]></content:encoded><enclosure url="https://www.vitorsousa.com/_astro/contextual-bandits-p4.C6h5Erdc.png" length="756000" type="image/png"/></item><item><title>Neural Contextual Bandits for High-Dimensional Data</title><link>https://www.vitorsousa.com/blog/contextual-bandits-neural/</link><guid isPermaLink="true">https://www.vitorsousa.com/blog/contextual-bandits-neural/</guid><description>When linear models fail, neural networks step in. Learn when to use neural bandits, how to quantify uncertainty with bootstrap ensembles, and handle high-dimensional action spaces with embeddings and two-stage selection.</description><pubDate>Wed, 19 Nov 2025 11:00:00 GMT</pubDate><content:encoded><![CDATA[<h1 id="a-transformer-from-raw-tensors-what-112-tests-taught-me">A Transformer from Raw Tensors: What 112 Tests Taught Me</h1>
<blockquote>
<p><strong>TL;DR:</strong> I built a decoder-only transformer from raw tensors — attention with RoPE, RMSNorm, SwiGLU, an incremental KV-cache, and greedy / temperature / top-k / top-p sampling — wrapped in 112 tests that run on every push. This post is not about the architecture; the <a href="/foundations/attention-from-scratch/">Foundations series</a> covers that. It’s about verification: the three properties that are easiest to get <em>silently</em> wrong — cache equivalence, the top-p boundary, causality — and the tests that pin each one. Plus the one bug that did ship: it lived in the only file the test suite didn’t cover.</p>
</blockquote>
<hr>
<h2 id="what-i-built">What I Built</h2>
<p>Phase 1 of <a href="https://github.com/vitorhcsousa/verifiable-reward-lab">verifiable-reward-lab</a> is a complete decoder-only transformer written from raw tensors — no <code>nn.Transformer</code>, no Hugging Face, no borrowed attention. Scaled dot-product and multi-head attention with an additive causal mask, positional encoding (sinusoidal, learned, and RoPE), RMSNorm and LayerNorm, GELU and SwiGLU feed-forward variants, an incremental KV-cache, and a <code>generate()</code> loop with greedy, temperature, top-k, and top-p decoding. Every component ships with shape tests, numerical tests, and gradient tests — 112 in total, green in CI on every push.</p>
<p>Everything is public at <a href="https://github.com/vitorhcsousa/verifiable-reward-lab">verifiable-reward-lab</a>, and the theory lives in the Foundations series: <a href="/foundations/attention-from-scratch/">Part 1: Attention</a> · <a href="/foundations/positional-encoding/">Part 2: Positional Encoding</a> · <a href="/foundations/building-a-transformer/">Part 3: Building a Transformer</a>.</p>
<p>The point of “from raw tensors” was never reinvention. It was ownership: when every shape, mask, and gradient is code I wrote, every failure is mine to explain — and every property I claim is one I can prove with a test. What follows is the part the architecture posts don’t tell you: the three places where a transformer can be wrong while <em>looking</em> right, and what it takes to be sure it isn’t.</p>
<hr>
<h2 id="three-properties-that-fail-silently--and-the-test-that-pins-each">Three Properties That Fail Silently — and the Test That Pins Each</h2>
<h3 id="1-a-kv-cache-can-lie-to-you">1. A KV-cache can lie to you</h3>
<p>The KV-cache is an optimization with a dangerous failure mode. It changes the compute path — feed one token per step against cached keys and values instead of re-processing the whole sequence — but it must not change the math. If it does, nothing crashes. Generation keeps producing plausible text, just from the wrong distribution. Eyeballing samples will never catch it.</p>
<p>The signature test of Phase 1 makes the equivalence explicit: prime the cache on a prefix, decode the rest one token at a time, and require the per-step logits to match a single full forward over the whole sequence:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#F97583">assert</span><span style="color:#E1E4E8"> torch.allclose(incremental, full, </span><span style="color:#FFAB70">atol</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1e-5</span><span style="color:#E1E4E8">)</span></span>
<span class="line"></span></code></pre>
<p>The same discipline shaped <code>generate()</code> itself. Additive positional encodings (sinusoidal, learned) have no valid incremental path — position is baked into the embedding at encode time — so caching is only trusted where it’s mathematically sound:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#6A737D"># Additive positional encodings have no incremental path -> recompute.</span></span>
<span class="line"><span style="color:#E1E4E8">cache_ok </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> use_cache </span><span style="color:#F97583">and</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.pos_emb </span><span style="color:#F97583">is</span><span style="color:#79B8FF"> None</span><span style="color:#6A737D">  # RoPE / none only</span></span>
<span class="line"></span></code></pre>
<p>Rather than serve subtly wrong logits, the model falls back to full recompute. Slower and correct beats fast and quietly wrong.</p>
<h3 id="2-the-top-p-off-by-one">2. The top-p off-by-one</h3>
<p>Nucleus sampling has a boundary condition that buggy implementations get wrong: the token that <em>crosses</em> the threshold p belongs <strong>inside</strong> the nucleus. The definition is “the smallest prefix whose cumulative probability reaches p” — drop the crossing token and you’ve kept the largest prefix that <em>doesn’t</em> reach p, which is a different (and occasionally empty) set.</p>
<p>The implementation makes the fix visible — after masking <code>cumprobs > top_p</code>, the mask is shifted right by one position, so the boundary token survives and every row keeps at least one candidate:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#E1E4E8">remove </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> cumprobs </span><span style="color:#F97583">></span><span style="color:#E1E4E8"> top_p</span></span>
<span class="line"><span style="color:#E1E4E8">remove[</span><span style="color:#79B8FF">...</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">:] </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> remove[</span><span style="color:#79B8FF">...</span><span style="color:#E1E4E8">, :</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">].clone()  </span><span style="color:#6A737D"># keep the token that crosses p</span></span>
<span class="line"><span style="color:#E1E4E8">remove[</span><span style="color:#79B8FF">...</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">] </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> False</span><span style="color:#6A737D">                      # at least one survivor per row</span></span>
<span class="line"></span></code></pre>
<p>The test pins it with numbers small enough to check by hand: probabilities <code>[0.50, 0.30, 0.15, 0.05]</code> and <code>p = 0.75</code> give cumulative sums <code>[0.50, 0.80, 0.95, 1.00]</code>, so the correct nucleus is exactly tokens {0, 1}. Two hundred seeded draws must never leave that set — and token 1, the crossing token, must actually be reachable. A companion test checks the renormalized distribution is exactly <code>[0.625, 0.375, 0, 0]</code>: out-of-nucleus probability is zero, not merely small.</p>
<h3 id="3-causality-checked-twice">3. Causality, checked twice</h3>
<p>”The mask blocks the future” is the kind of claim that deserves more than a shape check — a mask can have the right shape and still be applied to the wrong axis, or dropped entirely on some code path. Phase 1 checks causality by two independent routes.</p>
<p>The behavioral route: change the <em>last</em> token of a sequence and require every earlier position’s logits to stay identical at <code>atol=1e-6</code> — while the last position itself must change. If the causal mask is dropped or mis-shaped, this fails loudly.</p>
<p>The gradient route: an end-to-end finite-difference check, run in float64 through the whole stack, comparing autograd’s analytic gradients against centred numerical estimates on the LM head — plus a test asserting gradients actually reach every stage (embedding, attention projections, FFN, final norm) and are finite and non-zero. Hand-rolled components mean hand-rolled backward risks; this catches the class of bug where the forward pass is right and the learning is quietly broken.</p>
<hr>
<h2 id="sampling-trade-offs-condensed">Sampling Trade-offs, Condensed</h2>
<p>Decoding strategy is a set of trade-offs, not a single dial. The full reasoning lives in the code’s docstrings and tests; here is the compressed decision table:</p>



































<table><thead><tr><th>Mode</th><th>What it changes</th><th>Failure mode</th><th>Test that pins it</th></tr></thead><tbody><tr><td>Greedy</td><td>Takes the argmax; deterministic</td><td>Repetitive, degenerate text</td><td><code>sample()</code> at <code>temperature=0</code> equals argmax exactly, and short-circuits before any filter</td></tr><tr><td>Temperature</td><td>Rescales logits before softmax — sharper below 1, flatter above</td><td>High T degrades into noise; T=0 must not divide by zero</td><td>Temperature is <em>only</em> a logit rescale; T=0 routes to the greedy branch</td></tr><tr><td>Top-k</td><td>Hard-caps the candidate set at k tokens</td><td>Fixed k ignores how flat or peaked the distribution is</td><td>Support restricted per row; k=1 equals greedy; k ≥ vocab is a no-op</td></tr><tr><td>Top-p</td><td>Keeps the smallest prefix reaching cumulative probability p — adapts to the distribution</td><td>The off-by-one above — the boundary token dropped</td><td>Nucleus {0,1} pinned by hand-checkable numbers; renormalization exact</td></tr></tbody></table>
<p>One design decision worth naming: filters apply on <em>logits</em> (masking to <code>-inf</code>) before temperature and softmax, so masked tokens get probability exactly zero and the surviving distribution renormalizes for free. And the filters compose — top-k then top-p — with a test covering the combination.</p>
<hr>
<h2 id="performance-what-the-kv-cache-actually-buys">Performance: What the KV-Cache Actually Buys</h2>
<p>Measured on the reference config (<code>d_model=256</code>, 4 layers, 4 heads, vocab 1000, RoPE, float32, CPU, seed 0), median of 5 runs:</p>



































<table><thead><tr><th align="center">n_new</th><th align="center">use_cache</th><th align="center">median tok/s</th><th align="center">min–max</th></tr></thead><tbody><tr><td align="center">128</td><td align="center">False</td><td align="center">317.9</td><td align="center">312.2 – 326.8</td></tr><tr><td align="center">128</td><td align="center">True</td><td align="center">814.3</td><td align="center">785.9 – 837.3</td></tr><tr><td align="center">256</td><td align="center">False</td><td align="center">185.2</td><td align="center">175.3 – 185.7</td></tr><tr><td align="center">256</td><td align="center">True</td><td align="center">699.5</td><td align="center">683.7 – 707.2</td></tr></tbody></table>
<p>Without the cache, every step re-processes the whole sequence — O(T²) per generation — so doubling the tokens costs 42% of throughput. With the cache each step processes one token against cached K/V — O(T) — and the same doubling costs only 14%. The speedup widens from 2.6× at 128 tokens to 3.8× at 256, and keeps growing with context.</p>
<p>Now the confession. The one bug that shipped in Phase 1 wasn’t in the model — it was in the benchmark. The first committed version claimed a median over <code>N_RUNS</code> but actually timed each configuration once: <code>rates = [time_once(...)]</code> instead of a loop. The model code had 112 tests; the benchmark had none, and that’s exactly where the bug lived. The fix came with a lesson attached — the benchmark now opens with its own correctness gate before timing anything:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#F97583">assert</span><span style="color:#E1E4E8"> torch.equal(ids_nc, ids_c), </span><span style="color:#9ECBFF">"cache != no-cache in greedy — bug, not benchmark"</span></span>
<span class="line"></span></code></pre>
<p>Benchmarks are code too. They need the same review, and ideally the same tests, as the thing they measure.</p>
<hr>
<h2 id="what-opens-next">What Opens Next</h2>
<p>Phase 1 is closed and public: the model, the tests, the benchmark, and the three Foundations articles that explain the theory. What comes next is deliberately not decided here. The active plan sequences Transformer Internals → Research Engineering Core → an Eval / Verifiable System with failure analysis — and the next front gets chosen at the monthly checkpoint, not promised in a closing paragraph. Pretraining stays parked, not abandoned.</p>
<p>If you want the full picture — code, phases, and the articles as they ship — it lives on the <a href="/portfolio/learning-with-verifiable-rewards/">project page</a>.</p>]]></content:encoded><enclosure url="https://www.vitorsousa.com/_astro/contextual-bandits-p4.C6h5Erdc.png" length="756000" type="image/png"/></item><item><title>Implementing Contextual Bandits: Complete Algorithm Guide</title><link>https://www.vitorsousa.com/blog/contextual-bandits-algorithms/</link><guid isPermaLink="true">https://www.vitorsousa.com/blog/contextual-bandits-algorithms/</guid><description>Complete Python implementations of ε-greedy, UCB, LinUCB, and Thompson Sampling. Learn which algorithm to use for your problem with default hyperparameters and practical tuning guidance.</description><pubDate>Mon, 17 Nov 2025 10:30:00 GMT</pubDate><content:encoded><![CDATA[<h1 id="grpo-eliminating-the-value-network">GRPO: Eliminating the Value Network</h1>
<p><strong>Part 3 of 4:</strong> The Memory-Efficient Alternative</p>
<blockquote>
<p><strong>TL;DR:</strong> GRPO (Group Relative Policy Optimization) replaces PPO’s learned value function with a simple insight: 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> outputs per prompt and use their normalized relative rewards as advantages. This eliminates ~33% of memory overhead while maintaining (or exceeding) PPO’s performance. The algorithm powers DeepSeekMath’s 51.7% MATH benchmark score and later became central to DeepSeek-R1’s remarkable reasoning capabilities.</p>
</blockquote>
<p><strong>Reading time:</strong> ~32 minutes</p>
<p><strong>Prerequisites:</strong> <a href="https://claude.ai/blog/ppo-deep-dive/">Part 2: PPO for Language Models</a> covers the clipped objective and GAE that GRPO builds upon.</p>
<hr>
<h2 id="the-memory-problem-revisited">The Memory Problem Revisited</h2>
<p>In Part 2, we saw PPO’s four-model architecture:</p>



































<table><thead><tr><th>Model</th><th>Purpose</th><th>Memory (7B)</th></tr></thead><tbody><tr><td>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.03588em;">π</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.02778em;">θ</span></span></span></span><span 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></td><td>Generate responses</td><td>~28 GB</td></tr><tr><td>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.03588em;">π</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></td><td>KL anchor</td><td>~14 GB</td></tr><tr><td><strong>Value <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.22222em;">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.03588em;">ψ</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></strong></td><td><strong>Advantage estimation</strong></td><td><strong>~28 GB</strong></td></tr><tr><td>Reward <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.02778em;">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></td><td>Score responses</td><td>~14 GB</td></tr><tr><td><strong>Total</strong></td><td></td><td><strong>~84 GB</strong></td></tr></tbody></table>
<p>The value network accounts for one-third of memory, and it’s there for a single purpose: estimating advantages via GAE.</p>
<p><em><strong>But what if we didn’t need to learn <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.22222em;">V</span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mclose">)</span></span></span></span>? What if we could estimate advantages directly from samples?</strong></em></p>
<p>This is GRPO’s insight: instead of learning “how good is this state?”, just sample multiple completions for the same prompt and compare them. The mean reward becomes your baseline. No neural network required.</p>
<hr>
<h2 id="table-of-contents">Table of Contents</h2>
<ol>
<li><a href="https://claude.ai/chat/6bf87a3b-a3f3-419c-a9c9-5ecac5ac1df0#the-core-insight-group-relative-advantages">The Core Insight: Group-Relative Advantages</a></li>
<li><a href="https://claude.ai/chat/6bf87a3b-a3f3-419c-a9c9-5ecac5ac1df0#mathematical-formulation">Mathematical Formulation</a></li>
<li><a href="https://claude.ai/chat/6bf87a3b-a3f3-419c-a9c9-5ecac5ac1df0#why-this-works">Why This Works</a></li>
<li><a href="https://claude.ai/chat/6bf87a3b-a3f3-419c-a9c9-5ecac5ac1df0#outcome-vs-process-supervision">Outcome vs. Process Supervision</a></li>
<li><a href="https://claude.ai/chat/6bf87a3b-a3f3-419c-a9c9-5ecac5ac1df0#decoupled-kl-regularization">Decoupled KL Regularization</a></li>
<li><a href="https://claude.ai/chat/6bf87a3b-a3f3-419c-a9c9-5ecac5ac1df0#iterative-grpo">Iterative GRPO</a></li>
<li><a href="https://claude.ai/chat/6bf87a3b-a3f3-419c-a9c9-5ecac5ac1df0#pytorch-implementation">PyTorch Implementation</a></li>
<li><a href="https://claude.ai/chat/6bf87a3b-a3f3-419c-a9c9-5ecac5ac1df0#deepseek-r1-grpo-at-scale">DeepSeek-R1: GRPO at Scale</a></li>
<li><a href="https://claude.ai/chat/6bf87a3b-a3f3-419c-a9c9-5ecac5ac1df0#a-unified-view-grpo-ppo-dpo-and-rft">A Unified View: GRPO, PPO, DPO, and RFT</a></li>
<li><a href="https://claude.ai/chat/6bf87a3b-a3f3-419c-a9c9-5ecac5ac1df0#limitations-and-when-to-use-alternatives">Limitations and When to Use Alternatives</a></li>
<li><a href="https://claude.ai/chat/6bf87a3b-a3f3-419c-a9c9-5ecac5ac1df0#key-takeaways">Key Takeaways</a></li>
</ol>
<hr>
<h2 id="the-core-insight-group-relative-advantages">The Core Insight: Group-Relative Advantages</h2>
<h3 id="the-algorithm">The Algorithm</h3>
<p>For each prompt <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.03588em;">q</span></span></span></span>, GRPO:</p>
<ol>
<li><strong>Sample a group</strong> of <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> outputs <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>o</mi><mn>1</mn></msub><mo separator="true">,</mo><msub><mi>o</mi><mn>2</mn></msub><mo separator="true">,</mo><mo>…</mo><mo separator="true">,</mo><msub><mi>o</mi><mi>G</mi></msub></mrow><annotation encoding="application/x-tex">{o_1, o_2, \ldots, o_G}</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"><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.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">o</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="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">o</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 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></span></span></span> from the current policy</li>
<li><strong>Score each output</strong> with 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><mn>1</mn></msub><mo separator="true">,</mo><msub><mi>r</mi><mn>2</mn></msub><mo separator="true">,</mo><mo>…</mo><mo separator="true">,</mo><msub><mi>r</mi><mi>G</mi></msub></mrow><annotation encoding="application/x-tex">{r_1, r_2, \ldots, r_G}</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"><span class="mord mathnormal" style="margin-right:0.02778em;">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="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">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">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="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.02778em;">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></span></span></span></li>
<li><strong>Normalize rewards</strong> using group statistics</li>
<li><strong>Use normalized rewards</strong> as advantages for all tokens in each output</li>
</ol>
<p>The advantage for output <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>o</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">o_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">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></span></span> is:</p>
<p><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><mo>=</mo><mfrac><mrow><msub><mi>r</mi><mi>i</mi></msub><mo>−</mo><mtext>mean</mtext><mo stretchy="false">(</mo><mi mathvariant="bold">r</mi><mo stretchy="false">)</mo></mrow><mrow><mtext>std</mtext><mo stretchy="false">(</mo><mi mathvariant="bold">r</mi><mo stretchy="false">)</mo></mrow></mfrac></mrow><annotation encoding="application/x-tex">\hat{A}_i = \frac{r_i - \text{mean}(\mathbf{r})}{\text{std}(\mathbf{r})}</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:1.53em;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: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 text mtight"><span class="mord mtight">std</span></span><span class="mopen mtight">(</span><span class="mord mathbf mtight">r</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.02778em;">r</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.0278em;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 text mtight"><span class="mord mtight">mean</span></span><span class="mopen mtight">(</span><span class="mord mathbf mtight">r</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></p>
<p>where <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="bold">r</mi><mo>=</mo><mrow><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></mrow></mrow><annotation encoding="application/x-tex">\mathbf{r} = {r_1, \ldots, r_G}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4444em;"></span><span class="mord mathbf">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.625em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">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.02778em;">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></span></span></span> are rewards for all outputs in the group.</p>
<p>That’s it. No learned value function. No GAE computation. Just sample, score, normalize.</p>
<h3 id="visual-comparison">Visual Comparison</h3>
<div class="mermaid">
graph TB
    subgraph PPO["&#x3C;b>PPO: Learned Value Function&#x3C;/b>"]
        direction TB
        P_prompt["Prompt q"] --> P_policy["Policy π_θ"]
        P_policy --> P_output["Output o"]
        P_output --> P_reward["Reward r"]
        
        P_output --> P_value["&#x3C;b>Value Network V_ψ&#x3C;/b>&#x3C;br/>━━━━━━━━━━&#x3C;br/>Same size as policy&#x3C;br/>Learns V(s) for all states&#x3C;br/>&#x3C;i>~28 GB memory&#x3C;/i>"]
        
        P_value --> P_gae["GAE Computation&#x3C;br/>━━━━━━━━━━&#x3C;br/>δ_t = r + γV(s') − V(s)&#x3C;br/>Â = Σ (γλ)^k δ_{t+k}"]
        P_reward --> P_gae
        P_gae --> P_adv["Advantage Â_t&#x3C;br/>&#x3C;i>per token&#x3C;/i>"]
    end
    
    subgraph GRPO["&#x3C;b>GRPO: Group Statistics&#x3C;/b>"]
        direction TB
        G_prompt["Prompt q"] --> G_policy["Policy π_θ"]
        G_policy --> G_o1["o₁ → r₁"]
        G_policy --> G_o2["o₂ → r₂"]
        G_policy --> G_dots["⋮"]
        G_policy --> G_oG["o_G → r_G"]
        
        G_o1 --> G_norm["&#x3C;b>Normalize&#x3C;/b>&#x3C;br/>━━━━━━━━━━&#x3C;br/>Â_i = (r_i − μ) / σ&#x3C;br/>No neural network&#x3C;br/>&#x3C;i>0 GB memory&#x3C;/i>"]
        G_o2 --> G_norm
        G_dots --> G_norm
        G_oG --> G_norm
        
        G_norm --> G_adv["Advantage Â_i&#x3C;br/>&#x3C;i>per output&#x3C;/i>"]
    end
    
    style P_value stroke:#ef4444,stroke-width:2.5px
    style P_gae stroke:#f59e0b,stroke-width:2px
    style G_norm stroke:#10b981,stroke-width:2.5px
    
    style PPO stroke:#ef4444
    style GRPO stroke:#10b981</div>
<h3 id="memory-savings">Memory Savings</h3>



































<table><thead><tr><th>Component</th><th>PPO</th><th>GRPO</th></tr></thead><tbody><tr><td>Policy (training)</td><td>~28 GB</td><td>~28 GB</td></tr><tr><td>Reference (frozen)</td><td>~14 GB</td><td>~14 GB</td></tr><tr><td><strong>Value (training)</strong></td><td><strong>~28 GB</strong></td><td><strong>0 GB</strong></td></tr><tr><td>Reward (inference)</td><td>~14 GB</td><td>~14 GB</td></tr><tr><td><strong>Total</strong></td><td><strong>~84 GB</strong></td><td><strong>~56 GB</strong></td></tr></tbody></table>
<p>A 33% reduction in model memory. For large models, this often makes the difference between fitting on available hardware and needing more GPUs.</p>
<blockquote>
<p><strong>Key Insight:</strong> GRPO trades compute (sampling <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> outputs per prompt) for memory (no value network). For LLMs where memory is the bottleneck, this is an excellent trade.</p>
</blockquote>
<hr>
<h2 id="mathematical-formulation">Mathematical Formulation</h2>
<p>Let’s formalize GRPO precisely.</p>
<h3 id="the-grpo-objective">The GRPO Objective</h3>
<p>GRPO maximizes:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="script">J</mi><mo>∗</mo><mtext>GRPO</mtext><mo stretchy="false">(</mo><mi>θ</mi><mo stretchy="false">)</mo><mo>=</mo><mi mathvariant="double-struck">E</mi><mo>∗</mo><mrow><mi>q</mi><mo>∼</mo><mi>P</mi><mo stretchy="false">(</mo><mi>Q</mi><mo stretchy="false">)</mo><mo separator="true">,</mo><mo separator="true">,</mo><msub><mi>o</mi><mi>i</mi></msub><mo>∗</mo><msup><mrow><mi>i</mi><mo>=</mo><mn>1</mn></mrow><mi>G</mi></msup><mo>∼</mo><mi>π</mi><mo>∗</mo><msub><mi>θ</mi><mtext>old</mtext></msub></mrow><mrow><mo fence="true">[</mo><mfrac><mn>1</mn><mi>G</mi></mfrac><msubsup><mo>∑</mo><mrow><mi>i</mi><mo>=</mo><mn>1</mn></mrow><mi>G</mi></msubsup><mfrac><mn>1</mn><mrow><mi mathvariant="normal">∣</mi><msub><mi>o</mi><mi>i</mi></msub><mi mathvariant="normal">∣</mi></mrow></mfrac><msubsup><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></msubsup><mi mathvariant="script">L</mi><mo>∗</mo><mrow><mi>i</mi><mo separator="true">,</mo><mi>t</mi></mrow><mo>−</mo><mi>β</mi><mo separator="true">,</mo><mi mathvariant="double-struck">D</mi><mo>∗</mo><mtext>KL</mtext><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><mo fence="true">]</mo></mrow></mrow><annotation encoding="application/x-tex">\mathcal{J}*{\text{GRPO}}(\theta) = \mathbb{E}*{q \sim P(Q), , {o_i}*{i=1}^G \sim \pi*{\theta_{\text{old}}}}\left[\frac{1}{G}\sum_{i=1}^{G} \frac{1}{|o_i|}\sum_{t=1}^{|o_i|} \mathcal{L}*{i,t} - \beta , \mathbb{D}*{\text{KL}}[\pi_\theta | \pi_{\text{ref}}]\right]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7805em;vertical-align:-0.0972em;"></span><span class="mord mathcal" style="margin-right:0.18472em;">J</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 text"><span class="mord">GRPO</span></span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.02778em;">θ</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.6889em;"></span><span class="mord mathbb">E</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.03588em;">q</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.13889em;">P</span><span class="mopen">(</span><span class="mord mathnormal">Q</span><span class="mclose">)</span><span class="mpunct">,,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><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><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 class="mord mathnormal">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 class="mord">1</span></span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8908em;"><span style="top:-3.1124em;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></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.03588em;">π</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 class="mord mathnormal" style="margin-right:0.02778em;">θ</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">old</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 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: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">G</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.1667em;"></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.9812em;"><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">i</span><span class="mrel mtight">=</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.2029em;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">G</span></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="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 class="mspace" style="margin-right:0.1667em;"></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:1.0279em;"><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">t</span><span class="mrel mtight">=</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.2029em;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 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:0.2997em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathcal">L</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">i</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">t</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.05278em;">β</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathbb">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"><span class="mord text"><span class="mord">KL</span></span></span><span class="mopen">[</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">π</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.02778em;">θ</span></span></span></span><span 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.03588em;">π</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 class="mclose delimcenter" style="top:0em;"><span class="delimsizing size2">]</span></span></span></span></span></span></p>
<h3 id="the-per-token-loss">The Per-Token Loss</h3>
<p>Like PPO, GRPO uses the clipped surrogate objective:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="script">L</mi><mo>∗</mo><mrow><mi>i</mi><mo separator="true">,</mo><mi>t</mi></mrow><mo>=</mo><mi>min</mi><mo>⁡</mo><mrow><mo fence="true">[</mo><mi>ρ</mi><mo>∗</mo><mrow><mi>i</mi><mo separator="true">,</mo><mi>t</mi></mrow><mo separator="true">,</mo><mover accent="true"><mi>A</mi><mo>^</mo></mover><mo>∗</mo><mrow><mi>i</mi><mo separator="true">,</mo><mi>t</mi></mrow><mo separator="true">,</mo><mo separator="true">;</mo><mtext>clip</mtext><mo stretchy="false">(</mo><mi>ρ</mi><mo>∗</mo><mrow><mi>i</mi><mo separator="true">,</mo><mi>t</mi></mrow><mo separator="true">,</mo><mn>1</mn><mo>−</mo><mi>ε</mi><mo separator="true">,</mo><mn>1</mn><mo>+</mo><mi>ε</mi><mo stretchy="false">)</mo><mo>⋅</mo><msub><mover accent="true"><mi>A</mi><mo>^</mo></mover><mrow><mi>i</mi><mo separator="true">,</mo><mi>t</mi></mrow></msub><mo fence="true">]</mo></mrow></mrow><annotation encoding="application/x-tex">\mathcal{L}*{i,t} = \min\left[\rho*{i,t} , \hat{A}*{i,t}, ; \text{clip}(\rho*{i,t}, 1-\varepsilon, 1+\varepsilon) \cdot \hat{A}_{i,t}\right]</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">L</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.854em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal">i</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">t</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="mop">min</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 mathnormal">ρ</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">i</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">t</span></span><span class="mpunct">,</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.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="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">i</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">t</span></span><span class="mpunct">,;</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 mathnormal">ρ</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">i</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">t</span></span><span class="mpunct">,</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="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.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.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 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 delimcenter" style="top:0em;"><span class="delimsizing size2">]</span></span></span></span></span></span></p>
<p>where the importance ratio is:</p>
<p><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><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 separator="true">,</mo><mi mathvariant="normal">∣</mi><mo separator="true">,</mo><mi>q</mi><mo separator="true">,</mo><msub><mi>o</mi><mrow><mi>i</mi><mo separator="true">,</mo><mo>&#x3C;</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 separator="true">,</mo><mi mathvariant="normal">∣</mi><mo separator="true">,</mo><mi>q</mi><mo separator="true">,</mo><msub><mi>o</mi><mrow><mi>i</mi><mo separator="true">,</mo><mo>&#x3C;</mo><mi>t</mi></mrow></msub><mo stretchy="false">)</mo></mrow></mfrac></mrow><annotation encoding="application/x-tex">\rho_{i,t} = \frac{\pi_\theta(o_{i,t} ,|, q, o_{i,&#x3C;t})}{\pi_{\theta_{\text{old}}}(o_{i,t} ,|, q, o_{i,&#x3C;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:1.658em;vertical-align:-0.6257em;"></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.0323em;"><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.03588em;">π</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 mtight"><span class="mord mathnormal mtight" style="margin-right:0.02778em;">θ</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.3448em;margin-left:-0.0278em;margin-right:0.1em;"><span class="pstrut" style="height:2.6944em;"></span><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">old</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.3496em;"><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.401em;"><span></span></span></span></span></span></span><span class="mopen 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 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.2819em;"><span></span></span></span></span></span></span><span class="mpunct mtight">,</span><span class="mord mtight">∣</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">q</span><span class="mpunct 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 mtight"><span class="mord mathnormal mtight">i</span><span class="mpunct mtight">,</span><span class="mrel mtight">&#x3C;</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.2819em;"><span></span></span></span></span></span></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.5073em;"><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.03588em;">π</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.02778em;">θ</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">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 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.2819em;"><span></span></span></span></span></span></span><span class="mpunct mtight">,</span><span class="mord mtight">∣</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">q</span><span class="mpunct 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 mtight"><span class="mord mathnormal mtight">i</span><span class="mpunct mtight">,</span><span class="mrel mtight">&#x3C;</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.2819em;"><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.6257em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></p>
<p>The clipping mechanism is identical to PPO—what changes is how we compute <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><mrow><mi>i</mi><mo separator="true">,</mo><mi>t</mi></mrow></msub></mrow><annotation encoding="application/x-tex">\hat{A}_{i,t}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.2329em;vertical-align:-0.2861em;"></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 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></span></span>.</p>
<h3 id="group-relative-advantage-estimation">Group-Relative Advantage Estimation</h3>
<p>For <strong>outcome supervision</strong> (the standard case), all tokens in output <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>o</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">o_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">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></span></span> share the same advantage:</p>
<p><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><mrow><mi>i</mi><mo separator="true">,</mo><mi>t</mi></mrow></msub><mo>=</mo><msub><mover accent="true"><mi>r</mi><mo>~</mo></mover><mi>i</mi></msub><mo>=</mo><mfrac><mrow><msub><mi>r</mi><mi>i</mi></msub><mo>−</mo><mtext>mean</mtext><mo stretchy="false">(</mo><mi mathvariant="bold">r</mi><mo stretchy="false">)</mo></mrow><mrow><mtext>std</mtext><mo stretchy="false">(</mo><mi mathvariant="bold">r</mi><mo stretchy="false">)</mo></mrow></mfrac></mrow><annotation encoding="application/x-tex">\hat{A}_{i,t} = \tilde{r}_i = \frac{r_i - \text{mean}(\mathbf{r})}{\text{std}(\mathbf{r})}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.2329em;vertical-align:-0.2861em;"></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 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:0.8179em;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.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span></span><span style="top:-3.35em;"><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.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.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="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 text mtight"><span class="mord mtight">std</span></span><span class="mopen mtight">(</span><span class="mord mathbf mtight">r</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.02778em;">r</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.0278em;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 text mtight"><span class="mord mtight">mean</span></span><span class="mopen mtight">(</span><span class="mord mathbf mtight">r</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></p>
<p>where:</p>
<ul>
<li><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><mo>=</mo><msub><mi>r</mi><mi>ϕ</mi></msub><mo stretchy="false">(</mo><mi>q</mi><mo separator="true">,</mo><msub><mi>o</mi><mi>i</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">r_i = r_\phi(q, o_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.02778em;">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.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.0361em;vertical-align:-0.2861em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">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" style="margin-right:0.03588em;">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 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="mclose">)</span></span></span></span> is the reward for output <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>o</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">o_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">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></span></span></li>
<li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="bold">r</mi><mo>=</mo><mrow><msub><mi>r</mi><mn>1</mn></msub><mo separator="true">,</mo><msub><mi>r</mi><mn>2</mn></msub><mo separator="true">,</mo><mo>…</mo><mo separator="true">,</mo><msub><mi>r</mi><mi>G</mi></msub></mrow></mrow><annotation encoding="application/x-tex">\mathbf{r} = {r_1, r_2, \ldots, r_G}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4444em;"></span><span class="mord mathbf">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.625em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">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="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">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">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="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.02778em;">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></span></span></span> are rewards for all <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> outputs</li>
<li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mtext>mean</mtext><mo stretchy="false">(</mo><mi mathvariant="bold">r</mi><mo stretchy="false">)</mo><mo>=</mo><mfrac><mn>1</mn><mi>G</mi></mfrac><msubsup><mo>∑</mo><mrow><mi>i</mi><mo>=</mo><mn>1</mn></mrow><mi>G</mi></msubsup><msub><mi>r</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">\text{mean}(\mathbf{r}) = \frac{1}{G}\sum_{i=1}^G r_i</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">mean</span></span><span class="mopen">(</span><span class="mord mathbf">r</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.3262em;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">G</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.1667em;"></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.9812em;"><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">i</span><span class="mrel mtight">=</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.2029em;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.2997em;"><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.02778em;">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></li>
<li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mtext>std</mtext><mo stretchy="false">(</mo><mi mathvariant="bold">r</mi><mo stretchy="false">)</mo><mo>=</mo><msqrt><mrow><mfrac><mn>1</mn><mi>G</mi></mfrac><msubsup><mo>∑</mo><mrow><mi>i</mi><mo>=</mo><mn>1</mn></mrow><mi>G</mi></msubsup><mo stretchy="false">(</mo><msub><mi>r</mi><mi>i</mi></msub><mo>−</mo><mtext>mean</mtext><mo stretchy="false">(</mo><mi mathvariant="bold">r</mi><mo stretchy="false">)</mo><msup><mo stretchy="false">)</mo><mn>2</mn></msup></mrow></msqrt></mrow><annotation encoding="application/x-tex">\text{std}(\mathbf{r}) = \sqrt{\frac{1}{G}\sum_{i=1}^G (r_i - \text{mean}(\mathbf{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 text"><span class="mord">std</span></span><span class="mopen">(</span><span class="mord mathbf">r</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.84em;vertical-align:-0.5369em;"></span><span class="mord sqrt"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3031em;"><span class="svg-align" style="top:-3.8em;"><span class="pstrut" style="height:3.8em;"></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: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">G</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.1667em;"></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.9812em;"><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">i</span><span class="mrel mtight">=</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.2029em;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.2997em;"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">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="mord text"><span class="mord">mean</span></span><span class="mopen">(</span><span class="mord mathbf">r</span><span class="mclose">)</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.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.2631em;"><span class="pstrut" style="height:3.8em;"></span><span class="hide-tail" style="min-width:1.02em;height:1.88em;"><svg xmlns="http://www.w3.org/2000/svg" width="400em" height="1.88em" viewBox="0 0 400000 1944" preserveAspectRatio="xMinYMin slice"><path d="M983 90
l0 -0
c4,-6.7,10,-10,18,-10 H400000v40
H1013.1s-83.4,268,-264.1,840c-180.7,572,-277,876.3,-289,913c-4.7,4.7,-12.7,7,-24,7
s-12,0,-12,0c-1.3,-3.3,-3.7,-11.7,-7,-25c-35.3,-125.3,-106.7,-373.3,-214,-744
c-10,12,-21,25,-33,39s-32,39,-32,39c-6,-5.3,-15,-14,-27,-26s25,-30,25,-30
c26.7,-32.7,52,-63,76,-91s52,-60,52,-60s208,722,208,722
c56,-175.3,126.3,-397.3,211,-666c84.7,-268.7,153.8,-488.2,207.5,-658.5
c53.7,-170.3,84.5,-266.8,92.5,-289.5z
M1001 80h400000v40h-400000z"></path></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.5369em;"><span></span></span></span></span></span></span></span></span></li>
</ul>
<h3 id="properties-of-group-relative-advantages">Properties of Group-Relative Advantages</h3>
<p><strong>1. Zero-mean (approximately):</strong> Within each group, advantages sum to approximately zero: <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mo>∑</mo><mrow><mi>i</mi><mo>=</mo><mn>1</mn></mrow><mi>G</mi></msubsup><msub><mover accent="true"><mi>r</mi><mo>~</mo></mover><mi>i</mi></msub><mo>≈</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">\sum_{i=1}^G \tilde{r}_i \approx 0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.2809em;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.9812em;"><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">i</span><span class="mrel mtight">=</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.2029em;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.2997em;"><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.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span></span><span style="top:-3.35em;"><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.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.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>
<p>This provides balanced training signal: reinforcing good outputs requires suppressing worse ones.</p>
<p><strong>2. Unit variance:</strong> By dividing by std, advantages are normalized: <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mtext>Var</mtext><mo stretchy="false">(</mo><mover accent="true"><mi>r</mi><mo>~</mo></mover><mo stretchy="false">)</mo><mo>=</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">\text{Var}(\tilde{r}) = 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 text"><span class="mord">Var</span></span><span class="mopen">(</span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span></span><span style="top:-3.35em;"><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="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.6444em;"></span><span class="mord">1</span></span></span></span></p>
<p>This makes gradients comparable across prompts with different reward scales.</p>
<p><strong>3. Relative, not absolute:</strong> The advantage measures performance <em>relative to the group</em>, not absolute quality. An output with reward 0.5 could have positive advantage (if others scored 0.3) or negative (if others scored 0.7).</p>
<hr>
<h2 id="why-this-works">Why This Works</h2>
<p>The group-relative approach succeeds for several reasons:</p>
<h3 id="1-natural-baseline">1. Natural Baseline</h3>
<p>The mean reward <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mtext>mean</mtext><mo stretchy="false">(</mo><mi mathvariant="bold">r</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\text{mean}(\mathbf{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 text"><span class="mord">mean</span></span><span class="mopen">(</span><span class="mord mathbf">r</span><span class="mclose">)</span></span></span></span> estimates the expected reward under the current policy for this prompt:</p>
<p><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>∗</mo><mrow><mi>o</mi><mo>∼</mo><mi>π</mi><mo>∗</mo><mi>θ</mi><mo stretchy="false">(</mo><mi>O</mi><mi mathvariant="normal">∣</mi><mi>q</mi><mo stretchy="false">)</mo></mrow><mo stretchy="false">[</mo><mi>r</mi><mo stretchy="false">(</mo><mi>q</mi><mo separator="true">,</mo><mi>o</mi><mo stretchy="false">)</mo><mo stretchy="false">]</mo><mo>≈</mo><mfrac><mn>1</mn><mi>G</mi></mfrac><msubsup><mo>∑</mo><mrow><mi>i</mi><mo>=</mo><mn>1</mn></mrow><mi>G</mi></msubsup><msub><mi>r</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">\mathbb{E}*{o \sim \pi*\theta(O|q)}[r(q, o)] \approx \frac{1}{G}\sum_{i=1}^G r_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6889em;"></span><span class="mord mathbb">E</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 mathnormal">o</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.03588em;">π</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.02778em;">θ</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mord">∣</span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mclose">)</span></span><span class="mopen">[</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">o</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.3262em;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">G</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.1667em;"></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.9812em;"><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">i</span><span class="mrel mtight">=</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.2029em;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.2997em;"><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.02778em;">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></p>
<p>With <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi><mo>=</mo><mn>64</mn></mrow><annotation encoding="application/x-tex">G=64</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">64</span></span></span></span> samples (as in DeepSeekMath), this estimate is reasonably accurate. This is exactly what we want from a baseline.</p>
<h3 id="2-matched-to-reward-model-training">2. Matched to Reward Model Training</h3>
<p>Reward models are typically trained on <strong>comparisons</strong> between outputs for the same prompt:</p>
<ul>
<li>“Output A is better than output B for prompt q”</li>
</ul>
<p>GRPO’s group-relative advantages align with this comparative signal. We’re asking: “Among these <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> outputs, which are better?“</p>
<h3 id="3-automatic-scale-normalization">3. Automatic Scale Normalization</h3>
<p>Different prompts may have different “difficulty,” leading to different reward distributions:</p>
<ul>
<li>Easy prompt: All outputs score 0.8-0.9</li>
<li>Hard prompt: All outputs score 0.2-0.4</li>
</ul>
<p>Dividing by std normalizes these, preventing easy prompts from dominating gradients.</p>
<h3 id="4-no-bootstrapping-bias">4. No Bootstrapping Bias</h3>
<p>GAE uses <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><msub><mi>s</mi><mrow><mi>t</mi><mo>+</mo><mn>1</mn></mrow></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">V(s_{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 mathnormal" style="margin-right:0.22222em;">V</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></span></span> estimates, introducing bias if the value function is inaccurate. GRPO uses only observed rewards—no bootstrapping, no bias from learned estimates.</p>
<h3 id="5-simplicity">5. Simplicity</h3>
<p>The entire advantage computation is:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#E1E4E8">advantages </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> (rewards </span><span style="color:#F97583">-</span><span style="color:#E1E4E8"> rewards.mean()) </span><span style="color:#F97583">/</span><span style="color:#E1E4E8"> (rewards.std() </span><span style="color:#F97583">+</span><span style="color:#E1E4E8"> eps)</span></span>
<span class="line"></span></code></pre>
<p>No recursive GAE computation, no value function forward passes, no TD errors.</p>
<blockquote>
<p><strong>Key Insight:</strong> The group mean is a Monte Carlo estimate of <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><msub><mi>s</mi><mn>0</mn></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">V(s_0)</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.22222em;">V</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">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="mclose">)</span></span></span></span> for this prompt. By sampling enough outputs, we get a good baseline without learning anything.</p>
</blockquote>
<hr>
<h2 id="outcome-vs-process-supervision">Outcome vs. Process Supervision</h2>
<p>GRPO supports two supervision modes.</p>
<h3 id="outcome-supervision-default">Outcome Supervision (Default)</h3>
<p>A single reward at sequence end:</p>
<p><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><mrow><mi>i</mi><mo separator="true">,</mo><mi>t</mi></mrow></msub><mo>=</mo><msub><mover accent="true"><mi>r</mi><mo>~</mo></mover><mi>i</mi></msub><mspace width="1em"></mspace><mi mathvariant="normal">∀</mi><mo separator="true">,</mo><mi>t</mi><mo>∈</mo><mrow><mn>1</mn><mo separator="true">,</mo><mo>…</mo><mo separator="true">,</mo><mi mathvariant="normal">∣</mi><msub><mi>o</mi><mi>i</mi></msub><mi mathvariant="normal">∣</mi></mrow></mrow><annotation encoding="application/x-tex">\hat{A}_{i,t} = \tilde{r}_i \quad \forall , t \in {1, \ldots, |o_i|}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.2329em;vertical-align:-0.2861em;"></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 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:0.8889em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span></span><span style="top:-3.35em;"><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.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:1em;"></span><span class="mord">∀</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></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"><span class="mord">1</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><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></span></p>
<p>All tokens receive the same advantage—the normalized final reward.</p>
<p><strong>Pros:</strong></p>
<ul>
<li>Simple: Works with any output-level reward model</li>
<li>Stable: Single reward signal, no step-level noise</li>
</ul>
<p><strong>Cons:</strong></p>
<ul>
<li>Coarse credit assignment: Every token gets equal “blame” or “credit”</li>
</ul>
<h3 id="process-supervision">Process Supervision</h3>
<p>Rewards at each <strong>reasoning step</strong> (e.g., separated by newlines):</p>
<p>Let <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mtext>idx</mtext><mo stretchy="false">(</mo><mi>j</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\text{idx}(j)</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">idx</span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.05724em;">j</span><span class="mclose">)</span></span></span></span> be the token index at the end of step <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>j</mi></mrow><annotation encoding="application/x-tex">j</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.854em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.05724em;">j</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>K</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">K_i</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.07153em;">K</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.0715em;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 total steps in output <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>o</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">o_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">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></span></span>.</p>
<p><strong>Step 1: Normalize per-step rewards</strong></p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mover accent="true"><mi>r</mi><mo>~</mo></mover><mi>i</mi><mrow><mtext>idx</mtext><mo stretchy="false">(</mo><mi>j</mi><mo stretchy="false">)</mo></mrow></msubsup><mo>=</mo><mfrac><mrow><msubsup><mi>r</mi><mi>i</mi><mrow><mtext>idx</mtext><mo stretchy="false">(</mo><mi>j</mi><mo stretchy="false">)</mo></mrow></msubsup><mo>−</mo><mtext>mean</mtext><mo stretchy="false">(</mo><mi mathvariant="bold">R</mi><mo stretchy="false">)</mo></mrow><mrow><mtext>std</mtext><mo stretchy="false">(</mo><mi mathvariant="bold">R</mi><mo stretchy="false">)</mo></mrow></mfrac></mrow><annotation encoding="application/x-tex">\tilde{r}_i^{\text{idx}(j)} = \frac{r_i^{\text{idx}(j)} - \text{mean}(\mathbf{R})}{\text{std}(\mathbf{R})}</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 accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span></span><span style="top:-3.35em;"><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:1.0448em;"><span style="top:-2.4231em;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 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="mord text mtight"><span class="mord mtight">idx</span></span><span class="mopen mtight">(</span><span class="mord mathnormal mtight" style="margin-right:0.05724em;">j</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.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.797em;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:1.277em;"><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 text mtight"><span class="mord mtight">std</span></span><span class="mopen mtight">(</span><span class="mord mathbf mtight">R</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.5356em;"><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.02778em;">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.0591em;"><span style="top:-2.2134em;margin-left:-0.0278em;margin-right:0.0714em;"><span class="pstrut" style="height:2.5357em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mathnormal mtight">i</span></span></span><span style="top:-3.0591em;margin-right:0.0714em;"><span class="pstrut" style="height:2.5357em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">idx</span></span><span class="mopen mtight">(</span><span class="mord mathnormal mtight" style="margin-right:0.05724em;">j</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.3223em;"><span></span></span></span></span></span></span><span class="mbin mtight">−</span><span class="mord text mtight"><span class="mord mtight">mean</span></span><span class="mopen mtight">(</span><span class="mord mathbf mtight">R</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></p>
<p>where <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="bold">R</mi></mrow><annotation encoding="application/x-tex">\mathbf{R}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6861em;"></span><span class="mord mathbf">R</span></span></span></span> contains all step rewards across all outputs in the group.</p>
<p><strong>Step 2: Compute return-to-go</strong></p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>A</mi><mo>^</mo></mover><mo>∗</mo><mrow><mi>i</mi><mo separator="true">,</mo><mi>t</mi></mrow><mo>=</mo><mo>∑</mo><mo>∗</mo><mrow><mtext>idx</mtext><mo stretchy="false">(</mo><mi>j</mi><mo stretchy="false">)</mo><mo>≥</mo><mi>t</mi></mrow><msubsup><mover accent="true"><mi>r</mi><mo>~</mo></mover><mi>i</mi><mrow><mtext>idx</mtext><mo stretchy="false">(</mo><mi>j</mi><mo stretchy="false">)</mo></mrow></msubsup></mrow><annotation encoding="application/x-tex">\hat{A}*{i,t} = \sum*{\text{idx}(j) \geq t} \tilde{r}_i^{\text{idx}(j)}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9468em;"></span><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="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.854em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal">i</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">t</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.3217em;vertical-align:-0.2769em;"></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><span class="mord"><span class="mord text"><span class="mord">idx</span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.05724em;">j</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 class="mord mathnormal">t</span></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span></span><span style="top:-3.35em;"><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:1.0448em;"><span style="top:-2.4231em;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 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="mord text mtight"><span class="mord mtight">idx</span></span><span class="mopen mtight">(</span><span class="mord mathnormal mtight" style="margin-right:0.05724em;">j</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></p>
<p>The advantage at token <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> is the sum of normalized rewards from all subsequent steps.</p>
<div class="mermaid">
graph LR
    subgraph Process["&#x3C;b>Process Supervision&#x3C;/b>"]
        direction LR
        S1["Step 1&#x3C;br/>r̃₁ = 0.5&#x3C;br/>━━━━━&#x3C;br/>Â = 1.4"] --> S2["Step 2&#x3C;br/>r̃₂ = −0.2&#x3C;br/>━━━━━&#x3C;br/>Â = 0.9"]
        S2 --> S3["Step 3&#x3C;br/>r̃₃ = 0.8&#x3C;br/>━━━━━&#x3C;br/>Â = 1.1"]
        S3 --> S4["Step 4&#x3C;br/>r̃₄ = 0.3&#x3C;br/>━━━━━&#x3C;br/>Â = 0.3"]
    end
    
    subgraph Outcome["&#x3C;b>Outcome Supervision&#x3C;/b>"]
        direction LR
        T1["Step 1&#x3C;br/>━━━━━&#x3C;br/>Â = 0.7"] --> T2["Step 2&#x3C;br/>━━━━━&#x3C;br/>Â = 0.7"]
        T2 --> T3["Step 3&#x3C;br/>━━━━━&#x3C;br/>Â = 0.7"]
        T3 --> T4["Step 4&#x3C;br/>r = 0.7&#x3C;br/>━━━━━&#x3C;br/>Â = 0.7"]
    end
    
    style S1 stroke:#10b981
    style S2 stroke:#10b981
    style S3 stroke:#10b981
    style S4 stroke:#10b981
    
    style T1 stroke:#06b6d4
    style T2 stroke:#06b6d4
    style T3 stroke:#06b6d4
    style T4 stroke:#06b6d4
    
    style Process stroke:#10b981
    style Outcome stroke:#06b6d4</div>
<h3 id="which-to-choose">Which to Choose?</h3>

























<table><thead><tr><th>Situation</th><th>Recommendation</th></tr></thead><tbody><tr><td>Standard RLHF</td><td>Outcome supervision</td></tr><tr><td>Multi-step reasoning (math, code)</td><td>Process supervision if PRM available</td></tr><tr><td>No process reward model</td><td>Outcome supervision</td></tr><tr><td>Training stability concerns</td><td>Outcome supervision</td></tr></tbody></table>
<p>DeepSeekMath experiments show process supervision slightly outperforms outcome supervision for math, but the difference is modest. Unless you have a high-quality Process Reward Model (PRM), outcome supervision is the simpler default.</p>
<hr>
<h2 id="decoupled-kl-regularization">Decoupled KL Regularization</h2>
<p>GRPO handles KL penalty differently from standard PPO.</p>
<h3 id="ppo-kl-in-the-reward">PPO: KL in the Reward</h3>
<p>Standard PPO-RLHF adds KL to the per-token reward:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mi>r</mi><mi>t</mi><mtext>total</mtext></msubsup><mo>=</mo><msub><mi>r</mi><mi>t</mi></msub><mo>−</mo><mi>β</mi><mi>log</mi><mo>⁡</mo><mfrac><mrow><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>o</mi><mi>t</mi></msub><mi mathvariant="normal">∣</mi><mi>q</mi><mo separator="true">,</mo><msub><mi>o</mi><mrow><mo>&#x3C;</mo><mi>t</mi></mrow></msub><mo stretchy="false">)</mo></mrow><mrow><msub><mi>π</mi><mtext>ref</mtext></msub><mo stretchy="false">(</mo><msub><mi>o</mi><mi>t</mi></msub><mi mathvariant="normal">∣</mi><mi>q</mi><mo separator="true">,</mo><msub><mi>o</mi><mrow><mo>&#x3C;</mo><mi>t</mi></mrow></msub><mo stretchy="false">)</mo></mrow></mfrac></mrow><annotation encoding="application/x-tex">r_t^{\text{total}} = r_t - \beta \log\frac{\pi_\theta(o_t|q, o_{&#x3C;t})}{\pi_{\text{ref}}(o_t|q, o_{&#x3C;t})}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0961em;vertical-align:-0.247em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8491em;"><span style="top:-2.453em;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 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 text mtight"><span class="mord mtight">total</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><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.02778em;">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.53em;vertical-align:-0.52em;"></span><span class="mord mathnormal" style="margin-right:0.05278em;">β</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mop">lo<span style="margin-right:0.01389em;">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.03588em;">π</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">o</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2963em;"><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">t</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" style="margin-right:0.03588em;">q</span><span class="mpunct 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.2963em;"><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 mtight"><span class="mrel mtight">&#x3C;</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.1709em;"><span></span></span></span></span></span></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.03588em;">π</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.02778em;">θ</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">o</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2963em;"><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">t</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" style="margin-right:0.03588em;">q</span><span class="mpunct 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.2963em;"><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 mtight"><span class="mrel mtight">&#x3C;</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.1709em;"><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.52em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></p>
<p>This KL penalty flows into GAE computation, coupling reward signal with regularization.</p>
<h3 id="grpo-kl-in-the-loss">GRPO: KL in the Loss</h3>
<p>GRPO adds KL directly to the loss, <strong>decoupled</strong> from advantages:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="script">J</mi><mo>∗</mo><mtext>GRPO</mtext><mo>=</mo><msup><mi mathvariant="script">L</mi><mtext>CLIP</mtext></msup><mo>−</mo><mi>β</mi><mo separator="true">,</mo><mi mathvariant="double-struck">D</mi><mo>∗</mo><mtext>KL</mtext><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">\mathcal{J}*{\text{GRPO}} = \mathcal{L}^{\text{CLIP}} - \beta , \mathbb{D}*{\text{KL}}[\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.7805em;vertical-align:-0.0972em;"></span><span class="mord mathcal" style="margin-right:0.18472em;">J</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 class="mord text"><span class="mord">GRPO</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.9247em;vertical-align:-0.0833em;"></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.8413em;"><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 text mtight"><span class="mord mtight">CLIP</span></span></span></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.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.05278em;">β</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathbb">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"><span class="mord text"><span class="mord">KL</span></span></span><span class="mopen">[</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">π</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.02778em;">θ</span></span></span></span><span 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.03588em;">π</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></p>
<h3 id="the-schulman-kl-estimator">The Schulman KL Estimator</h3>
<p>GRPO uses an unbiased KL estimator:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="double-struck">D</mi><mo>∗</mo><mtext>KL</mtext><mo stretchy="false">[</mo><mi>π</mi><mo>∗</mo><mi>θ</mi><mi mathvariant="normal">∣</mi><msub><mi>π</mi><mtext>ref</mtext></msub><mo stretchy="false">]</mo><mo>=</mo><mfrac><mrow><msub><mi>π</mi><mtext>ref</mtext></msub><mo stretchy="false">(</mo><msub><mi>o</mi><mi>t</mi></msub><mi mathvariant="normal">∣</mi><mi>q</mi><mo separator="true">,</mo><msub><mi>o</mi><mrow><mo>&#x3C;</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><mi>t</mi></msub><mi mathvariant="normal">∣</mi><mi>q</mi><mo separator="true">,</mo><msub><mi>o</mi><mrow><mo>&#x3C;</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><mi>t</mi></msub><mi mathvariant="normal">∣</mi><mi>q</mi><mo separator="true">,</mo><msub><mi>o</mi><mrow><mo>&#x3C;</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><mi>t</mi></msub><mi mathvariant="normal">∣</mi><mi>q</mi><mo separator="true">,</mo><msub><mi>o</mi><mrow><mo>&#x3C;</mo><mi>t</mi></mrow></msub><mo stretchy="false">)</mo></mrow></mfrac><mo>−</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">\mathbb{D}*{\text{KL}}[\pi*\theta | \pi_{\text{ref}}] = \frac{\pi_{\text{ref}}(o_t|q, o_{&#x3C;t})}{\pi_\theta(o_t|q, o_{&#x3C;t})} - \log\frac{\pi_{\text{ref}}(o_t|q, o_{&#x3C;t})}{\pi_\theta(o_t|q, o_{&#x3C;t})} - 1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6889em;"></span><span class="mord mathbb">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"><span class="mord text"><span class="mord">KL</span></span></span><span class="mopen">[</span><span class="mord mathnormal" style="margin-right:0.03588em;">π</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.02778em;">θ</span><span class="mord">∣</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">π</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 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="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.03588em;">π</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.02778em;">θ</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">o</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2963em;"><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">t</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" style="margin-right:0.03588em;">q</span><span class="mpunct 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.2963em;"><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 mtight"><span class="mrel mtight">&#x3C;</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.1709em;"><span></span></span></span></span></span></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.03588em;">π</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">o</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2963em;"><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">t</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" style="margin-right:0.03588em;">q</span><span class="mpunct 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.2963em;"><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 mtight"><span class="mrel mtight">&#x3C;</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.1709em;"><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.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.01389em;">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.03588em;">π</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.02778em;">θ</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">o</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2963em;"><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">t</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" style="margin-right:0.03588em;">q</span><span class="mpunct 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.2963em;"><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 mtight"><span class="mrel mtight">&#x3C;</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.1709em;"><span></span></span></span></span></span></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.03588em;">π</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">o</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2963em;"><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">t</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" style="margin-right:0.03588em;">q</span><span class="mpunct 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.2963em;"><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 mtight"><span class="mrel mtight">&#x3C;</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.1709em;"><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.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:0.6444em;"></span><span class="mord">1</span></span></span></span></p>
<p>Properties:</p>
<ul>
<li><strong>Non-negative:</strong> Always <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>≥</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">\geq 0</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="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></li>
<li><strong>Zero at equality:</strong> Exactly 0 when <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>π</mi><mi>θ</mi></msub><mo>=</mo><msub><mi>π</mi><mtext>ref</mtext></msub></mrow><annotation encoding="application/x-tex">\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.5806em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">π</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.02778em;">θ</span></span></span></span><span 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" style="margin-right:0.03588em;">π</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>
<li><strong>Unbiased:</strong> Correct expectation under sampling</li>
</ul>
<h3 id="why-decouple">Why Decouple?</h3>
<p><strong>1. Cleaner advantage signal:</strong> Advantages reflect only task performance, not regularization.</p>
<p><strong>2. Independent tuning:</strong> KL coefficient <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.05278em;">β</span></span></span></span> doesn’t interact with advantage normalization.</p>
<p><strong>3. Simpler implementation:</strong> No need to track per-token KL during advantage computation.</p>
<blockquote>
<p><strong>Key Insight:</strong> Decoupling KL from rewards simplifies the algorithm. The policy gradient focuses on task performance; the KL term separately ensures we don’t drift too far from the reference.</p>
</blockquote>
<hr>
<h2 id="iterative-grpo">Iterative GRPO</h2>
<p>As training progresses, the reward model may become a poor discriminator for the improved policy’s outputs. Iterative GRPO addresses this.</p>
<h3 id="the-staleness-problem">The Staleness Problem</h3>
<ol>
<li><strong>Initially:</strong> Policy outputs vary in quality; reward model discriminates well</li>
<li><strong>After training:</strong> Policy consistently produces good outputs; reward model’s training distribution no longer matches</li>
<li><strong>Result:</strong> Degraded training signal</li>
</ol>
<h3 id="the-solution-co-evolve-policy-and-reward-model">The Solution: Co-evolve Policy and Reward Model</h3>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>Algorithm: Iterative GRPO</span></span>
<span class="line"><span></span></span>
<span class="line"><span>For iteration = 1, ..., I:</span></span>
<span class="line"><span>    </span></span>
<span class="line"><span>    ┌─────────────────────────────────────────────────────┐</span></span>
<span class="line"><span>    │ Phase 1: Reset Reference                            │</span></span>
<span class="line"><span>    └─────────────────────────────────────────────────────┘</span></span>
<span class="line"><span>    π_ref ← π_θ   (current policy becomes new reference)</span></span>
<span class="line"><span>    </span></span>
<span class="line"><span>    ┌─────────────────────────────────────────────────────┐</span></span>
<span class="line"><span>    │ Phase 2: Policy Training                            │</span></span>
<span class="line"><span>    └─────────────────────────────────────────────────────┘</span></span>
<span class="line"><span>    For step = 1, ..., M:</span></span>
<span class="line"><span>        Sample batch of prompts</span></span>
<span class="line"><span>        Sample G outputs per prompt from π_θ</span></span>
<span class="line"><span>        Compute rewards using r_φ</span></span>
<span class="line"><span>        Compute group-relative advantages</span></span>
<span class="line"><span>        Update π_θ using GRPO objective</span></span>
<span class="line"><span>    </span></span>
<span class="line"><span>    ┌─────────────────────────────────────────────────────┐</span></span>
<span class="line"><span>    │ Phase 3: Reward Model Update                        │</span></span>
<span class="line"><span>    └─────────────────────────────────────────────────────┘</span></span>
<span class="line"><span>    Generate comparison data from improved π_θ</span></span>
<span class="line"><span>    Continually train r_φ with:</span></span>
<span class="line"><span>        - 90% new data (from current policy)</span></span>
<span class="line"><span>        - 10% historical data (replay buffer)</span></span>
<span class="line"><span></span></span></code></pre>
<h3 id="key-design-choices">Key Design Choices</h3>
<p><strong>Reference reset:</strong> Each iteration starts fresh, allowing larger cumulative policy drift while constraining per-iteration change.</p>
<p><strong>Replay buffer:</strong> 10% historical data prevents the reward model from forgetting how to score earlier, easier outputs.</p>
<p><strong>Single GRPO update:</strong> DeepSeekMath uses one gradient step per sample batch (<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">\mu = 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">μ</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>), prioritizing fresh samples over reuse.</p>
<hr>
<h2 id="pytorch-implementation">PyTorch Implementation</h2>
<p>Here’s a complete, educational implementation of GRPO:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#9ECBFF">"""</span></span>
<span class="line"><span style="color:#9ECBFF">Group Relative Policy Optimization (GRPO)</span></span>
<span class="line"><span style="color:#9ECBFF">=========================================</span></span>
<span class="line"></span>
<span class="line"><span style="color:#9ECBFF">Memory-efficient alternative to PPO that eliminates the value network</span></span>
<span class="line"><span style="color:#9ECBFF">by computing advantages from groups of sampled outputs.</span></span>
<span class="line"></span>
<span class="line"><span style="color:#9ECBFF">Reference: DeepSeekMath (Shao et al., 2024)</span></span>
<span class="line"><span style="color:#9ECBFF">"""</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> torch</span></span>
<span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> torch.nn.functional </span><span style="color:#F97583">as</span><span style="color:#E1E4E8"> F</span></span>
<span class="line"><span style="color:#F97583">from</span><span style="color:#E1E4E8"> torch </span><span style="color:#F97583">import</span><span style="color:#E1E4E8"> Tensor</span></span>
<span class="line"><span style="color:#F97583">from</span><span style="color:#E1E4E8"> dataclasses </span><span style="color:#F97583">import</span><span style="color:#E1E4E8"> dataclass</span></span>
<span class="line"><span style="color:#F97583">from</span><span style="color:#E1E4E8"> typing </span><span style="color:#F97583">import</span><span style="color:#E1E4E8"> Tuple, List</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#B392F0">@dataclass</span></span>
<span class="line"><span style="color:#F97583">class</span><span style="color:#B392F0"> GRPOConfig</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#9ECBFF">    """GRPO hyperparameters."""</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Core settings</span></span>
<span class="line"><span style="color:#E1E4E8">    group_size: </span><span style="color:#79B8FF">int</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 64</span><span style="color:#6A737D">           # G: outputs per prompt</span></span>
<span class="line"><span style="color:#E1E4E8">    clip_epsilon: </span><span style="color:#79B8FF">float</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 0.2</span><span style="color:#6A737D">      # ε: PPO clipping</span></span>
<span class="line"><span style="color:#E1E4E8">    kl_coef: </span><span style="color:#79B8FF">float</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 0.04</span><span style="color:#6A737D">          # β: KL penalty coefficient</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Generation</span></span>
<span class="line"><span style="color:#E1E4E8">    max_length: </span><span style="color:#79B8FF">int</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 1024</span></span>
<span class="line"><span style="color:#E1E4E8">    temperature: </span><span style="color:#79B8FF">float</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 1.0</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Training</span></span>
<span class="line"><span style="color:#E1E4E8">    learning_rate: </span><span style="color:#79B8FF">float</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 1e-6</span></span>
<span class="line"><span style="color:#E1E4E8">    max_grad_norm: </span><span style="color:#79B8FF">float</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 1.0</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Numerical stability</span></span>
<span class="line"><span style="color:#E1E4E8">    eps: </span><span style="color:#79B8FF">float</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 1e-8</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">def</span><span style="color:#B392F0"> compute_group_advantages</span><span style="color:#E1E4E8">(rewards: Tensor, eps: </span><span style="color:#79B8FF">float</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 1e-8</span><span style="color:#E1E4E8">) -> Tensor:</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#9ECBFF">    Compute group-relative advantages.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    This is GRPO's key innovation: use group statistics</span></span>
<span class="line"><span style="color:#9ECBFF">    instead of a learned value function.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Args:</span></span>
<span class="line"><span style="color:#9ECBFF">        rewards: [G] rewards for each output in the group</span></span>
<span class="line"><span style="color:#9ECBFF">        eps: Small constant for numerical stability</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">    Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">        advantages: [G] normalized advantages</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#E1E4E8">    mean_r </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> rewards.mean()</span></span>
<span class="line"><span style="color:#E1E4E8">    std_r </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> rewards.std() </span><span style="color:#F97583">+</span><span style="color:#E1E4E8"> eps</span></span>
<span class="line"><span style="color:#E1E4E8">    advantages </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> (rewards </span><span style="color:#F97583">-</span><span style="color:#E1E4E8"> mean_r) </span><span style="color:#F97583">/</span><span style="color:#E1E4E8"> std_r</span></span>
<span class="line"><span style="color:#F97583">    return</span><span style="color:#E1E4E8"> advantages</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">def</span><span style="color:#B392F0"> compute_schulman_kl</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#E1E4E8">    policy_log_probs: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">    reference_log_probs: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">) -> Tensor:</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#9ECBFF">    Compute Schulman's unbiased KL estimator.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    D_KL = π_ref/π_θ - log(π_ref/π_θ) - 1</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    This estimator is:</span></span>
<span class="line"><span style="color:#9ECBFF">    - Non-negative (always >= 0)</span></span>
<span class="line"><span style="color:#9ECBFF">    - Unbiased (correct expectation)</span></span>
<span class="line"><span style="color:#9ECBFF">    - Zero when π_θ = π_ref</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Args:</span></span>
<span class="line"><span style="color:#9ECBFF">        policy_log_probs: [batch, seq_len] from π_θ</span></span>
<span class="line"><span style="color:#9ECBFF">        reference_log_probs: [batch, seq_len] from π_ref</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">    Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">        kl: [batch, seq_len] per-token KL</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#E1E4E8">    log_ratio </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> reference_log_probs </span><span style="color:#F97583">-</span><span style="color:#E1E4E8"> policy_log_probs  </span><span style="color:#6A737D"># log(π_ref/π_θ)</span></span>
<span class="line"><span style="color:#E1E4E8">    kl </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.exp(log_ratio) </span><span style="color:#F97583">-</span><span style="color:#E1E4E8"> log_ratio </span><span style="color:#F97583">-</span><span style="color:#79B8FF"> 1</span></span>
<span class="line"><span style="color:#F97583">    return</span><span style="color:#E1E4E8"> kl</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">def</span><span style="color:#B392F0"> compute_grpo_loss</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#E1E4E8">    policy_log_probs: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">    old_log_probs: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">    reference_log_probs: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">    advantages: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">    response_mask: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">    clip_epsilon: </span><span style="color:#79B8FF">float</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 0.2</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">    kl_coef: </span><span style="color:#79B8FF">float</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 0.04</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">) -> Tuple[Tensor, </span><span style="color:#79B8FF">dict</span><span style="color:#E1E4E8">]:</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#9ECBFF">    Compute GRPO loss with clipping and KL penalty.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Args:</span></span>
<span class="line"><span style="color:#9ECBFF">        policy_log_probs: [G, seq_len] current policy</span></span>
<span class="line"><span style="color:#9ECBFF">        old_log_probs: [G, seq_len] policy at sampling time</span></span>
<span class="line"><span style="color:#9ECBFF">        reference_log_probs: [G, seq_len] reference policy</span></span>
<span class="line"><span style="color:#9ECBFF">        advantages: [G] per-output advantages (will broadcast)</span></span>
<span class="line"><span style="color:#9ECBFF">        response_mask: [G, seq_len] valid token mask</span></span>
<span class="line"><span style="color:#9ECBFF">        clip_epsilon: PPO clipping parameter</span></span>
<span class="line"><span style="color:#9ECBFF">        kl_coef: KL penalty coefficient</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">    Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">        loss: Scalar loss to minimize</span></span>
<span class="line"><span style="color:#9ECBFF">        metrics: Dictionary of training metrics</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#E1E4E8">    G, seq_len </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> policy_log_probs.shape</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # =========================================</span></span>
<span class="line"><span style="color:#6A737D">    # 1. Importance ratio: ρ = π_θ / π_old</span></span>
<span class="line"><span style="color:#6A737D">    # =========================================</span></span>
<span class="line"><span style="color:#E1E4E8">    log_ratio </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> policy_log_probs </span><span style="color:#F97583">-</span><span style="color:#E1E4E8"> old_log_probs</span></span>
<span class="line"><span style="color:#E1E4E8">    ratio </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.exp(log_ratio)  </span><span style="color:#6A737D"># [G, seq_len]</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # =========================================</span></span>
<span class="line"><span style="color:#6A737D">    # 2. Expand advantages to token level</span></span>
<span class="line"><span style="color:#6A737D">    # =========================================</span></span>
<span class="line"><span style="color:#6A737D">    # [G] -> [G, seq_len]</span></span>
<span class="line"><span style="color:#E1E4E8">    token_advantages </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> advantages.unsqueeze(</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">).expand(</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">, seq_len)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # =========================================</span></span>
<span class="line"><span style="color:#6A737D">    # 3. PPO clipped objective</span></span>
<span class="line"><span style="color:#6A737D">    # =========================================</span></span>
<span class="line"><span style="color:#E1E4E8">    clipped_ratio </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.clamp(ratio, </span><span style="color:#79B8FF">1</span><span style="color:#F97583"> -</span><span style="color:#E1E4E8"> clip_epsilon, </span><span style="color:#79B8FF">1</span><span style="color:#F97583"> +</span><span style="color:#E1E4E8"> clip_epsilon)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#E1E4E8">    policy_loss_1 </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> ratio </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> token_advantages</span></span>
<span class="line"><span style="color:#E1E4E8">    policy_loss_2 </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> clipped_ratio </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> token_advantages</span></span>
<span class="line"><span style="color:#E1E4E8">    policy_loss </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.min(policy_loss_1, policy_loss_2)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # =========================================</span></span>
<span class="line"><span style="color:#6A737D">    # 4. KL penalty (decoupled from reward)</span></span>
<span class="line"><span style="color:#6A737D">    # =========================================</span></span>
<span class="line"><span style="color:#E1E4E8">    kl_penalty </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> compute_schulman_kl(policy_log_probs, reference_log_probs)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # =========================================</span></span>
<span class="line"><span style="color:#6A737D">    # 5. Combine losses</span></span>
<span class="line"><span style="color:#6A737D">    # =========================================</span></span>
<span class="line"><span style="color:#6A737D">    # Maximize policy objective, minimize KL</span></span>
<span class="line"><span style="color:#E1E4E8">    token_loss </span><span style="color:#F97583">=</span><span style="color:#F97583"> -</span><span style="color:#E1E4E8">policy_loss </span><span style="color:#F97583">+</span><span style="color:#E1E4E8"> kl_coef </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> kl_penalty</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Mask and average</span></span>
<span class="line"><span style="color:#E1E4E8">    masked_loss </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> token_loss </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> response_mask</span></span>
<span class="line"><span style="color:#E1E4E8">    per_output_loss </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> masked_loss.sum(</span><span style="color:#FFAB70">dim</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">) </span><span style="color:#F97583">/</span><span style="color:#E1E4E8"> response_mask.sum(</span><span style="color:#FFAB70">dim</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">).clamp(</span><span style="color:#FFAB70">min</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">    loss </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> per_output_loss.mean()</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # =========================================</span></span>
<span class="line"><span style="color:#6A737D">    # 6. Metrics</span></span>
<span class="line"><span style="color:#6A737D">    # =========================================</span></span>
<span class="line"><span style="color:#F97583">    with</span><span style="color:#E1E4E8"> torch.no_grad():</span></span>
<span class="line"><span style="color:#E1E4E8">        clip_fraction </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> ((ratio </span><span style="color:#F97583">-</span><span style="color:#79B8FF"> 1</span><span style="color:#E1E4E8">).abs() </span><span style="color:#F97583">></span><span style="color:#E1E4E8"> clip_epsilon).float()</span></span>
<span class="line"><span style="color:#E1E4E8">        clip_fraction </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> (clip_fraction </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> response_mask).sum() </span><span style="color:#F97583">/</span><span style="color:#E1E4E8"> response_mask.sum()</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#E1E4E8">        mean_kl </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> (kl_penalty </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> response_mask).sum() </span><span style="color:#F97583">/</span><span style="color:#E1E4E8"> response_mask.sum()</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#E1E4E8">    metrics </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#9ECBFF">        "loss"</span><span style="color:#E1E4E8">: loss.item(),</span></span>
<span class="line"><span style="color:#9ECBFF">        "clip_fraction"</span><span style="color:#E1E4E8">: clip_fraction.item(),</span></span>
<span class="line"><span style="color:#9ECBFF">        "mean_kl"</span><span style="color:#E1E4E8">: mean_kl.item(),</span></span>
<span class="line"><span style="color:#9ECBFF">        "mean_advantage"</span><span style="color:#E1E4E8">: advantages.mean().item(),</span></span>
<span class="line"><span style="color:#9ECBFF">        "advantage_std"</span><span style="color:#E1E4E8">: advantages.std().item(),</span></span>
<span class="line"><span style="color:#E1E4E8">    }</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    return</span><span style="color:#E1E4E8"> loss, metrics</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">class</span><span style="color:#B392F0"> GRPOTrainer</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#9ECBFF">    GRPO trainer for LLM fine-tuning.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Unlike PPO, requires only THREE models (no value network):</span></span>
<span class="line"><span style="color:#9ECBFF">    - policy_model: The LLM being trained</span></span>
<span class="line"><span style="color:#9ECBFF">    - reference_model: Frozen copy for KL penalty</span></span>
<span class="line"><span style="color:#9ECBFF">    - reward_model: Scores complete responses</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Memory savings: ~33% compared to PPO</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#79B8FF"> __init__</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#E1E4E8">        self,</span></span>
<span class="line"><span style="color:#E1E4E8">        policy_model: torch.nn.Module,</span></span>
<span class="line"><span style="color:#E1E4E8">        reference_model: torch.nn.Module,</span></span>
<span class="line"><span style="color:#E1E4E8">        reward_model,  </span><span style="color:#6A737D"># Can be a function or model</span></span>
<span class="line"><span style="color:#E1E4E8">        tokenizer,</span></span>
<span class="line"><span style="color:#E1E4E8">        config: GRPOConfig,</span></span>
<span class="line"><span style="color:#E1E4E8">    ):</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.policy </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> policy_model</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.reference </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> reference_model</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.reward_fn </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> reward_model</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.tokenizer </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> tokenizer</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.config </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> config</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Freeze reference model</span></span>
<span class="line"><span style="color:#F97583">        for</span><span style="color:#E1E4E8"> param </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.reference.parameters():</span></span>
<span class="line"><span style="color:#E1E4E8">            param.requires_grad </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> False</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Single optimizer (no value network!)</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.optimizer </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.optim.AdamW(</span></span>
<span class="line"><span style="color:#79B8FF">            self</span><span style="color:#E1E4E8">.policy.parameters(),</span></span>
<span class="line"><span style="color:#FFAB70">            lr</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">config.learning_rate,</span></span>
<span class="line"><span style="color:#FFAB70">            betas</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">0.9</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">0.95</span><span style="color:#E1E4E8">),</span></span>
<span class="line"><span style="color:#FFAB70">            weight_decay</span><span style="color:#F97583">=</span><span style="color:#79B8FF">0.1</span></span>
<span class="line"><span style="color:#E1E4E8">        )</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#B392F0">    @torch.no_grad</span><span style="color:#E1E4E8">()</span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> sample_group</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#E1E4E8">        self,</span></span>
<span class="line"><span style="color:#E1E4E8">        prompt_ids: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">        attention_mask: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">    ) -> Tuple[Tensor, Tensor]:</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Sample G outputs for a single prompt.</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">        Args:</span></span>
<span class="line"><span style="color:#9ECBFF">            prompt_ids: [1, prompt_len] token ids</span></span>
<span class="line"><span style="color:#9ECBFF">            attention_mask: [1, prompt_len]</span></span>
<span class="line"><span style="color:#9ECBFF">            </span></span>
<span class="line"><span style="color:#9ECBFF">        Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">            output_ids: [G, total_len] generated sequences</span></span>
<span class="line"><span style="color:#9ECBFF">            output_masks: [G, total_len] attention masks</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#E1E4E8">        G </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.config.group_size</span></span>
<span class="line"><span style="color:#E1E4E8">        device </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> prompt_ids.device</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Expand prompt for batch generation</span></span>
<span class="line"><span style="color:#E1E4E8">        prompt_ids </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> prompt_ids.expand(G, </span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        attention_mask </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> attention_mask.expand(G, </span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Sample from policy</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.policy.eval()</span></span>
<span class="line"><span style="color:#E1E4E8">        outputs </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.policy.generate(</span></span>
<span class="line"><span style="color:#FFAB70">            input_ids</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">prompt_ids,</span></span>
<span class="line"><span style="color:#FFAB70">            attention_mask</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">attention_mask,</span></span>
<span class="line"><span style="color:#FFAB70">            max_new_tokens</span><span style="color:#F97583">=</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.config.max_length,</span></span>
<span class="line"><span style="color:#FFAB70">            do_sample</span><span style="color:#F97583">=</span><span style="color:#79B8FF">True</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#FFAB70">            temperature</span><span style="color:#F97583">=</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.config.temperature,</span></span>
<span class="line"><span style="color:#FFAB70">            pad_token_id</span><span style="color:#F97583">=</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.tokenizer.pad_token_id,</span></span>
<span class="line"><span style="color:#FFAB70">            return_dict_in_generate</span><span style="color:#F97583">=</span><span style="color:#79B8FF">True</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">        )</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#E1E4E8">        output_ids </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> outputs.sequences</span></span>
<span class="line"><span style="color:#E1E4E8">        output_masks </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> (output_ids </span><span style="color:#F97583">!=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.tokenizer.pad_token_id).long()</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        return</span><span style="color:#E1E4E8"> output_ids, output_masks</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#B392F0">    @torch.no_grad</span><span style="color:#E1E4E8">()</span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> compute_rewards</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#E1E4E8">        self,</span></span>
<span class="line"><span style="color:#E1E4E8">        prompt_text: </span><span style="color:#79B8FF">str</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">        output_ids: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">        prompt_len: </span><span style="color:#79B8FF">int</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">    ) -> Tensor:</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Compute rewards for each output in the group.</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">        Args:</span></span>
<span class="line"><span style="color:#9ECBFF">            prompt_text: The original prompt string</span></span>
<span class="line"><span style="color:#9ECBFF">            output_ids: [G, total_len] generated sequences</span></span>
<span class="line"><span style="color:#9ECBFF">            prompt_len: Length of prompt in tokens</span></span>
<span class="line"><span style="color:#9ECBFF">            </span></span>
<span class="line"><span style="color:#9ECBFF">        Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">            rewards: [G] scalar reward per output</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#E1E4E8">        G </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> output_ids.shape[</span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">]</span></span>
<span class="line"><span style="color:#E1E4E8">        rewards </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> []</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        for</span><span style="color:#E1E4E8"> i </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> range</span><span style="color:#E1E4E8">(G):</span></span>
<span class="line"><span style="color:#6A737D">            # Extract response tokens</span></span>
<span class="line"><span style="color:#E1E4E8">            response_ids </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> output_ids[i, prompt_len:]</span></span>
<span class="line"><span style="color:#E1E4E8">            response_text </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.tokenizer.decode(</span></span>
<span class="line"><span style="color:#E1E4E8">                response_ids, </span><span style="color:#FFAB70">skip_special_tokens</span><span style="color:#F97583">=</span><span style="color:#79B8FF">True</span></span>
<span class="line"><span style="color:#E1E4E8">            )</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#6A737D">            # Score with reward model</span></span>
<span class="line"><span style="color:#E1E4E8">            reward </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.reward_fn(prompt_text, response_text)</span></span>
<span class="line"><span style="color:#E1E4E8">            rewards.append(reward)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        return</span><span style="color:#E1E4E8"> torch.tensor(rewards, </span><span style="color:#FFAB70">device</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">output_ids.device)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> compute_log_probs</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#E1E4E8">        self,</span></span>
<span class="line"><span style="color:#E1E4E8">        model: torch.nn.Module,</span></span>
<span class="line"><span style="color:#E1E4E8">        input_ids: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">        attention_mask: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">        response_start_idx: </span><span style="color:#79B8FF">int</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">    ) -> Tensor:</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Compute per-token log probabilities for response tokens.</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">        Args:</span></span>
<span class="line"><span style="color:#9ECBFF">            model: Language model</span></span>
<span class="line"><span style="color:#9ECBFF">            input_ids: [batch, seq_len] full sequences</span></span>
<span class="line"><span style="color:#9ECBFF">            attention_mask: [batch, seq_len]</span></span>
<span class="line"><span style="color:#9ECBFF">            response_start_idx: Where response tokens begin</span></span>
<span class="line"><span style="color:#9ECBFF">            </span></span>
<span class="line"><span style="color:#9ECBFF">        Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">            log_probs: [batch, response_len] per-token log probs</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#E1E4E8">        outputs </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> model(</span></span>
<span class="line"><span style="color:#FFAB70">            input_ids</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">input_ids,</span></span>
<span class="line"><span style="color:#FFAB70">            attention_mask</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">attention_mask,</span></span>
<span class="line"><span style="color:#FFAB70">            return_dict</span><span style="color:#F97583">=</span><span style="color:#79B8FF">True</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">        )</span></span>
<span class="line"><span style="color:#E1E4E8">        logits </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> outputs.logits  </span><span style="color:#6A737D"># [batch, seq_len, vocab]</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Shift for next-token prediction</span></span>
<span class="line"><span style="color:#E1E4E8">        shift_logits </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> logits[:, :</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">, :]</span></span>
<span class="line"><span style="color:#E1E4E8">        shift_labels </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> input_ids[:, </span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">:]</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Log probabilities</span></span>
<span class="line"><span style="color:#E1E4E8">        log_probs </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> F.log_softmax(shift_logits, </span><span style="color:#FFAB70">dim</span><span style="color:#F97583">=-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Gather log probs for actual tokens</span></span>
<span class="line"><span style="color:#E1E4E8">        token_log_probs </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> log_probs.gather(</span></span>
<span class="line"><span style="color:#FFAB70">            dim</span><span style="color:#F97583">=-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#FFAB70">            index</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">shift_labels.unsqueeze(</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        ).squeeze(</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Extract response tokens only</span></span>
<span class="line"><span style="color:#E1E4E8">        response_log_probs </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> token_log_probs[:, response_start_idx </span><span style="color:#F97583">-</span><span style="color:#79B8FF"> 1</span><span style="color:#E1E4E8">:]</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        return</span><span style="color:#E1E4E8"> response_log_probs</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> train_step</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#E1E4E8">        self,</span></span>
<span class="line"><span style="color:#E1E4E8">        prompt_text: </span><span style="color:#79B8FF">str</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">        prompt_ids: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">        attention_mask: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">    ) -> </span><span style="color:#79B8FF">dict</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Single GRPO training step.</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">        Args:</span></span>
<span class="line"><span style="color:#9ECBFF">            prompt_text: Original prompt string</span></span>
<span class="line"><span style="color:#9ECBFF">            prompt_ids: [1, prompt_len] tokenized prompt</span></span>
<span class="line"><span style="color:#9ECBFF">            attention_mask: [1, prompt_len]</span></span>
<span class="line"><span style="color:#9ECBFF">            </span></span>
<span class="line"><span style="color:#9ECBFF">        Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">            metrics: Training metrics dictionary</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#E1E4E8">        prompt_len </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> prompt_ids.shape[</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">]</span></span>
<span class="line"><span style="color:#E1E4E8">        device </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> prompt_ids.device</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # =============================================</span></span>
<span class="line"><span style="color:#6A737D">        # Step 1: Sample group of outputs</span></span>
<span class="line"><span style="color:#6A737D">        # =============================================</span></span>
<span class="line"><span style="color:#E1E4E8">        output_ids, output_masks </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.sample_group(prompt_ids, attention_mask)</span></span>
<span class="line"><span style="color:#E1E4E8">        G </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> output_ids.shape[</span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">]</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # =============================================</span></span>
<span class="line"><span style="color:#6A737D">        # Step 2: Compute rewards</span></span>
<span class="line"><span style="color:#6A737D">        # =============================================</span></span>
<span class="line"><span style="color:#E1E4E8">        rewards </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.compute_rewards(prompt_text, output_ids, prompt_len)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # =============================================</span></span>
<span class="line"><span style="color:#6A737D">        # Step 3: Compute group-relative advantages</span></span>
<span class="line"><span style="color:#6A737D">        # =============================================</span></span>
<span class="line"><span style="color:#E1E4E8">        advantages </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> compute_group_advantages(rewards, </span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.config.eps)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # =============================================</span></span>
<span class="line"><span style="color:#6A737D">        # Step 4: Get log probs (old = current pre-update)</span></span>
<span class="line"><span style="color:#6A737D">        # =============================================</span></span>
<span class="line"><span style="color:#F97583">        with</span><span style="color:#E1E4E8"> torch.no_grad():</span></span>
<span class="line"><span style="color:#E1E4E8">            old_log_probs </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.compute_log_probs(</span></span>
<span class="line"><span style="color:#79B8FF">                self</span><span style="color:#E1E4E8">.policy, output_ids, output_masks, prompt_len</span></span>
<span class="line"><span style="color:#E1E4E8">            )</span></span>
<span class="line"><span style="color:#E1E4E8">            reference_log_probs </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.compute_log_probs(</span></span>
<span class="line"><span style="color:#79B8FF">                self</span><span style="color:#E1E4E8">.reference, output_ids, output_masks, prompt_len</span></span>
<span class="line"><span style="color:#E1E4E8">            )</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # =============================================</span></span>
<span class="line"><span style="color:#6A737D">        # Step 5: Policy gradient update</span></span>
<span class="line"><span style="color:#6A737D">        # =============================================</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.policy.train()</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#E1E4E8">        policy_log_probs </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.compute_log_probs(</span></span>
<span class="line"><span style="color:#79B8FF">            self</span><span style="color:#E1E4E8">.policy, output_ids, output_masks, prompt_len</span></span>
<span class="line"><span style="color:#E1E4E8">        )</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Response mask</span></span>
<span class="line"><span style="color:#E1E4E8">        response_mask </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> output_masks[:, prompt_len:]</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#E1E4E8">        loss, metrics </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> compute_grpo_loss(</span></span>
<span class="line"><span style="color:#E1E4E8">            policy_log_probs,</span></span>
<span class="line"><span style="color:#E1E4E8">            old_log_probs,</span></span>
<span class="line"><span style="color:#E1E4E8">            reference_log_probs,</span></span>
<span class="line"><span style="color:#E1E4E8">            advantages,</span></span>
<span class="line"><span style="color:#E1E4E8">            response_mask,</span></span>
<span class="line"><span style="color:#79B8FF">            self</span><span style="color:#E1E4E8">.config.clip_epsilon,</span></span>
<span class="line"><span style="color:#79B8FF">            self</span><span style="color:#E1E4E8">.config.kl_coef,</span></span>
<span class="line"><span style="color:#E1E4E8">        )</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.optimizer.zero_grad()</span></span>
<span class="line"><span style="color:#E1E4E8">        loss.backward()</span></span>
<span class="line"><span style="color:#E1E4E8">        torch.nn.utils.clip_grad_norm_(</span></span>
<span class="line"><span style="color:#79B8FF">            self</span><span style="color:#E1E4E8">.policy.parameters(), </span></span>
<span class="line"><span style="color:#79B8FF">            self</span><span style="color:#E1E4E8">.config.max_grad_norm</span></span>
<span class="line"><span style="color:#E1E4E8">        )</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.optimizer.step()</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Add reward metrics</span></span>
<span class="line"><span style="color:#E1E4E8">        metrics[</span><span style="color:#9ECBFF">"mean_reward"</span><span style="color:#E1E4E8">] </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> rewards.mean().item()</span></span>
<span class="line"><span style="color:#E1E4E8">        metrics[</span><span style="color:#9ECBFF">"reward_std"</span><span style="color:#E1E4E8">] </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> rewards.std().item()</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        return</span><span style="color:#E1E4E8"> metrics</span></span>
<span class="line"></span></code></pre>
<h3 id="implementation-notes">Implementation Notes</h3>
<p><strong>Group size selection:</strong> DeepSeekMath uses <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi><mo>=</mo><mn>64</mn></mrow><annotation encoding="application/x-tex">G=64</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">64</span></span></span></span>. Larger groups give more stable advantage estimates. Reasonable range: 16-128.</p>
<p><strong>Single update per sample:</strong> The implementation does one gradient step per sampled batch. Multiple steps would require re-computing advantages as the policy changes.</p>
<p><strong>KL coefficient:</strong> Start with <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi><mo>=</mo><mn>0.04</mn></mrow><annotation encoding="application/x-tex">\beta = 0.04</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.05278em;">β</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.04</span></span></span></span>. Lower values allow more divergence; higher values constrain updates.</p>
<hr>
<h2 id="deepseek-r1-grpo-at-scale">DeepSeek-R1: GRPO at Scale</h2>
<p>In January 2025, DeepSeek released <strong>DeepSeek-R1</strong>, a reasoning model matching OpenAI’s o1 on many benchmarks. GRPO is central to its training.</p>
<h3 id="r1s-training-pipeline">R1’s Training Pipeline</h3>
<div class="mermaid">
graph TB
    subgraph R1Pipeline["&#x3C;b>DeepSeek-R1 Training Pipeline&#x3C;/b>"]
        direction TB
        
        Stage1["&#x3C;b>Stage 1: Cold Start&#x3C;/b>&#x3C;br/>━━━━━━━━━━&#x3C;br/>Small amount of long CoT data&#x3C;br/>Teaches extended reasoning format"]
        
        Stage2["&#x3C;b>Stage 2: Reasoning RL&#x3C;/b>&#x3C;br/>━━━━━━━━━━&#x3C;br/>GRPO with rule-based rewards&#x3C;br/>Math correctness, code execution&#x3C;br/>Model develops 'thinking' traces"]
        
        Stage3["&#x3C;b>Stage 3: Rejection Sampling + SFT&#x3C;/b>&#x3C;br/>━━━━━━━━━━&#x3C;br/>Sample from RL checkpoint&#x3C;br/>Human annotation for quality&#x3C;br/>SFT on curated reasoning data"]
        
        Stage4["&#x3C;b>Stage 4: Final RL&#x3C;/b>&#x3C;br/>━━━━━━━━━━&#x3C;br/>Broader GRPO training&#x3C;br/>Helpfulness + harmlessness&#x3C;br/>Multi-objective alignment"]
        
        Stage1 --> Stage2
        Stage2 --> Stage3
        Stage3 --> Stage4
    end
    
    style Stage1 stroke:#64748b,stroke-width:2px
    style Stage2 stroke:#10b981,stroke-width:2.5px
    style Stage3 stroke:#8b5cf6,stroke-width:2px
    style Stage4 stroke:#10b981,stroke-width:2.5px
    
    style R1Pipeline stroke:#334155,stroke-width:1px</div>
<h3 id="why-grpo-for-r1">Why GRPO for R1?</h3>
<p><strong>1. Long reasoning traces:</strong> R1 produces outputs with thousands of tokens. A value network would need to predict final reward from any point in this long sequence—extremely difficult.</p>
<p><strong>2. Verifiable rewards:</strong> For math and code, rewards are rule-based (correct/incorrect). No reward model uncertainty.</p>
<p><strong>3. Memory efficiency:</strong> At R1’s scale, GRPO’s memory savings are crucial.</p>
<p><strong>4. Training stability:</strong> Simpler architecture means fewer things can go wrong.</p>
<h3 id="the-aha-moment-phenomenon">The “Aha Moment” Phenomenon</h3>
<p>The R1 paper describes emergent behavior during GRPO training: the model spontaneously developed “aha moment” patterns—places where it reconsiders and corrects earlier reasoning mistakes.</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>Example reasoning trace:</span></span>
<span class="line"><span>"Let me solve this step by step...</span></span>
<span class="line"><span> First, I'll set up the equation: 2x + 5 = 13</span></span>
<span class="line"><span> Subtracting 5: 2x = 8</span></span>
<span class="line"><span> Wait, let me verify that. 13 - 5 = 8. Yes, correct.</span></span>
<span class="line"><span> Dividing by 2: x = 4</span></span>
<span class="line"><span> </span></span>
<span class="line"><span> Let me check: 2(4) + 5 = 8 + 5 = 13 ✓</span></span>
<span class="line"><span> </span></span>
<span class="line"><span> The answer is x = 4."</span></span>
<span class="line"><span></span></span></code></pre>
<p>This self-correction emerged without explicit supervision—GRPO’s optimization pressure (combined with correctness rewards) naturally discovered it.</p>
<h3 id="r1-zero-pure-rl-without-sft">R1-Zero: Pure RL Without SFT</h3>
<p>DeepSeek also released <strong>R1-Zero</strong>, trained with GRPO directly from the base model without any SFT stage. Remarkably, it still develops coherent reasoning, though with some readability issues that the full R1 pipeline addresses.</p>
<p>This demonstrates GRPO’s power: given only a correctness signal, the algorithm can induce sophisticated reasoning behavior.</p>
<blockquote>
<p><strong>Key Insight:</strong> GRPO’s simplicity makes it stable enough for extreme-scale training. DeepSeek-R1 shows that you can train world-class reasoning models without the complexity of PPO’s value network.</p>
</blockquote>
<hr>
<h2 id="a-unified-view-grpo-ppo-dpo-and-rft">A Unified View: GRPO, PPO, DPO, and RFT</h2>
<p>The DeepSeekMath paper provides a unified framework for understanding different alignment methods.</p>
<h3 id="the-general-policy-gradient">The General Policy Gradient</h3>
<p>All methods can be viewed through:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="normal">∇</mi><mi>θ</mi></msub><mi>J</mi><mo>=</mo><mi mathvariant="double-struck">E</mi><mo>∗</mo><mrow><mo stretchy="false">(</mo><mi>q</mi><mo separator="true">,</mo><mi>o</mi><mo stretchy="false">)</mo><mo>∼</mo><mi mathvariant="script">D</mi></mrow><mrow><mo fence="true">[</mo><msub><mo>∑</mo><mi>t</mi></msub><mi mathvariant="normal">∇</mi><mo>∗</mo><mi>θ</mi><mi>log</mi><mo>⁡</mo><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>o</mi><mi>t</mi></msub><mi mathvariant="normal">∣</mi><mi>q</mi><mo separator="true">,</mo><msub><mi>o</mi><mrow><mo>&#x3C;</mo><mi>t</mi></mrow></msub><mo stretchy="false">)</mo><mo>⋅</mo><mi>w</mi><mo stretchy="false">(</mo><mi>q</mi><mo separator="true">,</mo><mi>o</mi><mo stretchy="false">)</mo><mo fence="true">]</mo></mrow></mrow><annotation encoding="application/x-tex">\nabla_\theta J = \mathbb{E}*{(q, o) \sim \mathcal{D}}\left[\sum_t \nabla*\theta \log \pi_\theta(o_t|q, o_{&#x3C;t}) \cdot w(q, o)\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.02778em;">θ</span></span></span></span><span 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 mathnormal" style="margin-right:0.09618em;">J</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.6889em;"></span><span class="mord mathbb">E</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.0497em;vertical-align:-0.2997em;"></span><span class="mord"><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">o</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 class="mord mathcal" style="margin-right:0.02778em;">D</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner"><span class="mopen delimcenter" style="top:0em;">[</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.1308em;"><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">t</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><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.02778em;">θ</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mop">lo<span style="margin-right:0.01389em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">π</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.02778em;">θ</span></span></span></span><span 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.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><span class="mord mathnormal" style="margin-right:0.03588em;">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.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">&#x3C;</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.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">o</span><span class="mclose">)</span><span class="mclose delimcenter" style="top:0em;">]</span></span></span></span></span></p>
<p>Methods differ in:</p>
<ol>
<li><strong>Data source <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.02778em;">D</span></span></span></span>:</strong> Where do samples come from?</li>
<li><strong>Gradient coefficient <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>w</mi><mo stretchy="false">(</mo><mi>q</mi><mo separator="true">,</mo><mi>o</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">w(q, o)</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.02691em;">w</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">o</span><span class="mclose">)</span></span></span></span>:</strong> How are outputs weighted?</li>
</ol>
<h3 id="method-comparison">Method Comparison</h3>















































<table><thead><tr><th>Method</th><th>Data Source</th><th>Coefficient <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.4306em;"></span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span></span></span></span></th><th>Online?</th></tr></thead><tbody><tr><td><strong>SFT</strong></td><td>Human demos</td><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>w</mi><mo>=</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">w = 1</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.02691em;">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">1</span></span></span></span> (uniform)</td><td>❌</td></tr><tr><td><strong>RFT</strong></td><td>Filtered policy samples</td><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>w</mi><mo>=</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">w = 1</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.02691em;">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">1</span></span></span></span> (filtered)</td><td>❌</td></tr><tr><td><strong>DPO</strong></td><td>Preference pairs</td><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>w</mi><mo>=</mo><mo>−</mo><mi>β</mi><msup><mi>σ</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">(</mo><mo>⋅</mo><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">w = -\beta\sigma'(\cdot)</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.02691em;">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:1.0019em;vertical-align:-0.25em;"></span><span class="mord">−</span><span class="mord mathnormal" style="margin-right:0.05278em;">β</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">σ</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="mopen">(</span><span class="mord">⋅</span><span class="mclose">)</span></span></span></span></td><td>❌</td></tr><tr><td><strong>Online RFT</strong></td><td>Current policy</td><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>w</mi><mo>=</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">w = 1</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.02691em;">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">1</span></span></span></span> (filtered)</td><td>✅</td></tr><tr><td><strong>PPO</strong></td><td>Current policy</td><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>w</mi><mo>=</mo><msub><mi>A</mi><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">w = A_t</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.02691em;">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">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> (GAE)</td><td>✅</td></tr><tr><td><strong>GRPO</strong></td><td>Current policy</td><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>w</mi><mo>=</mo><mover accent="true"><mi>r</mi><mo>~</mo></mover></mrow><annotation encoding="application/x-tex">w = \tilde{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.02691em;">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.6679em;"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span></span><span style="top:-3.35em;"><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> (group-normalized)</td><td>✅</td></tr></tbody></table>
<h3 id="key-insights">Key Insights</h3>
<p><strong>Online vs. Offline:</strong> GRPO, PPO, and Online RFT sample from the current policy and can adapt. DPO and RFT use fixed datasets.</p>
<p><strong>Weighted vs. Filtered:</strong> RFT keeps only correct outputs (binary filter). GRPO keeps all outputs but weights by normalized reward (continuous weighting).</p>
<p><strong>Advantage estimation:</strong> PPO learns <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.22222em;">V</span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mclose">)</span></span></span></span>; GRPO uses group statistics; DPO implicitly computes advantages through preferences.</p>
<div class="mermaid">
graph LR
    subgraph Methods["&#x3C;b>Alignment Methods: A Unified View&#x3C;/b>"]
        direction LR
        
        SFT["&#x3C;b>SFT&#x3C;/b>&#x3C;br/>━━━━━&#x3C;br/>Offline&#x3C;br/>w = 1"] --> RFT["&#x3C;b>RFT&#x3C;/b>&#x3C;br/>━━━━━&#x3C;br/>Offline&#x3C;br/>w = 1 (filtered)"]
        RFT --> DPO["&#x3C;b>DPO&#x3C;/b>&#x3C;br/>━━━━━&#x3C;br/>Offline&#x3C;br/>w = −βσ'"]
        DPO --> PPO["&#x3C;b>PPO&#x3C;/b>&#x3C;br/>━━━━━&#x3C;br/>Online&#x3C;br/>w = GAE"]
        PPO --> GRPO["&#x3C;b>GRPO&#x3C;/b>&#x3C;br/>━━━━━&#x3C;br/>Online&#x3C;br/>w = (r−μ)/σ"]
    end
    
    style SFT stroke:#64748b,stroke-width:2px
    style RFT stroke:#64748b,stroke-width:2px
    style DPO stroke:#8b5cf6,stroke-width:2px
    style PPO stroke:#f59e0b,stroke-width:2px
    style GRPO stroke:#10b981,stroke-width:2.5px
    
    style Methods stroke:#334155,stroke-width:1px</div>
<hr>
<h2 id="limitations-and-when-to-use-alternatives">Limitations and When to Use Alternatives</h2>
<h3 id="grpo-limitations">GRPO Limitations</h3>
<p><strong>1. Sampling cost:</strong> <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> outputs per prompt (typically 64). More compute than PPO’s single sample.</p>
<p><strong>2. Advantage variance:</strong> With small groups, the mean is a noisy baseline. Need <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi><mo>≥</mo><mn>32</mn></mrow><annotation encoding="application/x-tex">G \geq 32</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8193em;vertical-align:-0.136em;"></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">32</span></span></span></span> for stability.</p>
<p><strong>3. Uniform token credit:</strong> All tokens get the same advantage. No fine-grained credit assignment without process supervision.</p>
<p><strong>4. Reward model dependency:</strong> Like all RL methods, susceptible to reward hacking if the reward model has exploits.</p>
<p><strong>5. No value function benefits:</strong> Can’t use value-guided search or intrinsic motivation bonuses.</p>
<h3 id="when-to-use-each-method">When to Use Each Method</h3>









































<table><thead><tr><th>Situation</th><th>Recommendation</th></tr></thead><tbody><tr><td>Memory-constrained</td><td>✅ GRPO</td></tr><tr><td>Compute-constrained</td><td>Consider PPO</td></tr><tr><td>Outcome rewards only</td><td>✅ GRPO</td></tr><tr><td>Dense per-step rewards</td><td>Consider PPO (GAE)</td></tr><tr><td>Offline preferences only</td><td>Use DPO</td></tr><tr><td>No reward model</td><td>Use DPO or RFT</td></tr><tr><td>Value-guided search needed</td><td>Use PPO</td></tr><tr><td>Multi-reward training</td><td>Use GDPO (Part 4)</td></tr></tbody></table>
<hr>
<h2 id="key-takeaways">Key Takeaways</h2>
<h3 id="the-core-innovation">The Core Innovation</h3>
<p><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><mo>=</mo><mfrac><mrow><msub><mi>r</mi><mi>i</mi></msub><mo>−</mo><mtext>mean</mtext><mo stretchy="false">(</mo><mi mathvariant="bold">r</mi><mo stretchy="false">)</mo></mrow><mrow><mtext>std</mtext><mo stretchy="false">(</mo><mi mathvariant="bold">r</mi><mo stretchy="false">)</mo></mrow></mfrac></mrow><annotation encoding="application/x-tex">\hat{A}_i = \frac{r_i - \text{mean}(\mathbf{r})}{\text{std}(\mathbf{r})}</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:1.53em;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: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 text mtight"><span class="mord mtight">std</span></span><span class="mopen mtight">(</span><span class="mord mathbf mtight">r</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.02778em;">r</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.0278em;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 text mtight"><span class="mord mtight">mean</span></span><span class="mopen mtight">(</span><span class="mord mathbf mtight">r</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></p>
<p>Sample multiple outputs, normalize rewards, use as advantages. No value network.</p>
<h3 id="memory-savings-1">Memory Savings</h3>
<ul>
<li>Eliminates ~28GB value network (for 7B model)</li>
<li>33% reduction in model memory</li>
<li>Often the difference between 1 GPU and multiple</li>
</ul>
<h3 id="design-choices">Design Choices</h3>
<ul>
<li><strong>Group size:</strong> <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi><mo>=</mo><mn>64</mn></mrow><annotation encoding="application/x-tex">G = 64</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">64</span></span></span></span> for stability</li>
<li><strong>Clip epsilon:</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.2</mn></mrow><annotation encoding="application/x-tex">\varepsilon = 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> (standard PPO)</li>
<li><strong>KL coefficient:</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.04</mn></mrow><annotation encoding="application/x-tex">\beta = 0.04</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.05278em;">β</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.04</span></span></span></span></li>
<li><strong>Decoupled KL:</strong> In loss, not reward</li>
</ul>
<h3 id="when-grpo-excels">When GRPO Excels</h3>
<ul>
<li>Memory-constrained training</li>
<li>Outcome-based rewards</li>
<li>Long generations (where value prediction is hard)</li>
<li>Verifiable rewards (math, code)</li>
</ul>
<h3 id="deepseek-r1-connection">DeepSeek-R1 Connection</h3>
<p>GRPO powers R1’s remarkable reasoning through:</p>
<ul>
<li>Rule-based correctness rewards</li>
<li>Emergent self-correction (“aha moments”)</li>
<li>Stable training at scale</li>
</ul>
<hr>
<h2 id="whats-next">What’s Next</h2>
<p>In <strong>Part 4: GDPO</strong>, we’ll see what happens when GRPO meets multi-reward settings:</p>
<ul>
<li>Why combined reward normalization causes “advantage collapse”</li>
<li>GDPO’s solution: decouple normalization per reward</li>
<li>Production patterns for tool calling, math, and coding</li>
</ul>
<hr>
<h2 id="further-reading">Further Reading</h2>
<p><strong>Original Papers:</strong></p>
<ul>
<li><a href="https://arxiv.org/abs/2402.03300">DeepSeekMath: Pushing the Limits of Mathematical Reasoning</a> (Shao et al., 2024)</li>
<li><a href="https://arxiv.org/abs/2501.12948">DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning</a> (DeepSeek-AI, 2025)</li>
</ul>
<p><strong>Related Work:</strong></p>
<ul>
<li><a href="https://arxiv.org/abs/1707.06347">Proximal Policy Optimization</a> (Schulman et al., 2017)</li>
<li><a href="https://arxiv.org/abs/2305.18290">Direct Preference Optimization</a> (Rafailov et al., 2023)</li>
</ul>
<p><strong>Implementations:</strong></p>
<ul>
<li><a href="https://github.com/huggingface/trl">TRL: Transformer Reinforcement Learning</a></li>
<li><a href="https://github.com/volcengine/verl">VERL</a></li>
<li><a href="https://github.com/OpenRLHF/OpenRLHF">OpenRLHF</a></li>
</ul>]]></content:encoded><enclosure url="https://www.vitorsousa.com/_astro/contextual-bandits-p3.DGVJ-AgP.png" length="756000" type="image/svg"/></item><item><title>Contextual Bandit Theory: Regret Bounds and Exploration</title><link>https://www.vitorsousa.com/blog/contextual-bandits-math/</link><guid isPermaLink="true">https://www.vitorsousa.com/blog/contextual-bandits-math/</guid><description>Understand the theory behind contextual bandits: regret bounds, the exploration-exploitation tradeoff, reward models, and why certain algorithms work. Math that directly informs practice.</description><pubDate>Sat, 15 Nov 2025 10:00:00 GMT</pubDate><content:encoded><![CDATA[<h1 id="neural-contextual-bandits-for-high-dimensional-data">Neural Contextual Bandits for High-Dimensional Data</h1>
<p><strong>Part 4 of 5:</strong> When Linear Models Aren’t Enough</p>
<blockquote>
<p><strong>TL;DR:</strong> Neural contextual bandits handle high-dimensional contexts (images, text) and complex nonlinear reward functions that linear models can’t capture. The key challenge is uncertainty quantification—neural networks don’t naturally provide confidence bounds. We solve this with bootstrap ensembles (approximating posteriors) and hybrid approaches like NeuralLinear (neural features + LinUCB). This post provides complete implementations and guidance on when the complexity is worth it.</p>
</blockquote>
<p><strong>Reading time:</strong> ~22 minutes</p>
<hr>
<h2 id="introduction-when-linear-models-break">Introduction: When Linear Models Break</h2>
<p>Parts 1-3 covered the foundations and core algorithms. LinUCB and Linear Thompson Sampling work great when rewards are approximately linear in features:</p>
<p><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>a</mi><mo stretchy="false">)</mo><mo>=</mo><msubsup><mi>θ</mi><mi>a</mi><mi>T</mi></msubsup><mi>x</mi><mo>+</mo><mi>ϵ</mi></mrow><annotation encoding="application/x-tex">r(x, a) = \theta_a^T x + \epsilon</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.02778em;">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">a</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.0883em;vertical-align:-0.247em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8413em;"><span style="top:-2.453em;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">a</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 mathnormal mtight" style="margin-right:0.13889em;">T</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></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.4306em;"></span><span class="mord mathnormal">ϵ</span></span></span></span></p>
<p>But what if:</p>
<ul>
<li><strong>Rewards are highly nonlinear?</strong> Complex interactions between features that linear models can’t capture</li>
<li><strong>Contexts are high-dimensional raw inputs?</strong> Images (100k+ pixels), text (embedding dimensions), audio</li>
<li><strong>You have complex feature interactions?</strong> Product of features, higher-order terms</li>
</ul>
<p><strong>Linear models fail.</strong> You need neural networks.</p>
<p>But neural networks introduce a critical challenge: <strong>how do we explore?</strong></p>
<ul>
<li>LinUCB uses confidence bounds: <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msqrt><mrow><msup><mi>x</mi><mi>T</mi></msup><msup><mi>A</mi><mrow><mo>−</mo><mn>1</mn></mrow></msup><mi>x</mi></mrow></msqrt></mrow><annotation encoding="application/x-tex">\sqrt{x^T A^{-1} x}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.04em;vertical-align:-0.0713em;"></span><span class="mord sqrt"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.9687em;"><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"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7673em;"><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 mathnormal mtight" style="margin-right:0.13889em;">T</span></span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal">A</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"><span class="mord mtight">−</span><span class="mord mtight">1</span></span></span></span></span></span></span></span></span><span class="mord mathnormal">x</span></span></span><span style="top:-2.9287em;"><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.0713em;"><span></span></span></span></span></span></span></span></span></li>
<li>Thompson Sampling uses posteriors: <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>θ</mi><mo>∼</mo><mi mathvariant="script">N</mi><mo stretchy="false">(</mo><mover accent="true"><mi>θ</mi><mo>^</mo></mover><mo separator="true">,</mo><mi mathvariant="normal">Σ</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\theta \sim \mathcal{N}(\hat{\theta}, \Sigma)</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.02778em;">θ</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.2079em;vertical-align:-0.25em;"></span><span class="mord mathcal" style="margin-right:0.14736em;">N</span><span class="mopen">(</span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9579em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">θ</span></span><span style="top:-3.2634em;"><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="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">Σ</span><span class="mclose">)</span></span></span></span></li>
<li>Neural networks give point predictions: <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>f</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">f_\theta(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"><span class="mord mathnormal" style="margin-right:0.10764em;">f</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.1076em;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.02778em;">θ</span></span></span></span><span 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></span></span> with no uncertainty</li>
</ul>
<p><strong>This post solves the uncertainty problem</strong> with three approaches:</p>
<ol>
<li><strong>Neural ε-greedy:</strong> Simplest baseline (random exploration)</li>
<li><strong>NeuralLinear:</strong> Neural features + LinUCB (best of both worlds)</li>
<li><strong>Bootstrap Thompson Sampling:</strong> Ensemble for uncertainty quantification</li>
</ol>
<p>Plus handling high-dimensional action spaces (thousands to millions of actions).</p>
<hr>
<h2 id="when-to-use-neural-contextual-bandits">When to Use Neural Contextual Bandits</h2>
<h3 id="decision-framework">Decision Framework</h3>
<div class="mermaid">
graph TD
    Start{Can linear model&#x3C;br/>work?}
    
    Start -->|Yes| Linear[Use LinUCB or&#x3C;br/>Linear Thompson&#x3C;br/>━━━━━━━━&#x3C;br/>Faster, interpretable&#x3C;br/>Lower sample complexity]
    
    Start -->|No| Q1{Why not?}
    
    Q1 -->|High-dim raw inputs&#x3C;br/>Images, text, audio| Neural[Use Neural Bandit&#x3C;br/>━━━━━━━━&#x3C;br/>Extract features first&#x3C;br/>Then apply bandit]
    
    Q1 -->|Nonlinear rewards&#x3C;br/>Complex interactions| Neural2[Use Neural Bandit&#x3C;br/>━━━━━━━━&#x3C;br/>or engineer features&#x3C;br/>for linear model]
    
    Q1 -->|Both| Neural3[Definitely Neural&#x3C;br/>━━━━━━━━&#x3C;br/>No other option]
    
    style Start stroke:#64748b,stroke-width:2.5px
    style Q1 stroke:#64748b,stroke-width:2px
    
    style Linear stroke:#10b981,stroke-width:2px
    style Neural stroke:#06b6d4,stroke-width:2px
    style Neural2 stroke:#06b6d4,stroke-width:2px
    style Neural3 stroke:#f59e0b,stroke-width:2.5px</div>
<h3 id="use-neural-bandits-when">Use Neural Bandits When:</h3>
<p><strong>✅ High-dimensional raw inputs:</strong></p>
<ul>
<li>Images (product photos, user-generated content)</li>
<li>Text (article content, user reviews)</li>
<li>Audio (podcast clips, voice commands)</li>
<li>Video (thumbnails, short clips)</li>
</ul>
<p><strong>✅ Complex nonlinear reward functions:</strong></p>
<ul>
<li>Deep feature interactions that linear models miss</li>
<li>Tried feature engineering, still underfitting</li>
<li>Domain experts say “relationships are definitely nonlinear”</li>
</ul>
<p><strong>✅ Sufficient training data:</strong></p>
<ul>
<li>Need 10-100x more data than linear models</li>
<li>Typically 10k+ interactions minimum</li>
<li>More for complex architectures</li>
</ul>
<h3 id="stick-with-linear-when">Stick with Linear When:</h3>
<p><strong>❌ You can engineer good features:</strong></p>
<ul>
<li>Domain knowledge suggests which features matter</li>
<li>Embeddings from pretrained models work well</li>
<li>Linear model achieves reasonable performance</li>
</ul>
<p><strong>❌ Limited training data:</strong></p>
<ul>
<li>&#x3C;1000 interactions per action</li>
<li>Linear models more sample-efficient</li>
</ul>
<p><strong>❌ Need interpretability:</strong></p>
<ul>
<li>Must explain why actions were chosen</li>
<li>Regulatory requirements for transparency</li>
</ul>
<p><strong>❌ Computational constraints:</strong></p>
<ul>
<li>Neural inference is 10-100x slower</li>
<li>Training is much more expensive</li>
</ul>
<hr>
<h2 id="the-uncertainty-problem-why-neural-bandits-are-hard">The Uncertainty Problem: Why Neural Bandits Are Hard</h2>
<h3 id="the-challenge">The Challenge</h3>
<p>Linear models give us <strong>uncertainty for free</strong>:</p>
<p><strong>LinUCB:</strong> Confidence ellipsoid <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msqrt><mrow><msup><mi>x</mi><mi>T</mi></msup><msup><mi>A</mi><mrow><mo>−</mo><mn>1</mn></mrow></msup><mi>x</mi></mrow></msqrt></mrow><annotation encoding="application/x-tex">\sqrt{x^T A^{-1} x}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.04em;vertical-align:-0.0713em;"></span><span class="mord sqrt"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.9687em;"><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"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7673em;"><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 mathnormal mtight" style="margin-right:0.13889em;">T</span></span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal">A</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"><span class="mord mtight">−</span><span class="mord mtight">1</span></span></span></span></span></span></span></span></span><span class="mord mathnormal">x</span></span></span><span style="top:-2.9287em;"><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.0713em;"><span></span></span></span></span></span></span></span></span> from covariance matrix</p>
<p><strong>Linear Thompson:</strong> Posterior <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>θ</mi><mo>∼</mo><mi mathvariant="script">N</mi><mo stretchy="false">(</mo><mover accent="true"><mi>θ</mi><mo>^</mo></mover><mo separator="true">,</mo><mi mathvariant="normal">Σ</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\theta \sim \mathcal{N}(\hat{\theta}, \Sigma)</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.02778em;">θ</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.2079em;vertical-align:-0.25em;"></span><span class="mord mathcal" style="margin-right:0.14736em;">N</span><span class="mopen">(</span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9579em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">θ</span></span><span style="top:-3.2634em;"><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="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">Σ</span><span class="mclose">)</span></span></span></span> from Bayesian updates</p>
<p><strong>Neural networks:</strong> Just a point estimate <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>f</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">f_\theta(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"><span class="mord mathnormal" style="margin-right:0.10764em;">f</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.1076em;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.02778em;">θ</span></span></span></span><span 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></span></span> with no uncertainty 😞</p>
<h3 id="why-uncertainty-matters">Why Uncertainty Matters</h3>
<p>Without uncertainty, we can’t explore intelligently:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#6A737D"># Bad: Neural network without uncertainty</span></span>
<span class="line"><span style="color:#E1E4E8">neural_net </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> train_neural_network(data)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">for</span><span style="color:#E1E4E8"> context </span><span style="color:#F97583">in</span><span style="color:#E1E4E8"> contexts:</span></span>
<span class="line"><span style="color:#6A737D">    # Get predictions for all actions</span></span>
<span class="line"><span style="color:#E1E4E8">    q_values </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> neural_net.predict(context)  </span><span style="color:#6A737D"># Just point estimates</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # No way to know which estimates are uncertain!</span></span>
<span class="line"><span style="color:#6A737D">    # Can only do pure exploitation (greedy) or random exploration (ε-greedy)</span></span>
<span class="line"><span style="color:#E1E4E8">    action </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> argmax(q_values)  </span><span style="color:#6A737D"># Greedy</span></span>
<span class="line"></span></code></pre>
<p><strong>Problem:</strong> Novel contexts (far from training data) get confident predictions that might be wrong. We need to know “I’m uncertain about action A in this context.”</p>
<h3 id="three-solutions">Three Solutions</h3>





























<table><thead><tr><th>Approach</th><th>How It Works</th><th>Pros</th><th>Cons</th></tr></thead><tbody><tr><td><strong>Neural ε-greedy</strong></td><td>Random exploration</td><td>Simple, works</td><td>Inefficient (wastes exploration)</td></tr><tr><td><strong>NeuralLinear</strong></td><td>Neural features + LinUCB</td><td>Directed exploration, interpretable</td><td>Assumes linear in learned features</td></tr><tr><td><strong>Bootstrap Ensemble</strong></td><td>Multiple networks, disagreement = uncertainty</td><td>Flexible, approximates Bayesian</td><td>Expensive (10x networks)</td></tr></tbody></table>
<p>Let’s implement all three.</p>
<hr>
<h2 id="neural-ε-greedy-the-simplest-baseline">Neural ε-greedy: The Simplest Baseline</h2>
<p>When you can’t quantify uncertainty, fall back to random exploration.</p>
<h3 id="architecture">Architecture</h3>
<div class="mermaid">
graph LR
    Context[Context&#x3C;br/>━━━━━━━━&#x3C;br/>High-dim input&#x3C;br/>Image, text, etc.] --> NN[Neural Network&#x3C;br/>━━━━━━━━&#x3C;br/>Dense layers&#x3C;br/>ReLU activations]
    
    NN --> Q[Q-values&#x3C;br/>━━━━━━━━&#x3C;br/>One per action&#x3C;br/>Point estimates]
    
    Q --> EG[ε-greedy&#x3C;br/>━━━━━━━━&#x3C;br/>Random with prob ε&#x3C;br/>Greedy with prob 1-ε]
    
    EG --> Action[Selected Action]
    
    style Context stroke:#10b981,stroke-width:2px
    style NN stroke:#06b6d4,stroke-width:2.5px
    style Q stroke:#f59e0b,stroke-width:2px
    style EG stroke:#8b5cf6,stroke-width:2px
    style Action stroke:#10b981,stroke-width:2px</div>
<h3 id="implementation">Implementation</h3>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> torch</span></span>
<span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> torch.nn </span><span style="color:#F97583">as</span><span style="color:#E1E4E8"> nn</span></span>
<span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> torch.optim </span><span style="color:#F97583">as</span><span style="color:#E1E4E8"> optim</span></span>
<span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> numpy </span><span style="color:#F97583">as</span><span style="color:#E1E4E8"> np</span></span>
<span class="line"><span style="color:#F97583">from</span><span style="color:#E1E4E8"> collections </span><span style="color:#F97583">import</span><span style="color:#E1E4E8"> deque</span></span>
<span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> random</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">class</span><span style="color:#B392F0"> NeuralEpsilonGreedy</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#9ECBFF">    Neural network with ε-greedy exploration.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Uses deep Q-learning: network predicts Q-value (expected reward)</span></span>
<span class="line"><span style="color:#9ECBFF">    for each action given context. Explores randomly with probability ε.</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#79B8FF"> __init__</span><span style="color:#E1E4E8">(self, n_features, n_actions, </span></span>
<span class="line"><span style="color:#E1E4E8">                 hidden_dims</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">[</span><span style="color:#79B8FF">256</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">128</span><span style="color:#E1E4E8">], epsilon</span><span style="color:#F97583">=</span><span style="color:#79B8FF">0.1</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">                 lr</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1e-3</span><span style="color:#E1E4E8">, buffer_size</span><span style="color:#F97583">=</span><span style="color:#79B8FF">10000</span><span style="color:#E1E4E8">, batch_size</span><span style="color:#F97583">=</span><span style="color:#79B8FF">32</span><span style="color:#E1E4E8">):</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Args:</span></span>
<span class="line"><span style="color:#9ECBFF">            n_features: Input dimensionality</span></span>
<span class="line"><span style="color:#9ECBFF">            n_actions: Number of actions</span></span>
<span class="line"><span style="color:#9ECBFF">            hidden_dims: Hidden layer sizes</span></span>
<span class="line"><span style="color:#9ECBFF">            epsilon: Exploration probability</span></span>
<span class="line"><span style="color:#9ECBFF">            lr: Learning rate</span></span>
<span class="line"><span style="color:#9ECBFF">            buffer_size: Experience replay buffer size</span></span>
<span class="line"><span style="color:#9ECBFF">            batch_size: Minibatch size for training</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.n_actions </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> n_actions</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.epsilon </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> epsilon</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.batch_size </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> batch_size</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Build neural network</span></span>
<span class="line"><span style="color:#E1E4E8">        layers </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> []</span></span>
<span class="line"><span style="color:#E1E4E8">        prev_dim </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> n_features</span></span>
<span class="line"><span style="color:#F97583">        for</span><span style="color:#E1E4E8"> hidden_dim </span><span style="color:#F97583">in</span><span style="color:#E1E4E8"> hidden_dims:</span></span>
<span class="line"><span style="color:#E1E4E8">            layers.append(nn.Linear(prev_dim, hidden_dim))</span></span>
<span class="line"><span style="color:#E1E4E8">            layers.append(nn.ReLU())</span></span>
<span class="line"><span style="color:#E1E4E8">            prev_dim </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> hidden_dim</span></span>
<span class="line"><span style="color:#E1E4E8">        layers.append(nn.Linear(prev_dim, n_actions))</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.model </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> nn.Sequential(</span><span style="color:#F97583">*</span><span style="color:#E1E4E8">layers)</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.optimizer </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> optim.Adam(</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.model.parameters(), </span><span style="color:#FFAB70">lr</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">lr)</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.criterion </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> nn.MSELoss()</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Experience replay buffer: (context, action, reward)</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.buffer </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> deque(</span><span style="color:#FFAB70">maxlen</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">buffer_size)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> select_action</span><span style="color:#E1E4E8">(self, context):</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        ε-greedy action selection.</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">        Args:</span></span>
<span class="line"><span style="color:#9ECBFF">            context: Feature vector (numpy array)</span></span>
<span class="line"><span style="color:#9ECBFF">            </span></span>
<span class="line"><span style="color:#9ECBFF">        Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">            action: Chosen action index</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#F97583">        if</span><span style="color:#E1E4E8"> np.random.random() </span><span style="color:#F97583">&#x3C;</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.epsilon:</span></span>
<span class="line"><span style="color:#6A737D">            # Explore: random action</span></span>
<span class="line"><span style="color:#F97583">            return</span><span style="color:#E1E4E8"> np.random.randint(</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.n_actions)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Exploit: best action according to Q-network</span></span>
<span class="line"><span style="color:#F97583">        with</span><span style="color:#E1E4E8"> torch.no_grad():</span></span>
<span class="line"><span style="color:#E1E4E8">            context_tensor </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.FloatTensor(context).unsqueeze(</span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">            q_values </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.model(context_tensor)</span></span>
<span class="line"><span style="color:#F97583">            return</span><span style="color:#E1E4E8"> torch.argmax(q_values).item()</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> update</span><span style="color:#E1E4E8">(self, context, action, reward):</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Store experience and train network.</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">        Args:</span></span>
<span class="line"><span style="color:#9ECBFF">            context: Observed context</span></span>
<span class="line"><span style="color:#9ECBFF">            action: Action taken</span></span>
<span class="line"><span style="color:#9ECBFF">            reward: Reward received</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#6A737D">        # Store in replay buffer</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.buffer.append((context, action, reward))</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Train on minibatch if enough data</span></span>
<span class="line"><span style="color:#F97583">        if</span><span style="color:#79B8FF"> len</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.buffer) </span><span style="color:#F97583">>=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.batch_size:</span></span>
<span class="line"><span style="color:#79B8FF">            self</span><span style="color:#E1E4E8">._train_step()</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> _train_step</span><span style="color:#E1E4E8">(self):</span></span>
<span class="line"><span style="color:#9ECBFF">        """Train network on random minibatch from buffer."""</span></span>
<span class="line"><span style="color:#6A737D">        # Sample minibatch</span></span>
<span class="line"><span style="color:#E1E4E8">        batch </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> random.sample(</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.buffer, </span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.batch_size)</span></span>
<span class="line"><span style="color:#E1E4E8">        contexts, actions, rewards </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> zip</span><span style="color:#E1E4E8">(</span><span style="color:#F97583">*</span><span style="color:#E1E4E8">batch)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Convert to tensors</span></span>
<span class="line"><span style="color:#E1E4E8">        context_tensor </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.FloatTensor(contexts)</span></span>
<span class="line"><span style="color:#E1E4E8">        action_tensor </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.LongTensor(actions)</span></span>
<span class="line"><span style="color:#E1E4E8">        reward_tensor </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.FloatTensor(rewards)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Forward pass: predict Q-values</span></span>
<span class="line"><span style="color:#E1E4E8">        q_values </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.model(context_tensor)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Extract Q-values for taken actions</span></span>
<span class="line"><span style="color:#E1E4E8">        q_values_selected </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> q_values.gather(</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">, action_tensor.unsqueeze(</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">)).squeeze()</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Loss: MSE between predicted Q and observed reward</span></span>
<span class="line"><span style="color:#E1E4E8">        loss </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.criterion(q_values_selected, reward_tensor)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Backprop</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.optimizer.zero_grad()</span></span>
<span class="line"><span style="color:#E1E4E8">        loss.backward()</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.optimizer.step()</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> get_q_values</span><span style="color:#E1E4E8">(self, context):</span></span>
<span class="line"><span style="color:#9ECBFF">        """Get Q-values for all actions (for debugging)."""</span></span>
<span class="line"><span style="color:#F97583">        with</span><span style="color:#E1E4E8"> torch.no_grad():</span></span>
<span class="line"><span style="color:#E1E4E8">            context_tensor </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.FloatTensor(context).unsqueeze(</span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#F97583">            return</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.model(context_tensor).squeeze().numpy()</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D"># Example usage</span></span>
<span class="line"><span style="color:#F97583">if</span><span style="color:#79B8FF"> __name__</span><span style="color:#F97583"> ==</span><span style="color:#9ECBFF"> "__main__"</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#6A737D">    # High-dimensional context (e.g., image embedding)</span></span>
<span class="line"><span style="color:#E1E4E8">    n_features </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 512</span></span>
<span class="line"><span style="color:#E1E4E8">    n_actions </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 10</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#E1E4E8">    bandit </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> NeuralEpsilonGreedy(</span></span>
<span class="line"><span style="color:#FFAB70">        n_features</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">n_features,</span></span>
<span class="line"><span style="color:#FFAB70">        n_actions</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">n_actions,</span></span>
<span class="line"><span style="color:#FFAB70">        hidden_dims</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">[</span><span style="color:#79B8FF">256</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">128</span><span style="color:#E1E4E8">],</span></span>
<span class="line"><span style="color:#FFAB70">        epsilon</span><span style="color:#F97583">=</span><span style="color:#79B8FF">0.1</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#FFAB70">        lr</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1e-3</span></span>
<span class="line"><span style="color:#E1E4E8">    )</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Simulate training</span></span>
<span class="line"><span style="color:#F97583">    for</span><span style="color:#E1E4E8"> t </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> range</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">1000</span><span style="color:#E1E4E8">):</span></span>
<span class="line"><span style="color:#E1E4E8">        context </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> np.random.randn(n_features)  </span><span style="color:#6A737D"># Random context</span></span>
<span class="line"><span style="color:#E1E4E8">        action </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> bandit.select_action(context)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Simulate reward (true reward = sum of context, varies by action)</span></span>
<span class="line"><span style="color:#E1E4E8">        true_reward </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> (action </span><span style="color:#F97583">/</span><span style="color:#E1E4E8"> n_actions) </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> np.sum(context[:</span><span style="color:#79B8FF">10</span><span style="color:#E1E4E8">])</span></span>
<span class="line"><span style="color:#E1E4E8">        noise </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> np.random.normal(</span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">0.1</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        reward </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> true_reward </span><span style="color:#F97583">+</span><span style="color:#E1E4E8"> noise</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#E1E4E8">        bandit.update(context, action, reward)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        if</span><span style="color:#E1E4E8"> t </span><span style="color:#F97583">%</span><span style="color:#79B8FF"> 100</span><span style="color:#F97583"> ==</span><span style="color:#79B8FF"> 0</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#79B8FF">            print</span><span style="color:#E1E4E8">(</span><span style="color:#F97583">f</span><span style="color:#9ECBFF">"Round </span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">t</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">: Action </span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">action</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">, Reward </span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">reward</span><span style="color:#F97583">:.3f</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">"</span><span style="color:#E1E4E8">)</span></span>
<span class="line"></span></code></pre>
<h3 id="when-to-use-neural-ε-greedy">When to Use Neural ε-greedy</h3>
<p><strong>✅ Use as baseline when:</strong></p>
<ul>
<li>First trying neural bandits (validate infrastructure)</li>
<li>High-dimensional contexts (images, text)</li>
<li>Simple to implement and explain</li>
</ul>
<p><strong>❌ Limitations:</strong></p>
<ul>
<li>Random exploration (inefficient)</li>
<li>No directed exploration toward uncertainty</li>
<li>Still O(T^(2/3)) regret (suboptimal)</li>
</ul>
<p><strong>Production tip:</strong> Start here, then upgrade to NeuralLinear or Bootstrap if you need better sample efficiency.</p>
<hr>
<h2 id="neurallinear-best-of-both-worlds">NeuralLinear: Best of Both Worlds</h2>
<p><strong>Key insight:</strong> Use neural network to learn good features, then apply LinUCB in the learned feature space.</p>
<h3 id="architecture-1">Architecture</h3>
<div class="mermaid">
graph LR
    Context[Raw Context&#x3C;br/>━━━━━━━━&#x3C;br/>High-dim&#x3C;br/>Images, text] --> NN[Neural Feature&#x3C;br/>Extractor&#x3C;br/>━━━━━━━━&#x3C;br/>CNN, Transformer&#x3C;br/>Dense layers]
    
    NN --> Phi[Learned Features&#x3C;br/>━━━━━━━━&#x3C;br/>Low-dim embedding&#x3C;br/>φx]
    
    Phi --> LinUCB[LinUCB&#x3C;br/>━━━━━━━━&#x3C;br/>Confidence bounds&#x3C;br/>Directed exploration]
    
    LinUCB --> Action[Selected Action&#x3C;br/>━━━━━━━━&#x3C;br/>Highest UCB]
    
    style Context stroke:#8b5cf6,stroke-width:2px
    style NN stroke:#06b6d4,stroke-width:2.5px
    style Phi stroke:#10b981,stroke-width:2px
    style LinUCB stroke:#f59e0b,stroke-width:2.5px
    style Action stroke:#10b981,stroke-width:2px</div>
<h3 id="why-this-works">Why This Works</h3>
<ol>
<li><strong>Neural network</strong> learns good representations φ(x) from raw inputs</li>
<li><strong>LinUCB</strong> operates in feature space, providing confidence bounds</li>
<li><strong>Best of both:</strong> Neural expressiveness + principled exploration</li>
</ol>
<p><strong>Assumption:</strong> Rewards are linear in learned features φ(x), even if nonlinear in raw context x.</p>
<h3 id="implementation-1">Implementation</h3>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#F97583">class</span><span style="color:#B392F0"> NeuralLinear</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#9ECBFF">    Neural feature extractor + LinUCB.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Neural network learns representation φ(x), then LinUCB operates</span></span>
<span class="line"><span style="color:#9ECBFF">    in this learned feature space with confidence-based exploration.</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#79B8FF"> __init__</span><span style="color:#E1E4E8">(self, n_actions, input_dim, feature_dim</span><span style="color:#F97583">=</span><span style="color:#79B8FF">128</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">                 hidden_dims</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">[</span><span style="color:#79B8FF">256</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">128</span><span style="color:#E1E4E8">], alpha</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1.0</span><span style="color:#E1E4E8">, lambda_</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1.0</span><span style="color:#E1E4E8">):</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Args:</span></span>
<span class="line"><span style="color:#9ECBFF">            n_actions: Number of actions</span></span>
<span class="line"><span style="color:#9ECBFF">            input_dim: Raw context dimensionality</span></span>
<span class="line"><span style="color:#9ECBFF">            feature_dim: Learned feature dimensionality</span></span>
<span class="line"><span style="color:#9ECBFF">            hidden_dims: Hidden layer sizes for feature extractor</span></span>
<span class="line"><span style="color:#9ECBFF">            alpha: LinUCB exploration parameter</span></span>
<span class="line"><span style="color:#9ECBFF">            lambda_: LinUCB regularization parameter</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.n_actions </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> n_actions</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.feature_dim </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> feature_dim</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.alpha </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> alpha</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.lambda_ </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> lambda_</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Neural feature extractor</span></span>
<span class="line"><span style="color:#E1E4E8">        layers </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> []</span></span>
<span class="line"><span style="color:#E1E4E8">        prev_dim </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> input_dim</span></span>
<span class="line"><span style="color:#F97583">        for</span><span style="color:#E1E4E8"> hidden_dim </span><span style="color:#F97583">in</span><span style="color:#E1E4E8"> hidden_dims:</span></span>
<span class="line"><span style="color:#E1E4E8">            layers.append(nn.Linear(prev_dim, hidden_dim))</span></span>
<span class="line"><span style="color:#E1E4E8">            layers.append(nn.ReLU())</span></span>
<span class="line"><span style="color:#E1E4E8">            prev_dim </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> hidden_dim</span></span>
<span class="line"><span style="color:#E1E4E8">        layers.append(nn.Linear(prev_dim, feature_dim))</span></span>
<span class="line"><span style="color:#E1E4E8">        layers.append(nn.ReLU())  </span><span style="color:#6A737D"># Ensure positive features</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.feature_model </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> nn.Sequential(</span><span style="color:#F97583">*</span><span style="color:#E1E4E8">layers)</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.optimizer </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> optim.Adam(</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.feature_model.parameters(), </span><span style="color:#FFAB70">lr</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1e-3</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # LinUCB in learned feature space</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.A </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> [lambda_ </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> np.eye(feature_dim) </span><span style="color:#F97583">for</span><span style="color:#E1E4E8"> _ </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> range</span><span style="color:#E1E4E8">(n_actions)]</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.b </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> [np.zeros(feature_dim) </span><span style="color:#F97583">for</span><span style="color:#E1E4E8"> _ </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> range</span><span style="color:#E1E4E8">(n_actions)]</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Training buffer</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.buffer </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> deque(</span><span style="color:#FFAB70">maxlen</span><span style="color:#F97583">=</span><span style="color:#79B8FF">10000</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> extract_features</span><span style="color:#E1E4E8">(self, context):</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Extract features using neural network.</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">        Args:</span></span>
<span class="line"><span style="color:#9ECBFF">            context: Raw context (numpy array)</span></span>
<span class="line"><span style="color:#9ECBFF">            </span></span>
<span class="line"><span style="color:#9ECBFF">        Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">            features: Learned representation φ(x)</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#F97583">        with</span><span style="color:#E1E4E8"> torch.no_grad():</span></span>
<span class="line"><span style="color:#E1E4E8">            context_tensor </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.FloatTensor(context).unsqueeze(</span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">            features </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.feature_model(context_tensor).squeeze().numpy()</span></span>
<span class="line"><span style="color:#F97583">        return</span><span style="color:#E1E4E8"> features</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> select_action</span><span style="color:#E1E4E8">(self, context):</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Select action with highest UCB in learned feature space.</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">        Args:</span></span>
<span class="line"><span style="color:#9ECBFF">            context: Raw context</span></span>
<span class="line"><span style="color:#9ECBFF">            </span></span>
<span class="line"><span style="color:#9ECBFF">        Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">            action: Chosen action index</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#6A737D">        # Extract features</span></span>
<span class="line"><span style="color:#E1E4E8">        features </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.extract_features(context)</span></span>
<span class="line"><span style="color:#E1E4E8">        features </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> features.reshape(</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">)  </span><span style="color:#6A737D"># Column vector</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Compute UCB for each action (same as LinUCB)</span></span>
<span class="line"><span style="color:#E1E4E8">        ucb_values </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> np.zeros(</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.n_actions)</span></span>
<span class="line"><span style="color:#F97583">        for</span><span style="color:#E1E4E8"> a </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> range</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.n_actions):</span></span>
<span class="line"><span style="color:#E1E4E8">            A_inv </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> np.linalg.inv(</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.A[a])</span></span>
<span class="line"><span style="color:#E1E4E8">            theta </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> A_inv </span><span style="color:#F97583">@</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.b[a]</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#E1E4E8">            predicted_reward </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> theta.T </span><span style="color:#F97583">@</span><span style="color:#E1E4E8"> features</span></span>
<span class="line"><span style="color:#E1E4E8">            confidence_bonus </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.alpha </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> np.sqrt(features.T </span><span style="color:#F97583">@</span><span style="color:#E1E4E8"> A_inv </span><span style="color:#F97583">@</span><span style="color:#E1E4E8"> features)</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#E1E4E8">            ucb_values[a] </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> predicted_reward </span><span style="color:#F97583">+</span><span style="color:#E1E4E8"> confidence_bonus</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        return</span><span style="color:#E1E4E8"> np.argmax(ucb_values)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> update</span><span style="color:#E1E4E8">(self, context, action, reward):</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Update LinUCB estimates and (optionally) train feature extractor.</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">        Args:</span></span>
<span class="line"><span style="color:#9ECBFF">            context: Raw context</span></span>
<span class="line"><span style="color:#9ECBFF">            action: Action taken</span></span>
<span class="line"><span style="color:#9ECBFF">            reward: Reward received</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#6A737D">        # Extract features</span></span>
<span class="line"><span style="color:#E1E4E8">        features </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.extract_features(context)</span></span>
<span class="line"><span style="color:#E1E4E8">        features </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> features.reshape(</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Update LinUCB (same as before)</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.A[action] </span><span style="color:#F97583">+=</span><span style="color:#E1E4E8"> features </span><span style="color:#F97583">@</span><span style="color:#E1E4E8"> features.T</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.b[action] </span><span style="color:#F97583">+=</span><span style="color:#E1E4E8"> reward </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> features.squeeze()</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Store for feature learning</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.buffer.append((context, action, reward))</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Periodically update feature extractor</span></span>
<span class="line"><span style="color:#F97583">        if</span><span style="color:#79B8FF"> len</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.buffer) </span><span style="color:#F97583">>=</span><span style="color:#79B8FF"> 32</span><span style="color:#F97583"> and</span><span style="color:#79B8FF"> len</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.buffer) </span><span style="color:#F97583">%</span><span style="color:#79B8FF"> 10</span><span style="color:#F97583"> ==</span><span style="color:#79B8FF"> 0</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#79B8FF">            self</span><span style="color:#E1E4E8">._train_features()</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> _train_features</span><span style="color:#E1E4E8">(self):</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Train feature extractor to predict rewards.</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">        This is optional but can improve feature quality over time.</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#F97583">        if</span><span style="color:#79B8FF"> len</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.buffer) </span><span style="color:#F97583">&#x3C;</span><span style="color:#79B8FF"> 32</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#F97583">            return</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Sample minibatch</span></span>
<span class="line"><span style="color:#E1E4E8">        batch </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> random.sample(</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.buffer, </span><span style="color:#79B8FF">32</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        contexts, actions, rewards </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> zip</span><span style="color:#E1E4E8">(</span><span style="color:#F97583">*</span><span style="color:#E1E4E8">batch)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Extract features</span></span>
<span class="line"><span style="color:#E1E4E8">        context_tensor </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.FloatTensor(contexts)</span></span>
<span class="line"><span style="color:#E1E4E8">        features </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.feature_model(context_tensor)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Predict rewards using current LinUCB parameters</span></span>
<span class="line"><span style="color:#E1E4E8">        predicted_rewards </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> []</span></span>
<span class="line"><span style="color:#F97583">        for</span><span style="color:#E1E4E8"> i, a </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> enumerate</span><span style="color:#E1E4E8">(actions):</span></span>
<span class="line"><span style="color:#E1E4E8">            A_inv </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> np.linalg.inv(</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.A[a])</span></span>
<span class="line"><span style="color:#E1E4E8">            theta </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> A_inv </span><span style="color:#F97583">@</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.b[a]</span></span>
<span class="line"><span style="color:#E1E4E8">            pred </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> theta.T </span><span style="color:#F97583">@</span><span style="color:#E1E4E8"> features[i].detach().numpy()</span></span>
<span class="line"><span style="color:#E1E4E8">            predicted_rewards.append(pred)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#E1E4E8">        predicted_tensor </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.FloatTensor(predicted_rewards)</span></span>
<span class="line"><span style="color:#E1E4E8">        reward_tensor </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.FloatTensor(rewards)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Train to predict rewards better</span></span>
<span class="line"><span style="color:#E1E4E8">        loss </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> nn.MSELoss()(predicted_tensor, reward_tensor)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.optimizer.zero_grad()</span></span>
<span class="line"><span style="color:#E1E4E8">        loss.backward()</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.optimizer.step()</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> get_theta</span><span style="color:#E1E4E8">(self, action):</span></span>
<span class="line"><span style="color:#9ECBFF">        """Get learned LinUCB parameters for an action."""</span></span>
<span class="line"><span style="color:#E1E4E8">        A_inv </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> np.linalg.inv(</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.A[action])</span></span>
<span class="line"><span style="color:#F97583">        return</span><span style="color:#E1E4E8"> A_inv </span><span style="color:#F97583">@</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.b[action]</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D"># Example usage</span></span>
<span class="line"><span style="color:#F97583">if</span><span style="color:#79B8FF"> __name__</span><span style="color:#F97583"> ==</span><span style="color:#9ECBFF"> "__main__"</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#6A737D">    # Image-like context (e.g., 28x28 = 784 pixels)</span></span>
<span class="line"><span style="color:#E1E4E8">    input_dim </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 784</span></span>
<span class="line"><span style="color:#E1E4E8">    n_actions </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 10</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#E1E4E8">    bandit </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> NeuralLinear(</span></span>
<span class="line"><span style="color:#FFAB70">        n_actions</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">n_actions,</span></span>
<span class="line"><span style="color:#FFAB70">        input_dim</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">input_dim,</span></span>
<span class="line"><span style="color:#FFAB70">        feature_dim</span><span style="color:#F97583">=</span><span style="color:#79B8FF">64</span><span style="color:#E1E4E8">,  </span><span style="color:#6A737D"># Compress to 64-dim</span></span>
<span class="line"><span style="color:#FFAB70">        hidden_dims</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">[</span><span style="color:#79B8FF">256</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">128</span><span style="color:#E1E4E8">],</span></span>
<span class="line"><span style="color:#FFAB70">        alpha</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1.0</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#FFAB70">        lambda_</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1.0</span></span>
<span class="line"><span style="color:#E1E4E8">    )</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    for</span><span style="color:#E1E4E8"> t </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> range</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">1000</span><span style="color:#E1E4E8">):</span></span>
<span class="line"><span style="color:#E1E4E8">        context </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> np.random.randn(input_dim)</span></span>
<span class="line"><span style="color:#E1E4E8">        action </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> bandit.select_action(context)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Simulate reward</span></span>
<span class="line"><span style="color:#E1E4E8">        reward </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> (action </span><span style="color:#F97583">/</span><span style="color:#E1E4E8"> n_actions) </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> np.sum(context[:</span><span style="color:#79B8FF">10</span><span style="color:#E1E4E8">]) </span><span style="color:#F97583">+</span><span style="color:#E1E4E8"> np.random.normal(</span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">0.1</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#E1E4E8">        bandit.update(context, action, reward)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        if</span><span style="color:#E1E4E8"> t </span><span style="color:#F97583">%</span><span style="color:#79B8FF"> 100</span><span style="color:#F97583"> ==</span><span style="color:#79B8FF"> 0</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#79B8FF">            print</span><span style="color:#E1E4E8">(</span><span style="color:#F97583">f</span><span style="color:#9ECBFF">"Round </span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">t</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">: Action </span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">action</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">, Reward </span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">reward</span><span style="color:#F97583">:.3f</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">"</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#79B8FF">            print</span><span style="color:#E1E4E8">(</span><span style="color:#F97583">f</span><span style="color:#9ECBFF">"  Feature norm: </span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">np.linalg.norm(bandit.extract_features(context))</span><span style="color:#F97583">:.3f</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">"</span><span style="color:#E1E4E8">)</span></span>
<span class="line"></span></code></pre>
<h3 id="when-to-use-neurallinear">When to Use NeuralLinear</h3>
<p><strong>✅ Use when:</strong></p>
<ul>
<li>High-dimensional raw contexts (images, text)</li>
<li>Rewards are linear in good features (but you don’t know what features)</li>
<li>Want directed exploration (better than ε-greedy)</li>
<li>Need some interpretability (can examine θ weights)</li>
</ul>
<p><strong>✅ Advantages:</strong></p>
<ul>
<li>Confidence bounds guide exploration</li>
<li>More sample-efficient than neural ε-greedy</li>
<li>Can inspect learned features and weights</li>
</ul>
<p><strong>❌ Limitations:</strong></p>
<ul>
<li>Assumes linearity in learned features</li>
<li>Training feature extractor adds complexity</li>
<li>Not as flexible as full neural Thompson Sampling</li>
</ul>
<hr>
<h2 id="bootstrap-thompson-sampling-ensemble-for-uncertainty">Bootstrap Thompson Sampling: Ensemble for Uncertainty</h2>
<p><strong>Core idea:</strong> Train an ensemble of neural networks on bootstrap samples. Disagreement between networks = uncertainty.</p>
<h3 id="why-ensembles-approximate-bayesian-posteriors">Why Ensembles Approximate Bayesian Posteriors</h3>
<p><strong>Bayesian Thompson Sampling:</strong></p>
<ul>
<li>Posterior: <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><mi>θ</mi><mo>∣</mo><mtext>data</mtext><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\theta \sim P(\theta \mid \text{data})</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.02778em;">θ</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.13889em;">P</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.02778em;">θ</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">data</span></span><span class="mclose">)</span></span></span></span></li>
<li>Sample: <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>θ</mi><mo>~</mo></mover><mo>∼</mo><mi>P</mi><mo stretchy="false">(</mo><mi>θ</mi><mo>∣</mo><mtext>data</mtext><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\tilde{\theta} \sim P(\theta \mid \text{data})</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9313em;"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9313em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">θ</span></span><span style="top:-3.6134em;"><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="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.13889em;">P</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.02778em;">θ</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">data</span></span><span class="mclose">)</span></span></span></span></li>
<li>Choose: <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>a</mi><mo>=</mo><mi>arg</mi><mo>⁡</mo><msub><mrow><mi>max</mi><mo>⁡</mo></mrow><mi>a</mi></msub><msub><mi>f</mi><mover accent="true"><mi>θ</mi><mo>~</mo></mover></msub><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">a = \arg\max_a f_{\tilde{\theta}}(x, a)</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">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:1.0571em;vertical-align:-0.3071em;"></span><span class="mop">ar<span style="margin-right:0.01389em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mop"><span class="mop">max</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 mathnormal mtight">a</span></span></span></span><span 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" style="margin-right:0.10764em;">f</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.3929em;margin-left:-0.1076em;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"><span class="vlist-r"><span class="vlist" style="height:0.9313em;"><span style="top:-2.7em;"><span class="pstrut" style="height:2.7em;"></span><span class="mord mathnormal mtight" style="margin-right:0.02778em;">θ</span></span><span style="top:-3.3134em;"><span class="pstrut" style="height:2.7em;"></span><span class="accent-body" style="left:-0.1667em;"><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.3071em;"><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">a</span><span class="mclose">)</span></span></span></span></li>
</ul>
<p><strong>Bootstrap approximation:</strong></p>
<ul>
<li>Train K networks on different bootstrap samples</li>
<li>Each network <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>f</mi><mi>k</mi></msub></mrow><annotation encoding="application/x-tex">f_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"><span class="mord mathnormal" style="margin-right:0.10764em;">f</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.1076em;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.03148em;">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> approximates a sample from posterior</li>
<li>Sample random network <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi><mo>∼</mo><mtext>Uniform</mtext><mo stretchy="false">(</mo><mn>1</mn><mo separator="true">,</mo><mi>K</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">k \sim \text{Uniform}(1, 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.03148em;">k</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">Uniform</span></span><span class="mopen">(</span><span class="mord">1</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.07153em;">K</span><span class="mclose">)</span></span></span></span></li>
<li>Choose: <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>a</mi><mo>=</mo><mi>arg</mi><mo>⁡</mo><msub><mrow><mi>max</mi><mo>⁡</mo></mrow><mi>a</mi></msub><msub><mi>f</mi><mi>k</mi></msub><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">a = \arg\max_a f_k(x, a)</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">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:1em;vertical-align:-0.25em;"></span><span class="mop">ar<span style="margin-right:0.01389em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mop"><span class="mop">max</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 mathnormal mtight">a</span></span></span></span><span 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" style="margin-right:0.10764em;">f</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.1076em;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.03148em;">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="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</span><span class="mclose">)</span></span></span></span></li>
</ul>
<p><strong>Intuition:</strong> In regions with lots of data, all networks agree (low uncertainty). In novel regions, networks disagree (high uncertainty) → naturally explores.</p>
<h3 id="implementation-2">Implementation</h3>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#F97583">class</span><span style="color:#B392F0"> BootstrappedNeuralBandit</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#9ECBFF">    Bootstrap ensemble for Thompson Sampling approximation.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Trains K neural networks on bootstrap samples of data.</span></span>
<span class="line"><span style="color:#9ECBFF">    Action selection: randomly pick one network, use its predictions.</span></span>
<span class="line"><span style="color:#9ECBFF">    Disagreement between networks indicates uncertainty.</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#79B8FF"> __init__</span><span style="color:#E1E4E8">(self, n_features, n_actions, n_models</span><span style="color:#F97583">=</span><span style="color:#79B8FF">10</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">                 hidden_dims</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">[</span><span style="color:#79B8FF">256</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">128</span><span style="color:#E1E4E8">], lr</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1e-3</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">                 buffer_size</span><span style="color:#F97583">=</span><span style="color:#79B8FF">10000</span><span style="color:#E1E4E8">, batch_size</span><span style="color:#F97583">=</span><span style="color:#79B8FF">32</span><span style="color:#E1E4E8">):</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Args:</span></span>
<span class="line"><span style="color:#9ECBFF">            n_features: Input dimensionality</span></span>
<span class="line"><span style="color:#9ECBFF">            n_actions: Number of actions</span></span>
<span class="line"><span style="color:#9ECBFF">            n_models: Number of networks in ensemble</span></span>
<span class="line"><span style="color:#9ECBFF">            hidden_dims: Hidden layer sizes</span></span>
<span class="line"><span style="color:#9ECBFF">            lr: Learning rate</span></span>
<span class="line"><span style="color:#9ECBFF">            buffer_size: Replay buffer size</span></span>
<span class="line"><span style="color:#9ECBFF">            batch_size: Minibatch size</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.n_models </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> n_models</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.n_actions </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> n_actions</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.batch_size </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> batch_size</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Create ensemble of neural networks</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.models </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> []</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.optimizers </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> []</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        for</span><span style="color:#E1E4E8"> _ </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> range</span><span style="color:#E1E4E8">(n_models):</span></span>
<span class="line"><span style="color:#6A737D">            # Build network</span></span>
<span class="line"><span style="color:#E1E4E8">            layers </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> []</span></span>
<span class="line"><span style="color:#E1E4E8">            prev_dim </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> n_features</span></span>
<span class="line"><span style="color:#F97583">            for</span><span style="color:#E1E4E8"> hidden_dim </span><span style="color:#F97583">in</span><span style="color:#E1E4E8"> hidden_dims:</span></span>
<span class="line"><span style="color:#E1E4E8">                layers.append(nn.Linear(prev_dim, hidden_dim))</span></span>
<span class="line"><span style="color:#E1E4E8">                layers.append(nn.ReLU())</span></span>
<span class="line"><span style="color:#E1E4E8">                prev_dim </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> hidden_dim</span></span>
<span class="line"><span style="color:#E1E4E8">            layers.append(nn.Linear(prev_dim, n_actions))</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#E1E4E8">            model </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> nn.Sequential(</span><span style="color:#F97583">*</span><span style="color:#E1E4E8">layers)</span></span>
<span class="line"><span style="color:#E1E4E8">            optimizer </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> optim.Adam(model.parameters(), </span><span style="color:#FFAB70">lr</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">lr)</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#79B8FF">            self</span><span style="color:#E1E4E8">.models.append(model)</span></span>
<span class="line"><span style="color:#79B8FF">            self</span><span style="color:#E1E4E8">.optimizers.append(optimizer)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.criterion </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> nn.MSELoss()</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.buffer </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> deque(</span><span style="color:#FFAB70">maxlen</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">buffer_size)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> select_action</span><span style="color:#E1E4E8">(self, context):</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Thompson Sampling style: sample one network, use its predictions.</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">        Args:</span></span>
<span class="line"><span style="color:#9ECBFF">            context: Feature vector</span></span>
<span class="line"><span style="color:#9ECBFF">            </span></span>
<span class="line"><span style="color:#9ECBFF">        Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">            action: Chosen action index</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#6A737D">        # Randomly sample one model from ensemble (Thompson Sampling)</span></span>
<span class="line"><span style="color:#E1E4E8">        model_idx </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> np.random.randint(</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.n_models)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        with</span><span style="color:#E1E4E8"> torch.no_grad():</span></span>
<span class="line"><span style="color:#E1E4E8">            context_tensor </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.FloatTensor(context).unsqueeze(</span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">            q_values </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.models[model_idx](context_tensor)</span></span>
<span class="line"><span style="color:#F97583">            return</span><span style="color:#E1E4E8"> torch.argmax(q_values).item()</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> update</span><span style="color:#E1E4E8">(self, context, action, reward):</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Store experience and train all networks on bootstrap samples.</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">        Args:</span></span>
<span class="line"><span style="color:#9ECBFF">            context: Observed context</span></span>
<span class="line"><span style="color:#9ECBFF">            action: Action taken</span></span>
<span class="line"><span style="color:#9ECBFF">            reward: Reward received</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#6A737D">        # Store in buffer</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.buffer.append((context, action, reward))</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Train each model on a bootstrap sample</span></span>
<span class="line"><span style="color:#F97583">        if</span><span style="color:#79B8FF"> len</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.buffer) </span><span style="color:#F97583">>=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.batch_size:</span></span>
<span class="line"><span style="color:#79B8FF">            self</span><span style="color:#E1E4E8">._train_step()</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> _train_step</span><span style="color:#E1E4E8">(self):</span></span>
<span class="line"><span style="color:#9ECBFF">        """Train each model on a different bootstrap sample."""</span></span>
<span class="line"><span style="color:#F97583">        for</span><span style="color:#E1E4E8"> k </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> range</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.n_models):</span></span>
<span class="line"><span style="color:#6A737D">            # Bootstrap: resample with replacement</span></span>
<span class="line"><span style="color:#E1E4E8">            bootstrap_batch </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> random.choices(</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.buffer, </span><span style="color:#FFAB70">k</span><span style="color:#F97583">=</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.batch_size)</span></span>
<span class="line"><span style="color:#E1E4E8">            contexts, actions, rewards </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> zip</span><span style="color:#E1E4E8">(</span><span style="color:#F97583">*</span><span style="color:#E1E4E8">bootstrap_batch)</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#6A737D">            # Convert to tensors</span></span>
<span class="line"><span style="color:#E1E4E8">            context_tensor </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.FloatTensor(contexts)</span></span>
<span class="line"><span style="color:#E1E4E8">            action_tensor </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.LongTensor(actions)</span></span>
<span class="line"><span style="color:#E1E4E8">            reward_tensor </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.FloatTensor(rewards)</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#6A737D">            # Forward pass</span></span>
<span class="line"><span style="color:#E1E4E8">            q_values </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.models[k](context_tensor)</span></span>
<span class="line"><span style="color:#E1E4E8">            q_selected </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> q_values.gather(</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">, action_tensor.unsqueeze(</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">)).squeeze()</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#6A737D">            # Loss</span></span>
<span class="line"><span style="color:#E1E4E8">            loss </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.criterion(q_selected, reward_tensor)</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#6A737D">            # Backprop</span></span>
<span class="line"><span style="color:#79B8FF">            self</span><span style="color:#E1E4E8">.optimizers[k].zero_grad()</span></span>
<span class="line"><span style="color:#E1E4E8">            loss.backward()</span></span>
<span class="line"><span style="color:#79B8FF">            self</span><span style="color:#E1E4E8">.optimizers[k].step()</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> get_uncertainty</span><span style="color:#E1E4E8">(self, context):</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Compute uncertainty as disagreement between models.</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">        Args:</span></span>
<span class="line"><span style="color:#9ECBFF">            context: Feature vector</span></span>
<span class="line"><span style="color:#9ECBFF">            </span></span>
<span class="line"><span style="color:#9ECBFF">        Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">            mean: Mean Q-values across ensemble</span></span>
<span class="line"><span style="color:#9ECBFF">            std: Standard deviation (uncertainty measure)</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#F97583">        with</span><span style="color:#E1E4E8"> torch.no_grad():</span></span>
<span class="line"><span style="color:#E1E4E8">            context_tensor </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.FloatTensor(context).unsqueeze(</span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#6A737D">            # Get predictions from all models</span></span>
<span class="line"><span style="color:#E1E4E8">            predictions </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> []</span></span>
<span class="line"><span style="color:#F97583">            for</span><span style="color:#E1E4E8"> model </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.models:</span></span>
<span class="line"><span style="color:#E1E4E8">                q_values </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> model(context_tensor).squeeze().numpy()</span></span>
<span class="line"><span style="color:#E1E4E8">                predictions.append(q_values)</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#E1E4E8">            predictions </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> np.array(predictions)  </span><span style="color:#6A737D"># Shape: [n_models, n_actions]</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#E1E4E8">            mean </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> predictions.mean(</span><span style="color:#FFAB70">axis</span><span style="color:#F97583">=</span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">            std </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> predictions.std(</span><span style="color:#FFAB70">axis</span><span style="color:#F97583">=</span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#F97583">            return</span><span style="color:#E1E4E8"> mean, std</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D"># Example usage with uncertainty visualization</span></span>
<span class="line"><span style="color:#F97583">if</span><span style="color:#79B8FF"> __name__</span><span style="color:#F97583"> ==</span><span style="color:#9ECBFF"> "__main__"</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#E1E4E8">    n_features </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 512</span></span>
<span class="line"><span style="color:#E1E4E8">    n_actions </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 10</span></span>
<span class="line"><span style="color:#E1E4E8">    n_models </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 10</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#E1E4E8">    bandit </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> BootstrappedNeuralBandit(</span></span>
<span class="line"><span style="color:#FFAB70">        n_features</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">n_features,</span></span>
<span class="line"><span style="color:#FFAB70">        n_actions</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">n_actions,</span></span>
<span class="line"><span style="color:#FFAB70">        n_models</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">n_models,</span></span>
<span class="line"><span style="color:#FFAB70">        hidden_dims</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">[</span><span style="color:#79B8FF">256</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">128</span><span style="color:#E1E4E8">],</span></span>
<span class="line"><span style="color:#FFAB70">        lr</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1e-3</span></span>
<span class="line"><span style="color:#E1E4E8">    )</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    for</span><span style="color:#E1E4E8"> t </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> range</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">2000</span><span style="color:#E1E4E8">):</span></span>
<span class="line"><span style="color:#E1E4E8">        context </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> np.random.randn(n_features)</span></span>
<span class="line"><span style="color:#E1E4E8">        action </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> bandit.select_action(context)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Simulate reward</span></span>
<span class="line"><span style="color:#E1E4E8">        true_reward </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> (action </span><span style="color:#F97583">/</span><span style="color:#E1E4E8"> n_actions) </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> np.sum(context[:</span><span style="color:#79B8FF">10</span><span style="color:#E1E4E8">])</span></span>
<span class="line"><span style="color:#E1E4E8">        noise </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> np.random.normal(</span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">0.1</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        reward </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> true_reward </span><span style="color:#F97583">+</span><span style="color:#E1E4E8"> noise</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#E1E4E8">        bandit.update(context, action, reward)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        if</span><span style="color:#E1E4E8"> t </span><span style="color:#F97583">%</span><span style="color:#79B8FF"> 200</span><span style="color:#F97583"> ==</span><span style="color:#79B8FF"> 0</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#E1E4E8">            mean, std </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> bandit.get_uncertainty(context)</span></span>
<span class="line"><span style="color:#79B8FF">            print</span><span style="color:#E1E4E8">(</span><span style="color:#F97583">f</span><span style="color:#9ECBFF">"</span><span style="color:#79B8FF">\n</span><span style="color:#9ECBFF">Round </span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">t</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">:"</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#79B8FF">            print</span><span style="color:#E1E4E8">(</span><span style="color:#F97583">f</span><span style="color:#9ECBFF">"  Chosen action: </span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">action</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">, Reward: </span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">reward</span><span style="color:#F97583">:.3f</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">"</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#79B8FF">            print</span><span style="color:#E1E4E8">(</span><span style="color:#F97583">f</span><span style="color:#9ECBFF">"  Uncertainty (avg std): </span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">std.mean()</span><span style="color:#F97583">:.3f</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">"</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#79B8FF">            print</span><span style="color:#E1E4E8">(</span><span style="color:#F97583">f</span><span style="color:#9ECBFF">"  Action </span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">action</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF"> uncertainty: </span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">std[action]</span><span style="color:#F97583">:.3f</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">"</span><span style="color:#E1E4E8">)</span></span>
<span class="line"></span></code></pre>
<h3 id="when-to-use-bootstrap-thompson-sampling">When to Use Bootstrap Thompson Sampling</h3>
<p><strong>✅ Use when:</strong></p>
<ul>
<li>Need best sample efficiency (Thompson Sampling properties)</li>
<li>Can afford computational cost (K networks, K forward passes)</li>
<li>Want uncertainty quantification (ensemble disagreement)</li>
<li>Don’t need interpretability</li>
</ul>
<p><strong>✅ Advantages:</strong></p>
<ul>
<li>Approximates Bayesian Thompson Sampling</li>
<li>Natural exploration-exploitation balance</li>
<li>Often best empirical performance</li>
</ul>
<p><strong>❌ Limitations:</strong></p>
<ul>
<li>Expensive: K times the cost (typically K = 5-20)</li>
<li>No theoretical guarantees (approximate Thompson)</li>
<li>Less interpretable than NeuralLinear</li>
</ul>
<h3 id="ensemble-size-tuning">Ensemble Size Tuning</h3>





























<table><thead><tr><th>n_models</th><th>Computational Cost</th><th>Uncertainty Quality</th><th>When to Use</th></tr></thead><tbody><tr><td>3-5</td><td>Low (3-5x)</td><td>Rough approximation</td><td>Quick prototyping</td></tr><tr><td>10</td><td>Medium (10x)</td><td>Good uncertainty</td><td><strong>Production default</strong></td></tr><tr><td>20</td><td>High (20x)</td><td>Best uncertainty</td><td>Critical applications</td></tr></tbody></table>
<p><strong>Diminishing returns:</strong> Beyond 10-15 models, improvement is marginal.</p>
<hr>
<h2 id="high-dimensional-action-spaces">High-Dimensional Action Spaces</h2>
<p><strong>Problem:</strong> With thousands to millions of actions (e.g., all products in catalog), standard bandits don’t scale.</p>
<ul>
<li>Can’t maintain separate parameters for each action</li>
<li>Can’t explore all actions</li>
<li>Need generalization across actions</li>
</ul>
<h3 id="solution-1-action-embeddings">Solution 1: Action Embeddings</h3>
<p><strong>Idea:</strong> Represent actions in low-dimensional space. Learn that similar actions have similar rewards.</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#F97583">class</span><span style="color:#B392F0"> ActionEmbeddingBandit</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#9ECBFF">    Contextual bandit with action embeddings.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Instead of learning separate parameters for each action,</span></span>
<span class="line"><span style="color:#9ECBFF">    learns reward as function of (context, action_embedding).</span></span>
<span class="line"><span style="color:#9ECBFF">    Enables generalization across similar actions.</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#79B8FF"> __init__</span><span style="color:#E1E4E8">(self, context_dim, action_embedding_dim,</span></span>
<span class="line"><span style="color:#E1E4E8">                 hidden_dims</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">[</span><span style="color:#79B8FF">256</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">128</span><span style="color:#E1E4E8">], lr</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1e-3</span><span style="color:#E1E4E8">):</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Args:</span></span>
<span class="line"><span style="color:#9ECBFF">            context_dim: Context feature dimensionality</span></span>
<span class="line"><span style="color:#9ECBFF">            action_embedding_dim: Action embedding dimensionality</span></span>
<span class="line"><span style="color:#9ECBFF">            hidden_dims: Hidden layer sizes</span></span>
<span class="line"><span style="color:#9ECBFF">            lr: Learning rate</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.action_embedding_dim </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> action_embedding_dim</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Neural network: (context, action_embedding) → reward</span></span>
<span class="line"><span style="color:#E1E4E8">        input_dim </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> context_dim </span><span style="color:#F97583">+</span><span style="color:#E1E4E8"> action_embedding_dim</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#E1E4E8">        layers </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> []</span></span>
<span class="line"><span style="color:#E1E4E8">        prev_dim </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> input_dim</span></span>
<span class="line"><span style="color:#F97583">        for</span><span style="color:#E1E4E8"> hidden_dim </span><span style="color:#F97583">in</span><span style="color:#E1E4E8"> hidden_dims:</span></span>
<span class="line"><span style="color:#E1E4E8">            layers.append(nn.Linear(prev_dim, hidden_dim))</span></span>
<span class="line"><span style="color:#E1E4E8">            layers.append(nn.ReLU())</span></span>
<span class="line"><span style="color:#E1E4E8">            prev_dim </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> hidden_dim</span></span>
<span class="line"><span style="color:#E1E4E8">        layers.append(nn.Linear(prev_dim, </span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">))  </span><span style="color:#6A737D"># Scalar reward prediction</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.model </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> nn.Sequential(</span><span style="color:#F97583">*</span><span style="color:#E1E4E8">layers)</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.optimizer </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> optim.Adam(</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.model.parameters(), </span><span style="color:#FFAB70">lr</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">lr)</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.criterion </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> nn.MSELoss()</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.buffer </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> deque(</span><span style="color:#FFAB70">maxlen</span><span style="color:#F97583">=</span><span style="color:#79B8FF">10000</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> select_action</span><span style="color:#E1E4E8">(self, context, candidate_actions, action_embeddings):</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Select action from candidates using learned reward function.</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">        Args:</span></span>
<span class="line"><span style="color:#9ECBFF">            context: User/situation features</span></span>
<span class="line"><span style="color:#9ECBFF">            candidate_actions: List of action IDs to choose from</span></span>
<span class="line"><span style="color:#9ECBFF">            action_embeddings: Dict mapping action_id → embedding vector</span></span>
<span class="line"><span style="color:#9ECBFF">            </span></span>
<span class="line"><span style="color:#9ECBFF">        Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">            action: Chosen action ID</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#E1E4E8">        scores </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> []</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        with</span><span style="color:#E1E4E8"> torch.no_grad():</span></span>
<span class="line"><span style="color:#E1E4E8">            context_tensor </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.FloatTensor(context)</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#F97583">            for</span><span style="color:#E1E4E8"> action_id </span><span style="color:#F97583">in</span><span style="color:#E1E4E8"> candidate_actions:</span></span>
<span class="line"><span style="color:#6A737D">                # Get action embedding</span></span>
<span class="line"><span style="color:#E1E4E8">                action_emb </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> action_embeddings[action_id]</span></span>
<span class="line"><span style="color:#E1E4E8">                action_tensor </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.FloatTensor(action_emb)</span></span>
<span class="line"><span style="color:#E1E4E8">                </span></span>
<span class="line"><span style="color:#6A737D">                # Concatenate context + action embedding</span></span>
<span class="line"><span style="color:#E1E4E8">                combined </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.cat([context_tensor, action_tensor])</span></span>
<span class="line"><span style="color:#E1E4E8">                </span></span>
<span class="line"><span style="color:#6A737D">                # Predict reward</span></span>
<span class="line"><span style="color:#E1E4E8">                reward_pred </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.model(combined.unsqueeze(</span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">)).item()</span></span>
<span class="line"><span style="color:#E1E4E8">                scores.append(reward_pred)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Choose action with highest predicted reward</span></span>
<span class="line"><span style="color:#6A737D">        # (could add exploration here with ε-greedy or UCB)</span></span>
<span class="line"><span style="color:#E1E4E8">        best_idx </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> np.argmax(scores)</span></span>
<span class="line"><span style="color:#F97583">        return</span><span style="color:#E1E4E8"> candidate_actions[best_idx]</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> update</span><span style="color:#E1E4E8">(self, context, action_embedding, reward):</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Update model based on observed reward.</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">        Args:</span></span>
<span class="line"><span style="color:#9ECBFF">            context: Context features</span></span>
<span class="line"><span style="color:#9ECBFF">            action_embedding: Embedding of chosen action</span></span>
<span class="line"><span style="color:#9ECBFF">            reward: Observed reward</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.buffer.append((context, action_embedding, reward))</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        if</span><span style="color:#79B8FF"> len</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.buffer) </span><span style="color:#F97583">>=</span><span style="color:#79B8FF"> 32</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#79B8FF">            self</span><span style="color:#E1E4E8">._train_step()</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> _train_step</span><span style="color:#E1E4E8">(self):</span></span>
<span class="line"><span style="color:#9ECBFF">        """Train on minibatch."""</span></span>
<span class="line"><span style="color:#E1E4E8">        batch </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> random.sample(</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.buffer, </span><span style="color:#79B8FF">32</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        contexts, action_embs, rewards </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> zip</span><span style="color:#E1E4E8">(</span><span style="color:#F97583">*</span><span style="color:#E1E4E8">batch)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Concatenate context + action embeddings</span></span>
<span class="line"><span style="color:#E1E4E8">        combined </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> []</span></span>
<span class="line"><span style="color:#F97583">        for</span><span style="color:#E1E4E8"> ctx, act_emb </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> zip</span><span style="color:#E1E4E8">(contexts, action_embs):</span></span>
<span class="line"><span style="color:#E1E4E8">            combined.append(np.concatenate([ctx, act_emb]))</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#E1E4E8">        combined_tensor </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.FloatTensor(combined)</span></span>
<span class="line"><span style="color:#E1E4E8">        reward_tensor </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.FloatTensor(rewards).unsqueeze(</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Forward pass</span></span>
<span class="line"><span style="color:#E1E4E8">        predictions </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.model(combined_tensor)</span></span>
<span class="line"><span style="color:#E1E4E8">        loss </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.criterion(predictions, reward_tensor)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Backprop</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.optimizer.zero_grad()</span></span>
<span class="line"><span style="color:#E1E4E8">        loss.backward()</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.optimizer.step()</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D"># Example: E-commerce with product embeddings</span></span>
<span class="line"><span style="color:#F97583">if</span><span style="color:#79B8FF"> __name__</span><span style="color:#F97583"> ==</span><span style="color:#9ECBFF"> "__main__"</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#6A737D">    # Simulate product catalog</span></span>
<span class="line"><span style="color:#E1E4E8">    n_products </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 10000</span></span>
<span class="line"><span style="color:#E1E4E8">    context_dim </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 50</span><span style="color:#6A737D">  # User features</span></span>
<span class="line"><span style="color:#E1E4E8">    embedding_dim </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 64</span><span style="color:#6A737D">  # Product embeddings</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Generate product embeddings (in practice, from item2vec, product features, etc.)</span></span>
<span class="line"><span style="color:#E1E4E8">    product_embeddings </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#E1E4E8">        product_id: np.random.randn(embedding_dim)</span></span>
<span class="line"><span style="color:#F97583">        for</span><span style="color:#E1E4E8"> product_id </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> range</span><span style="color:#E1E4E8">(n_products)</span></span>
<span class="line"><span style="color:#E1E4E8">    }</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#E1E4E8">    bandit </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> ActionEmbeddingBandit(</span></span>
<span class="line"><span style="color:#FFAB70">        context_dim</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">context_dim,</span></span>
<span class="line"><span style="color:#FFAB70">        action_embedding_dim</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">embedding_dim,</span></span>
<span class="line"><span style="color:#FFAB70">        hidden_dims</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">[</span><span style="color:#79B8FF">256</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">128</span><span style="color:#E1E4E8">]</span></span>
<span class="line"><span style="color:#E1E4E8">    )</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    for</span><span style="color:#E1E4E8"> t </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> range</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">1000</span><span style="color:#E1E4E8">):</span></span>
<span class="line"><span style="color:#6A737D">        # User context</span></span>
<span class="line"><span style="color:#E1E4E8">        context </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> np.random.randn(context_dim)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Candidate products (e.g., from retrieval stage)</span></span>
<span class="line"><span style="color:#E1E4E8">        candidates </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> random.sample(</span><span style="color:#79B8FF">range</span><span style="color:#E1E4E8">(n_products), </span><span style="color:#79B8FF">100</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Select product</span></span>
<span class="line"><span style="color:#E1E4E8">        chosen_product </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> bandit.select_action(context, candidates, product_embeddings)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Simulate reward</span></span>
<span class="line"><span style="color:#E1E4E8">        reward </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> np.random.random()  </span><span style="color:#6A737D"># In practice: purchase, click, etc.</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Update</span></span>
<span class="line"><span style="color:#E1E4E8">        action_emb </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> product_embeddings[chosen_product]</span></span>
<span class="line"><span style="color:#E1E4E8">        bandit.update(context, action_emb, reward)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        if</span><span style="color:#E1E4E8"> t </span><span style="color:#F97583">%</span><span style="color:#79B8FF"> 100</span><span style="color:#F97583"> ==</span><span style="color:#79B8FF"> 0</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#79B8FF">            print</span><span style="color:#E1E4E8">(</span><span style="color:#F97583">f</span><span style="color:#9ECBFF">"Round </span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">t</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">: Chose product </span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">chosen_product</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">, Reward: </span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">reward</span><span style="color:#F97583">:.3f</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">"</span><span style="color:#E1E4E8">)</span></span>
<span class="line"></span></code></pre>
<h3 id="solution-2-two-stage-selection">Solution 2: Two-Stage Selection</h3>
<p><strong>Idea:</strong> First, narrow to top-K candidates (fast heuristic). Then, run bandit on top-K.</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#F97583">def</span><span style="color:#B392F0"> two_stage_selection</span><span style="color:#E1E4E8">(context, all_actions, bandit, K</span><span style="color:#F97583">=</span><span style="color:#79B8FF">100</span><span style="color:#E1E4E8">):</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#9ECBFF">    Two-stage action selection for large action spaces.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Stage 1: Fast filtering to top-K candidates</span></span>
<span class="line"><span style="color:#9ECBFF">    Stage 2: Bandit selection from candidates</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Args:</span></span>
<span class="line"><span style="color:#9ECBFF">        context: User/situation features</span></span>
<span class="line"><span style="color:#9ECBFF">        all_actions: Full action space (large)</span></span>
<span class="line"><span style="color:#9ECBFF">        bandit: Bandit algorithm instance</span></span>
<span class="line"><span style="color:#9ECBFF">        K: Number of candidates to keep</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">    Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">        action: Final selected action</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#6A737D">    # Stage 1: Fast filtering (e.g., dot product with user embedding)</span></span>
<span class="line"><span style="color:#E1E4E8">    user_embedding </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> context[:</span><span style="color:#79B8FF">64</span><span style="color:#E1E4E8">]  </span><span style="color:#6A737D"># First 64 dims</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#E1E4E8">    scores </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> []</span></span>
<span class="line"><span style="color:#F97583">    for</span><span style="color:#E1E4E8"> action </span><span style="color:#F97583">in</span><span style="color:#E1E4E8"> all_actions:</span></span>
<span class="line"><span style="color:#6A737D">        # Fast heuristic: dot product similarity</span></span>
<span class="line"><span style="color:#E1E4E8">        action_embedding </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> get_action_embedding(action)</span></span>
<span class="line"><span style="color:#E1E4E8">        score </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> np.dot(user_embedding, action_embedding)</span></span>
<span class="line"><span style="color:#E1E4E8">        scores.append((score, action))</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Keep top K</span></span>
<span class="line"><span style="color:#E1E4E8">    scores.sort(</span><span style="color:#FFAB70">reverse</span><span style="color:#F97583">=</span><span style="color:#79B8FF">True</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">    candidates </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> [action </span><span style="color:#F97583">for</span><span style="color:#E1E4E8"> _, action </span><span style="color:#F97583">in</span><span style="color:#E1E4E8"> scores[:K]]</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Stage 2: Bandit selects from candidates</span></span>
<span class="line"><span style="color:#E1E4E8">    action </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> bandit.select_action(context, candidates)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    return</span><span style="color:#E1E4E8"> action</span></span>
<span class="line"></span></code></pre>
<p><strong>Tradeoff:</strong> Speed vs optimality. Fast heuristic might filter out good actions, but makes bandits tractable.</p>
<hr>
<h2 id="algorithm-comparison-for-neural-bandits">Algorithm Comparison for Neural Bandits</h2>
<h3 id="performance-vs-complexity">Performance vs Complexity</h3>
<div class="mermaid">
graph TB
    subgraph Performance["Sample Efficiency vs Computational Cost"]
        direction LR
        
        NEG[Neural ε-greedy&#x3C;br/>━━━━━━━━&#x3C;br/>Low cost&#x3C;br/>Suboptimal efficiency]
        
        NL[NeuralLinear&#x3C;br/>━━━━━━━━&#x3C;br/>Medium cost&#x3C;br/>Good efficiency]
        
        BTS[Bootstrap TS&#x3C;br/>━━━━━━━━&#x3C;br/>High cost&#x3C;br/>Best efficiency]
    end
    
    NEG -.better.-> NL
    NL -.better.-> BTS
    
    style NEG stroke:#f59e0b,stroke-width:2px
    style NL stroke:#10b981,stroke-width:2px
    style BTS stroke:#06b6d4,stroke-width:2.5px
    style Performance stroke:#64748b,stroke-width:2px
    
    linkStyle 0,1 stroke:#10b981,stroke-width:2px,stroke-dasharray:3</div>
<h3 id="detailed-comparison">Detailed Comparison</h3>








































<table><thead><tr><th>Algorithm</th><th>Exploration</th><th>Computational Cost</th><th>Sample Efficiency</th><th>When to Use</th></tr></thead><tbody><tr><td><strong>Neural ε-greedy</strong></td><td>Random</td><td>1x (baseline)</td><td>Low</td><td>Simple baseline, quick start</td></tr><tr><td><strong>NeuralLinear</strong></td><td>Confidence bounds</td><td>1.5x (feature extraction)</td><td>Medium</td><td>Want interpretability + efficiency</td></tr><tr><td><strong>Bootstrap TS</strong></td><td>Ensemble disagreement</td><td>10-20x (K networks)</td><td>High</td><td>Need best performance, have compute</td></tr><tr><td><strong>Action Embeddings</strong></td><td>Depends on base</td><td>1x</td><td>Medium</td><td>High-dim action spaces (1000s+)</td></tr></tbody></table>
<hr>
<h2 id="key-takeaways">Key Takeaways</h2>
<p><strong>Essential concepts:</strong></p>
<p><strong>Use neural bandits when linear models fail:</strong></p>
<ul>
<li>High-dimensional raw inputs (images, text, audio)</li>
<li>Complex nonlinear reward functions</li>
<li>But need 10-100x more data than linear models</li>
</ul>
<p><strong>Uncertainty quantification is the key challenge:</strong></p>
<ul>
<li>Neural networks give point estimates, not uncertainty</li>
<li>Need uncertainty to explore intelligently</li>
<li>Three solutions: ε-greedy (random), NeuralLinear (features), Bootstrap (ensemble)</li>
</ul>
<p><strong>NeuralLinear is often the best starting point:</strong></p>
<ul>
<li>Neural features + LinUCB confidence bounds</li>
<li>Directed exploration (better than random)</li>
<li>Some interpretability (examine θ weights)</li>
</ul>
<p><strong>Bootstrap Thompson Sampling for maximum performance:</strong></p>
<ul>
<li>Trains K networks on bootstrap samples</li>
<li>Disagreement = uncertainty</li>
<li>Best sample efficiency, but K times the cost</li>
</ul>
<p><strong>High-dimensional action spaces need special handling:</strong></p>
<ul>
<li>Action embeddings (generalize across similar actions)</li>
<li>Two-stage selection (filter then select)</li>
<li>Can’t maintain separate parameters for millions of actions</li>
</ul>
<p><strong>Practical recommendations:</strong></p>

































<table><thead><tr><th>Your Situation</th><th>Recommended Approach</th></tr></thead><tbody><tr><td><strong>Quick prototype</strong></td><td>Neural ε-greedy (simplest)</td></tr><tr><td><strong>Production baseline</strong></td><td>NeuralLinear (balanced)</td></tr><tr><td><strong>Maximum performance</strong></td><td>Bootstrap TS with 10 networks</td></tr><tr><td><strong>>1000 actions</strong></td><td>Action embeddings or two-stage</td></tr><tr><td><strong>Limited compute</strong></td><td>NeuralLinear or ε-greedy</td></tr><tr><td><strong>Need interpretability</strong></td><td>NeuralLinear (can inspect θ)</td></tr></tbody></table>
<p><strong>Common pitfalls to avoid:</strong></p>
<p>❌ Using neural bandits when linear suffices (overfitting, sample inefficiency)<br>
❌ Forgetting to normalize features (neural networks are sensitive)<br>
❌ Too few bootstrap models (K &#x3C; 5 gives poor uncertainty)<br>
❌ Too many bootstrap models (K > 20 diminishing returns)<br>
❌ Not maintaining replay buffer (catastrophic forgetting)</p>
<hr>
<h2 id="further-reading">Further Reading</h2>
<p><strong>Neural bandit papers:</strong></p>
<ul>
<li><strong>Deep Bayesian Bandits Showdown</strong> (Riquelme et al., 2018) - Comprehensive comparison of neural methods. <a href="https://arxiv.org/abs/1802.09127">ICLR 2018</a></li>
<li><strong>Neural Contextual Bandits with UCB</strong> (Zhou et al., 2020) - NeuralUCB algorithm. <a href="https://arxiv.org/abs/1911.04462">ICML 2020</a></li>
<li><strong>Deep Exploration via Bootstrapped DQN</strong> (Osband et al., 2016) - Bootstrap for deep RL/bandits. <a href="https://arxiv.org/abs/1602.04621">NeurIPS 2016</a></li>
</ul>
<p><strong>Code repositories:</strong></p>
<ul>
<li><strong><a href="https://www.tensorflow.org/agents/tutorials/intro_bandit">TensorFlow Agents</a></strong>: Neural bandit implementations</li>
<li><strong><a href="https://github.com/criteo-research/reco-gym">RecoGym</a></strong>: Recommendation environment for testing neural bandits</li>
</ul>
<p><strong>Blog posts:</strong></p>
<ul>
<li><a href="https://distill.pub/2017/momentum/">Distill: Neural Network Uncertainty</a></li>
<li><a href="https://lilianweng.github.io/posts/2018-02-19-rl-overview/">Lil’Log: Deep RL</a></li>
</ul>
<hr>]]></content:encoded><enclosure url="https://www.vitorsousa.com/_astro/contextual-bandits-p2.BdxH4sfj.png" length="756000" type="image/png"/></item><item><title>When to Use Contextual Bandits: The Decision Framework</title><link>https://www.vitorsousa.com/blog/contextual-bandits/</link><guid isPermaLink="true">https://www.vitorsousa.com/blog/contextual-bandits/</guid><description>Stop running month-long A/B tests that leave value on the table. Learn when contextual bandits are the right choice for adaptive, personalized optimization—and when to stick with simpler alternatives.</description><pubDate>Thu, 13 Nov 2025 09:30:00 GMT</pubDate><content:encoded><![CDATA[<h1 id="beyond-the-vibe-check-a-systematic-approach-to-llm-evaluation">Beyond the Vibe Check: A Systematic Approach to LLM Evaluation</h1>
<p><img src="/images/beyond-the-vibe-check-a-systematic-approach-to-llm-evaluation.png" alt="Abstract evaluation dashboard illustration"></p>
<blockquote>
<p><strong>TL;DR:</strong> I’m outlining a repeatable, measurement-first approach to evaluating LLM systems so teams can ship with confidence instead of gut feeling.</p>
</blockquote>
<h2 id="-who-should-read-what">👥 Who Should Read What?</h2>
<p><strong>Executives/PMs (20 min read):</strong></p>
<ul>
<li>Introduction</li>
<li>Understanding Evaluation Dimensions</li>
<li>The Evaluation Process</li>
<li>Conclusion</li>
</ul>
<p><strong>ML Engineers (35 min read):</strong></p>
<ul>
<li>All of the above +</li>
<li>Evaluation Methods</li>
<li>Evaluation Metrics</li>
<li>Known Limitations</li>
</ul>
<p><strong>Researchers/Specialists (60 min read):</strong></p>
<ul>
<li>Everything + Appendix</li>
</ul>
<h2 id="introduction-why-llm-evaluation-is-the-critical-bottleneck-in-ai-product-development">Introduction: Why LLM evaluation is the critical bottleneck in AI product development</h2>
<p><em><strong>You’ve built an LLM-powered feature. The prompt engineering looks solid. You have tested the model responses, they seem… good? You show it to your team, they nod approvingly. SHIP IT 🚀</strong></em></p>
<p>Then users report hallucinations, irrelevant responses or other kind of failures. You’re relying on “vibe checks”—subjective impressions that don’t scale.</p>
<blockquote>
<p>This is the evaluation bottleneck strangling GENAI products development.</p>
</blockquote>
<h3 id="why-traditional-metrics-fail">Why traditional metrics fail</h3>
<p><em>BLEU</em> and <em>ROUGE</em> measure n-gram overlap. They work for constrained tasks like translation but break completely for LLMs. Check the following example:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#F97583">from</span><span style="color:#E1E4E8"> nltk.translate.bleu_score </span><span style="color:#F97583">import</span><span style="color:#E1E4E8"> sentence_bleu, SmoothingFunction</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8">reference </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> [</span><span style="color:#9ECBFF">"The company was founded in 2018"</span><span style="color:#E1E4E8">.split()]</span></span>
<span class="line"><span style="color:#E1E4E8">candidate_good </span><span style="color:#F97583">=</span><span style="color:#9ECBFF"> "2018 marked the company's founding year"</span><span style="color:#E1E4E8">.split()</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8">smooth </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> SmoothingFunction().method1</span></span>
<span class="line"><span style="color:#E1E4E8">bleu_good </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> sentence_bleu(reference, candidate_good, </span><span style="color:#FFAB70">smoothing_function</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">smooth)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#79B8FF">print</span><span style="color:#E1E4E8">(</span><span style="color:#F97583">f</span><span style="color:#9ECBFF">"BLEU score: </span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">bleu_good</span><span style="color:#F97583">:.4f</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">"</span><span style="color:#E1E4E8">)</span></span>
<span class="line"></span></code></pre>
<p><strong>Output:</strong></p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>BLEU score: 0.0408</span></span>
<span class="line"><span></span></span></code></pre>
<blockquote>
<p>💡 Even though both sentences have the same meaning, <em>BLEU</em> gives a very low score because it measures <strong>n-gram overlap</strong>, not <strong>semantic similarity</strong>. It rewards verbose responses that happen to overlap with references, even when they bury useful information.</p>
</blockquote>
<p>The deeper problem: LLMs handle open-ended tasks without single correct answers. How do you score a product description, code explanation, or story outline? Traditional metrics measure surface similarity when we need semantic correctness, relevance, and usefulness.</p>
<h3 id="the-new-complexity">The new complexity</h3>
<p>Evaluation difficulty has exploded as LLMs grew:</p>
<ul>
<li><strong>Long-context understanding</strong>: Models processing 100k+ tokens. Does your system work equally well whether the answer is in paragraph 1 or 292?</li>
<li><strong>Multi-hop reasoning</strong>: Questions requiring synthesis across sources. Your model may excel at lookups but fail at comparisons and inferences.</li>
<li><strong>Open-ended generation</strong>: Tasks where <code>correctness</code> is subjective. Is this email professional? Is this explanation clear?</li>
<li><strong>RAG Systems</strong>: Failure might be retrieval (wrong documents), synthesis (wrong answer from right documents), or both. You must evaluate the entire pipeline.</li>
</ul>
<p>Without rigorous evaluation, you can’t distinguish failure modes, prioritize improvements or know if changes help.</p>
<h3 id="three-pillars-of-systematic-evaluation">Three pillars of systematic evaluation</h3>
<h4 id="what-to-evaluate">What to evaluate</h4>
<p>A two-dimensional framework separating faithfulness (grounded and accurate) from helpfulness (relevant, comprehensive, concise). This distinction changes how you build datasets, choose metrics, and prioritize work.</p>
<h4 id="how-to-evaluate">How to evaluate</h4>
<p>Survey the methods from code-based evaluators to LLM-as-a-Judge. When to use direct scoring versus pairwise comparisons, claim-based faithfulness verification, and the significant biases in LLM-evaluators you must mitigate.</p>
<h4 id="making-it-systematic">Making it systematic</h4>
<p>Applying the scientific method to evaluation, practicing eval-driven development, and maintaining continuous monitoring with human oversight for production systems.</p>
<div class="mermaid">
graph TB
    Start["&#x3C;b>Systematic Evaluation&#x3C;/b>"]
    
    Start --> P1["&#x3C;b>What to Evaluate&#x3C;/b>"]
    Start --> P2["&#x3C;b>How to Evaluate&#x3C;/b>"]
    Start --> P3["&#x3C;b>Making it Systematic&#x3C;/b>"]
    
    P1 --> F["&#x3C;b>Faithfulness&#x3C;/b>&#x3C;br/>━━━━━━━━&#x3C;br/>grounded · accurate"]
    P1 --> H["&#x3C;b>Helpfulness&#x3C;/b>&#x3C;br/>━━━━━━━━&#x3C;br/>relevant · comprehensive"]
    
    P2 --> M1["code-based&#x3C;br/>evaluators"]
    P2 --> M2["LLM-as-Judge"]
    P2 --> M3["claim verification&#x3C;br/>bias mitigation"]
    
    P3 --> S1["scientific method&#x3C;br/>eval-driven dev"]
    P3 --> S2["continuous&#x3C;br/>monitoring"]
    P3 --> S3["human oversight"]
    
    %% Level 1: Core (darkest, neutral glow)
    style Start stroke:#64748b,stroke-width:3px
    
    %% Level 2: Pillars (mid-tone, color-coded borders)
    style P1 stroke:#10b981,stroke-width:2.5px
    style P2 stroke:#06b6d4,stroke-width:2.5px
    style P3 stroke:#8b5cf6,stroke-width:2.5px
    
    %% Level 3: Details (lighter, matching pillar colors)
    style F stroke:#10b981,stroke-width:1.5px
    style H stroke:#10b981,stroke-width:1.5px
    
    style M1 stroke:#06b6d4,stroke-width:1.5px
    style M2 stroke:#06b6d4,stroke-width:1.5px
    style M3 stroke:#06b6d4,stroke-width:1.5px
    
    style S1 stroke:#8b5cf6,stroke-width:1.5px
    style S2 stroke:#8b5cf6,stroke-width:1.5px
    style S3 stroke:#8b5cf6,stroke-width:1.5px
    
    %% Links match their source pillar color
    linkStyle 0 stroke:#10b981,stroke-width:2px
    linkStyle 1 stroke:#06b6d4,stroke-width:2px
    linkStyle 2 stroke:#8b5cf6,stroke-width:2px
    linkStyle 3,4 stroke:#10b981,stroke-width:1.5px
    linkStyle 5,6,7 stroke:#06b6d4,stroke-width:1.5px
    linkStyle 8,9,10 stroke:#8b5cf6,stroke-width:1.5px</div>
<h3 id="who-this-is-for">Who this is for</h3>
<p><strong>ML practitioners:</strong> Comprehensive reference with evaluation methods, metrics selection, bias mitigation, and production practices. Includes decision trees, comparison tables, and implementation checklists.</p>
<p><strong>Newcomers to LLM evals:</strong> Fundamentals-first approach explaining why LLM evaluation differs, core concepts with examples, progressing systematically to advanced techniques.</p>
<blockquote>
<p>Building LLM systems is often easier than measuring if they work. This guide provides systematic approaches grounded in research and production experience.</p>
</blockquote>
<h2 id="understanding-evaluation-dimensions-faithfulness-and-helpfulness">Understanding Evaluation Dimensions: Faithfulness and Helpfulness</h2>
<blockquote>
<p>When you ask <em><strong>Is this LLM output good?</strong></em>, you’re actually asking two distinct questions:</p>
<ol>
<li>Is it grounded in facts? (<em><strong>Faithfulness</strong></em>)</li>
<li>Is it useful to the user? (<em><strong>Helpfulness</strong></em>)</li>
</ol>
<p>These dimensions are independent. An output can be perfectly faithful but unhelpful. Or helpful but unfaithful. The best LLM outputs nail both.</p>
</blockquote>
<p>This two-dimensional framework changes everything about how you evaluate, build datasets, and prioritize improvements.</p>
<h3 id="the-two-dimensional-framework-for-quality-assessment">The two-dimensional framework for quality assessment</h3>
<h4 id="faithfulness-grounded-and-accurate">Faithfulness: Grounded and Accurate</h4>
<p><strong>Definition:</strong> Does the output stay grounded in the source material without adding unsupported claims?</p>
<p>Faithfulness means every claim in the response can be traced back to the provided context. It’s not about being <em>generally correct</em>—it’s about being <em>verifiably correct</em> from the given sources.</p>
<p><strong>Example of faithful vs unfaithful:</strong></p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>Context: "The Q3 2024 report shows revenue of $2.1M, up 15% from Q2."</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Faithful: "Q3 2024 revenue was $2.1M, representing a 15% increase from Q2."</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Unfaithful: "Q3 2024 revenue was $2.1M, up 15% from Q2. This growth continues </span></span>
<span class="line"><span>the company's strong performance trend since its 2020 founding."</span></span>
<span class="line"><span></span></span></code></pre>
<p>The second response adds unverified claims that aren’t in the source material—even if they happen to be true elsewhere.</p>
<p><strong>Why it matters:</strong></p>
<ul>
<li><strong>Legal/Medical domains:</strong> Unfaithful outputs create liability. A medical chatbot can’t speculate beyond documented information.</li>
<li><strong>Enterprise RAG systems:</strong> Users need to trust responses are grounded in company documents, not hallucinated.</li>
<li><strong>Fact-checking applications:</strong> The entire purpose is verification against sources.</li>
</ul>
<h5 id="distinguishing-faithfulness-from-general-correctness">Distinguishing faithfulness from general correctness</h5>
<p>An output can be <em>factually correct</em> but <em>unfaithful</em>. <strong>For example,</strong> if your LLM knows that Vizela is a city in Braga, Portugal, but the provided documents <strong>don’t</strong> mention <strong>this information</strong>, <strong>then stating</strong> “Vizela is a city in Braga, Portugal” <strong>would be</strong> unfaithful to that context**—**even though it’s true.</p>
<blockquote>
<p>Faithfulness is about <strong>groundedness in provided sources</strong>, not general world knowledge.</p>
</blockquote>
<h4 id="helpfulness-relevant-comprehensive-and-concise">Helpfulness: Relevant, Comprehensive, and Concise</h4>
<p><strong>Three sub-components in constant tension</strong>:</p>
<h5 id="relevance--does-it-address-what-the-user-actually-asked">Relevance — Does it address what the user actually asked?</h5>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>Question: "What's the return policy for electronics?"</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Relevant: "Electronics can be returned within 30 days with original packaging..."</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Irrelevant: "Our company was founded in 1995 and values customer satisfaction. </span></span>
<span class="line"><span>We offer many products including electronics, furniture, and clothing. </span></span>
<span class="line"><span>Electronics can be returned within 30 days..."</span></span>
<span class="line"><span></span></span></code></pre>
<h5 id="comprehensiveness--does-it-cover-all-important-aspects">Comprehensiveness — Does it cover all important aspects?</h5>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>Question: "How do I reset my password?"</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Incomplete: "Click 'Forgot Password' on the login page."</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Comprehensive: "Click 'Forgot Password' on the login page. Enter your email </span></span>
<span class="line"><span>address. Check your email for a reset link (may take 5 minutes). Click the link </span></span>
<span class="line"><span>and create a new password with at least 8 characters including a number and symbol."</span></span>
<span class="line"><span></span></span></code></pre>
<h5 id="conciseness--does-it-avoid-unnecessary-information">Conciseness — Does it avoid unnecessary information?</h5>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>Question: "What time does the store close?"</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Concise: "The store closes at 9 PM daily."</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Verbose: "Our store, which has been serving the community since 1995, closes </span></span>
<span class="line"><span>at 9 PM every day of the week. This closing time was established after careful </span></span>
<span class="line"><span>consideration of customer shopping patterns and employee work-life balance..."</span></span>
<span class="line"><span></span></span></code></pre>
<p><strong>The three-way tension:</strong></p>
<ul>
<li>More comprehensive → less concise</li>
<li>More concise → risk missing important details</li>
<li>More relevant → might sacrifice completeness</li>
</ul>
<p>Finding the right balance depends on your use case.</p>
<h4 id="the-inherent-tension-between-faithfulness-and-helpfulness">The inherent tension between faithfulness and helpfulness</h4>
<p>Here’s where it gets interesting: <strong>faithfulness and helpfulness often conflict</strong>.</p>
<p><strong>Scenario 1: High faithfulness, low helpfulness</strong></p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>Question: "What caused the 2008 financial crisis?"</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Context: [A single paper discussing mortgage-backed securities]</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Faithful but unhelpful: "I can only find information about mortgage-backed </span></span>
<span class="line"><span>securities. The document doesn't provide a complete picture of the crisis causes."</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Helpful but unfaithful: "The 2008 financial crisis was caused by multiple factors: </span></span>
<span class="line"><span>subprime mortgages, mortgage-backed securities, credit default swaps, deregulation, </span></span>
<span class="line"><span>and global imbalances." [Adds information beyond the provided context]</span></span>
<span class="line"><span></span></span></code></pre>
<p>The faithful response refuses to speculate. The helpful response gives a complete answer but goes beyond the source material.</p>
<p><strong>Scenario 2: The “no information” dilemma</strong></p>
<p>When the context doesn’t contain the answer, you face a choice:</p>
<ul>
<li><strong>Maximize faithfulness</strong>: “I don’t have information to answer this question” (Type I error: false negative)</li>
<li><strong>Maximize helpfulness</strong>: Generate an answer anyway (Type II error: hallucination)</li>
</ul>
<p>Neither option is universally correct. The right choice depends on domain priorities.</p>
<h4 id="domain-specific-priorities">Domain-specific priorities</h4>
<p>Different applications weigh these dimensions differently:</p>
<p><strong>Faithfulness-prioritized domains:</strong></p>
<ul>
<li><strong>Legal</strong>: Contract analysis must stay strictly grounded in document text. Speculation creates liability.</li>
<li><strong>Medical</strong>: Diagnoses and treatment recommendations must be verifiable against medical literature.</li>
<li><strong>Compliance</strong>: Regulatory guidance must cite specific rules and regulations.</li>
<li><strong>Academic research</strong>: Citations must accurately reflect source material.</li>
</ul>
<p><em>In these domains, an unhelpful but faithful response is better than a helpful but unfaithful one.</em></p>
<p><strong>Helpfulness-prioritized domains:</strong></p>
<ul>
<li><strong>Consumer chatbots</strong>: Users want their questions answered, full stop. Strict groundedness might frustrate them.</li>
<li><strong>Creative writing assistants</strong>: Generating beyond provided context is the entire point.</li>
<li><strong>General Q&#x26;A</strong>: Users expect comprehensive answers drawing on model knowledge.</li>
<li><strong>Brainstorming tools</strong>: Helpfulness through broad ideation matters more than strict source grounding.</li>
</ul>
<p><em>In these domains, moderate faithfulness tradeoffs for significantly better helpfulness might be acceptable.</em></p>
<p><strong>Balanced domains:</strong></p>
<p>Most production RAG systems need both. Enterprise search should be grounded in company docs (faithfulness) while providing complete, relevant answers (helpfulness).</p>
<p><strong>Real-world example: E-commerce Product Support</strong></p>





























<table><thead><tr><th>Use Case</th><th>Faithfulness Priority</th><th>Helpfulness Priority</th><th>Approach</th></tr></thead><tbody><tr><td>Legal compliance chatbot</td><td>High</td><td>Medium</td><td>Must cite exact return policies from terms of service</td></tr><tr><td>Shopping assistant</td><td>Medium</td><td>High</td><td>Can synthesize across product reviews and specs</td></tr><tr><td>Technical support</td><td>High</td><td>High</td><td>Needs accurate troubleshooting + complete solutions</td></tr></tbody></table>
<h3 id="key-takeaways">Key takeaways</h3>
<div class="mermaid">
graph LR
    F["&#x3C;b>Faithfulness&#x3C;/b>&#x3C;br/>━━━━━━━━&#x3C;br/>grounded · verifiable&#x3C;br/>no hallucinations"]
    H["&#x3C;b>Helpfulness&#x3C;/b>&#x3C;br/>━━━━━━━━&#x3C;br/>relevant · comprehensive&#x3C;br/>concise"]
    
    F -.tension.-> H
    H -.tension.-> F
    
    style F stroke:#10b981,stroke-width:2.5px
    style H stroke:#06b6d4,stroke-width:2.5px
    linkStyle 0,1 stroke:#ef4444,stroke-width:2px,stroke-dasharray:5</div>
<p><strong>Faithfulness</strong> = Grounded in sources, verifiable, no hallucinations<br>
<strong>Helpfulness</strong> = Relevant + Comprehensive + Concise</p>
<p>These dimensions <strong>often conflict</strong>—consciously choose priorities<br>
<strong>Domain determines</strong> which matters more<br>
<strong>Match evaluators</strong> to end users’ expectations</p>
<p>This framework guides everything: dataset building, metric selection, improvement prioritization.</p>
<hr>
<h2 id="building-evaluation-datasets-the-foundation-of-good-evals">Building Evaluation Datasets: The Foundation of Good Evals</h2>
<p>Quality evaluation starts with quality data. You can’t build reliable evaluators without representative, well-annotated examples that capture the real challenges your system will face in production.</p>
<h3 id="the-hybrid-approach-to-question-generation">The hybrid approach to question generation</h3>
<p>The most practical approach combines LLM generation with human refinement. Let LLMs draft questions at scale, then have humans polish them for quality. This gives you volume without sacrificing rigor.</p>
<p><strong>The NarrativeQA insight:</strong> One of the most important lessons from research is to generate questions from <strong>summaries or abstracts</strong> rather than full documents.[^narrativeqa] When you generate questions from complete texts, you get shallow pattern-matching queries that can be answered by simple keyword lookups. Questions from summaries force deeper comprehension because the model must understand and synthesize information from the full document.</p>
<p>Consider a financial report example. If you generate questions directly from the full document, you might get: “What was Q3 revenue?” But generate from a summary, and you’re more likely to see: “How did the revenue growth rate compare to the increase in marketing spend?” The latter requires actual reasoning, not just finding numbers.</p>
<p><strong>Diversity in question types matters immensely.</strong> Your evaluation dataset should include:</p>
<ul>
<li><strong>Factual recall</strong>: “What was Q3 2024 revenue?” — tests basic information retrieval</li>
<li><strong>Multi-hop reasoning</strong>: “Calculate the total revenue increase from Q1 to Q3, given Q1 was <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1.5</mn><mi>M</mi><mi>a</mi><mi>n</mi><mi>d</mi><mi>Q</mi><mn>2</mn><mi>w</mi><mi>a</mi><mi>s</mi></mrow><annotation encoding="application/x-tex">1.5M and Q2 was </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">1.5</span><span class="mord mathnormal" style="margin-right:0.10903em;">M</span><span class="mord mathnormal">an</span><span class="mord mathnormal">d</span><span class="mord mathnormal">Q</span><span class="mord">2</span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mord mathnormal">a</span><span class="mord mathnormal">s</span></span></span></span>1.82M” — requires combining multiple facts</li>
<li><strong>Comparative</strong>: “Which department saw bigger budget changes: Marketing or R&#x26;D?” — demands synthesis across sections</li>
<li><strong>Inference</strong>: “What does the marketing spend increase suggest about company strategy?” — tests deeper understanding</li>
<li><strong>Counterfactual</strong>: “How would profitability change if marketing spend stayed flat?” — probes reasoning ability</li>
</ul>
<p>Without this diversity, you’re testing only narrow capabilities and will miss critical failure modes.</p>
<h3 id="designing-for-robustness">Designing for robustness</h3>
<p><strong>Position bias is pervasive in long-context scenarios.</strong> Models often perform better when relevant information appears at the beginning or end of documents, struggling with information buried in the middle. This is called the “lost in the middle” problem, and it’s a real production concern.</p>
<p>Test for this systematically by creating variants of the same question where the evidence appears in different locations. Take a base document and place the critical information at the start, middle, and end. If your model’s accuracy drops significantly for middle-position evidence, you’ve identified a weakness that needs addressing.</p>
<p><strong>Multi-hop questions are your secret weapon</strong> for identifying shallow reasoning. Single-hop questions like “What was Q3 revenue?” can often be answered by glorified search. Multi-hop questions force the model to synthesize information across sections, revealing whether it’s truly understanding or just matching patterns.</p>
<p>The distinction is subtle but critical. “What was Q3 revenue?” requires finding one fact. “Given Q1 revenue of <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1.5</mn><mi>M</mi><mi>a</mi><mi>n</mi><mi>d</mi><mi>Q</mi><mn>2</mn><mi>o</mi><mi>f</mi></mrow><annotation encoding="application/x-tex">1.5M and Q2 of </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">1.5</span><span class="mord mathnormal" style="margin-right:0.10903em;">M</span><span class="mord mathnormal">an</span><span class="mord mathnormal">d</span><span class="mord mathnormal">Q</span><span class="mord">2</span><span class="mord mathnormal">o</span><span class="mord mathnormal" style="margin-right:0.10764em;">f</span></span></span></span>1.82M, what was the total increase by Q3?” requires finding Q3 revenue, performing arithmetic, and combining multiple data points. The second question tests capabilities the first doesn’t touch.</p>
<h3 id="human-annotation-the-discipline-that-makes-or-breaks-evals">Human annotation: The discipline that makes or breaks evals</h3>
<p><strong>Calibration sessions are non-negotiable.</strong> Before full annotation begins, run calibration sessions where annotators label the same examples and discuss disagreements. This aligns everyone on what “faithful” and “helpful” actually mean in practice, not just in theory.</p>
<p>Your annotation guidelines should be concrete, not abstract. Instead of “label outputs as faithful or unfaithful,” provide decision rules:</p>
<ul>
<li>
<p><strong>Faithfulness</strong>: Every claim must trace to the document. If even one sentence contains unsupported information—even if factually correct from general knowledge—mark it UNFAITHFUL. When in doubt, mark FAIL and discuss.</p>
</li>
<li>
<p><strong>Helpfulness</strong>: The response must be relevant (addresses the actual question), comprehensive (covers key points), and concise (no unnecessary verbosity). Missing any dimension means marking it as unhelpful.</p>
</li>
</ul>
<p><strong>The 50:50 balance is critical for training evaluator models.</strong> If your dataset has 90% good examples and 10% bad ones, models will learn to just predict “good” for everything and achieve 90% accuracy. Deliberately curate equal numbers of passing and failing examples so the model learns to discriminate.</p>
<p><strong>Inter-annotator agreement quantifies annotation quality.</strong> Cohen’s kappa (κ) measures how much annotators agree beyond chance. Target κ > 0.60 for production use—anything lower suggests your guidelines are unclear or the task is too subjective.[^cohen-kappa]</p>
<p>Here’s how to interpret kappa scores:</p>



































<table><thead><tr><th><strong>Cohen’s κ</strong></th><th><strong>Interpretation</strong></th><th><strong>Action</strong></th></tr></thead><tbody><tr><td>&#x3C; 0.20</td><td>Poor agreement</td><td>Fix your guidelines immediately</td></tr><tr><td>0.21 - 0.40</td><td>Fair agreement</td><td>Still needs significant work</td></tr><tr><td>0.41 - 0.60</td><td>Moderate agreement</td><td>Acceptable for research, marginal for production</td></tr><tr><td>0.61 - 0.80</td><td>Substantial agreement</td><td>Good for production</td></tr><tr><td>> 0.80</td><td>Almost perfect agreement</td><td>Ideal but rare for subjective tasks</td></tr></tbody></table>
<p>If you’re below 0.60, don’t proceed to full annotation. Revisit your guidelines, add clarifying examples, and re-calibrate.</p>
<h3 id="the-practical-checklist">The practical checklist</h3>
<p>Before considering your evaluation dataset complete, verify:</p>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" disabled> <strong>Questions generated from summaries</strong>, not full texts — forces deeper comprehension</li>
<li class="task-list-item"><input type="checkbox" disabled> <strong>Diverse question types</strong> — factual, multi-hop, inference, comparative, counterfactual</li>
<li class="task-list-item"><input type="checkbox" disabled> <strong>Position bias testing</strong> — evidence at document start, middle, and end</li>
<li class="task-list-item"><input type="checkbox" disabled> <strong>Balanced pass/fail distribution</strong> — 50:50 split for model training</li>
<li class="task-list-item"><input type="checkbox" disabled> <strong>Clear annotation guidelines</strong> — concrete decision rules, not vague principles</li>
<li class="task-list-item"><input type="checkbox" disabled> <strong>Measured inter-annotator agreement</strong> — Cohen’s κ > 0.60</li>
<li class="task-list-item"><input type="checkbox" disabled> <strong>Human review of LLM-generated questions</strong> — aim for ~80% acceptance rate after refinement</li>
</ul>
<blockquote>
<p><strong>Common Mistake: Skipping Dataset Quality Checks</strong></p>
<p>Teams often rush to build evaluators without validating their dataset first.
If your inter-annotator agreement is low (κ &#x3C; 0.60), your evaluators will be unreliable
no matter how sophisticated the method. Fix the foundation first.</p>
</blockquote>
<p>Building evaluation datasets is tedious work. There’s no shortcut. But this foundation determines everything downstream—the quality of your evaluators, your ability to measure improvements, and ultimately whether users can trust your system. Get this right, and the rest becomes manageable. Skip it, and you’re building on sand.</p>
<h3 id="key-takeaways-building-evaluation-datasets">Key Takeaways: Building Evaluation Datasets</h3>
<p>Use LLMs to generate questions, humans to refine them<br>
Generate from summaries, not full documents (forces deeper reasoning)<br>
Include diverse question types (factual, multi-hop, comparative, inference)<br>
Test for position bias systematically<br>
Maintain 50:50 pass/fail balance<br>
Target Cohen’s κ > 0.60 for inter-annotator agreement<br>
Never skip calibration sessions</p>
<hr>
<h2 id="evaluation-methods-from-traditional-metrics-to-llm-as-judge">Evaluation Methods: From Traditional Metrics to LLM-as-Judge</h2>
<p>Start simple, add complexity only when needed. The best evaluation approach is the simplest one that actually works for your use case.</p>
<h3 id="code-based-evaluators-your-first-line-of-defense">Code-based evaluators: Your first line of defense</h3>
<p>Before jumping to LLM-evaluators, implement straightforward programmatic checks. These are fast, deterministic, and completely transparent—you know exactly what they’re checking and why they pass or fail.</p>
<p><strong>Format validation</strong> catches structural issues immediately. Does the response return valid JSON? Are required fields present? Is the output parseable? These checks cost nothing to run and catch obvious failures before they reach users.</p>
<p><strong>Citation verification</strong> ensures your system is grounded. If your application requires references to source material, a simple regex can confirm citations are present. You’re not validating correctness yet—just that the system attempted to ground its response.</p>
<p><strong>Length bounds</strong> prevent degenerate outputs. A response with 3 words probably didn’t answer the question. A response with 5,000 words for “What time does the store close?” is clearly broken. Sanity-check that outputs fall within reasonable ranges.</p>
<p><strong>Consistency checks</strong> validate internal logic. If your system returns confidence scores, do they sum to 1.0? If it provides a numerical answer, does it match the format specified? These deterministic checks catch bugs that LLM-evaluators might miss.</p>
<p>The principle here is simple: <strong>use the simplest approach that works.</strong> Code-based checks are fast, cheap, and reliable. Don’t reach for complex LLM evaluation when a 5-line function solves the problem.</p>
<p><strong>When code-based evaluation is sufficient:</strong></p>









































<table><thead><tr><th>Check Type</th><th>Example</th><th>Cost</th><th>Speed</th></tr></thead><tbody><tr><td>Format validation</td><td>JSON structure, required fields</td><td>Free</td><td>Instant</td></tr><tr><td>Length constraints</td><td>10-500 words for summaries</td><td>Free</td><td>Instant</td></tr><tr><td>Citation presence</td><td>Contains [1], [2] references</td><td>Free</td><td>Instant</td></tr><tr><td>Regex patterns</td><td>Email format, date format</td><td>Free</td><td>Instant</td></tr><tr><td>Consistency checks</td><td>Confidence scores sum to 1.0</td><td>Free</td><td>Instant</td></tr></tbody></table>
<h3 id="llm-evaluators-three-core-approaches">LLM-evaluators: Three core approaches</h3>
<p>When you need semantic understanding—when correctness depends on meaning, not just structure—LLM-evaluators become necessary. But not all LLM evaluation methods are created equal.</p>
<h4 id="direct-scoring-best-for-objective-assessments">Direct scoring: Best for objective assessments</h4>
<p><strong>Direct scoring</strong> means giving the LLM a single output and asking it to judge quality. This works well for relatively objective criteria where there’s a clear right or wrong answer.</p>
<p>Faithfulness evaluation is the canonical example. You provide the context, the generated answer, and ask: “Is every claim in this answer supported by the context?” This is verifiable—either the claims trace back to the source or they don’t.</p>
<p>The key is being specific about what you’re evaluating. Don’t ask “Is this output good?”—that’s too vague and subjective. Ask “Does this answer contain any unsupported claims?” or “Does this response answer the question that was asked?” Give the LLM concrete, verifiable criteria.</p>
<p><strong>When direct scoring works well:</strong></p>
<ul>
<li>Faithfulness (groundedness in source material)</li>
<li>Toxicity detection (clear policy violations)</li>
<li>Format correctness (follows specified structure)</li>
<li>Policy compliance (adheres to defined rules)</li>
<li>Binary questions with objective answers</li>
</ul>
<p><strong>Limitations:</strong> Direct scoring struggles with subjective qualities. Is this explanation “clear”? Is this email “professional”? Different evaluators—human or LLM—will disagree substantially on these judgments.</p>
<h4 id="pairwise-comparison-more-reliable-for-subjective-quality">Pairwise comparison: More reliable for subjective quality</h4>
<p><strong>Pairwise comparison</strong> asks the LLM to choose which of two responses is better. This is consistently more reliable than direct scoring for subjective assessments.[^judging-llm-judge]</p>
<p>Why does pairwise work better? Comparative judgments are easier than absolute ones. It’s hard to say “This email is 7/10 professional,” but easy to say “Email A is more professional than Email B.” Humans and LLMs both make more consistent judgments when comparing directly.</p>
<p>This approach is essential for A/B testing. You’re not asking “Is variant B good?”—you’re asking “Is variant B better than our baseline A?” That’s the question that actually matters for shipping decisions.</p>
<p><strong>The critical detail: position bias mitigation.</strong> LLMs exhibit strong position bias, preferring the first response 50-70% of the time regardless of quality.[^position-bias] This isn’t subtle—it’s a massive systematic error that invalidates naive pairwise evaluation.</p>
<p>The solution is simple but non-negotiable: evaluate both orderings. Compare A then B, then compare B then A. If the LLM picks A in the first comparison and A in the second (when A is in the second position), you have high confidence A is actually better. If results flip based on position, you’ve detected bias and should treat it as a tie or require human review.</p>
<p><strong>When pairwise comparison excels:</strong></p>
<ul>
<li>Subjective quality assessments (coherence, clarity, persuasiveness)</li>
<li>A/B testing (comparing system variants)</li>
<li>Ranking outputs (determining relative quality)</li>
<li>Cases where absolute scoring is unreliable</li>
</ul>
<p><strong>Limitations:</strong> Requires reference responses to compare against. Can’t use pairwise when you only have one output. And you must run both orderings, doubling evaluation cost.</p>
<h4 id="reference-based-evaluation-when-you-have-gold-standards">Reference-based evaluation: When you have gold standards</h4>
<p><strong>Reference-based evaluation</strong> compares generated outputs to human-written gold standards. If you have high-quality reference answers, this is powerful—you’re measuring how closely the system matches expert judgment.</p>
<p>The LLM evaluator assesses whether the generated answer captures the key information from the reference. Not word-for-word matching (that’s what BLEU does, and we’ve established it fails for LLMs), but semantic alignment.</p>
<p>This works when references are available and high-quality. In practice, that’s rarer than you’d hope. Creating good reference answers is expensive—it requires domain experts, careful review, and ongoing maintenance as your system evolves.</p>
<p><strong>When reference-based makes sense:</strong></p>
<ul>
<li>You have expert-written gold standard answers</li>
<li>The task has relatively deterministic correct answers</li>
<li>You can afford ongoing reference maintenance</li>
<li>You’re evaluating research systems with curated datasets</li>
</ul>
<p><strong>Limitations:</strong> Expensive to create and maintain references. May penalize valid alternative answers that differ from the reference but are equally correct.</p>
<h3 id="advanced-prompting-techniques-for-better-evaluation">Advanced prompting techniques for better evaluation</h3>
<p>Once you’ve chosen direct scoring, pairwise, or reference-based evaluation, you can improve accuracy through better prompting.</p>
<h4 id="chain-of-thought-reasoning-before-judging">Chain-of-Thought: Reasoning before judging</h4>
<p><strong>Chain-of-Thought (CoT)</strong> prompting asks the LLM to explain its reasoning before rendering judgment. Instead of jumping directly to “FAITHFUL” or “UNFAITHFUL,” you ask it to think step-by-step.</p>
<p>For faithfulness evaluation, this looks like:</p>
<ol>
<li>List all claims in the answer</li>
<li>For each claim, find supporting evidence in the context</li>
<li>Mark any unsupported claims</li>
<li>Final verdict: FAITHFUL or UNFAITHFUL</li>
</ol>
<p>Research shows CoT improves accuracy by 10-15% across various evaluation tasks.[^geval] The tradeoff is clear: you’re doubling latency and cost because the LLM must generate reasoning text before the final judgment. For high-stakes production evaluation, this is often worth it. For rapid iteration during development, the overhead may be prohibitive.</p>
<h4 id="few-shot-prompting-learning-by-example">Few-shot prompting: Learning by example</h4>
<p><strong>Few-shot prompting</strong> provides example evaluations before asking the LLM to judge new cases. This is especially powerful when your evaluation criteria are nuanced or domain-specific.</p>
<p>Instead of just defining “faithful,” you show examples:</p>
<ul>
<li><strong>Example 1</strong>: Context says “Revenue was 2M in Q3.” Answer says “Q3 revenue was 2M.” → FAITHFUL (exact match)</li>
<li><strong>Example 2</strong>: Context says “Revenue was 2M in Q3.” Answer says “Q3 revenue was 2M, up 20% YoY.” → UNFAITHFUL (YoY claim unsupported)</li>
</ul>
<p>The LLM learns the boundaries of your criteria from examples, not just abstract definitions. This typically improves consistency and accuracy, especially for edge cases.</p>
<p>The challenge is selecting good examples. They should cover common failure modes, clarify ambiguous cases, and represent the diversity of your data. Poor examples can bias the evaluator or teach the wrong lessons.</p>
<h4 id="cross-examination-multi-turn-probing-for-high-recall-detection">Cross-examination: Multi-turn probing for high-recall detection</h4>
<p><strong>Cross-examination</strong> uses multi-turn dialogue to probe for issues. Instead of a single evaluation pass, you have the LLM evaluator question the response multiple times, digging deeper with each round.</p>
<p>This achieves significantly higher recall on defect detection—catching 75-84% of hallucinations compared to 30-60% for single-pass evaluation. The reason is intuitive: subtle inconsistencies or unsupported claims often require multiple angles of inquiry to surface.</p>
<p>The cost is substantial: 3-5x more expensive than single-pass evaluation due to multiple LLM calls. Reserve this for critical production monitoring or high-stakes domains where missing a hallucination has serious consequences.</p>
<p><strong>When to use cross-examination:</strong></p>
<ul>
<li>Legal or medical domains (high consequence of errors)</li>
<li>Final production monitoring (cost justified by importance)</li>
<li>Investigating specific failure cases in detail</li>
</ul>
<p><strong>When to skip it:</strong></p>
<ul>
<li>Rapid development iteration (too slow and expensive)</li>
<li>High-volume evaluation (cost prohibitive)</li>
<li>Lower-stakes applications (single-pass sufficient)</li>
</ul>
<h3 id="choosing-your-evaluation-method-a-practical-guide">Choosing your evaluation method: A practical guide</h3>
<div class="mermaid">
graph TD
    Start{Can code&#x3C;br/>check it?}
    
    Start -->|Yes| Code[Use code-based&#x3C;br/>evaluators&#x3C;br/>Fast, free, deterministic]
    
    Start -->|No| Semantic{Need semantic&#x3C;br/>understanding?}
    
    Semantic -->|Yes| Type{Objective or&#x3C;br/>subjective?}
    
    Type -->|Objective| Direct[Direct Scoring&#x3C;br/>Faithfulness, toxicity&#x3C;br/>policy compliance]
    
    Type -->|Subjective| Pair[Pairwise Comparison&#x3C;br/>A/B testing, quality&#x3C;br/>Must mitigate position bias]
    
    Type -->|Have gold standard?| Ref[Reference-Based&#x3C;br/>Compare to expert answers&#x3C;br/>Expensive to maintain]
    
    style Start stroke:#64748b,stroke-width:2px
    style Semantic stroke:#64748b,stroke-width:2px
    style Type stroke:#64748b,stroke-width:2px
    
    style Code stroke:#10b981,stroke-width:2px
    style Direct stroke:#06b6d4,stroke-width:2px
    style Pair stroke:#f59e0b,stroke-width:2px
    style Ref stroke:#8b5cf6,stroke-width:2px</div>
<p>The decision tree is simpler than it appears:</p>
<p><strong>Start with code-based checks</strong> for anything deterministic. Format validation, length bounds, citation presence, consistency rules. These catch obvious issues at zero cost.</p>
<p><strong>Use direct scoring with LLM-evaluators</strong> for objective semantic judgments. Faithfulness, toxicity, policy compliance—cases where there’s a verifiable ground truth.</p>
<p><strong>Switch to pairwise comparison</strong> for subjective quality. Coherence, clarity, professionalism—anything where absolute scoring is unreliable but relative comparison works.</p>
<p><strong>Add reference-based evaluation</strong> only if you have high-quality gold standards and can maintain them.</p>
<p><strong>Layer on CoT prompting</strong> when accuracy matters more than cost. Development and critical production use cases.</p>
<p><strong>Use few-shot examples</strong> when criteria are nuanced or domain-specific. Complex evaluation tasks benefit from learning by example.</p>
<p><strong>Reserve cross-examination</strong> for high-stakes production monitoring where missing errors is costly.</p>
<p>The pattern is clear: start simple, add complexity only where it demonstrably improves results. Every additional technique trades cost and latency for accuracy. Make those trades consciously based on your actual requirements, not what sounds sophisticated.</p>
<h3 id="key-takeaways-evaluation-methods">Key Takeaways: Evaluation Methods</h3>
<p>Start with code-based checks (free, fast, deterministic)<br>
Use direct scoring for objective criteria (faithfulness, compliance)<br>
Use pairwise comparison for subjective quality (always mitigate position bias!)<br>
Add CoT when accuracy > cost (10-15% improvement, 2x latency)<br>
Use few-shot for nuanced criteria (domain-specific evaluation)<br>
Reserve cross-examination for high-stakes domains (3-5x cost, 75-84% recall)</p>
<hr>
<h2 id="evaluation-metrics-measuring-what-matters">Evaluation Metrics: Measuring What Matters</h2>
<p>Choose metrics based on your evaluation goals and task type. The right metric answers the operational question you actually care about.</p>
<h3 id="classification-metrics-for-production">Classification metrics for production</h3>
<p><strong>Classification metrics are most actionable for production systems</strong>—they directly answer operational questions that drive decisions.</p>
<p>When you evaluate LLM outputs as pass/fail (faithful/unfaithful, helpful/unhelpful, safe/unsafe), you’re doing binary classification. The standard metrics apply: precision, recall, F1, and accuracy. But which one matters depends entirely on your use case.</p>
<p><strong>Precision answers:</strong> “When my evaluator flags an output as bad, how often is it actually bad?” High precision means few false alarms. Your team isn’t wasting time investigating false positives.</p>
<p><strong>Recall answers:</strong> “What percentage of truly bad outputs does my evaluator catch?” High recall means you’re not missing problems. Users aren’t seeing failures that slipped through.</p>
<p><strong>F1 balances both:</strong> It’s the harmonic mean of precision and recall. Use it when you need both to be reasonably good and don’t have a strong preference for one over the other.</p>
<p><strong>Accuracy is usually misleading</strong> for evaluation tasks. If 95% of your outputs are good, an evaluator that always predicts “good” gets 95% accuracy while catching zero problems. Accuracy only makes sense when your dataset has balanced pass/fail distribution.</p>
<p>The critical insight: <strong>different use cases demand different metric priorities.</strong></p>





























<table><thead><tr><th>Use Case</th><th>Priority Metric</th><th>Target</th><th>Why</th></tr></thead><tbody><tr><td><strong>Guardrail systems</strong></td><td>Recall</td><td>> 0.95</td><td>Must catch harmful outputs; false positives acceptable</td></tr><tr><td><strong>Ranking systems</strong></td><td>Precision</td><td>> 0.85</td><td>Only promote quality; false negatives acceptable</td></tr><tr><td><strong>Monitoring systems</strong></td><td>F1 Score</td><td>> 0.75</td><td>Balance both; avoid alert fatigue</td></tr></tbody></table>
<div class="mermaid">
graph TD
    Start{What's your&#x3C;br/>use case?}
    
    Start -->|Guardrail| G[Prioritize Recall&#x3C;br/>━━━━━━━━&#x3C;br/>Target: >0.95&#x3C;br/>Must catch harmful outputs]
    Start -->|Ranking| R[Prioritize Precision&#x3C;br/>━━━━━━━━&#x3C;br/>Target: >0.85&#x3C;br/>Only promote quality]
    Start -->|Monitoring| M[Balance: F1 Score&#x3C;br/>━━━━━━━━&#x3C;br/>Target: >0.75&#x3C;br/>Avoid alert fatigue]
    
    style Start stroke:#64748b,stroke-width:2px
    style G stroke:#ef4444,stroke-width:2px
    style R stroke:#f59e0b,stroke-width:2px
    style M stroke:#8b5cf6,stroke-width:2px</div>
<p><strong>Guardrail systems prioritize recall.</strong> You’re preventing harmful outputs from reaching users. Missing a toxic response is catastrophic; a false positive just means one extra human review. You need to catch 95%+ of problems even if that means flagging some good outputs.</p>
<p><strong>Ranking systems prioritize precision.</strong> You’re ordering results by quality to show users the best responses first. A false positive means showing a bad result prominently, degrading user experience. Better to be conservative and only promote responses you’re confident are good.</p>
<p><strong>Monitoring systems need balance.</strong> You’re tracking system health over time. Low recall means problems go undetected. Low precision means alert fatigue—your team starts ignoring notifications because most are false alarms. F1 score is usually the right target here.</p>
<p><strong>AUROC (Area Under ROC Curve)</strong> is useful when you have confidence scores, not just binary predictions. It measures how well your evaluator separates good from bad across all possible thresholds. AUROC of 0.90+ indicates strong discriminative ability.</p>
<blockquote>
<p><strong>Common Mistake: Trusting Accuracy Alone</strong></p>
<p>Accuracy of 95% sounds great, but if your data is 95% positive examples,
a model that always predicts “positive” gets 0.95 accuracy while being useless.
Always check precision and recall separately for imbalanced data.</p>
</blockquote>
<h3 id="correlation-metrics-for-alignment">Correlation metrics for alignment</h3>
<p>Correlation metrics measure how well LLM-evaluators agree with human judgment. These are common in research but less actionable for production than classification metrics.</p>
<p><strong>Cohen’s kappa</strong> is the gold standard for measuring agreement. Unlike simple percent agreement, kappa adjusts for chance agreement.[^cohen-kappa] Two evaluators who randomly guess will occasionally agree just by luck—kappa accounts for this.</p>
<p>The interpretation scale is well-established:</p>



































<table><thead><tr><th><strong>Cohen’s κ</strong></th><th><strong>Interpretation</strong></th><th><strong>Action</strong></th></tr></thead><tbody><tr><td>&#x3C; 0.20</td><td>Poor agreement</td><td>Fix guidelines immediately</td></tr><tr><td>0.21 - 0.40</td><td>Fair agreement</td><td>Needs significant work</td></tr><tr><td>0.41 - 0.60</td><td>Moderate agreement</td><td>Marginal for production</td></tr><tr><td>0.61 - 0.80</td><td>Substantial agreement</td><td>Good for production</td></tr><tr><td>> 0.80</td><td>Almost perfect agreement</td><td>Ideal (rare for subjective tasks)</td></tr></tbody></table>
<p><strong>Target kappa > 0.60 for production use.</strong> Below this threshold, your LLM-evaluator is insufficiently aligned with human judgment to be trustworthy. Above 0.60, you have substantial agreement—good enough for most applications.</p>
<p><strong>Spearman’s rho and Kendall’s tau</strong> measure correlation for ranked or scored data. If humans rate outputs on a 1-5 scale and your LLM-evaluator does the same, these metrics tell you how well the rankings correlate.</p>
<p>The problem: they don’t adjust for chance agreement. Two random rankings will show some positive correlation just by luck. This leads to overoptimistic estimates compared to kappa.</p>
<p><strong>Use Cohen’s kappa whenever possible.</strong> It’s more conservative and more honest about agreement. Reserve Spearman’s rho and Kendall’s tau for cases where kappa doesn’t apply—continuous scores rather than discrete categories.</p>
<h3 id="metric-selection-decision-tree">Metric selection decision tree</h3>
<div class="mermaid">
graph LR
    Start{Evaluation&#x3C;br/>Type?}
    
    Start -->|Direct Scoring| DS{Your Goal?}
    Start -->|Pairwise Comparison| PC{Your Goal?}
    
    DS -->|Production Guardrail| Rec[Recall&#x3C;br/>Catch all failures]
    DS -->|Production Ranking| Prec[Precision&#x3C;br/>Avoid false positives]
    DS -->|Production Monitoring| F1[F1 Score&#x3C;br/>Balance both]
    DS -->|Evaluator Alignment| K1[Cohen's κ&#x3C;br/>vs human labels]
    
    PC -->|A/B Testing| Win[Win Rate&#x3C;br/>Which variant better?]
    PC -->|Evaluator Alignment| K2[Cohen's κ&#x3C;br/>Pairwise agreement]
    
    style Start stroke:#64748b,stroke-width:2px
    style DS stroke:#10b981,stroke-width:2px
    style PC stroke:#06b6d4,stroke-width:2px
    
    style Rec stroke:#ef4444,stroke-width:2px
    style Prec stroke:#f59e0b,stroke-width:2px
    style F1 stroke:#8b5cf6,stroke-width:2px
    style K1 stroke:#10b981,stroke-width:2px
    style Win stroke:#06b6d4,stroke-width:2px
    style K2 stroke:#10b981,stroke-width:2px</div>
<h3 id="the-metrics-you-should-actually-track">The metrics you should actually track</h3>
<p>In production, track these metrics continuously:</p>
<p><strong>For your LLM system outputs:</strong></p>
<ul>
<li><strong>Failure rate</strong>: Percentage flagged by evaluators</li>
<li><strong>Category breakdown</strong>: Faithfulness failures vs helpfulness failures</li>
<li><strong>Temporal trends</strong>: Is quality improving or degrading?</li>
<li><strong>User feedback alignment</strong>: Do users agree with evaluator judgments?</li>
</ul>
<p><strong>For your evaluators themselves:</strong></p>
<ul>
<li><strong>Recall on validation set</strong>: Performance on known failure cases with ground truth</li>
<li><strong>Precision on production samples</strong>: Human review of flagged outputs</li>
<li><strong>Cohen’s kappa vs humans</strong>: Monthly calibration checks</li>
<li><strong>Consistency over time</strong>: Are judgments stable?</li>
</ul>
<div class="mermaid">graph TB
    subgraph sub1[" "]
        System[LLM System&#x3C;br/>Outputs]
        System --> M1[Failure Rate]
        System --> M2[Category&#x3C;br/>Breakdown]
        System --> M3[Temporal&#x3C;br/>Trends]
    end
    
    subgraph sub2[" "]
        Eval[Evaluator&#x3C;br/>Quality]
        Eval --> M4[Recall on&#x3C;br/>Validation]
        Eval --> M5[Precision on&#x3C;br/>Production]
        Eval --> M6[Cohen's κ&#x3C;br/>vs Humans]
    end
    
    style System stroke:#10b981,stroke-width:2px
    style Eval stroke:#06b6d4,stroke-width:2px
    
    style M1 stroke:#10b981,stroke-width:1.5px
    style M2 stroke:#10b981,stroke-width:1.5px
    style M3 stroke:#10b981,stroke-width:1.5px
    
    style M4 stroke:#06b6d4,stroke-width:1.5px
    style M5 stroke:#06b6d4,stroke-width:1.5px
    style M6 stroke:#06b6d4,stroke-width:1.5px
    
    style sub1 stroke:none
    style sub2 stroke:none</div>
<p>The pattern is clear: <strong>choose metrics that answer your actual operational questions.</strong> “Is my system catching harmful outputs?” needs recall. “Are my quality rankings trustworthy?” needs precision. “Is my evaluator aligned with human judgment?” needs Cohen’s kappa.</p>
<p>Don’t report metrics because they’re common in papers. Report metrics that drive decisions. If a metric going up or down doesn’t change what you do, stop tracking it.</p>
<h3 id="key-takeaways-evaluation-metrics">Key Takeaways: Evaluation Metrics</h3>
<p>Choose metrics based on operational questions, not what sounds sophisticated<br>
Guardrails → Recall (catch all failures)<br>
Ranking → Precision (avoid false positives)<br>
Monitoring → F1 (balance both)<br>
Evaluator alignment → Cohen’s κ > 0.60<br>
Track both system outputs AND evaluator quality</p>
<hr>
<h2 id="known-limitations-and-biases-in-llm-evaluators">Known Limitations and Biases in LLM-Evaluators</h2>
<p>LLM-evaluators are powerful but not perfect. Understanding their limitations is critical for proper use in production systems.</p>
<h3 id="the-performance-reality-check">The performance reality check</h3>
<p><strong>LLM-evaluators correlate moderately with humans</strong>, but significant gaps remain. This isn’t a minor caveat—it’s a fundamental limitation that shapes how you should deploy these tools.</p>
<p>Human annotators typically achieve 0.80-0.90 inter-annotator agreement (measured by Cohen’s kappa) on well-defined tasks. LLM-evaluators achieve 0.30-0.60 correlation with human judgment—substantially lower.[^llm-bias] This means LLM-evaluators miss patterns humans catch and sometimes flag issues humans don’t see.</p>
<p><strong>Recall on defect detection is particularly concerning.</strong> When researchers test LLM-evaluators’ ability to catch factual inconsistencies and hallucinations, they find 30-60% recall in single-pass evaluation. Your evaluator is missing 40-70% of actual problems. Cross-examination techniques can push this to 75-84%, but at 3-5x the cost.</p>
<p>The implication: <strong>LLM-evaluators are not a replacement for human oversight in critical domains.</strong> They’re a scaling mechanism that catches some issues automatically, flagging candidates for human review. Treat them as a first-pass filter, not a final arbiter.</p>
<div class="mermaid">
graph LR
    H[Human-Human&#x3C;br/>Agreement&#x3C;br/>κ: 0.80-0.90]
    
    L[LLM-Human&#x3C;br/>Correlation&#x3C;br/>κ: 0.30-0.60]
    
    R[Defect Detection&#x3C;br/>Recall&#x3C;br/>30-60% single-pass&#x3C;br/>75-84% cross-exam]
    
    H -.substantial gap.-> L
    L -.limited recall.-> R
    
    style H stroke:#10b981,stroke-width:2px
    style L stroke:#f59e0b,stroke-width:2px
    style R stroke:#ef4444,stroke-width:2px
    
    linkStyle 0,1 stroke:#ef4444,stroke-width:2px,stroke-dasharray:5</div>
<h3 id="systematic-biases-you-must-address">Systematic biases you must address</h3>
<p>LLM-evaluators exhibit predictable, measurable biases. These aren’t occasional quirks—they’re systematic patterns that corrupt evaluation if left unmitigated.</p>
<h4 id="position-bias-the-50-70-first-position-preference">Position bias: The 50-70% first-position preference</h4>
<p><strong>Position bias is the most severe and well-documented issue</strong> in pairwise comparison.[^position-bias] LLMs prefer whichever response appears first in the prompt 50-70% of the time, regardless of actual quality.</p>
<p>This isn’t subtle. If you run naive pairwise evaluation without mitigation, your results are dominated by prompt ordering rather than quality differences. You’ll think variant A is better when it’s actually just appearing first more often.</p>
<p>The research is unambiguous: multiple studies across different models and tasks consistently find this bias.[^judging-llm-judge][^portia] It affects GPT-4, Claude, Llama, and every other model tested. It’s not going away.</p>
<p><strong>Mitigation is non-negotiable:</strong> Evaluate both orderings (A then B, B then A) and aggregate results. If judgments flip based on position, treat it as a tie or flag for human review. This doubles evaluation cost but it’s the only way to get valid results.</p>
<h4 id="verbosity-bias-preferring-longer-responses-over-90-of-the-time">Verbosity bias: Preferring longer responses over 90% of the time</h4>
<p><strong>LLM-evaluators systematically prefer longer responses</strong>, even when the shorter response is objectively better. In controlled tests with identical content at different lengths, longer versions win over 90% of the time.[^llm-bias]</p>
<p>This creates perverse incentives. If your system learns that verbose outputs score higher with LLM-evaluators, it will generate unnecessarily long responses. Users get worse experiences while your metrics claim improvement.</p>
<p>The problem compounds with pairwise comparison. When comparing a concise, complete answer to a verbose, rambling one with the same information, LLM-evaluators favor verbosity. This directly contradicts the “conciseness” criterion in helpfulness.</p>
<p><strong>Mitigation strategies:</strong></p>
<ul>
<li>Evaluate based on specific criteria (faithfulness, relevance, completeness) rather than vague “overall quality”</li>
<li>Use reference-based evaluation when you have concise gold standards</li>
<li>Add explicit length penalties in your evaluation rubric</li>
<li>Monitor response length distributions over time—increasing length may signal verbosity bias</li>
</ul>
<h4 id="self-enhancement-bias-10-25-higher-win-rates-for-own-outputs">Self-enhancement bias: 10-25% higher win rates for own outputs</h4>
<p><strong>Models rate their own outputs more favorably</strong> than competing models’ outputs. When GPT-4 evaluates GPT-4 vs Claude outputs, GPT-4 wins ~60% of pairwise comparisons. When Claude evaluates the same pairs, Claude wins ~55%.[^llm-bias]</p>
<p>This 10-25% self-preference isn’t explained by objective quality differences—it’s systematic bias. Using the same model for generation and evaluation inflates your quality estimates.</p>
<p><strong>The solution is simple: use a third-party model for evaluation.</strong> If your production system uses GPT-4, evaluate with Claude or vice versa. This adds cost and complexity, but it’s necessary for unbiased assessment.</p>
<h4 id="over-reasoning-injecting-background-knowledge-and-excessive-critique">Over-reasoning: Injecting background knowledge and excessive critique</h4>
<p><strong>LLM-evaluators often bring in information beyond the provided context</strong>, similar to how they hallucinate in generation tasks. When evaluating faithfulness, they might mark an answer as correct because they know it’s true from training data—even though it’s not supported by the given context.</p>
<p>They also <strong>nitpick more than humans do</strong>. An LLM-evaluator might flag missing Oxford commas, outdated terminology, or edge cases not mentioned in the question. Human evaluators focus on whether the response answers the question adequately; LLM-evaluators sometimes apply unrealistic standards.</p>
<p><strong>Example contrast:</strong></p>
<ul>
<li><strong>Human critique</strong>: “Answer is mostly accurate but could be more concise.”</li>
<li><strong>LLM critique</strong>: “The answer is technically correct but exhibits several issues: 1) Verbose phrasing in paragraph 2, 2) Missing Oxford comma in line 3, 3) Could elaborate on edge case X (not mentioned in question), 4) Terminology Y is outdated, prefer Z…”</li>
</ul>
<p><strong>Mitigation approaches:</strong></p>
<ul>
<li>Use highly specific evaluation prompts that constrain reasoning to provided materials</li>
<li>Add explicit instructions: “Only consider information in the given context”</li>
<li>Few-shot examples showing appropriate vs excessive critique</li>
<li>Human calibration to identify when LLM-evaluators are too strict</li>
</ul>
<div class="mermaid">
graph TB
    Biases[Known LLM-Evaluator&#x3C;br/>Biases]
    
    Biases --> P[Position Bias&#x3C;br/>━━━━━━━━&#x3C;br/>50-70% first preference&#x3C;br/>Mitigate: both orders]
    
    Biases --> V[Verbosity Bias&#x3C;br/>━━━━━━━━&#x3C;br/>90%+ prefer longer&#x3C;br/>Mitigate: specific criteria]
    
    Biases --> S[Self-Enhancement&#x3C;br/>━━━━━━━━&#x3C;br/>10-25% own-model boost&#x3C;br/>Mitigate: third-party eval]
    
    Biases --> O[Over-Reasoning&#x3C;br/>━━━━━━━━&#x3C;br/>Inject knowledge, nitpick&#x3C;br/>Mitigate: constrain scope]
    
    style Biases stroke:#64748b,stroke-width:2.5px
    style P stroke:#ef4444,stroke-width:2px
    style V stroke:#f59e0b,stroke-width:2px
    style S stroke:#8b5cf6,stroke-width:2px
    style O stroke:#06b6d4,stroke-width:2px</div>
<h3 id="finetuned-models-the-specialization-tradeoff">Finetuned models: The specialization tradeoff</h3>
<p><strong>Finetuned evaluator models</strong> like Prometheus achieve strong performance on specific tasks but generalize poorly.[^finetuned-eval] Train a model to evaluate summarization quality, and it excels at that narrow task. Ask it to evaluate dialogue coherence, and performance collapses.</p>
<p>The performance delta is dramatic:</p>

























<table><thead><tr><th>Task Type</th><th>Correlation with Humans</th><th>Assessment</th></tr></thead><tbody><tr><td>Trained task</td><td>κ: 0.85</td><td>Excellent</td></tr><tr><td>Similar task</td><td>κ: 0.65</td><td>Substantial degradation</td></tr><tr><td>Different task</td><td>κ: 0.35</td><td>Catastrophic failure</td></tr></tbody></table>
<p>This is the opposite of general-purpose API models like GPT-4 or Claude, which maintain reasonable performance across diverse evaluation tasks even without task-specific training.</p>
<p><strong>When to finetune evaluators:</strong></p>
<ul>
<li>High-volume single task (millions of evaluations make training cost worthwhile)</li>
<li>Task-specific rubrics available (clear training signal)</li>
<li>Don’t need cross-domain generalization (narrow scope acceptable)</li>
<li>Have resources for ongoing model maintenance</li>
</ul>
<p><strong>When to use API models:</strong></p>
<ul>
<li>Multiple evaluation tasks (flexibility needed)</li>
<li>Evolving requirements (rubrics changing)</li>
<li>Need quick iteration (no retraining overhead)</li>
<li>Limited ML infrastructure (leverage external providers)</li>
</ul>
<p>The tradeoff is clear: finetuned models optimize for narrow excellence; API models optimize for broad capability. Choose based on your deployment reality, not what sounds more sophisticated.</p>
<h3 id="living-with-imperfect-evaluators">Living with imperfect evaluators</h3>
<p>The limitations and biases documented here aren’t reasons to avoid LLM-evaluators—they’re reasons to use them carefully with eyes open.</p>
<p><strong>Key principles for production deployment:</strong></p>
<ol>
<li><strong>Layer human oversight:</strong> Use LLM-evaluators for volume, humans for verification and calibration</li>
<li><strong>Measure and monitor bias:</strong> Track position bias, length distributions, self-enhancement effects</li>
<li><strong>Mitigate systematically:</strong> Implement both-orderings, third-party models, specific criteria</li>
<li><strong>Calibrate regularly:</strong> Monthly checks of LLM-evaluator vs human judgment (target κ > 0.60)</li>
<li><strong>Start conservative:</strong> Use LLM-evaluators for flagging candidates, not final decisions</li>
</ol>
<p>The goal isn’t perfect evaluation—that’s impossible even with humans. The goal is <strong>good enough evaluation at scale</strong> with known and managed limitations. Understanding biases is the first step toward building systems that work despite them.</p>
<h3 id="key-takeaways-limitations-and-biases">Key Takeaways: Limitations and Biases</h3>
<p>LLM-evaluators have 0.30-0.60 agreement with humans (vs 0.80-0.90 human-human)<br>
Single-pass recall is only 30-60% for defect detection<br>
Position bias affects 50-70% of pairwise comparisons → mitigate with both orderings<br>
Verbosity bias favors longer responses 90%+ of the time → use specific criteria<br>
Self-enhancement bias inflates own-model scores by 10-25% → use third-party evaluators<br>
Over-reasoning injects external knowledge → constrain evaluation scope<br>
Finetuned models excel at narrow tasks, fail at generalization</p>
<hr>
<h2 id="the-evaluation-process-making-it-systematic">The Evaluation Process: Making It Systematic</h2>
<p>Tools alone won’t save your product. Disciplined process is essential.</p>
<h3 id="the-scientific-method-applied-to-evaluation">The scientific method applied to evaluation</h3>
<p>The “secret sauce” for building production evals: <strong>it’s just the scientific method</strong>. No magic, no proprietary frameworks—systematic application of observe, hypothesize, experiment, measure, iterate.</p>
<div class="mermaid">
graph LR
    O[1. Observe&#x3C;br/>━━━━━━━━&#x3C;br/>Examine failures&#x3C;br/>Find patterns] --> A[2. Annotate&#x3C;br/>━━━━━━━━&#x3C;br/>Label good/bad&#x3C;br/>Build dataset]
    
    A --> H[3. Hypothesize&#x3C;br/>━━━━━━━━&#x3C;br/>Why failures occur&#x3C;br/>Propose fixes]
    
    H --> E[4. Experiment&#x3C;br/>━━━━━━━━&#x3C;br/>Test changes&#x3C;br/>Measure impact]
    
    E --> M[5. Measure&#x3C;br/>━━━━━━━━&#x3C;br/>Quantify results&#x3C;br/>Statistical rigor]
    
    M -.iterate.-> O
    
    style O stroke:#10b981,stroke-width:2px
    style A stroke:#10b981,stroke-width:2px
    style H stroke:#06b6d4,stroke-width:2px
    style E stroke:#06b6d4,stroke-width:2px
    style M stroke:#8b5cf6,stroke-width:2px
    
    linkStyle 4 stroke:#64748b,stroke-width:2px,stroke-dasharray:5</div>
<p><strong>Phase 1: Observe.</strong> Where does your system fail? Examine production outputs, user complaints, spot-checks. Look for patterns—are failures random or clustered? Do certain query types consistently break? Does performance degrade with document length?</p>
<p>Don’t skip this. Many teams jump straight to “solutions” without understanding what’s actually wrong. Observation grounds everything that follows.</p>
<p><strong>Phase 2: Annotate.</strong> Label 50-100 examples as pass/fail based on your quality criteria. This forces you to operationalize vague notions of “good” into concrete judgments. Can multiple annotators agree? If not, your criteria are too subjective or poorly defined.</p>
<p>Maintain 50:50 pass/fail balance. You need both positive and negative examples to train evaluators and measure progress. A dataset of all good examples tells you nothing about failure detection.</p>
<p><strong>Phase 3: Hypothesize.</strong> Based on observed patterns, form testable hypotheses about why failures occur. “Multi-hop questions fail because retrieval returns single passages” is testable. “The model isn’t smart enough” is not.</p>
<p>Good hypotheses have clear success criteria. “Increasing retrieval k from 3 to 5 will improve multi-hop accuracy by at least 10 percentage points.” Now you know exactly what to test and how to measure success.</p>
<p><strong>Phase 4: Experiment.</strong> Test your hypothesis with controlled changes. Establish a baseline metric before making changes. Try the intervention. Measure again. Did the metric move in the predicted direction? By how much?</p>
<p>Critical principle: <strong>change one thing at a time.</strong> If you simultaneously increase retrieval k, add reranking, and switch models, you won’t know which change drove improvement (or degradation).</p>
<p><strong>Phase 5: Measure.</strong> Quantify outcomes with statistical rigor. Is the 10-point accuracy improvement significant or within noise? Use confidence intervals, p-values, or bootstrapping—whatever’s appropriate for your data size.</p>
<p>Never ship based on “it seems better.” That’s a vibe check. You need numbers that withstand scrutiny.</p>
<p><strong>Then iterate.</strong> Measuring outcomes often reveals new failure modes you hadn’t noticed. The loop continues. This isn’t a one-time project—it’s ongoing practice.</p>
<h3 id="example-workflow-in-action">Example workflow in action</h3>
<p>Here’s what this looks like concretely:</p>
<p><strong>Observe:</strong> 40% of multi-hop questions fail. Single-hop questions are fine. Failures cluster around queries requiring synthesis across document sections.</p>
<p><strong>Annotate:</strong> Label 100 multi-hop examples. 30 are hallucinations (answer includes unsupported claims), 10 are incomplete (missing key information from second passage).</p>
<p><strong>Hypothesize:</strong> “Retrieval returns single passages. Multi-hop questions need 2+ passages for complete answers. Increasing k from 3 to 5 and adding reranking will improve accuracy from 40% to 65%.”</p>
<p><strong>Experiment:</strong></p>
<ul>
<li>Baseline: k=3, no reranking → 40% accuracy</li>
<li>Test 1: k=5, no reranking → 52% accuracy</li>
<li>Test 2: k=5, with reranking → 65% accuracy</li>
</ul>
<p><strong>Measure:</strong> Improvement is 25 percentage points (p &#x3C; 0.01), exceeding the 10-point threshold. Ship the change.</p>
<p><strong>Iterate:</strong> New observation—accuracy drops for questions about information near document end. Hypothesis: retrieval has recency bias. Next experiment cycle begins.</p>
<p>This is what systematic evaluation looks like. Not magic, not complex tooling—just disciplined application of scientific method.</p>
<h3 id="eval-driven-development-edd">Eval-Driven Development (EDD)</h3>
<p><strong>Write evals before building features.</strong> This inverts the traditional flow and forces clarity upfront.</p>
<div class="mermaid">
graph TD
    E1[Write Evals&#x3C;br/>━━━━━━━━&#x3C;br/>Define success criteria] --> E2[Establish Baseline&#x3C;br/>━━━━━━━━&#x3C;br/>Measure current performance]
    
    E2 --> E3[Build/Improve&#x3C;br/>━━━━━━━━&#x3C;br/>Implement changes]
    
    E3 --> E4[Evaluate&#x3C;br/>━━━━━━━━&#x3C;br/>Run evals on new version]
    
    E4 --> E5{Pass Criteria?}
    
    E5 -->|No| E3
    E5 -->|Yes| E6[Ship&#x3C;br/>━━━━━━━━&#x3C;br/>Deploy to production]
    
    style E1 stroke:#10b981,stroke-width:2px
    style E2 stroke:#10b981,stroke-width:2px
    style E3 stroke:#06b6d4,stroke-width:2px
    style E4 stroke:#06b6d4,stroke-width:2px
    style E5 stroke:#f59e0b,stroke-width:2px
    style E6 stroke:#8b5cf6,stroke-width:2px</div>
<p><strong>Traditional approach:</strong></p>
<ol>
<li>Build feature based on intuition</li>
<li>Test manually with a few examples</li>
<li>Ship if it “looks good”</li>
<li>Hope users don’t find issues</li>
</ol>
<p>Problems: No objective criteria. No way to measure improvement. Scope creep because requirements were never explicit. You discover problems after shipping.</p>
<p><strong>Eval-Driven Development:</strong></p>
<ol>
<li>Write evaluation dataset defining success</li>
<li>Measure baseline performance</li>
<li>Build/improve system</li>
<li>Run evals after every significant change</li>
<li>Ship only when evals pass threshold</li>
</ol>
<p>Benefits: Forced clarity upfront—you must define “good” before building. Measurable progress at every step. No scope creep—evals are the spec. Catch problems before users see them.</p>
<p><strong>Example success criteria:</strong></p>



































<table><thead><tr><th><strong>Dimension</strong></th><th><strong>Metric</strong></th><th><strong>Threshold</strong></th><th><strong>Why</strong></th></tr></thead><tbody><tr><td>Faithfulness</td><td>Recall</td><td>> 0.85</td><td>Catch 85%+ of hallucinations</td></tr><tr><td>Helpfulness</td><td>Score</td><td>> 0.80</td><td>80%+ helpful responses</td></tr><tr><td>Latency</td><td>P95</td><td>&#x3C; 500ms</td><td>User experience requirement</td></tr><tr><td>User satisfaction</td><td>Stars</td><td>> 4.0</td><td>Business success metric</td></tr></tbody></table>
<p>These aren’t aspirational—they’re gates. Your feature doesn’t ship until evals pass. This prevents the classic trap: “We’ll improve quality after launch” (which never happens because you’re on to the next feature).</p>
<p>EDD works because it makes trade-offs explicit. Want to ship faster? Lower the threshold or reduce eval coverage—but do it consciously, understanding the quality implications.</p>
<h3 id="continuous-monitoring-with-human-in-the-loop">Continuous monitoring with human-in-the-loop</h3>
<p>Production evaluation requires ongoing vigilance. Neither automated evaluators nor human reviewers are sufficient alone—you need both in a structured feedback loop.</p>
<h4 id="the-three-tier-monitoring-system">The three-tier monitoring system</h4>
<div class="mermaid">
graph TD
    T1[Tier 1: Human Sampling&#x3C;br/>━━━━━━━━&#x3C;br/>Daily/Weekly&#x3C;br/>50-100 outputs&#x3C;br/>Ground truth] --> T2[Tier 2: Calibrate Evaluators&#x3C;br/>━━━━━━━━&#x3C;br/>Monthly&#x3C;br/>LLM vs Human&#x3C;br/>Target: Recall>0.70, κ>0.60]
    
    T2 --> T3[Tier 3: Scaled Monitoring&#x3C;br/>━━━━━━━━&#x3C;br/>Continuous&#x3C;br/>Automated + Spot-checks&#x3C;br/>Flag issues at volume]
    
    T3 -.feedback loop.-> T2
    T2 -.feedback loop.-> T1
    
    style T1 stroke:#10b981,stroke-width:2px
    style T2 stroke:#06b6d4,stroke-width:2px
    style T3 stroke:#8b5cf6,stroke-width:2px
    
    linkStyle 2,3 stroke:#64748b,stroke-width:2px,stroke-dasharray:5</div>
<p><strong>Tier 1: Human sampling (daily/weekly).</strong> Manually review 50-100 production outputs. This serves multiple purposes:</p>
<ul>
<li>Establish ground truth for calibration</li>
<li>Catch edge cases automated evaluators miss</li>
<li>Stay connected to actual user experience</li>
<li>Identify emerging failure patterns</li>
</ul>
<p>Sample randomly, but oversample flagged outputs to verify your evaluators aren’t crying wolf. If 80% of flagged outputs are actually fine, you have precision problems.</p>
<p><strong>Tier 2: Calibrate evaluators (monthly).</strong> Compare LLM-evaluator judgments against human annotations from Tier 1. Calculate recall (what percentage of actual failures did the evaluator catch?) and Cohen’s kappa (overall agreement).</p>
<p>Target thresholds:</p>
<ul>
<li><strong>Recall > 0.70</strong> for faithfulness evaluation</li>
<li><strong>Cohen’s κ > 0.60</strong> for helpfulness evaluation</li>
</ul>
<p>Below these thresholds, your evaluators aren’t trustworthy. Refine prompts, add few-shot examples, or switch to better models. Then re-calibrate.</p>
<p><strong>Tier 3: Scaled monitoring (continuous).</strong> Run automated evaluators on all production outputs. Flag issues for human review. Track failure rates, category breakdowns, and temporal trends.</p>
<p>The key insight: automation provides coverage, humans provide accuracy. Spot-check flagged outputs to ensure precision stays high. Investigate unflagged outputs periodically to catch recall gaps.</p>
<h4 id="why-the-hybrid-approach-is-non-negotiable">Why the hybrid approach is non-negotiable</h4>
<p>Neither humans nor LLM-evaluators are perfect. The table tells the story:</p>





























<table><thead><tr><th><strong>Evaluator Type</strong></th><th><strong>Strengths</strong></th><th><strong>Weaknesses</strong></th><th><strong>Role</strong></th></tr></thead><tbody><tr><td><strong>Automated</strong></td><td>Scale, consistency, speed, 24/7 operation</td><td>Biases, limited recall (30-60%), misses subtle issues</td><td>Volume monitoring, first-pass filtering</td></tr><tr><td><strong>Human</strong></td><td>Nuanced judgment, catches edge cases, domain expertise</td><td>Expensive, inconsistent, doesn’t scale, subjective</td><td>Calibration, spot-checks, complex cases</td></tr><tr><td><strong>Hybrid</strong></td><td>Best of both—scale + accuracy</td><td>Requires organizational discipline</td><td><strong>Production standard</strong></td></tr></tbody></table>
<p>Production reality: You’ll process thousands or millions of outputs. Humans can’t review them all. But automated evaluators miss 40-70% of issues in single-pass mode. The solution is the feedback loop:</p>
<ol>
<li>Humans review samples → establish ground truth</li>
<li>Calibrate automated evaluators → measure alignment</li>
<li>Automated evaluators flag candidates → scale coverage</li>
<li>Humans spot-check flags → verify precision</li>
<li>Update evaluators based on findings → continuous improvement</li>
</ol>
<h3 id="organizational-discipline-the-unsexy-necessity">Organizational discipline: The unsexy necessity</h3>
<p>Success requires <strong>maintaining consistent feedback loops</strong>. No tool compensates for lack of discipline.</p>
<p><strong>Common failure modes:</strong></p>
<p><strong>Tool-thinking trap:</strong> “We bought the best LLM-evaluator, we’re done.” Tools are commodities. Process is the differentiator.</p>
<p><strong>Trusting automation blindly:</strong> “The evaluator flagged it, ship the fix.” Did you verify the flag was correct? Or chase a false positive?</p>
<p><strong>Vibe-check culture:</strong> “The new prompt feels better.” Does your data agree? Or are you fooling yourself?</p>
<p><strong>Static eval mindset:</strong> “We ran evals at launch.” How do you know quality hasn’t degraded? Continuous monitoring or it didn’t happen.</p>
<p><strong>Skipping baselines:</strong> “Let’s try this new approach!” Compared to what? Measure before and after or you’re flying blind.</p>
<p><strong>Required practices:</strong></p>
<p><strong>Process over tools:</strong> Scientific method > any specific evaluator. The framework is system-agnostic.</p>
<p><strong>Human-in-the-loop:</strong> Weekly sampling, monthly calibration. Non-negotiable for production systems.</p>
<p><strong>Measurement first:</strong> Track metrics before and after every change. Decisions driven by data, not intuition.</p>
<p><strong>Continuous improvement:</strong> Evals evolve with your product. Last quarter’s eval dataset doesn’t capture this quarter’s failures.</p>
<p><strong>Hypothesis-driven:</strong> Every change tests a specific hypothesis with clear success criteria. No “let’s try this and see.”</p>
<h4 id="the-data-flywheel">The data flywheel</h4>
<p>When you do this right, a virtuous cycle emerges:</p>
<div class="mermaid">
graph LR
    M[Measure&#x3C;br/>Outcomes&#x3C;br/>━━━━━━━━&#x3C;br/>Track failures&#x3C;br/>Quantify impact] --> I[Improve&#x3C;br/>Product&#x3C;br/>━━━━━━━━&#x3C;br/>Fix root causes&#x3C;br/>Ship changes]
    
    I --> R[Reduce&#x3C;br/>Defects&#x3C;br/>━━━━━━━━&#x3C;br/>Fewer failures&#x3C;br/>Better quality]
    
    R --> T[Earn User&#x3C;br/>Trust&#x3C;br/>━━━━━━━━&#x3C;br/>Reliability grows&#x3C;br/>Adoption increases]
    
    T --> M
    
    style M stroke:#10b981,stroke-width:2px
    style I stroke:#06b6d4,stroke-width:2px
    style R stroke:#8b5cf6,stroke-width:2px
    style T stroke:#f59e0b,stroke-width:2px</div>
<p>Measurement reveals problems → improvements fix them → defects decrease → users trust the system more → you can invest in harder problems because the basics work.</p>
<p>This flywheel requires consistent effort. Miss a calibration cycle, skip baseline measurements, stop human sampling—the wheel stops spinning. Momentum evaporates.</p>
<h3 id="practical-implementation-checklist">Practical implementation checklist</h3>
<p>Are you actually doing systematic evaluation? Check yourself:</p>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" disabled> <strong>Regular sampling:</strong> Weekly/daily production output review by humans</li>
<li class="task-list-item"><input type="checkbox" disabled> <strong>Annotation consistency:</strong> Inter-annotator agreement measured (κ > 0.60)</li>
<li class="task-list-item"><input type="checkbox" disabled> <strong>Evaluator calibration:</strong> Monthly LLM-evaluator vs human judgment comparison</li>
<li class="task-list-item"><input type="checkbox" disabled> <strong>Hypothesis-driven changes:</strong> Every modification tests specific hypothesis with clear success criteria</li>
<li class="task-list-item"><input type="checkbox" disabled> <strong>Baseline measurement:</strong> Always measure before making changes</li>
<li class="task-list-item"><input type="checkbox" disabled> <strong>Statistical rigor:</strong> Use p-values, confidence intervals—not gut feelings</li>
<li class="task-list-item"><input type="checkbox" disabled> <strong>Continuous monitoring:</strong> Automated evaluation running on production outputs</li>
<li class="task-list-item"><input type="checkbox" disabled> <strong>Feedback loops:</strong> Human findings improve evaluators, evaluator findings trigger human review</li>
</ul>
<p>If you can’t check most of these boxes, you’re not doing systematic evaluation—you’re doing vibe checks at scale.</p>
<h3 id="the-bottom-line">The bottom line</h3>
<p>Building LLM systems can feel like magic, but building reliable LLM products still takes elbow grease. The evaluation bottleneck is real, but it’s solvable through disciplined process:</p>
<p><strong>Process matters more than tools:</strong></p>
<ul>
<li>Scientific method > any specific evaluator</li>
<li>EDD > ad-hoc testing</li>
<li>Human-in-the-loop > fully automated</li>
<li>Continuous improvement > one-time evaluation</li>
</ul>
<p>The teams shipping trustworthy LLM products aren’t using secret tools or magic techniques. They’re applying boring, systematic evaluation practices with organizational discipline. That’s the unsexy truth.</p>
<h3 id="key-takeaways-the-evaluation-process">Key Takeaways: The Evaluation Process</h3>
<p>Apply the scientific method: observe → annotate → hypothesize → experiment → measure → iterate<br>
Practice eval-driven development (write evals before building features)<br>
Implement three-tier monitoring: human sampling → evaluator calibration → scaled automation<br>
Maintain feedback loops: continuous improvement, not one-time setup<br>
Organizational discipline > fancy tools<br>
Measure everything, ship based on data not vibes</p>
<hr>
<h2 id="conclusion-evaluation-as-continuous-practice">Conclusion: Evaluation as Continuous Practice</h2>
<p>Evaluation isn’t a solved problem—it’s an evolving practice requiring ongoing attention and discipline.</p>
<h3 id="the-three-pillars-reviewed">The three pillars reviewed</h3>
<p><strong>What to evaluate: Faithfulness vs helpfulness.</strong> These dimensions often conflict, and you must consciously choose priorities based on domain needs. Legal and medical applications prioritize faithfulness—speculation creates liability. Consumer chatbots may prioritize helpfulness—users want complete answers. Most production systems need both, carefully balanced.</p>
<p>The framework itself is simple: faithfulness means grounded in sources without unsupported claims; helpfulness means relevant, comprehensive, and concise. But applying it requires judgment. When context is incomplete, do you refuse to answer (maximize faithfulness) or generate anyway (maximize helpfulness)? There’s no universal right answer—it depends on your users and domain.</p>
<p><strong>How to evaluate: Start simple, add complexity deliberately.</strong> Code-based evaluators for deterministic checks. Direct scoring with LLMs for objective semantic judgments like faithfulness. Pairwise comparison for subjective quality assessments. Reference-based when you have gold standards.</p>
<p>Layer on advanced techniques—Chain-of-Thought, few-shot prompting, cross-examination—only where they demonstrably improve results. Every technique trades cost and latency for accuracy. Make those trades consciously based on your requirements, not what sounds sophisticated in papers.</p>
<p>Understand and mitigate systematic biases. Position bias corrupts pairwise comparison without both-orderings evaluation. Verbosity bias rewards unnecessarily long outputs. Self-enhancement bias inflates quality when using the same model for generation and evaluation. These aren’t edge cases—they’re pervasive, documented patterns that invalidate naive evaluation.</p>
<p><strong>Making it systematic: Process over tools.</strong> Apply the scientific method: observe failures, annotate examples, hypothesize causes, experiment with fixes, measure outcomes, iterate. Practice eval-driven development—write evals before building features. Maintain continuous monitoring with three-tier human-in-the-loop systems.</p>
<p>The unsexy truth: organizational discipline matters more than any specific tool or technique. Weekly human sampling, monthly evaluator calibration, hypothesis-driven changes, baseline measurements before modifications. These boring practices separate teams shipping trustworthy products from teams chasing vibe checks.</p>
<div class="mermaid">
graph TD
    What[What to Evaluate&#x3C;br/>━━━━━━━━&#x3C;br/>Faithfulness vs Helpfulness&#x3C;br/>Domain priorities] --> How[How to Evaluate&#x3C;br/>━━━━━━━━&#x3C;br/>Methods + Metrics + Biases&#x3C;br/>Start simple, add complexity]
    
    How --> Sys[Making it Systematic&#x3C;br/>━━━━━━━━&#x3C;br/>Scientific method + EDD&#x3C;br/>Human-in-the-loop]
    
    Sys -.continuous practice.-> What
    
    style What stroke:#10b981,stroke-width:2.5px
    style How stroke:#06b6d4,stroke-width:2.5px
    style Sys stroke:#8b5cf6,stroke-width:2.5px
    
    linkStyle 2 stroke:#64748b,stroke-width:2px,stroke-dasharray:5</div>
<h3 id="core-principles-distilled">Core principles distilled</h3>
<p>If you remember nothing else from this guide, remember these principles:</p>
<p><strong>Process over tools.</strong> The scientific method beats any specific evaluator. Teams succeed through disciplined practices, not magic frameworks.</p>
<p><strong>Balance automation with human judgment.</strong> Scale monitoring with LLM-evaluators, calibrate quality with human review. Neither alone is sufficient.</p>
<p><strong>Measure everything.</strong> If you can’t measure it, you can’t improve it. Track baselines before changes. Use statistical rigor, not gut feelings.</p>
<p><strong>Domain expertise matters.</strong> Expert human judgment beats crowd workers for specialized domains. LLM-evaluators correlate moderately with humans but miss patterns domain experts catch.</p>
<p><strong>Evaluation is practice, not artifact.</strong> You don’t “finish” evaluation and move on. Requirements evolve, failure modes shift, systems drift. Continuous improvement is the standard.</p>
<p><strong>Bias awareness is mandatory.</strong> LLM-evaluators have systematic biases—position preference, verbosity bias, self-enhancement. Mitigate them consciously or get corrupted results.</p>
<p><strong>Start simple, prove value, scale deliberately.</strong> Don’t architect elaborate evaluation systems upfront. Build a 50-example eval dataset. Run simple evaluators. Measure alignment with your judgment. Expand as you learn what works.</p>
<h3 id="moving-beyond-vibe-checks">Moving beyond vibe checks</h3>
<p>The evaluation bottleneck is real. You’ve built an LLM-powered feature, tested it manually, shown it to colleagues who nod approvingly. But you don’t actually know if it works reliably. Users report hallucinations, irrelevant responses, other failures. You were relying on vibes, not measurements.</p>
<p>This guide provides the systematic approaches to escape that trap. The techniques aren’t revolutionary—they’re borrowed from decades of ML research and production experience. What’s new is applying them systematically to LLM evaluation.</p>
<p>Building LLM systems is often easier than measuring if they work. Generation feels like magic; evaluation feels like drudgery. But evaluation is how you transform prototypes into trustworthy products users can rely on.</p>
<p>The good news: evaluation isn’t magic either. It’s systematic application of the scientific method, disciplined annotation practices, continuous monitoring with human oversight. It takes work—elbow grease, not genius—but the payoff is products users trust.</p>
<h3 id="your-starting-point">Your starting point</h3>
<p>Don’t try to implement everything at once. Start here:</p>
<p><strong>Week 1-2: Build your first eval dataset</strong></p>
<ul>
<li>Collect 50-100 production examples (or generate synthetic ones)</li>
<li>Manually label them as pass/fail</li>
<li>Balance 50:50 between passing and failing examples</li>
<li>Document what makes outputs good vs bad</li>
</ul>
<p><strong>Week 3: Implement simple evaluators</strong></p>
<ul>
<li>Code-based checks for format, length, citation presence</li>
<li>Basic LLM-evaluator for faithfulness (direct scoring)</li>
<li>Measure how often the LLM-evaluator agrees with your labels</li>
</ul>
<p><strong>Week 4: Establish baselines and iterate</strong></p>
<ul>
<li>Measure current system performance on your eval dataset</li>
<li>Identify the biggest failure mode</li>
<li>Form a hypothesis about the cause</li>
<li>Test a fix and measure improvement</li>
</ul>
<p><strong>Month 2 onward: Scale and systematize</strong></p>
<ul>
<li>Expand eval dataset to 200-500 examples</li>
<li>Add diverse question types and position bias tests</li>
<li>Implement pairwise comparison for quality assessment</li>
<li>Start weekly human sampling of production outputs</li>
<li>Monthly calibration: LLM-evaluator vs human judgment</li>
</ul>
<p>You don’t need sophisticated tooling to start. A spreadsheet, a Python script, and disciplined sampling will take you far. Add complexity only when simple approaches prove insufficient.</p>
<h3 id="the-path-forward">The path forward</h3>
<p>Evaluation is evolving rapidly. New techniques emerge, models improve, best practices shift. This guide captures the current state of knowledge, but it won’t be the final word.</p>
<p>What won’t change: the fundamental challenge of measuring whether LLM outputs are good. Traditional metrics like BLEU fail completely. Human evaluation doesn’t scale. LLM-evaluators are powerful but imperfect. The tension between faithfulness and helpfulness persists. The need for systematic approaches remains.</p>
<p>Stay grounded in first principles:</p>
<ul>
<li>Define what “good” means for your domain</li>
<li>Build evaluation datasets that represent real challenges</li>
<li>Choose appropriate methods and metrics</li>
<li>Understand limitations and biases</li>
<li>Apply the scientific method</li>
<li>Maintain human oversight</li>
<li>Iterate continuously</li>
</ul>
<p>The teams succeeding with LLM products aren’t waiting for perfect evaluation tools. They’re shipping with good-enough evaluation at scale, knowing and managing limitations, improving continuously through disciplined practice.</p>
<p>That’s the path forward. Not magic, not genius—systematic, boring, effective evaluation practices applied with organizational discipline.</p>
<h3 id="final-thought">Final thought</h3>
<blockquote>
<p><strong>”If you can’t measure it, you can’t improve it.”</strong></p>
</blockquote>
<p>The evaluation bottleneck strangling LLM product development is real, but it’s solvable. Move beyond vibe checks. Build systematic evaluation into your development process. Measure rigorously. Iterate based on data, not intuition.</p>
<p>Your users—and your product—will thank you.</p>
<hr>
<h2 id="appendix-tools-and-resources">Appendix: Tools and Resources</h2>
<p>This appendix provides curated resources for deepening your evaluation practice—frameworks for implementation, benchmarks for testing, and research papers for understanding the foundations.</p>
<h3 id="evaluation-frameworks-and-tools">Evaluation frameworks and tools</h3>
<p><strong>Production-ready frameworks:</strong></p>
<ul>
<li>
<p><strong><a href="https://github.com/openai/evals">OpenAI Evals</a></strong>: Open-source framework for creating and running evaluations. Extensible architecture, community-contributed eval templates, integrates with OpenAI API. Good starting point for teams new to systematic evaluation.</p>
</li>
<li>
<p><strong><a href="https://python.langchain.com/docs/guides/evaluation">LangChain Evaluation</a></strong>: Evaluation utilities integrated with LangChain’s LLM application framework. Includes reference-based, criteria-based, and pairwise evaluators. Useful if you’re already building with LangChain.</p>
</li>
<li>
<p><strong><a href="https://docs.anthropic.com/claude/docs/evaluations-and-testing">Anthropic’s Claude Evals</a></strong>: Documentation and best practices for evaluating Claude-based applications. Focus on constitutional AI and critique-based evaluation approaches.</p>
</li>
<li>
<p><strong><a href="https://www.promptfoo.dev/">Promptfoo</a></strong>: Open-source tool for testing and evaluating LLM outputs. Supports multiple providers, configurable test cases, and automated regression testing.</p>
</li>
</ul>
<p><strong>Research and specialized tools:</strong></p>
<ul>
<li>
<p><strong><a href="https://github.com/confident-ai/deepeval">DeepEval</a></strong>: Open-source evaluation framework with 14+ metrics for LLMs including G-Eval, hallucination detection, toxicity, and bias. Features unit testing interface, synthetic dataset generation, and integration with CI/CD pipelines. Good for teams wanting pytest-style evaluation workflows.</p>
</li>
<li>
<p><strong><a href="https://aligneval.com/">AlignEval</a></strong>: Tool from Alignment Research Center for calibrating automated evaluators against human judgment. Implements techniques for measuring and improving evaluator-human alignment.</p>
</li>
<li>
<p><strong><a href="https://github.com/explodinggradients/ragas">RAGAS</a></strong>: Framework specifically for evaluating Retrieval-Augmented Generation systems. Measures faithfulness, answer relevance, context precision, and context recall.</p>
</li>
<li>
<p><strong><a href="https://www.trulens.org/">TruLens</a></strong>: Evaluation and tracking for LLM applications with focus on groundedness, relevance, and harmlessness. Provides instrumentation for debugging evaluation failures.</p>
</li>
</ul>
<p><strong>Specialized evaluators:</strong></p>
<ul>
<li>
<p><strong>SelfCheckGPT</strong>: Consistency-based hallucination detection. Generates multiple responses to the same query and measures consistency—inconsistency signals hallucination.</p>
</li>
<li>
<p><strong>SummaC, QAFactEval, RefChecker</strong>: Claim-based faithfulness evaluation. Decomposes responses into atomic claims and verifies each against source documents.</p>
</li>
<li>
<p><strong>Prometheus, Shepherd, CriticGPT</strong>: Finetuned evaluator models trained on human preference data. Strong performance on specific tasks but limited generalization.</p>
</li>
</ul>
<h3 id="benchmarks-and-evaluation-datasets">Benchmarks and evaluation datasets</h3>
<p><strong>Long-context comprehension:</strong></p>
<ul>
<li>
<p><strong><a href="https://github.com/deepmind/narrativeqa">NarrativeQA</a></strong>: 1,500 stories with questions generated from Wikipedia summaries. Tests deep comprehension rather than shallow pattern matching. Essential reference for question generation methodology.</p>
</li>
<li>
<p><strong><a href="https://allenai.org/data/qasper">QASPER</a></strong>: 5,049 questions on NLP research papers. Domain-specific, requires technical understanding and multi-hop reasoning.</p>
</li>
<li>
<p><strong><a href="https://github.com/OpenLMLab/LEval">L-Eval</a></strong>: Multi-task long-context evaluation suite. Covers summarization, QA, and reasoning across diverse domains.</p>
</li>
<li>
<p><strong><a href="https://github.com/princeton-nlp/HELMET">HELMET</a></strong>: Hierarchical Evaluation for Long-context Models. Tests both retrieval and reasoning capabilities separately.</p>
</li>
</ul>
<p><strong>RAG and faithfulness:</strong></p>
<ul>
<li>
<p><strong><a href="https://github.com/chen700564/RGB">RGB Benchmark</a></strong>: Retrieval-Grounded Benchmark for evaluating noise robustness, negative rejection, and information integration in RAG systems.</p>
</li>
<li>
<p><strong><a href="https://github.com/Yale-LILY/SummEval">SummEval</a></strong>: Summarization evaluation with human annotations for faithfulness, relevance, coherence, and consistency.</p>
</li>
<li>
<p><strong><a href="https://github.com/RUCAIBox/HaluEval">HaluEval</a></strong>: Large-scale hallucination evaluation benchmark covering QA, dialogue, and summarization tasks.</p>
</li>
</ul>
<p><strong>Multi-turn and conversational:</strong></p>
<ul>
<li>
<p><strong><a href="https://github.com/lm-sys/FastChat/tree/main/fastchat/llm_judge">MT-Bench</a></strong>: Multi-turn conversation benchmark with 80 high-quality questions spanning writing, roleplay, reasoning, math, coding, and knowledge. Includes LLM-as-judge evaluation pipeline.</p>
</li>
<li>
<p><strong><a href="https://huggingface.co/spaces/lmarena-ai/lmarena-leaderboard">Chatbot Arena</a></strong>: Large-scale human preference data from pairwise comparisons. Over 500K human votes across diverse models and prompts.</p>
</li>
</ul>
<p><strong>Domain-specific:</strong></p>
<ul>
<li>
<p><strong><a href="https://github.com/jind11/MedQA">MedQA</a></strong>: Medical question answering from USMLE exams. Tests domain knowledge and reasoning.</p>
</li>
<li>
<p><strong><a href="https://github.com/HazyResearch/legalbench">LegalBench</a></strong>: 162 legal reasoning tasks across issue-spotting, rule application, and interpretation.</p>
</li>
<li>
<p><strong><a href="https://github.com/patronus-ai/financebench">FinanceBench</a></strong>: Financial question answering requiring reasoning over earnings reports and financial statements.</p>
</li>
</ul>
<h3 id="research-papers-foundations-and-cutting-edge">Research papers: Foundations and cutting edge</h3>
<p><strong>Core evaluation methodology:</strong></p>
<ul>
<li>
<p><strong>G-Eval: NLG Evaluation using GPT-4 with Better Human Alignment</strong> (Liu et al., 2023) - Introduces Chain-of-Thought evaluation and form-filling for improved LLM-as-judge accuracy. <a href="https://arxiv.org/abs/2303.16634">arXiv:2303.16634</a>[^geval]</p>
</li>
<li>
<p><strong>Judging LLM-as-a-Judge with MT-Bench and Chatbot Arena</strong> (Zheng et al., 2023) - Documents position bias, agreement patterns, and best practices for LLM evaluators. Essential reading. <a href="https://arxiv.org/abs/2306.05685">arXiv:2306.05685</a>[^judging-llm-judge]</p>
</li>
<li>
<p><strong>The NarrativeQA Reading Comprehension Challenge</strong> (Kočiský et al., 2018) - Foundational work on question generation from summaries to encourage deep comprehension. <a href="https://arxiv.org/abs/1712.07040">arXiv:1712.07040</a>[^narrativeqa]</p>
</li>
</ul>
<p><strong>Bias and limitations:</strong></p>
<ul>
<li>
<p><strong>Large Language Models are Inconsistent and Biased Evaluators</strong> (Panickssery et al., 2024) - Comprehensive analysis of LLM-evaluator biases including position, verbosity, and self-enhancement. <a href="https://arxiv.org/abs/2405.01724">arXiv:2405.01724</a>[^llm-bias]</p>
</li>
<li>
<p><strong>Judging the Judges: A Systematic Investigation of Position Bias in Pairwise Comparative Assessments by LLMs</strong> (Li et al., 2024) - Deep dive into position bias mechanisms and mitigation strategies. <a href="https://arxiv.org/abs/2406.07791">arXiv:2406.07791</a>[^position-bias]</p>
</li>
<li>
<p><strong>Split and Merge: Aligning Position Biases in LLM-based Evaluators</strong> (Li et al., 2024) - Introduces PORTIA method for position bias mitigation through multiple orderings. <a href="https://aclanthology.org/2024.emnlp-main.621/">EMNLP 2024</a>[^portia]</p>
</li>
</ul>
<p><strong>Agreement and correlation:</strong></p>
<ul>
<li>
<p><strong>A Coefficient of Agreement for Nominal Scales</strong> (Cohen, 1960) - Original Cohen’s kappa paper. Foundation for measuring inter-annotator agreement beyond chance. Interpretation guidelines from Landis &#x26; Koch (1977).[^cohen-kappa]</p>
</li>
<li>
<p><strong>Inter-Coder Agreement for Computational Linguistics</strong> (Artstein &#x26; Poesio, 2008) - Comprehensive treatment of agreement metrics and their proper application in NLP contexts.[^artstein-poesio]</p>
</li>
</ul>
<p><strong>Advanced techniques:</strong></p>
<ul>
<li>
<p><strong>Constitutional AI: Harmlessness from AI Feedback</strong> (Anthropic, 2022) - Critique-based evaluation and iterative refinement. Foundation for self-improvement approaches.</p>
</li>
<li>
<p><strong>Improving Factuality and Reasoning in Language Models through Multiagent Debate</strong> (Du et al., 2023) - Multiple LLM evaluators debating to reach consensus. Improves accuracy but increases cost. <a href="https://arxiv.org/abs/2305.14325">arXiv:2305.14325</a>[^multiagent-debate]</p>
</li>
<li>
<p><strong>Panel of LLMs: A Novel Ensemble for LLM Evaluation</strong> (Chan et al., 2024) - Combining multiple LLM-evaluators to reduce individual model biases and improve reliability.[^poll]</p>
</li>
</ul>
<p><strong>Finetuned evaluators:</strong></p>
<ul>
<li>
<p><strong>Prometheus: Inducing Fine-grained Evaluation Capability in Language Models</strong> (Kim et al., 2023) - Training specialized evaluator models. Strong on specific tasks, weak on generalization.[^finetuned-eval]</p>
</li>
<li>
<p><strong>CriticGPT</strong> (OpenAI, 2024) - Finetuned model for finding bugs in code. Demonstrates value and limitations of specialized evaluators.</p>
</li>
</ul>
<p><strong>Faithfulness and hallucination detection:</strong></p>
<ul>
<li>
<p><strong>SelfCheckGPT: Zero-Resource Black-Box Hallucination Detection for Generative Large Language Models</strong> (Manakul et al., 2023) - Consistency-based approach that doesn’t require source documents.</p>
</li>
<li>
<p><strong>SummaC: Re-Visiting NLI-based Models for Inconsistency Detection in Summarization</strong> (Laban et al., 2022) - Claim-based faithfulness using natural language inference.</p>
</li>
</ul>
<h3 id="learning-resources-and-practical-guides">Learning resources and practical guides</h3>
<p><strong>Comprehensive guides:</strong></p>
<ul>
<li>
<p><strong><a href="https://eugeneyan.com/writing/llm-patterns/">Patterns for Building LLM-based Systems &#x26; Products</a></strong> by Eugene Yan - Practical patterns from production experience. Covers evaluation, monitoring, and debugging throughout.</p>
</li>
<li>
<p><strong><a href="https://hamel.dev/blog/posts/evals/">AI Evals for Engineers and PMs</a></strong> by Hamel Husain and Shreya Shankar - End-to-end guide to building evaluation pipelines. Emphasis on practical implementation over theory.</p>
</li>
<li>
<p><strong><a href="https://docs.claude.com/en/docs/test-and-evaluate/">Anthropic’s Evaluation Documentation</a></strong> - Best practices for evaluating Claude-based applications. Constitutional AI principles and critique-based evaluation.</p>
</li>
<li>
<p><strong><a href="https://cookbook.openai.com/topic/evals">OpenAI Cookbook: Evaluation</a></strong> - Practical examples and code for implementing various evaluation approaches with OpenAI models.</p>
</li>
</ul>
<p><strong>Community resources:</strong></p>
<ul>
<li>
<p><strong><a href="https://huggingface.co/docs/evaluate/">Hugging Face Evaluate Library</a></strong> - Open-source library with evaluation metrics and model evaluation tools.</p>
</li>
<li>
<p><strong><a href="https://blog.langchain.dev/">LangChain Blog: Evaluation Series</a></strong> - Regular posts on evaluation techniques, case studies, and best practices.</p>
</li>
</ul>
<h2 id="common-problems-and-solutions">Common Problems and Solutions</h2>
<h3 id="troubleshooting-guide">Troubleshooting Guide</h3>
<p><strong>Problem: Low inter-annotator agreement (κ &#x3C; 0.40)</strong></p>
<p><strong>Bad approach:</strong> “It’s subjective, proceed anyway”</p>
<p><strong>Good approach:</strong></p>
<ol>
<li>Review 10 disagreement cases with annotators</li>
<li>Identify pattern: are guidelines unclear? Is task too subjective?</li>
<li>Refine guidelines with concrete examples</li>
<li>Run another calibration session</li>
<li>If still low, consider simplifying criteria (e.g., binary instead of 5-point scale)</li>
</ol>
<hr>
<p><strong>Problem: LLM evaluator has 90% precision but 30% recall</strong></p>
<p><strong>Bad approach:</strong> “Precision is high, ship it”</p>
<p><strong>Good approach:</strong></p>
<ol>
<li>Identify use case: Is this a guardrail? If yes, recall matters most!</li>
<li>Lower threshold or adjust prompts to increase recall</li>
<li>Accept some precision loss if needed (false positives &#x3C; false negatives for guardrails)</li>
<li>Add human review of borderline cases</li>
<li>Target: Recall > 0.85 for guardrails, even if precision drops to 0.70</li>
</ol>
<hr>
<p><strong>Problem: Evaluation too expensive (thousands of LLM calls/day)</strong></p>
<p><strong>Bad approach:</strong> Reduce eval coverage or frequency</p>
<p><strong>Good approach:</strong></p>
<ol>
<li>Implement tiered evaluation pipeline:
<ul>
<li>Tier 1: Code checks (free, instant)</li>
<li>Tier 2: Cheaper LLM for screening (GPT-3.5, Claude Haiku)</li>
<li>Tier 3: Premium LLM only for flagged cases</li>
</ul>
</li>
<li>Sample strategically: 100% of flagged outputs, 5-10% of clean outputs</li>
<li>Consider finetuning a smaller model for high-volume, narrow tasks</li>
<li>Use caching for repeated evaluations</li>
</ol>
<hr>
<p><strong>Problem: Evaluator performance degrading over time</strong></p>
<p><strong>Bad approach:</strong> “It worked at launch, must be fine”</p>
<p><strong>Good approach:</strong></p>
<ol>
<li>Check monthly calibration data - has agreement dropped?</li>
<li>Review recent failure cases - new failure modes emerging?</li>
<li>User behavior shift? New query patterns?</li>
<li>Model drift? Provider updated their model?</li>
<li>Update eval dataset with new failure examples</li>
<li>Retrain or adjust prompts based on findings</li>
</ol>
<hr>
<p><strong>Problem: Can’t decide between faithfulness and helpfulness priority</strong></p>
<p><strong>Bad approach:</strong> “Let’s optimize both equally”</p>
<p><strong>Good approach:</strong></p>
<ol>
<li>Ask: What’s worse for users?
<ul>
<li>Users getting wrong info (faithfulness failure)</li>
<li>Users getting incomplete/irrelevant info (helpfulness failure)</li>
</ul>
</li>
<li>Domain analysis:
<ul>
<li>Legal/medical/compliance → faithfulness priority</li>
<li>Consumer support/creative → helpfulness priority</li>
<li>Enterprise search → balanced</li>
</ul>
</li>
<li>Start with your domain’s default, measure user satisfaction</li>
<li>Adjust based on actual user feedback and complaints</li>
</ol>
<hr>
<p><strong>Problem: Too many false positives from automated evaluator</strong></p>
<p><strong>Bad approach:</strong> “Turn off the evaluator”</p>
<p><strong>Good approach:</strong></p>
<ol>
<li>Measure current precision on labeled sample (100 cases)</li>
<li>Review false positives - what’s the pattern?</li>
<li>Common causes:
<ul>
<li>Evaluator too strict (adjust prompts, add examples)</li>
<li>Overfitting to training distribution (expand eval dataset diversity)</li>
<li>Position/verbosity bias (implement mitigations)</li>
</ul>
</li>
<li>Iterate on evaluator, re-measure precision</li>
<li>Target: Precision > 0.70 to avoid alert fatigue</li>
</ol>
<hr>
<p><strong>Problem: Team wants to ship before evals pass threshold</strong></p>
<p><strong>Bad approach:</strong> “Just this once, we’ll fix it later” (never happens)</p>
<p><strong>Good approach:</strong></p>
<ol>
<li>Make tradeoffs explicit:
<ul>
<li>Current: 75% accuracy, target: 85%</li>
<li>Risk: 10% of users see failures</li>
<li>Business impact: X complaints/day, Y support cost</li>
</ul>
</li>
<li>Document decision and timeline for fixes</li>
<li>Implement monitoring to track actual failure rate</li>
<li>Schedule follow-up to hit threshold within 2-4 weeks</li>
<li>Or adjust threshold with leadership buy-in, understanding consequences</li>
</ol>
<h2 id="quick-reference-decision-trees">Quick Reference: Decision Trees</h2>
<h3 id="when-should-i-use-which-evaluation-method">When Should I Use Which Evaluation Method?</h3>
<div class="mermaid">
graph TD
    Q1{Can I check it&#x3C;br/>with code?}
    
    Q1 -->|Yes| A1[Code-based&#x3C;br/> Free, fast, deterministic&#x3C;br/>Use for: format, length,&#x3C;br/>citations, consistency]
    
    Q1 -->|No| Q2{Is the criterion&#x3C;br/>objective?}
    
    Q2 -->|Yes| A2[Direct Scoring&#x3C;br/> One evaluation pass&#x3C;br/>Use for: faithfulness,&#x3C;br/>toxicity, compliance]
    
    Q2 -->|No| Q3{Comparing&#x3C;br/>variants?}
    
    Q3 -->|Yes| A3[Pairwise Comparison&#x3C;br/>⚠️ Must evaluate both orders&#x3C;br/>Use for: A/B testing,&#x3C;br/>subjective quality]
    
    Q3 -->|No| Q4{Have gold&#x3C;br/>standards?}
    
    Q4 -->|Yes| A4[Reference-based&#x3C;br/>⚠️ Expensive to maintain&#x3C;br/>Use when: expert answers&#x3C;br/>available and updated]
    
    Q4 -->|No| A5[Direct Scoring&#x3C;br/>with clear rubric&#x3C;br/> Define concrete criteria&#x3C;br/>Use few-shot examples]
    
    style Q1 stroke:#64748b,stroke-width:2px
    style Q2 stroke:#64748b,stroke-width:2px
    style Q3 stroke:#64748b,stroke-width:2px
    style Q4 stroke:#64748b,stroke-width:2px
    
    style A1 stroke:#10b981,stroke-width:2px
    style A2 stroke:#06b6d4,stroke-width:2px
    style A3 stroke:#f59e0b,stroke-width:2px
    style A4 stroke:#8b5cf6,stroke-width:2px
    style A5 stroke:#06b6d4,stroke-width:2px</div>
<h3 id="when-should-i-add-advanced-techniques">When Should I Add Advanced Techniques?</h3>



































<table><thead><tr><th>Technique</th><th>When to Use</th><th>When to Skip</th></tr></thead><tbody><tr><td><strong>Chain-of-Thought</strong></td><td>High-stakes decisions, need transparency</td><td>Rapid iteration, cost-sensitive</td></tr><tr><td><strong>Few-shot examples</strong></td><td>Nuanced criteria, domain-specific</td><td>Simple binary decisions, clear criteria</td></tr><tr><td><strong>Cross-examination</strong></td><td>Legal/medical domains, critical monitoring</td><td>Development, high-volume, low-stakes</td></tr><tr><td><strong>Both-orderings (pairwise)</strong></td><td>ANY pairwise comparison</td><td>Never skip for pairwise</td></tr><tr><td><strong>Third-party evaluator</strong></td><td>Production systems, final decisions</td><td>Early prototyping, budget-constrained</td></tr></tbody></table>
<hr>
<h2 id="acknowledgments">Acknowledgments</h2>
<p>This guide synthesizes research and production experience from many sources. Special thanks to the researchers and practitioners who’ve made their work public, particularly:</p>
<ul>
<li>The teams at OpenAI, Anthropic, Google DeepMind, and Meta for advancing LLM evaluation methodology</li>
<li>The academic community for rigorous analysis of evaluator biases and limitations</li>
<li>Practitioners sharing production lessons: Eugene Yan, Hamel Husain, Shreya Shankar, and many others</li>
<li>The open-source community building evaluation frameworks and tools</li>
</ul>]]></content:encoded><enclosure url="https://www.vitorsousa.com/_astro/contextual-bandits-p1.BKWb_URn.png" length="756000" type="image/png"/></item><item><title>Beyond the Vibe Check: A Systematic Approach to LLM Evaluation</title><link>https://www.vitorsousa.com/blog/beyond-the-vibe-check-a-systematic-approach-to-llm-evaluation/</link><guid isPermaLink="true">https://www.vitorsousa.com/blog/beyond-the-vibe-check-a-systematic-approach-to-llm-evaluation/</guid><description>Stop relying on gut feelings to evaluate LLM outputs. Learn systematic approaches to build trustworthy evaluation pipelines with measurable metrics, proven methods, and production-ready practices. A practical guide covering faithfulness vs helpfulness, LLM-as-judge techniques, bias mitigation, and continuous monitoring.</description><pubDate>Wed, 05 Nov 2025 08:16:00 GMT</pubDate><content:encoded><![CDATA[<h1 id="gdpo-multi-reward-rl-done-right">GDPO: Multi-Reward RL Done Right</h1>
<p><strong>Part 4 of 4:</strong> The Multi-Reward Frontier</p>
<blockquote>
<p><strong>TL;DR:</strong> GRPO works beautifully for single rewards, but breaks down with multiple rewards. When you normalize the sum of rewards, distinct reward combinations collapse to identical advantages—you lose training signal resolution. GDPO (Group reward-Decoupled normalization Policy Optimization) fixes this by normalizing each reward independently, then combining. Simple change, significant improvement for tool calling, math reasoning, and any multi-objective alignment task.</p>
</blockquote>
<p><strong>Reading time:</strong> ~25 minutes</p>
<p><strong>Prerequisites:</strong> <a href="https://claude.ai/blog/grpo/">Part 3: GRPO</a> covers group-relative advantage estimation.</p>
<hr>
<h2 id="the-multi-reward-reality">The Multi-Reward Reality</h2>
<p>Modern LLM training rarely optimizes a single reward. Consider these common scenarios:</p>
<p><strong>Tool Calling:</strong></p>
<ul>
<li><strong>Format reward:</strong> Does the output follow the required JSON schema?</li>
<li><strong>Correctness reward:</strong> Are the tool calls semantically correct?</li>
</ul>
<p><strong>Math Reasoning:</strong></p>
<ul>
<li><strong>Format reward:</strong> Does the output use <code>&#x3C;think></code> and <code>&#x3C;answer></code> tags?</li>
<li><strong>Correctness reward:</strong> Is the final answer mathematically correct?</li>
<li><strong>Integer reward:</strong> Is the answer an integer (not a float)?</li>
</ul>
<p><strong>Coding:</strong></p>
<ul>
<li><strong>Execution reward:</strong> Does the code run without errors?</li>
<li><strong>Correctness reward:</strong> Does it pass test cases?</li>
<li><strong>Style reward:</strong> Does it follow conventions?</li>
</ul>
<p>The naive approach: sum the rewards and apply GRPO. <em><strong>This doesn’t work well.</strong></em></p>
<hr>
<h2 id="table-of-contents">Table of Contents</h2>
<ol>
<li><a href="https://claude.ai/chat/6bf87a3b-a3f3-419c-a9c9-5ecac5ac1df0#the-advantage-collapse-problem">The Advantage Collapse Problem</a></li>
<li><a href="https://claude.ai/chat/6bf87a3b-a3f3-419c-a9c9-5ecac5ac1df0#mathematical-analysis">Mathematical Analysis</a></li>
<li><a href="https://claude.ai/chat/6bf87a3b-a3f3-419c-a9c9-5ecac5ac1df0#gdpo-the-solution">GDPO: The Solution</a></li>
<li><a href="https://claude.ai/chat/6bf87a3b-a3f3-419c-a9c9-5ecac5ac1df0#implementation">Implementation</a></li>
<li><a href="https://claude.ai/chat/6bf87a3b-a3f3-419c-a9c9-5ecac5ac1df0#experimental-results">Experimental Results</a></li>
<li><a href="https://claude.ai/chat/6bf87a3b-a3f3-419c-a9c9-5ecac5ac1df0#when-to-use-gdpo-vs-grpo">When to Use GDPO vs GRPO</a></li>
<li><a href="https://claude.ai/chat/6bf87a3b-a3f3-419c-a9c9-5ecac5ac1df0#production-patterns">Production Patterns</a></li>
<li><a href="https://claude.ai/chat/6bf87a3b-a3f3-419c-a9c9-5ecac5ac1df0#key-takeaways">Key Takeaways</a></li>
</ol>
<hr>
<h2 id="the-advantage-collapse-problem">The Advantage Collapse Problem</h2>
<h3 id="grpo-with-multiple-rewards-the-naive-approach">GRPO with Multiple Rewards: The Naive Approach</h3>
<p>Suppose we have two rewards: format (<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>r</mi><mi>f</mi></msup></mrow><annotation encoding="application/x-tex">r^f</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8491em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">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 mathnormal mtight" style="margin-right:0.10764em;">f</span></span></span></span></span></span></span></span></span></span></span>) and correctness (<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>r</mi><mi>c</mi></msup></mrow><annotation encoding="application/x-tex">r^c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6644em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><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">c</span></span></span></span></span></span></span></span></span></span></span>). The naive approach:</p>
<ol>
<li>Combine rewards: <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><mo>=</mo><msub><mi>w</mi><mi>f</mi></msub><mo>⋅</mo><msubsup><mi>r</mi><mi>i</mi><mi>f</mi></msubsup><mo>+</mo><msub><mi>w</mi><mi>c</mi></msub><mo>⋅</mo><msubsup><mi>r</mi><mi>i</mi><mi>c</mi></msubsup></mrow><annotation encoding="application/x-tex">r_i = w_f \cdot r_i^f + w_c \cdot r_i^c</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.02778em;">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.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.7306em;vertical-align:-0.2861em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02691em;">w</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.0269em;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.10764em;">f</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:1.2439em;vertical-align:-0.2769em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.967em;"><span style="top:-2.4231em;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 style="top:-3.1809em;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.10764em;">f</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:0.5945em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02691em;">w</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.0269em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">c</span></span></span></span><span 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.9231em;vertical-align:-0.2587em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><span style="top:-2.4413em;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 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">c</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2587em;"><span></span></span></span></span></span></span></span></span></span></li>
<li>Apply GRPO normalization: <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><mo>=</mo><mfrac><mrow><msub><mi>r</mi><mi>i</mi></msub><mo>−</mo><mtext>mean</mtext><mo stretchy="false">(</mo><mi mathvariant="bold">r</mi><mo stretchy="false">)</mo></mrow><mrow><mtext>std</mtext><mo stretchy="false">(</mo><mi mathvariant="bold">r</mi><mo stretchy="false">)</mo></mrow></mfrac></mrow><annotation encoding="application/x-tex">\hat{A}_i = \frac{r_i - \text{mean}(\mathbf{r})}{\text{std}(\mathbf{r})}</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:1.53em;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: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 text mtight"><span class="mord mtight">std</span></span><span class="mopen mtight">(</span><span class="mord mathbf mtight">r</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.02778em;">r</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.0278em;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 text mtight"><span class="mord mtight">mean</span></span><span class="mopen mtight">(</span><span class="mord mathbf mtight">r</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></li>
</ol>
<p>This is exactly what happens if you use standard GRPO with a multi-reward setup.</p>
<h3 id="the-collapse">The Collapse</h3>
<p>Consider four outputs with these reward combinations:</p>



































<table><thead><tr><th>Output</th><th>Format <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>r</mi><mi>f</mi></msup></mrow><annotation encoding="application/x-tex">r^f</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8491em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">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 mathnormal mtight" style="margin-right:0.10764em;">f</span></span></span></span></span></span></span></span></span></span></span></th><th>Correctness <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>r</mi><mi>c</mi></msup></mrow><annotation encoding="application/x-tex">r^c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6644em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><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">c</span></span></span></span></span></span></span></span></span></span></span></th><th>Combined <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi><mo>=</mo><msup><mi>r</mi><mi>f</mi></msup><mo>+</mo><msup><mi>r</mi><mi>c</mi></msup></mrow><annotation encoding="application/x-tex">r = r^f + r^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" style="margin-right:0.02778em;">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.9324em;vertical-align:-0.0833em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">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 mathnormal mtight" style="margin-right:0.10764em;">f</span></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.6644em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><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">c</span></span></span></span></span></span></span></span></span></span></span></th></tr></thead><tbody><tr><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>o</mi><mn>1</mn></msub></mrow><annotation encoding="application/x-tex">o_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">o</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></td><td>1.0</td><td>0.0</td><td>1.0</td></tr><tr><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>o</mi><mn>2</mn></msub></mrow><annotation encoding="application/x-tex">o_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">o</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></td><td>0.0</td><td>1.0</td><td>1.0</td></tr><tr><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>o</mi><mn>3</mn></msub></mrow><annotation encoding="application/x-tex">o_3</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">o</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">3</span></span></span></span><span 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></td><td>1.0</td><td>1.0</td><td>2.0</td></tr><tr><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>o</mi><mn>4</mn></msub></mrow><annotation encoding="application/x-tex">o_4</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">o</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">4</span></span></span></span><span 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></td><td>0.0</td><td>0.0</td><td>0.0</td></tr></tbody></table>
<p>Now apply GRPO normalization to combined rewards:</p>
<ul>
<li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mtext>mean</mtext><mo stretchy="false">(</mo><mi mathvariant="bold">r</mi><mo stretchy="false">)</mo><mo>=</mo><mo stretchy="false">(</mo><mn>1</mn><mo>+</mo><mn>1</mn><mo>+</mo><mn>2</mn><mo>+</mo><mn>0</mn><mo stretchy="false">)</mo><mi mathvariant="normal">/</mi><mn>4</mn><mo>=</mo><mn>1.0</mn></mrow><annotation encoding="application/x-tex">\text{mean}(\mathbf{r}) = (1 + 1 + 2 + 0) / 4 = 1.0</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">mean</span></span><span class="mopen">(</span><span class="mord mathbf">r</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="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: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.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:1em;vertical-align:-0.25em;"></span><span class="mord">0</span><span class="mclose">)</span><span class="mord">/4</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></li>
<li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mtext>std</mtext><mo stretchy="false">(</mo><mi mathvariant="bold">r</mi><mo stretchy="false">)</mo><mo>=</mo><msqrt><mrow><mo stretchy="false">(</mo><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><mn>1</mn><msup><mo stretchy="false">)</mo><mn>2</mn></msup><mo>+</mo><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><mn>1</mn><msup><mo stretchy="false">)</mo><mn>2</mn></msup><mo>+</mo><mo stretchy="false">(</mo><mn>2</mn><mo>−</mo><mn>1</mn><msup><mo stretchy="false">)</mo><mn>2</mn></msup><mo>+</mo><mo stretchy="false">(</mo><mn>0</mn><mo>−</mo><mn>1</mn><msup><mo stretchy="false">)</mo><mn>2</mn></msup><mo stretchy="false">)</mo><mi mathvariant="normal">/</mi><mn>4</mn></mrow></msqrt><mo>=</mo><msqrt><mn>0.5</mn></msqrt><mo>≈</mo><mn>0.71</mn></mrow><annotation encoding="application/x-tex">\text{std}(\mathbf{r}) = \sqrt{((1-1)^2 + (1-1)^2 + (2-1)^2 + (0-1)^2)/4} = \sqrt{0.5} \approx 0.71</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">std</span></span><span class="mopen">(</span><span class="mord mathbf">r</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.24em;vertical-align:-0.305em;"></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="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">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.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 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">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.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 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">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 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.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 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">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</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.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 class="mclose">)</span><span class="mord">/4</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:1.04em;vertical-align:-0.1328em;"></span><span class="mord sqrt"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.9072em;"><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">0.5</span></span></span><span style="top:-2.8672em;"><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.1328em;"><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.71</span></span></span></span></li>
</ul>






























<table><thead><tr><th>Output</th><th>Combined <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.02778em;">r</span></span></span></span></th><th>Advantage <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>A</mi><mo>^</mo></mover></mrow><annotation encoding="application/x-tex">\hat{A}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9468em;"></span><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></span></span></th></tr></thead><tbody><tr><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>o</mi><mn>1</mn></msub></mrow><annotation encoding="application/x-tex">o_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">o</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></td><td>1.0</td><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><mn>1</mn><mo stretchy="false">)</mo><mi mathvariant="normal">/</mi><mn>0.71</mn><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">(1-1)/0.71 = 0</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">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">1</span><span class="mclose">)</span><span class="mord">/0.71</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></td></tr><tr><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>o</mi><mn>2</mn></msub></mrow><annotation encoding="application/x-tex">o_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">o</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></td><td>1.0</td><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><mn>1</mn><mo stretchy="false">)</mo><mi mathvariant="normal">/</mi><mn>0.71</mn><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">(1-1)/0.71 = 0</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">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">1</span><span class="mclose">)</span><span class="mord">/0.71</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></td></tr><tr><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>o</mi><mn>3</mn></msub></mrow><annotation encoding="application/x-tex">o_3</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">o</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">3</span></span></span></span><span 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></td><td>2.0</td><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mn>2</mn><mo>−</mo><mn>1</mn><mo stretchy="false">)</mo><mi mathvariant="normal">/</mi><mn>0.71</mn><mo>=</mo><mn>1.41</mn></mrow><annotation encoding="application/x-tex">(2-1)/0.71 = 1.41</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">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:1em;vertical-align:-0.25em;"></span><span class="mord">1</span><span class="mclose">)</span><span class="mord">/0.71</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.41</span></span></span></span></td></tr><tr><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>o</mi><mn>4</mn></msub></mrow><annotation encoding="application/x-tex">o_4</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">o</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">4</span></span></span></span><span 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></td><td>0.0</td><td><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><mo>−</mo><mn>1</mn><mo stretchy="false">)</mo><mi mathvariant="normal">/</mi><mn>0.71</mn><mo>=</mo><mo>−</mo><mn>1.41</mn></mrow><annotation encoding="application/x-tex">(0-1)/0.71 = -1.41</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="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="mord">/0.71</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">−</span><span class="mord">1.41</span></span></span></span></td></tr></tbody></table>
<p><strong>The problem:</strong> Outputs <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>o</mi><mn>1</mn></msub></mrow><annotation encoding="application/x-tex">o_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">o</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> and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>o</mi><mn>2</mn></msub></mrow><annotation encoding="application/x-tex">o_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">o</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> get identical advantages (zero), despite having completely different reward profiles:</p>
<ul>
<li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>o</mi><mn>1</mn></msub></mrow><annotation encoding="application/x-tex">o_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">o</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>: Good format, bad correctness</li>
<li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>o</mi><mn>2</mn></msub></mrow><annotation encoding="application/x-tex">o_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">o</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>: Bad format, good correctness</li>
</ul>
<p>The normalization has <strong>collapsed</strong> these distinct cases into the same training signal.</p>
<h3 id="why-this-matters">Why This Matters</h3>
<p>With advantage collapse:</p>
<ol>
<li><strong>Lost resolution:</strong> The model can’t distinguish “format good, correctness bad” from “format bad, correctness good”</li>
<li><strong>Suboptimal convergence:</strong> Training signal is weaker than it could be</li>
<li><strong>Training instability:</strong> In extreme cases, can cause early training failure</li>
</ol>
<div class="mermaid">
graph LR
    subgraph Collapse["&#x3C;b>GRPO: Advantage Collapse&#x3C;/b>"]
        direction TB
        
        O1_grpo["o₁: Format ✓ Correct ✗&#x3C;br/>r = 1.0&#x3C;br/>━━━━━━━━&#x3C;br/>Â = 0"]
        O2_grpo["o₂: Format ✗ Correct ✓&#x3C;br/>r = 1.0&#x3C;br/>━━━━━━━━&#x3C;br/>Â = 0"]
        
        Same["&#x3C;b>Same advantage!&#x3C;/b>&#x3C;br/>━━━━━━━━&#x3C;br/>Can't distinguish&#x3C;br/>different failures"]
        
        O1_grpo --> Same
        O2_grpo --> Same
    end
    
    subgraph Preserved["&#x3C;b>GDPO: Signal Preserved&#x3C;/b>"]
        direction TB
        
        O1_gdpo["o₁: Format ✓ Correct ✗&#x3C;br/>Â_f = +1, Â_c = −1&#x3C;br/>━━━━━━━━&#x3C;br/>Combined Â = 0"]
        O2_gdpo["o₂: Format ✗ Correct ✓&#x3C;br/>Â_f = −1, Â_c = +1&#x3C;br/>━━━━━━━━&#x3C;br/>Combined Â = 0"]
        
        Diff["&#x3C;b>Different gradients!&#x3C;/b>&#x3C;br/>━━━━━━━━&#x3C;br/>Format vs correctness&#x3C;br/>learned independently"]
        
        O1_gdpo --> Diff
        O2_gdpo --> Diff
    end
    
    style O1_grpo stroke:#ef4444,stroke-width:2px
    style O2_grpo stroke:#ef4444,stroke-width:2px
    style Same stroke:#ef4444,stroke-width:2.5px
    
    style O1_gdpo stroke:#10b981,stroke-width:2px
    style O2_gdpo stroke:#10b981,stroke-width:2px
    style Diff stroke:#10b981,stroke-width:2.5px
    
    style Collapse stroke:#ef4444
    style Preserved stroke:#10b981</div>
<blockquote>
<p><strong>Key Insight:</strong> When rewards measure different aspects (format vs. correctness), combining them before normalization destroys information about <em>which</em> aspect each output excels at.</p>
</blockquote>
<hr>
<h2 id="mathematical-analysis">Mathematical Analysis</h2>
<p>Let’s formalize why advantage collapse happens.</p>
<h3 id="grpos-combined-normalization">GRPO’s Combined Normalization</h3>
<p>Given <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.07153em;">K</span></span></span></span> reward functions with weights <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>w</mi><mi>k</mi></msub></mrow><annotation encoding="application/x-tex">{w_k}</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"><span class="mord mathnormal" style="margin-right:0.02691em;">w</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.0269em;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.03148em;">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></span>, GRPO computes:</p>
<p><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><mo>=</mo><msubsup><mo>∑</mo><mrow><mi>k</mi><mo>=</mo><mn>1</mn></mrow><mi>K</mi></msubsup><msub><mi>w</mi><mi>k</mi></msub><mo>⋅</mo><msubsup><mi>r</mi><mi>i</mi><mrow><mo stretchy="false">(</mo><mi>k</mi><mo stretchy="false">)</mo></mrow></msubsup></mrow><annotation encoding="application/x-tex">r_i = \sum_{k=1}^{K} w_k \cdot r_i^{(k)}</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.02778em;">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.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.2809em;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.9812em;"><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.03148em;">k</span><span class="mrel mtight">=</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.2029em;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.07153em;">K</span></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 mathnormal" style="margin-right:0.02691em;">w</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.0269em;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.03148em;">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: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" style="margin-right:0.02778em;">r</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.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 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.03148em;">k</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></p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mover accent="true"><mi>A</mi><mo>^</mo></mover><mi>i</mi><mtext>GRPO</mtext></msubsup><mo>=</mo><mfrac><mrow><msub><mi>r</mi><mi>i</mi></msub><mo>−</mo><msub><mi>μ</mi><mi>r</mi></msub></mrow><msub><mi>σ</mi><mi>r</mi></msub></mfrac></mrow><annotation encoding="application/x-tex">\hat{A}_i^{\text{GRPO}} = \frac{r_i - \mu_r}{\sigma_r}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.2054em;vertical-align:-0.2587em;"></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.8413em;"><span style="top:-2.4413em;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.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 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.2587em;"><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.2995em;vertical-align:-0.4451em;"></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.8544em;"><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.03588em;">σ</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.02778em;">r</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></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.4461em;"><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.02778em;">r</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.0278em;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 mtight"><span class="mord mathnormal mtight">μ</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: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" style="margin-right:0.02778em;">r</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></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.4451em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></p>
<p>where <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>μ</mi><mi>r</mi></msub><mo>=</mo><mtext>mean</mtext><mo stretchy="false">(</mo><mi mathvariant="bold">r</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\mu_r = \text{mean}(\mathbf{r})</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.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.02778em;">r</span></span></span></span><span 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 text"><span class="mord">mean</span></span><span class="mopen">(</span><span class="mord mathbf">r</span><span class="mclose">)</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><mi>r</mi></msub><mo>=</mo><mtext>std</mtext><mo stretchy="false">(</mo><mi mathvariant="bold">r</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\sigma_r = \text{std}(\mathbf{r})</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.03588em;">σ</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.02778em;">r</span></span></span></span><span 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 text"><span class="mord">std</span></span><span class="mopen">(</span><span class="mord mathbf">r</span><span class="mclose">)</span></span></span></span>.</p>
<h3 id="the-collapse-condition">The Collapse Condition</h3>
<p>Outputs <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>o</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">o_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">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></span></span> and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>o</mi><mi>j</mi></msub></mrow><annotation encoding="application/x-tex">o_j</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">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" style="margin-right:0.05724em;">j</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> have identical advantages when:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mo>∑</mo><mrow><mi>k</mi><mo>=</mo><mn>1</mn></mrow><mi>K</mi></msubsup><msub><mi>w</mi><mi>k</mi></msub><mo>⋅</mo><msubsup><mi>r</mi><mi>i</mi><mrow><mo stretchy="false">(</mo><mi>k</mi><mo stretchy="false">)</mo></mrow></msubsup><mo>=</mo><msubsup><mo>∑</mo><mrow><mi>k</mi><mo>=</mo><mn>1</mn></mrow><mi>K</mi></msubsup><msub><mi>w</mi><mi>k</mi></msub><mo>⋅</mo><msubsup><mi>r</mi><mi>j</mi><mrow><mo stretchy="false">(</mo><mi>k</mi><mo stretchy="false">)</mo></mrow></msubsup></mrow><annotation encoding="application/x-tex">\sum_{k=1}^{K} w_k \cdot r_i^{(k)} = \sum_{k=1}^{K} w_k \cdot r_j^{(k)}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.2809em;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.9812em;"><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.03148em;">k</span><span class="mrel mtight">=</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.2029em;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.07153em;">K</span></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 mathnormal" style="margin-right:0.02691em;">w</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.0269em;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.03148em;">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: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" style="margin-right:0.02778em;">r</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.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 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.03148em;">k</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.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.2809em;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.9812em;"><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.03148em;">k</span><span class="mrel mtight">=</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.2029em;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.07153em;">K</span></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 mathnormal" style="margin-right:0.02691em;">w</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.0269em;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.03148em;">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: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.4578em;vertical-align:-0.413em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">r</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.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.05724em;">j</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.03148em;">k</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.413em;"><span></span></span></span></span></span></span></span></span></span></p>
<p>This happens whenever different reward combinations sum to the same value—<strong>regardless of which specific rewards contributed</strong>.</p>
<h3 id="information-loss">Information Loss</h3>
<p>Consider the space of possible reward vectors in <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi mathvariant="double-struck">R</mi><mi>K</mi></msup></mrow><annotation encoding="application/x-tex">\mathbb{R}^K</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8413em;"></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.8413em;"><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.07153em;">K</span></span></span></span></span></span></span></span></span></span></span> (K rewards). GRPO projects this to <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi mathvariant="double-struck">R</mi><mn>1</mn></msup></mrow><annotation encoding="application/x-tex">\mathbb{R}^1</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 mathbb">R</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">1</span></span></span></span></span></span></span></span></span></span></span> (the weighted sum), then normalizes.</p>
<p><strong>Information lost:</strong> All reward vectors mapping to the same sum become indistinguishable.</p>
<p>For <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>K</mi><mo>=</mo><mn>2</mn></mrow><annotation encoding="application/x-tex">K=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.07153em;">K</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> rewards with equal weights, the level sets are lines in 2D:</p>
<ul>
<li><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><mo separator="true">,</mo><mn>2</mn><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">(0, 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="mopen">(</span><span class="mord">0</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">2</span><span class="mclose">)</span></span></span></span> and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mn>2</mn><mo separator="true">,</mo><mn>0</mn><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">(2, 0)</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">2</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">0</span><span class="mclose">)</span></span></span></span> and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mn>1</mn><mo separator="true">,</mo><mn>1</mn><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">(1, 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">1</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> all map to sum <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>=</mo><mn>2</mn></mrow><annotation encoding="application/x-tex">= 2</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:0.6444em;"></span><span class="mord">2</span></span></span></span></li>
<li>These represent very different behaviors, but get identical treatment</li>
</ul>
<h3 id="variance-collapse">Variance Collapse</h3>
<p>Even more problematic: when rewards are highly correlated or when one dominates, the variance of combined rewards shrinks.</p>
<p><strong>Example:</strong> If format reward is always 1 (all outputs follow format), then:</p>
<ul>
<li><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><mo>=</mo><mn>1</mn><mo>⋅</mo><msub><mi>w</mi><mi>f</mi></msub><mo>+</mo><msubsup><mi>r</mi><mi>i</mi><mi>c</mi></msubsup><mo>⋅</mo><msub><mi>w</mi><mi>c</mi></msub></mrow><annotation encoding="application/x-tex">r_i = 1 \cdot w_f + r_i^c \cdot w_c</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.02778em;">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.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 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.8694em;vertical-align:-0.2861em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02691em;">w</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.0269em;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.10764em;">f</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.9231em;vertical-align:-0.2587em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><span style="top:-2.4413em;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 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">c</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2587em;"><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.5806em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02691em;">w</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.0269em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">c</span></span></span></span><span 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>
<li>Variance comes only from correctness</li>
<li>Format reward contributes nothing to the training signal</li>
</ul>
<hr>
<h2 id="gdpo-the-solution">GDPO: The Solution</h2>
<p><strong>Group reward-Decoupled normalization Policy Optimization (GDPO)</strong> normalizes each reward independently before combining.</p>
<h3 id="the-gdpo-algorithm">The GDPO Algorithm</h3>
<p>For each reward <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi><mo>∈</mo><mrow><mn>1</mn><mo separator="true">,</mo><mo>…</mo><mo separator="true">,</mo><mi>K</mi></mrow></mrow><annotation encoding="application/x-tex">k \in {1, \ldots, K}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7335em;vertical-align:-0.0391em;"></span><span class="mord mathnormal" style="margin-right:0.03148em;">k</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.8778em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord">1</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 mathnormal" style="margin-right:0.07153em;">K</span></span></span></span></span>:</p>
<p><strong>Step 1: Normalize each reward independently</strong></p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mover accent="true"><mi>A</mi><mo>^</mo></mover><mi>i</mi><mrow><mo stretchy="false">(</mo><mi>k</mi><mo stretchy="false">)</mo></mrow></msubsup><mo>=</mo><mfrac><mrow><msubsup><mi>r</mi><mi>i</mi><mrow><mo stretchy="false">(</mo><mi>k</mi><mo stretchy="false">)</mo></mrow></msubsup><mo>−</mo><msup><mi>μ</mi><mrow><mo stretchy="false">(</mo><mi>k</mi><mo stretchy="false">)</mo></mrow></msup></mrow><msup><mi>σ</mi><mrow><mo stretchy="false">(</mo><mi>k</mi><mo stretchy="false">)</mo></mrow></msup></mfrac></mrow><annotation encoding="application/x-tex">\hat{A}_i^{(k)} = \frac{r_i^{(k)} - \mu^{(k)}}{\sigma^{(k)}}</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 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: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.03148em;">k</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.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.6624em;vertical-align:-0.3854em;"></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.277em;"><span style="top:-2.6146em;"><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.03588em;">σ</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.822em;"><span style="top:-2.822em;margin-right:0.0714em;"><span class="pstrut" style="height:2.5357em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight"><span class="mopen mtight">(</span><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</span><span class="mclose mtight">)</span></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.5356em;"><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.02778em;">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.0591em;"><span style="top:-2.2134em;margin-left:-0.0278em;margin-right:0.0714em;"><span class="pstrut" style="height:2.5357em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mathnormal mtight">i</span></span></span><span style="top:-3.0591em;margin-right:0.0714em;"><span class="pstrut" style="height:2.5357em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight"><span class="mopen mtight">(</span><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</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.3223em;"><span></span></span></span></span></span></span><span class="mbin mtight">−</span><span class="mord mtight"><span class="mord mathnormal mtight">μ</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9667em;"><span style="top:-2.9667em;margin-right:0.0714em;"><span class="pstrut" style="height:2.5357em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight"><span class="mopen mtight">(</span><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</span><span class="mclose mtight">)</span></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.3854em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></p>
<p>where <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>μ</mi><mrow><mo stretchy="false">(</mo><mi>k</mi><mo stretchy="false">)</mo></mrow></msup><mo>=</mo><mtext>mean</mtext><mo stretchy="false">(</mo><msup><mi mathvariant="bold">r</mi><mrow><mo stretchy="false">(</mo><mi>k</mi><mo stretchy="false">)</mo></mrow></msup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\mu^{(k)} = \text{mean}(\mathbf{r}^{(k)})</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0824em;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.888em;"><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="mopen mtight">(</span><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</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.138em;vertical-align:-0.25em;"></span><span class="mord text"><span class="mord">mean</span></span><span class="mopen">(</span><span class="mord"><span class="mord mathbf">r</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.888em;"><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="mopen mtight">(</span><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</span><span class="mclose mtight">)</span></span></span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span> and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>σ</mi><mrow><mo stretchy="false">(</mo><mi>k</mi><mo stretchy="false">)</mo></mrow></msup><mo>=</mo><mtext>std</mtext><mo stretchy="false">(</mo><msup><mi mathvariant="bold">r</mi><mrow><mo stretchy="false">(</mo><mi>k</mi><mo stretchy="false">)</mo></mrow></msup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\sigma^{(k)} = \text{std}(\mathbf{r}^{(k)})</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.888em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">σ</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.888em;"><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="mopen mtight">(</span><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</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.138em;vertical-align:-0.25em;"></span><span class="mord text"><span class="mord">std</span></span><span class="mopen">(</span><span class="mord"><span class="mord mathbf">r</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.888em;"><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="mopen mtight">(</span><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</span><span class="mclose mtight">)</span></span></span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span>.</p>
<p><strong>Step 2: Combine normalized advantages</strong></p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>A</mi><mo>^</mo></mover><mo>∗</mo><msup><mi>i</mi><mtext>pre</mtext></msup><mo>=</mo><mo>∑</mo><mo>∗</mo><msup><mrow><mi>k</mi><mo>=</mo><mn>1</mn></mrow><mi>K</mi></msup><msub><mi>w</mi><mi>k</mi></msub><mo>⋅</mo><msubsup><mover accent="true"><mi>A</mi><mo>^</mo></mover><mi>i</mi><mrow><mo stretchy="false">(</mo><mi>k</mi><mo stretchy="false">)</mo></mrow></msubsup></mrow><annotation encoding="application/x-tex">\hat{A}*i^{\text{pre}} = \sum*{k=1}^{K} w_k \cdot \hat{A}_i^{(k)}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9468em;"></span><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="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.6644em;"></span><span class="mord"><span class="mord mathnormal">i</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><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 text mtight"><span class="mord mtight">pre</span></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.1757em;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><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.03148em;">k</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">1</span></span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9257em;"><span style="top:-3.1473em;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.07153em;">K</span></span></span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02691em;">w</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.0269em;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.03148em;">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: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 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: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.03148em;">k</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></p>
<p><strong>Step 3: Final batch normalization</strong></p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>A</mi><mo>^</mo></mover><mo>∗</mo><msup><mi>i</mi><mtext>GDPO</mtext></msup><mo>=</mo><mfrac><mrow><mover accent="true"><mi>A</mi><mo>^</mo></mover><mstyle mathcolor="#cc0000"><mtext>\*</mtext></mstyle><msup><mi>i</mi><mtext>pre</mtext></msup><mo>−</mo><mi>μ</mi><mstyle mathcolor="#cc0000"><mtext>\*</mtext></mstyle><mtext>pre</mtext></mrow><mrow><mi>σ</mi><mo>∗</mo><mtext>pre</mtext></mrow></mfrac></mrow><annotation encoding="application/x-tex">\hat{A}*i^{\text{GDPO}} = \frac{\hat{A}\*i^{\text{pre}} - \mu\*{\text{pre}}}{\sigma*{\text{pre}}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9468em;"></span><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="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.8413em;"></span><span class="mord"><span class="mord mathnormal">i</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8413em;"><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 text mtight"><span class="mord mtight">GDPO</span></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.6288em;vertical-align:-0.4811em;"></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.1477em;"><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.03588em;">σ</span><span class="mbin mtight">∗</span><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">pre</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.485em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord accent mtight"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9468em;"><span style="top:-2.7em;"><span class="pstrut" style="height:2.7em;"></span><span class="mord mathnormal mtight">A</span></span><span style="top:-2.9523em;"><span class="pstrut" style="height:2.7em;"></span><span class="accent-body" style="left:-0.1111em;"><span class="mord mtight">^</span></span></span></span></span></span></span><span class="mord text mtight" style="color:#cc0000;"><span class="mord mtight" style="color:#cc0000;">\*</span></span><span class="mord mtight"><span class="mord mathnormal mtight">i</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7385em;"><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"><span class="mord text mtight"><span class="mord mtight">pre</span></span></span></span></span></span></span></span></span></span><span class="mbin mtight">−</span><span class="mord mathnormal mtight">μ</span><span class="mord text mtight" style="color:#cc0000;"><span class="mord mtight" style="color:#cc0000;">\*</span></span><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">pre</span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.4811em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></p>
<h3 id="why-this-works">Why This Works</h3>
<p><strong>Decoupled normalization preserves individual reward signals:</strong></p>
<p>Going back to our example:</p>













































<table><thead><tr><th>Output</th><th><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>r</mi><mi>f</mi></msup></mrow><annotation encoding="application/x-tex">r^f</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8491em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">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 mathnormal mtight" style="margin-right:0.10764em;">f</span></span></span></span></span></span></span></span></span></span></span></th><th><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>r</mi><mi>c</mi></msup></mrow><annotation encoding="application/x-tex">r^c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6644em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><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">c</span></span></span></span></span></span></span></span></span></span></span></th><th><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mover accent="true"><mi>A</mi><mo>^</mo></mover><mi>f</mi></msup></mrow><annotation encoding="application/x-tex">\hat{A}^f</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9468em;"></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"><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.10764em;">f</span></span></span></span></span></span></span></span></span></span></span></th><th><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mover accent="true"><mi>A</mi><mo>^</mo></mover><mi>c</mi></msup></mrow><annotation encoding="application/x-tex">\hat{A}^c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9468em;"></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"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><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">c</span></span></span></span></span></span></span></span></span></span></span></th><th><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mover accent="true"><mi>A</mi><mo>^</mo></mover><mtext>pre</mtext></msup></mrow><annotation encoding="application/x-tex">\hat{A}^{\text{pre}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9468em;"></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"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><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 text mtight"><span class="mord mtight">pre</span></span></span></span></span></span></span></span></span></span></span></span></span></th></tr></thead><tbody><tr><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>o</mi><mn>1</mn></msub></mrow><annotation encoding="application/x-tex">o_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">o</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></td><td>1.0</td><td>0.0</td><td>+1.0</td><td>-1.0</td><td>0.0</td></tr><tr><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>o</mi><mn>2</mn></msub></mrow><annotation encoding="application/x-tex">o_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">o</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></td><td>0.0</td><td>1.0</td><td>-1.0</td><td>+1.0</td><td>0.0</td></tr><tr><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>o</mi><mn>3</mn></msub></mrow><annotation encoding="application/x-tex">o_3</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">o</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">3</span></span></span></span><span 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></td><td>1.0</td><td>1.0</td><td>+1.0</td><td>+1.0</td><td>+2.0</td></tr><tr><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>o</mi><mn>4</mn></msub></mrow><annotation encoding="application/x-tex">o_4</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">o</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">4</span></span></span></span><span 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></td><td>0.0</td><td>0.0</td><td>-1.0</td><td>-1.0</td><td>-2.0</td></tr></tbody></table>
<p>Now <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>o</mi><mn>1</mn></msub></mrow><annotation encoding="application/x-tex">o_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">o</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> and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>o</mi><mn>2</mn></msub></mrow><annotation encoding="application/x-tex">o_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">o</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> still have the same final advantage (after step 3), <strong>but the gradient is different</strong>:</p>
<p>For <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>o</mi><mn>1</mn></msub></mrow><annotation encoding="application/x-tex">o_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">o</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>: Gradient encourages improving correctness (negative <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mover accent="true"><mi>A</mi><mo>^</mo></mover><mi>c</mi></msup></mrow><annotation encoding="application/x-tex">\hat{A}^c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9468em;"></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"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><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">c</span></span></span></span></span></span></span></span></span></span></span>), maintaining format (positive <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mover accent="true"><mi>A</mi><mo>^</mo></mover><mi>f</mi></msup></mrow><annotation encoding="application/x-tex">\hat{A}^f</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9468em;"></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"><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.10764em;">f</span></span></span></span></span></span></span></span></span></span></span>)</p>
<p>For <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>o</mi><mn>2</mn></msub></mrow><annotation encoding="application/x-tex">o_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">o</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>: Gradient encourages improving format (negative <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mover accent="true"><mi>A</mi><mo>^</mo></mover><mi>f</mi></msup></mrow><annotation encoding="application/x-tex">\hat{A}^f</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9468em;"></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"><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.10764em;">f</span></span></span></span></span></span></span></span></span></span></span>), maintaining correctness (positive <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mover accent="true"><mi>A</mi><mo>^</mo></mover><mi>c</mi></msup></mrow><annotation encoding="application/x-tex">\hat{A}^c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9468em;"></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"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><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">c</span></span></span></span></span></span></span></span></span></span></span>)</p>
<p>The model learns which specific reward to improve for each output.</p>
<h3 id="visual-comparison">Visual Comparison</h3>
<div class="mermaid">
graph TB
    subgraph GRPO["&#x3C;b>GRPO: Combined Normalization&#x3C;/b>"]
        direction TB
        
        G_R1["r₁ (format)"] --> G_Sum["&#x3C;b>Sum&#x3C;/b>&#x3C;br/>r = w₁r₁ + w₂r₂"]
        G_R2["r₂ (correct)"] --> G_Sum
        G_Sum --> G_Norm["&#x3C;b>Normalize&#x3C;/b>&#x3C;br/>Â = (r − μ) / σ"]
        G_Norm --> G_Adv["Advantage Â"]
    end
    
    subgraph GDPO["&#x3C;b>GDPO: Decoupled Normalization&#x3C;/b>"]
        direction TB
        
        D_R1["r₁ (format)"] --> D_N1["&#x3C;b>Normalize&#x3C;/b>&#x3C;br/>Â₁ = (r₁ − μ₁) / σ₁"]
        D_R2["r₂ (correct)"] --> D_N2["&#x3C;b>Normalize&#x3C;/b>&#x3C;br/>Â₂ = (r₂ − μ₂) / σ₂"]
        
        D_N1 --> D_Comb["&#x3C;b>Combine&#x3C;/b>&#x3C;br/>Â_pre = w₁Â₁ + w₂Â₂"]
        D_N2 --> D_Comb
        
        D_Comb --> D_BN["&#x3C;b>Batch Normalize&#x3C;/b>&#x3C;br/>Â = (Â_pre − μ) / σ"]
        D_BN --> D_Adv["Advantage Â"]
    end
    
    style G_Sum stroke:#ef4444,stroke-width:2px
    style G_Norm stroke:#ef4444,stroke-width:2px
    
    style D_N1 stroke:#10b981,stroke-width:2px
    style D_N2 stroke:#10b981,stroke-width:2px
    style D_Comb stroke:#06b6d4,stroke-width:2px
    style D_BN stroke:#8b5cf6,stroke-width:2px
    
    style GRPO stroke:#ef4444
    style GDPO stroke:#10b981</div>
<blockquote>
<p><strong>Key Insight:</strong> GDPO’s decoupled normalization ensures each reward contributes meaningfully to the gradient, regardless of its scale or correlation with other rewards.</p>
</blockquote>
<hr>
<h2 id="implementation">Implementation</h2>
<p>GDPO is a straightforward modification to GRPO. Here’s the key difference:</p>
<h3 id="grpo-original">GRPO (Original)</h3>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#F97583">def</span><span style="color:#B392F0"> grpo_advantages</span><span style="color:#E1E4E8">(rewards_per_func, reward_weights, num_generations):</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#9ECBFF">    GRPO: Normalize combined rewards.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Args:</span></span>
<span class="line"><span style="color:#9ECBFF">        rewards_per_func: [batch, K] rewards from K reward functions</span></span>
<span class="line"><span style="color:#9ECBFF">        reward_weights: [K] weights for each reward</span></span>
<span class="line"><span style="color:#9ECBFF">        num_generations: G, outputs per prompt</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#6A737D">    # Combine rewards first</span></span>
<span class="line"><span style="color:#E1E4E8">    rewards </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> (rewards_per_func </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> reward_weights).sum(</span><span style="color:#FFAB70">dim</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">)  </span><span style="color:#6A737D"># [batch]</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Then normalize</span></span>
<span class="line"><span style="color:#E1E4E8">    mean_grouped </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> rewards.view(</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">, num_generations).mean(</span><span style="color:#FFAB70">dim</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">    std_grouped </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> rewards.view(</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">, num_generations).std(</span><span style="color:#FFAB70">dim</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#E1E4E8">    mean_grouped </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> mean_grouped.repeat_interleave(num_generations)</span></span>
<span class="line"><span style="color:#E1E4E8">    std_grouped </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> std_grouped.repeat_interleave(num_generations)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#E1E4E8">    advantages </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> (rewards </span><span style="color:#F97583">-</span><span style="color:#E1E4E8"> mean_grouped) </span><span style="color:#F97583">/</span><span style="color:#E1E4E8"> (std_grouped </span><span style="color:#F97583">+</span><span style="color:#79B8FF"> 1e-4</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    return</span><span style="color:#E1E4E8"> advantages</span></span>
<span class="line"></span></code></pre>
<h3 id="gdpo-decoupled">GDPO (Decoupled)</h3>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#F97583">def</span><span style="color:#B392F0"> gdpo_advantages</span><span style="color:#E1E4E8">(rewards_per_func, reward_weights, num_generations):</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#9ECBFF">    GDPO: Normalize each reward independently, then combine.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Args:</span></span>
<span class="line"><span style="color:#9ECBFF">        rewards_per_func: [batch, K] rewards from K reward functions</span></span>
<span class="line"><span style="color:#9ECBFF">        reward_weights: [K] weights for each reward</span></span>
<span class="line"><span style="color:#9ECBFF">        num_generations: G, outputs per prompt</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#E1E4E8">    K </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> len</span><span style="color:#E1E4E8">(reward_weights)</span></span>
<span class="line"><span style="color:#E1E4E8">    device </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> rewards_per_func.device</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Handle NaN values</span></span>
<span class="line"><span style="color:#E1E4E8">    rewards_per_func </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.nan_to_num(rewards_per_func)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#E1E4E8">    all_reward_advantages </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> []</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Step 1: Normalize each reward independently</span></span>
<span class="line"><span style="color:#F97583">    for</span><span style="color:#E1E4E8"> k </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> range</span><span style="color:#E1E4E8">(K):</span></span>
<span class="line"><span style="color:#E1E4E8">        reward_k </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> rewards_per_func[:, k]  </span><span style="color:#6A737D"># [batch]</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Group-wise normalization for this reward</span></span>
<span class="line"><span style="color:#E1E4E8">        mean_k </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> reward_k.view(</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">, num_generations).mean(</span><span style="color:#FFAB70">dim</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        std_k </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> reward_k.view(</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">, num_generations).std(</span><span style="color:#FFAB70">dim</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#E1E4E8">        mean_k </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> mean_k.repeat_interleave(num_generations)</span></span>
<span class="line"><span style="color:#E1E4E8">        std_k </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> std_k.repeat_interleave(num_generations)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#E1E4E8">        advantage_k </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> (reward_k </span><span style="color:#F97583">-</span><span style="color:#E1E4E8"> mean_k) </span><span style="color:#F97583">/</span><span style="color:#E1E4E8"> (std_k </span><span style="color:#F97583">+</span><span style="color:#79B8FF"> 1e-4</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        all_reward_advantages.append(advantage_k)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Step 2: Combine normalized advantages</span></span>
<span class="line"><span style="color:#E1E4E8">    combined_advantages </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.stack(all_reward_advantages, </span><span style="color:#FFAB70">dim</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">)  </span><span style="color:#6A737D"># [batch, K]</span></span>
<span class="line"><span style="color:#E1E4E8">    pre_bn_advantages </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> (combined_advantages </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> reward_weights).sum(</span><span style="color:#FFAB70">dim</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">)  </span><span style="color:#6A737D"># [batch]</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Step 3: Final batch normalization</span></span>
<span class="line"><span style="color:#E1E4E8">    bn_mean </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> pre_bn_advantages.mean()</span></span>
<span class="line"><span style="color:#E1E4E8">    bn_std </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> pre_bn_advantages.std()</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#E1E4E8">    advantages </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> (pre_bn_advantages </span><span style="color:#F97583">-</span><span style="color:#E1E4E8"> bn_mean) </span><span style="color:#F97583">/</span><span style="color:#E1E4E8"> (bn_std </span><span style="color:#F97583">+</span><span style="color:#79B8FF"> 1e-4</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    return</span><span style="color:#E1E4E8"> advantages</span></span>
<span class="line"></span></code></pre>
<h3 id="complete-gdpo-trainer">Complete GDPO Trainer</h3>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#9ECBFF">"""</span></span>
<span class="line"><span style="color:#9ECBFF">Group reward-Decoupled normalization Policy Optimization (GDPO)</span></span>
<span class="line"><span style="color:#9ECBFF">===============================================================</span></span>
<span class="line"></span>
<span class="line"><span style="color:#9ECBFF">Extends GRPO for multi-reward settings by normalizing each</span></span>
<span class="line"><span style="color:#9ECBFF">reward independently before combining.</span></span>
<span class="line"></span>
<span class="line"><span style="color:#9ECBFF">Reference: NVIDIA (Liu et al., 2026)</span></span>
<span class="line"><span style="color:#9ECBFF">"""</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> torch</span></span>
<span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> torch.nn.functional </span><span style="color:#F97583">as</span><span style="color:#E1E4E8"> F</span></span>
<span class="line"><span style="color:#F97583">from</span><span style="color:#E1E4E8"> torch </span><span style="color:#F97583">import</span><span style="color:#E1E4E8"> Tensor</span></span>
<span class="line"><span style="color:#F97583">from</span><span style="color:#E1E4E8"> dataclasses </span><span style="color:#F97583">import</span><span style="color:#E1E4E8"> dataclass</span></span>
<span class="line"><span style="color:#F97583">from</span><span style="color:#E1E4E8"> typing </span><span style="color:#F97583">import</span><span style="color:#E1E4E8"> List, Tuple, Callable</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#B392F0">@dataclass</span></span>
<span class="line"><span style="color:#F97583">class</span><span style="color:#B392F0"> GDPOConfig</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#9ECBFF">    """GDPO hyperparameters."""</span></span>
<span class="line"><span style="color:#E1E4E8">    group_size: </span><span style="color:#79B8FF">int</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 64</span></span>
<span class="line"><span style="color:#E1E4E8">    clip_epsilon: </span><span style="color:#79B8FF">float</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 0.2</span></span>
<span class="line"><span style="color:#E1E4E8">    kl_coef: </span><span style="color:#79B8FF">float</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 0.04</span></span>
<span class="line"><span style="color:#E1E4E8">    learning_rate: </span><span style="color:#79B8FF">float</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 1e-6</span></span>
<span class="line"><span style="color:#E1E4E8">    max_grad_norm: </span><span style="color:#79B8FF">float</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 1.0</span></span>
<span class="line"><span style="color:#E1E4E8">    eps: </span><span style="color:#79B8FF">float</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 1e-4</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">def</span><span style="color:#B392F0"> compute_gdpo_advantages</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#E1E4E8">    rewards_list: List[Tensor],</span></span>
<span class="line"><span style="color:#E1E4E8">    reward_weights: List[</span><span style="color:#79B8FF">float</span><span style="color:#E1E4E8">],</span></span>
<span class="line"><span style="color:#E1E4E8">    eps: </span><span style="color:#79B8FF">float</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 1e-4</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">) -> Tuple[Tensor, List[Tensor]]:</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#9ECBFF">    Compute GDPO advantages with decoupled normalization.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Args:</span></span>
<span class="line"><span style="color:#9ECBFF">        rewards_list: List of [G] tensors, one per reward function</span></span>
<span class="line"><span style="color:#9ECBFF">        reward_weights: Weights for each reward</span></span>
<span class="line"><span style="color:#9ECBFF">        eps: Numerical stability constant</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">    Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">        advantages: [G] final normalized advantages</span></span>
<span class="line"><span style="color:#9ECBFF">        per_reward_advantages: List of [G] tensors for logging</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#E1E4E8">    K </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> len</span><span style="color:#E1E4E8">(rewards_list)</span></span>
<span class="line"><span style="color:#E1E4E8">    G </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> rewards_list[</span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">].shape[</span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">]</span></span>
<span class="line"><span style="color:#E1E4E8">    device </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> rewards_list[</span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">].device</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Step 1: Normalize each reward independently</span></span>
<span class="line"><span style="color:#E1E4E8">    normalized_rewards </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> []</span></span>
<span class="line"><span style="color:#F97583">    for</span><span style="color:#E1E4E8"> k </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> range</span><span style="color:#E1E4E8">(K):</span></span>
<span class="line"><span style="color:#E1E4E8">        r_k </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> rewards_list[k]</span></span>
<span class="line"><span style="color:#E1E4E8">        mean_k </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> r_k.mean()</span></span>
<span class="line"><span style="color:#E1E4E8">        std_k </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> r_k.std() </span><span style="color:#F97583">+</span><span style="color:#E1E4E8"> eps</span></span>
<span class="line"><span style="color:#E1E4E8">        normalized_k </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> (r_k </span><span style="color:#F97583">-</span><span style="color:#E1E4E8"> mean_k) </span><span style="color:#F97583">/</span><span style="color:#E1E4E8"> std_k</span></span>
<span class="line"><span style="color:#E1E4E8">        normalized_rewards.append(normalized_k)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Step 2: Combine with weights</span></span>
<span class="line"><span style="color:#E1E4E8">    combined </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.zeros(G, </span><span style="color:#FFAB70">device</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">device)</span></span>
<span class="line"><span style="color:#F97583">    for</span><span style="color:#E1E4E8"> k </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> range</span><span style="color:#E1E4E8">(K):</span></span>
<span class="line"><span style="color:#E1E4E8">        combined </span><span style="color:#F97583">+=</span><span style="color:#E1E4E8"> reward_weights[k] </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> normalized_rewards[k]</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Step 3: Final batch normalization</span></span>
<span class="line"><span style="color:#E1E4E8">    final_mean </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> combined.mean()</span></span>
<span class="line"><span style="color:#E1E4E8">    final_std </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> combined.std() </span><span style="color:#F97583">+</span><span style="color:#E1E4E8"> eps</span></span>
<span class="line"><span style="color:#E1E4E8">    advantages </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> (combined </span><span style="color:#F97583">-</span><span style="color:#E1E4E8"> final_mean) </span><span style="color:#F97583">/</span><span style="color:#E1E4E8"> final_std</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    return</span><span style="color:#E1E4E8"> advantages, normalized_rewards</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">class</span><span style="color:#B392F0"> GDPOTrainer</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#9ECBFF">    GDPO trainer for multi-reward LLM alignment.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Key difference from GRPO: each reward is normalized</span></span>
<span class="line"><span style="color:#9ECBFF">    independently before combining, preserving signal resolution.</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#79B8FF"> __init__</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#E1E4E8">        self,</span></span>
<span class="line"><span style="color:#E1E4E8">        policy_model: torch.nn.Module,</span></span>
<span class="line"><span style="color:#E1E4E8">        reference_model: torch.nn.Module,</span></span>
<span class="line"><span style="color:#E1E4E8">        reward_functions: List[Callable],</span></span>
<span class="line"><span style="color:#E1E4E8">        reward_weights: List[</span><span style="color:#79B8FF">float</span><span style="color:#E1E4E8">],</span></span>
<span class="line"><span style="color:#E1E4E8">        tokenizer,</span></span>
<span class="line"><span style="color:#E1E4E8">        config: GDPOConfig,</span></span>
<span class="line"><span style="color:#E1E4E8">    ):</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.policy </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> policy_model</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.reference </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> reference_model</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.reward_fns </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> reward_functions</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.reward_weights </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.tensor(reward_weights)</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.tokenizer </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> tokenizer</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.config </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> config</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        assert</span><span style="color:#79B8FF"> len</span><span style="color:#E1E4E8">(reward_functions) </span><span style="color:#F97583">==</span><span style="color:#79B8FF"> len</span><span style="color:#E1E4E8">(reward_weights), \</span></span>
<span class="line"><span style="color:#9ECBFF">            "Must have same number of reward functions and weights"</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Freeze reference</span></span>
<span class="line"><span style="color:#F97583">        for</span><span style="color:#E1E4E8"> param </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.reference.parameters():</span></span>
<span class="line"><span style="color:#E1E4E8">            param.requires_grad </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> False</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.optimizer </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.optim.AdamW(</span></span>
<span class="line"><span style="color:#79B8FF">            self</span><span style="color:#E1E4E8">.policy.parameters(),</span></span>
<span class="line"><span style="color:#FFAB70">            lr</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">config.learning_rate,</span></span>
<span class="line"><span style="color:#E1E4E8">        )</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#B392F0">    @torch.no_grad</span><span style="color:#E1E4E8">()</span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> sample_and_score</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#E1E4E8">        self,</span></span>
<span class="line"><span style="color:#E1E4E8">        prompt_text: </span><span style="color:#79B8FF">str</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">        prompt_ids: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">        attention_mask: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">    ) -> Tuple[Tensor, List[Tensor], Tensor]:</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Sample G outputs and compute all rewards.</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">        Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">            output_ids: [G, seq_len] generated sequences</span></span>
<span class="line"><span style="color:#9ECBFF">            rewards_list: List of [G] tensors, one per reward function</span></span>
<span class="line"><span style="color:#9ECBFF">            output_masks: [G, seq_len] attention masks</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#E1E4E8">        G </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.config.group_size</span></span>
<span class="line"><span style="color:#E1E4E8">        prompt_len </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> prompt_ids.shape[</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">]</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Expand for batch generation</span></span>
<span class="line"><span style="color:#E1E4E8">        prompt_ids </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> prompt_ids.expand(G, </span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        attention_mask </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> attention_mask.expand(G, </span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Sample</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.policy.eval()</span></span>
<span class="line"><span style="color:#E1E4E8">        outputs </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.policy.generate(</span></span>
<span class="line"><span style="color:#FFAB70">            input_ids</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">prompt_ids,</span></span>
<span class="line"><span style="color:#FFAB70">            attention_mask</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">attention_mask,</span></span>
<span class="line"><span style="color:#FFAB70">            max_new_tokens</span><span style="color:#F97583">=</span><span style="color:#79B8FF">512</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#FFAB70">            do_sample</span><span style="color:#F97583">=</span><span style="color:#79B8FF">True</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#FFAB70">            temperature</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1.0</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#FFAB70">            pad_token_id</span><span style="color:#F97583">=</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.tokenizer.pad_token_id,</span></span>
<span class="line"><span style="color:#E1E4E8">        )</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#E1E4E8">        output_ids </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> outputs</span></span>
<span class="line"><span style="color:#E1E4E8">        output_masks </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> (output_ids </span><span style="color:#F97583">!=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.tokenizer.pad_token_id).long()</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Score with each reward function</span></span>
<span class="line"><span style="color:#E1E4E8">        rewards_list </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> []</span></span>
<span class="line"><span style="color:#F97583">        for</span><span style="color:#E1E4E8"> reward_fn </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.reward_fns:</span></span>
<span class="line"><span style="color:#E1E4E8">            rewards_k </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> []</span></span>
<span class="line"><span style="color:#F97583">            for</span><span style="color:#E1E4E8"> i </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> range</span><span style="color:#E1E4E8">(G):</span></span>
<span class="line"><span style="color:#E1E4E8">                response_ids </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> output_ids[i, prompt_len:]</span></span>
<span class="line"><span style="color:#E1E4E8">                response_text </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.tokenizer.decode(</span></span>
<span class="line"><span style="color:#E1E4E8">                    response_ids, </span><span style="color:#FFAB70">skip_special_tokens</span><span style="color:#F97583">=</span><span style="color:#79B8FF">True</span></span>
<span class="line"><span style="color:#E1E4E8">                )</span></span>
<span class="line"><span style="color:#E1E4E8">                r </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> reward_fn(prompt_text, response_text)</span></span>
<span class="line"><span style="color:#E1E4E8">                rewards_k.append(r)</span></span>
<span class="line"><span style="color:#E1E4E8">            rewards_list.append(</span></span>
<span class="line"><span style="color:#E1E4E8">                torch.tensor(rewards_k, </span><span style="color:#FFAB70">device</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">output_ids.device)</span></span>
<span class="line"><span style="color:#E1E4E8">            )</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        return</span><span style="color:#E1E4E8"> output_ids, rewards_list, output_masks</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> compute_log_probs</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#E1E4E8">        self,</span></span>
<span class="line"><span style="color:#E1E4E8">        model: torch.nn.Module,</span></span>
<span class="line"><span style="color:#E1E4E8">        input_ids: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">        attention_mask: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">        response_start: </span><span style="color:#79B8FF">int</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">    ) -> Tensor:</span></span>
<span class="line"><span style="color:#9ECBFF">        """Compute per-token log probs for response."""</span></span>
<span class="line"><span style="color:#E1E4E8">        outputs </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> model(input_ids, </span><span style="color:#FFAB70">attention_mask</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">attention_mask)</span></span>
<span class="line"><span style="color:#E1E4E8">        logits </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> outputs.logits</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#E1E4E8">        shift_logits </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> logits[:, :</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">, :]</span></span>
<span class="line"><span style="color:#E1E4E8">        shift_labels </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> input_ids[:, </span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">:]</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#E1E4E8">        log_probs </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> F.log_softmax(shift_logits, </span><span style="color:#FFAB70">dim</span><span style="color:#F97583">=-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        token_log_probs </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> log_probs.gather(</span></span>
<span class="line"><span style="color:#FFAB70">            dim</span><span style="color:#F97583">=-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">index</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">shift_labels.unsqueeze(</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        ).squeeze(</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        return</span><span style="color:#E1E4E8"> token_log_probs[:, response_start </span><span style="color:#F97583">-</span><span style="color:#79B8FF"> 1</span><span style="color:#E1E4E8">:]</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> train_step</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#E1E4E8">        self,</span></span>
<span class="line"><span style="color:#E1E4E8">        prompt_text: </span><span style="color:#79B8FF">str</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">        prompt_ids: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">        attention_mask: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">    ) -> </span><span style="color:#79B8FF">dict</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Single GDPO training step.</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#E1E4E8">        prompt_len </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> prompt_ids.shape[</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">]</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Sample and score</span></span>
<span class="line"><span style="color:#E1E4E8">        output_ids, rewards_list, output_masks </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.sample_and_score(</span></span>
<span class="line"><span style="color:#E1E4E8">            prompt_text, prompt_ids, attention_mask</span></span>
<span class="line"><span style="color:#E1E4E8">        )</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Compute GDPO advantages</span></span>
<span class="line"><span style="color:#E1E4E8">        advantages, per_reward_adv </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> compute_gdpo_advantages(</span></span>
<span class="line"><span style="color:#E1E4E8">            rewards_list,</span></span>
<span class="line"><span style="color:#79B8FF">            self</span><span style="color:#E1E4E8">.reward_weights.tolist(),</span></span>
<span class="line"><span style="color:#79B8FF">            self</span><span style="color:#E1E4E8">.config.eps,</span></span>
<span class="line"><span style="color:#E1E4E8">        )</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Get log probs</span></span>
<span class="line"><span style="color:#F97583">        with</span><span style="color:#E1E4E8"> torch.no_grad():</span></span>
<span class="line"><span style="color:#E1E4E8">            old_log_probs </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.compute_log_probs(</span></span>
<span class="line"><span style="color:#79B8FF">                self</span><span style="color:#E1E4E8">.policy, output_ids, output_masks, prompt_len</span></span>
<span class="line"><span style="color:#E1E4E8">            )</span></span>
<span class="line"><span style="color:#E1E4E8">            ref_log_probs </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.compute_log_probs(</span></span>
<span class="line"><span style="color:#79B8FF">                self</span><span style="color:#E1E4E8">.reference, output_ids, output_masks, prompt_len</span></span>
<span class="line"><span style="color:#E1E4E8">            )</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Update</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.policy.train()</span></span>
<span class="line"><span style="color:#E1E4E8">        policy_log_probs </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.compute_log_probs(</span></span>
<span class="line"><span style="color:#79B8FF">            self</span><span style="color:#E1E4E8">.policy, output_ids, output_masks, prompt_len</span></span>
<span class="line"><span style="color:#E1E4E8">        )</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#E1E4E8">        response_mask </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> output_masks[:, prompt_len:]</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Compute loss (same as GRPO)</span></span>
<span class="line"><span style="color:#E1E4E8">        loss, metrics </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">._compute_loss(</span></span>
<span class="line"><span style="color:#E1E4E8">            policy_log_probs,</span></span>
<span class="line"><span style="color:#E1E4E8">            old_log_probs,</span></span>
<span class="line"><span style="color:#E1E4E8">            ref_log_probs,</span></span>
<span class="line"><span style="color:#E1E4E8">            advantages,</span></span>
<span class="line"><span style="color:#E1E4E8">            response_mask,</span></span>
<span class="line"><span style="color:#E1E4E8">        )</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.optimizer.zero_grad()</span></span>
<span class="line"><span style="color:#E1E4E8">        loss.backward()</span></span>
<span class="line"><span style="color:#E1E4E8">        torch.nn.utils.clip_grad_norm_(</span></span>
<span class="line"><span style="color:#79B8FF">            self</span><span style="color:#E1E4E8">.policy.parameters(), </span></span>
<span class="line"><span style="color:#79B8FF">            self</span><span style="color:#E1E4E8">.config.max_grad_norm</span></span>
<span class="line"><span style="color:#E1E4E8">        )</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.optimizer.step()</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Add per-reward metrics</span></span>
<span class="line"><span style="color:#F97583">        for</span><span style="color:#E1E4E8"> k, rewards_k </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> enumerate</span><span style="color:#E1E4E8">(rewards_list):</span></span>
<span class="line"><span style="color:#E1E4E8">            metrics[</span><span style="color:#F97583">f</span><span style="color:#9ECBFF">"reward_</span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">k</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">_mean"</span><span style="color:#E1E4E8">] </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> rewards_k.mean().item()</span></span>
<span class="line"><span style="color:#E1E4E8">            metrics[</span><span style="color:#F97583">f</span><span style="color:#9ECBFF">"reward_</span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">k</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">_std"</span><span style="color:#E1E4E8">] </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> rewards_k.std().item()</span></span>
<span class="line"><span style="color:#E1E4E8">            metrics[</span><span style="color:#F97583">f</span><span style="color:#9ECBFF">"adv_</span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">k</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">_mean"</span><span style="color:#E1E4E8">] </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> per_reward_adv[k].mean().item()</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        return</span><span style="color:#E1E4E8"> metrics</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> _compute_loss</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#E1E4E8">        self,</span></span>
<span class="line"><span style="color:#E1E4E8">        policy_log_probs: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">        old_log_probs: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">        ref_log_probs: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">        advantages: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">        mask: Tensor,</span></span>
<span class="line"><span style="color:#E1E4E8">    ) -> Tuple[Tensor, </span><span style="color:#79B8FF">dict</span><span style="color:#E1E4E8">]:</span></span>
<span class="line"><span style="color:#9ECBFF">        """Compute GDPO loss with clipping and KL."""</span></span>
<span class="line"><span style="color:#E1E4E8">        G, seq_len </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> policy_log_probs.shape</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Importance ratio</span></span>
<span class="line"><span style="color:#E1E4E8">        ratio </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.exp(policy_log_probs </span><span style="color:#F97583">-</span><span style="color:#E1E4E8"> old_log_probs)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Expand advantages</span></span>
<span class="line"><span style="color:#E1E4E8">        adv_expanded </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> advantages.unsqueeze(</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">).expand(</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">, seq_len)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Clipped objective</span></span>
<span class="line"><span style="color:#E1E4E8">        clipped </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.clamp(</span></span>
<span class="line"><span style="color:#E1E4E8">            ratio,</span></span>
<span class="line"><span style="color:#79B8FF">            1</span><span style="color:#F97583"> -</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.config.clip_epsilon,</span></span>
<span class="line"><span style="color:#79B8FF">            1</span><span style="color:#F97583"> +</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.config.clip_epsilon</span></span>
<span class="line"><span style="color:#E1E4E8">        )</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#E1E4E8">        policy_loss </span><span style="color:#F97583">=</span><span style="color:#F97583"> -</span><span style="color:#E1E4E8">torch.min(</span></span>
<span class="line"><span style="color:#E1E4E8">            ratio </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> adv_expanded,</span></span>
<span class="line"><span style="color:#E1E4E8">            clipped </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> adv_expanded</span></span>
<span class="line"><span style="color:#E1E4E8">        )</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # KL penalty</span></span>
<span class="line"><span style="color:#E1E4E8">        log_ratio </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> ref_log_probs </span><span style="color:#F97583">-</span><span style="color:#E1E4E8"> policy_log_probs</span></span>
<span class="line"><span style="color:#E1E4E8">        kl </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> torch.exp(log_ratio) </span><span style="color:#F97583">-</span><span style="color:#E1E4E8"> log_ratio </span><span style="color:#F97583">-</span><span style="color:#79B8FF"> 1</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Combine</span></span>
<span class="line"><span style="color:#E1E4E8">        token_loss </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> policy_loss </span><span style="color:#F97583">+</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.config.kl_coef </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> kl</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Mask and average</span></span>
<span class="line"><span style="color:#E1E4E8">        masked_loss </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> (token_loss </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> mask).sum(</span><span style="color:#FFAB70">dim</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">) </span><span style="color:#F97583">/</span><span style="color:#E1E4E8"> mask.sum(</span><span style="color:#FFAB70">dim</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">).clamp(</span><span style="color:#FFAB70">min</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        loss </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> masked_loss.mean()</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        with</span><span style="color:#E1E4E8"> torch.no_grad():</span></span>
<span class="line"><span style="color:#E1E4E8">            clip_frac </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> ((ratio </span><span style="color:#F97583">-</span><span style="color:#79B8FF"> 1</span><span style="color:#E1E4E8">).abs() </span><span style="color:#F97583">></span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.config.clip_epsilon).float()</span></span>
<span class="line"><span style="color:#E1E4E8">            clip_frac </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> (clip_frac </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> mask).sum() </span><span style="color:#F97583">/</span><span style="color:#E1E4E8"> mask.sum()</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#E1E4E8">        metrics </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#9ECBFF">            "loss"</span><span style="color:#E1E4E8">: loss.item(),</span></span>
<span class="line"><span style="color:#9ECBFF">            "mean_kl"</span><span style="color:#E1E4E8">: (kl </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> mask).sum().item() </span><span style="color:#F97583">/</span><span style="color:#E1E4E8"> mask.sum().item(),</span></span>
<span class="line"><span style="color:#9ECBFF">            "clip_fraction"</span><span style="color:#E1E4E8">: clip_frac.item(),</span></span>
<span class="line"><span style="color:#9ECBFF">            "mean_advantage"</span><span style="color:#E1E4E8">: advantages.mean().item(),</span></span>
<span class="line"><span style="color:#E1E4E8">        }</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        return</span><span style="color:#E1E4E8"> loss, metrics</span></span>
<span class="line"></span></code></pre>
<hr>
<h2 id="experimental-results">Experimental Results</h2>
<p>The GDPO paper demonstrates consistent improvements across multiple tasks.</p>
<h3 id="tool-calling-2-rewards">Tool Calling (2 Rewards)</h3>
<p>Training Qwen2.5-1.5B-Instruct with format + correctness rewards:</p>























<table><thead><tr><th>Method</th><th>Format Compliance</th><th>Tool Accuracy</th><th>Training Stability</th></tr></thead><tbody><tr><td>GRPO</td><td>78%</td><td>61%</td><td>Some oscillation</td></tr><tr><td>GDPO</td><td>94%</td><td>73%</td><td>Stable convergence</td></tr></tbody></table>
<p>GDPO achieves better performance on both metrics with more stable training curves.</p>
<h3 id="math-reasoning-3-rewards">Math Reasoning (3 Rewards)</h3>
<p>Training on GSM8K with format + correctness + integer rewards:</p>


























<table><thead><tr><th>Method</th><th>Format</th><th>Accuracy</th><th>Integer</th><th>Overall</th></tr></thead><tbody><tr><td>GRPO</td><td>82%</td><td>45%</td><td>71%</td><td>42%</td></tr><tr><td>GDPO</td><td>96%</td><td>52%</td><td>89%</td><td>51%</td></tr></tbody></table>
<p>The three-reward setting amplifies GDPO’s advantage—more rewards mean more potential for collapse.</p>
<h3 id="code-generation">Code Generation</h3>
<p>Similar patterns: GDPO consistently outperforms GRPO on execution, correctness, and style metrics.</p>
<h3 id="key-observations">Key Observations</h3>
<ol>
<li><strong>More rewards → bigger gap:</strong> The advantage collapse problem worsens with more rewards</li>
<li><strong>Training stability:</strong> GDPO shows smoother loss curves and fewer failures</li>
<li><strong>Complementary rewards:</strong> When rewards measure different aspects, GDPO’s decoupling is most beneficial</li>
</ol>
<blockquote>
<p><strong>Key Insight:</strong> GDPO’s improvement scales with the number of rewards. For single-reward settings, use GRPO. For multi-reward, GDPO is the clear choice.</p>
</blockquote>
<hr>
<h2 id="when-to-use-gdpo-vs-grpo">When to Use GDPO vs GRPO</h2>
<h3 id="use-grpo-when">Use GRPO When:</h3>
<ul>
<li><strong>Single reward:</strong> No multi-reward setting</li>
<li><strong>Highly correlated rewards:</strong> Rewards that move together don’t benefit from decoupling</li>
<li><strong>Simplicity preferred:</strong> GRPO is simpler to implement</li>
</ul>
<h3 id="use-gdpo-when">Use GDPO When:</h3>
<ul>
<li><strong>Multiple rewards:</strong> Two or more distinct reward functions</li>
<li><strong>Complementary rewards:</strong> Rewards measure different aspects (format vs. correctness)</li>
<li><strong>Training instability with GRPO:</strong> If GRPO shows oscillation or failure, try GDPO</li>
<li><strong>Production multi-objective alignment:</strong> Default for real-world multi-reward scenarios</li>
</ul>
<h3 id="decision-tree">Decision Tree</h3>
<div class="mermaid">
graph TD
    Start{"&#x3C;b>How many&#x3C;br/>rewards?&#x3C;/b>"} 
    
    Start -->|"One"| GRPO["&#x3C;b>Use GRPO&#x3C;/b>&#x3C;br/>━━━━━━━━&#x3C;br/>No multi-reward&#x3C;br/>complexity"]
    
    Start -->|"Multiple"| Q2{"&#x3C;b>Are rewards&#x3C;br/>complementary?&#x3C;/b>"}
    
    Q2 -->|"Yes (different aspects)"| GDPO["&#x3C;b>Use GDPO&#x3C;/b>&#x3C;br/>━━━━━━━━&#x3C;br/>Preserves signal&#x3C;br/>resolution"]
    
    Q2 -->|"No (highly correlated)"| Q3{"&#x3C;b>Training&#x3C;br/>stable?&#x3C;/b>"}
    
    Q3 -->|"Yes"| GRPO2["&#x3C;b>Use GRPO&#x3C;/b>&#x3C;br/>━━━━━━━━&#x3C;br/>Simpler, works"]
    
    Q3 -->|"No"| GDPO2["&#x3C;b>Try GDPO&#x3C;/b>&#x3C;br/>━━━━━━━━&#x3C;br/>May stabilize&#x3C;br/>training"]
    
    style Start stroke:#64748b
    style GRPO stroke:#06b6d4,stroke-width:2px
    style GRPO2 stroke:#06b6d4,stroke-width:2px
    style GDPO stroke:#10b981,stroke-width:2.5px
    style GDPO2 stroke:#10b981,stroke-width:2.5px
    style Q2 stroke:#64748b
    style Q3 stroke:#64748b</div>
<hr>
<h2 id="production-patterns">Production Patterns</h2>
<h3 id="pattern-1-format--correctness">Pattern 1: Format + Correctness</h3>
<p>The most common multi-reward setup:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#F97583">def</span><span style="color:#B392F0"> format_reward</span><span style="color:#E1E4E8">(prompt: </span><span style="color:#79B8FF">str</span><span style="color:#E1E4E8">, response: </span><span style="color:#79B8FF">str</span><span style="color:#E1E4E8">) -> </span><span style="color:#79B8FF">float</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#9ECBFF">    """Binary: follows required structure?"""</span></span>
<span class="line"><span style="color:#E1E4E8">    has_think </span><span style="color:#F97583">=</span><span style="color:#9ECBFF"> "&#x3C;think>"</span><span style="color:#F97583"> in</span><span style="color:#E1E4E8"> response </span><span style="color:#F97583">and</span><span style="color:#9ECBFF"> "&#x3C;/think>"</span><span style="color:#F97583"> in</span><span style="color:#E1E4E8"> response</span></span>
<span class="line"><span style="color:#E1E4E8">    has_answer </span><span style="color:#F97583">=</span><span style="color:#9ECBFF"> "&#x3C;answer>"</span><span style="color:#F97583"> in</span><span style="color:#E1E4E8"> response </span><span style="color:#F97583">and</span><span style="color:#9ECBFF"> "&#x3C;/answer>"</span><span style="color:#F97583"> in</span><span style="color:#E1E4E8"> response</span></span>
<span class="line"><span style="color:#F97583">    return</span><span style="color:#79B8FF"> 1.0</span><span style="color:#F97583"> if</span><span style="color:#E1E4E8"> (has_think </span><span style="color:#F97583">and</span><span style="color:#E1E4E8"> has_answer) </span><span style="color:#F97583">else</span><span style="color:#79B8FF"> 0.0</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">def</span><span style="color:#B392F0"> correctness_reward</span><span style="color:#E1E4E8">(prompt: </span><span style="color:#79B8FF">str</span><span style="color:#E1E4E8">, response: </span><span style="color:#79B8FF">str</span><span style="color:#E1E4E8">) -> </span><span style="color:#79B8FF">float</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#9ECBFF">    """Continuous: semantically correct?"""</span></span>
<span class="line"><span style="color:#6A737D">    # Extract answer and check against ground truth</span></span>
<span class="line"><span style="color:#6A737D">    # Returns value in [-1, 1]</span></span>
<span class="line"><span style="color:#79B8FF">    ...</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8">reward_fns </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> [format_reward, correctness_reward]</span></span>
<span class="line"><span style="color:#E1E4E8">reward_weights </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> [</span><span style="color:#79B8FF">1.0</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">1.0</span><span style="color:#E1E4E8">]  </span><span style="color:#6A737D"># Equal weighting</span></span>
<span class="line"></span></code></pre>
<p>Use GDPO—format and correctness are complementary.</p>
<h3 id="pattern-2-hierarchical-rewards">Pattern 2: Hierarchical Rewards</h3>
<p>When one reward is prerequisite for another:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#F97583">def</span><span style="color:#B392F0"> hierarchical_correctness</span><span style="color:#E1E4E8">(prompt: </span><span style="color:#79B8FF">str</span><span style="color:#E1E4E8">, response: </span><span style="color:#79B8FF">str</span><span style="color:#E1E4E8">) -> </span><span style="color:#79B8FF">float</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#9ECBFF">    """Only evaluate correctness if format is satisfied."""</span></span>
<span class="line"><span style="color:#F97583">    if</span><span style="color:#E1E4E8"> format_reward(prompt, response) </span><span style="color:#F97583">&#x3C;</span><span style="color:#79B8FF"> 0.5</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#F97583">        return</span><span style="color:#79B8FF"> 0.0</span><span style="color:#6A737D">  # Can't evaluate without proper format</span></span>
<span class="line"><span style="color:#F97583">    return</span><span style="color:#E1E4E8"> base_correctness_reward(prompt, response)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8">reward_fns </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> [format_reward, hierarchical_correctness]</span></span>
<span class="line"></span></code></pre>
<p>GDPO still helps, but consider the dependency structure.</p>
<h3 id="pattern-3-weighted-multi-objective">Pattern 3: Weighted Multi-Objective</h3>
<p>Different importance for different rewards:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#E1E4E8">reward_fns </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> [safety_reward, helpfulness_reward, conciseness_reward]</span></span>
<span class="line"><span style="color:#E1E4E8">reward_weights </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> [</span><span style="color:#79B8FF">2.0</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">1.0</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">0.5</span><span style="color:#E1E4E8">]  </span><span style="color:#6A737D"># Safety prioritized</span></span>
<span class="line"></span></code></pre>
<p>GDPO’s per-reward normalization ensures each contributes proportionally.</p>
<h3 id="pattern-4-dynamic-weights">Pattern 4: Dynamic Weights</h3>
<p>Adjusting weights during training:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#F97583">def</span><span style="color:#B392F0"> get_weights</span><span style="color:#E1E4E8">(step: </span><span style="color:#79B8FF">int</span><span style="color:#E1E4E8">) -> List[</span><span style="color:#79B8FF">float</span><span style="color:#E1E4E8">]:</span></span>
<span class="line"><span style="color:#9ECBFF">    """Start with format emphasis, shift to correctness."""</span></span>
<span class="line"><span style="color:#E1E4E8">    format_w </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> max</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">0.5</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">1.0</span><span style="color:#F97583"> -</span><span style="color:#E1E4E8"> step </span><span style="color:#F97583">/</span><span style="color:#79B8FF"> 10000</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">    correct_w </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> min</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">1.5</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">0.5</span><span style="color:#F97583"> +</span><span style="color:#E1E4E8"> step </span><span style="color:#F97583">/</span><span style="color:#79B8FF"> 10000</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#F97583">    return</span><span style="color:#E1E4E8"> [format_w, correct_w]</span></span>
<span class="line"></span></code></pre>
<p>GDPO handles this naturally since normalization is per-reward.</p>
<hr>
<h2 id="key-takeaways">Key Takeaways</h2>
<h3 id="the-problem-advantage-collapse">The Problem: Advantage Collapse</h3>
<p>When GRPO normalizes combined rewards, distinct reward combinations can collapse to identical advantages, losing training signal resolution.</p>
<h3 id="the-solution-gdpo">The Solution: GDPO</h3>
<p>Normalize each reward independently, then combine:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#6A737D"># GRPO (problematic)</span></span>
<span class="line"><span style="color:#E1E4E8">combined </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> sum</span><span style="color:#E1E4E8">(w </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> r </span><span style="color:#F97583">for</span><span style="color:#E1E4E8"> w, r </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> zip</span><span style="color:#E1E4E8">(weights, rewards))</span></span>
<span class="line"><span style="color:#E1E4E8">advantage </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> (combined </span><span style="color:#F97583">-</span><span style="color:#E1E4E8"> mean) </span><span style="color:#F97583">/</span><span style="color:#E1E4E8"> std</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D"># GDPO (preserves signal)</span></span>
<span class="line"><span style="color:#E1E4E8">normalized </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> [(r </span><span style="color:#F97583">-</span><span style="color:#E1E4E8"> mean(r)) </span><span style="color:#F97583">/</span><span style="color:#E1E4E8"> std(r) </span><span style="color:#F97583">for</span><span style="color:#E1E4E8"> r </span><span style="color:#F97583">in</span><span style="color:#E1E4E8"> rewards]</span></span>
<span class="line"><span style="color:#E1E4E8">combined </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> sum</span><span style="color:#E1E4E8">(w </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> n </span><span style="color:#F97583">for</span><span style="color:#E1E4E8"> w, n </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> zip</span><span style="color:#E1E4E8">(weights, normalized))</span></span>
<span class="line"><span style="color:#E1E4E8">advantage </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> (combined </span><span style="color:#F97583">-</span><span style="color:#E1E4E8"> mean) </span><span style="color:#F97583">/</span><span style="color:#E1E4E8"> std  </span><span style="color:#6A737D"># Final batch norm</span></span>
<span class="line"></span></code></pre>
<h3 id="when-to-use-gdpo">When to Use GDPO</h3>
<ul>
<li>Multiple rewards measuring different aspects</li>
<li>Tool calling, math reasoning, code generation</li>
<li>Any multi-objective LLM alignment</li>
</ul>
<h3 id="implementation-1">Implementation</h3>
<p>GDPO is a <strong>drop-in replacement</strong> for GRPO’s advantage computation. Minimal code change, significant improvement.</p>
<h3 id="the-broader-lesson">The Broader Lesson</h3>
<p>As LLM training becomes more sophisticated, we’ll see more multi-reward settings. Understanding how normalization affects training signal is crucial for designing effective alignment pipelines.</p>
<hr>
<h2 id="series-summary">Series Summary</h2>
<p>Over four articles, we’ve built a complete understanding of policy optimization for LLMs:</p>






























<table><thead><tr><th>Part</th><th>Topic</th><th>Key Contribution</th></tr></thead><tbody><tr><td><strong>1</strong></td><td>RL Foundations</td><td>MDPs, policy gradients, variance reduction, value functions</td></tr><tr><td><strong>2</strong></td><td>PPO</td><td>Clipped objectives, GAE, the four-model architecture</td></tr><tr><td><strong>3</strong></td><td>GRPO</td><td>Group-relative advantages, eliminating the value network</td></tr><tr><td><strong>4</strong></td><td>GDPO</td><td>Multi-reward settings, decoupled normalization</td></tr></tbody></table>
<p>Each algorithm addresses specific limitations of its predecessor:</p>
<ul>
<li>PPO addresses policy gradient variance</li>
<li>GRPO addresses PPO’s memory overhead</li>
<li>GDPO addresses GRPO’s multi-reward limitations</li>
</ul>
<p>Understanding this progression helps you choose the right tool for your alignment task.</p>
<hr>
<h2 id="further-reading">Further Reading</h2>
<p><strong>GDPO:</strong></p>
<ul>
<li><a href="https://arxiv.org/abs/2601.05242">GDPO: Group reward-Decoupled Normalization Policy Optimization</a> (Liu et al., 2026)</li>
<li><a href="https://github.com/NVlabs/GDPO">GitHub: NVlabs/GDPO</a></li>
</ul>
<p><strong>Foundations:</strong></p>
<ul>
<li><a href="https://arxiv.org/abs/2402.03300">DeepSeekMath</a> — GRPO origin</li>
<li><a href="https://arxiv.org/abs/1707.06347">PPO</a> — The baseline</li>
</ul>
<p><strong>Multi-Objective RL:</strong></p>
<ul>
<li><a href="https://arxiv.org/abs/2006.12254">Multi-Objective Reinforcement Learning: A Survey</a></li>
<li><a href="https://arxiv.org/abs/2209.13085">Reward Hacking in Reinforcement Learning</a></li>
</ul>
<p><strong>Implementations:</strong></p>
<ul>
<li><a href="https://github.com/NVlabs/GDPO/tree/main/trl-GDPO">TRL with GDPO</a></li>
<li><a href="https://github.com/NVlabs/GDPO/tree/main/verl-GDPO">VERL with GDPO</a></li>
<li><a href="https://github.com/NVlabs/GDPO/tree/main/nemo_rl-GDPO">NeMo-RL with GDPO</a></li>
</ul>]]></content:encoded><enclosure url="https://www.vitorsousa.com/_astro/beyond-the-vibe-check-a-systematic-approach-to-llm-evaluation.DN7yoDr3.png" length="756000" type="image/png"/></item><item><title>OpenELM Notes</title><link>https://www.vitorsousa.com/blog/openelm/</link><guid isPermaLink="true">https://www.vitorsousa.com/blog/openelm/</guid><description>I wrote about OpenELM and how Apple approaches efficient language models.</description><pubDate>Sun, 30 Apr 2023 08:00:00 GMT</pubDate><content:encoded><![CDATA[<h1 id="deploying-contextual-bandits-production-guide-and-offline-evaluation">Deploying Contextual Bandits: Production Guide and Offline Evaluation</h1>
<p><strong>Part 5 of 5:</strong> From Research to Production</p>
<blockquote>
<p><strong>TL;DR:</strong> Deploying contextual bandits requires more than implementing an algorithm. You need offline evaluation (to validate before going live), production architecture (8 core components), comprehensive logging, monitoring dashboards, safety mechanisms, and a phased deployment strategy. This post provides the complete production playbook with checklists, code, and architecture patterns used at scale.</p>
</blockquote>
<p><strong>Reading time:</strong> ~28 minutes</p>
<hr>
<h2 id="introduction-the-production-gap">Introduction: The Production Gap</h2>
<p>You’ve implemented LinUCB or Thompson Sampling. It works great in simulation. Now what?</p>
<p><strong>The gap between research and production is enormous:</strong></p>
<ul>
<li><strong>Offline evaluation:</strong> How do you validate performance before going live?</li>
<li><strong>Architecture:</strong> What components do you need? How do they interact?</li>
<li><strong>Logging:</strong> What data must you capture? How to handle delayed rewards?</li>
<li><strong>Monitoring:</strong> What metrics indicate problems? When to trigger alerts?</li>
<li><strong>Safety:</strong> How to prevent disasters? Circuit breakers? Fallbacks?</li>
<li><strong>Deployment:</strong> How to roll out gradually? Validate at each stage?</li>
</ul>
<p><strong>This post bridges that gap.</strong> By the end, you’ll have:</p>
<ul>
<li>Complete offline evaluation pipeline (IPS, Doubly Robust)</li>
<li>Production architecture blueprint (8 components)</li>
<li>Monitoring dashboard specification</li>
<li>Pre-deployment checklist (56 criteria across 7 phases)</li>
</ul>
<p>Let’s deploy safely.</p>
<hr>
<h2 id="offline-evaluation-validate-before-you-deploy">Offline Evaluation: Validate Before You Deploy</h2>
<p><strong>Critical problem:</strong> You can’t A/B test your bandit against production until it’s already in production. But deploying an untested bandit risks disasters.</p>
<p><strong>Solution:</strong> Offline evaluation—estimate bandit performance using historical logged data.</p>
<h3 id="the-counterfactual-problem">The Counterfactual Problem</h3>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#6A737D"># Historical data from production logging policy π₀</span></span>
<span class="line"><span style="color:#E1E4E8">logged_data </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> [</span></span>
<span class="line"><span style="color:#E1E4E8">    (context₁, action₁, reward₁),  </span><span style="color:#6A737D"># π₀ chose action₁</span></span>
<span class="line"><span style="color:#E1E4E8">    (context₂, action₂, reward₂),  </span><span style="color:#6A737D"># π₀ chose action₂</span></span>
<span class="line"><span style="color:#79B8FF">    ...</span></span>
<span class="line"><span style="color:#E1E4E8">]</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D"># Question: How would our new policy π₁ perform?</span></span>
<span class="line"><span style="color:#6A737D"># Problem: We only observed rewards for actions π₀ chose,</span></span>
<span class="line"><span style="color:#6A737D">#          not for actions π₁ would have chosen</span></span>
<span class="line"></span></code></pre>
<p><strong>Counterfactual inference:</strong> Estimating what would have happened under a different policy using observed data from another policy.</p>
<h3 id="three-offline-evaluation-methods">Three Offline Evaluation Methods</h3>





























<table><thead><tr><th>Method</th><th>Bias</th><th>Variance</th><th>When to Use</th></tr></thead><tbody><tr><td><strong>Replay</strong></td><td>High (if policies differ)</td><td>Low</td><td>Policies are very similar</td></tr><tr><td><strong>IPS (Inverse Propensity Score)</strong></td><td>Unbiased</td><td>High</td><td>Have propensity scores, small policy divergence</td></tr><tr><td><strong>Doubly Robust</strong></td><td>Unbiased</td><td>Lower</td><td>Best of both worlds, recommended</td></tr></tbody></table>
<hr>
<h2 id="method-1-replay-evaluation-biased-but-simple">Method 1: Replay Evaluation (Biased but Simple)</h2>
<p><strong>Idea:</strong> Replay logged data, only count rounds where new policy would have made same decision as logging policy.</p>
<h3 id="implementation">Implementation</h3>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> numpy </span><span style="color:#F97583">as</span><span style="color:#E1E4E8"> np</span></span>
<span class="line"><span style="color:#F97583">from</span><span style="color:#E1E4E8"> typing </span><span style="color:#F97583">import</span><span style="color:#E1E4E8"> List, Tuple, Dict</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">def</span><span style="color:#B392F0"> replay_evaluation</span><span style="color:#E1E4E8">(logged_data: List[Tuple], new_policy) -> </span><span style="color:#79B8FF">float</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#9ECBFF">    Replay evaluation: only use data where policies agree.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Biased if new policy differs significantly from logging policy.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Args:</span></span>
<span class="line"><span style="color:#9ECBFF">        logged_data: List of (context, action_logged, reward) tuples</span></span>
<span class="line"><span style="color:#9ECBFF">        new_policy: Policy to evaluate (must have select_action method)</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">    Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">        estimated_value: Estimated average reward</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#E1E4E8">    matching_rewards </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> []</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    for</span><span style="color:#E1E4E8"> context, action_logged, reward </span><span style="color:#F97583">in</span><span style="color:#E1E4E8"> logged_data:</span></span>
<span class="line"><span style="color:#6A737D">        # What would new policy choose?</span></span>
<span class="line"><span style="color:#E1E4E8">        action_new </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> new_policy.select_action(context)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Only count if policies agree</span></span>
<span class="line"><span style="color:#F97583">        if</span><span style="color:#E1E4E8"> action_new </span><span style="color:#F97583">==</span><span style="color:#E1E4E8"> action_logged:</span></span>
<span class="line"><span style="color:#E1E4E8">            matching_rewards.append(reward)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    if</span><span style="color:#79B8FF"> len</span><span style="color:#E1E4E8">(matching_rewards) </span><span style="color:#F97583">==</span><span style="color:#79B8FF"> 0</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#F97583">        return</span><span style="color:#E1E4E8"> np.nan  </span><span style="color:#6A737D"># No matching data</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    return</span><span style="color:#E1E4E8"> np.mean(matching_rewards)</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D"># Example usage</span></span>
<span class="line"><span style="color:#F97583">if</span><span style="color:#79B8FF"> __name__</span><span style="color:#F97583"> ==</span><span style="color:#9ECBFF"> "__main__"</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#F97583">    from</span><span style="color:#E1E4E8"> contextual_bandits </span><span style="color:#F97583">import</span><span style="color:#E1E4E8"> LinUCB</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Load historical data</span></span>
<span class="line"><span style="color:#6A737D">    # logged_data = load_from_database()</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Simulated data</span></span>
<span class="line"><span style="color:#E1E4E8">    logged_data </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> [</span></span>
<span class="line"><span style="color:#E1E4E8">        (np.random.randn(</span><span style="color:#79B8FF">10</span><span style="color:#E1E4E8">), np.random.randint(</span><span style="color:#79B8FF">3</span><span style="color:#E1E4E8">), np.random.random())</span></span>
<span class="line"><span style="color:#F97583">        for</span><span style="color:#E1E4E8"> _ </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> range</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">1000</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">    ]</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # New policy to evaluate</span></span>
<span class="line"><span style="color:#E1E4E8">    new_policy </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> LinUCB(</span><span style="color:#FFAB70">n_actions</span><span style="color:#F97583">=</span><span style="color:#79B8FF">3</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">n_features</span><span style="color:#F97583">=</span><span style="color:#79B8FF">10</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">alpha</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1.0</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Estimate performance</span></span>
<span class="line"><span style="color:#E1E4E8">    estimated_reward </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> replay_evaluation(logged_data, new_policy)</span></span>
<span class="line"><span style="color:#79B8FF">    print</span><span style="color:#E1E4E8">(</span><span style="color:#F97583">f</span><span style="color:#9ECBFF">"Estimated average reward: </span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">estimated_reward</span><span style="color:#F97583">:.3f</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">"</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Coverage: what fraction of data was usable?</span></span>
<span class="line"><span style="color:#E1E4E8">    coverage </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> sum</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#79B8FF">        1</span><span style="color:#F97583"> for</span><span style="color:#E1E4E8"> ctx, a_log, _ </span><span style="color:#F97583">in</span><span style="color:#E1E4E8"> logged_data </span></span>
<span class="line"><span style="color:#F97583">        if</span><span style="color:#E1E4E8"> new_policy.select_action(ctx) </span><span style="color:#F97583">==</span><span style="color:#E1E4E8"> a_log</span></span>
<span class="line"><span style="color:#E1E4E8">    ) </span><span style="color:#F97583">/</span><span style="color:#79B8FF"> len</span><span style="color:#E1E4E8">(logged_data)</span></span>
<span class="line"><span style="color:#79B8FF">    print</span><span style="color:#E1E4E8">(</span><span style="color:#F97583">f</span><span style="color:#9ECBFF">"Policy coverage: </span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">coverage</span><span style="color:#F97583">:.1%</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">"</span><span style="color:#E1E4E8">)</span></span>
<span class="line"></span></code></pre>
<h3 id="when-to-use-replay">When to Use Replay</h3>
<p><strong>✅ Use when:</strong></p>
<ul>
<li>New policy is similar to logging policy (high coverage >50%)</li>
<li>Need simple, interpretable evaluation</li>
<li>Quick validation during development</li>
</ul>
<p><strong>❌ Limitations:</strong></p>
<ul>
<li><strong>Biased:</strong> Systematically underestimates performance if new policy is better</li>
<li><strong>Low coverage:</strong> Wastes most data if policies differ</li>
<li><strong>Can’t evaluate exploration:</strong> Only uses data where policies agree</li>
</ul>
<hr>
<h2 id="method-2-inverse-propensity-score-ips">Method 2: Inverse Propensity Score (IPS)</h2>
<p><strong>Idea:</strong> Reweight observations by inverse probability that logging policy would choose that action.</p>
<h3 id="mathematical-foundation">Mathematical Foundation</h3>
<p>Logging policy π₀ chooses actions with probabilities p₀(a|x).<br>
We want to estimate value of new policy π₁:</p>
<p><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><msub><mi>π</mi><mn>1</mn></msub><mo stretchy="false">)</mo><mo>=</mo><msub><mi mathvariant="double-struck">E</mi><mrow><mi>x</mi><mo>∼</mo><mi>D</mi><mo separator="true">,</mo><mi>a</mi><mo>∼</mo><msub><mi>π</mi><mn>1</mn></msub></mrow></msub><mo stretchy="false">[</mo><mi>r</mi><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo><mo stretchy="false">]</mo></mrow><annotation encoding="application/x-tex">V(\pi_1) = \mathbb{E}_{x \sim D, a \sim \pi_1}[r(x,a)]</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.22222em;">V</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">π</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">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="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.0361em;vertical-align:-0.2861em;"></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 mathnormal mtight" style="margin-right:0.02778em;">D</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">a</span><span class="mrel mtight">∼</span><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.03588em;">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3173em;"><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 mtight">1</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></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" style="margin-right:0.02778em;">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">a</span><span class="mclose">)]</span></span></span></span></p>
<p><strong>IPS estimator:</strong></p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mover accent="true"><mi>V</mi><mo>^</mo></mover><mtext>IPS</mtext></msub><mo stretchy="false">(</mo><msub><mi>π</mi><mn>1</mn></msub><mo stretchy="false">)</mo><mo>=</mo><mfrac><mn>1</mn><mi>n</mi></mfrac><msubsup><mo>∑</mo><mrow><mi>i</mi><mo>=</mo><mn>1</mn></mrow><mi>n</mi></msubsup><mfrac><mrow><msub><mi>π</mi><mn>1</mn></msub><mo stretchy="false">(</mo><msub><mi>a</mi><mi>i</mi></msub><mi mathvariant="normal">∣</mi><msub><mi>x</mi><mi>i</mi></msub><mo stretchy="false">)</mo></mrow><mrow><msub><mi>π</mi><mn>0</mn></msub><mo stretchy="false">(</mo><msub><mi>a</mi><mi>i</mi></msub><mi mathvariant="normal">∣</mi><msub><mi>x</mi><mi>i</mi></msub><mo stretchy="false">)</mo></mrow></mfrac><msub><mi>r</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">\hat{V}_{\text{IPS}}(\pi_1) = \frac{1}{n} \sum_{i=1}^{n} \frac{\pi_1(a_i|x_i)}{\pi_0(a_i|x_i)} r_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.1968em;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.9468em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.22222em;">V</span></span><span style="top:-3.2523em;"><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.3283em;"><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 mtight"><span class="mord text mtight"><span class="mord mtight">IPS</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.03588em;">π</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">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="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.53em;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 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">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.1667em;"></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.8043em;"><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">i</span><span class="mrel mtight">=</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.2029em;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">n</span></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="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.03588em;">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3173em;"><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 mtight">0</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="mopen mtight">(</span><span class="mord mtight"><span class="mord mathnormal mtight">a</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 class="mord mtight"><span class="mord mathnormal mtight">x</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="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.03588em;">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3173em;"><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 mtight">1</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="mopen mtight">(</span><span class="mord mtight"><span class="mord mathnormal mtight">a</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 class="mord mtight"><span class="mord mathnormal mtight">x</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="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="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">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></p>
<p>where:</p>
<ul>
<li>π₁(aᵢ|xᵢ) = probability new policy chooses action aᵢ given context xᵢ</li>
<li>π₀(aᵢ|xᵢ) = probability logging policy chose action aᵢ (propensity score)</li>
<li>rᵢ = observed reward</li>
</ul>
<p><strong>Intuition:</strong> If logging policy rarely chose action A (small p₀), but new policy chooses it often (large π₁), upweight those observations.</p>
<h3 id="implementation-1">Implementation</h3>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#F97583">def</span><span style="color:#B392F0"> inverse_propensity_score</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#E1E4E8">    logged_data: List[Tuple],</span></span>
<span class="line"><span style="color:#E1E4E8">    new_policy,</span></span>
<span class="line"><span style="color:#E1E4E8">    propensity_scores: List[</span><span style="color:#79B8FF">float</span><span style="color:#E1E4E8">],</span></span>
<span class="line"><span style="color:#E1E4E8">    clip: </span><span style="color:#79B8FF">float</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 0.01</span></span>
<span class="line"><span style="color:#E1E4E8">) -> Tuple[</span><span style="color:#79B8FF">float</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">float</span><span style="color:#E1E4E8">]:</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#9ECBFF">    Inverse Propensity Score (IPS) evaluation.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Unbiased estimator that reweights observations by propensity scores.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Args:</span></span>
<span class="line"><span style="color:#9ECBFF">        logged_data: List of (context, action_logged, reward) tuples</span></span>
<span class="line"><span style="color:#9ECBFF">        new_policy: Policy to evaluate</span></span>
<span class="line"><span style="color:#9ECBFF">        propensity_scores: P(action_logged | context) from logging policy</span></span>
<span class="line"><span style="color:#9ECBFF">        clip: Minimum propensity score (prevent extreme weights)</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">    Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">        estimated_value: Estimated average reward</span></span>
<span class="line"><span style="color:#9ECBFF">        effective_n: Effective sample size (variance indicator)</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#E1E4E8">    weights </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> []</span></span>
<span class="line"><span style="color:#E1E4E8">    weighted_rewards </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> []</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    for</span><span style="color:#E1E4E8"> (context, action_logged, reward), prop_score </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> zip</span><span style="color:#E1E4E8">(logged_data, propensity_scores):</span></span>
<span class="line"><span style="color:#6A737D">        # Clip propensity score to prevent extreme weights</span></span>
<span class="line"><span style="color:#E1E4E8">        prop_score </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> max</span><span style="color:#E1E4E8">(prop_score, clip)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Would new policy choose this action?</span></span>
<span class="line"><span style="color:#E1E4E8">        action_new </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> new_policy.select_action(context)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Importance weight: π₁(a|x) / π₀(a|x)</span></span>
<span class="line"><span style="color:#F97583">        if</span><span style="color:#E1E4E8"> action_new </span><span style="color:#F97583">==</span><span style="color:#E1E4E8"> action_logged:</span></span>
<span class="line"><span style="color:#E1E4E8">            weight </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 1.0</span><span style="color:#F97583"> /</span><span style="color:#E1E4E8"> prop_score  </span><span style="color:#6A737D"># π₁(a|x) = 1 (deterministic)</span></span>
<span class="line"><span style="color:#F97583">        else</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#E1E4E8">            weight </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 0.0</span><span style="color:#6A737D">  # π₁(a|x) = 0</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#E1E4E8">        weights.append(weight)</span></span>
<span class="line"><span style="color:#E1E4E8">        weighted_rewards.append(weight </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> reward)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # IPS estimate</span></span>
<span class="line"><span style="color:#E1E4E8">    estimated_value </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> np.sum(weighted_rewards) </span><span style="color:#F97583">/</span><span style="color:#79B8FF"> len</span><span style="color:#E1E4E8">(logged_data)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Effective sample size (measures variance)</span></span>
<span class="line"><span style="color:#6A737D">    # ESS = (Σw)² / Σw²</span></span>
<span class="line"><span style="color:#E1E4E8">    sum_weights </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> np.sum(weights)</span></span>
<span class="line"><span style="color:#E1E4E8">    sum_weights_sq </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> np.sum([w</span><span style="color:#F97583">**</span><span style="color:#79B8FF">2</span><span style="color:#F97583"> for</span><span style="color:#E1E4E8"> w </span><span style="color:#F97583">in</span><span style="color:#E1E4E8"> weights])</span></span>
<span class="line"><span style="color:#E1E4E8">    effective_n </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> sum_weights</span><span style="color:#F97583">**</span><span style="color:#79B8FF">2</span><span style="color:#F97583"> /</span><span style="color:#E1E4E8"> sum_weights_sq </span><span style="color:#F97583">if</span><span style="color:#E1E4E8"> sum_weights_sq </span><span style="color:#F97583">></span><span style="color:#79B8FF"> 0</span><span style="color:#F97583"> else</span><span style="color:#79B8FF"> 0</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    return</span><span style="color:#E1E4E8"> estimated_value, effective_n</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">def</span><span style="color:#B392F0"> ips_with_confidence_interval</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#E1E4E8">    logged_data: List[Tuple],</span></span>
<span class="line"><span style="color:#E1E4E8">    new_policy,</span></span>
<span class="line"><span style="color:#E1E4E8">    propensity_scores: List[</span><span style="color:#79B8FF">float</span><span style="color:#E1E4E8">],</span></span>
<span class="line"><span style="color:#E1E4E8">    alpha: </span><span style="color:#79B8FF">float</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 0.05</span></span>
<span class="line"><span style="color:#E1E4E8">) -> Dict[</span><span style="color:#79B8FF">str</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">float</span><span style="color:#E1E4E8">]:</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#9ECBFF">    IPS with confidence interval using bootstrap.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Args:</span></span>
<span class="line"><span style="color:#9ECBFF">        logged_data: Historical data</span></span>
<span class="line"><span style="color:#9ECBFF">        new_policy: Policy to evaluate</span></span>
<span class="line"><span style="color:#9ECBFF">        propensity_scores: Propensity scores</span></span>
<span class="line"><span style="color:#9ECBFF">        alpha: Confidence level (0.05 = 95% CI)</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">    Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">        dict with 'estimate', 'ci_lower', 'ci_upper', 'effective_n'</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#E1E4E8">    n_bootstrap </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 1000</span></span>
<span class="line"><span style="color:#E1E4E8">    estimates </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> []</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#E1E4E8">    n </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> len</span><span style="color:#E1E4E8">(logged_data)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    for</span><span style="color:#E1E4E8"> _ </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> range</span><span style="color:#E1E4E8">(n_bootstrap):</span></span>
<span class="line"><span style="color:#6A737D">        # Bootstrap resample</span></span>
<span class="line"><span style="color:#E1E4E8">        indices </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> np.random.choice(n, </span><span style="color:#FFAB70">size</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">n, </span><span style="color:#FFAB70">replace</span><span style="color:#F97583">=</span><span style="color:#79B8FF">True</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        sample_data </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> [logged_data[i] </span><span style="color:#F97583">for</span><span style="color:#E1E4E8"> i </span><span style="color:#F97583">in</span><span style="color:#E1E4E8"> indices]</span></span>
<span class="line"><span style="color:#E1E4E8">        sample_props </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> [propensity_scores[i] </span><span style="color:#F97583">for</span><span style="color:#E1E4E8"> i </span><span style="color:#F97583">in</span><span style="color:#E1E4E8"> indices]</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # IPS on this sample</span></span>
<span class="line"><span style="color:#E1E4E8">        estimate, _ </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> inverse_propensity_score(sample_data, new_policy, sample_props)</span></span>
<span class="line"><span style="color:#E1E4E8">        estimates.append(estimate)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Point estimate on full data</span></span>
<span class="line"><span style="color:#E1E4E8">    point_estimate, effective_n </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> inverse_propensity_score(</span></span>
<span class="line"><span style="color:#E1E4E8">        logged_data, new_policy, propensity_scores</span></span>
<span class="line"><span style="color:#E1E4E8">    )</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Confidence interval from bootstrap distribution</span></span>
<span class="line"><span style="color:#E1E4E8">    ci_lower </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> np.percentile(estimates, </span><span style="color:#79B8FF">100</span><span style="color:#F97583"> *</span><span style="color:#E1E4E8"> alpha </span><span style="color:#F97583">/</span><span style="color:#79B8FF"> 2</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">    ci_upper </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> np.percentile(estimates, </span><span style="color:#79B8FF">100</span><span style="color:#F97583"> *</span><span style="color:#E1E4E8"> (</span><span style="color:#79B8FF">1</span><span style="color:#F97583"> -</span><span style="color:#E1E4E8"> alpha </span><span style="color:#F97583">/</span><span style="color:#79B8FF"> 2</span><span style="color:#E1E4E8">))</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    return</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#9ECBFF">        'estimate'</span><span style="color:#E1E4E8">: point_estimate,</span></span>
<span class="line"><span style="color:#9ECBFF">        'ci_lower'</span><span style="color:#E1E4E8">: ci_lower,</span></span>
<span class="line"><span style="color:#9ECBFF">        'ci_upper'</span><span style="color:#E1E4E8">: ci_upper,</span></span>
<span class="line"><span style="color:#9ECBFF">        'effective_n'</span><span style="color:#E1E4E8">: effective_n,</span></span>
<span class="line"><span style="color:#9ECBFF">        'std'</span><span style="color:#E1E4E8">: np.std(estimates)</span></span>
<span class="line"><span style="color:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D"># Example usage</span></span>
<span class="line"><span style="color:#F97583">if</span><span style="color:#79B8FF"> __name__</span><span style="color:#F97583"> ==</span><span style="color:#9ECBFF"> "__main__"</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#6A737D">    # Simulate logged data with known propensities</span></span>
<span class="line"><span style="color:#E1E4E8">    n </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 5000</span></span>
<span class="line"><span style="color:#E1E4E8">    n_actions </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 5</span></span>
<span class="line"><span style="color:#E1E4E8">    n_features </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 10</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#E1E4E8">    logged_data </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> []</span></span>
<span class="line"><span style="color:#E1E4E8">    propensity_scores </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> []</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    for</span><span style="color:#E1E4E8"> _ </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> range</span><span style="color:#E1E4E8">(n):</span></span>
<span class="line"><span style="color:#E1E4E8">        context </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> np.random.randn(n_features)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Logging policy: ε-greedy with ε=0.2</span></span>
<span class="line"><span style="color:#F97583">        if</span><span style="color:#E1E4E8"> np.random.random() </span><span style="color:#F97583">&#x3C;</span><span style="color:#79B8FF"> 0.2</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#E1E4E8">            action </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> np.random.randint(n_actions)  </span><span style="color:#6A737D"># Random</span></span>
<span class="line"><span style="color:#E1E4E8">            prop_score </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 0.2</span><span style="color:#F97583"> /</span><span style="color:#E1E4E8"> n_actions  </span><span style="color:#6A737D"># Uniform exploration</span></span>
<span class="line"><span style="color:#F97583">        else</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#E1E4E8">            action </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 0</span><span style="color:#6A737D">  # Always choose action 0 when exploiting</span></span>
<span class="line"><span style="color:#E1E4E8">            prop_score </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 0.8</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Simulate reward</span></span>
<span class="line"><span style="color:#E1E4E8">        true_best </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> np.argmax([np.sum(context[:</span><span style="color:#79B8FF">2</span><span style="color:#E1E4E8">]), np.sum(context[</span><span style="color:#79B8FF">2</span><span style="color:#E1E4E8">:</span><span style="color:#79B8FF">4</span><span style="color:#E1E4E8">]), </span></span>
<span class="line"><span style="color:#E1E4E8">                               np.sum(context[</span><span style="color:#79B8FF">4</span><span style="color:#E1E4E8">:</span><span style="color:#79B8FF">6</span><span style="color:#E1E4E8">]), np.sum(context[</span><span style="color:#79B8FF">6</span><span style="color:#E1E4E8">:</span><span style="color:#79B8FF">8</span><span style="color:#E1E4E8">]),</span></span>
<span class="line"><span style="color:#E1E4E8">                               np.sum(context[</span><span style="color:#79B8FF">8</span><span style="color:#E1E4E8">:])])</span></span>
<span class="line"><span style="color:#E1E4E8">        reward </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 1.0</span><span style="color:#F97583"> if</span><span style="color:#E1E4E8"> action </span><span style="color:#F97583">==</span><span style="color:#E1E4E8"> true_best </span><span style="color:#F97583">else</span><span style="color:#79B8FF"> 0.3</span></span>
<span class="line"><span style="color:#E1E4E8">        reward </span><span style="color:#F97583">+=</span><span style="color:#E1E4E8"> np.random.normal(</span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">0.1</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#E1E4E8">        logged_data.append((context, action, reward))</span></span>
<span class="line"><span style="color:#E1E4E8">        propensity_scores.append(prop_score)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Evaluate new policy</span></span>
<span class="line"><span style="color:#E1E4E8">    new_policy </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> LinUCB(</span><span style="color:#FFAB70">n_actions</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">n_actions, </span><span style="color:#FFAB70">n_features</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">n_features, </span><span style="color:#FFAB70">alpha</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1.0</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Train on logged data (for fair comparison)</span></span>
<span class="line"><span style="color:#F97583">    for</span><span style="color:#E1E4E8"> context, action, reward </span><span style="color:#F97583">in</span><span style="color:#E1E4E8"> logged_data[:</span><span style="color:#79B8FF">3000</span><span style="color:#E1E4E8">]:</span></span>
<span class="line"><span style="color:#E1E4E8">        new_policy.update(action, context, reward)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Evaluate</span></span>
<span class="line"><span style="color:#E1E4E8">    result </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> ips_with_confidence_interval(</span></span>
<span class="line"><span style="color:#E1E4E8">        logged_data[</span><span style="color:#79B8FF">3000</span><span style="color:#E1E4E8">:], new_policy, propensity_scores[</span><span style="color:#79B8FF">3000</span><span style="color:#E1E4E8">:]</span></span>
<span class="line"><span style="color:#E1E4E8">    )</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#79B8FF">    print</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">"IPS Evaluation Results:"</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#79B8FF">    print</span><span style="color:#E1E4E8">(</span><span style="color:#F97583">f</span><span style="color:#9ECBFF">"  Estimated reward: </span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">result[</span><span style="color:#9ECBFF">'estimate'</span><span style="color:#E1E4E8">]</span><span style="color:#F97583">:.3f</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">"</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#79B8FF">    print</span><span style="color:#E1E4E8">(</span><span style="color:#F97583">f</span><span style="color:#9ECBFF">"  95% CI: [</span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">result[</span><span style="color:#9ECBFF">'ci_lower'</span><span style="color:#E1E4E8">]</span><span style="color:#F97583">:.3f</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">, </span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">result[</span><span style="color:#9ECBFF">'ci_upper'</span><span style="color:#E1E4E8">]</span><span style="color:#F97583">:.3f</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">]"</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#79B8FF">    print</span><span style="color:#E1E4E8">(</span><span style="color:#F97583">f</span><span style="color:#9ECBFF">"  Effective sample size: </span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">result[</span><span style="color:#9ECBFF">'effective_n'</span><span style="color:#E1E4E8">]</span><span style="color:#F97583">:.0f</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF"> / </span><span style="color:#79B8FF">{len</span><span style="color:#E1E4E8">(logged_data) </span><span style="color:#F97583">-</span><span style="color:#79B8FF"> 3000}</span><span style="color:#9ECBFF">"</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#79B8FF">    print</span><span style="color:#E1E4E8">(</span><span style="color:#F97583">f</span><span style="color:#9ECBFF">"  Standard error: </span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">result[</span><span style="color:#9ECBFF">'std'</span><span style="color:#E1E4E8">]</span><span style="color:#F97583">:.3f</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">"</span><span style="color:#E1E4E8">)</span></span>
<span class="line"></span></code></pre>
<h3 id="ips-challenges">IPS Challenges</h3>
<p><strong>⚠️ High variance:</strong> If π₀(a|x) is small and π₁(a|x) is large, weights explode.</p>
<p><strong>Example:</strong> Logging policy chose action A with 1% probability. New policy always chooses A. Weight = 1/0.01 = 100×.</p>
<p><strong>Solutions:</strong></p>
<ol>
<li><strong>Clipping:</strong> Cap propensity scores at minimum (e.g., 0.01)</li>
<li><strong>Normalization:</strong> Self-normalized IPS (divide by sum of weights)</li>
<li><strong>Doubly Robust:</strong> Combine with model-based predictions</li>
</ol>
<hr>
<h2 id="method-3-doubly-robust-estimation-recommended">Method 3: Doubly Robust Estimation (Recommended)</h2>
<p><strong>Idea:</strong> Combine IPS with a reward model. Get benefits of both, robust to misspecification of either.</p>
<h3 id="mathematical-foundation-1">Mathematical Foundation</h3>
<p><strong>Doubly Robust estimator:</strong></p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mover accent="true"><mi>V</mi><mo>^</mo></mover><mtext>DR</mtext></msub><mo stretchy="false">(</mo><msub><mi>π</mi><mn>1</mn></msub><mo stretchy="false">)</mo><mo>=</mo><mfrac><mn>1</mn><mi>n</mi></mfrac><msubsup><mo>∑</mo><mrow><mi>i</mi><mo>=</mo><mn>1</mn></mrow><mi>n</mi></msubsup><mrow><mo fence="true">[</mo><mover accent="true"><mi>r</mi><mo>^</mo></mover><mo stretchy="false">(</mo><msub><mi>x</mi><mi>i</mi></msub><mo separator="true">,</mo><msub><mi>π</mi><mn>1</mn></msub><mo stretchy="false">(</mo><msub><mi>x</mi><mi>i</mi></msub><mo stretchy="false">)</mo><mo stretchy="false">)</mo><mo>+</mo><mfrac><mrow><msub><mi>π</mi><mn>1</mn></msub><mo stretchy="false">(</mo><msub><mi>a</mi><mi>i</mi></msub><mi mathvariant="normal">∣</mi><msub><mi>x</mi><mi>i</mi></msub><mo stretchy="false">)</mo></mrow><mrow><msub><mi>π</mi><mn>0</mn></msub><mo stretchy="false">(</mo><msub><mi>a</mi><mi>i</mi></msub><mi mathvariant="normal">∣</mi><msub><mi>x</mi><mi>i</mi></msub><mo stretchy="false">)</mo></mrow></mfrac><mo stretchy="false">(</mo><msub><mi>r</mi><mi>i</mi></msub><mo>−</mo><mover accent="true"><mi>r</mi><mo>^</mo></mover><mo stretchy="false">(</mo><msub><mi>x</mi><mi>i</mi></msub><mo separator="true">,</mo><msub><mi>a</mi><mi>i</mi></msub><mo stretchy="false">)</mo><mo stretchy="false">)</mo><mo fence="true">]</mo></mrow></mrow><annotation encoding="application/x-tex">\hat{V}_{\text{DR}}(\pi_1) = \frac{1}{n} \sum_{i=1}^{n} \left[ \hat{r}(x_i, \pi_1(x_i)) + \frac{\pi_1(a_i|x_i)}{\pi_0(a_i|x_i)} (r_i - \hat{r}(x_i, a_i)) \right]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.1968em;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.9468em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.22222em;">V</span></span><span style="top:-3.2523em;"><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.3283em;"><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 mtight"><span class="mord text mtight"><span class="mord mtight">DR</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.03588em;">π</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">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="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="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">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">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.1667em;"></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.8043em;"><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">i</span><span class="mrel mtight">=</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.2029em;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">n</span></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="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size2">[</span></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.02778em;">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="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.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" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">π</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">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="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.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="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="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.03588em;">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3173em;"><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 mtight">0</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="mopen mtight">(</span><span class="mord mtight"><span class="mord mathnormal mtight">a</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 class="mord mtight"><span class="mord mathnormal mtight">x</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="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.03588em;">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3173em;"><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 mtight">1</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="mopen mtight">(</span><span class="mord mtight"><span class="mord mathnormal mtight">a</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 class="mord mtight"><span class="mord mathnormal mtight">x</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="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="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">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="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.02778em;">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="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.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" style="margin-right:0.1667em;"></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.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="mclose">))</span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size2">]</span></span></span></span></span></span></p>
<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 stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\hat{r}(x,a)</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 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.02778em;">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="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">a</span><span class="mclose">)</span></span></span></span> is a learned reward model.</p>
<p><strong>Components:</strong></p>
<ol>
<li><strong>Direct method:</strong> <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 stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><msub><mi>π</mi><mn>1</mn></msub><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\hat{r}(x, \pi_1(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 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.02778em;">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="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.03588em;">π</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">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="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">))</span></span></span></span> — model-based prediction</li>
<li><strong>IPS correction:</strong> <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mfrac><mrow><msub><mi>π</mi><mn>1</mn></msub><mo stretchy="false">(</mo><mi>a</mi><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo></mrow><mrow><msub><mi>π</mi><mn>0</mn></msub><mo stretchy="false">(</mo><mi>a</mi><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo></mrow></mfrac><mo stretchy="false">(</mo><mi>r</mi><mo>−</mo><mover accent="true"><mi>r</mi><mo>^</mo></mover><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\frac{\pi_1(a|x)}{\pi_0(a|x)}(r - \hat{r}(x,a))</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.53em;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: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.03588em;">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3173em;"><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 mtight">0</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="mopen mtight">(</span><span class="mord mathnormal mtight">a</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.03588em;">π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3173em;"><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 mtight">1</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="mopen mtight">(</span><span class="mord mathnormal mtight">a</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="mopen">(</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</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 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.02778em;">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="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">a</span><span class="mclose">))</span></span></span></span> — corrects model errors using observed rewards</li>
</ol>
<p><strong>Why “doubly robust”:</strong></p>
<ul>
<li>If reward model is perfect: IPS correction = 0, uses model</li>
<li>If reward model is terrible but propensities correct: IPS correction fixes it</li>
<li>Unbiased if <strong>either</strong> model or propensities are correct (not necessarily both)</li>
</ul>
<h3 id="implementation-2">Implementation</h3>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#F97583">from</span><span style="color:#E1E4E8"> sklearn.ensemble </span><span style="color:#F97583">import</span><span style="color:#E1E4E8"> RandomForestRegressor</span></span>
<span class="line"><span style="color:#F97583">from</span><span style="color:#E1E4E8"> sklearn.preprocessing </span><span style="color:#F97583">import</span><span style="color:#E1E4E8"> StandardScaler</span></span>
<span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> numpy </span><span style="color:#F97583">as</span><span style="color:#E1E4E8"> np</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">class</span><span style="color:#B392F0"> DoublyRobustEvaluator</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#9ECBFF">    Doubly Robust offline evaluation.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Combines reward model predictions with IPS corrections.</span></span>
<span class="line"><span style="color:#9ECBFF">    More robust and lower variance than pure IPS.</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#79B8FF"> __init__</span><span style="color:#E1E4E8">(self, n_actions: </span><span style="color:#79B8FF">int</span><span style="color:#E1E4E8">, n_features: </span><span style="color:#79B8FF">int</span><span style="color:#E1E4E8">):</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Args:</span></span>
<span class="line"><span style="color:#9ECBFF">            n_actions: Number of actions</span></span>
<span class="line"><span style="color:#9ECBFF">            n_features: Context dimensionality</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.n_actions </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> n_actions</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.n_features </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> n_features</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Reward models for each action</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.reward_models </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> [</span></span>
<span class="line"><span style="color:#E1E4E8">            RandomForestRegressor(</span><span style="color:#FFAB70">n_estimators</span><span style="color:#F97583">=</span><span style="color:#79B8FF">100</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">max_depth</span><span style="color:#F97583">=</span><span style="color:#79B8FF">10</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#F97583">            for</span><span style="color:#E1E4E8"> _ </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> range</span><span style="color:#E1E4E8">(n_actions)</span></span>
<span class="line"><span style="color:#E1E4E8">        ]</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.scaler </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> StandardScaler()</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.is_trained </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> False</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> train_reward_models</span><span style="color:#E1E4E8">(self, logged_data: List[Tuple]):</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Train reward models on logged data.</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">        Args:</span></span>
<span class="line"><span style="color:#9ECBFF">            logged_data: List of (context, action, reward) tuples</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#6A737D">        # Organize data by action</span></span>
<span class="line"><span style="color:#E1E4E8">        action_data </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> {a: {</span><span style="color:#9ECBFF">'X'</span><span style="color:#E1E4E8">: [], </span><span style="color:#9ECBFF">'y'</span><span style="color:#E1E4E8">: []} </span><span style="color:#F97583">for</span><span style="color:#E1E4E8"> a </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> range</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.n_actions)}</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        for</span><span style="color:#E1E4E8"> context, action, reward </span><span style="color:#F97583">in</span><span style="color:#E1E4E8"> logged_data:</span></span>
<span class="line"><span style="color:#E1E4E8">            action_data[action][</span><span style="color:#9ECBFF">'X'</span><span style="color:#E1E4E8">].append(context)</span></span>
<span class="line"><span style="color:#E1E4E8">            action_data[action][</span><span style="color:#9ECBFF">'y'</span><span style="color:#E1E4E8">].append(reward)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Fit scaler on all contexts</span></span>
<span class="line"><span style="color:#E1E4E8">        all_contexts </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> [ctx </span><span style="color:#F97583">for</span><span style="color:#E1E4E8"> ctx, _, _ </span><span style="color:#F97583">in</span><span style="color:#E1E4E8"> logged_data]</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.scaler.fit(all_contexts)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Train model for each action</span></span>
<span class="line"><span style="color:#F97583">        for</span><span style="color:#E1E4E8"> a </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> range</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.n_actions):</span></span>
<span class="line"><span style="color:#F97583">            if</span><span style="color:#79B8FF"> len</span><span style="color:#E1E4E8">(action_data[a][</span><span style="color:#9ECBFF">'X'</span><span style="color:#E1E4E8">]) </span><span style="color:#F97583">></span><span style="color:#79B8FF"> 10</span><span style="color:#E1E4E8">:  </span><span style="color:#6A737D"># Need minimum data</span></span>
<span class="line"><span style="color:#E1E4E8">                X </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.scaler.transform(action_data[a][</span><span style="color:#9ECBFF">'X'</span><span style="color:#E1E4E8">])</span></span>
<span class="line"><span style="color:#E1E4E8">                y </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> action_data[a][</span><span style="color:#9ECBFF">'y'</span><span style="color:#E1E4E8">]</span></span>
<span class="line"><span style="color:#79B8FF">                self</span><span style="color:#E1E4E8">.reward_models[a].fit(X, y)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.is_trained </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> True</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> predict_reward</span><span style="color:#E1E4E8">(self, context: np.ndarray, action: </span><span style="color:#79B8FF">int</span><span style="color:#E1E4E8">) -> </span><span style="color:#79B8FF">float</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Predict reward for context-action pair.</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">        Args:</span></span>
<span class="line"><span style="color:#9ECBFF">            context: Context features</span></span>
<span class="line"><span style="color:#9ECBFF">            action: Action index</span></span>
<span class="line"><span style="color:#9ECBFF">            </span></span>
<span class="line"><span style="color:#9ECBFF">        Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">            predicted_reward: Model's prediction</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#F97583">        if</span><span style="color:#F97583"> not</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.is_trained:</span></span>
<span class="line"><span style="color:#F97583">            return</span><span style="color:#79B8FF"> 0.0</span><span style="color:#6A737D">  # No model yet</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#E1E4E8">        context_scaled </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.scaler.transform([context])</span></span>
<span class="line"><span style="color:#F97583">        return</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.reward_models[action].predict(context_scaled)[</span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">]</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> evaluate</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#E1E4E8">        self,</span></span>
<span class="line"><span style="color:#E1E4E8">        logged_data: List[Tuple],</span></span>
<span class="line"><span style="color:#E1E4E8">        new_policy,</span></span>
<span class="line"><span style="color:#E1E4E8">        propensity_scores: List[</span><span style="color:#79B8FF">float</span><span style="color:#E1E4E8">],</span></span>
<span class="line"><span style="color:#E1E4E8">        clip: </span><span style="color:#79B8FF">float</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 0.01</span></span>
<span class="line"><span style="color:#E1E4E8">    ) -> Dict[</span><span style="color:#79B8FF">str</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">float</span><span style="color:#E1E4E8">]:</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Doubly Robust evaluation.</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">        Args:</span></span>
<span class="line"><span style="color:#9ECBFF">            logged_data: Historical data</span></span>
<span class="line"><span style="color:#9ECBFF">            new_policy: Policy to evaluate</span></span>
<span class="line"><span style="color:#9ECBFF">            propensity_scores: Logging policy probabilities</span></span>
<span class="line"><span style="color:#9ECBFF">            clip: Minimum propensity score</span></span>
<span class="line"><span style="color:#9ECBFF">            </span></span>
<span class="line"><span style="color:#9ECBFF">        Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">            dict with estimates and diagnostics</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#F97583">        if</span><span style="color:#F97583"> not</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.is_trained:</span></span>
<span class="line"><span style="color:#F97583">            raise</span><span style="color:#79B8FF"> ValueError</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">"Must train reward models first"</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#E1E4E8">        n </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> len</span><span style="color:#E1E4E8">(logged_data)</span></span>
<span class="line"><span style="color:#E1E4E8">        direct_estimates </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> []</span></span>
<span class="line"><span style="color:#E1E4E8">        ips_corrections </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> []</span></span>
<span class="line"><span style="color:#E1E4E8">        dr_estimates </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> []</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        for</span><span style="color:#E1E4E8"> (context, action_logged, reward), prop_score </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> zip</span><span style="color:#E1E4E8">(logged_data, propensity_scores):</span></span>
<span class="line"><span style="color:#6A737D">            # Clip propensity</span></span>
<span class="line"><span style="color:#E1E4E8">            prop_score </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> max</span><span style="color:#E1E4E8">(prop_score, clip)</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#6A737D">            # New policy's action</span></span>
<span class="line"><span style="color:#E1E4E8">            action_new </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> new_policy.select_action(context)</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#6A737D">            # Direct method: predict reward for new policy's action</span></span>
<span class="line"><span style="color:#E1E4E8">            r_hat_new </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.predict_reward(context, action_new)</span></span>
<span class="line"><span style="color:#E1E4E8">            direct_estimates.append(r_hat_new)</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#6A737D">            # IPS correction term</span></span>
<span class="line"><span style="color:#F97583">            if</span><span style="color:#E1E4E8"> action_new </span><span style="color:#F97583">==</span><span style="color:#E1E4E8"> action_logged:</span></span>
<span class="line"><span style="color:#6A737D">                # Model prediction for logged action</span></span>
<span class="line"><span style="color:#E1E4E8">                r_hat_logged </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.predict_reward(context, action_logged)</span></span>
<span class="line"><span style="color:#E1E4E8">                </span></span>
<span class="line"><span style="color:#6A737D">                # Correction: (r - r_hat) / p</span></span>
<span class="line"><span style="color:#E1E4E8">                correction </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> (reward </span><span style="color:#F97583">-</span><span style="color:#E1E4E8"> r_hat_logged) </span><span style="color:#F97583">/</span><span style="color:#E1E4E8"> prop_score</span></span>
<span class="line"><span style="color:#E1E4E8">                ips_corrections.append(correction)</span></span>
<span class="line"><span style="color:#F97583">            else</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#E1E4E8">                ips_corrections.append(</span><span style="color:#79B8FF">0.0</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#6A737D">            # Doubly Robust: direct + correction</span></span>
<span class="line"><span style="color:#E1E4E8">            dr_estimates.append(r_hat_new </span><span style="color:#F97583">+</span><span style="color:#E1E4E8"> ips_corrections[</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">])</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        return</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#9ECBFF">            'doubly_robust'</span><span style="color:#E1E4E8">: np.mean(dr_estimates),</span></span>
<span class="line"><span style="color:#9ECBFF">            'direct_method'</span><span style="color:#E1E4E8">: np.mean(direct_estimates),</span></span>
<span class="line"><span style="color:#9ECBFF">            'ips_correction'</span><span style="color:#E1E4E8">: np.mean(ips_corrections),</span></span>
<span class="line"><span style="color:#9ECBFF">            'std'</span><span style="color:#E1E4E8">: np.std(dr_estimates),</span></span>
<span class="line"><span style="color:#9ECBFF">            'stderr'</span><span style="color:#E1E4E8">: np.std(dr_estimates) </span><span style="color:#F97583">/</span><span style="color:#E1E4E8"> np.sqrt(n)</span></span>
<span class="line"><span style="color:#E1E4E8">        }</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> evaluate_with_ci</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#E1E4E8">        self,</span></span>
<span class="line"><span style="color:#E1E4E8">        logged_data: List[Tuple],</span></span>
<span class="line"><span style="color:#E1E4E8">        new_policy,</span></span>
<span class="line"><span style="color:#E1E4E8">        propensity_scores: List[</span><span style="color:#79B8FF">float</span><span style="color:#E1E4E8">],</span></span>
<span class="line"><span style="color:#E1E4E8">        alpha: </span><span style="color:#79B8FF">float</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 0.05</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">        n_bootstrap: </span><span style="color:#79B8FF">int</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 1000</span></span>
<span class="line"><span style="color:#E1E4E8">    ) -> Dict[</span><span style="color:#79B8FF">str</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">float</span><span style="color:#E1E4E8">]:</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Doubly Robust evaluation with bootstrap confidence intervals.</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">        Args:</span></span>
<span class="line"><span style="color:#9ECBFF">            logged_data: Historical data</span></span>
<span class="line"><span style="color:#9ECBFF">            new_policy: Policy to evaluate</span></span>
<span class="line"><span style="color:#9ECBFF">            propensity_scores: Logging probabilities</span></span>
<span class="line"><span style="color:#9ECBFF">            alpha: Confidence level</span></span>
<span class="line"><span style="color:#9ECBFF">            n_bootstrap: Number of bootstrap samples</span></span>
<span class="line"><span style="color:#9ECBFF">            </span></span>
<span class="line"><span style="color:#9ECBFF">        Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">            dict with point estimate and confidence interval</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#E1E4E8">        n </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> len</span><span style="color:#E1E4E8">(logged_data)</span></span>
<span class="line"><span style="color:#E1E4E8">        estimates </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> []</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        for</span><span style="color:#E1E4E8"> _ </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> range</span><span style="color:#E1E4E8">(n_bootstrap):</span></span>
<span class="line"><span style="color:#6A737D">            # Bootstrap resample</span></span>
<span class="line"><span style="color:#E1E4E8">            indices </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> np.random.choice(n, </span><span style="color:#FFAB70">size</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">n, </span><span style="color:#FFAB70">replace</span><span style="color:#F97583">=</span><span style="color:#79B8FF">True</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">            sample_data </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> [logged_data[i] </span><span style="color:#F97583">for</span><span style="color:#E1E4E8"> i </span><span style="color:#F97583">in</span><span style="color:#E1E4E8"> indices]</span></span>
<span class="line"><span style="color:#E1E4E8">            sample_props </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> [propensity_scores[i] </span><span style="color:#F97583">for</span><span style="color:#E1E4E8"> i </span><span style="color:#F97583">in</span><span style="color:#E1E4E8"> indices]</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#6A737D">            # Evaluate on sample</span></span>
<span class="line"><span style="color:#E1E4E8">            result </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.evaluate(sample_data, new_policy, sample_props)</span></span>
<span class="line"><span style="color:#E1E4E8">            estimates.append(result[</span><span style="color:#9ECBFF">'doubly_robust'</span><span style="color:#E1E4E8">])</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Point estimate on full data</span></span>
<span class="line"><span style="color:#E1E4E8">        point_result </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.evaluate(logged_data, new_policy, propensity_scores)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Confidence interval</span></span>
<span class="line"><span style="color:#E1E4E8">        ci_lower </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> np.percentile(estimates, </span><span style="color:#79B8FF">100</span><span style="color:#F97583"> *</span><span style="color:#E1E4E8"> alpha </span><span style="color:#F97583">/</span><span style="color:#79B8FF"> 2</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        ci_upper </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> np.percentile(estimates, </span><span style="color:#79B8FF">100</span><span style="color:#F97583"> *</span><span style="color:#E1E4E8"> (</span><span style="color:#79B8FF">1</span><span style="color:#F97583"> -</span><span style="color:#E1E4E8"> alpha </span><span style="color:#F97583">/</span><span style="color:#79B8FF"> 2</span><span style="color:#E1E4E8">))</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        return</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#F97583">            **</span><span style="color:#E1E4E8">point_result,</span></span>
<span class="line"><span style="color:#9ECBFF">            'ci_lower'</span><span style="color:#E1E4E8">: ci_lower,</span></span>
<span class="line"><span style="color:#9ECBFF">            'ci_upper'</span><span style="color:#E1E4E8">: ci_upper</span></span>
<span class="line"><span style="color:#E1E4E8">        }</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D"># Example usage</span></span>
<span class="line"><span style="color:#F97583">if</span><span style="color:#79B8FF"> __name__</span><span style="color:#F97583"> ==</span><span style="color:#9ECBFF"> "__main__"</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#6A737D">    # Generate logged data</span></span>
<span class="line"><span style="color:#E1E4E8">    n_train </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 10000</span></span>
<span class="line"><span style="color:#E1E4E8">    n_test </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 2000</span></span>
<span class="line"><span style="color:#E1E4E8">    n_actions </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 5</span></span>
<span class="line"><span style="color:#E1E4E8">    n_features </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 10</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> generate_data</span><span style="color:#E1E4E8">(n):</span></span>
<span class="line"><span style="color:#E1E4E8">        data </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> []</span></span>
<span class="line"><span style="color:#E1E4E8">        props </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> []</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        for</span><span style="color:#E1E4E8"> _ </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> range</span><span style="color:#E1E4E8">(n):</span></span>
<span class="line"><span style="color:#E1E4E8">            context </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> np.random.randn(n_features)</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#6A737D">            # True best action (unknown to evaluator)</span></span>
<span class="line"><span style="color:#E1E4E8">            true_rewards </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> [</span></span>
<span class="line"><span style="color:#E1E4E8">                np.sum(context[:</span><span style="color:#79B8FF">2</span><span style="color:#E1E4E8">]) </span><span style="color:#F97583">+</span><span style="color:#E1E4E8"> np.random.normal(</span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">0.1</span><span style="color:#E1E4E8">),</span></span>
<span class="line"><span style="color:#E1E4E8">                np.sum(context[</span><span style="color:#79B8FF">2</span><span style="color:#E1E4E8">:</span><span style="color:#79B8FF">4</span><span style="color:#E1E4E8">]) </span><span style="color:#F97583">+</span><span style="color:#E1E4E8"> np.random.normal(</span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">0.1</span><span style="color:#E1E4E8">),</span></span>
<span class="line"><span style="color:#E1E4E8">                np.sum(context[</span><span style="color:#79B8FF">4</span><span style="color:#E1E4E8">:</span><span style="color:#79B8FF">6</span><span style="color:#E1E4E8">]) </span><span style="color:#F97583">+</span><span style="color:#E1E4E8"> np.random.normal(</span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">0.1</span><span style="color:#E1E4E8">),</span></span>
<span class="line"><span style="color:#E1E4E8">                np.sum(context[</span><span style="color:#79B8FF">6</span><span style="color:#E1E4E8">:</span><span style="color:#79B8FF">8</span><span style="color:#E1E4E8">]) </span><span style="color:#F97583">+</span><span style="color:#E1E4E8"> np.random.normal(</span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">0.1</span><span style="color:#E1E4E8">),</span></span>
<span class="line"><span style="color:#E1E4E8">                np.sum(context[</span><span style="color:#79B8FF">8</span><span style="color:#E1E4E8">:]) </span><span style="color:#F97583">+</span><span style="color:#E1E4E8"> np.random.normal(</span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">0.1</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">            ]</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#6A737D">            # Logging policy: ε-greedy (ε=0.3)</span></span>
<span class="line"><span style="color:#F97583">            if</span><span style="color:#E1E4E8"> np.random.random() </span><span style="color:#F97583">&#x3C;</span><span style="color:#79B8FF"> 0.3</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#E1E4E8">                action </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> np.random.randint(n_actions)</span></span>
<span class="line"><span style="color:#E1E4E8">                prop </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 0.3</span><span style="color:#F97583"> /</span><span style="color:#E1E4E8"> n_actions</span></span>
<span class="line"><span style="color:#F97583">            else</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#E1E4E8">                action </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 0</span><span style="color:#6A737D">  # Always exploit action 0</span></span>
<span class="line"><span style="color:#E1E4E8">                prop </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 0.7</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#E1E4E8">            reward </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> true_rewards[action]</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#E1E4E8">            data.append((context, action, reward))</span></span>
<span class="line"><span style="color:#E1E4E8">            props.append(prop)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        return</span><span style="color:#E1E4E8"> data, props</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#E1E4E8">    train_data, train_props </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> generate_data(n_train)</span></span>
<span class="line"><span style="color:#E1E4E8">    test_data, test_props </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> generate_data(n_test)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Train reward models</span></span>
<span class="line"><span style="color:#79B8FF">    print</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">"Training reward models..."</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">    evaluator </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> DoublyRobustEvaluator(n_actions, n_features)</span></span>
<span class="line"><span style="color:#E1E4E8">    evaluator.train_reward_models(train_data)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Create new policy</span></span>
<span class="line"><span style="color:#E1E4E8">    new_policy </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> LinUCB(</span><span style="color:#FFAB70">n_actions</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">n_actions, </span><span style="color:#FFAB70">n_features</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">n_features, </span><span style="color:#FFAB70">alpha</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1.0</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Train new policy on first half of train data</span></span>
<span class="line"><span style="color:#F97583">    for</span><span style="color:#E1E4E8"> context, action, reward </span><span style="color:#F97583">in</span><span style="color:#E1E4E8"> train_data[:</span><span style="color:#79B8FF">5000</span><span style="color:#E1E4E8">]:</span></span>
<span class="line"><span style="color:#E1E4E8">        new_policy.update(action, context, reward)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Evaluate</span></span>
<span class="line"><span style="color:#79B8FF">    print</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">"</span><span style="color:#79B8FF">\n</span><span style="color:#9ECBFF">Evaluating new policy..."</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">    result </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> evaluator.evaluate_with_ci(test_data, new_policy, test_props)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#79B8FF">    print</span><span style="color:#E1E4E8">(</span><span style="color:#F97583">f</span><span style="color:#9ECBFF">"</span><span style="color:#79B8FF">\n</span><span style="color:#9ECBFF">Doubly Robust Evaluation:"</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#79B8FF">    print</span><span style="color:#E1E4E8">(</span><span style="color:#F97583">f</span><span style="color:#9ECBFF">"  Estimate: </span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">result[</span><span style="color:#9ECBFF">'doubly_robust'</span><span style="color:#E1E4E8">]</span><span style="color:#F97583">:.3f</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">"</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#79B8FF">    print</span><span style="color:#E1E4E8">(</span><span style="color:#F97583">f</span><span style="color:#9ECBFF">"  95% CI: [</span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">result[</span><span style="color:#9ECBFF">'ci_lower'</span><span style="color:#E1E4E8">]</span><span style="color:#F97583">:.3f</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">, </span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">result[</span><span style="color:#9ECBFF">'ci_upper'</span><span style="color:#E1E4E8">]</span><span style="color:#F97583">:.3f</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">]"</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#79B8FF">    print</span><span style="color:#E1E4E8">(</span><span style="color:#F97583">f</span><span style="color:#9ECBFF">"  Direct method: </span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">result[</span><span style="color:#9ECBFF">'direct_method'</span><span style="color:#E1E4E8">]</span><span style="color:#F97583">:.3f</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">"</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#79B8FF">    print</span><span style="color:#E1E4E8">(</span><span style="color:#F97583">f</span><span style="color:#9ECBFF">"  IPS correction: </span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">result[</span><span style="color:#9ECBFF">'ips_correction'</span><span style="color:#E1E4E8">]</span><span style="color:#F97583">:.3f</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">"</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#79B8FF">    print</span><span style="color:#E1E4E8">(</span><span style="color:#F97583">f</span><span style="color:#9ECBFF">"  Std error: </span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">result[</span><span style="color:#9ECBFF">'stderr'</span><span style="color:#E1E4E8">]</span><span style="color:#F97583">:.4f</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">"</span><span style="color:#E1E4E8">)</span></span>
<span class="line"></span></code></pre>
<h3 id="when-to-use-each-method">When to Use Each Method</h3>





























<table><thead><tr><th>Method</th><th>Bias</th><th>Variance</th><th>Best For</th></tr></thead><tbody><tr><td><strong>Replay</strong></td><td>High</td><td>Low</td><td>Quick validation, similar policies</td></tr><tr><td><strong>IPS</strong></td><td>None</td><td>High</td><td>Have good propensities, policies not too different</td></tr><tr><td><strong>Doubly Robust</strong></td><td>None*</td><td>Medium</td><td><strong>Production recommendation</strong></td></tr></tbody></table>
<p>*Unbiased if either model or propensities are correct</p>
<hr>
<h2 id="production-architecture-8-core-components">Production Architecture: 8 Core Components</h2>
<p>Here’s the complete system architecture for production contextual bandits:</p>
<div class="mermaid">
graph TB
    subgraph Serving["🚀 SERVING LAYER"]
        API[API Service&#x3C;br/>━━━━━━━━&#x3C;br/>Context builder&#x3C;br/>Action filtering]
        Policy[Policy Service&#x3C;br/>━━━━━━━━&#x3C;br/>LinUCB/Thompson&#x3C;br/>Feature extraction]
        Cache[Cache Layer&#x3C;br/>━━━━━━━━&#x3C;br/>Redis&#x3C;br/>Model params]
    end
    
    subgraph Logging["📝 LOGGING LAYER"]
        Logger[Event Logger&#x3C;br/>━━━━━━━━&#x3C;br/>Context, action, props&#x3C;br/>Kafka → S3]
        Reward[Reward Collector&#x3C;br/>━━━━━━━━&#x3C;br/>Match decisions&#x3C;br/>Handle delays]
    end
    
    subgraph Training["🔬 TRAINING LAYER"]
        Eval[Offline Evaluator&#x3C;br/>━━━━━━━━&#x3C;br/>IPS, Doubly Robust&#x3C;br/>Policy comparison]
        Trainer[Policy Trainer&#x3C;br/>━━━━━━━━&#x3C;br/>Batch updates&#x3C;br/>Model versioning]
    end
    
    subgraph Monitoring["📊 MONITORING LAYER"]
        Monitor[Monitor Service&#x3C;br/>━━━━━━━━&#x3C;br/>Metrics, alerts&#x3C;br/>Dashboards]
        Safety[Safety Layer&#x3C;br/>━━━━━━━━&#x3C;br/>Circuit breaker&#x3C;br/>Fallback policy]
    end
    
    User[User Request] --> API
    API --> Policy
    Policy --> Cache
    Policy --> Action[Chosen Action]
    
    Action --> Logger
    Action --> User
    User --> Reward
    
    Logger --> Eval
    Reward --> Eval
    Eval --> Trainer
    Trainer --> Cache
    
    Policy --> Monitor
    Logger --> Monitor
    Monitor --> Safety
    Safety -.fallback.-> Policy
    
    style Serving stroke:#10b981,stroke-width:2px
    style Logging stroke:#06b6d4,stroke-width:2px
    style Training stroke:#8b5cf6,stroke-width:2px
    style Monitoring stroke:#f59e0b,stroke-width:2px
    
    style API stroke:#10b981
    style Policy stroke:#10b981
    style Cache stroke:#10b981
    
    style Logger stroke:#06b6d4
    style Reward stroke:#06b6d4
    
    style Eval stroke:#8b5cf6
    style Trainer stroke:#8b5cf6
    
    style Monitor stroke:#f59e0b
    style Safety stroke:#ef4444</div>
<h3 id="component-details">Component Details</h3>
<h4 id="1-api-service-context-builder">1. <strong>API Service (Context Builder)</strong></h4>
<p><strong>Purpose:</strong> Build context features from request, filter available actions</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#F97583">class</span><span style="color:#B392F0"> ContextBuilder</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#9ECBFF">    Builds context vector from raw request data.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Extracts user features, item features, situational features.</span></span>
<span class="line"><span style="color:#9ECBFF">    Handles missing values, normalization, feature engineering.</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#79B8FF"> __init__</span><span style="color:#E1E4E8">(self, feature_config: Dict):</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.feature_config </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> feature_config</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.scaler </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> StandardScaler()</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> build_context</span><span style="color:#E1E4E8">(self, request: Dict) -> np.ndarray:</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Build context vector from request.</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">        Args:</span></span>
<span class="line"><span style="color:#9ECBFF">            request: Raw request data (user_id, session_data, etc.)</span></span>
<span class="line"><span style="color:#9ECBFF">            </span></span>
<span class="line"><span style="color:#9ECBFF">        Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">            context: Feature vector for bandit</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#E1E4E8">        features </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> []</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # User features</span></span>
<span class="line"><span style="color:#E1E4E8">        user_id </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> request.get(</span><span style="color:#9ECBFF">'user_id'</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        user_features </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">._get_user_features(user_id)</span></span>
<span class="line"><span style="color:#E1E4E8">        features.extend(user_features)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Situational features</span></span>
<span class="line"><span style="color:#E1E4E8">        time_features </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">._get_time_features(request.get(</span><span style="color:#9ECBFF">'timestamp'</span><span style="color:#E1E4E8">))</span></span>
<span class="line"><span style="color:#E1E4E8">        features.extend(time_features)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Device features</span></span>
<span class="line"><span style="color:#E1E4E8">        device_features </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">._get_device_features(request.get(</span><span style="color:#9ECBFF">'device'</span><span style="color:#E1E4E8">))</span></span>
<span class="line"><span style="color:#E1E4E8">        features.extend(device_features)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Convert to array and normalize</span></span>
<span class="line"><span style="color:#E1E4E8">        context </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> np.array(features)</span></span>
<span class="line"><span style="color:#F97583">        return</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.scaler.transform([context])[</span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">]</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> filter_actions</span><span style="color:#E1E4E8">(self, request: Dict, all_actions: List) -> List:</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Filter actions based on business constraints.</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">        Args:</span></span>
<span class="line"><span style="color:#9ECBFF">            request: User request</span></span>
<span class="line"><span style="color:#9ECBFF">            all_actions: All possible actions</span></span>
<span class="line"><span style="color:#9ECBFF">            </span></span>
<span class="line"><span style="color:#9ECBFF">        Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">            eligible_actions: Actions that can be shown to this user</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#E1E4E8">        eligible </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> []</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        for</span><span style="color:#E1E4E8"> action </span><span style="color:#F97583">in</span><span style="color:#E1E4E8"> all_actions:</span></span>
<span class="line"><span style="color:#6A737D">            # Business rules</span></span>
<span class="line"><span style="color:#F97583">            if</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">._is_eligible(action, request):</span></span>
<span class="line"><span style="color:#E1E4E8">                eligible.append(action)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        return</span><span style="color:#E1E4E8"> eligible</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> _is_eligible</span><span style="color:#E1E4E8">(self, action, request) -> </span><span style="color:#79B8FF">bool</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#9ECBFF">        """Check if action satisfies constraints."""</span></span>
<span class="line"><span style="color:#6A737D">        # Example constraints:</span></span>
<span class="line"><span style="color:#6A737D">        # - User hasn't seen this in last 24h</span></span>
<span class="line"><span style="color:#6A737D">        # - Action is available in user's region</span></span>
<span class="line"><span style="color:#6A737D">        # - User meets age/subscription requirements</span></span>
<span class="line"><span style="color:#F97583">        return</span><span style="color:#79B8FF"> True</span><span style="color:#6A737D">  # Implement actual logic</span></span>
<span class="line"></span></code></pre>
<h4 id="2-policy-service">2. <strong>Policy Service</strong></h4>
<p><strong>Purpose:</strong> Execute bandit algorithm, return action with propensity</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#F97583">class</span><span style="color:#B392F0"> PolicyService</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#9ECBFF">    Serves bandit policy decisions.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Loads model from cache, computes action, logs propensity.</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#79B8FF"> __init__</span><span style="color:#E1E4E8">(self, model_cache, policy_type</span><span style="color:#F97583">=</span><span style="color:#9ECBFF">'linucb'</span><span style="color:#E1E4E8">):</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.cache </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> model_cache</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.policy_type </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> policy_type</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.policy </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> None</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> load_policy</span><span style="color:#E1E4E8">(self, version: </span><span style="color:#79B8FF">str</span><span style="color:#F97583"> =</span><span style="color:#9ECBFF"> 'latest'</span><span style="color:#E1E4E8">):</span></span>
<span class="line"><span style="color:#9ECBFF">        """Load policy from cache or storage."""</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.policy </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.cache.get(</span><span style="color:#F97583">f</span><span style="color:#9ECBFF">'policy:</span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">version</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">'</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#F97583">        if</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.policy </span><span style="color:#F97583">is</span><span style="color:#79B8FF"> None</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#F97583">            raise</span><span style="color:#79B8FF"> ValueError</span><span style="color:#E1E4E8">(</span><span style="color:#F97583">f</span><span style="color:#9ECBFF">"Policy version </span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">version</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF"> not found"</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> select_action</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#E1E4E8">        self,</span></span>
<span class="line"><span style="color:#E1E4E8">        context: np.ndarray,</span></span>
<span class="line"><span style="color:#E1E4E8">        eligible_actions: List[</span><span style="color:#79B8FF">int</span><span style="color:#E1E4E8">]</span></span>
<span class="line"><span style="color:#E1E4E8">    ) -> Dict:</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Select action using policy.</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">        Args:</span></span>
<span class="line"><span style="color:#9ECBFF">            context: Feature vector</span></span>
<span class="line"><span style="color:#9ECBFF">            eligible_actions: Available actions</span></span>
<span class="line"><span style="color:#9ECBFF">            </span></span>
<span class="line"><span style="color:#9ECBFF">        Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">            dict with 'action', 'propensity', 'scores', 'version'</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#6A737D">        # Get scores for all eligible actions</span></span>
<span class="line"><span style="color:#E1E4E8">        scores </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> {}</span></span>
<span class="line"><span style="color:#F97583">        for</span><span style="color:#E1E4E8"> action </span><span style="color:#F97583">in</span><span style="color:#E1E4E8"> eligible_actions:</span></span>
<span class="line"><span style="color:#F97583">            if</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.policy_type </span><span style="color:#F97583">==</span><span style="color:#9ECBFF"> 'linucb'</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#E1E4E8">                scores[action] </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">._compute_ucb(context, action)</span></span>
<span class="line"><span style="color:#F97583">            elif</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.policy_type </span><span style="color:#F97583">==</span><span style="color:#9ECBFF"> 'thompson'</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#E1E4E8">                scores[action] </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">._sample_thompson(context, action)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Choose action</span></span>
<span class="line"><span style="color:#E1E4E8">        chosen_action </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> max</span><span style="color:#E1E4E8">(scores.keys(), </span><span style="color:#FFAB70">key</span><span style="color:#F97583">=lambda</span><span style="color:#E1E4E8"> a: scores[a])</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Compute propensity (for offline eval)</span></span>
<span class="line"><span style="color:#6A737D">        # For deterministic policies: propensity = 1.0 for chosen, 0 for others</span></span>
<span class="line"><span style="color:#6A737D">        # For stochastic: use softmax or actual sampling probability</span></span>
<span class="line"><span style="color:#E1E4E8">        propensity </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 1.0</span><span style="color:#6A737D">  # Deterministic</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        return</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#9ECBFF">            'action'</span><span style="color:#E1E4E8">: chosen_action,</span></span>
<span class="line"><span style="color:#9ECBFF">            'propensity'</span><span style="color:#E1E4E8">: propensity,</span></span>
<span class="line"><span style="color:#9ECBFF">            'scores'</span><span style="color:#E1E4E8">: scores,</span></span>
<span class="line"><span style="color:#9ECBFF">            'version'</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.policy.version,</span></span>
<span class="line"><span style="color:#9ECBFF">            'timestamp'</span><span style="color:#E1E4E8">: time.time()</span></span>
<span class="line"><span style="color:#E1E4E8">        }</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> _compute_ucb</span><span style="color:#E1E4E8">(self, context, action):</span></span>
<span class="line"><span style="color:#9ECBFF">        """Compute UCB score for action."""</span></span>
<span class="line"><span style="color:#6A737D">        # Implementation depends on LinUCB details</span></span>
<span class="line"><span style="color:#F97583">        return</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.policy.get_ucb(context, action)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> _sample_thompson</span><span style="color:#E1E4E8">(self, context, action):</span></span>
<span class="line"><span style="color:#9ECBFF">        """Sample from posterior for action."""</span></span>
<span class="line"><span style="color:#F97583">        return</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.policy.sample_reward(context, action)</span></span>
<span class="line"></span></code></pre>
<h4 id="3-event-logger">3. <strong>Event Logger</strong></h4>
<p><strong>Purpose:</strong> Log all decisions with full context for offline evaluation</p>
<p><strong>Critical data to log:</strong></p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#E1E4E8">{</span></span>
<span class="line"><span style="color:#6A737D">    # Decision event</span></span>
<span class="line"><span style="color:#9ECBFF">    "event_id"</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"uuid"</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#9ECBFF">    "timestamp"</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"2025-11-15T10:30:45Z"</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#9ECBFF">    "user_id"</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"user_12345"</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Context (features)</span></span>
<span class="line"><span style="color:#9ECBFF">    "context"</span><span style="color:#E1E4E8">: [</span><span style="color:#79B8FF">0.5</span><span style="color:#E1E4E8">, </span><span style="color:#F97583">-</span><span style="color:#79B8FF">1.2</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">0.3</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">...</span><span style="color:#E1E4E8">],  </span><span style="color:#6A737D"># Full feature vector</span></span>
<span class="line"><span style="color:#9ECBFF">    "context_metadata"</span><span style="color:#E1E4E8">: {</span></span>
<span class="line"><span style="color:#9ECBFF">        "user_tenure_days"</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">45</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#9ECBFF">        "device"</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"mobile"</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#9ECBFF">        "time_of_day"</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"morning"</span></span>
<span class="line"><span style="color:#E1E4E8">    },</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Decision</span></span>
<span class="line"><span style="color:#9ECBFF">    "action_chosen"</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">42</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#9ECBFF">    "eligible_actions"</span><span style="color:#E1E4E8">: [</span><span style="color:#79B8FF">42</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">17</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">91</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">...</span><span style="color:#E1E4E8">],</span></span>
<span class="line"><span style="color:#9ECBFF">    "propensity"</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">1.0</span><span style="color:#E1E4E8">,  </span><span style="color:#6A737D"># P(action_chosen | context)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Model</span></span>
<span class="line"><span style="color:#9ECBFF">    "policy_version"</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"v1.2.3"</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#9ECBFF">    "algorithm"</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"linucb"</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#9ECBFF">    "exploration_param"</span><span style="color:#E1E4E8">: {</span><span style="color:#9ECBFF">"alpha"</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">1.0</span><span style="color:#E1E4E8">},</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Scores (for debugging)</span></span>
<span class="line"><span style="color:#9ECBFF">    "action_scores"</span><span style="color:#E1E4E8">: {</span><span style="color:#79B8FF">42</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">0.85</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">17</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">0.72</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">91</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">0.68</span><span style="color:#E1E4E8">},</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#6A737D">    # Reward (initially null, filled later)</span></span>
<span class="line"><span style="color:#9ECBFF">    "reward"</span><span style="color:#E1E4E8">: null,</span></span>
<span class="line"><span style="color:#9ECBFF">    "reward_timestamp"</span><span style="color:#E1E4E8">: null</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span>
<span class="line"></span></code></pre>
<p><strong>Implementation:</strong></p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#F97583">class</span><span style="color:#B392F0"> EventLogger</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#9ECBFF">    Logs bandit decisions to Kafka/Kinesis for downstream processing.</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#79B8FF"> __init__</span><span style="color:#E1E4E8">(self, kafka_producer):</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.producer </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> kafka_producer</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.topic </span><span style="color:#F97583">=</span><span style="color:#9ECBFF"> 'bandit-decisions'</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> log_decision</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#E1E4E8">        self,</span></span>
<span class="line"><span style="color:#E1E4E8">        event_id: </span><span style="color:#79B8FF">str</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">        context: np.ndarray,</span></span>
<span class="line"><span style="color:#E1E4E8">        action: </span><span style="color:#79B8FF">int</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">        propensity: </span><span style="color:#79B8FF">float</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">        metadata: Dict</span></span>
<span class="line"><span style="color:#E1E4E8">    ):</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Log a bandit decision event.</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">        Args:</span></span>
<span class="line"><span style="color:#9ECBFF">            event_id: Unique identifier for this decision</span></span>
<span class="line"><span style="color:#9ECBFF">            context: Feature vector</span></span>
<span class="line"><span style="color:#9ECBFF">            action: Chosen action</span></span>
<span class="line"><span style="color:#9ECBFF">            propensity: P(action | context)</span></span>
<span class="line"><span style="color:#9ECBFF">            metadata: Additional context (user_id, version, etc.)</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#E1E4E8">        event </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#9ECBFF">            'event_id'</span><span style="color:#E1E4E8">: event_id,</span></span>
<span class="line"><span style="color:#9ECBFF">            'timestamp'</span><span style="color:#E1E4E8">: time.time(),</span></span>
<span class="line"><span style="color:#9ECBFF">            'context'</span><span style="color:#E1E4E8">: context.tolist(),</span></span>
<span class="line"><span style="color:#9ECBFF">            'action'</span><span style="color:#E1E4E8">: action,</span></span>
<span class="line"><span style="color:#9ECBFF">            'propensity'</span><span style="color:#E1E4E8">: propensity,</span></span>
<span class="line"><span style="color:#F97583">            **</span><span style="color:#E1E4E8">metadata</span></span>
<span class="line"><span style="color:#E1E4E8">        }</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Send to Kafka</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.producer.send(</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.topic, </span><span style="color:#FFAB70">value</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">event)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Also log to structured storage (S3, BigQuery)</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">._archive_to_storage(event)</span></span>
<span class="line"></span></code></pre>
<h4 id="4-reward-collector">4. <strong>Reward Collector</strong></h4>
<p><strong>Purpose:</strong> Match rewards to decisions, handle delayed rewards</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#F97583">class</span><span style="color:#B392F0"> RewardCollector</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#9ECBFF">    Collects rewards and matches them to logged decisions.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Handles delayed rewards (e.g., purchases hours after click).</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#79B8FF"> __init__</span><span style="color:#E1E4E8">(self, event_store):</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.event_store </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> event_store</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> record_reward</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#E1E4E8">        self,</span></span>
<span class="line"><span style="color:#E1E4E8">        event_id: </span><span style="color:#79B8FF">str</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">        reward: </span><span style="color:#79B8FF">float</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">        reward_timestamp: </span><span style="color:#79B8FF">float</span></span>
<span class="line"><span style="color:#E1E4E8">    ):</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Record reward for a decision event.</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">        Args:</span></span>
<span class="line"><span style="color:#9ECBFF">            event_id: ID of the decision event</span></span>
<span class="line"><span style="color:#9ECBFF">            reward: Observed reward value</span></span>
<span class="line"><span style="color:#9ECBFF">            reward_timestamp: When reward occurred</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#6A737D">        # Update event with reward</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.event_store.update(</span></span>
<span class="line"><span style="color:#E1E4E8">            event_id,</span></span>
<span class="line"><span style="color:#E1E4E8">            {</span></span>
<span class="line"><span style="color:#9ECBFF">                'reward'</span><span style="color:#E1E4E8">: reward,</span></span>
<span class="line"><span style="color:#9ECBFF">                'reward_timestamp'</span><span style="color:#E1E4E8">: reward_timestamp,</span></span>
<span class="line"><span style="color:#9ECBFF">                'reward_delay'</span><span style="color:#E1E4E8">: reward_timestamp </span><span style="color:#F97583">-</span><span style="color:#E1E4E8"> event[</span><span style="color:#9ECBFF">'timestamp'</span><span style="color:#E1E4E8">]</span></span>
<span class="line"><span style="color:#E1E4E8">            }</span></span>
<span class="line"><span style="color:#E1E4E8">        )</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> match_rewards_batch</span><span style="color:#E1E4E8">(self, reward_events: List[Dict]):</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Batch process reward events and match to decisions.</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">        For example: purchases (rewards) matched to product clicks (decisions)</span></span>
<span class="line"><span style="color:#9ECBFF">        based on user_id and time window.</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#F97583">        for</span><span style="color:#E1E4E8"> reward_event </span><span style="color:#F97583">in</span><span style="color:#E1E4E8"> reward_events:</span></span>
<span class="line"><span style="color:#6A737D">            # Find corresponding decision event</span></span>
<span class="line"><span style="color:#E1E4E8">            decision_events </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.event_store.find(</span></span>
<span class="line"><span style="color:#FFAB70">                user_id</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">reward_event[</span><span style="color:#9ECBFF">'user_id'</span><span style="color:#E1E4E8">],</span></span>
<span class="line"><span style="color:#FFAB70">                timestamp_after</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">reward_event[</span><span style="color:#9ECBFF">'timestamp'</span><span style="color:#E1E4E8">] </span><span style="color:#F97583">-</span><span style="color:#79B8FF"> 3600</span><span style="color:#E1E4E8">,  </span><span style="color:#6A737D"># 1 hour window</span></span>
<span class="line"><span style="color:#FFAB70">                timestamp_before</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">reward_event[</span><span style="color:#9ECBFF">'timestamp'</span><span style="color:#E1E4E8">]</span></span>
<span class="line"><span style="color:#E1E4E8">            )</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#6A737D">            # Attribute reward to most recent eligible decision</span></span>
<span class="line"><span style="color:#F97583">            if</span><span style="color:#E1E4E8"> decision_events:</span></span>
<span class="line"><span style="color:#E1E4E8">                latest_decision </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> decision_events[</span><span style="color:#F97583">-</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">]</span></span>
<span class="line"><span style="color:#79B8FF">                self</span><span style="color:#E1E4E8">.record_reward(</span></span>
<span class="line"><span style="color:#E1E4E8">                    latest_decision[</span><span style="color:#9ECBFF">'event_id'</span><span style="color:#E1E4E8">],</span></span>
<span class="line"><span style="color:#E1E4E8">                    reward_event[</span><span style="color:#9ECBFF">'reward'</span><span style="color:#E1E4E8">],</span></span>
<span class="line"><span style="color:#E1E4E8">                    reward_event[</span><span style="color:#9ECBFF">'timestamp'</span><span style="color:#E1E4E8">]</span></span>
<span class="line"><span style="color:#E1E4E8">                )</span></span>
<span class="line"></span></code></pre>
<h4 id="5-offline-evaluator">5. <strong>Offline Evaluator</strong></h4>
<p><strong>Purpose:</strong> Continuously evaluate policy performance on historical data</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#F97583">class</span><span style="color:#B392F0"> OfflineEvaluator</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#9ECBFF">    Continuously evaluates policy candidates using logged data.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Runs IPS, Doubly Robust, and other offline metrics.</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#79B8FF"> __init__</span><span style="color:#E1E4E8">(self):</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.evaluators </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#9ECBFF">            'ips'</span><span style="color:#E1E4E8">: IPSEvaluator(),</span></span>
<span class="line"><span style="color:#9ECBFF">            'doubly_robust'</span><span style="color:#E1E4E8">: DoublyRobustEvaluator(),</span></span>
<span class="line"><span style="color:#9ECBFF">            'replay'</span><span style="color:#E1E4E8">: ReplayEvaluator()</span></span>
<span class="line"><span style="color:#E1E4E8">        }</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> evaluate_policy</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#E1E4E8">        self,</span></span>
<span class="line"><span style="color:#E1E4E8">        policy_candidate,</span></span>
<span class="line"><span style="color:#E1E4E8">        evaluation_data: List[Tuple],</span></span>
<span class="line"><span style="color:#E1E4E8">        propensity_scores: List[</span><span style="color:#79B8FF">float</span><span style="color:#E1E4E8">]</span></span>
<span class="line"><span style="color:#E1E4E8">    ) -> Dict:</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Evaluate policy candidate on historical data.</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">        Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">            dict with estimates from multiple methods</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#E1E4E8">        results </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> {}</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        for</span><span style="color:#E1E4E8"> name, evaluator </span><span style="color:#F97583">in</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.evaluators.items():</span></span>
<span class="line"><span style="color:#E1E4E8">            estimate </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> evaluator.evaluate(</span></span>
<span class="line"><span style="color:#E1E4E8">                evaluation_data,</span></span>
<span class="line"><span style="color:#E1E4E8">                policy_candidate,</span></span>
<span class="line"><span style="color:#E1E4E8">                propensity_scores</span></span>
<span class="line"><span style="color:#E1E4E8">            )</span></span>
<span class="line"><span style="color:#E1E4E8">            results[name] </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> estimate</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        return</span><span style="color:#E1E4E8"> results</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> compare_policies</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#E1E4E8">        self,</span></span>
<span class="line"><span style="color:#E1E4E8">        policies: Dict[</span><span style="color:#79B8FF">str</span><span style="color:#E1E4E8">, Policy],</span></span>
<span class="line"><span style="color:#E1E4E8">        evaluation_data: List[Tuple],</span></span>
<span class="line"><span style="color:#E1E4E8">        propensity_scores: List[</span><span style="color:#79B8FF">float</span><span style="color:#E1E4E8">]</span></span>
<span class="line"><span style="color:#E1E4E8">    ) -> pd.DataFrame:</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Compare multiple policy candidates.</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">        Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">            DataFrame with comparison results</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#E1E4E8">        comparison </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> []</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        for</span><span style="color:#E1E4E8"> name, policy </span><span style="color:#F97583">in</span><span style="color:#E1E4E8"> policies.items():</span></span>
<span class="line"><span style="color:#E1E4E8">            results </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.evaluate_policy(</span></span>
<span class="line"><span style="color:#E1E4E8">                policy, evaluation_data, propensity_scores</span></span>
<span class="line"><span style="color:#E1E4E8">            )</span></span>
<span class="line"><span style="color:#E1E4E8">            comparison.append({</span><span style="color:#9ECBFF">'policy'</span><span style="color:#E1E4E8">: name, </span><span style="color:#F97583">**</span><span style="color:#E1E4E8">results})</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        return</span><span style="color:#E1E4E8"> pd.DataFrame(comparison)</span></span>
<span class="line"></span></code></pre>
<h4 id="6-policy-trainer">6. <strong>Policy Trainer</strong></h4>
<p><strong>Purpose:</strong> Train new policy versions on logged data</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#F97583">class</span><span style="color:#B392F0"> PolicyTrainer</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#9ECBFF">    Trains bandit policies on logged reward data.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Handles batch training, model versioning, validation.</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#79B8FF"> __init__</span><span style="color:#E1E4E8">(self, algorithm_type</span><span style="color:#F97583">=</span><span style="color:#9ECBFF">'linucb'</span><span style="color:#E1E4E8">):</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.algorithm_type </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> algorithm_type</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> train</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#E1E4E8">        self,</span></span>
<span class="line"><span style="color:#E1E4E8">        training_data: List[Tuple],</span></span>
<span class="line"><span style="color:#E1E4E8">        validation_data: List[Tuple],</span></span>
<span class="line"><span style="color:#E1E4E8">        hyperparams: Dict</span></span>
<span class="line"><span style="color:#E1E4E8">    ) -> Policy:</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Train policy on logged data.</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">        Args:</span></span>
<span class="line"><span style="color:#9ECBFF">            training_data: (context, action, reward) tuples</span></span>
<span class="line"><span style="color:#9ECBFF">            validation_data: Held-out data for validation</span></span>
<span class="line"><span style="color:#9ECBFF">            hyperparams: Algorithm hyperparameters</span></span>
<span class="line"><span style="color:#9ECBFF">            </span></span>
<span class="line"><span style="color:#9ECBFF">        Returns:</span></span>
<span class="line"><span style="color:#9ECBFF">            trained_policy: Trained policy ready for deployment</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#6A737D">        # Initialize policy</span></span>
<span class="line"><span style="color:#F97583">        if</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.algorithm_type </span><span style="color:#F97583">==</span><span style="color:#9ECBFF"> 'linucb'</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#E1E4E8">            policy </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> LinUCB(</span><span style="color:#F97583">**</span><span style="color:#E1E4E8">hyperparams)</span></span>
<span class="line"><span style="color:#F97583">        elif</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.algorithm_type </span><span style="color:#F97583">==</span><span style="color:#9ECBFF"> 'thompson'</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#E1E4E8">            policy </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> LinearThompsonSampling(</span><span style="color:#F97583">**</span><span style="color:#E1E4E8">hyperparams)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Train on batch data</span></span>
<span class="line"><span style="color:#F97583">        for</span><span style="color:#E1E4E8"> context, action, reward </span><span style="color:#F97583">in</span><span style="color:#E1E4E8"> training_data:</span></span>
<span class="line"><span style="color:#E1E4E8">            policy.update(action, context, reward)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Validate</span></span>
<span class="line"><span style="color:#E1E4E8">        val_performance </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">._validate(policy, validation_data)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Version and save</span></span>
<span class="line"><span style="color:#E1E4E8">        policy.version </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">._generate_version()</span></span>
<span class="line"><span style="color:#E1E4E8">        policy.validation_metrics </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> val_performance</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        return</span><span style="color:#E1E4E8"> policy</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> _validate</span><span style="color:#E1E4E8">(self, policy, validation_data):</span></span>
<span class="line"><span style="color:#9ECBFF">        """Compute validation metrics."""</span></span>
<span class="line"><span style="color:#6A737D">        # Replay evaluation on validation set</span></span>
<span class="line"><span style="color:#E1E4E8">        total_reward </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 0</span></span>
<span class="line"><span style="color:#E1E4E8">        matches </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 0</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        for</span><span style="color:#E1E4E8"> context, action_logged, reward </span><span style="color:#F97583">in</span><span style="color:#E1E4E8"> validation_data:</span></span>
<span class="line"><span style="color:#E1E4E8">            action_pred </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> policy.select_action(context)</span></span>
<span class="line"><span style="color:#F97583">            if</span><span style="color:#E1E4E8"> action_pred </span><span style="color:#F97583">==</span><span style="color:#E1E4E8"> action_logged:</span></span>
<span class="line"><span style="color:#E1E4E8">                total_reward </span><span style="color:#F97583">+=</span><span style="color:#E1E4E8"> reward</span></span>
<span class="line"><span style="color:#E1E4E8">                matches </span><span style="color:#F97583">+=</span><span style="color:#79B8FF"> 1</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        return</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#9ECBFF">            'avg_reward'</span><span style="color:#E1E4E8">: total_reward </span><span style="color:#F97583">/</span><span style="color:#E1E4E8"> matches </span><span style="color:#F97583">if</span><span style="color:#E1E4E8"> matches </span><span style="color:#F97583">></span><span style="color:#79B8FF"> 0</span><span style="color:#F97583"> else</span><span style="color:#79B8FF"> 0</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#9ECBFF">            'coverage'</span><span style="color:#E1E4E8">: matches </span><span style="color:#F97583">/</span><span style="color:#79B8FF"> len</span><span style="color:#E1E4E8">(validation_data)</span></span>
<span class="line"><span style="color:#E1E4E8">        }</span></span>
<span class="line"></span></code></pre>
<h4 id="7-monitor-service">7. <strong>Monitor Service</strong></h4>
<p><strong>Purpose:</strong> Track KPIs, detect anomalies, trigger alerts</p>
<p><strong>Key metrics to monitor:</strong></p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#F97583">class</span><span style="color:#B392F0"> BanditMonitor</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#9ECBFF">    Monitors bandit system health and performance.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Tracks KPIs, detects anomalies, sends alerts.</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#79B8FF"> __init__</span><span style="color:#E1E4E8">(self):</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.metrics </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#6A737D">            # Performance metrics</span></span>
<span class="line"><span style="color:#9ECBFF">            'average_reward'</span><span style="color:#E1E4E8">: RollingAverage(</span><span style="color:#FFAB70">window</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1000</span><span style="color:#E1E4E8">),</span></span>
<span class="line"><span style="color:#9ECBFF">            'action_distribution'</span><span style="color:#E1E4E8">: Counter(),</span></span>
<span class="line"><span style="color:#9ECBFF">            'regret_estimate'</span><span style="color:#E1E4E8">: RollingAverage(</span><span style="color:#FFAB70">window</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1000</span><span style="color:#E1E4E8">),</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#6A737D">            # System metrics</span></span>
<span class="line"><span style="color:#9ECBFF">            'latency_p50'</span><span style="color:#E1E4E8">: Percentile(</span><span style="color:#79B8FF">50</span><span style="color:#E1E4E8">),</span></span>
<span class="line"><span style="color:#9ECBFF">            'latency_p99'</span><span style="color:#E1E4E8">: Percentile(</span><span style="color:#79B8FF">99</span><span style="color:#E1E4E8">),</span></span>
<span class="line"><span style="color:#9ECBFF">            'error_rate'</span><span style="color:#E1E4E8">: RollingAverage(</span><span style="color:#FFAB70">window</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1000</span><span style="color:#E1E4E8">),</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#6A737D">            # Exploration metrics</span></span>
<span class="line"><span style="color:#9ECBFF">            'exploration_rate'</span><span style="color:#E1E4E8">: RollingAverage(</span><span style="color:#FFAB70">window</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1000</span><span style="color:#E1E4E8">),</span></span>
<span class="line"><span style="color:#9ECBFF">            'novel_contexts'</span><span style="color:#E1E4E8">: Counter()</span></span>
<span class="line"><span style="color:#E1E4E8">        }</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> log_decision</span><span style="color:#E1E4E8">(self, decision_event: Dict):</span></span>
<span class="line"><span style="color:#9ECBFF">        """Log metrics from a decision event."""</span></span>
<span class="line"><span style="color:#6A737D">        # Update performance metrics</span></span>
<span class="line"><span style="color:#F97583">        if</span><span style="color:#9ECBFF"> 'reward'</span><span style="color:#F97583"> in</span><span style="color:#E1E4E8"> decision_event </span><span style="color:#F97583">and</span><span style="color:#E1E4E8"> decision_event[</span><span style="color:#9ECBFF">'reward'</span><span style="color:#E1E4E8">] </span><span style="color:#F97583">is</span><span style="color:#F97583"> not</span><span style="color:#79B8FF"> None</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#79B8FF">            self</span><span style="color:#E1E4E8">.metrics[</span><span style="color:#9ECBFF">'average_reward'</span><span style="color:#E1E4E8">].update(decision_event[</span><span style="color:#9ECBFF">'reward'</span><span style="color:#E1E4E8">])</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Track action distribution</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.metrics[</span><span style="color:#9ECBFF">'action_distribution'</span><span style="color:#E1E4E8">][decision_event[</span><span style="color:#9ECBFF">'action'</span><span style="color:#E1E4E8">]] </span><span style="color:#F97583">+=</span><span style="color:#79B8FF"> 1</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Track latency</span></span>
<span class="line"><span style="color:#F97583">        if</span><span style="color:#9ECBFF"> 'latency_ms'</span><span style="color:#F97583"> in</span><span style="color:#E1E4E8"> decision_event:</span></span>
<span class="line"><span style="color:#79B8FF">            self</span><span style="color:#E1E4E8">.metrics[</span><span style="color:#9ECBFF">'latency_p99'</span><span style="color:#E1E4E8">].update(decision_event[</span><span style="color:#9ECBFF">'latency_ms'</span><span style="color:#E1E4E8">])</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> check_alerts</span><span style="color:#E1E4E8">(self) -> List[Alert]:</span></span>
<span class="line"><span style="color:#9ECBFF">        """Check for alert conditions."""</span></span>
<span class="line"><span style="color:#E1E4E8">        alerts </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> []</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Alert: Reward drop</span></span>
<span class="line"><span style="color:#F97583">        if</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.metrics[</span><span style="color:#9ECBFF">'average_reward'</span><span style="color:#E1E4E8">].value </span><span style="color:#F97583">&#x3C;</span><span style="color:#79B8FF"> 0.5</span><span style="color:#F97583"> *</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.baseline_reward:</span></span>
<span class="line"><span style="color:#E1E4E8">            alerts.append(Alert(</span></span>
<span class="line"><span style="color:#FFAB70">                severity</span><span style="color:#F97583">=</span><span style="color:#9ECBFF">'high'</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#FFAB70">                message</span><span style="color:#F97583">=</span><span style="color:#9ECBFF">'Average reward dropped >50</span><span style="color:#79B8FF">% b</span><span style="color:#9ECBFF">elow baseline'</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#FFAB70">                metric</span><span style="color:#F97583">=</span><span style="color:#9ECBFF">'average_reward'</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#FFAB70">                value</span><span style="color:#F97583">=</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.metrics[</span><span style="color:#9ECBFF">'average_reward'</span><span style="color:#E1E4E8">].value</span></span>
<span class="line"><span style="color:#E1E4E8">            ))</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Alert: Action imbalance</span></span>
<span class="line"><span style="color:#E1E4E8">        action_dist </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.metrics[</span><span style="color:#9ECBFF">'action_distribution'</span><span style="color:#E1E4E8">]</span></span>
<span class="line"><span style="color:#F97583">        if</span><span style="color:#79B8FF"> max</span><span style="color:#E1E4E8">(action_dist.values()) </span><span style="color:#F97583">></span><span style="color:#79B8FF"> 0.9</span><span style="color:#F97583"> *</span><span style="color:#79B8FF"> sum</span><span style="color:#E1E4E8">(action_dist.values()):</span></span>
<span class="line"><span style="color:#E1E4E8">            alerts.append(Alert(</span></span>
<span class="line"><span style="color:#FFAB70">                severity</span><span style="color:#F97583">=</span><span style="color:#9ECBFF">'medium'</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#FFAB70">                message</span><span style="color:#F97583">=</span><span style="color:#9ECBFF">'Single action getting >90</span><span style="color:#79B8FF">% o</span><span style="color:#9ECBFF">f traffic'</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#FFAB70">                metric</span><span style="color:#F97583">=</span><span style="color:#9ECBFF">'action_distribution'</span></span>
<span class="line"><span style="color:#E1E4E8">            ))</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Alert: High latency</span></span>
<span class="line"><span style="color:#F97583">        if</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.metrics[</span><span style="color:#9ECBFF">'latency_p99'</span><span style="color:#E1E4E8">].value </span><span style="color:#F97583">></span><span style="color:#79B8FF"> 200</span><span style="color:#E1E4E8">:  </span><span style="color:#6A737D"># ms</span></span>
<span class="line"><span style="color:#E1E4E8">            alerts.append(Alert(</span></span>
<span class="line"><span style="color:#FFAB70">                severity</span><span style="color:#F97583">=</span><span style="color:#9ECBFF">'medium'</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#FFAB70">                message</span><span style="color:#F97583">=</span><span style="color:#9ECBFF">'P99 latency >200ms'</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#FFAB70">                metric</span><span style="color:#F97583">=</span><span style="color:#9ECBFF">'latency_p99'</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#FFAB70">                value</span><span style="color:#F97583">=</span><span style="color:#79B8FF">self</span><span style="color:#E1E4E8">.metrics[</span><span style="color:#9ECBFF">'latency_p99'</span><span style="color:#E1E4E8">].value</span></span>
<span class="line"><span style="color:#E1E4E8">            ))</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#F97583">        return</span><span style="color:#E1E4E8"> alerts</span></span>
<span class="line"></span></code></pre>
<p><strong>Dashboard KPIs:</strong></p>


















































<table><thead><tr><th>Category</th><th>Metric</th><th>Alert Threshold</th></tr></thead><tbody><tr><td><strong>Performance</strong></td><td>Average reward</td><td>&#x3C;80% of baseline</td></tr><tr><td></td><td>Estimated regret</td><td>Growing linearly</td></tr><tr><td></td><td>Policy value (offline)</td><td>Decreasing</td></tr><tr><td><strong>Exploration</strong></td><td>Action entropy</td><td>&#x3C;0.5 (too concentrated)</td></tr><tr><td></td><td>Exploration rate</td><td>&#x3C;5% (under-exploring)</td></tr><tr><td><strong>System</strong></td><td>P99 latency</td><td>>200ms</td></tr><tr><td></td><td>Error rate</td><td>>1%</td></tr><tr><td></td><td>Cache hit rate</td><td>&#x3C;95%</td></tr></tbody></table>
<h4 id="8-safety-layer">8. <strong>Safety Layer</strong></h4>
<p><strong>Purpose:</strong> Prevent disasters, provide fallbacks</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#F97583">class</span><span style="color:#B392F0"> SafetyLayer</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#9ECBFF">    Safety mechanisms for production bandit systems.</span></span>
<span class="line"><span style="color:#9ECBFF">    </span></span>
<span class="line"><span style="color:#9ECBFF">    Implements circuit breakers, rate limits, fallback policies.</span></span>
<span class="line"><span style="color:#9ECBFF">    """</span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#79B8FF"> __init__</span><span style="color:#E1E4E8">(self, fallback_policy):</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.fallback_policy </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> fallback_policy</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.circuit_breaker </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> CircuitBreaker(</span></span>
<span class="line"><span style="color:#FFAB70">            failure_threshold</span><span style="color:#F97583">=</span><span style="color:#79B8FF">10</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#FFAB70">            timeout</span><span style="color:#F97583">=</span><span style="color:#79B8FF">60</span><span style="color:#6A737D">  # seconds</span></span>
<span class="line"><span style="color:#E1E4E8">        )</span></span>
<span class="line"><span style="color:#79B8FF">        self</span><span style="color:#E1E4E8">.rate_limiter </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> RateLimiter(</span><span style="color:#FFAB70">max_requests_per_second</span><span style="color:#F97583">=</span><span style="color:#79B8FF">1000</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> safe_select_action</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#E1E4E8">        self,</span></span>
<span class="line"><span style="color:#E1E4E8">        policy,</span></span>
<span class="line"><span style="color:#E1E4E8">        context: np.ndarray,</span></span>
<span class="line"><span style="color:#E1E4E8">        eligible_actions: List[</span><span style="color:#79B8FF">int</span><span style="color:#E1E4E8">]</span></span>
<span class="line"><span style="color:#E1E4E8">    ) -> Dict:</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#9ECBFF">        Select action with safety checks.</span></span>
<span class="line"><span style="color:#9ECBFF">        </span></span>
<span class="line"><span style="color:#9ECBFF">        Falls back to safe policy if main policy fails.</span></span>
<span class="line"><span style="color:#9ECBFF">        """</span></span>
<span class="line"><span style="color:#F97583">        try</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#6A737D">            # Check rate limit</span></span>
<span class="line"><span style="color:#F97583">            if</span><span style="color:#F97583"> not</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.rate_limiter.allow_request():</span></span>
<span class="line"><span style="color:#F97583">                raise</span><span style="color:#E1E4E8"> RateLimitExceeded(</span><span style="color:#9ECBFF">"Too many requests"</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#6A737D">            # Check circuit breaker</span></span>
<span class="line"><span style="color:#F97583">            if</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.circuit_breaker.is_open():</span></span>
<span class="line"><span style="color:#F97583">                raise</span><span style="color:#E1E4E8"> CircuitBreakerOpen(</span><span style="color:#9ECBFF">"Policy service unavailable"</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#6A737D">            # Try main policy</span></span>
<span class="line"><span style="color:#E1E4E8">            result </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> policy.select_action(context, eligible_actions)</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#6A737D">            # Sanity checks</span></span>
<span class="line"><span style="color:#79B8FF">            self</span><span style="color:#E1E4E8">._validate_result(result, eligible_actions)</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#79B8FF">            self</span><span style="color:#E1E4E8">.circuit_breaker.record_success()</span></span>
<span class="line"><span style="color:#F97583">            return</span><span style="color:#E1E4E8"> result</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#F97583">        except</span><span style="color:#79B8FF"> Exception</span><span style="color:#F97583"> as</span><span style="color:#E1E4E8"> e:</span></span>
<span class="line"><span style="color:#6A737D">            # Record failure</span></span>
<span class="line"><span style="color:#79B8FF">            self</span><span style="color:#E1E4E8">.circuit_breaker.record_failure()</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#6A737D">            # Log error</span></span>
<span class="line"><span style="color:#E1E4E8">            logger.error(</span><span style="color:#F97583">f</span><span style="color:#9ECBFF">"Policy selection failed: </span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">e</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">"</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">            </span></span>
<span class="line"><span style="color:#6A737D">            # Fallback to safe policy</span></span>
<span class="line"><span style="color:#F97583">            return</span><span style="color:#79B8FF"> self</span><span style="color:#E1E4E8">.fallback_policy.select_action(context, eligible_actions)</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#F97583">    def</span><span style="color:#B392F0"> _validate_result</span><span style="color:#E1E4E8">(self, result: Dict, eligible_actions: List[</span><span style="color:#79B8FF">int</span><span style="color:#E1E4E8">]):</span></span>
<span class="line"><span style="color:#9ECBFF">        """Sanity check policy output."""</span></span>
<span class="line"><span style="color:#6A737D">        # Check action is eligible</span></span>
<span class="line"><span style="color:#F97583">        if</span><span style="color:#E1E4E8"> result[</span><span style="color:#9ECBFF">'action'</span><span style="color:#E1E4E8">] </span><span style="color:#F97583">not</span><span style="color:#F97583"> in</span><span style="color:#E1E4E8"> eligible_actions:</span></span>
<span class="line"><span style="color:#F97583">            raise</span><span style="color:#79B8FF"> ValueError</span><span style="color:#E1E4E8">(</span><span style="color:#F97583">f</span><span style="color:#9ECBFF">"Invalid action </span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">result[</span><span style="color:#9ECBFF">'action'</span><span style="color:#E1E4E8">]</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">"</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Check scores are reasonable</span></span>
<span class="line"><span style="color:#F97583">        if</span><span style="color:#79B8FF"> any</span><span style="color:#E1E4E8">(score </span><span style="color:#F97583">></span><span style="color:#79B8FF"> 100</span><span style="color:#F97583"> or</span><span style="color:#E1E4E8"> score </span><span style="color:#F97583">&#x3C;</span><span style="color:#F97583"> -</span><span style="color:#79B8FF">100</span><span style="color:#F97583"> for</span><span style="color:#E1E4E8"> score </span><span style="color:#F97583">in</span><span style="color:#E1E4E8"> result[</span><span style="color:#9ECBFF">'scores'</span><span style="color:#E1E4E8">].values()):</span></span>
<span class="line"><span style="color:#F97583">            raise</span><span style="color:#79B8FF"> ValueError</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">"Unreasonable action scores"</span><span style="color:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E1E4E8">        </span></span>
<span class="line"><span style="color:#6A737D">        # Check propensity</span></span>
<span class="line"><span style="color:#F97583">        if</span><span style="color:#F97583"> not</span><span style="color:#E1E4E8"> (</span><span style="color:#79B8FF">0</span><span style="color:#F97583"> &#x3C;=</span><span style="color:#E1E4E8"> result[</span><span style="color:#9ECBFF">'propensity'</span><span style="color:#E1E4E8">] </span><span style="color:#F97583">&#x3C;=</span><span style="color:#79B8FF"> 1</span><span style="color:#E1E4E8">):</span></span>
<span class="line"><span style="color:#F97583">            raise</span><span style="color:#79B8FF"> ValueError</span><span style="color:#E1E4E8">(</span><span style="color:#F97583">f</span><span style="color:#9ECBFF">"Invalid propensity </span><span style="color:#79B8FF">{</span><span style="color:#E1E4E8">result[</span><span style="color:#9ECBFF">'propensity'</span><span style="color:#E1E4E8">]</span><span style="color:#79B8FF">}</span><span style="color:#9ECBFF">"</span><span style="color:#E1E4E8">)</span></span>
<span class="line"></span></code></pre>
<hr>
<h2 id="monitoring-dashboard-specification">Monitoring Dashboard Specification</h2>
<h3 id="overview-dashboard">Overview Dashboard</h3>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>┌─────────────────────────────────────────────────────────────────┐</span></span>
<span class="line"><span>│ Contextual Bandit System - Production Dashboard                 │</span></span>
<span class="line"><span>├─────────────────────────────────────────────────────────────────┤</span></span>
<span class="line"><span>│                                                                   │</span></span>
<span class="line"><span>│  📊 PERFORMANCE METRICS                                          │</span></span>
<span class="line"><span>│  ┌─────────────────┬─────────────────┬─────────────────┐        │</span></span>
<span class="line"><span>│  │ Avg Reward      │ Estimated Regret│ Policy Value    │        │</span></span>
<span class="line"><span>│  │ 0.732 ▲ +2.1%   │ 142.3 ▼ -5.2%  │ 0.745 ▲ +1.8%   │        │</span></span>
<span class="line"><span>│  │ (vs baseline)   │ (cumulative)    │ (offline eval)  │        │</span></span>
<span class="line"><span>│  └─────────────────┴─────────────────┴─────────────────┘        │</span></span>
<span class="line"><span>│                                                                   │</span></span>
<span class="line"><span>│  🎯 EXPLORATION METRICS                                          │</span></span>
<span class="line"><span>│  ┌─────────────────┬─────────────────┬─────────────────┐        │</span></span>
<span class="line"><span>│  │ Action Entropy  │ Exploration %   │ Novel Contexts  │        │</span></span>
<span class="line"><span>│  │ 1.83 / 2.32     │ 12.4%           │ 347 / hour      │        │</span></span>
<span class="line"><span>│  │ (healthy)       │ (good)          │ (normal)        │        │</span></span>
<span class="line"><span>│  └─────────────────┴─────────────────┴─────────────────┘        │</span></span>
<span class="line"><span>│                                                                   │</span></span>
<span class="line"><span>│  ⚙️  SYSTEM HEALTH                                               │</span></span>
<span class="line"><span>│  ┌─────────────────┬─────────────────┬─────────────────┐        │</span></span>
<span class="line"><span>│  │ P99 Latency     │ Error Rate      │ Cache Hit Rate  │        │</span></span>
<span class="line"><span>│  │ 87ms ✓          │ 0.12% ✓         │ 98.3% ✓         │        │</span></span>
<span class="line"><span>│  │ (&#x3C;200ms)        │ (&#x3C;1%)           │ (>95%)          │        │</span></span>
<span class="line"><span>│  └─────────────────┴─────────────────┴─────────────────┘        │</span></span>
<span class="line"><span>│                                                                   │</span></span>
<span class="line"><span>│  📈 REWARD OVER TIME (Last 24h)                                  │</span></span>
<span class="line"><span>│  ┌──────────────────────────────────────────────────────────┐   │</span></span>
<span class="line"><span>│  │ 0.8┤                                          ╭──╮          │   │</span></span>
<span class="line"><span>│  │ 0.7┤                  ╭─╮              ╭────╯  ╰─╮        │   │</span></span>
<span class="line"><span>│  │ 0.6┤         ╭───────╯ ╰──────────────╯          ╰─╮      │   │</span></span>
<span class="line"><span>│  │ 0.5┤  ╭─────╯                                       ╰────  │   │</span></span>
<span class="line"><span>│  │    └────────────────────────────────────────────────────  │   │</span></span>
<span class="line"><span>│  │     00:00    06:00    12:00    18:00    24:00            │   │</span></span>
<span class="line"><span>│  └──────────────────────────────────────────────────────────┘   │</span></span>
<span class="line"><span>│                                                                   │</span></span>
<span class="line"><span>│  🎬 ACTION DISTRIBUTION (Last 1000 decisions)                    │</span></span>
<span class="line"><span>│  ┌──────────────────────────────────────────────────────────┐   │</span></span>
<span class="line"><span>│  │ Action 1: ████████████████████░░░░░░░░░░ 45.2%            │   │</span></span>
<span class="line"><span>│  │ Action 2: ████████████░░░░░░░░░░░░░░░░░░ 28.7%            │   │</span></span>
<span class="line"><span>│  │ Action 3: ██████░░░░░░░░░░░░░░░░░░░░░░░░ 15.3%            │   │</span></span>
<span class="line"><span>│  │ Action 4: ████░░░░░░░░░░░░░░░░░░░░░░░░░░ 8.1%             │   │</span></span>
<span class="line"><span>│  │ Action 5: ██░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 2.7%             │   │</span></span>
<span class="line"><span>│  └──────────────────────────────────────────────────────────┘   │</span></span>
<span class="line"><span>│                                                                   │</span></span>
<span class="line"><span>│  ⚠️  ACTIVE ALERTS: 0                                            │</span></span>
<span class="line"><span>│                                                                   │</span></span>
<span class="line"><span>└─────────────────────────────────────────────────────────────────┘</span></span>
<span class="line"><span></span></span></code></pre>
<h3 id="alert-configuration">Alert Configuration</h3>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="yaml"><code><span class="line"><span style="color:#85E89D">alerts</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#E1E4E8">  - </span><span style="color:#85E89D">name</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">reward_drop</span></span>
<span class="line"><span style="color:#85E89D">    condition</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">avg_reward &#x3C; 0.8 * baseline_reward</span></span>
<span class="line"><span style="color:#85E89D">    severity</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">high</span></span>
<span class="line"><span style="color:#85E89D">    notification</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">pagerduty</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#E1E4E8">  - </span><span style="color:#85E89D">name</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">action_concentration</span></span>
<span class="line"><span style="color:#85E89D">    condition</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">max(action_dist) / sum(action_dist) > 0.9</span></span>
<span class="line"><span style="color:#85E89D">    severity</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">medium</span></span>
<span class="line"><span style="color:#85E89D">    notification</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">slack</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#E1E4E8">  - </span><span style="color:#85E89D">name</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">exploration_too_low</span></span>
<span class="line"><span style="color:#85E89D">    condition</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">exploration_rate &#x3C; 0.05</span></span>
<span class="line"><span style="color:#85E89D">    severity</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">medium</span></span>
<span class="line"><span style="color:#85E89D">    notification</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">slack</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#E1E4E8">  - </span><span style="color:#85E89D">name</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">high_latency</span></span>
<span class="line"><span style="color:#85E89D">    condition</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">p99_latency > 200</span></span>
<span class="line"><span style="color:#85E89D">    severity</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">medium</span></span>
<span class="line"><span style="color:#85E89D">    notification</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">slack</span></span>
<span class="line"><span style="color:#E1E4E8">    </span></span>
<span class="line"><span style="color:#E1E4E8">  - </span><span style="color:#85E89D">name</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">high_error_rate</span></span>
<span class="line"><span style="color:#85E89D">    condition</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">error_rate > 0.01</span></span>
<span class="line"><span style="color:#85E89D">    severity</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">high</span></span>
<span class="line"><span style="color:#85E89D">    notification</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">pagerduty</span></span>
<span class="line"></span></code></pre>
<hr>
<h2 id="deployment-workflow-research-to-production">Deployment Workflow: Research to Production</h2>
<h3 id="phase-1-research--development-week-1-2">Phase 1: Research &#x26; Development (Week 1-2)</h3>
<p><strong>Objectives:</strong></p>
<ul>
<li>Choose algorithm (LinUCB, Thompson Sampling, etc.)</li>
<li>Implement and test locally</li>
<li>Validate on simulated data</li>
</ul>
<p><strong>Checklist:</strong></p>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" disabled> Algorithm implemented with tests</li>
<li class="task-list-item"><input type="checkbox" disabled> Regret curves show sublinear growth</li>
<li class="task-list-item"><input type="checkbox" disabled> Hyperparameters tuned</li>
<li class="task-list-item"><input type="checkbox" disabled> Code reviewed</li>
</ul>
<h3 id="phase-2-offline-evaluation-week-3-4">Phase 2: Offline Evaluation (Week 3-4)</h3>
<p><strong>Objectives:</strong></p>
<ul>
<li>Validate on historical production data</li>
<li>Compare to baseline policy</li>
<li>Estimate expected lift</li>
</ul>
<p><strong>Checklist:</strong></p>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" disabled> Historical data prepared (context, actions, rewards, propensities)</li>
<li class="task-list-item"><input type="checkbox" disabled> Offline evaluation implemented (IPS, Doubly Robust)</li>
<li class="task-list-item"><input type="checkbox" disabled> New policy shows improvement over baseline</li>
<li class="task-list-item"><input type="checkbox" disabled> Confidence intervals computed</li>
<li class="task-list-item"><input type="checkbox" disabled> Results reviewed by stakeholders</li>
</ul>
<p><strong>Example results to present:</strong></p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>Offline Evaluation Results:</span></span>
<span class="line"><span>━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Baseline Policy (Production):</span></span>
<span class="line"><span>  Average reward: 0.650</span></span>
<span class="line"><span>  </span></span>
<span class="line"><span>New Policy (LinUCB):</span></span>
<span class="line"><span>  Doubly Robust estimate: 0.732 ± 0.018</span></span>
<span class="line"><span>  95% CI: [0.714, 0.750]</span></span>
<span class="line"><span>  Estimated lift: +12.6%</span></span>
<span class="line"><span>  </span></span>
<span class="line"><span>Statistical significance: p &#x3C; 0.001</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Recommendation: Proceed to staging deployment</span></span>
<span class="line"><span></span></span></code></pre>
<h3 id="phase-3-staging-deployment-week-5">Phase 3: Staging Deployment (Week 5)</h3>
<p><strong>Objectives:</strong></p>
<ul>
<li>Deploy to staging environment</li>
<li>Validate end-to-end system</li>
<li>Test logging, monitoring, safety mechanisms</li>
</ul>
<p><strong>Checklist:</strong></p>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" disabled> Policy service deployed to staging</li>
<li class="task-list-item"><input type="checkbox" disabled> Logging pipeline configured</li>
<li class="task-list-item"><input type="checkbox" disabled> Monitoring dashboard created</li>
<li class="task-list-item"><input type="checkbox" disabled> Safety mechanisms tested (circuit breaker, fallback)</li>
<li class="task-list-item"><input type="checkbox" disabled> Load testing passed (latency &#x3C;100ms at 1000 RPS)</li>
<li class="task-list-item"><input type="checkbox" disabled> Error handling tested</li>
<li class="task-list-item"><input type="checkbox" disabled> Staging traffic validated (1 week of data)</li>
</ul>
<h3 id="phase-4-shadow-mode-week-6-7">Phase 4: Shadow Mode (Week 6-7)</h3>
<p><strong>Objectives:</strong></p>
<ul>
<li>Run new policy in parallel with production</li>
<li>Log what decisions new policy would make</li>
<li>No user impact (don’t show new policy’s actions)</li>
</ul>
<p><strong>Checklist:</strong></p>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" disabled> Shadow logging enabled</li>
<li class="task-list-item"><input type="checkbox" disabled> Compare decisions: new policy vs production</li>
<li class="task-list-item"><input type="checkbox" disabled> Analyze disagreement (where do policies differ?)</li>
<li class="task-list-item"><input type="checkbox" disabled> Offline eval on shadow data confirms expected performance</li>
<li class="task-list-item"><input type="checkbox" disabled> No bugs or anomalies detected</li>
</ul>
<h3 id="phase-5-canary-deployment-week-8-9">Phase 5: Canary Deployment (Week 8-9)</h3>
<p><strong>Objectives:</strong></p>
<ul>
<li>Route 5% of traffic to new policy</li>
<li>Monitor closely for issues</li>
<li>Gradually increase if successful</li>
</ul>
<p><strong>Canary schedule:</strong></p>
<ul>
<li>Week 8: 5% traffic</li>
<li>Week 9: 10% → 20% → 50%</li>
</ul>
<p><strong>Checklist:</strong></p>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" disabled> 5% traffic routed to new policy</li>
<li class="task-list-item"><input type="checkbox" disabled> Monitoring dashboard shows healthy metrics</li>
<li class="task-list-item"><input type="checkbox" disabled> No increase in errors or latency</li>
<li class="task-list-item"><input type="checkbox" disabled> Reward metrics stable or improving</li>
<li class="task-list-item"><input type="checkbox" disabled> Stakeholder approval to increase traffic</li>
<li class="task-list-item"><input type="checkbox" disabled> Gradual increase to 50%</li>
</ul>
<h3 id="phase-6-full-rollout-week-10">Phase 6: Full Rollout (Week 10)</h3>
<p><strong>Objectives:</strong></p>
<ul>
<li>Route 100% of traffic</li>
<li>Monitor for 1 week</li>
<li>Declare production-ready</li>
</ul>
<p><strong>Checklist:</strong></p>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" disabled> 100% traffic on new policy</li>
<li class="task-list-item"><input type="checkbox" disabled> All metrics stable</li>
<li class="task-list-item"><input type="checkbox" disabled> Offline evaluation confirms expected lift</li>
<li class="task-list-item"><input type="checkbox" disabled> Documentation updated</li>
<li class="task-list-item"><input type="checkbox" disabled> Runbook created for on-call</li>
<li class="task-list-item"><input type="checkbox" disabled> Rollback plan documented</li>
</ul>
<h3 id="phase-7-continuous-improvement-ongoing">Phase 7: Continuous Improvement (Ongoing)</h3>
<p><strong>Objectives:</strong></p>
<ul>
<li>Retrain periodically</li>
<li>Monitor for distribution drift</li>
<li>Iterate on features and hyperparameters</li>
</ul>
<p><strong>Ongoing tasks:</strong></p>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" disabled> Weekly offline evaluation</li>
<li class="task-list-item"><input type="checkbox" disabled> Monthly policy retraining</li>
<li class="task-list-item"><input type="checkbox" disabled> Quarterly hyperparameter tuning</li>
<li class="task-list-item"><input type="checkbox" disabled> Feature engineering experiments</li>
</ul>
<hr>
<h2 id="pre-deployment-checklist">Pre-Deployment Checklist</h2>
<h3 id="comprehensive-56-point-checklist">Comprehensive 56-Point Checklist</h3>
<h4 id="algorithm--model-8-items"><strong>Algorithm &#x26; Model (8 items)</strong></h4>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" disabled> Algorithm selected with justification</li>
<li class="task-list-item"><input type="checkbox" disabled> Hyperparameters tuned on offline data</li>
<li class="task-list-item"><input type="checkbox" disabled> Regret bounds validated (sublinear growth)</li>
<li class="task-list-item"><input type="checkbox" disabled> Code reviewed by at least 2 engineers</li>
<li class="task-list-item"><input type="checkbox" disabled> Unit tests pass (>90% coverage)</li>
<li class="task-list-item"><input type="checkbox" disabled> Integration tests pass</li>
<li class="task-list-item"><input type="checkbox" disabled> Model serialization/deserialization tested</li>
<li class="task-list-item"><input type="checkbox" disabled> Model versioning implemented</li>
</ul>
<h4 id="offline-evaluation-8-items"><strong>Offline Evaluation (8 items)</strong></h4>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" disabled> Historical data collected (min 10k decisions)</li>
<li class="task-list-item"><input type="checkbox" disabled> Propensity scores logged or estimated</li>
<li class="task-list-item"><input type="checkbox" disabled> IPS evaluation implemented</li>
<li class="task-list-item"><input type="checkbox" disabled> Doubly Robust evaluation implemented</li>
<li class="task-list-item"><input type="checkbox" disabled> Confidence intervals computed</li>
<li class="task-list-item"><input type="checkbox" disabled> Statistical significance test passed (p &#x3C; 0.05)</li>
<li class="task-list-item"><input type="checkbox" disabled> Comparison to baseline documented</li>
<li class="task-list-item"><input type="checkbox" disabled> Results presented to stakeholders</li>
</ul>
<h4 id="logging-infrastructure-8-items"><strong>Logging Infrastructure (8 items)</strong></h4>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" disabled> Event logger implemented (Kafka/Kinesis)</li>
<li class="task-list-item"><input type="checkbox" disabled> All required fields logged (context, action, propensity)</li>
<li class="task-list-item"><input type="checkbox" disabled> Reward collector implemented</li>
<li class="task-list-item"><input type="checkbox" disabled> Delayed reward handling tested</li>
<li class="task-list-item"><input type="checkbox" disabled> Log archival to S3/BigQuery configured</li>
<li class="task-list-item"><input type="checkbox" disabled> Log retention policy defined</li>
<li class="task-list-item"><input type="checkbox" disabled> PII/sensitive data handling reviewed</li>
<li class="task-list-item"><input type="checkbox" disabled> Logging performance tested (no bottlenecks)</li>
</ul>
<h4 id="serving-infrastructure-8-items"><strong>Serving Infrastructure (8 items)</strong></h4>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" disabled> Policy service deployed</li>
<li class="task-list-item"><input type="checkbox" disabled> Context builder tested</li>
<li class="task-list-item"><input type="checkbox" disabled> Action filtering logic validated</li>
<li class="task-list-item"><input type="checkbox" disabled> Cache layer configured (Redis)</li>
<li class="task-list-item"><input type="checkbox" disabled> Load balancing configured</li>
<li class="task-list-item"><input type="checkbox" disabled> Latency SLA met (P99 &#x3C;200ms)</li>
<li class="task-list-item"><input type="checkbox" disabled> Error handling robust</li>
<li class="task-list-item"><input type="checkbox" disabled> Circuit breaker implemented</li>
</ul>
<h4 id="monitoring--alerting-8-items"><strong>Monitoring &#x26; Alerting (8 items)</strong></h4>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" disabled> Dashboard created (Grafana/DataDog)</li>
<li class="task-list-item"><input type="checkbox" disabled> Key metrics tracked (reward, latency, errors)</li>
<li class="task-list-item"><input type="checkbox" disabled> Alert rules configured</li>
<li class="task-list-item"><input type="checkbox" disabled> Alert routing to PagerDuty/Slack</li>
<li class="task-list-item"><input type="checkbox" disabled> On-call runbook created</li>
<li class="task-list-item"><input type="checkbox" disabled> Log aggregation (ELK/Splunk)</li>
<li class="task-list-item"><input type="checkbox" disabled> Anomaly detection configured</li>
<li class="task-list-item"><input type="checkbox" disabled> Weekly review process defined</li>
</ul>
<h4 id="safety--reliability-8-items"><strong>Safety &#x26; Reliability (8 items)</strong></h4>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" disabled> Fallback policy implemented</li>
<li class="task-list-item"><input type="checkbox" disabled> Circuit breaker tested</li>
<li class="task-list-item"><input type="checkbox" disabled> Rate limiting configured</li>
<li class="task-list-item"><input type="checkbox" disabled> Graceful degradation tested</li>
<li class="task-list-item"><input type="checkbox" disabled> Rollback plan documented</li>
<li class="task-list-item"><input type="checkbox" disabled> Disaster recovery plan</li>
<li class="task-list-item"><input type="checkbox" disabled> Security review passed</li>
<li class="task-list-item"><input type="checkbox" disabled> Compliance review passed (GDPR, etc.)</li>
</ul>
<h4 id="deployment-process-8-items"><strong>Deployment Process (8 items)</strong></h4>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" disabled> Staging environment validated</li>
<li class="task-list-item"><input type="checkbox" disabled> Shadow mode run (1+ week)</li>
<li class="task-list-item"><input type="checkbox" disabled> Canary deployment plan defined</li>
<li class="task-list-item"><input type="checkbox" disabled> Gradual rollout schedule</li>
<li class="task-list-item"><input type="checkbox" disabled> A/B test designed (if needed)</li>
<li class="task-list-item"><input type="checkbox" disabled> Rollback criteria defined</li>
<li class="task-list-item"><input type="checkbox" disabled> Stakeholder sign-off obtained</li>
<li class="task-list-item"><input type="checkbox" disabled> Go-live date scheduled</li>
</ul>
<hr>
<h2 id="common-pitfalls--solutions">Common Pitfalls &#x26; Solutions</h2>
<h3 id="pitfall-1-insufficient-logging">Pitfall 1: Insufficient Logging</h3>
<p><strong>Problem:</strong> Missing propensity scores or context features makes offline evaluation impossible.</p>
<p><strong>Solution:</strong></p>
<ul>
<li>Log EVERYTHING: full context vector, all eligible actions, propensity for chosen action</li>
<li>Include metadata: model version, hyperparameters, timestamp</li>
<li>Test logging before deployment</li>
</ul>
<h3 id="pitfall-2-ignoring-delayed-rewards">Pitfall 2: Ignoring Delayed Rewards</h3>
<p><strong>Problem:</strong> Purchase happens hours after click. Naive logging misses the connection.</p>
<p><strong>Solution:</strong></p>
<ul>
<li>Implement reward collector with attribution window (e.g., 24 hours)</li>
<li>Log decision_id with user_id to enable matching</li>
<li>Use last-click or probabilistic attribution</li>
</ul>
<h3 id="pitfall-3-not-monitoring-exploration">Pitfall 3: Not Monitoring Exploration</h3>
<p><strong>Problem:</strong> Policy converges to always choosing one action (stopped exploring).</p>
<p><strong>Solution:</strong></p>
<ul>
<li>Track action entropy: H = -Σ p(a) log p(a)</li>
<li>Alert if entropy drops below threshold</li>
<li>Ensure exploration parameter (ε, α) is properly tuned</li>
</ul>
<h3 id="pitfall-4-deploying-without-offline-validation">Pitfall 4: Deploying Without Offline Validation</h3>
<p><strong>Problem:</strong> “It worked in simulation!” (then fails in production)</p>
<p><strong>Solution:</strong></p>
<ul>
<li>ALWAYS run offline evaluation on real historical data</li>
<li>Require statistical significance (p &#x3C; 0.05, CI doesn’t include baseline)</li>
<li>Get stakeholder approval before deploying</li>
</ul>
<h3 id="pitfall-5-inadequate-safety-mechanisms">Pitfall 5: Inadequate Safety Mechanisms</h3>
<p><strong>Problem:</strong> Policy fails, routes 100% of traffic to broken action.</p>
<p><strong>Solution:</strong></p>
<ul>
<li>Implement circuit breaker (fallback after N failures)</li>
<li>Rate limits prevent runaway behavior</li>
<li>Gradual rollout (canary deployment)</li>
</ul>
<h3 id="pitfall-6-forgetting-about-distribution-drift">Pitfall 6: Forgetting About Distribution Drift</h3>
<p><strong>Problem:</strong> Policy trained on November data, deployed in December (holiday shopping behavior differs).</p>
<p><strong>Solution:</strong></p>
<ul>
<li>Monitor for distribution drift (compare current vs training contexts)</li>
<li>Retrain periodically (weekly or monthly)</li>
<li>Use constant small exploration (ε = 0.05) to adapt</li>
</ul>
<h3 id="pitfall-7-over-optimizing-exploration">Pitfall 7: Over-Optimizing Exploration</h3>
<p><strong>Problem:</strong> Spending weeks tuning α from 1.0 to 0.95 for 0.1% lift.</p>
<p><strong>Solution:</strong></p>
<ul>
<li>Defaults work well: α = 1.0, λ = 1.0, ε = 0.1</li>
<li>Focus on feature engineering, not hyperparameters</li>
<li>Offline eval tells you if it matters</li>
</ul>
<hr>
<h2 id="key-takeaways">Key Takeaways</h2>
<p><strong>Essential concepts:</strong></p>
<p><strong>Offline evaluation is mandatory before production</strong></p>
<ul>
<li>IPS (unbiased, high variance)</li>
<li>Doubly Robust (best of both worlds, recommended)</li>
<li>Validate on historical data with proper propensity scores</li>
</ul>
<p><strong>Production requires 8 core components:</strong></p>
<ol>
<li>Context builder (feature extraction)</li>
<li>Policy service (bandit algorithm)</li>
<li>Event logger (comprehensive logging)</li>
<li>Reward collector (delayed rewards)</li>
<li>Offline evaluator (validate new policies)</li>
<li>Policy trainer (batch updates)</li>
<li>Monitor service (KPIs, alerts)</li>
<li>Safety layer (circuit breaker, fallback)</li>
</ol>
<p><strong>Deploy gradually: staging → shadow → canary → full</strong></p>
<ul>
<li>Shadow mode: Run in parallel, don’t impact users</li>
<li>Canary: 5% → 10% → 20% → 50% → 100%</li>
<li>Monitor closely at each stage</li>
</ul>
<p><strong>Logging is critical for success:</strong></p>
<ul>
<li>Full context vector (all features)</li>
<li>Propensity scores (for offline eval)</li>
<li>Model metadata (version, hyperparams)</li>
<li>Rewards (handle delayed attribution)</li>
</ul>
<p><strong>Monitor these KPIs:</strong></p>
<ul>
<li>Performance: average reward, regret, policy value</li>
<li>Exploration: action entropy, exploration rate</li>
<li>System: latency (P99 &#x3C;200ms), error rate (&#x3C;1%)</li>
</ul>
<p><strong>Safety mechanisms prevent disasters:</strong></p>
<ul>
<li>Circuit breaker (fallback after failures)</li>
<li>Rate limiting (prevent runaway)</li>
<li>Gradual rollout (detect issues early)</li>
</ul>
<p><strong>Production timeline:</strong></p>
<ul>
<li>Weeks 1-2: Research &#x26; development</li>
<li>Weeks 3-4: Offline evaluation</li>
<li>Week 5: Staging deployment</li>
<li>Weeks 6-7: Shadow mode</li>
<li>Weeks 8-9: Canary deployment</li>
<li>Week 10: Full rollout</li>
</ul>
<p><strong>Common failure modes:</strong></p>



































<table><thead><tr><th>Problem</th><th>Symptom</th><th>Solution</th></tr></thead><tbody><tr><td>Missing propensities</td><td>Can’t do offline eval</td><td>Log propensity with every decision</td></tr><tr><td>Delayed rewards</td><td>Underestimate performance</td><td>Implement reward collector with attribution</td></tr><tr><td>Stopped exploring</td><td>All traffic on one action</td><td>Monitor entropy, tune exploration</td></tr><tr><td>Distribution drift</td><td>Performance degrades</td><td>Retrain monthly, maintain small ε</td></tr><tr><td>No fallback</td><td>One failure breaks system</td><td>Circuit breaker + fallback policy</td></tr></tbody></table>
<hr>
<h2 id="conclusion-youre-ready-for-production">Conclusion: You’re Ready for Production</h2>
<p>You now have the complete production playbook:</p>
<p>✅ <strong>Offline evaluation</strong> to validate before deploying<br>
✅ <strong>Production architecture</strong> with 8 core components<br>
✅ <strong>Monitoring dashboard</strong> specification with KPIs<br>
✅ <strong>Deployment workflow</strong> from research to production<br>
✅ <strong>Pre-deployment checklist</strong> with 56 criteria<br>
✅ <strong>Common pitfalls</strong> and how to avoid them</p>
<p><strong>Next steps:</strong></p>
<ol>
<li><strong>Review your use case</strong> against Part 1 decision framework</li>
<li><strong>Implement algorithm</strong> from Part 3 (LinUCB or Thompson)</li>
<li><strong>Run offline evaluation</strong> using methods from this post</li>
<li><strong>Deploy gradually</strong> following the phased approach</li>
<li><strong>Monitor continuously</strong> using the dashboard specification</li>
</ol>
<p><strong>Remember:</strong> Start simple (LinUCB with α=1.0), validate thoroughly (offline eval), deploy gradually (canary), monitor closely (dashboard), and iterate (retrain monthly).</p>
<p>Contextual bandits are powerful, but production deployment requires rigor. Follow this guide, and you’ll deploy successfully.</p>
<hr>
<h2 id="series-complete">Series Complete 🎉</h2>
<p><strong>This concludes the 5-part series on Contextual Bandits in Production.</strong></p>
<h3 id="the-complete-journey"><strong>The Complete Journey:</strong></h3>
<p><strong><a href="contextual-bandits-part-1-introduction">Part 1: When to Use Contextual Bandits</a></strong><br>
Decision framework, CB vs A/B vs MAB vs RL</p>
<p><strong><a href="contextual-bandits-part-2-mathematics">Part 2: Mathematical Foundations</a></strong><br>
Regret bounds, exploration-exploitation, reward models</p>
<p><strong><a href="contextual-bandits-part-3-algorithms">Part 3: Core Algorithms &#x26; Implementation</a></strong><br>
ε-greedy, UCB, LinUCB, Thompson Sampling with code</p>
<p><strong><a href="contextual-bandits-part-4-neural">Part 4: Neural Contextual Bandits</a></strong><br>
Deep learning, uncertainty quantification, high-dim actions</p>
<p><strong><a href="contextual-bandits-part-5-production">Part 5: Production Deployment</a></strong> (this post)<br>
Offline evaluation, architecture, monitoring, deployment</p>
<hr>
<h2 id="further-reading">Further Reading</h2>
<p><strong>Production ML:</strong></p>
<ul>
<li><a href="https://developers.google.com/machine-learning/guides/rules-of-ml">Rules of Machine Learning</a> - Google’s best practices</li>
<li><a href="https://papers.nips.cc/paper/2015/file/86df7dcfd896fcaf2674f757a2463eba-Paper.pdf">Hidden Technical Debt in ML Systems</a> - Sculley et al.</li>
</ul>
<p><strong>Offline Evaluation:</strong></p>
<ul>
<li><a href="https://arxiv.org/abs/1605.04812">Counterfactual Evaluation of Slate Recommendations</a> - IPS for bandits</li>
<li><a href="https://arxiv.org/abs/1103.4601">Doubly Robust Policy Evaluation</a> - Dudík et al.</li>
</ul>
<p><strong>Industry case studies:</strong></p>
<ul>
<li><a href="https://netflixtechblog.com/artwork-personalization-c589f074ad76">Netflix: Artwork Personalization via Bandits</a></li>
<li><a href="https://engineering.atspotify.com/2020/01/16/for-your-ears-only-personalizing-spotify-home-with-machine-learning/">Spotify: Home Screen Recommendations</a></li>
<li><a href="https://engineering.linkedin.com/blog/">LinkedIn: Feed Ranking</a></li>
</ul>
<p><strong>Tools:</strong></p>
<ul>
<li><a href="https://vowpalwabbit.org/">Vowpal Wabbit</a> - Production bandit library</li>
<li><a href="https://github.com/microsoft/mwt-ds">Microsoft Decision Service</a> - Cloud bandit platform</li>
<li><a href="https://cloud.google.com/ai-platform">Google Cloud AI Platform</a> - Managed ML infrastructure</li>
</ul>
<hr>
<p><strong>Questions? Feedback?</strong> Reach out on <a href="https://twitter.com/vitorsousa">Twitter</a> or <a href="https://linkedin.com/in/vitorsousa">LinkedIn</a></p>
<p><strong>Want more?</strong> Subscribe for updates on advanced topics: non-stationary bandits, fairness constraints, multi-objective optimization.</p>]]></content:encoded><enclosure url="https://www.vitorsousa.com/_astro/openelm.DiPoN2BH.png" length="756000" type="image/png"/></item></channel></rss>