<?xml version="1.0" encoding="UTF-8"?>
<rss  xmlns:atom="http://www.w3.org/2005/Atom" 
      xmlns:media="http://search.yahoo.com/mrss/" 
      xmlns:content="http://purl.org/rss/1.0/modules/content/" 
      xmlns:dc="http://purl.org/dc/elements/1.1/" 
      version="2.0">
<channel>
<title>Rosh Beed</title>
<link>https://roshbeed.com/blog.html</link>
<atom:link href="https://roshbeed.com/blog.xml" rel="self" type="application/rss+xml"/>
<description>Machine learning and AI solutions engineer, London.</description>
<generator>quarto-1.10.18</generator>
<lastBuildDate>Fri, 18 Sep 2026 00:00:00 GMT</lastBuildDate>
<item>
  <title>Words get their meaning from the company they keep</title>
  <dc:creator>Rosh Beed</dc:creator>
  <link>https://roshbeed.com/posts/2026-09-18-company-they-keep/</link>
  <description><![CDATA[ 




<p>The first week of the residency was word2vec: build the thing from scratch, no pretrained anything, and see whether the vectors that come out behave the way Mikolov’s papers say they do.</p>
<p>I’m not claiming anything new here. The 2013 papers [1, 2] are thirteen years old and the results are well known. The point of building it was to understand <em>why</em> the training objective is shaped the way it is, which is not obvious from reading about it.</p>
<p>The full run trained on <code>text8</code> — 17 million words of Wikipedia — and scored at or above published word2vec numbers on the same corpus:</p>
<table class="caption-top table">
<thead>
<tr class="header">
<th>benchmark</th>
<th>untrained</th>
<th>skip-gram</th>
<th>CBOW</th>
<th>published word2vec</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>WordSim-353 (ρ)</td>
<td>0.028</td>
<td>0.726</td>
<td><strong>0.730</strong></td>
<td>0.68</td>
</tr>
<tr class="even">
<td>SimLex-999 (ρ)</td>
<td>0.008</td>
<td><strong>0.297</strong></td>
<td>0.290</td>
<td>0.30</td>
</tr>
<tr class="odd">
<td>Google analogies</td>
<td>0.000</td>
<td><strong>0.464</strong></td>
<td>0.408</td>
<td>0.38</td>
</tr>
</tbody>
</table>
<p>The untrained column matters more than it looks. A score means nothing until you know what the same model reports before it has learned anything, and I got that habit from a later project where a model that looked trained turned out not to be.</p>
<p>This post rebuilds the idea at a size you can watch: a fiftieth of the corpus, 64 dimensions, trained here while the page builds.</p>
<section id="the-idea-you-never-say-what-a-word-means" class="level2">
<h2 class="anchored" data-anchor-id="the-idea-you-never-say-what-a-word-means">The idea: you never say what a word means</h2>
<p>Nobody labels the training data. The model is only ever shown a word and one of its neighbours, and asked whether that pairing is real.</p>
<p>That’s the distributional hypothesis, and it’s an old idea in linguistics: words that turn up in the same contexts tend to mean similar things. <em>Coffee</em> and <em>tea</em> get poured, spilled and brewed. The model never learns that either is a drink. It learns that they keep the same company, which turns out to be close enough to be useful.</p>
<p>So the training signal is: here is the word <code>king</code>, here is the word <code>throne</code> which appeared four words away — score that pair high. And here are five words drawn at random from the corpus — score those low.</p>
</section>
<section id="the-problem-the-paper-is-actually-solving" class="level2">
<h2 class="anchored" data-anchor-id="the-problem-the-paper-is-actually-solving">The problem the paper is actually solving</h2>
<p>The obvious way to train this is a softmax over the whole vocabulary: given the centre word, produce a probability for every word in the corpus being its neighbour, and push up the probability of the one that really was.</p>
<p>That needs a normalising sum over every word in the vocabulary, on every training pair. With 200,000 words and tens of millions of pairs per epoch, you are doing 200,000 dot products to learn one thing.</p>
<p>Negative sampling replaces that question with a much cheaper one. Instead of <em>which of these 200,000 words is the neighbour</em>, ask <em>is this specific pair real or did I make it up</em> — once for the true neighbour, and k more times for randomly drawn words. With k = 5 that is six dot products instead of 200,000.</p>
<div id="cell-fig-softmax-cost" class="cell" data-execution_count="1">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> sys</span>
<span id="cb1-2"></span>
<span id="cb1-3">sys.path.insert(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".."</span>)</span>
<span id="cb1-4"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> numpy <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> np</span>
<span id="cb1-5"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> _style <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> COLOURS, MUTED, figure, style_axes</span>
<span id="cb1-6"></span>
<span id="cb1-7">vocab_sizes <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.logspace(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, np.log10(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">300_000</span>), <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">200</span>)</span>
<span id="cb1-8">k <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span></span>
<span id="cb1-9"></span>
<span id="cb1-10">fig, ax <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> figure()</span>
<span id="cb1-11">ax.plot(vocab_sizes, vocab_sizes, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>], linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb1-12">ax.plot(vocab_sizes, np.full_like(vocab_sizes, k <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>), color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb1-13"></span>
<span id="cb1-14">ax.annotate(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"full softmax</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">(one per word in the vocabulary)"</span>, xy<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3000</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3000</span>),</span>
<span id="cb1-15">            xytext<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">14</span>), textcoords<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"offset points"</span>, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>])</span>
<span id="cb1-16">ax.annotate(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"negative sampling (k = 5)"</span>, xy<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3000</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>), xytext<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>),</span>
<span id="cb1-17">            textcoords<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"offset points"</span>, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>])</span>
<span id="cb1-18"></span>
<span id="cb1-19">ax.set_xscale(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"log"</span>)</span>
<span id="cb1-20">ax.set_yscale(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"log"</span>)</span>
<span id="cb1-21">style_axes(ax, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Vocabulary size"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Dot products per training pair"</span>)</span>
<span id="cb1-22">fig.tight_layout()</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-display">
<div id="fig-softmax-cost" class="quarto-float quarto-figure quarto-figure-center anchored" alt="A log-scale chart. The full softmax line rises steadily with vocabulary size to 200,000 dot products per pair. The negative sampling line is flat at 6 across the whole range.">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-softmax-cost-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://roshbeed.com/posts/2026-09-18-company-they-keep/index_files/figure-html/fig-softmax-cost-output-1.png" alt="A log-scale chart. The full softmax line rises steadily with vocabulary size to 200,000 dot products per pair. The negative sampling line is flat at 6 across the whole range." width="661" height="392" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-softmax-cost-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;1: Dot products per training pair, as the vocabulary grows. Negative sampling does not care how big the vocabulary is.
</figcaption>
</figure>
</div>
</div>
</div>
<p>That is the whole reason word2vec was practical in 2013, and it is why the loss you see below is not a probability over words. It is six binary decisions averaged together.</p>
<p>Which has a consequence worth knowing before we train anything: <strong>an untrained model’s loss is predictable</strong>. Six coin flips, each costing ln 2, is 4.159. If training starts anywhere else, something is wrong with the setup rather than the model.</p>
</section>
<section id="the-toy" class="level2">
<h2 class="anchored" data-anchor-id="the-toy">The toy</h2>
<p>2 million characters of <code>text8</code> — about a fiftieth of what the real run used. Everything else is the real recipe: subsample the very frequent words, draw negatives from the unigram distribution raised to the 3/4 power, and train two embedding tables against each other.</p>
<p>The data comes from a Hugging Face dataset pinned to a commit, so this page cannot silently change because the corpus did.</p>
<div id="db905004" class="cell" data-execution_count="2">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb2-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> collections</span>
<span id="cb2-2"></span>
<span id="cb2-3"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> numpy <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> np</span>
<span id="cb2-4"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> torch</span>
<span id="cb2-5"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> torch.nn.functional <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> F</span>
<span id="cb2-6"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> huggingface_hub <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> hf_hub_download</span>
<span id="cb2-7"></span>
<span id="cb2-8">REVISION <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"fe0e9a9549d55bdd3c238f265ecfbe0792dcdc1e"</span></span>
<span id="cb2-9">path <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> hf_hub_download(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"roshbeed/ai-residency-blog-data"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"text8/text8-2m.txt"</span>,</span>
<span id="cb2-10">                       repo_type<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"dataset"</span>, revision<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>REVISION)</span>
<span id="cb2-11"></span>
<span id="cb2-12">words <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">open</span>(path).read().split()</span>
<span id="cb2-13">counts <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> collections.Counter(words)</span>
<span id="cb2-14">vocab <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [w <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> w, c <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> counts.most_common() <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> c <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>]</span>
<span id="cb2-15">stoi <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> {w: i <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i, w <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">enumerate</span>(vocab)}</span>
<span id="cb2-16">ids <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.array([stoi[w] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> w <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> words <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> w <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> stoi], dtype<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>np.int64)</span>
<span id="cb2-17"></span>
<span id="cb2-18"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(words)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:,}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> tokens, </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(vocab)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:,}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> words kept (seen 10+ times)"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>336,026 tokens, 3,704 words kept (seen 10+ times)</code></pre>
</div>
</div>
<section id="throwing-away-the-common-words" class="level3">
<h3 class="anchored" data-anchor-id="throwing-away-the-common-words">Throwing away the common words</h3>
<p><code>the</code> appears in the corpus about 70,000 times. Every one of those is a training pair, and none of them says much: <code>the</code> sits next to everything, so it tells you nothing about what its neighbours mean.</p>
<p>Mikolov’s second paper discards frequent words with a probability that rises with their frequency. It is the highest-leverage setting in the whole recipe — in my sweep over the full corpus it moved the benchmark score further than architecture, learning rate or window size did.</p>
<div id="4ec6cfd6" class="cell" data-execution_count="3">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb4-1">frequency <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.bincount(ids, minlength<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(vocab)).astype(np.float64)</span>
<span id="cb4-2">frequency <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/=</span> frequency.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>()</span>
<span id="cb4-3"></span>
<span id="cb4-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Mikolov et al. (2013b), equation 5. A larger t keeps more of the corpus, which</span></span>
<span id="cb4-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># a toy this size needs; the full run used a far more aggressive 1e-5.</span></span>
<span id="cb4-6">t <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1e-3</span></span>
<span id="cb4-7">keep_probability <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.minimum(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.0</span>, np.sqrt(t <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> frequency))</span>
<span id="cb4-8"></span>
<span id="cb4-9">rng <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.random.default_rng(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb4-10">kept <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ids[rng.random(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(ids)) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> keep_probability[ids]]</span>
<span id="cb4-11"></span>
<span id="cb4-12"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(ids)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:,}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> tokens in, </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(kept)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:,}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> out — </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(kept) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(ids)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.0f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">% kept"</span>)</span>
<span id="cb4-13"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"'the' kept with probability </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>keep_probability[stoi[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'the'</span>]]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.3f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">, "</span></span>
<span id="cb4-14">      <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"'philosophy' with </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>keep_probability[stoi[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'philosophy'</span>]]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.3f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>285,806 tokens in, 183,984 out — 64% kept
'the' kept with probability 0.115, 'philosophy' with 1.000</code></pre>
</div>
</div>
<div id="7279ee46" class="cell" data-execution_count="4">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb6-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Skip-gram pairs: every word paired with each neighbour up to 5 positions away,</span></span>
<span id="cb6-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># in both directions. Built by slicing the token array rather than looping.</span></span>
<span id="cb6-3">WINDOW <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span></span>
<span id="cb6-4"></span>
<span id="cb6-5">centres, contexts <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [], []</span>
<span id="cb6-6"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> offset <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, WINDOW <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>):</span>
<span id="cb6-7">    centres.append(kept[offset:])<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span>  contexts.append(kept[:<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>offset])</span>
<span id="cb6-8">    centres.append(kept[:<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>offset])<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> contexts.append(kept[offset:])</span>
<span id="cb6-9"></span>
<span id="cb6-10">centre <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.from_numpy(np.concatenate(centres))</span>
<span id="cb6-11">context <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.from_numpy(np.concatenate(contexts))</span>
<span id="cb6-12"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(centre)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:,}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> training pairs"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>1,839,810 training pairs</code></pre>
</div>
</div>
<p>Negatives are not drawn uniformly. The paper raises the unigram frequency to the power 3/4, which pulls rare words up and common words down relative to how often they actually occur — frequent words still get chosen more, just less overwhelmingly than their raw counts would give.</p>
<div id="9bc5e2ab" class="cell" data-execution_count="5">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb8-1">noise <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.from_numpy(frequency <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.75</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> (frequency <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.75</span>).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>())</span>
<span id="cb8-2"></span>
<span id="cb8-3">DIMENSIONS, K <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">64</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span></span>
<span id="cb8-4">generator <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.Generator().manual_seed(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb8-5"></span>
<span id="cb8-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Two tables: a word as a centre, and the same word as somebody's context. Only</span></span>
<span id="cb8-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># the first is kept at the end — the second exists to give the first something to</span></span>
<span id="cb8-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># be scored against.</span></span>
<span id="cb8-9">centre_vectors <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (torch.randn(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(vocab), DIMENSIONS, generator<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>generator) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.01</span>).requires_grad_()</span>
<span id="cb8-10">context_vectors <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.zeros(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(vocab), DIMENSIONS, requires_grad<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span>
<span id="cb8-11">optimiser <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.optim.Adam([centre_vectors, context_vectors], lr<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2e-3</span>)</span>
<span id="cb8-12"></span>
<span id="cb8-13"></span>
<span id="cb8-14"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> loss_on(centre_batch, context_batch):</span>
<span id="cb8-15">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""One real pair scored up, K invented pairs scored down."""</span></span>
<span id="cb8-16">    v <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> centre_vectors[centre_batch]</span>
<span id="cb8-17">    real <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> F.logsigmoid((v <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> context_vectors[context_batch]).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>))</span>
<span id="cb8-18"></span>
<span id="cb8-19">    fake_ids <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.multinomial(noise, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(centre_batch) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> K,</span>
<span id="cb8-20">                                 replacement<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>, generator<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>generator)</span>
<span id="cb8-21">    fake <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> context_vectors[fake_ids.view(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(centre_batch), K)]</span>
<span id="cb8-22">    invented <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> F.logsigmoid(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>(fake <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@</span> v.unsqueeze(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)).squeeze(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb8-23"></span>
<span id="cb8-24">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>(real <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> invented).mean()</span></code></pre></div></div>
</details>
</div>
<div id="e21c4f4f" class="cell" data-execution_count="6">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb9-1"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">with</span> torch.no_grad():</span>
<span id="cb9-2">    baseline <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> loss_on(centre[:<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8192</span>], context[:<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8192</span>]).item()</span>
<span id="cb9-3"></span>
<span id="cb9-4"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"loss before any training: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>baseline<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.3f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb9-5"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"predicted for a model guessing: (1 + </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>K<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">) * ln 2 = </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> K) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> np<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>log(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.3f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>loss before any training: 4.159
predicted for a model guessing: (1 + 5) * ln 2 = 4.159</code></pre>
</div>
</div>
<div id="14b1f816" class="cell" data-execution_count="7">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb11" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb11-1">EPOCHS, BATCH <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">25</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8192</span></span>
<span id="cb11-2">history <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []</span>
<span id="cb11-3"></span>
<span id="cb11-4"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> epoch <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(EPOCHS):</span>
<span id="cb11-5">    order <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.randperm(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(centre), generator<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>generator)</span>
<span id="cb11-6">    total <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> steps <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span></span>
<span id="cb11-7">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(order) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> BATCH, BATCH):</span>
<span id="cb11-8">        batch <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> order[i:i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> BATCH]</span>
<span id="cb11-9">        loss <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> loss_on(centre[batch], context[batch])</span>
<span id="cb11-10">        optimiser.zero_grad()</span>
<span id="cb11-11">        loss.backward()</span>
<span id="cb11-12">        optimiser.step()</span>
<span id="cb11-13">        total <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> loss.item()</span>
<span id="cb11-14">        steps <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span>
<span id="cb11-15">    history.append(total <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> steps)</span>
<span id="cb11-16"></span>
<span id="cb11-17"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"loss: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>baseline<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.3f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> untrained -&gt; </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>history[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.3f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> after </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>EPOCHS<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> epochs"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>loss: 4.159 untrained -&gt; 2.213 after 25 epochs</code></pre>
</div>
</div>
<div id="cell-fig-loss" class="cell" data-execution_count="8">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb13" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb13-1">fig, ax <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> figure(height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">3.6</span>)</span>
<span id="cb13-2">ax.axhline(baseline, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.2</span>, linestyle<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"--"</span>)</span>
<span id="cb13-3">ax.plot(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, EPOCHS <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>), history, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>], linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb13-4">ax.annotate(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"untrained: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>baseline<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.2f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>, xy<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(EPOCHS, baseline), xytext<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>),</span>
<span id="cb13-5">            textcoords<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"offset points"</span>, ha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"right"</span>, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED)</span>
<span id="cb13-6">ax.set_ylim(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, baseline <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.15</span>)</span>
<span id="cb13-7">style_axes(ax, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Epoch"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Loss"</span>)</span>
<span id="cb13-8">fig.tight_layout()</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-display">
<div id="fig-loss" class="quarto-float quarto-figure quarto-figure-center anchored" alt="A curve falling from about 3.2 to 2.2 over 25 epochs, with a dashed horizontal line at 4.159 marking the untrained model well above it.">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-loss-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://roshbeed.com/posts/2026-09-18-company-they-keep/index_files/figure-html/fig-loss-output-1.png" alt="A curve falling from about 3.2 to 2.2 over 25 epochs, with a dashed horizontal line at 4.159 marking the untrained model well above it." width="662" height="335" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-loss-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;2: Training loss against the untrained baseline. The dashed line is where a model that has learned nothing sits.
</figcaption>
</figure>
</div>
</div>
</div>
</section>
</section>
<section id="what-it-learned" class="level2">
<h2 class="anchored" data-anchor-id="what-it-learned">What it learned</h2>
<p>The vectors are normalised and compared by cosine similarity. Nothing below was labelled, grouped or supervised — it all falls out of which words appeared near which.</p>
<div id="2f776324" class="cell" data-execution_count="9">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb14" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb14-1">embeddings <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> F.normalize(centre_vectors.detach(), dim<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb14-2"></span>
<span id="cb14-3"></span>
<span id="cb14-4"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> nearest(word, k<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>):</span>
<span id="cb14-5">    similarity <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> embeddings <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@</span> embeddings[stoi[word]]</span>
<span id="cb14-6">    similarity[stoi[word]] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># a word is always its own nearest neighbour</span></span>
<span id="cb14-7">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> [vocab[i] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> similarity.topk(k).indices.tolist()]</span>
<span id="cb14-8"></span>
<span id="cb14-9"></span>
<span id="cb14-10"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> word <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> (<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"king"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"france"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"computer"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"three"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"war"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"music"</span>):</span>
<span id="cb14-11">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>word<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:10}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> -&gt; </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">', '</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>join(nearest(word))<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>king       -&gt; alexander, darius, philip, macedon, plutarch, kings
france     -&gt; germany, spain, morocco, italy, brazil, portugal
computer   -&gt; software, windows, hardware, extension, animation, processing
three      -&gt; two, four, five, one, eight, zero
war        -&gt; civil, defeat, loyalists, battle, occupation, generals
music      -&gt; dance, folk, literature, radio, opera, publishing</code></pre>
</div>
</div>
<p><code>three</code> lands among the other small numbers, <code>france</code> among other countries, <code>king</code> among kings and the names of specific ones. From a third of a million words, with no labels anywhere.</p>
<p>The famous result is the arithmetic: if the offset from <code>man</code> to <code>woman</code> runs in the same direction as the offset from <code>king</code> to <code>queen</code>, then subtracting and adding those vectors should land near <code>queen</code>.</p>
<p>Rather than print the top few words — which at this size is mostly noise — this asks a sharper question: <strong>where does the expected answer actually rank?</strong> Out of 3,704 words, a model that had learned nothing would put it around 1,850 on average.</p>
<div id="100f7b34" class="cell" data-execution_count="10">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb16" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb16-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> analogy_rank(a, b, c, expected):</span>
<span id="cb16-2">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""b is to a as ? is to c. Returns where `expected` lands, and what won."""</span></span>
<span id="cb16-3">    target <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> embeddings[stoi[b]] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> embeddings[stoi[a]] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> embeddings[stoi[c]]</span>
<span id="cb16-4">    similarity <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> embeddings <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@</span> F.normalize(target, dim<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb16-5">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> word <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> (a, b, c):</span>
<span id="cb16-6">        similarity[stoi[word]] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># all three query words have to be excluded</span></span>
<span id="cb16-7"></span>
<span id="cb16-8">    order <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> similarity.argsort(descending<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>).tolist()</span>
<span id="cb16-9">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> expected <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> stoi:</span>
<span id="cb16-10">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>, vocab[order[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]]</span>
<span id="cb16-11">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> order.index(stoi[expected]) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, vocab[order[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]]</span>
<span id="cb16-12"></span>
<span id="cb16-13"></span>
<span id="cb16-14">tests <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"he"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"his"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"she"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"her"</span>),</span>
<span id="cb16-15">         (<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"brother"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"sister"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"son"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"daughter"</span>),</span>
<span id="cb16-16">         (<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"man"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"men"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"woman"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"women"</span>),</span>
<span id="cb16-17">         (<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"man"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"king"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"woman"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"queen"</span>),</span>
<span id="cb16-18">         (<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"england"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"london"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"france"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"paris"</span>),</span>
<span id="cb16-19">         (<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"good"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"better"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bad"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"worse"</span>)]</span>
<span id="cb16-20"></span>
<span id="cb16-21"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'analogy'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:34}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'expected'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:10}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'rank'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;6}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">   top-1"</span>)</span>
<span id="cb16-22"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> a, b, c, expected <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> tests:</span>
<span id="cb16-23">    rank, winner <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> analogy_rank(a, b, c, expected)</span>
<span id="cb16-24">    shown <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"not in vocab"</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> rank <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">is</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>rank<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;6}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb16-25">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>b <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">' - '</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> a <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">' + '</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> c<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:34}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>expected<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:10}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>shown<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">   </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>winner<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>analogy                            expected     rank   top-1
his - he + she                     her             1   her
sister - brother + son             daughter        1   daughter
men - man + woman                  women           3   retrieved
king - man + woman                 queen          30   macedon
london - england + france          paris         365   vienna
better - good + bad                worse      not in vocab   produced</code></pre>
</div>
</div>
<p>The grammatical ones work. <code>his - he + she</code> puts <code>her</code> first out of 3,704, and the sibling and plural analogies land in the top handful.</p>
<p>The semantic ones don’t. <code>queen</code> comes in a couple of hundred places down — far better than the ~1,850 chance would give, so the direction is really there, but nowhere near first. And <code>worse</code> isn’t in the vocabulary at all, because a third of a million words doesn’t contain it ten times.</p>
<p>That gap is the honest result of a toy this size, and it is the same gap the full run closes: 17 million words and 300 dimensions gets 46% of the Google analogy set exactly right.</p>
<p>One detail in that function cost me real time on the full project: <strong>all three query words have to be excluded from the search.</strong> <code>king - man + woman</code> lands nearest to <code>king</code> itself far more often than to <code>queen</code>. A scorer that forgets to exclude them reports a near-constant zero however good the vectors are, and that looks exactly like a broken model rather than a broken metric.</p>
</section>
<section id="what-the-toy-doesnt-show" class="level2">
<h2 class="anchored" data-anchor-id="what-the-toy-doesnt-show">What the toy doesn’t show</h2>
<p>A fiftieth of the corpus and 64 dimensions gets the shape of the result, not the quality of it. The benchmark numbers in the table at the top came from the full 17 million words, 300 dimensions, and a 48-run hyperparameter sweep.</p>
<p>The sweep also turned up the more useful finding, which is which knobs <em>don’t</em> matter: window size and the number of negatives moved the score by less than 0.004 between their best and worst settings. That is worth more than another point of accuracy, because it says where not to spend the next twelve hours.</p>
<p>The full project, with the sweep and the deployed nearest-neighbour API, is <a href="https://github.com/RoshBeed/ai-residency/tree/main/services/word-embeddings">on GitHub</a>.</p>
<hr>
<p>[1] Mikolov, Chen, Corrado, Dean. <em>Efficient Estimation of Word Representations in Vector Space.</em> 2013. [2] Mikolov, Sutskever, Chen, Corrado, Dean. <em>Distributed Representations of Words and Phrases and their Compositionality.</em> NeurIPS 2013.</p>
<div id="b6470ffb" class="cell" data-execution_count="11">
<div class="cell-output cell-output-stdout">
<pre><code>Built:      2026-09-18 02:16:53 UTC
Python:     3.13.15
matplotlib: 3.11.2
numpy:      2.5.3
torch:      2.14.0+cpu</code></pre>
</div>
</div>


</section>

 ]]></description>
  <category>nlp</category>
  <category>embeddings</category>
  <category>week-1</category>
  <guid>https://roshbeed.com/posts/2026-09-18-company-they-keep/</guid>
  <pubDate>Fri, 18 Sep 2026 00:00:00 GMT</pubDate>
</item>
<item>
  <title>The emotion is a token, not a head</title>
  <dc:creator>Rosh Beed</dc:creator>
  <link>https://roshbeed.com/posts/2026-09-18-emotion-is-a-token/</link>
  <description><![CDATA[ 




<p>The other half of week 5: recognise the emotion in a clip of speech.</p>
<p>The obvious way to build this is a classifier. Take the encoder you already have, mean-pool its output, put a <code>Linear(dim, n_emotions)</code> on top, train with cross-entropy. It works, it’s three lines, and it’s what most people reach for.</p>
<p>I built it the other way, because of something about how Whisper already works.</p>
<section id="whisper-already-talks-about-itself-in-tokens" class="level2">
<h2 class="anchored" data-anchor-id="whisper-already-talks-about-itself-in-tokens">Whisper already talks about itself in tokens</h2>
<p>Whisper’s decoder doesn’t only emit words. Its output starts with control tokens drawn from the same vocabulary as everything else — <code>&lt;|en|&gt;</code> for the language, <code>&lt;|transcribe|&gt;</code> or <code>&lt;|translate|&gt;</code> for the task, timestamps if you ask for them.</p>
<p>They aren’t a separate mechanism. They are ordinary vocabulary entries, predicted by the same softmax, trained by the same cross-entropy, sampled the same way.</p>
<p>So the question is whether emotion is a different kind of thing from language and task, or the same kind of thing. And it’s hard to argue it’s different. Adding <code>&lt;happy&gt;</code>, <code>&lt;sad&gt;</code> and the rest to the vocabulary lets the model say what it heard through machinery it already has.</p>
<div id="cell-fig-two-designs" class="cell" data-execution_count="1">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> sys</span>
<span id="cb1-2"></span>
<span id="cb1-3">sys.path.insert(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".."</span>)</span>
<span id="cb1-4"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> matplotlib.pyplot <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> plt</span>
<span id="cb1-5"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> _style <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> COLOURS, MUTED</span>
<span id="cb1-6"></span>
<span id="cb1-7">fig, axes <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> plt.subplots(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, figsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">8.4</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">3.4</span>))</span>
<span id="cb1-8"></span>
<span id="cb1-9"></span>
<span id="cb1-10"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> box(ax, x, y, w, h, label, colour, text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"white"</span>, size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>):</span>
<span id="cb1-11">    ax.add_patch(plt.Rectangle((x, y), w, h, facecolor<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>colour, edgecolor<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"none"</span>))</span>
<span id="cb1-12">    ax.text(x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> w <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, y <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> h <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, label, ha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"center"</span>, va<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"center"</span>,</span>
<span id="cb1-13">            color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>text, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>size)</span>
<span id="cb1-14"></span>
<span id="cb1-15"></span>
<span id="cb1-16"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> arrow(ax, x1, y1, x2, y2):</span>
<span id="cb1-17">    ax.annotate(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span>, xy<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(x2, y2), xytext<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(x1, y1),</span>
<span id="cb1-18">                arrowprops<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">dict</span>(arrowstyle<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"-|&gt;"</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#aeb6bf"</span>, linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.3</span>))</span>
<span id="cb1-19"></span>
<span id="cb1-20"></span>
<span id="cb1-21"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> ax, title <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">zip</span>(axes, (<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"a classifier head"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"a token in the vocabulary"</span>)):</span>
<span id="cb1-22">    box(ax, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.05</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.42</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.22</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.18</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"encoder"</span>, COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>])</span>
<span id="cb1-23">    ax.set_title(title, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, pad<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>)</span>
<span id="cb1-24">    ax.set_xlim(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> ax.set_ylim(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> ax.axis(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"off"</span>)</span>
<span id="cb1-25"></span>
<span id="cb1-26"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># left: two outputs, two losses</span></span>
<span id="cb1-27">arrow(axes[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>], <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.27</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.56</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.40</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.72</span>)</span>
<span id="cb1-28">arrow(axes[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>], <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.27</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.46</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.40</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.30</span>)</span>
<span id="cb1-29">box(axes[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>], <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.40</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.64</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.22</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.16</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"decoder"</span>, COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>])</span>
<span id="cb1-30">box(axes[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>], <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.40</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.22</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.22</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.16</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Linear"</span>, COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>])</span>
<span id="cb1-31">axes[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>].text(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.66</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.72</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"a c d"</span>, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, va<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"center"</span>)</span>
<span id="cb1-32">axes[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>].text(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.66</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.30</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"shaky"</span>, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, va<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"center"</span>)</span>
<span id="cb1-33">axes[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>].text(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.06</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"two losses"</span>, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, ha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"center"</span>)</span>
<span id="cb1-34"></span>
<span id="cb1-35"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># right: one output, one loss</span></span>
<span id="cb1-36">arrow(axes[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.27</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.51</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.40</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.51</span>)</span>
<span id="cb1-37">box(axes[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.40</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.43</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.22</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.16</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"decoder"</span>, COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>])</span>
<span id="cb1-38">arrow(axes[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.62</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.51</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.70</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.51</span>)</span>
<span id="cb1-39">axes[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>].text(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.72</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.51</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;shaky&gt;  a c d"</span>, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, va<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"center"</span>)</span>
<span id="cb1-40">axes[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>].text(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.06</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"one loss"</span>, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, ha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"center"</span>)</span>
<span id="cb1-41"></span>
<span id="cb1-42">fig.tight_layout()</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-display">
<div id="fig-two-designs" class="quarto-float quarto-figure quarto-figure-center anchored" alt="Two diagrams. On the left an encoder feeds both a decoder producing letters and a separate small classifier box producing an emotion. On the right the encoder feeds one decoder whose output sequence begins with an emotion token followed by the letters.">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-two-designs-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://roshbeed.com/posts/2026-09-18-emotion-is-a-token/index_files/figure-html/fig-two-designs-output-1.png" alt="Two diagrams. On the left an encoder feeds both a decoder producing letters and a separate small classifier box producing an emotion. On the right the encoder feeds one decoder whose output sequence begins with an emotion token followed by the letters." width="778" height="316" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-two-designs-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;1: The same encoder, two ways to get an emotion out of it. On the left the emotion leaves through a separate head trained by its own loss; on the right it is the first thing the decoder says.
</figcaption>
</figure>
</div>
</div>
</div>
<p>The appeal isn’t elegance for its own sake. Three concrete things follow:</p>
<p><strong>Adding a label is a vocabulary entry, not an architecture change.</strong> A new emotion is one more row in an embedding table. With a head it’s a new output dimension, a reshaped weight matrix, and a checkpoint that no longer loads.</p>
<p><strong>One loss instead of two.</strong> No weighting term to tune between a classification loss and a transcription loss.</p>
<p><strong>The emotion conditions what comes after it.</strong> It’s emitted first, so every subsequent token is generated with it in context. A head produces its answer off to the side, where the transcription can’t see it.</p>
<p>That last one is the real argument, and it’s also the one that costs something.</p>
</section>
<section id="both-measured" class="level2">
<h2 class="anchored" data-anchor-id="both-measured">Both, measured</h2>
<p>Same synthetic speech as the <a href="../2026-09-18-knowing-when-to-stop/">fine-tuning post</a>: three-letter words where each letter is a tone. Emotion is an acoustic property on top — <code>bright</code> raises the pitch, <code>shaky</code> adds a tremolo, <code>calm</code> is neither.</p>
<p>Same encoder, same decoder size, same data. The only difference is where the emotion comes out.</p>
<div id="465ddc72" class="cell" data-execution_count="2">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb2-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> numpy <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> np</span>
<span id="cb2-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> torch</span>
<span id="cb2-3"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> torch.nn <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> nn</span>
<span id="cb2-4"></span>
<span id="cb2-5">RATE, TONE_SECONDS, NOISE <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8000</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.08</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.15</span></span>
<span id="cb2-6">ALPHABET <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"abcdefgh"</span></span>
<span id="cb2-7">TONES <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> {c: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">300</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.28</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span> i <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i, c <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">enumerate</span>(ALPHABET)}</span>
<span id="cb2-8">EMOTIONS <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"calm"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bright"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"shaky"</span>]</span>
<span id="cb2-9"></span>
<span id="cb2-10">rng <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.random.default_rng(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb2-11">WORDS <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sorted</span>({<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span>.join(rng.choice(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">list</span>(ALPHABET), <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>)) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> _ <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">60</span>)})[:<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">40</span>]</span>
<span id="cb2-12"></span>
<span id="cb2-13"></span>
<span id="cb2-14"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> speak(word, seed, emotion):</span>
<span id="cb2-15">    g <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.random.default_rng(seed)</span>
<span id="cb2-16">    parts <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []</span>
<span id="cb2-17">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> c <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> word:</span>
<span id="cb2-18">        t <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.arange(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>(RATE <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> TONE_SECONDS)) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> RATE</span>
<span id="cb2-19">        f <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> TONES[c] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.02</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> g.normal())</span>
<span id="cb2-20">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> emotion <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>:                       <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># bright: everything a bit higher</span></span>
<span id="cb2-21">            f <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.18</span></span>
<span id="cb2-22">        tone <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.sin(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> np.pi <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> f <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> t) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.4</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> np.sin(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> np.pi <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> f <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> t)</span>
<span id="cb2-23">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> emotion <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>:                       <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># shaky: amplitude wobbles at 22 Hz</span></span>
<span id="cb2-24">            tone <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> tone <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.6</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> np.sin(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> np.pi <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">22</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> t))</span>
<span id="cb2-25">        parts.append(tone <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> np.hanning(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(t)))</span>
<span id="cb2-26">    x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.concatenate(parts)</span>
<span id="cb2-27">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> (x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> NOISE <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> g.normal(size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(x))).astype(np.float32)</span></code></pre></div></div>
</details>
</div>
<div id="aa637253" class="cell" data-execution_count="3">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb3-1">N_FFT, HOP, N_MELS <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">256</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">64</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">32</span></span>
<span id="cb3-2"></span>
<span id="cb3-3"></span>
<span id="cb3-4"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> stft(x, n_fft, hop):</span>
<span id="cb3-5">    window <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.hanning(n_fft <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)[:<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]</span>
<span id="cb3-6">    frames <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> (<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(x) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> n_fft) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">//</span> hop</span>
<span id="cb3-7">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> np.stack([np.fft.rfft(x[i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> hop:i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> hop <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> n_fft] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> window)</span>
<span id="cb3-8">                     <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(frames)], axis<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb3-9"></span>
<span id="cb3-10"></span>
<span id="cb3-11"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> mel_filterbank(rate, n_fft, n_mels):</span>
<span id="cb3-12">    to_mel <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">lambda</span> f: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2595</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> np.log10(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> f <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">700</span>)</span>
<span id="cb3-13">    to_hz <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">lambda</span> m: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">700</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span> (m <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2595</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb3-14">    edges <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> to_hz(np.linspace(to_mel(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>), to_mel(rate <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>), n_mels <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>))</span>
<span id="cb3-15">    bins <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.floor((n_fft <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> edges <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> rate).astype(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>)</span>
<span id="cb3-16">    bank <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.zeros((n_mels, n_fft <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">//</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>))</span>
<span id="cb3-17">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> m <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, n_mels <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>):</span>
<span id="cb3-18">        left, centre, right <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> bins[m <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], bins[m], bins[m <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]</span>
<span id="cb3-19">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> centre <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> left:</span>
<span id="cb3-20">            bank[m <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, left:centre] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (np.arange(left, centre) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> left) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> (centre <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> left)</span>
<span id="cb3-21">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> right <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> centre:</span>
<span id="cb3-22">            bank[m <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, centre:right] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (right <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> np.arange(centre, right)) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> (right <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> centre)</span>
<span id="cb3-23">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> bank</span>
<span id="cb3-24"></span>
<span id="cb3-25"></span>
<span id="cb3-26">BANK <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> mel_filterbank(RATE, N_FFT, N_MELS)</span>
<span id="cb3-27"></span>
<span id="cb3-28"></span>
<span id="cb3-29"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> to_picture(x):</span>
<span id="cb3-30">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> np.log10(np.maximum(BANK <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@</span> np.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">abs</span>(stft(x, N_FFT, HOP)) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1e-10</span>)).T.astype(np.float32)</span>
<span id="cb3-31"></span>
<span id="cb3-32"></span>
<span id="cb3-33"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> build(repeats, seed0):</span>
<span id="cb3-34">    pictures, words, emotions <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [], [], []</span>
<span id="cb3-35">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> index, word <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">enumerate</span>(WORDS):</span>
<span id="cb3-36">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> k <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(repeats):</span>
<span id="cb3-37">            emotion <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (index <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">7</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> k) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span></span>
<span id="cb3-38">            pictures.append(to_picture(speak(word, seed0 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> index <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1000</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> k, emotion)))</span>
<span id="cb3-39">            words.append(index)</span>
<span id="cb3-40">            emotions.append(emotion)</span>
<span id="cb3-41">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> torch.from_numpy(np.stack(pictures)), torch.tensor(words), torch.tensor(emotions)</span>
<span id="cb3-42"></span>
<span id="cb3-43"></span>
<span id="cb3-44">X, W, E <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> build(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb3-45">X_test, W_test, E_test <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> build(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">500_000</span>)</span>
<span id="cb3-46"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(X)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> clips, </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(WORDS)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> words x </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(EMOTIONS)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> emotions"</span>)</span>
<span id="cb3-47"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"guessing the emotion: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(EMOTIONS)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.3f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>480 clips, 40 words x 3 emotions
guessing the emotion: 0.333</code></pre>
</div>
</div>
<div id="382c3a3f" class="cell" data-execution_count="4">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb5-1">LETTERS <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sorted</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">set</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span>.join(WORDS)))</span>
<span id="cb5-2">EMOTION_TOKENS <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(LETTERS) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> i <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>)]   <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># the extra vocabulary entries</span></span>
<span id="cb5-3">START, END <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(LETTERS) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(LETTERS) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span></span>
<span id="cb5-4">VOCAB <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(LETTERS) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span></span>
<span id="cb5-5">DIM, FRAMES <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">64</span>, X.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]</span>
<span id="cb5-6"></span>
<span id="cb5-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># &lt;start&gt; &lt;emotion&gt; l l l &lt;end&gt; — the emotion slot is filled per clip</span></span>
<span id="cb5-8">TARGET <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.stack([torch.tensor([START, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> [LETTERS.index(c) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> c <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> w] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> [END])</span>
<span id="cb5-9">                      <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> w <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> WORDS])</span>
<span id="cb5-10"></span>
<span id="cb5-11"></span>
<span id="cb5-12"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> Model(nn.Module):</span>
<span id="cb5-13">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""One encoder, one decoder, and a classifier head that only the head</span></span>
<span id="cb5-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    variant ever trains."""</span></span>
<span id="cb5-15"></span>
<span id="cb5-16">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>):</span>
<span id="cb5-17">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">super</span>().<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>()</span>
<span id="cb5-18">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Linear(N_MELS, DIM)</span>
<span id="cb5-19">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.audio_positions <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Parameter(torch.randn(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, FRAMES, DIM) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.02</span>)</span>
<span id="cb5-20">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.encoder <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.TransformerEncoder(</span>
<span id="cb5-21">            nn.TransformerEncoderLayer(DIM, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> DIM, batch_first<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>,</span>
<span id="cb5-22">                                       norm_first<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>, dropout<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.0</span>), <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb5-23">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.embed <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Embedding(VOCAB, DIM)</span>
<span id="cb5-24">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.text_positions <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Parameter(torch.randn(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>, DIM) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.02</span>)</span>
<span id="cb5-25">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.decoder <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.TransformerDecoder(</span>
<span id="cb5-26">            nn.TransformerDecoderLayer(DIM, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> DIM, batch_first<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>,</span>
<span id="cb5-27">                                       norm_first<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>, dropout<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.0</span>), <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb5-28">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.out <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Linear(DIM, VOCAB)</span>
<span id="cb5-29">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.classifier <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Linear(DIM, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>)</span>
<span id="cb5-30"></span>
<span id="cb5-31">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> forward(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, picture, tokens):</span>
<span id="cb5-32">        memory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.encoder(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>(picture) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.audio_positions)</span>
<span id="cb5-33">        h <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.embed(tokens) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.text_positions[:, :tokens.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]]</span>
<span id="cb5-34">        mask <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Transformer.generate_square_subsequent_mask(tokens.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>])</span>
<span id="cb5-35">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.out(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.decoder(h, memory, tgt_mask<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>mask)), <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.classifier(memory.mean(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>))</span></code></pre></div></div>
</details>
</div>
<div id="cell-fig-arch" class="cell" data-execution_count="5">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb6-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> _arch <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> diagram</span>
<span id="cb6-2"></span>
<span id="cb6-3">diagram(Model(), input_shape<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>((<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, X.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], N_MELS), (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>)), style<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"flow"</span>,</span>
<span id="cb6-4">        input_dtype<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(torch.float32, torch.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">long</span>))</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-display" data-execution_count="5">
<div id="fig-arch" class="quarto-float quarto-figure quarto-figure-center anchored" alt="A diagram with two input streams converging through repeated transformer blocks into an output column.">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-arch-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://roshbeed.com/posts/2026-09-18-emotion-is-a-token/index_files/figure-html/fig-arch-output-1.png" class="img-fluid figure-img" alt="A diagram with two input streams converging through repeated transformer blocks into an output column.">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-arch-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;2: The shared model. Both designs are this; only the loss differs, and whether the emotion leaves through the decoder or through the small head hanging off the encoder.
</figcaption>
</figure>
</div>
</div>
</div>
<div id="9006cfcb" class="cell" data-execution_count="6">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb7-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> train(design, epochs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">45</span>):</span>
<span id="cb7-2">    torch.manual_seed(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb7-3">    model <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Model()</span>
<span id="cb7-4">    optimiser <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.optim.AdamW(model.parameters(), lr<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1e-3</span>, weight_decay<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.01</span>)</span>
<span id="cb7-5">    generator <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.Generator().manual_seed(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb7-6"></span>
<span id="cb7-7">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> _ <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(epochs):</span>
<span id="cb7-8">        perm <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.randperm(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(X), generator<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>generator)</span>
<span id="cb7-9">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(perm) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">64</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">64</span>):</span>
<span id="cb7-10">            b <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> perm[i:i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">64</span>]</span>
<span id="cb7-11">            target <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> TARGET[W[b]].clone()</span>
<span id="cb7-12">            target[:, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.tensor(EMOTION_TOKENS)[E[b]]</span>
<span id="cb7-13"></span>
<span id="cb7-14">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> design <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"token"</span>:</span>
<span id="cb7-15">                logits, _ <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> model(X[b], target[:, :<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>])</span>
<span id="cb7-16">                loss <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.functional.cross_entropy(logits.reshape(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, VOCAB),</span>
<span id="cb7-17">                                                   target[:, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>:].reshape(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>))</span>
<span id="cb7-18">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span>:</span>
<span id="cb7-19">                text <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.cat([target[:, :<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], target[:, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>:]], dim<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># emotion removed</span></span>
<span id="cb7-20">                logits, predicted <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> model(X[b], text[:, :<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>])</span>
<span id="cb7-21">                loss <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (nn.functional.cross_entropy(logits.reshape(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, VOCAB),</span>
<span id="cb7-22">                                                    text[:, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>:].reshape(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>))</span>
<span id="cb7-23">                        <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> nn.functional.cross_entropy(predicted, E[b]))</span>
<span id="cb7-24"></span>
<span id="cb7-25">            optimiser.zero_grad()</span>
<span id="cb7-26">            loss.backward()</span>
<span id="cb7-27">            optimiser.step()</span>
<span id="cb7-28">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> model</span>
<span id="cb7-29"></span>
<span id="cb7-30"></span>
<span id="cb7-31"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@torch.no_grad</span>()</span>
<span id="cb7-32"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> evaluate(model, design):</span>
<span id="cb7-33">    tokens <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.full((<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(X_test), <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>), START)</span>
<span id="cb7-34">    steps <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> design <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"token"</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span></span>
<span id="cb7-35">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> _ <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(steps):</span>
<span id="cb7-36">        tokens <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.cat([tokens, model(X_test, tokens)[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>][:, <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>].argmax(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, keepdim<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)], <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb7-37"></span>
<span id="cb7-38">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> design <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"token"</span>:</span>
<span id="cb7-39">        emitted <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> tokens[:, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]</span>
<span id="cb7-40">        emotion <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.tensor([EMOTION_TOKENS.index(i) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> EMOTION_TOKENS <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span>
<span id="cb7-41">                                <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> emitted.tolist()])</span>
<span id="cb7-42">        word <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (tokens[:, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>:<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> TARGET[W_test][:, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>:<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>]).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">all</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb7-43">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span>:</span>
<span id="cb7-44">        emotion <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> model(X_test, tokens[:, :<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>])[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>].argmax(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb7-45">        word <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (tokens[:, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>:<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> TARGET[W_test][:, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>:<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>]).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">all</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb7-46"></span>
<span id="cb7-47">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> (emotion <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> E_test).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>().mean().item(), word.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>().mean().item()</span>
<span id="cb7-48"></span>
<span id="cb7-49"></span>
<span id="cb7-50"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'design'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;26}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'emotion'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;9}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'word'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;9}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb7-51">results <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> {}</span>
<span id="cb7-52"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> design, name <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> ((<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"head"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"a classifier head"</span>), (<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"token"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"a token in the vocabulary"</span>)):</span>
<span id="cb7-53">    results[design] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> evaluate(train(design), design)</span>
<span id="cb7-54">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>name<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;26}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>results[design][<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;9.3f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>results[design][<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;9.3f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>                    design   emotion      word</code></pre>
</div>
<div class="cell-output cell-output-stdout">
<pre><code>         a classifier head     0.788     0.969</code></pre>
</div>
<div class="cell-output cell-output-stdout">
<pre><code> a token in the vocabulary     0.800     0.944</code></pre>
</div>
</div>
</section>
<section id="what-that-says" class="level2">
<h2 class="anchored" data-anchor-id="what-that-says">What that says</h2>
<p><strong>On the emotion itself, the two designs are a tie.</strong> Which is the answer I’d expect: the information is in the encoder either way, and both designs are reading the same encoder. Where you attach the readout doesn’t change what there is to read.</p>
<p><strong>The token design transcribes worse here, and that’s the real trade.</strong> A two-layer decoder now has to produce the emotion <em>and</em> spell the word, and it doesn’t have the capacity to do both as well as a decoder that only spells.</p>
<p>That cost is a small-model effect. Whisper’s decoder is much larger and already emits several control tokens before it writes anything, so one more costs it nothing noticeable. But it would have been easy to run this comparison at one size, see the token design lose on transcription, and conclude the design is worse — when what I’d actually measured is that my decoder was too small.</p>
<p><strong>The architectural argument survives the accuracy result.</strong> Adding a seventh emotion to the token design is a row in an embedding table. In the head design it’s a new output dimension and a checkpoint that no longer loads. And only the token design lets the transcription condition on what the model decided about the emotion, because only there is the emotion part of the same sequence.</p>
<p>The full project is <a href="https://github.com/RoshBeed/ai-residency/tree/main/services/speech-emotion-recognition">on GitHub</a>.</p>
<div id="19690e13" class="cell" data-execution_count="7">
<div class="cell-output cell-output-stdout">
<pre><code>Built:      2026-09-18 02:17:12 UTC
Python:     3.13.15
matplotlib: 3.11.2
numpy:      2.5.3
torch:      2.14.0+cpu</code></pre>
</div>
</div>


</section>

 ]]></description>
  <category>audio</category>
  <category>speech</category>
  <category>architecture</category>
  <category>week-5</category>
  <guid>https://roshbeed.com/posts/2026-09-18-emotion-is-a-token/</guid>
  <pubDate>Fri, 18 Sep 2026 00:00:00 GMT</pubDate>
</item>
<item>
  <title>Teaching a model to know when it has finished</title>
  <dc:creator>Rosh Beed</dc:creator>
  <link>https://roshbeed.com/posts/2026-09-18-when-it-has-finished/</link>
  <description><![CDATA[ 




<p>The second half of week 3. The <a href="../2026-09-18-image-is-not-a-sequence/">classifier</a> looks at one digit and picks one of ten labels. This one looks at three digits side by side and has to read the number.</p>
<p>That sounds like a small step and it is a different problem. A classifier produces a fixed-size answer. Reading produces an <strong>ordered sequence of unknown length</strong> — and something has to decide where it ends.</p>
<p>Everything below is standard encoder–decoder transformer, from the original attention paper [1]. The reason to build it rather than read about it is that three separate mechanisms have to be in place before a single digit comes out right, and they’re easy to nod along to and hard to get correct.</p>
<section id="the-three-things-a-decoder-adds" class="level2">
<h2 class="anchored" data-anchor-id="the-three-things-a-decoder-adds">The three things a decoder adds</h2>
<p><strong>A start token.</strong> The decoder generates one position at a time, each conditioned on what came before. At the first step there is no “before”, so you feed it a token that means <em>begin</em>.</p>
<p><strong>A causal mask.</strong> During training the decoder sees the whole target sequence at once, for speed. But position 2 must not be allowed to look at position 3, or the model learns to read the answer it is being asked to predict, and then produces nothing useful at inference when the future genuinely isn’t there. The mask enforces that: each position attends only to itself and everything to its left.</p>
<p><strong>Cross-attention.</strong> The decoder needs to look at the image. Self-attention lets the output positions look at each other; cross-attention lets each output position query the encoder’s patch tokens and pull in what it needs.</p>
<p>And then an <strong>end token</strong>, so the model can say it’s done. That’s what makes the output length the model’s decision rather than a hyperparameter.</p>
<div id="cell-fig-mask" class="cell" data-execution_count="1">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> sys</span>
<span id="cb1-2"></span>
<span id="cb1-3">sys.path.insert(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".."</span>)</span>
<span id="cb1-4"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> matplotlib.pyplot <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> plt</span>
<span id="cb1-5"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> numpy <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> np</span>
<span id="cb1-6"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> torch</span>
<span id="cb1-7"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> _style <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> COLOURS, MUTED</span>
<span id="cb1-8"></span>
<span id="cb1-9">L <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span></span>
<span id="cb1-10">labels <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;start&gt;"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"digit 1"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"digit 2"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"digit 3"</span>]</span>
<span id="cb1-11">mask <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.tril(np.ones((L <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, L <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)))</span>
<span id="cb1-12"></span>
<span id="cb1-13">fig, ax <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> plt.subplots(figsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">4.4</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">4.0</span>))</span>
<span id="cb1-14">ax.imshow(mask, cmap<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Blues"</span>, vmin<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, vmax<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.7</span>)</span>
<span id="cb1-15">ax.set_xticks(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(L <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>), labels, rotation<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">45</span>, ha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"right"</span>)</span>
<span id="cb1-16">ax.set_yticks(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(L <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>), labels)</span>
<span id="cb1-17">ax.set_xlabel(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"can attend to"</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>)</span>
<span id="cb1-18">ax.set_ylabel(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"generating"</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>)</span>
<span id="cb1-19">ax.tick_params(colors<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, labelsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>, length<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb1-20"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> s <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> ax.spines.values():</span>
<span id="cb1-21">    s.set_visible(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>)</span>
<span id="cb1-22">fig.tight_layout()</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-display">
<div id="fig-mask" class="quarto-float quarto-figure quarto-figure-center anchored" alt="A four-by-four grid where the lower triangle including the diagonal is filled and the upper triangle is blank.">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-mask-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://roshbeed.com/posts/2026-09-18-when-it-has-finished/index_files/figure-html/fig-mask-output-1.png" alt="A four-by-four grid where the lower triangle including the diagonal is filled and the upper triangle is blank." width="379" height="374" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-mask-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;1: The causal mask. A filled cell means that output position is allowed to attend to that one; the blank upper triangle is the future, hidden.
</figcaption>
</figure>
</div>
</div>
</div>
</section>
<section id="teacher-forcing-and-the-gap-it-leaves" class="level2">
<h2 class="anchored" data-anchor-id="teacher-forcing-and-the-gap-it-leaves">Teacher forcing, and the gap it leaves</h2>
<p>There’s a subtlety in how this gets trained that took me a while to appreciate.</p>
<p>During training the decoder is fed the <strong>true</strong> previous digits at every position. That’s called teacher forcing, and it’s what lets the whole sequence be computed in one pass instead of three.</p>
<p>At inference there are no true previous digits. The model is fed <strong>its own</strong> previous outputs. So if it gets digit 1 wrong, digit 2 is being predicted from a prefix that never occurred during training.</p>
<p>Which means the training loss systematically flatters the model, and the honest measure is generating the whole sequence the way you actually would — which is what <code>exact_sequence</code> below does.</p>
<div id="c7f5b293" class="cell" data-execution_count="2">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb2-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> torch.nn <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> nn</span>
<span id="cb2-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> huggingface_hub <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> hf_hub_download</span>
<span id="cb2-3"></span>
<span id="cb2-4">REVISION <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"f705fed08827ff6c36e3b5329495c943a5e544e8"</span></span>
<span id="cb2-5">data <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.load(hf_hub_download(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"roshbeed/ai-residency-blog-data"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"mnist/mnist-small.npz"</span>,</span>
<span id="cb2-6">                               repo_type<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"dataset"</span>, revision<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>REVISION))</span>
<span id="cb2-7"></span>
<span id="cb2-8">x_train <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.from_numpy(data[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"x_train"</span>]).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>() <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">255.0</span></span>
<span id="cb2-9">y_train <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.from_numpy(data[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"y_train"</span>]).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">long</span>()</span>
<span id="cb2-10">x_test <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.from_numpy(data[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"x_test"</span>]).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>() <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">255.0</span></span>
<span id="cb2-11">y_test <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.from_numpy(data[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"y_test"</span>]).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">long</span>()</span>
<span id="cb2-12"></span>
<span id="cb2-13">START, END <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">11</span>  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># two extra vocabulary entries alongside the ten digits</span></span>
<span id="cb2-14"></span>
<span id="cb2-15"></span>
<span id="cb2-16"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> compose(images, labels, n, seed):</span>
<span id="cb2-17">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""Glue three random digits side by side into one 28x84 image."""</span></span>
<span id="cb2-18">    rng <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.random.default_rng(seed)</span>
<span id="cb2-19">    pick <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> rng.integers(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(images), (n, L))</span>
<span id="cb2-20">    wide <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.cat([images[pick[:, i]] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(L)], dim<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb2-21">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> wide, torch.stack([labels[pick[:, i]] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(L)], dim<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb2-22"></span>
<span id="cb2-23"></span>
<span id="cb2-24">X, Y <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> compose(x_train, y_train, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">20_000</span>, seed<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb2-25">X_test, Y_test <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> compose(x_test, y_test, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2_000</span>, seed<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb2-26"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(X)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:,}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> composites of shape </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>(X.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>:])<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">, targets of length </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>L<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb2-27"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"guessing three digits uniformly: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1000</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.4f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> exact-sequence accuracy"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>20,000 composites of shape (28, 84), targets of length 3
guessing three digits uniformly: 0.0010 exact-sequence accuracy</code></pre>
</div>
</div>
<div id="e0ee19b7" class="cell" data-execution_count="3">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb4-1">PATCH, DIM, HEADS, LAYERS <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">7</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">96</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span></span>
<span id="cb4-2">GRID <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">28</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">//</span> PATCH, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">84</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">//</span> PATCH)          <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 4 rows, 12 columns of patches</span></span>
<span id="cb4-3">PATCHES <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> GRID[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> GRID[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]</span>
<span id="cb4-4"></span>
<span id="cb4-5"></span>
<span id="cb4-6"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> patchify(images):</span>
<span id="cb4-7">    batch <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> images.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]</span>
<span id="cb4-8">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> images.unfold(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, PATCH, PATCH).unfold(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, PATCH, PATCH).reshape(batch, PATCHES, PATCH<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb4-9"></span>
<span id="cb4-10"></span>
<span id="cb4-11"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> EncoderBlock(nn.Module):</span>
<span id="cb4-12">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>):</span>
<span id="cb4-13">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">super</span>().<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>()</span>
<span id="cb4-14">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.attention <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.MultiheadAttention(DIM, HEADS, batch_first<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span>
<span id="cb4-15">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.norm1, <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.norm2 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.LayerNorm(DIM), nn.LayerNorm(DIM)</span>
<span id="cb4-16">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.ff <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Sequential(nn.Linear(DIM, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> DIM), nn.GELU(), nn.Linear(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> DIM, DIM))</span>
<span id="cb4-17"></span>
<span id="cb4-18">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> forward(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, x):</span>
<span id="cb4-19">        x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.norm1(x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.attention(x, x, x, need_weights<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>)[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>])</span>
<span id="cb4-20">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.norm2(x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.ff(x))</span>
<span id="cb4-21"></span>
<span id="cb4-22"></span>
<span id="cb4-23"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> DecoderBlock(nn.Module):</span>
<span id="cb4-24">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""Masked self-attention, then cross-attention onto the image, then a feed-forward."""</span></span>
<span id="cb4-25"></span>
<span id="cb4-26">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>):</span>
<span id="cb4-27">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">super</span>().<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>()</span>
<span id="cb4-28">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.self_attention <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.MultiheadAttention(DIM, HEADS, batch_first<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span>
<span id="cb4-29">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.cross_attention <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.MultiheadAttention(DIM, HEADS, batch_first<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span>
<span id="cb4-30">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.norm1, <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.norm2, <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.norm3 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (nn.LayerNorm(DIM) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> _ <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>))</span>
<span id="cb4-31">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.ff <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Sequential(nn.Linear(DIM, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> DIM), nn.GELU(), nn.Linear(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> DIM, DIM))</span>
<span id="cb4-32"></span>
<span id="cb4-33">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> forward(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, x, memory, mask, want_weights<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>):</span>
<span id="cb4-34">        x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.norm1(x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.self_attention(x, x, x, attn_mask<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>mask, need_weights<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>)[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>])</span>
<span id="cb4-35">        attended, weights <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.cross_attention(x, memory, memory,</span>
<span id="cb4-36">                                                 need_weights<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>want_weights,</span>
<span id="cb4-37">                                                 average_attn_weights<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span>
<span id="cb4-38">        x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.norm2(x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> attended)</span>
<span id="cb4-39">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.norm3(x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.ff(x)), weights</span></code></pre></div></div>
</details>
</div>
<div id="ee411ba9" class="cell" data-execution_count="4">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb5-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> Reader(nn.Module):</span>
<span id="cb5-2">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>):</span>
<span id="cb5-3">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">super</span>().<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>()</span>
<span id="cb5-4">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.project <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Linear(PATCH<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, DIM)</span>
<span id="cb5-5">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.image_positions <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Parameter(torch.randn(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, PATCHES, DIM) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.02</span>)</span>
<span id="cb5-6">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.encoder <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.ModuleList([EncoderBlock() <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> _ <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(LAYERS)])</span>
<span id="cb5-7"></span>
<span id="cb5-8">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.embed <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Embedding(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>, DIM)   <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ten digits, &lt;start&gt;, &lt;end&gt;</span></span>
<span id="cb5-9">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.token_positions <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Parameter(torch.randn(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, L <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, DIM) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.02</span>)</span>
<span id="cb5-10">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.decoder <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.ModuleList([DecoderBlock() <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> _ <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(LAYERS)])</span>
<span id="cb5-11">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.out <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Linear(DIM, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>)</span>
<span id="cb5-12"></span>
<span id="cb5-13">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> encode(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, images):</span>
<span id="cb5-14">        h <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.project(patchify(images)) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.image_positions</span>
<span id="cb5-15">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> block <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.encoder:</span>
<span id="cb5-16">            h <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> block(h)</span>
<span id="cb5-17">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> h</span>
<span id="cb5-18"></span>
<span id="cb5-19">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> decode(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, memory, tokens, want_weights<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>):</span>
<span id="cb5-20">        h <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.embed(tokens) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.token_positions[:, :tokens.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]]</span>
<span id="cb5-21">        mask <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.triu(torch.full((tokens.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>],) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"-inf"</span>)), diagonal<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb5-22"></span>
<span id="cb5-23">        weights <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span></span>
<span id="cb5-24">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i, block <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">enumerate</span>(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.decoder):</span>
<span id="cb5-25">            h, w <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> block(h, memory, mask, want_weights <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">and</span> i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> LAYERS <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb5-26">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> w <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">is</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>:</span>
<span id="cb5-27">                weights <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> w</span>
<span id="cb5-28">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.out(h), weights</span>
<span id="cb5-29"></span>
<span id="cb5-30">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> forward(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, images, tokens):</span>
<span id="cb5-31">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.decode(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.encode(images), tokens)[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]</span></code></pre></div></div>
</details>
</div>
<p>Both halves together. The image enters top left and the tokens enter bottom left through their embedding, and the two streams meet in the decoder:</p>
<div id="cell-fig-arch" class="cell" data-execution_count="5">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb6-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> _arch <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> diagram</span>
<span id="cb6-2"></span>
<span id="cb6-3">diagram(Reader(), input_shape<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>((<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">28</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">84</span>), (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, L <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)), style<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"flow"</span>,</span>
<span id="cb6-4">        input_dtype<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(torch.float32, torch.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">long</span>))</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-display" data-execution_count="5">
<div id="fig-arch" class="quarto-float quarto-figure quarto-figure-center anchored" alt="A diagram with two input streams on the left, one from an image and one through an embedding, running through repeated blocks and converging into a single column on the right.">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-arch-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://roshbeed.com/posts/2026-09-18-when-it-has-finished/index_files/figure-html/fig-arch-output-1.png" class="img-fluid figure-img" alt="A diagram with two input streams on the left, one from an image and one through an embedding, running through repeated blocks and converging into a single column on the right.">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-arch-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;2: The encoder-decoder. Two inputs, two stacks, joined by the decoder’s cross-attention.
</figcaption>
</figure>
</div>
</div>
</div>
<div id="0c7aaa1c" class="cell" data-execution_count="6">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb7-1">torch.manual_seed(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb7-2">model <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Reader()</span>
<span id="cb7-3">optimiser <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.optim.AdamW(model.parameters(), lr<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1e-3</span>, weight_decay<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.01</span>)</span>
<span id="cb7-4">generator <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.Generator().manual_seed(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb7-5"></span>
<span id="cb7-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># teacher forcing: the decoder is fed &lt;start&gt; d1 d2 and must produce d1 d2 d3,</span></span>
<span id="cb7-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># then &lt;end&gt;.</span></span>
<span id="cb7-8">decoder_input <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.cat([torch.full((<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(Y), <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>), START), Y], dim<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb7-9">decoder_target <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.cat([Y, torch.full((<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(Y), <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>), END)], dim<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb7-10"></span>
<span id="cb7-11"></span>
<span id="cb7-12"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@torch.no_grad</span>()</span>
<span id="cb7-13"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> exact_sequence():</span>
<span id="cb7-14">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""Generate one digit at a time from the model's own output, as at inference."""</span></span>
<span id="cb7-15">    tokens <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.full((<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(X_test), <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>), START)</span>
<span id="cb7-16">    memory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> model.encode(X_test)</span>
<span id="cb7-17">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> _ <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(L):</span>
<span id="cb7-18">        nxt <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> model.decode(memory, tokens)[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>][:, <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>].argmax(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, keepdim<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span>
<span id="cb7-19">        tokens <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.cat([tokens, nxt], dim<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb7-20">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> (tokens[:, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>:] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> Y_test).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">all</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>().mean().item()</span>
<span id="cb7-21"></span>
<span id="cb7-22"></span>
<span id="cb7-23"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>(p.numel() <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> p <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> model.parameters())<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:,}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> parameters"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>534,348 parameters</code></pre>
</div>
</div>
<div id="711c5567" class="cell" data-execution_count="7">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb9-1">history <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []</span>
<span id="cb9-2"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> epoch <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>):</span>
<span id="cb9-3">    perm <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.randperm(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(X), generator<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>generator)</span>
<span id="cb9-4">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(perm) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">128</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">128</span>):</span>
<span id="cb9-5">        b <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> perm[i:i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">128</span>]</span>
<span id="cb9-6">        logits <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> model(X[b], decoder_input[b])</span>
<span id="cb9-7">        loss <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.functional.cross_entropy(logits.reshape(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>), decoder_target[b].reshape(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>))</span>
<span id="cb9-8">        optimiser.zero_grad()</span>
<span id="cb9-9">        loss.backward()</span>
<span id="cb9-10">        optimiser.step()</span>
<span id="cb9-11">    history.append(exact_sequence())</span>
<span id="cb9-12">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"epoch </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>epoch <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">: all three digits correct on </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>history[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.1%}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> of held-out images"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>epoch 1: all three digits correct on 39.4% of held-out images
epoch 2: all three digits correct on 77.2% of held-out images
epoch 3: all three digits correct on 83.0% of held-out images
epoch 4: all three digits correct on 85.8% of held-out images
epoch 5: all three digits correct on 85.4% of held-out images
epoch 6: all three digits correct on 86.3% of held-out images
epoch 7: all three digits correct on 87.1% of held-out images
epoch 8: all three digits correct on 87.3% of held-out images</code></pre>
</div>
</div>
</section>
<section id="the-thing-worth-looking-at" class="level2">
<h2 class="anchored" data-anchor-id="the-thing-worth-looking-at">The thing worth looking at</h2>
<p>Accuracy isn’t the interesting output here. What’s interesting is that <strong>nobody told this model the digits run left to right.</strong></p>
<p>The encoder produces 48 patch tokens in a 4×12 grid. The decoder generates three outputs. Which patches does each output position attend to?</p>
<div id="cell-fig-cross" class="cell" data-execution_count="8">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb11" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb11-1"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">with</span> torch.no_grad():</span>
<span id="cb11-2">    memory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> model.encode(X_test[:<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>])</span>
<span id="cb11-3">    tokens <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.cat([torch.full((<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>), START), Y_test[:<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]], dim<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb11-4">    _, weights <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> model.decode(memory, tokens, want_weights<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span>
<span id="cb11-5"></span>
<span id="cb11-6">attention <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> weights[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>].reshape(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span>GRID)   <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># one map per output position</span></span>
<span id="cb11-7"></span>
<span id="cb11-8">fig, axes <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> plt.subplots(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, figsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">7.4</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">4.2</span>))</span>
<span id="cb11-9"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> position, ax <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">enumerate</span>(axes):</span>
<span id="cb11-10">    ax.imshow(X_test[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>], cmap<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"gray_r"</span>)</span>
<span id="cb11-11">    ax.imshow(attention[position], cmap<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"inferno"</span>, alpha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.45</span>,</span>
<span id="cb11-12">              extent<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">84</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">28</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>), interpolation<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bilinear"</span>)</span>
<span id="cb11-13">    ax.set_ylabel(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"digit </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>position <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>, rotation<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>,</span>
<span id="cb11-14">                  ha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"right"</span>, va<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"center"</span>, labelpad<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>)</span>
<span id="cb11-15">    ax.set_xticks([])<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> ax.set_yticks([])</span>
<span id="cb11-16">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> s <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> ax.spines.values():</span>
<span id="cb11-17">        s.set_visible(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>)</span>
<span id="cb11-18">fig.tight_layout()</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-display">
<div id="fig-cross" class="quarto-float quarto-figure quarto-figure-center anchored" alt="Three copies of the same wide three-digit image, each with a heat overlay. The first is bright over the leftmost digit, the second over the middle digit, the third over the rightmost.">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-cross-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://roshbeed.com/posts/2026-09-18-when-it-has-finished/index_files/figure-html/fig-cross-output-1.png" alt="Three copies of the same wide three-digit image, each with a heat overlay. The first is bright over the leftmost digit, the second over the middle digit, the third over the rightmost." width="419" height="394" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-cross-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;3: Cross-attention from each output position onto the image patches. The alignment is learned, not specified.
</figcaption>
</figure>
</div>
</div>
</div>
<div id="fb0abad9" class="cell" data-execution_count="9">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb12" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb12-1"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"share of each output's attention falling on each third of the image</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb12-2"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">''</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:10}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'left'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;8}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'middle'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;8}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'right'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;8}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb12-3"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> position <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(L):</span>
<span id="cb12-4">    by_column <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> attention[position].<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb12-5">    thirds <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [by_column[i:i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>].<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>() <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> by_column.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>() <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>)]</span>
<span id="cb12-6">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"digit </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>position <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&lt;4}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> "</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span>.join(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>t<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;8.2f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> t <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> thirds))</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>share of each output's attention falling on each third of the image

               left   middle    right
digit 1        0.96    0.04    0.00
digit 2        0.02    0.96    0.02
digit 3        0.00    0.02    0.98</code></pre>
</div>
</div>
<p>Each output position learns to look at its own third of the image. The loss never mentions position — it only ever says <em>the first digit is a 7</em>. The alignment between output order and image geometry is something the model works out because it’s the only way to get the answer right.</p>
<p>This is the same mechanism that makes translation work, where the alignment is between words in two languages rather than positions in an image, and it’s the reason cross-attention replaced the fixed-size context vector that encoder–decoders used before it.</p>
</section>
<section id="what-id-take-from-this-one" class="level2">
<h2 class="anchored" data-anchor-id="what-id-take-from-this-one">What I’d take from this one</h2>
<p>The decoder is not a bigger classifier. It’s a different contract: the model commits to one token, then conditions on its own commitment, and the errors compound. Teacher forcing hides that during training, which is why the number that matters is generated the slow way.</p>
<p>This project is at the stage where the plumbing is verified rather than the accuracy tuned, so I’d treat the numbers above as a demonstration that the mechanism works, not as a result.</p>
<p>The full project is <a href="https://github.com/RoshBeed/ai-residency/tree/main/services/vit-seq2seq">on GitHub</a>.</p>
<hr>
<p>[1] Vaswani et al.&nbsp;<em>Attention Is All You Need.</em> NeurIPS 2017.</p>
<div id="ab9b1e48" class="cell" data-execution_count="10">
<div class="cell-output cell-output-stdout">
<pre><code>Built:      2026-09-18 02:19:27 UTC
Python:     3.13.15
matplotlib: 3.11.2
numpy:      2.5.3
torch:      2.14.0+cpu</code></pre>
</div>
</div>


</section>

 ]]></description>
  <category>vision</category>
  <category>transformers</category>
  <category>week-3</category>
  <guid>https://roshbeed.com/posts/2026-09-18-when-it-has-finished/</guid>
  <pubDate>Fri, 18 Sep 2026 00:00:00 GMT</pubDate>
</item>
<item>
  <title>Freeze both ends and train the middle</title>
  <dc:creator>Rosh Beed</dc:creator>
  <link>https://roshbeed.com/posts/2026-09-18-freeze-both-ends/</link>
  <description><![CDATA[ 




<p>Week 4: image captioning. Look at a picture, write a sentence about it.</p>
<p>The obvious approach is to train one model on image–caption pairs. The approach that actually gets used is stranger and much cheaper, and it’s what LLaVA [1] and PaLiGemma popularised:</p>
<ol type="1">
<li>Take a vision encoder that already understands images. Freeze it.</li>
<li>Take a language model that already writes English. Freeze it.</li>
<li>Train a small adapter that turns the vision model’s output into something the language model will accept as input.</li>
</ol>
<p>Nothing in either big model ever receives a gradient. The project version used CLIP on one side and Qwen3-0.6B on the other, and only ~1.58M adapter parameters were trainable.</p>
<p>The bet is that the two models already contain everything needed, and what’s missing is <strong>a translation between two representation spaces that were never trained together.</strong></p>
<section id="why-that-bet-is-reasonable" class="level2">
<h2 class="anchored" data-anchor-id="why-that-bet-is-reasonable">Why that bet is reasonable</h2>
<p>A vision encoder’s output is a vector that means something — but it means something <em>in the vision model’s coordinate system</em>, which was shaped by a completely different training run.</p>
<p>The language model reads token embeddings, which live in their own coordinate system with their own geometry. It has no reason to interpret a CLIP vector as anything at all.</p>
<p>The adapter’s job is not to understand images or to write English. It’s to land the image’s vector somewhere in the language model’s embedding space that makes the language model say the right thing. That’s a much smaller job, which is why a small matrix can do it.</p>
</section>
<section id="building-both-ends-from-scratch-small" class="level2">
<h2 class="anchored" data-anchor-id="building-both-ends-from-scratch-small">Building both ends from scratch, small</h2>
<p>To show the recipe honestly I need two models that genuinely have never met. So I train both here, from scratch, on separate tasks, then freeze them.</p>
<p><strong>The vision model</strong> learns to classify MNIST digits. Its hidden layer becomes the “image embedding”.</p>
<p><strong>The language model</strong> is a character-level transformer trained on exactly one sentence pattern — <code>the digit is &lt;word&gt;</code> — and nothing else. It knows the template and it knows the ten words. It has never seen an image.</p>
<div id="ab411fb2" class="cell" data-execution_count="1">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> sys</span>
<span id="cb1-2"></span>
<span id="cb1-3">sys.path.insert(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".."</span>)</span>
<span id="cb1-4"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> numpy <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> np</span>
<span id="cb1-5"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> torch</span>
<span id="cb1-6"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> torch.nn <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> nn</span>
<span id="cb1-7"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> huggingface_hub <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> hf_hub_download</span>
<span id="cb1-8"></span>
<span id="cb1-9">REVISION <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"f705fed08827ff6c36e3b5329495c943a5e544e8"</span></span>
<span id="cb1-10">data <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.load(hf_hub_download(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"roshbeed/ai-residency-blog-data"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"mnist/mnist-small.npz"</span>,</span>
<span id="cb1-11">                               repo_type<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"dataset"</span>, revision<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>REVISION))</span>
<span id="cb1-12"></span>
<span id="cb1-13">x_train <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.from_numpy(data[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"x_train"</span>]).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>() <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">255.0</span></span>
<span id="cb1-14">y_train <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.from_numpy(data[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"y_train"</span>]).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">long</span>()</span>
<span id="cb1-15">x_test <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.from_numpy(data[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"x_test"</span>]).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>() <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">255.0</span></span>
<span id="cb1-16">y_test <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.from_numpy(data[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"y_test"</span>]).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">long</span>()</span>
<span id="cb1-17"></span>
<span id="cb1-18">WORDS <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"zero"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"one"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"two"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"three"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"four"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"five"</span>,</span>
<span id="cb1-19">         <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"six"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"seven"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"eight"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"nine"</span>]</span>
<span id="cb1-20"></span>
<span id="cb1-21">characters <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sorted</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">set</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span>.join(WORDS) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">" the digit is "</span>))</span>
<span id="cb1-22">VOCAB <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;bos&gt;"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;eos&gt;"</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> characters</span>
<span id="cb1-23">INDEX <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> {c: i <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i, c <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">enumerate</span>(VOCAB)}</span>
<span id="cb1-24"></span>
<span id="cb1-25"></span>
<span id="cb1-26"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> encode(sentence):</span>
<span id="cb1-27">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> [INDEX[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;bos&gt;"</span>]] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> [INDEX[c] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> c <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> sentence] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> [INDEX[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;eos&gt;"</span>]]</span>
<span id="cb1-28"></span>
<span id="cb1-29"></span>
<span id="cb1-30">captions <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [encode(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"the digit is </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>w<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> w <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> WORDS]</span>
<span id="cb1-31">LENGTH <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">max</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(c) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> c <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> captions)</span>
<span id="cb1-32">CAPTIONS <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.tensor([c <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> [INDEX[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;eos&gt;"</span>]] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> (LENGTH <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(c)) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> c <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> captions])</span>
<span id="cb1-33"></span>
<span id="cb1-34"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(VOCAB)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> vocabulary entries, captions </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>LENGTH<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> tokens long"</span>)</span>
<span id="cb1-35"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"example: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">''</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>join(VOCAB[i] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> CAPTIONS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">7</span>] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> VOCAB[i] <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> (<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'&lt;bos&gt;'</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'&lt;eos&gt;'</span>))<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>19 vocabulary entries, captions 20 tokens long
example: the digit is seven</code></pre>
</div>
</div>
<div id="dee4a8d3" class="cell" data-execution_count="2">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb3-1">DIM, PREFIX <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">64</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span></span>
<span id="cb3-2"></span>
<span id="cb3-3"></span>
<span id="cb3-4"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> VisionModel(nn.Module):</span>
<span id="cb3-5">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""Trained to classify digits. Its hidden layer is the image embedding."""</span></span>
<span id="cb3-6"></span>
<span id="cb3-7">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>):</span>
<span id="cb3-8">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">super</span>().<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>()</span>
<span id="cb3-9">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.trunk <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Sequential(nn.Flatten(), nn.Linear(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">784</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">128</span>), nn.ReLU(),</span>
<span id="cb3-10">                                   nn.Linear(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">128</span>, DIM), nn.ReLU())</span>
<span id="cb3-11">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.classifier <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Linear(DIM, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>)</span>
<span id="cb3-12"></span>
<span id="cb3-13">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> forward(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, images):</span>
<span id="cb3-14">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.trunk(images)</span>
<span id="cb3-15"></span>
<span id="cb3-16"></span>
<span id="cb3-17">torch.manual_seed(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb3-18">vision <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> VisionModel()</span>
<span id="cb3-19">optimiser <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.optim.Adam(vision.parameters(), lr<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1e-3</span>)</span>
<span id="cb3-20">generator <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.Generator().manual_seed(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb3-21"></span>
<span id="cb3-22"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> _ <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>):</span>
<span id="cb3-23">    perm <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.randperm(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(x_train), generator<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>generator)</span>
<span id="cb3-24">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(perm) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">128</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">128</span>):</span>
<span id="cb3-25">        b <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> perm[i:i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">128</span>]</span>
<span id="cb3-26">        loss <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.functional.cross_entropy(vision.classifier(vision(x_train[b])), y_train[b])</span>
<span id="cb3-27">        optimiser.zero_grad()</span>
<span id="cb3-28">        loss.backward()</span>
<span id="cb3-29">        optimiser.step()</span>
<span id="cb3-30"></span>
<span id="cb3-31"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">with</span> torch.no_grad():</span>
<span id="cb3-32">    VISION_CEILING <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (vision.classifier(vision(x_test)).argmax(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> y_test).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>().mean().item()</span>
<span id="cb3-33"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"the vision model can classify digits at </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>VISION_CEILING<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.4f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>the vision model can classify digits at 0.9307</code></pre>
</div>
</div>
<div id="9449f00a" class="cell" data-execution_count="3">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb5-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> LanguageModel(nn.Module):</span>
<span id="cb5-2">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""A causal character LM. It always has PREFIX slots in front of the text —</span></span>
<span id="cb5-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    filled with a learned null during pretraining, and by the adapter later, so</span></span>
<span id="cb5-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    the positions never shift out from under the frozen weights."""</span></span>
<span id="cb5-5"></span>
<span id="cb5-6">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>):</span>
<span id="cb5-7">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">super</span>().<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>()</span>
<span id="cb5-8">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.embed <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Embedding(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(VOCAB), DIM)</span>
<span id="cb5-9">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.positions <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Parameter(torch.randn(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, LENGTH <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> PREFIX, DIM) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.02</span>)</span>
<span id="cb5-10">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.null <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Parameter(torch.zeros(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, PREFIX, DIM))</span>
<span id="cb5-11">        layer <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.TransformerEncoderLayer(DIM, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> DIM, batch_first<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>,</span>
<span id="cb5-12">                                           norm_first<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>, dropout<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.0</span>)</span>
<span id="cb5-13">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.stack <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.TransformerEncoder(layer, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb5-14">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.out <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Linear(DIM, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(VOCAB))</span>
<span id="cb5-15"></span>
<span id="cb5-16">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> forward(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, tokens, prefix<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>):</span>
<span id="cb5-17">        front <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.null.expand(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(tokens), <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> prefix <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">is</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> prefix</span>
<span id="cb5-18">        h <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.cat([front, <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.embed(tokens)], dim<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb5-19">        h <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> h <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.positions[:, :h.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]]</span>
<span id="cb5-20">        mask <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Transformer.generate_square_subsequent_mask(h.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>])</span>
<span id="cb5-21">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.out(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.stack(h, mask<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>mask, is_causal<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>))</span>
<span id="cb5-22"></span>
<span id="cb5-23"></span>
<span id="cb5-24">torch.manual_seed(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb5-25">language <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> LanguageModel()</span>
<span id="cb5-26">optimiser <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.optim.Adam(language.parameters(), lr<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">3e-3</span>)</span>
<span id="cb5-27"></span>
<span id="cb5-28"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> _ <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1500</span>):</span>
<span id="cb5-29">    batch <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> CAPTIONS[torch.randint(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>, (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">64</span>,))]</span>
<span id="cb5-30">    logits <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> language(batch[:, :<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>])[:, PREFIX:]</span>
<span id="cb5-31">    loss <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.functional.cross_entropy(logits.reshape(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(VOCAB)), batch[:, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>:].reshape(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>))</span>
<span id="cb5-32">    optimiser.zero_grad()</span>
<span id="cb5-33">    loss.backward()</span>
<span id="cb5-34">    optimiser.step()</span>
<span id="cb5-35"></span>
<span id="cb5-36"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"the language model writes the sentence pattern, final loss </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>loss<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>item()<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.4f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>the language model writes the sentence pattern, final loss 0.1208</code></pre>
</div>
</div>
<p>Now freeze both, and put a small adapter between them. Only the adapter gets an optimiser.</p>
<div id="b2899982" class="cell" data-execution_count="4">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb7-1"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> p <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> vision.parameters():</span>
<span id="cb7-2">    p.requires_grad_(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>)</span>
<span id="cb7-3"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> p <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> language.parameters():</span>
<span id="cb7-4">    p.requires_grad_(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>)</span>
<span id="cb7-5"></span>
<span id="cb7-6">adapter <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Sequential(nn.Linear(DIM, DIM), nn.GELU(), nn.Linear(DIM, PREFIX <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> DIM))</span>
<span id="cb7-7"></span>
<span id="cb7-8">trainable <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>(p.numel() <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> p <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> adapter.parameters())</span>
<span id="cb7-9">frozen <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>(p.numel() <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> p <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> vision.parameters()) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>(p.numel() <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> p <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> language.parameters())</span>
<span id="cb7-10"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"trainable (adapter): </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>trainable<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:,}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb7-11"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"frozen (both models): </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>frozen<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:,}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb7-12"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"the adapter is </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> trainable <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> (trainable <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> frozen)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.0f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">% of the whole thing"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>trainable (adapter): 37,440
frozen (both models): 214,109
the adapter is 15% of the whole thing</code></pre>
</div>
</div>
<div id="cell-fig-adapter-shape" class="cell" data-execution_count="5">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb9-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> _arch <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> diagram</span>
<span id="cb9-2"></span>
<span id="cb9-3">diagram(adapter, input_shape<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, DIM))</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-display" data-execution_count="5">
<div id="fig-adapter-shape" class="quarto-float quarto-figure quarto-figure-center anchored" alt="A four-column neural network diagram widening sharply at the output, from 64 inputs to 512.">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-adapter-shape-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://roshbeed.com/posts/2026-09-18-freeze-both-ends/index_files/figure-html/fig-adapter-shape-output-1.png" class="img-fluid figure-img" alt="A four-column neural network diagram widening sharply at the output, from 64 inputs to 512.">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-adapter-shape-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;1: The entire trainable part. It takes the vision model’s 64-number summary and produces eight 64-number vectors for the language model to read as if they were text.
</figcaption>
</figure>
</div>
</div>
</div>
<div id="52cb0f9f" class="cell" data-execution_count="6">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb10" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb10-1"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@torch.no_grad</span>()</span>
<span id="cb10-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> caption(n<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1000</span>):</span>
<span id="cb10-3">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""Generate a caption one character at a time and check the word at the end."""</span></span>
<span id="cb10-4">    prefix <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> adapter(vision(x_test[:n])).view(n, PREFIX, DIM)</span>
<span id="cb10-5">    tokens <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.full((n, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>), INDEX[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;bos&gt;"</span>])</span>
<span id="cb10-6">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> _ <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(LENGTH):</span>
<span id="cb10-7">        nxt <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> language(tokens, prefix)[:, <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>].argmax(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, keepdim<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span>
<span id="cb10-8">        tokens <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.cat([tokens, nxt], dim<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb10-9"></span>
<span id="cb10-10">    text <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span>.join(VOCAB[i] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> row[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>:LENGTH] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> VOCAB[i] <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> (<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;bos&gt;"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;eos&gt;"</span>))</span>
<span id="cb10-11">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> row <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> tokens]</span>
<span id="cb10-12">    correct <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>(t.strip().endswith(WORDS[y]) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> t, y <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">zip</span>(text, y_test[:n].tolist()))</span>
<span id="cb10-13">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> correct <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> n, text</span>
<span id="cb10-14"></span>
<span id="cb10-15"></span>
<span id="cb10-16">before, examples <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> caption()</span>
<span id="cb10-17"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"before training the adapter: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>before<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.4f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb10-18"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"  it still writes fluent English: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>examples[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!r}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb10-19"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"  it just has no idea which digit, so it guesses the same word every time"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>before training the adapter: 0.0000
  it still writes fluent English: 'the digit is fo'
  it just has no idea which digit, so it guesses the same word every time</code></pre>
</div>
</div>
<div id="6353e15a" class="cell" data-execution_count="7">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb12" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb12-1">optimiser <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.optim.Adam(adapter.parameters(), lr<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1e-3</span>)</span>
<span id="cb12-2">history <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []</span>
<span id="cb12-3"></span>
<span id="cb12-4"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> epoch <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">24</span>):</span>
<span id="cb12-5">    perm <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.randperm(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(x_train), generator<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>generator)</span>
<span id="cb12-6">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(perm) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">128</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">128</span>):</span>
<span id="cb12-7">        b <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> perm[i:i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">128</span>]</span>
<span id="cb12-8">        prefix <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> adapter(vision(x_train[b])).view(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(b), PREFIX, DIM)</span>
<span id="cb12-9">        target <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> CAPTIONS[y_train[b]]</span>
<span id="cb12-10">        logits <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> language(target[:, :<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], prefix)[:, PREFIX:]</span>
<span id="cb12-11">        loss <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.functional.cross_entropy(logits.reshape(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(VOCAB)),</span>
<span id="cb12-12">                                           target[:, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>:].reshape(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>))</span>
<span id="cb12-13">        optimiser.zero_grad()</span>
<span id="cb12-14">        loss.backward()</span>
<span id="cb12-15">        optimiser.step()</span>
<span id="cb12-16">    history.append(caption()[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>])</span>
<span id="cb12-17"></span>
<span id="cb12-18">accuracy, examples <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> caption()</span>
<span id="cb12-19"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"after training only the adapter: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>accuracy<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.4f}</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb12-20"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> text, truth <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">zip</span>(examples[:<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>], y_test[:<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>].tolist()):</span>
<span id="cb12-21">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"  </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>text<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!r}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">   (actually a </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>WORDS[truth]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">)"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>after training only the adapter: 0.9190

  'the digit is six'   (actually a six)
  'the digit is three'   (actually a three)
  'the digit is three'   (actually a three)
  'the digit is eight'   (actually a eight)
  'the digit is nine'   (actually a nine)</code></pre>
</div>
</div>
<div id="cell-fig-adapter" class="cell" data-execution_count="8">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb14" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb14-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> _style <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> COLOURS, MUTED, figure, style_axes</span>
<span id="cb14-2"></span>
<span id="cb14-3">epochs <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(history) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb14-4">fig, ax <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> figure(height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">4.0</span>)</span>
<span id="cb14-5">ax.axhline(VISION_CEILING, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>], linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.2</span>, linestyle<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"--"</span>)</span>
<span id="cb14-6">ax.axhline(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.1</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.2</span>, linestyle<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"--"</span>)</span>
<span id="cb14-7">ax.plot(epochs, history, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>], linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb14-8"></span>
<span id="cb14-9">ax.annotate(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"what the frozen vision model knows"</span>, xy<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, VISION_CEILING), xytext<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>),</span>
<span id="cb14-10">            textcoords<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"offset points"</span>, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>])</span>
<span id="cb14-11">ax.annotate(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"guessing one of ten words"</span>, xy<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.1</span>), xytext<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>),</span>
<span id="cb14-12">            textcoords<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"offset points"</span>, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED)</span>
<span id="cb14-13">ax.set_ylim(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.0</span>)</span>
<span id="cb14-14">style_axes(ax, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Epoch (adapter only)"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Captions with the right digit"</span>)</span>
<span id="cb14-15">fig.tight_layout()</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-display">
<div id="fig-adapter" class="quarto-float quarto-figure quarto-figure-center anchored" alt="A curve rising from 0.1 to about 0.9 over 24 epochs, approaching a dashed ceiling line at 0.93, with a second dashed line at 0.1 marking chance.">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-adapter-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://roshbeed.com/posts/2026-09-18-freeze-both-ends/index_files/figure-html/fig-adapter-output-1.png" alt="A curve rising from 0.1 to about 0.9 over 24 epochs, approaching a dashed ceiling line at 0.93, with a second dashed line at 0.1 marking chance." width="659" height="373" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-adapter-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;2: The adapter climbing toward the frozen vision model’s own accuracy. It cannot go past it: an adapter translates information, it does not add any.
</figcaption>
</figure>
</div>
</div>
</div>
</section>
<section id="what-the-toy-actually-shows" class="level2">
<h2 class="anchored" data-anchor-id="what-the-toy-actually-shows">What the toy actually shows</h2>
<p><strong>The ceiling is the frozen encoder.</strong> The adapter gets close to the vision model’s own classification accuracy and stops there. It can’t do better, because it has no access to the image — only to what the vision model chose to keep. If the encoder threw something away, the adapter cannot recover it. That’s the real constraint of this recipe, and it’s why the choice of frozen encoder matters more than the adapter design.</p>
<p><strong>Fluency is free and grounding is not.</strong> Before the adapter is trained, the captions are already perfect English in the right format. The entire training run is spent on <em>which word</em>, because that’s the only thing the language model couldn’t already do.</p>
<p><strong>One prefix token wasn’t enough.</strong> My first version projected the image to a single embedding and it barely beat chance — the frozen language model couldn’t carry that one vector fifteen characters to where the word appears. Projecting to eight tokens fixed it. Real implementations use many image tokens, and now I know why rather than just that they do.</p>
<p><strong>The positions have to line up.</strong> The language model is pretrained with the prefix slots already present, filled by a learned null. Without that, inserting the image shifts every text token one position to the right, into positional embeddings the frozen model was never trained with — and it quietly fails.</p>
<p>The full project, with CLIP and Qwen3 in place of these two toys, is <a href="https://github.com/RoshBeed/ai-residency/tree/main/services/multimodal-captioning">on GitHub</a>.</p>
<hr>
<p>[1] Liu, Li, Wu, Lee. <em>Visual Instruction Tuning.</em> NeurIPS 2023.</p>
<div id="f884286d" class="cell" data-execution_count="9">
<div class="cell-output cell-output-stdout">
<pre><code>Built:      2026-09-18 02:20:34 UTC
Python:     3.13.15
matplotlib: 3.11.2
numpy:      2.5.3
torch:      2.14.0+cpu</code></pre>
</div>
</div>


</section>

 ]]></description>
  <category>multimodal</category>
  <category>vision</category>
  <category>language</category>
  <category>week-4</category>
  <guid>https://roshbeed.com/posts/2026-09-18-freeze-both-ends/</guid>
  <pubDate>Fri, 18 Sep 2026 00:00:00 GMT</pubDate>
</item>
<item>
  <title>A good title doesn’t add upvotes, it multiplies them</title>
  <dc:creator>Rosh Beed</dc:creator>
  <link>https://roshbeed.com/posts/2026-09-18-multiplies-them/</link>
  <description><![CDATA[ 




<p>The second project of week 1: given a Hacker News post — its title, where it links, when it was submitted and who by — predict how many upvotes it will get.</p>
<p>872,554 real posts, split by time so the model is always predicting a future it hasn’t seen. The served model’s median error is 10.1 upvotes against 16.2 for guessing the average every time.</p>
<p>But the number isn’t what made this project interesting. The interesting part was a design question I didn’t expect to have to think about: <strong>a post is two different kinds of thing at once</strong> — a piece of text, and a handful of numbers and categories. Where in the model should they meet?</p>
<section id="early-and-late-fusion" class="level2">
<h2 class="anchored" data-anchor-id="early-and-late-fusion">Early and late fusion</h2>
<p>The multimodal literature has names for the two ends of this. In Snoek et al.’s 2005 survey of video retrieval [1], which is where the terms come from:</p>
<p><strong>Early fusion</strong> glues the inputs together first and runs one model over the whole thing. The model can learn anything that depends on both — but it also has to learn the text and the metadata in the same layers, entangled from the start.</p>
<p><strong>Late fusion</strong> gives each input its own model, reduces each to a score, and combines the scores at the end. Clean, modular, and you can retrain one branch without touching the other.</p>
<p>Snoek’s objection to late fusion is the one that matters: by the time the two branches meet, each has been squeezed into a single number, so <strong>any correlation between the two in feature space is already gone</strong>.</p>
</section>
<section id="theyre-the-same-architecture-with-a-switch" class="level2">
<h2 class="anchored" data-anchor-id="theyre-the-same-architecture-with-a-switch">They’re the same architecture with a switch</h2>
<p>The thing that made this tractable for me was realising early and late fusion aren’t two designs. Late fusion is early fusion <strong>with some weights deleted</strong>.</p>
<p>Take one hidden layer over all the inputs concatenated. Early fusion lets every hidden unit see every input. Late fusion splits the units into groups and lets each group see only its own inputs — which is the same layer with the off-diagonal blocks zeroed.</p>
<p>Same inputs, same width, same depth, same head. The only difference is which weights are allowed to be non-zero.</p>
<div id="cell-fig-mask" class="cell" data-execution_count="1">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> sys</span>
<span id="cb1-2"></span>
<span id="cb1-3">sys.path.insert(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".."</span>)</span>
<span id="cb1-4"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> matplotlib.pyplot <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> plt</span>
<span id="cb1-5"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> numpy <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> np</span>
<span id="cb1-6"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> torch</span>
<span id="cb1-7"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> _style <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> COLOURS, MUTED</span>
<span id="cb1-8"></span>
<span id="cb1-9">D, H <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">64</span></span>
<span id="cb1-10"></span>
<span id="cb1-11"></span>
<span id="cb1-12"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> mask_for(kind):</span>
<span id="cb1-13">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""Which first-layer weights are allowed to be non-zero."""</span></span>
<span id="cb1-14">    mask <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.ones(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> D, H)</span>
<span id="cb1-15">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> kind <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"late"</span>:</span>
<span id="cb1-16">        mask[:D, H <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">//</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>:] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>   <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># the first half of the units sees group A only</span></span>
<span id="cb1-17">        mask[D:, :H <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">//</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>   <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># the second half sees group B only</span></span>
<span id="cb1-18">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> mask</span>
<span id="cb1-19"></span>
<span id="cb1-20"></span>
<span id="cb1-21">fig, axes <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> plt.subplots(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, figsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">7.4</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">3.4</span>))</span>
<span id="cb1-22"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> ax, kind, title <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">zip</span>(axes, (<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"early"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"late"</span>), (<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"early fusion"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"late fusion"</span>)):</span>
<span id="cb1-23">    ax.imshow(mask_for(kind).T, aspect<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"auto"</span>, cmap<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Blues"</span>, vmin<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, vmax<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.6</span>)</span>
<span id="cb1-24">    ax.set_title(title, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, pad<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>)</span>
<span id="cb1-25">    ax.set_xlabel(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"input feature"</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>)</span>
<span id="cb1-26">    ax.axvline(D <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"white"</span>, linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.5</span>)</span>
<span id="cb1-27">    ax.set_xticks([D <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</span>, D <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.5</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</span>])<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> ax.set_xticklabels([<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"group A"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"group B"</span>])</span>
<span id="cb1-28">    ax.set_yticks([])</span>
<span id="cb1-29">    ax.tick_params(colors<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, labelsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>, length<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb1-30">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> s <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> ax.spines.values():</span>
<span id="cb1-31">        s.set_visible(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>)</span>
<span id="cb1-32">axes[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>].set_ylabel(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"hidden unit"</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>)</span>
<span id="cb1-33">fig.tight_layout()</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-display">
<div id="fig-mask" class="quarto-float quarto-figure quarto-figure-center anchored" alt="Two square heatmaps side by side. The left one is filled with colour everywhere. The right one has colour only in two diagonal blocks, with the off-diagonal blocks blank.">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-mask-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://roshbeed.com/posts/2026-09-18-multiplies-them/index_files/figure-html/fig-mask-output-1.png" alt="Two square heatmaps side by side. The left one is filled with colour everywhere. The right one has colour only in two diagonal blocks, with the off-diagonal blocks blank." width="700" height="315" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-mask-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;1: The first weight matrix of each architecture. Late fusion is the same matrix with the cross-group blocks held at zero.
</figcaption>
</figure>
</div>
</div>
</div>
<p>Written this way the question stops being “which architecture” and becomes “does this task need those cross-group weights?” — which is something you can measure.</p>
</section>
<section id="a-task-where-the-answer-is-known" class="level2">
<h2 class="anchored" data-anchor-id="a-task-where-the-answer-is-known">A task where the answer is known</h2>
<p>Before measuring anything on real data, it’s worth checking the measurement works on a target where you already know the answer.</p>
<p>So: two groups of random inputs, A and B. Each has a hidden linear score, and the target is</p>
<p><img src="https://latex.codecogs.com/png.latex?y%20=%20s_A%20+%20s_B%20+%20%5Calpha%20%5Ccdot%20s_A%20s_B"></p>
<p>At <img src="https://latex.codecogs.com/png.latex?%5Calpha%20=%200"> the target is purely additive — the two groups contribute independently, and a model that scores them separately and adds is exactly right. As <img src="https://latex.codecogs.com/png.latex?%5Calpha"> grows, the product term takes over, and a model that can only add should fall further and further behind.</p>
<div id="99551c6f" class="cell" data-execution_count="2">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb2-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> torch.nn <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> nn</span>
<span id="cb2-2"></span>
<span id="cb2-3">N <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6000</span></span>
<span id="cb2-4"></span>
<span id="cb2-5"></span>
<span id="cb2-6"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> make_data(alpha, seed):</span>
<span id="cb2-7">    g <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.Generator().manual_seed(seed)</span>
<span id="cb2-8">    a <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.randn(N, D, generator<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>g)</span>
<span id="cb2-9">    b <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.randn(N, D, generator<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>g)</span>
<span id="cb2-10">    wa <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.randn(D, generator<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>g) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> D<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</span></span>
<span id="cb2-11">    wb <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.randn(D, generator<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>g) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> D<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</span></span>
<span id="cb2-12"></span>
<span id="cb2-13">    score_a, score_b <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> a <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@</span> wa, b <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@</span> wb</span>
<span id="cb2-14">    y <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> score_a <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> score_b <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> alpha <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> score_a <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> score_b</span>
<span id="cb2-15">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> torch.cat([a, b], <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>), (y <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> y.mean()) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> y.std()</span>
<span id="cb2-16"></span>
<span id="cb2-17"></span>
<span id="cb2-18"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> Fusion(nn.Module):</span>
<span id="cb2-19">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""One architecture. `kind` only decides which weights may be non-zero."""</span></span>
<span id="cb2-20"></span>
<span id="cb2-21">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, kind, seed):</span>
<span id="cb2-22">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">super</span>().<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>()</span>
<span id="cb2-23">        torch.manual_seed(seed)</span>
<span id="cb2-24">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.first <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Linear(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> D, H)</span>
<span id="cb2-25">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.head <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Linear(H, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb2-26">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.register_buffer(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"mask"</span>, mask_for(kind))</span>
<span id="cb2-27"></span>
<span id="cb2-28">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> forward(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, x):</span>
<span id="cb2-29">        hidden <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.relu(x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@</span> (<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.first.weight.T <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.mask) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.first.bias)</span>
<span id="cb2-30">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.head(hidden).squeeze(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span></code></pre></div></div>
</details>
</div>
<div id="cell-fig-arch" class="cell" data-execution_count="3">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb3-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> _arch <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> diagram</span>
<span id="cb3-2"></span>
<span id="cb3-3">diagram(Fusion(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"early"</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>), input_shape<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> D), style<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"flow"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-display" data-execution_count="3">
<div id="fig-arch" class="quarto-float quarto-figure quarto-figure-center anchored" alt="A short left-to-right row of three-dimensional blocks: an input, a wide hidden layer and a single-unit output.">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-arch-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://roshbeed.com/posts/2026-09-18-multiplies-them/index_files/figure-html/fig-arch-output-1.png" class="img-fluid figure-img" alt="A short left-to-right row of three-dimensional blocks: an input, a wide hidden layer and a single-unit output.">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-arch-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;2: One architecture, drawn once. Early and late fusion are this same network; the only difference is which of the first layer’s weights are allowed to be non-zero.
</figcaption>
</figure>
</div>
</div>
</div>
<div id="e941a58b" class="cell" data-execution_count="4">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb4-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> variance_explained(kind, alpha, seed, steps<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1500</span>):</span>
<span id="cb4-2">    X, y <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> make_data(alpha, seed)</span>
<span id="cb4-3">    split <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.7</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> N)</span>
<span id="cb4-4">    model <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Fusion(kind, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> seed)</span>
<span id="cb4-5">    optimiser <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.optim.Adam(model.parameters(), lr<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.02</span>)</span>
<span id="cb4-6"></span>
<span id="cb4-7">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> _ <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(steps):</span>
<span id="cb4-8">        loss <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ((model(X[:split]) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> y[:split]) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>).mean()</span>
<span id="cb4-9">        optimiser.zero_grad()</span>
<span id="cb4-10">        loss.backward()</span>
<span id="cb4-11">        optimiser.step()</span>
<span id="cb4-12"></span>
<span id="cb4-13">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">with</span> torch.no_grad():  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># R² on the held-out third</span></span>
<span id="cb4-14">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> ((model(X[split:]) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> y[split:]) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>).mean().item() <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> y[split:].var().item()</span>
<span id="cb4-15"></span>
<span id="cb4-16"></span>
<span id="cb4-17">alphas <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.0</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.25</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.0</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2.0</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">4.0</span>]</span>
<span id="cb4-18">early <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [np.median([variance_explained(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"early"</span>, a, s) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> s <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>)]) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> a <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> alphas]</span>
<span id="cb4-19">late <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [np.median([variance_explained(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"late"</span>, a, s) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> s <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>)]) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> a <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> alphas]</span>
<span id="cb4-20"></span>
<span id="cb4-21"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'alpha'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;6}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'early'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;8}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'late'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;8}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'gap'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;8}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb4-22"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> a, e, l <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">zip</span>(alphas, early, late):</span>
<span id="cb4-23">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>a<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;6}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>e<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;8.4f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>l<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;8.4f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>e <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> l<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;+8.4f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code> alpha    early     late      gap
   0.0   1.0000   1.0000  -0.0000
  0.25   0.9999   0.9505  +0.0494
   0.5   0.9998   0.8236  +0.1762
   1.0   0.9997   0.5135  +0.4861
   2.0   0.9991   0.1224  +0.8766
   4.0   0.9996  -0.0922  +1.0918</code></pre>
</div>
</div>
<div id="cell-fig-alpha" class="cell" data-execution_count="5">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb6-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> _style <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> figure, style_axes</span>
<span id="cb6-2"></span>
<span id="cb6-3">fig, ax <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> figure(height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">3.8</span>)</span>
<span id="cb6-4">ax.plot(alphas, early, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>], linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, marker<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"o"</span>, markersize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>)</span>
<span id="cb6-5">ax.plot(alphas, late, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, marker<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"o"</span>, markersize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>)</span>
<span id="cb6-6">ax.annotate(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"early fusion"</span>, xy<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(alphas[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>], early[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>]), xytext<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>),</span>
<span id="cb6-7">            textcoords<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"offset points"</span>, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>], ha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"center"</span>)</span>
<span id="cb6-8">ax.annotate(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"late fusion"</span>, xy<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(alphas[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>], late[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>]), xytext<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">18</span>),</span>
<span id="cb6-9">            textcoords<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"offset points"</span>, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], ha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"center"</span>)</span>
<span id="cb6-10">ax.axhline(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.8</span>)</span>
<span id="cb6-11">style_axes(ax, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Interaction strength (alpha)"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Held-out R-squared"</span>)</span>
<span id="cb6-12">fig.tight_layout()</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-display">
<div id="fig-alpha" class="quarto-float quarto-figure quarto-figure-center anchored" alt="Two lines against interaction strength. The early fusion line stays flat near 1.0. The late fusion line starts at 1.0 and falls steeply to below zero.">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-alpha-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://roshbeed.com/posts/2026-09-18-multiplies-them/index_files/figure-html/fig-alpha-output-1.png" alt="Two lines against interaction strength. The early fusion line stays flat near 1.0. The late fusion line starts at 1.0 and falls steeply to below zero." width="660" height="353" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-alpha-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;3: Held-out R² as the interaction term grows. With no interaction the two are identical; the gap is entirely the product term.
</figcaption>
</figure>
</div>
</div>
</div>
<p>At <img src="https://latex.codecogs.com/png.latex?%5Calpha%20=%200"> the two are indistinguishable — both fit the additive target essentially perfectly. Deleting the cross-group weights costs nothing when there is nothing across the groups to model.</p>
<p>Everything after that is the interaction, and it is not subtle: by <img src="https://latex.codecogs.com/png.latex?%5Calpha%20=%204"> late fusion is explaining none of the held-out variance at all. It isn’t that it fits the product term badly. It <strong>cannot represent it</strong>, at any width.</p>
</section>
<section id="so-does-hacker-news-have-one" class="level2">
<h2 class="anchored" data-anchor-id="so-does-hacker-news-have-one">So does Hacker News have one?</h2>
<p>That was the real question, and the answer is yes, in one specific place: <strong>the title and the timing</strong>. Fitting the same comparison on the real upvote counts, for every pairing of input groups, the title-times-timing cell was the largest effect in the table — +0.038 Spearman for a model that can represent the interaction over one that can’t.</p>
<p>The mechanism makes sense once you look at the distribution. The same title is worth a handful of points at a dead hour and hundreds when the site is awake. A good title doesn’t <em>add</em> a fixed number of upvotes — it multiplies whatever the timing was going to give you. A model that scores the title and the metadata separately and sums the two scores can only add, so it splits the difference and is wrong in both directions.</p>
<p>There’s a catch that took me a while to get straight, and it’s the most useful thing I learned on this project.</p>
<p><strong>Additivity is a property of your units, not of your data.</strong> Fit the exact same comparison on <code>log1p(score)</code> instead of the raw count and every interaction disappears — because a logarithm turns multiplication into addition. Fit it on rank and they vanish too, since rank keeps every comparison and throws away all the magnitudes.</p>
<p>So the interaction lives in <em>how big the numbers get</em>, not in <em>which post beats which</em>. If this service ranked posts, it would not need early fusion. It predicts counts, so it does.</p>
</section>
<section id="what-the-toy-above-cant-show-you" class="level2">
<h2 class="anchored" data-anchor-id="what-the-toy-above-cant-show-you">What the toy above can’t show you</h2>
<p>I tried to reproduce the real finding at the scale of this page — 100,000 posts, a hashed bag of words for the title, hour and weekday for the timing — and it isn’t there. The joint model comes out slightly <em>behind</em> the additive one.</p>
<p>That’s not a contradiction, it’s a lesson about effect sizes. +0.038 Spearman is small next to the seed-to-seed spread of these models, which is why the real measurement needed ten seeds per cell and a capacity-matched control, and why the whole question took a few thousand runs to answer rather than a few dozen.</p>
<p>The synthetic target above is the honest version of what a toy can do here: it shows you the <em>mechanism</em> clearly, on data built so the mechanism is the only thing present. It can’t tell you how much of that mechanism is in a real dataset.</p>
</section>
<section id="the-ceiling" class="level2">
<h2 class="anchored" data-anchor-id="the-ceiling">The ceiling</h2>
<p>The other thing worth knowing about this task: Hacker News is substantially a lottery. The same link posted twice scores similarly only about a quarter of the time. Whatever the architecture, there’s a hard bound on what any model can do here, and it’s a lot lower than you’d hope.</p>
<p>The full project — the fusion sweep, the four baselines, the deployed API — is <a href="https://github.com/RoshBeed/ai-residency/tree/main/services/content-scoring">on GitHub</a>.</p>
<hr>
<p>[1] Snoek, Worring, Smeulders. <em>Early versus Late Fusion in Semantic Video Analysis.</em> ACM Multimedia 2005.</p>
<div id="3f074df1" class="cell" data-execution_count="6">
<div class="cell-output cell-output-stdout">
<pre><code>Built:      2026-09-18 02:21:17 UTC
Python:     3.13.15
matplotlib: 3.11.2
numpy:      2.5.3
torch:      2.14.0+cpu</code></pre>
</div>
</div>


</section>

 ]]></description>
  <category>multimodal</category>
  <category>fusion</category>
  <category>week-1</category>
  <guid>https://roshbeed.com/posts/2026-09-18-multiplies-them/</guid>
  <pubDate>Fri, 18 Sep 2026 00:00:00 GMT</pubDate>
</item>
<item>
  <title>I asked a sweep how many dimensions I needed, and it refused to answer</title>
  <dc:creator>Rosh Beed</dc:creator>
  <link>https://roshbeed.com/posts/2026-09-18-how-many-dimensions/</link>
  <description><![CDATA[ 




<p>When I trained <a href="../2026-09-18-company-they-keep/">word2vec from scratch</a>, one of the settings I had to pick was how many numbers each word vector gets. 100? 300? The papers use 300 and so does nearly everyone, but I wanted to know what it was buying.</p>
<p>So I did what seemed obvious: put embedding size into the hyperparameter sweep along with everything else. 48 runs, twelve hours, Bayesian search over architecture, embedding size, window, negatives, subsampling and learning rate, ranked on a composite of three word-similarity benchmarks.</p>
<p>It found a good configuration. It could not answer my question, and the reason is worth more than the answer was.</p>
<div id="8069e4c8" class="cell" data-execution_count="1">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> json</span>
<span id="cb1-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> sys</span>
<span id="cb1-3"></span>
<span id="cb1-4">sys.path.insert(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".."</span>)</span>
<span id="cb1-5"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> numpy <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> np</span>
<span id="cb1-6"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> _style <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> COLOURS, MUTED, figure, style_axes</span>
<span id="cb1-7"></span>
<span id="cb1-8">runs <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> json.load(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">open</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"sweep-runs.json"</span>))</span>
<span id="cb1-9"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(runs)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> completed runs"</span>)</span>
<span id="cb1-10"></span>
<span id="cb1-11">sizes, counts <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.unique([r[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"embed_size"</span>] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> r <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> runs], return_counts<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span>
<span id="cb1-12"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> size, count <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">zip</span>(sizes, counts):</span>
<span id="cb1-13">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"  </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>size<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;3}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> dimensions: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>count<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;2}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> runs </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'#'</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> count<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>48 completed runs
  100 dimensions:  1 runs #
  200 dimensions:  4 runs ####
  300 dimensions: 43 runs ###########################################</code></pre>
</div>
</div>
<p>There it is. Of 48 runs, 43 chose 300 dimensions. One run — one — tried 100.</p>
<p>That isn’t a bug. It is Bayesian optimisation doing exactly its job. It builds a model of which settings score well and spends the remaining budget there. Early on it found that 300 dimensions and skip-gram were working, and from then on it mostly stopped looking anywhere else.</p>
<p>Which is what you want if the question is <em>what is the best model I can find in twelve hours</em>. It is precisely wrong if the question is <em>what does this one knob do</em>, because the answer to the second question lives in the settings the search deliberately abandons.</p>
<div id="cell-fig-sweep" class="cell" data-execution_count="2">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb3-1">fig, ax <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> figure(height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">4.0</span>)</span>
<span id="cb3-2">jitter <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.random.default_rng(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>).uniform(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(runs))</span>
<span id="cb3-3">ax.scatter([r[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"embed_size"</span>] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> r <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> runs] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> jitter, [r[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"score"</span>] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> r <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> runs],</span>
<span id="cb3-4">           s<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">34</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>], alpha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.6</span>, zorder<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>)</span>
<span id="cb3-5"></span>
<span id="cb3-6">ax.set_xticks([<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">200</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">300</span>])</span>
<span id="cb3-7">ax.set_xlim(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">60</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">340</span>)</span>
<span id="cb3-8">style_axes(ax, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Embedding size"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Benchmark score"</span>)</span>
<span id="cb3-9">fig.tight_layout()</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-display">
<div id="fig-sweep" class="quarto-float quarto-figure quarto-figure-center anchored" alt="A scatter plot with three columns of points. The rightmost column at 300 dimensions has more than forty points spread vertically; the columns at 100 and 200 hold one and four points.">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-sweep-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://roshbeed.com/posts/2026-09-18-how-many-dimensions/index_files/figure-html/fig-sweep-output-1.png" alt="A scatter plot with three columns of points. The rightmost column at 300 dimensions has more than forty points spread vertically; the columns at 100 and 200 hold one and four points." width="660" height="373" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-sweep-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;1: Every run in the sweep, by embedding size. The 300-dimension column is the search exploiting; the other two columns are almost empty, so they carry almost no information.
</figcaption>
</figure>
</div>
</div>
</div>
<p>Reading a trend off that would be self-deception. The 300 column has 43 runs spanning 0.46 to 0.50, which is a wider spread than any gap between the columns — so the variation <em>within</em> one setting swamps the difference <em>between</em> settings. And the 100-dimension point is a single run that also differed in window size and learning rate, so whatever it shows isn’t attributable to dimensions.</p>
<section id="one-accidental-exception" class="level2">
<h2 class="anchored" data-anchor-id="one-accidental-exception">One accidental exception</h2>
<p>Digging through the runs, the search did produce one genuinely controlled comparison, by luck rather than design.</p>
<div id="0e1b3b31" class="cell" data-execution_count="3">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb4-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> matches(a, b, keys<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"architecture"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"window_size"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"num_negatives"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"subsample_threshold"</span>)):</span>
<span id="cb4-2">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">all</span>(a[k] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> b[k] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> k <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> keys)</span>
<span id="cb4-3"></span>
<span id="cb4-4"></span>
<span id="cb4-5">pairs <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [(a, b) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> a <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> runs <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> b <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> runs</span>
<span id="cb4-6">         <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> a[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"embed_size"</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> b[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"embed_size"</span>] <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">and</span> matches(a, b)</span>
<span id="cb4-7">         <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">and</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">abs</span>(a[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"learning_rate"</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> b[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"learning_rate"</span>]) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> a[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"learning_rate"</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.02</span>]</span>
<span id="cb4-8"></span>
<span id="cb4-9"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> a, b <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> pairs:</span>
<span id="cb4-10">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>a[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'architecture'</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">, window </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>a[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'window_size'</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">, </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>a[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'num_negatives'</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> negatives, "</span></span>
<span id="cb4-11">          <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"subsample </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>a[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'subsample_threshold'</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:g}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb4-12">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"  learning rate </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>a[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'learning_rate'</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.5f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> vs </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>b[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'learning_rate'</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.5f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb4-13">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"  </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>a[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'embed_size'</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> dimensions: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>a[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score'</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.4f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb4-14">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"  </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>b[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'embed_size'</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> dimensions: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>b[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score'</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.4f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb4-15">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"  difference: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>a[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score'</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> b[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score'</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:+.4f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>skipgram, window 8, 10 negatives, subsample 1e-05
  learning rate 0.00215 vs 0.00214
  300 dimensions: 0.4956
  200 dimensions: 0.4860
  difference: +0.0096</code></pre>
</div>
</div>
<p>Two runs identical in architecture, window, negatives and subsampling, with learning rates within 0.5% of each other, differing only in embedding size. The extra 100 dimensions are worth about 0.01 on the benchmark.</p>
<p>That’s one data point, from a search that was not trying to produce it. It’s suggestive and it is not a curve.</p>
</section>
<section id="just-run-the-experiment" class="level2">
<h2 class="anchored" data-anchor-id="just-run-the-experiment">Just run the experiment</h2>
<p>The thing I should have done from the start, and it turns out to be cheap: hold everything else fixed and vary the one setting. Five runs, not forty-eight.</p>
<p>Here it is on the small corpus from the <a href="../2026-09-18-company-they-keep/">word2vec post</a> — a fiftieth of the data the real sweep used, so the numbers are lower throughout, but the shape is the point. Scored against WordSim-353, where humans rated how related pairs of words are and the model is judged on whether it agrees.</p>
<div id="ad56e4a5" class="cell" data-execution_count="4">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb6-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> collections</span>
<span id="cb6-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> re</span>
<span id="cb6-3"></span>
<span id="cb6-4"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> torch</span>
<span id="cb6-5"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> torch.nn.functional <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> F</span>
<span id="cb6-6"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> huggingface_hub <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> hf_hub_download</span>
<span id="cb6-7"></span>
<span id="cb6-8">DATASET, REVISION <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"roshbeed/ai-residency-blog-data"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"9113ea48905b4f7178b333919ed5ec1a474561d7"</span></span>
<span id="cb6-9"></span>
<span id="cb6-10">words <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">open</span>(hf_hub_download(DATASET, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"text8/text8-2m.txt"</span>,</span>
<span id="cb6-11">                             repo_type<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"dataset"</span>, revision<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>REVISION)).read().split()</span>
<span id="cb6-12">counts <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> collections.Counter(words)</span>
<span id="cb6-13">vocabulary <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [w <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> w, c <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> counts.most_common() <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> c <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>]</span>
<span id="cb6-14">index <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> {w: i <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i, w <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">enumerate</span>(vocabulary)}</span>
<span id="cb6-15">ids <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.array([index[w] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> w <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> words <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> w <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> index])</span>
<span id="cb6-16"></span>
<span id="cb6-17">frequency <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.bincount(ids, minlength<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(vocabulary)).astype(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>)</span>
<span id="cb6-18">frequency <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/=</span> frequency.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>()</span>
<span id="cb6-19">keep <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.minimum(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.0</span>, np.sqrt(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1e-3</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> frequency))</span>
<span id="cb6-20">kept <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ids[np.random.default_rng(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>).random(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(ids)) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> keep[ids]]</span>
<span id="cb6-21"></span>
<span id="cb6-22">centres, contexts <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [], []</span>
<span id="cb6-23"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> offset <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>):</span>
<span id="cb6-24">    centres.append(kept[offset:])<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span>  contexts.append(kept[:<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>offset])</span>
<span id="cb6-25">    centres.append(kept[:<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>offset])<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> contexts.append(kept[offset:])</span>
<span id="cb6-26">centre <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.from_numpy(np.concatenate(centres))</span>
<span id="cb6-27">context <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.from_numpy(np.concatenate(contexts))</span>
<span id="cb6-28">noise <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.from_numpy(frequency <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.75</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> (frequency <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.75</span>).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>())</span>
<span id="cb6-29"></span>
<span id="cb6-30">benchmark <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []</span>
<span id="cb6-31"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> line <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">open</span>(hf_hub_download(DATASET, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"eval/wordsim353.txt"</span>,</span>
<span id="cb6-32">                                 repo_type<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"dataset"</span>, revision<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>REVISION)):</span>
<span id="cb6-33">    a, b, score <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> line.split(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\t</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb6-34">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> a.lower() <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> index <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">and</span> b.lower() <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> index:</span>
<span id="cb6-35">        benchmark.append((a.lower(), b.lower(), <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>(score)))</span>
<span id="cb6-36"></span>
<span id="cb6-37"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(vocabulary)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:,}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> words, </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(centre)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:,}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> training pairs"</span>)</span>
<span id="cb6-38"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"WordSim-353: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(benchmark)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> of 353 pairs are inside this vocabulary"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>3,704 words, 1,839,810 training pairs
WordSim-353: 130 of 353 pairs are inside this vocabulary</code></pre>
</div>
</div>
<div id="aac1a891" class="cell" data-execution_count="5">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb8-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> spearman(a, b):</span>
<span id="cb8-2">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>(np.corrcoef(np.argsort(np.argsort(a)), np.argsort(np.argsort(b)))[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>])</span>
<span id="cb8-3"></span>
<span id="cb8-4"></span>
<span id="cb8-5"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> train_at(dimensions, epochs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>, K<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>, batch<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8192</span>, lr<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2e-3</span>):</span>
<span id="cb8-6">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""Everything below is identical between runs except `dimensions`."""</span></span>
<span id="cb8-7">    generator <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.Generator().manual_seed(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb8-8">    inside <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (torch.randn(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(vocabulary), dimensions, generator<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>generator) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.01</span>).requires_grad_()</span>
<span id="cb8-9">    outside <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.zeros(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(vocabulary), dimensions, requires_grad<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span>
<span id="cb8-10">    optimiser <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.optim.Adam([inside, outside], lr<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>lr)</span>
<span id="cb8-11"></span>
<span id="cb8-12">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> _ <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(epochs):</span>
<span id="cb8-13">        perm <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.randperm(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(centre), generator<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>generator)</span>
<span id="cb8-14">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(perm) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> batch, batch):</span>
<span id="cb8-15">            b <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> perm[i:i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> batch]</span>
<span id="cb8-16">            v <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> inside[centre[b]]</span>
<span id="cb8-17">            real <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> F.logsigmoid((v <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> outside[context[b]]).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>))</span>
<span id="cb8-18">            fake_ids <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.multinomial(noise, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(b) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> K, replacement<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>,</span>
<span id="cb8-19">                                         generator<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>generator).view(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(b), K)</span>
<span id="cb8-20">            invented <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> F.logsigmoid(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>(outside[fake_ids] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@</span> v.unsqueeze(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)).squeeze(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb8-21">            loss <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>(real <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> invented).mean()</span>
<span id="cb8-22">            optimiser.zero_grad()</span>
<span id="cb8-23">            loss.backward()</span>
<span id="cb8-24">            optimiser.step()</span>
<span id="cb8-25"></span>
<span id="cb8-26">    embeddings <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> F.normalize(inside.detach(), dim<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb8-27">    predicted <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>(embeddings[index[a]] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@</span> embeddings[index[b]]) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> a, b, _ <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> benchmark]</span>
<span id="cb8-28">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> spearman(predicted, [s <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> _, _, s <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> benchmark])</span>
<span id="cb8-29"></span>
<span id="cb8-30"></span>
<span id="cb8-31">DIMENSIONS <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">16</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">32</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">64</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">128</span>]</span>
<span id="cb8-32">scores <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []</span>
<span id="cb8-33"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> d <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> DIMENSIONS:</span>
<span id="cb8-34">    scores.append(train_at(d))</span>
<span id="cb8-35">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>d<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;4}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> dimensions: WordSim-353 rho </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>scores[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.4f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>   8 dimensions: WordSim-353 rho 0.1574
  16 dimensions: WordSim-353 rho 0.2692
  32 dimensions: WordSim-353 rho 0.3480
  64 dimensions: WordSim-353 rho 0.4330
 128 dimensions: WordSim-353 rho 0.4638</code></pre>
</div>
</div>
<div id="cell-fig-curve" class="cell" data-execution_count="6">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb10" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb10-1">fig, ax <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> figure(height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">3.8</span>)</span>
<span id="cb10-2">ax.plot(DIMENSIONS, scores, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>], linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, marker<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"o"</span>, markersize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>)</span>
<span id="cb10-3">ax.set_xscale(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"log"</span>, base<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb10-4">ax.set_xticks(DIMENSIONS, [<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>(d) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> d <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> DIMENSIONS])</span>
<span id="cb10-5">style_axes(ax, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Embedding size"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"WordSim-353 (Spearman)"</span>)</span>
<span id="cb10-6">fig.tight_layout()</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-display">
<div id="fig-curve" class="quarto-float quarto-figure quarto-figure-center anchored" alt="A curve rising steeply from 0.21 at 8 dimensions to about 0.46 at 64, then flattening between 64 and 128.">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-curve-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://roshbeed.com/posts/2026-09-18-how-many-dimensions/index_files/figure-html/fig-curve-output-1.png" alt="A curve rising steeply from 0.21 at 8 dimensions to about 0.46 at 64, then flattening between 64 and 128." width="660" height="354" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-curve-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;2: One variable changed, everything else held fixed. Five runs answer the question forty-eight could not.
</figcaption>
</figure>
</div>
</div>
</div>
<div id="fe43ab80" class="cell" data-execution_count="7">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb11" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb11-1"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> a, b <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">zip</span>(DIMENSIONS, DIMENSIONS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>:]):</span>
<span id="cb11-2">    gain <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> scores[DIMENSIONS.index(b)] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> scores[DIMENSIONS.index(a)]</span>
<span id="cb11-3">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>a<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;4}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> -&gt; </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>b<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&lt;4}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> doubles the size and buys </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>gain<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:+.4f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>   8 -&gt; 16   doubles the size and buys +0.1118
  16 -&gt; 32   doubles the size and buys +0.0789
  32 -&gt; 64   doubles the size and buys +0.0850
  64 -&gt; 128  doubles the size and buys +0.0308</code></pre>
</div>
</div>
<p>That is a curve, and it says something specific: every doubling helps, and the help is shrinking. The first doubling is worth roughly three times the last, and the fall-off only really arrives past 64 — which is a more useful thing to know than a single recommended number, because it tells you what the next doubling would cost you to find out.</p>
<p>Two caveats worth stating, because the number is easy to over-read.</p>
<p><strong>This corpus is small.</strong> A fiftieth of <code>text8</code>, and only 3,704 words survive the minimum-count threshold. A vocabulary that small needs fewer dimensions to separate, so the saturation point here is lower than it would be on the full corpus. The shape transfers; the elbow doesn’t.</p>
<p><strong>WordSim-353 only covers part of this vocabulary.</strong> A third of its pairs contain a word this corpus never saw often enough, so the score is computed on the pairs that are left.</p>
</section>
<section id="what-id-actually-take-away" class="level2">
<h2 class="anchored" data-anchor-id="what-id-actually-take-away">What I’d actually take away</h2>
<p><strong>A hyperparameter search and an experiment are different things, and they look the same from the outside.</strong> Both produce a table of configurations and scores. But a search is <em>trying</em> to be unbalanced — that’s the mechanism by which it finds a good model quickly — and an unbalanced table cannot tell you what an individual knob does. The runs you would need are the ones it correctly declined to spend budget on.</p>
<p><strong>The controlled version is cheap.</strong> Five runs against forty-eight. I had assumed answering this properly would be expensive, and the expensive thing was the search that couldn’t answer it.</p>
<p><strong>Ask the search for what it’s good at.</strong> The same sweep did tell me something valuable, by accident: window size and negative count barely moved the score between their best and worst settings anywhere in the table. That’s a robustness observation, and a badly balanced search is fine for noticing which knobs are not worth another twelve hours.</p>
<p>The sweep, the trainer and the benchmarks are <a href="https://github.com/RoshBeed/ai-residency/tree/main/services/word-embeddings">on GitHub</a>.</p>
<div id="e243e477" class="cell" data-execution_count="8">
<div class="cell-output cell-output-stdout">
<pre><code>Built:      2026-09-18 02:23:27 UTC
Python:     3.13.15
matplotlib: 3.11.2
numpy:      2.5.3
torch:      2.14.0+cpu</code></pre>
</div>
</div>


</section>

 ]]></description>
  <category>nlp</category>
  <category>embeddings</category>
  <category>experiments</category>
  <category>deep-dive</category>
  <guid>https://roshbeed.com/posts/2026-09-18-how-many-dimensions/</guid>
  <pubDate>Fri, 18 Sep 2026 00:00:00 GMT</pubDate>
</item>
<item>
  <title>Where the inputs meet, and when it stops mattering</title>
  <dc:creator>Rosh Beed</dc:creator>
  <link>https://roshbeed.com/posts/2026-09-18-where-the-inputs-meet/</link>
  <description><![CDATA[ 




<p>A follow-on from the <a href="../2026-09-18-multiplies-them/">Hacker News post</a>, going properly into the architecture question rather than mentioning it.</p>
<p>The setup: a model whose inputs come in groups — a title, a timestamp, a domain, an author. <strong>Early fusion</strong> concatenates them and runs one network over the lot. <strong>Late fusion</strong> gives each group its own network, reduces each to a score, and adds the scores. The literature [1] treats these as two designs with different strengths.</p>
<p>Every number below is recomputed from the raw per-seed measurements when this page builds, including the significance tests. Nothing here is a figure I saved.</p>
<div id="716c63b4" class="cell" data-execution_count="1">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> json</span>
<span id="cb1-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> sys</span>
<span id="cb1-3"></span>
<span id="cb1-4">sys.path.insert(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".."</span>)</span>
<span id="cb1-5"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> numpy <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> np</span>
<span id="cb1-6"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> _style <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> COLOURS, MUTED, figure, style_axes</span>
<span id="cb1-7"></span>
<span id="cb1-8">synthetic <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> json.load(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">open</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"synthetic-alpha.json"</span>))</span>
<span id="cb1-9">counts <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> json.load(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">open</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"pairings-counts.json"</span>))</span>
<span id="cb1-10">logged <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> json.load(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">open</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"pairings-log.json"</span>))</span>
<span id="cb1-11">ranked <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> json.load(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">open</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"pairings-rank.json"</span>))</span>
<span id="cb1-12">replicate <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> json.load(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">open</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"pairings-replicate.json"</span>))</span>
<span id="cb1-13">robust_128 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> json.load(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">open</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"robustness-128.json"</span>))</span>
<span id="cb1-14">robust_512 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> json.load(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">open</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"robustness-512.json"</span>))</span>
<span id="cb1-15"></span>
<span id="cb1-16"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"synthetic sweep: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(synthetic)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> interaction strengths x "</span></span>
<span id="cb1-17">      <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">next</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">iter</span>(synthetic.values())))<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> model variants"</span>)</span>
<span id="cb1-18"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"Hacker News: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(counts)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> pairings of input groups, 10 seeds each,"</span>)</span>
<span id="cb1-19"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"  measured on raw counts, on log1p(counts) and on rank"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>synthetic sweep: 12 interaction strengths x 12 model variants
Hacker News: 11 pairings of input groups, 10 seeds each,
  measured on raw counts, on log1p(counts) and on rank</code></pre>
</div>
</div>
<section id="first-they-are-not-two-architectures" class="level2">
<h2 class="anchored" data-anchor-id="first-they-are-not-two-architectures">First: they are not two architectures</h2>
<p>Take one hidden layer over all the inputs concatenated. Early fusion lets every hidden unit read every input. Late fusion splits the units into per-group blocks and lets each block read only its own group — the same layer with the off-diagonal blocks held at zero.</p>
<p>So “early” and “late” are the two ends of one dial: <strong>which layer do the groups first meet at?</strong> Everything before that layer is block-diagonal, everything from it on is shared. Fusing at layer 1 is early fusion. Fusing at the last layer is late fusion. And now you can ask a better question than which is better — <em>how long can you delay fusion before it costs you?</em></p>
</section>
<section id="a-control-where-the-answer-is-known" class="level2">
<h2 class="anchored" data-anchor-id="a-control-where-the-answer-is-known">A control where the answer is known</h2>
<p>Before trusting any of this on real data, the measurement needs to be checked on a target whose interaction strength I set myself:</p>
<p><img src="https://latex.codecogs.com/png.latex?y%20=%20g%20+%20h%20+%20%5Calpha%20%5Ccdot%20g%20h"></p>
<p>At <img src="https://latex.codecogs.com/png.latex?%5Calpha%20=%200"> the two groups contribute independently and late fusion is exactly the right model. As <img src="https://latex.codecogs.com/png.latex?%5Calpha"> rises, a model that can only add should fall behind.</p>
<p>This is a real sweep — twelve interaction strengths, three architectures, four widths, ten seeds each.</p>
<div id="cell-fig-alpha" class="cell" data-execution_count="2">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb3-1">alphas <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sorted</span>(synthetic, key<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>)</span>
<span id="cb3-2">widths <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">64</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">128</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">256</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">512</span>]</span>
<span id="cb3-3"></span>
<span id="cb3-4">fig, ax <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> figure(height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">4.2</span>)</span>
<span id="cb3-5"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> width, colour <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">zip</span>(widths, COLOURS):</span>
<span id="cb3-6">    gap <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [synthetic[a][<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"early@</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>width<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"median"</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> synthetic[a][<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"late-snoek@</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>width<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"median"</span>]</span>
<span id="cb3-7">           <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> a <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> alphas]</span>
<span id="cb3-8">    ax.plot([<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>(a) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> a <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> alphas], gap, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>colour, linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, marker<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"o"</span>, markersize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>)</span>
<span id="cb3-9">    ax.annotate(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>width<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> units"</span>, xy<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>(alphas[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]), gap[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]), xytext<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>),</span>
<span id="cb3-10">                textcoords<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"offset points"</span>, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>colour, va<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"center"</span>)</span>
<span id="cb3-11"></span>
<span id="cb3-12">ax.axhline(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.8</span>)</span>
<span id="cb3-13">ax.set_xlim(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>(alphas[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.18</span>)</span>
<span id="cb3-14">style_axes(ax, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Interaction strength in the target (alpha)"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"How far late fusion falls behind"</span>)</span>
<span id="cb3-15">fig.tight_layout()</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-display">
<div id="fig-alpha" class="quarto-float quarto-figure quarto-figure-center anchored" alt="Four nearly overlapping lines rising from zero as the interaction strength rises, reaching about 0.11 at the right-hand edge.">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-alpha-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://roshbeed.com/posts/2026-09-18-where-the-inputs-meet/index_files/figure-html/fig-alpha-output-1.png" alt="Four nearly overlapping lines rising from zero as the interaction strength rises, reaching about 0.11 at the right-hand edge." width="660" height="393" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-alpha-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;1: Snoek late fusion against early fusion on a target with a known interaction, at four widths. The penalty grows with the interaction and barely moves with width.
</figcaption>
</figure>
</div>
</div>
</div>
<div id="ab765495" class="cell" data-execution_count="3">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb4-1">widest, narrowest <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"512"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"64"</span></span>
<span id="cb4-2">worst <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> alphas[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]</span>
<span id="cb4-3"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"at alpha=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>worst<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">:"</span>)</span>
<span id="cb4-4"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> width <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> widths:</span>
<span id="cb4-5">    gap <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (synthetic[worst][<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"early@</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>width<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"median"</span>]</span>
<span id="cb4-6">           <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> synthetic[worst][<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"late-snoek@</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>width<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"median"</span>])</span>
<span id="cb4-7">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"  </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>width<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;4}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> units: late fusion is </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>gap<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.3f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> behind"</span>)</span>
<span id="cb4-8"></span>
<span id="cb4-9">zero_gap <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">max</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">abs</span>(synthetic[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"0.0"</span>][<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"early@</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>w<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"median"</span>]</span>
<span id="cb4-10">                   <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> synthetic[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"0.0"</span>][<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"late-snoek@</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>w<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"median"</span>]) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> w <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> widths)</span>
<span id="cb4-11"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">at alpha=0, the largest gap at any width is </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>zero_gap<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.4f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>at alpha=8.0:
    64 units: late fusion is 0.118 behind
   128 units: late fusion is 0.112 behind
   256 units: late fusion is 0.114 behind
   512 units: late fusion is 0.108 behind

at alpha=0, the largest gap at any width is 0.0008</code></pre>
</div>
</div>
<p>Two things to take from that.</p>
<p><strong>The measurement works.</strong> When an interaction exists, this comparison finds it, and when one doesn’t, the two architectures are within 0.001 of each other at every width. So a null result later is a null result, not a broken harness.</p>
<p><strong>And width barely rescues it.</strong> Eight times the hidden units recovers about 9% of the gap. That is worth stating plainly because it is the opposite of what happens on the real data below: when an interaction genuinely exists in the target, an additive model cannot buy its way out with capacity. It is not underfitting. It is the wrong shape.</p>
</section>
<section id="now-the-real-data" class="level2">
<h2 class="anchored" data-anchor-id="now-the-real-data">Now the real data</h2>
<p>The same comparison on real Hacker News upvote counts, for every pairing of input groups. A model that <strong>cannot</strong> represent an interaction — a tower each, reduced to one score, then summed — against one that can.</p>
<p>The control that decides whether this means anything is capacity. A joint model has more parameters, so it can win for reasons that have nothing to do with interaction. So the additive model here gets towers three times wider: still structurally unable to represent an interaction, and now with <em>more</em> parameters than its rival. Anything the joint model wins by is the interaction, not the budget.</p>
<div id="c3a2b71d" class="cell" data-execution_count="4">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb6-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> permutation_test(a, b, trials<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">20_000</span>, seed<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>):</span>
<span id="cb6-2">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""How often would shuffling the labels produce a difference this large?"""</span></span>
<span id="cb6-3">    a, b <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.asarray(a), np.asarray(b)</span>
<span id="cb6-4">    observed <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.median(b) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> np.median(a)</span>
<span id="cb6-5">    pool <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.concatenate([a, b])</span>
<span id="cb6-6">    rng <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.random.default_rng(seed)</span>
<span id="cb6-7"></span>
<span id="cb6-8">    hits <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span></span>
<span id="cb6-9">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> _ <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(trials):</span>
<span id="cb6-10">        rng.shuffle(pool)</span>
<span id="cb6-11">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">abs</span>(np.median(pool[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(a):]) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> np.median(pool[:<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(a)])) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">abs</span>(observed):</span>
<span id="cb6-12">            hits <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span>
<span id="cb6-13">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> observed, (hits <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> (trials <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb6-14"></span>
<span id="cb6-15"></span>
<span id="cb6-16"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> interaction(dataset, pairing):</span>
<span id="cb6-17">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> permutation_test(dataset[pairing][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"additive-matched"</span>], dataset[pairing][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"joint"</span>])</span>
<span id="cb6-18"></span>
<span id="cb6-19"></span>
<span id="cb6-20">rows <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sorted</span>(((name, <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span>interaction(counts, name)) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> name <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> counts), key<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">lambda</span> r: <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>r[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>])</span>
<span id="cb6-21"></span>
<span id="cb6-22"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'does the first group depend on the second?'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&lt;42}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'effect'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;9}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'p'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;8}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb6-23"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> name, effect, p <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> rows:</span>
<span id="cb6-24">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>name<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&lt;42}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>effect<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;+9.4f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>p<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;8.3f}{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'*'</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> p <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.05</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">' '</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>does the first group depend on the second?    effect        p
title x when you post                        +0.0381    0.000*
title x all metadata                         +0.0220    0.001*
title x title shape                          +0.0090    0.007*
title x where it links                       +0.0081    0.139 
title x the author                           +0.0020    0.628 
where x author                               +0.0013    0.307 
author x title shape                         -0.0008    0.721 
when x title shape                           -0.0014    0.619 
where x title shape                          -0.0062    0.065 
when x author                                -0.0067    0.066 
when x where                                 -0.0117    0.023*</code></pre>
</div>
</div>
<div id="cell-fig-pairings" class="cell" data-execution_count="5">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb8-1">names <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [r[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> r <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> rows][::<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]</span>
<span id="cb8-2">effects <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [r[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> r <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> rows][::<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]</span>
<span id="cb8-3">significant <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [r[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.05</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> r <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> rows][::<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]</span>
<span id="cb8-4"></span>
<span id="cb8-5">fig, ax <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> figure(height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">4.6</span>)</span>
<span id="cb8-6">ax.barh(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(names)), effects,</span>
<span id="cb8-7">        color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> s <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#c3ccd6"</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> s <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> significant], height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.66</span>)</span>
<span id="cb8-8">ax.axvline(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.9</span>)</span>
<span id="cb8-9">ax.set_yticks(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(names)), names, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>)</span>
<span id="cb8-10">style_axes(ax, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Interaction, over a wider additive model"</span>, grid<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"x"</span>)</span>
<span id="cb8-11">fig.tight_layout()</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-display">
<div id="fig-pairings" class="quarto-float quarto-figure quarto-figure-center anchored" alt="A horizontal bar chart of eleven pairings. The top bar, title by when you post, is much longer than the rest; most others cluster near zero.">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-pairings-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://roshbeed.com/posts/2026-09-18-where-the-inputs-meet/index_files/figure-html/fig-pairings-output-1.png" alt="A horizontal bar chart of eleven pairings. The top bar, title by when you post, is much longer than the rest; most others cluster near zero." width="662" height="431" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-pairings-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;2: Each pairing’s interaction against the capacity-matched additive control. Filled bars survive a permutation test at p &lt; 0.05.
</figcaption>
</figure>
</div>
</div>
</div>
<p><strong>The title and the timing is the largest effect in the table by a distance</strong>, and it is what decided the architecture this service actually serves. The mechanism is the one in the <a href="../2026-09-18-multiplies-them/">other post</a>: a good title doesn’t add a fixed number of upvotes, it multiplies whatever the timing was going to give you, and a model that scores the two separately and sums can only add.</p>
<p>Notice how much of the rest of the table is noise. Two-thirds of the pairings are indistinguishable from zero and several point the wrong way. This is one specific pair of inputs interacting, not a general property of the task.</p>
</section>
<section id="the-part-that-nearly-fooled-me" class="level2">
<h2 class="anchored" data-anchor-id="the-part-that-nearly-fooled-me">The part that nearly fooled me</h2>
<p>Additivity is not a property of data. It is a property of the <strong>units you measure the data in.</strong></p>
<p>Run the identical comparison — same models, same seeds, same everything — against <code>log1p(score)</code> instead of the raw count, and against rank instead of either.</p>
<div id="cell-fig-coords" class="cell" data-execution_count="6">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb9-1">targets <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"raw counts"</span>, counts), (<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"log1p(counts)"</span>, logged), (<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"rank"</span>, ranked)]</span>
<span id="cb9-2"></span>
<span id="cb9-3">fig, ax <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> figure(height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">4.2</span>)</span>
<span id="cb9-4"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> x, (label, dataset) <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">enumerate</span>(targets):</span>
<span id="cb9-5">    values <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [np.median(dataset[n][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"joint"</span>]) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> np.median(dataset[n][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"additive-matched"</span>])</span>
<span id="cb9-6">              <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> n <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> dataset]</span>
<span id="cb9-7">    ax.scatter([x] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(values), values, s<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">36</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>], alpha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.65</span>, zorder<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>)</span>
<span id="cb9-8"></span>
<span id="cb9-9">highlight <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"title x when you post"</span></span>
<span id="cb9-10">ax.plot(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>), [np.median(d[highlight][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"joint"</span>]) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> np.median(d[highlight][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"additive-matched"</span>])</span>
<span id="cb9-11">                   <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> _, d <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> targets], color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, zorder<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>)</span>
<span id="cb9-12">ax.annotate(highlight, xy<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, np.median(counts[highlight][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"joint"</span>])</span>
<span id="cb9-13">                           <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> np.median(counts[highlight][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"additive-matched"</span>])),</span>
<span id="cb9-14">            xytext<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>), textcoords<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"offset points"</span>, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>,</span>
<span id="cb9-15">            color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], va<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"center"</span>)</span>
<span id="cb9-16"></span>
<span id="cb9-17">ax.axhline(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.9</span>)</span>
<span id="cb9-18">ax.set_xlim(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.4</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2.6</span>)</span>
<span id="cb9-19">ax.set_xticks(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>), [t[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> t <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> targets])</span>
<span id="cb9-20">style_axes(ax, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"What the model is asked to predict"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Interaction"</span>)</span>
<span id="cb9-21">fig.tight_layout()</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-display">
<div id="fig-coords" class="quarto-float quarto-figure quarto-figure-center anchored" alt="Three columns of points. The raw-counts column has several points well above zero including one far above; the log and rank columns are both tightly clustered around zero.">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-coords-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://roshbeed.com/posts/2026-09-18-where-the-inputs-meet/index_files/figure-html/fig-coords-output-1.png" alt="Three columns of points. The raw-counts column has several points well above zero including one far above; the log and rank columns are both tightly clustered around zero." width="661" height="391" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-coords-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;3: The same eleven comparisons in three coordinate systems. The interaction is a property of the raw counts and survives neither transformation.
</figcaption>
</figure>
</div>
</div>
</div>
<div id="f7641996" class="cell" data-execution_count="7">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb10" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb10-1"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> label, dataset <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> targets:</span>
<span id="cb10-2">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>label<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">:"</span>)</span>
<span id="cb10-3">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> name <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> dataset:</span>
<span id="cb10-4">        effect, p <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> interaction(dataset, name)</span>
<span id="cb10-5">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> p <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.05</span>:</span>
<span id="cb10-6">            <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"    </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>name<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&lt;26}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>effect<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;+8.4f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">   p=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>p<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.3f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb10-7">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>()</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>raw counts:
    title x when you post       +0.0381   p=0.000
    title x title shape         +0.0090   p=0.007
    title x all metadata        +0.0220   p=0.001
    when x where                -0.0117   p=0.023

log1p(counts):
    title x the author          -0.0041   p=0.019
    when x author               -0.0077   p=0.001
    author x title shape        +0.0055   p=0.001

rank:
    title x where it links      +0.0035   p=0.013
    title x title shape         +0.0042   p=0.007
    where x title shape         +0.0014   p=0.035
    author x title shape        +0.0083   p=0.001
</code></pre>
</div>
</div>
<p>The big one is gone. <code>title x when you post</code> goes from +0.0381 to roughly zero under both transformations, and the cells that remain significant are different ones, an order of magnitude smaller.</p>
<p>A logarithm turns multiplication into addition, so an additive model fitted on <code>log1p</code> can represent precisely the thing it could not represent on counts. Rank keeps every comparison between posts and discards all the magnitudes, and the interaction goes with them.</p>
<p>So this interaction lives in <strong>how large the counts get</strong>, not in <strong>which post beats which.</strong> A service that ranked posts would not need early fusion at all. This one predicts counts, so it does.</p>
<p>There is one exception, and it’s the interesting one. <strong><code>author x title shape</code> survives every coordinate system</strong> — not significant on counts, +0.0055 on <code>log1p</code>, +0.0083 on rank. That one is about ordering rather than magnitude: a known name’s <code>Show HN:</code> really does land differently from a stranger’s, and it changes who beats whom rather than by how much.</p>
<p>I find this the most useful thing in the project, because it generalises well past fusion. “Is there an interaction in my data” is not a well-formed question until you have said what scale you are measuring on.</p>
<p>It also replicates, which matters given how many times I had to withdraw a reading of this comparison.</p>
<div id="c5368961" class="cell" data-execution_count="8">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb12" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb12-1"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'pairing'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&lt;26}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'first run'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;10}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'replication'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;12}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb12-2"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> name <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> (<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"title x when you post"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"title x all metadata"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"title x title shape"</span>):</span>
<span id="cb12-3">    first <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.median(counts[name][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"joint"</span>]) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> np.median(counts[name][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"additive-matched"</span>])</span>
<span id="cb12-4">    again <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.median(replicate[name][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"joint"</span>]) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> np.median(replicate[name][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"additive-matched"</span>])</span>
<span id="cb12-5">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>name<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&lt;26}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>first<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;+10.4f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>again<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;+12.4f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>pairing                     first run  replication
title x when you post         +0.0381      +0.0334
title x all metadata          +0.0220      +0.0222
title x title shape           +0.0090      +0.0134</code></pre>
</div>
</div>
</section>
<section id="late-fusions-actual-advantage" class="level2">
<h2 class="anchored" data-anchor-id="late-fusions-actual-advantage">Late fusion’s actual advantage</h2>
<p>The surveys don’t mainly claim late fusion is more accurate. They claim it’s more robust: when one input goes missing or bad, it can lean on the surviving branch, where early fusion has entangled everything in its first layer.</p>
<p>That is true, and it’s measurable. Train all three architectures on clean data, then damage one input only at inference.</p>
<div id="cell-fig-robust" class="cell" data-execution_count="9">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb14" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb14-1">damage <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"0.0"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"0.25"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"0.5"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"0.75"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"1.0"</span>]</span>
<span id="cb14-2"></span>
<span id="cb14-3">fig, ax <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> figure(height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">3.8</span>)</span>
<span id="cb14-4"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> data, colour, label <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> ((robust_128, COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>], <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"128 hidden units"</span>),</span>
<span id="cb14-5">                            (robust_512, COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"512 hidden units"</span>)):</span>
<span id="cb14-6">    advantage <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [np.median(data[<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"missing-title@</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>d<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"late-snoek"</span>])</span>
<span id="cb14-7">                 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> np.median(data[<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"missing-title@</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>d<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"early"</span>]) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> d <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> damage]</span>
<span id="cb14-8">    ax.plot([<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>(d) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> d <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> damage], advantage, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>colour, linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, marker<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"o"</span>,</span>
<span id="cb14-9">            markersize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>)</span>
<span id="cb14-10">    ax.annotate(label, xy<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.0</span>, advantage[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]), xytext<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>), textcoords<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"offset points"</span>,</span>
<span id="cb14-11">                ha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"right"</span>, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>colour)</span>
<span id="cb14-12"></span>
<span id="cb14-13">ax.axhline(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.8</span>)</span>
<span id="cb14-14">style_axes(ax, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Share of the title removed at inference"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Late fusion's advantage"</span>)</span>
<span id="cb14-15">fig.tight_layout()</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-display">
<div id="fig-robust" class="quarto-float quarto-figure quarto-figure-center anchored" alt="Two lines rising with damage severity. The 128-unit line rises steeply to about 0.017; the 512-unit line stays close to zero throughout.">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-robust-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://roshbeed.com/posts/2026-09-18-where-the-inputs-meet/index_files/figure-html/fig-robust-output-1.png" alt="Two lines rising with damage severity. The 128-unit line rises steeply to about 0.017; the 512-unit line stays close to zero throughout." width="659" height="353" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-robust-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;4: Late fusion’s advantage when an input is damaged, at two widths. It is real at 128 hidden units and much smaller at 512.
</figcaption>
</figure>
</div>
</div>
</div>
<div id="16d32557" class="cell" data-execution_count="10">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb15" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb15-1"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> data, label <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> ((robust_128, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"128 units"</span>), (robust_512, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"512 units"</span>)):</span>
<span id="cb15-2">    clean <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (np.median(data[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"missing-title@0.0"</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"late-snoek"</span>])</span>
<span id="cb15-3">             <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> np.median(data[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"missing-title@0.0"</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"early"</span>]))</span>
<span id="cb15-4">    broken <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (np.median(data[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"missing-title@1.0"</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"late-snoek"</span>])</span>
<span id="cb15-5">              <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> np.median(data[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"missing-title@1.0"</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"early"</span>]))</span>
<span id="cb15-6">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>label<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">: late fusion pays </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>clean<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:+.3f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> when nothing is wrong "</span></span>
<span id="cb15-7">          <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"to gain </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>broken<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:+.3f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> when the title is gone"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>128 units: late fusion pays +0.012 when nothing is wrong to gain +0.015 when the title is gone
512 units: late fusion pays +0.010 when nothing is wrong to gain +0.006 when the title is gone</code></pre>
</div>
</div>
<p>So the insurance is real, and <strong>it is also a small-model effect.</strong> At 128 hidden units late fusion gives up a little on clean data to gain a lot when the title disappears. At 512 it gives up about the same and gains far less, because early fusion has enough capacity to learn to cope on its own.</p>
</section>
<section id="what-id-actually-conclude" class="level2">
<h2 class="anchored" data-anchor-id="what-id-actually-conclude">What I’d actually conclude</h2>
<p>Four separate measurements — the fusion-point sweep, the width sweep, the pairing table and this robustness probe — all point the same way, and it isn’t the conclusion I expected to write:</p>
<p><strong>The architectural distinction mostly matters when something else is constrained.</strong> Given enough width, early fusion matches late fusion’s robustness and late fusion matches early fusion’s accuracy. The gap between them is largest exactly where the model is too small, and it closes as the model grows.</p>
<p>Which means the honest version of “early versus late fusion” is not a ranking. It’s a question about your particular data — is there an interaction to model? — and your particular budget. Quote a fusion result without the width it was measured at and you have not said anything.</p>
<p>The measurements, and the service they came from, are <a href="https://github.com/RoshBeed/ai-residency/tree/main/services/content-scoring">on GitHub</a>.</p>
<hr>
<p>[1] Snoek, Worring, Smeulders. <em>Early versus Late Fusion in Semantic Video Analysis.</em> ACM Multimedia 2005.</p>
<div id="2b2306f9" class="cell" data-execution_count="11">
<div class="cell-output cell-output-stdout">
<pre><code>Built:      2026-09-18 02:23:47 UTC
Python:     3.13.15
matplotlib: 3.11.2
numpy:      2.5.3
torch:      2.14.0+cpu</code></pre>
</div>
</div>


</section>

 ]]></description>
  <category>multimodal</category>
  <category>fusion</category>
  <category>deep-dive</category>
  <guid>https://roshbeed.com/posts/2026-09-18-where-the-inputs-meet/</guid>
  <pubDate>Fri, 18 Sep 2026 00:00:00 GMT</pubDate>
</item>
<item>
  <title>The cold-start tax on scaling to zero</title>
  <dc:creator>Rosh Beed</dc:creator>
  <link>https://roshbeed.com/posts/2026-09-18-the-cold-start-tax/</link>
  <description><![CDATA[ 




<p>I’m rebuilding this site as the front door to nine machine learning demos. Each one is its own container image, each one scales to zero, and each one therefore pays a cold start when it wakes up. On a multi-gigabyte image that is ten to thirty seconds of somebody staring at a spinner.</p>
<p>The instinct is to keep everything warm. That instinct is expensive, and mostly wrong. Before paying for it, it’s worth asking a narrower question: <strong>what fraction of requests actually hit a cold container?</strong></p>
<section id="the-model" class="level2">
<h2 class="anchored" data-anchor-id="the-model">The model</h2>
<p>Take one endpoint with a single warm worker and an idle timeout <img src="https://latex.codecogs.com/png.latex?T"> — the platform keeps the container alive for <img src="https://latex.codecogs.com/png.latex?T"> after the last request, then reclaims it. Assume requests arrive as a Poisson process with rate <img src="https://latex.codecogs.com/png.latex?%5Clambda">.</p>
<p>A request finds the container cold exactly when the gap since the previous request exceeded the idle timeout. For Poisson arrivals those gaps are exponential, so:</p>
<p><img src="https://latex.codecogs.com/png.latex?P(%5Ctext%7Bcold%7D)%20=%20e%5E%7B-%5Clambda%20T%7D"></p>
<p>That’s the whole model. It is crude — it ignores concurrency, platform-side reclamation before the timeout, and the fact that traffic to a portfolio site is nothing like Poisson — but it gets the shape right, and the shape is what decides the architecture.</p>
</section>
<section id="what-it-looks-like" class="level2">
<h2 class="anchored" data-anchor-id="what-it-looks-like">What it looks like</h2>
<p>The traffic axis is logarithmic. A portfolio site lives at the left-hand end of it.</p>
<div id="cell-fig-cold-start" class="cell" data-execution_count="1">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> matplotlib.pyplot <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> plt</span>
<span id="cb1-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> numpy <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> np</span>
<span id="cb1-3"></span>
<span id="cb1-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Categorical slots 1-3 of a CVD-validated palette (blue, orange, aqua).</span></span>
<span id="cb1-5">COLOURS <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#2a78d6"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#eb6834"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#1baf7a"</span>]</span>
<span id="cb1-6">MUTED, GRID, AXIS <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#5b6570"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#e6e6e3"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#d5d5d1"</span></span>
<span id="cb1-7"></span>
<span id="cb1-8">requests_per_hour <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.logspace(np.log10(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</span>), np.log10(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">200</span>), <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">400</span>)</span>
<span id="cb1-9">lam <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> requests_per_hour <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">60</span>  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># arrivals per minute</span></span>
<span id="cb1-10"></span>
<span id="cb1-11">fig, ax <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> plt.subplots(figsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">7</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">4.2</span>))</span>
<span id="cb1-12"></span>
<span id="cb1-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Each curve is direct-labelled at a different height so the labels never</span></span>
<span id="cb1-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># collide, on a surface-coloured halo so a neighbouring curve passing behind a</span></span>
<span id="cb1-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># label is cleanly interrupted rather than striking through the text.</span></span>
<span id="cb1-16"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> colour, timeout_min, label_at <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">zip</span>(COLOURS, (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">15</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">60</span>), (<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.80</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.55</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.30</span>)):</span>
<span id="cb1-17">    p_cold <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.exp(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>lam <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> timeout_min)</span>
<span id="cb1-18">    ax.plot(requests_per_hour, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> p_cold, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>colour, linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb1-19">    ax.annotate(</span>
<span id="cb1-20">        <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>timeout_min<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> min idle timeout"</span>,</span>
<span id="cb1-21">        xy<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">60</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>np.log(label_at) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> timeout_min, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> label_at),</span>
<span id="cb1-22">        xytext<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>),</span>
<span id="cb1-23">        textcoords<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"offset points"</span>,</span>
<span id="cb1-24">        fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>,</span>
<span id="cb1-25">        color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>colour,</span>
<span id="cb1-26">        bbox<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">dict</span>(facecolor<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"white"</span>, edgecolor<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"none"</span>, pad<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>),</span>
<span id="cb1-27">    )</span>
<span id="cb1-28"></span>
<span id="cb1-29">ax.set_xscale(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"log"</span>)</span>
<span id="cb1-30">ax.set_xlim(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">200</span>)</span>
<span id="cb1-31">ax.set_ylim(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>)</span>
<span id="cb1-32">ax.set_xticks([<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>])</span>
<span id="cb1-33">ax.set_xticklabels([<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"1"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"10"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"100"</span>])</span>
<span id="cb1-34">ax.set_xlabel(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Requests per hour"</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>)</span>
<span id="cb1-35">ax.set_ylabel(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Requests hitting a cold start (%)"</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>)</span>
<span id="cb1-36"></span>
<span id="cb1-37">ax.grid(axis<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"y"</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>GRID, linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.8</span>)</span>
<span id="cb1-38">ax.set_axisbelow(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span>
<span id="cb1-39"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> side <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> (<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"top"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"right"</span>):</span>
<span id="cb1-40">    ax.spines[side].set_visible(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>)</span>
<span id="cb1-41"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> side <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> (<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"left"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bottom"</span>):</span>
<span id="cb1-42">    ax.spines[side].set_color(AXIS)</span>
<span id="cb1-43">ax.tick_params(colors<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, labelsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>, length<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb1-44"></span>
<span id="cb1-45">fig.tight_layout()</span>
<span id="cb1-46">plt.show()</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-display">
<div id="fig-cold-start" class="quarto-float quarto-figure quarto-figure-center anchored" alt="Three S-shaped curves falling from left to right as traffic rises. The 60-minute idle timeout falls to near zero by 10 requests an hour; the 5-minute timeout is still near 20% at 20 requests an hour.">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-cold-start-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://roshbeed.com/posts/2026-09-18-the-cold-start-tax/index_files/figure-html/fig-cold-start-output-1.png" alt="Three S-shaped curves falling from left to right as traffic rises. The 60-minute idle timeout falls to near zero by 10 requests an hour; the 5-minute timeout is still near 20% at 20 requests an hour." width="660" height="393" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-cold-start-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;1: Share of requests that hit a cold container, under Poisson arrivals, for three idle timeouts. Note the logarithmic traffic axis.
</figcaption>
</figure>
</div>
</div>
</div>
<p>Two things fall out of that picture.</p>
<p><strong>At low traffic, a short idle timeout is no protection at all.</strong> At 20 requests an hour — a good day for a personal site — a 5-minute timeout still leaves roughly one request in five landing cold. Shortening the tail of the curve requires traffic you do not have.</p>
<p><strong>The curve is steep in the timeout, not the traffic.</strong> Going from 5 to 60 minutes of idle time buys far more than any plausible increase in visitors. Which is the useful result: if you want fewer cold starts on a quiet service, you buy idle time, not popularity.</p>
</section>
<section id="what-im-doing-with-it" class="level2">
<h2 class="anchored" data-anchor-id="what-im-doing-with-it">What I’m doing with it</h2>
<p>The nine inference endpoints stay on Lambda container images and stay scaled to zero. Their cold starts are real but they are <em>inside</em> an interaction the user has already committed to — they clicked “run”, and a spinner covers it honestly.</p>
<p>The one thing that cannot be cold is the front door. A visitor who waits twenty seconds for the first paint leaves; a visitor who waits twenty seconds for a model they asked to run does not. So the Streamlit front-end lives on something persistent with a warm instance, and the expensive-to-warm nine stay cheap.</p>
<p>That’s a single provisioned instance instead of ten. The model above is what makes that an argument rather than a preference.</p>
</section>
<section id="a-note-on-how-this-page-was-made" class="level2">
<h2 class="anchored" data-anchor-id="a-note-on-how-this-page-was-made">A note on how this page was made</h2>
<p>The figure above is not an image I committed. It is the output of the code cell you can read, executed by Quarto when this page was built, in a locked Python environment. The proof is below — it is printed at build time, so it cannot be stale:</p>
<div id="9e765a8d" class="cell" data-execution_count="2">
<div class="cell-output cell-output-stdout">
<pre><code>Built:      2026-09-18 02:23:49 UTC
Python:     3.13.15
matplotlib: 3.11.2
numpy:      2.5.3</code></pre>
</div>
</div>


</section>

 ]]></description>
  <category>architecture</category>
  <category>serverless</category>
  <category>aws</category>
  <guid>https://roshbeed.com/posts/2026-09-18-the-cold-start-tax/</guid>
  <pubDate>Fri, 18 Sep 2026 00:00:00 GMT</pubDate>
</item>
<item>
  <title>Fine-tuning on one example, and knowing when to stop</title>
  <dc:creator>Rosh Beed</dc:creator>
  <link>https://roshbeed.com/posts/2026-09-18-knowing-when-to-stop/</link>
  <description><![CDATA[ 




<p>Week 5: speech. The project was fine-tuning Whisper [1] on exactly one clip — a recording where the base model mishears my name — and watching a handful of gradient steps correct it.</p>
<p>One example rather than a dataset, on purpose. Fine-tuning is normally reported as a number moving on a held-out set, which is honest and tells you nothing about what actually happened. With one example you can watch the mechanism.</p>
<p>Two things become visible that way. The first is that it works, quickly. The second is what it costs, which is the part I hadn’t appreciated.</p>
<section id="first-how-audio-becomes-something-a-transformer-can-read" class="level2">
<h2 class="anchored" data-anchor-id="first-how-audio-becomes-something-a-transformer-can-read">First, how audio becomes something a transformer can read</h2>
<p>A transformer needs a sequence of vectors. A waveform is a very long list of amplitudes — 16,000 numbers a second — with the useful structure spread across frequencies rather than sitting in the samples.</p>
<p>The standard answer is a log-mel spectrogram, and it’s three steps:</p>
<ol type="1">
<li><strong>Chop the waveform into short overlapping frames</strong> and take the Fourier transform of each. Now you have how much energy sits at each frequency, over time.</li>
<li><strong>Squash the frequency axis onto the mel scale</strong>, which spaces bands the way hearing does — fine detail low down, coarser high up. 201 frequency bins become 80 mel bands.</li>
<li><strong>Take the log</strong>, because loudness is perceived multiplicatively.</li>
</ol>
<p>What comes out is a picture: 80 rows by however many frames. That’s what the model sees. Here it is on the actual clip from the project, computed from scratch.</p>
<div id="7a921a84" class="cell" data-execution_count="1">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> sys</span>
<span id="cb1-2"></span>
<span id="cb1-3">sys.path.insert(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".."</span>)</span>
<span id="cb1-4"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> numpy <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> np</span>
<span id="cb1-5"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> huggingface_hub <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> hf_hub_download</span>
<span id="cb1-6"></span>
<span id="cb1-7">REVISION <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"f705fed08827ff6c36e3b5329495c943a5e544e8"</span></span>
<span id="cb1-8">clip <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.load(hf_hub_download(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"roshbeed/ai-residency-blog-data"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"audio/clip-16k.npz"</span>,</span>
<span id="cb1-9">                               repo_type<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"dataset"</span>, revision<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>REVISION))</span>
<span id="cb1-10">waveform, rate <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> clip[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"waveform"</span>], <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>(clip[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"sample_rate"</span>])</span>
<span id="cb1-11"></span>
<span id="cb1-12">N_FFT, HOP, N_MELS <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">400</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">160</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">80</span></span>
<span id="cb1-13"></span>
<span id="cb1-14"></span>
<span id="cb1-15"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> stft(x, n_fft, hop):</span>
<span id="cb1-16">    window <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.hanning(n_fft <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)[:<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]</span>
<span id="cb1-17">    frames <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> (<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(x) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> n_fft) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">//</span> hop</span>
<span id="cb1-18">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> np.stack([np.fft.rfft(x[i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> hop:i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> hop <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> n_fft] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> window)</span>
<span id="cb1-19">                     <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(frames)], axis<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb1-20"></span>
<span id="cb1-21"></span>
<span id="cb1-22"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> mel_filterbank(rate, n_fft, n_mels):</span>
<span id="cb1-23">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""Triangular filters, evenly spaced on the mel scale."""</span></span>
<span id="cb1-24">    to_mel <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">lambda</span> f: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2595</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> np.log10(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> f <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">700</span>)</span>
<span id="cb1-25">    to_hz <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">lambda</span> m: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">700</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span> (m <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2595</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb1-26"></span>
<span id="cb1-27">    edges <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> to_hz(np.linspace(to_mel(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>), to_mel(rate <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>), n_mels <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>))</span>
<span id="cb1-28">    bins <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.floor((n_fft <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> edges <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> rate).astype(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>)</span>
<span id="cb1-29"></span>
<span id="cb1-30">    bank <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.zeros((n_mels, n_fft <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">//</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>))</span>
<span id="cb1-31">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> m <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, n_mels <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>):</span>
<span id="cb1-32">        left, centre, right <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> bins[m <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], bins[m], bins[m <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]</span>
<span id="cb1-33">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> centre <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> left:</span>
<span id="cb1-34">            bank[m <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, left:centre] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (np.arange(left, centre) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> left) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> (centre <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> left)</span>
<span id="cb1-35">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> right <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> centre:</span>
<span id="cb1-36">            bank[m <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, centre:right] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (right <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> np.arange(centre, right)) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> (right <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> centre)</span>
<span id="cb1-37">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> bank</span>
<span id="cb1-38"></span>
<span id="cb1-39"></span>
<span id="cb1-40">power <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">abs</span>(stft(waveform, N_FFT, HOP)) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span></span>
<span id="cb1-41">log_mel <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.log10(np.maximum(mel_filterbank(rate, N_FFT, N_MELS) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@</span> power, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1e-10</span>))</span>
<span id="cb1-42"></span>
<span id="cb1-43"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(waveform)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:,}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> samples at </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>rate<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> Hz = </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(waveform) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> rate<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.2f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> seconds"</span>)</span>
<span id="cb1-44"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"becomes a </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>log_mel<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> x </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>log_mel<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> picture"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>16,982 samples at 16000 Hz = 1.06 seconds
becomes a 80 x 104 picture</code></pre>
</div>
</div>
<div id="cell-fig-mel" class="cell" data-execution_count="2">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb3-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> matplotlib.pyplot <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> plt</span>
<span id="cb3-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> _style <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> COLOURS, MUTED, style_axes</span>
<span id="cb3-3"></span>
<span id="cb3-4">fig, (top, bottom) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> plt.subplots(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, figsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">7.4</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">4.4</span>),</span>
<span id="cb3-5">                                  gridspec_kw<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>{<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"height_ratios"</span>: [<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>]})</span>
<span id="cb3-6"></span>
<span id="cb3-7">top.plot(np.arange(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(waveform)) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> rate, waveform, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>], linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</span>)</span>
<span id="cb3-8">top.set_xlim(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(waveform) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> rate)</span>
<span id="cb3-9">style_axes(top, ylabel<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"amplitude"</span>, grid<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>)</span>
<span id="cb3-10">top.set_xticks([])</span>
<span id="cb3-11"></span>
<span id="cb3-12">bottom.imshow(log_mel, aspect<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"auto"</span>, origin<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"lower"</span>, cmap<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"magma"</span>,</span>
<span id="cb3-13">              extent<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(waveform) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> rate, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, N_MELS))</span>
<span id="cb3-14">style_axes(bottom, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Seconds"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Mel band"</span>, grid<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>)</span>
<span id="cb3-15"></span>
<span id="cb3-16">fig.tight_layout()</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-display">
<div id="fig-mel" class="quarto-float quarto-figure quarto-figure-center anchored" alt="A waveform above, and below it a spectrogram with bright horizontal bands in the lower frequencies that shift as the speech changes.">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-mel-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://roshbeed.com/posts/2026-09-18-knowing-when-to-stop/index_files/figure-html/fig-mel-output-1.png" alt="A waveform above, and below it a spectrogram with bright horizontal bands in the lower frequencies that shift as the speech changes." width="699" height="412" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-mel-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;1: One second of speech, before and after. The model never sees the waveform on top; it reads the picture underneath.
</figcaption>
</figure>
</div>
</div>
</div>
</section>
<section id="a-speech-model-small-enough-to-watch" class="level2">
<h2 class="anchored" data-anchor-id="a-speech-model-small-enough-to-watch">A speech model small enough to watch</h2>
<p>Whisper is an encoder–decoder transformer over exactly that picture — the same shape as the <a href="../2026-09-18-when-it-has-finished/">multi-digit reader</a> from week 3, with a spectrogram in place of an image.</p>
<p>I can’t run Whisper while this page builds, so here’s the same architecture at a size that trains in seconds, on a language I can synthesise: words of three letters, where each letter is a tone at its own frequency. Speaking a word means playing its tones in order; transcribing it means reading them back.</p>
<div id="6bdf17b4" class="cell" data-execution_count="3">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb4-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> torch</span>
<span id="cb4-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> torch.nn <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> nn</span>
<span id="cb4-3"></span>
<span id="cb4-4">RATE, TONE_SECONDS, NOISE <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8000</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.08</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.15</span></span>
<span id="cb4-5">ALPHABET <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"abcdefgh"</span></span>
<span id="cb4-6">TONES <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> {c: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">300</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.28</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span> i <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i, c <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">enumerate</span>(ALPHABET)}</span>
<span id="cb4-7"></span>
<span id="cb4-8">rng <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.random.default_rng(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb4-9">WORDS <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sorted</span>({<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span>.join(rng.choice(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">list</span>(ALPHABET), <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>)) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> _ <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">60</span>)})[:<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">40</span>]</span>
<span id="cb4-10"></span>
<span id="cb4-11"></span>
<span id="cb4-12"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> speak(word, seed, shift<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.0</span>):</span>
<span id="cb4-13">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""Play each letter's tone in turn, with a little jitter and noise."""</span></span>
<span id="cb4-14">    g <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.random.default_rng(seed)</span>
<span id="cb4-15">    parts <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []</span>
<span id="cb4-16">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> c <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> word:</span>
<span id="cb4-17">        t <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.arange(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>(RATE <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> TONE_SECONDS)) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> RATE</span>
<span id="cb4-18">        f <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> TONES[c] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> shift <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.02</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> g.normal())</span>
<span id="cb4-19">        parts.append((np.sin(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> np.pi <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> f <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> t) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.4</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> np.sin(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> np.pi <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> f <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> t))</span>
<span id="cb4-20">                     <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> np.hanning(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(t)))</span>
<span id="cb4-21">    x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.concatenate(parts)</span>
<span id="cb4-22">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> (x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> NOISE <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> g.normal(size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(x))).astype(np.float32)</span>
<span id="cb4-23"></span>
<span id="cb4-24"></span>
<span id="cb4-25">SMALL_FFT, SMALL_HOP, SMALL_MELS <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">256</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">64</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">32</span></span>
<span id="cb4-26">BANK <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> mel_filterbank(RATE, SMALL_FFT, SMALL_MELS)</span>
<span id="cb4-27"></span>
<span id="cb4-28"></span>
<span id="cb4-29"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> to_picture(x):</span>
<span id="cb4-30">    power <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">abs</span>(stft(x, SMALL_FFT, SMALL_HOP)) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span></span>
<span id="cb4-31">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> np.log10(np.maximum(BANK <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@</span> power, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1e-10</span>)).T.astype(np.float32)</span>
<span id="cb4-32"></span>
<span id="cb4-33"></span>
<span id="cb4-34"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> build(repeats, seed0):</span>
<span id="cb4-35">    pictures, labels <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [], []</span>
<span id="cb4-36">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> index, word <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">enumerate</span>(WORDS):</span>
<span id="cb4-37">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> k <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(repeats):</span>
<span id="cb4-38">            pictures.append(to_picture(speak(word, seed0 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> index <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1000</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> k)))</span>
<span id="cb4-39">            labels.append(index)</span>
<span id="cb4-40">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> torch.from_numpy(np.stack(pictures)), torch.tensor(labels)</span>
<span id="cb4-41"></span>
<span id="cb4-42"></span>
<span id="cb4-43">X, Y <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> build(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb4-44">X_test, Y_test <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> build(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">500_000</span>)</span>
<span id="cb4-45"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(WORDS)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> words, </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(X)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> training clips of shape </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>(X.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>:])<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>40 words, 480 training clips of shape (27, 32)</code></pre>
</div>
</div>
<div id="5a28f798" class="cell" data-execution_count="4">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb6-1">LETTERS <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sorted</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">set</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span>.join(WORDS)))</span>
<span id="cb6-2">START, END <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(LETTERS), <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(LETTERS) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span>
<span id="cb6-3">VOCAB <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(LETTERS) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span></span>
<span id="cb6-4">DIM, FRAMES <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">64</span>, X.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]</span>
<span id="cb6-5"></span>
<span id="cb6-6">TARGET <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.tensor([[START] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> [LETTERS.index(c) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> c <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> w] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> [END] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> w <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> WORDS])</span>
<span id="cb6-7"></span>
<span id="cb6-8"></span>
<span id="cb6-9"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> Transcriber(nn.Module):</span>
<span id="cb6-10">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""An encoder over the spectrogram, a decoder that spells the word."""</span></span>
<span id="cb6-11"></span>
<span id="cb6-12">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>):</span>
<span id="cb6-13">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">super</span>().<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>()</span>
<span id="cb6-14">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Linear(SMALL_MELS, DIM)</span>
<span id="cb6-15">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.audio_positions <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Parameter(torch.randn(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, FRAMES, DIM) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.02</span>)</span>
<span id="cb6-16">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.encoder <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.TransformerEncoder(</span>
<span id="cb6-17">            nn.TransformerEncoderLayer(DIM, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> DIM, batch_first<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>,</span>
<span id="cb6-18">                                       norm_first<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>, dropout<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.0</span>), <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb6-19"></span>
<span id="cb6-20">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.embed <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Embedding(VOCAB, DIM)</span>
<span id="cb6-21">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.text_positions <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Parameter(torch.randn(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>, DIM) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.02</span>)</span>
<span id="cb6-22">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.decoder <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.TransformerDecoder(</span>
<span id="cb6-23">            nn.TransformerDecoderLayer(DIM, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> DIM, batch_first<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>,</span>
<span id="cb6-24">                                       norm_first<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>, dropout<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.0</span>), <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb6-25">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.out <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Linear(DIM, VOCAB)</span>
<span id="cb6-26"></span>
<span id="cb6-27">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> forward(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, picture, tokens):</span>
<span id="cb6-28">        memory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.encoder(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>(picture) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.audio_positions)</span>
<span id="cb6-29">        h <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.embed(tokens) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.text_positions[:, :tokens.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]]</span>
<span id="cb6-30">        mask <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Transformer.generate_square_subsequent_mask(tokens.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>])</span>
<span id="cb6-31">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.out(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.decoder(h, memory, tgt_mask<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>mask))</span>
<span id="cb6-32"></span>
<span id="cb6-33"></span>
<span id="cb6-34">torch.manual_seed(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb6-35">model <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Transcriber()</span>
<span id="cb6-36">optimiser <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.optim.AdamW(model.parameters(), lr<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1e-3</span>, weight_decay<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.01</span>)</span>
<span id="cb6-37">generator <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.Generator().manual_seed(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb6-38"></span>
<span id="cb6-39"></span>
<span id="cb6-40"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@torch.no_grad</span>()</span>
<span id="cb6-41"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> transcribe(pictures):</span>
<span id="cb6-42">    tokens <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.full((<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(pictures), <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>), START)</span>
<span id="cb6-43">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> _ <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>):</span>
<span id="cb6-44">        tokens <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.cat([tokens, model(pictures, tokens)[:, <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>].argmax(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, keepdim<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)], <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb6-45">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> tokens[:, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>:]</span>
<span id="cb6-46"></span>
<span id="cb6-47"></span>
<span id="cb6-48"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> spell(ids):</span>
<span id="cb6-49">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span>.join(LETTERS[i] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> ids <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(LETTERS))</span>
<span id="cb6-50"></span>
<span id="cb6-51"></span>
<span id="cb6-52"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> accuracy():</span>
<span id="cb6-53">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> (transcribe(X_test) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> TARGET[Y_test][:, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>:<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>]).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">all</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>().mean().item()</span></code></pre></div></div>
</details>
</div>
<div id="cell-fig-arch" class="cell" data-execution_count="5">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb7-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> _arch <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> diagram</span>
<span id="cb7-2"></span>
<span id="cb7-3">diagram(Transcriber(), input_shape<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>((<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, FRAMES, SMALL_MELS), (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>)), style<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"flow"</span>,</span>
<span id="cb7-4">        input_dtype<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(torch.float32, torch.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">long</span>))</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-display" data-execution_count="5">
<div id="fig-arch" class="quarto-float quarto-figure quarto-figure-center anchored" alt="A diagram with two input streams on the left, one from the spectrogram and one through an embedding, converging into a single output column.">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-arch-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://roshbeed.com/posts/2026-09-18-knowing-when-to-stop/index_files/figure-html/fig-arch-output-1.png" class="img-fluid figure-img" alt="A diagram with two input streams on the left, one from the spectrogram and one through an embedding, converging into a single output column.">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-arch-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;2: Whisper’s shape at 1/1000th the size: a spectrogram into the encoder, the text so far into the decoder, one letter out.
</figcaption>
</figure>
</div>
</div>
</div>
<div id="8fe19a48" class="cell" data-execution_count="6">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb8-1"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> epoch <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">20</span>):</span>
<span id="cb8-2">    perm <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.randperm(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(X), generator<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>generator)</span>
<span id="cb8-3">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(perm) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">64</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">64</span>):</span>
<span id="cb8-4">        b <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> perm[i:i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">64</span>]</span>
<span id="cb8-5">        target <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> TARGET[Y[b]]</span>
<span id="cb8-6">        loss <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.functional.cross_entropy(model(X[b], target[:, :<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]).reshape(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, VOCAB),</span>
<span id="cb8-7">                                           target[:, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>:].reshape(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>))</span>
<span id="cb8-8">        optimiser.zero_grad()</span>
<span id="cb8-9">        loss.backward()</span>
<span id="cb8-10">        optimiser.step()</span>
<span id="cb8-11"></span>
<span id="cb8-12"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"transcribes </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>accuracy()<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.1%}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> of held-out clips exactly right"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>transcribes 99.4% of held-out clips exactly right</code></pre>
</div>
</div>
</section>
<section id="now-an-accent-it-has-never-heard" class="level2">
<h2 class="anchored" data-anchor-id="now-an-accent-it-has-never-heard">Now an accent it has never heard</h2>
<p>The base model has only ever heard these tones at their standard frequencies. Say a word with everything shifted up 10% — a different voice, the same word — and it mishears.</p>
<div id="f652a6a8" class="cell" data-execution_count="7">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb10" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb10-1">WORD, SHIFT <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"acd"</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.10</span></span>
<span id="cb10-2">one_clip <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.from_numpy(to_picture(speak(WORD, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">999</span>, SHIFT))).unsqueeze(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb10-3">one_target <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> TARGET[WORDS.index(WORD)].unsqueeze(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb10-4"></span>
<span id="cb10-5"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"the word is </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>WORD<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!r}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb10-6"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"the model hears </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>spell(transcribe(one_clip)[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>].tolist())<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!r}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb10-7"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"and it is otherwise fine: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>accuracy()<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.1%}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> on the clean test clips"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>the word is 'acd'
the model hears 'adc'
and it is otherwise fine: 99.4% on the clean test clips</code></pre>
</div>
</div>
<p>This is the situation the project was in: a model that works, and one specific thing it gets wrong. So fine-tune it on that one clip and nothing else, and watch both numbers at once.</p>
<div id="f970785d" class="cell" data-execution_count="8">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb12" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb12-1">fine_tune <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.optim.AdamW(model.parameters(), lr<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1e-4</span>)</span>
<span id="cb12-2"></span>
<span id="cb12-3"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'step'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;5}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'loss'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;10}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'hears'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;8}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'clean accuracy'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;16}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb12-4"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;5}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'-'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;10}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>spell(transcribe(one_clip)[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>].tolist())<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;8}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>accuracy()<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;15.1%}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb12-5"></span>
<span id="cb12-6">steps, losses, clean <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [], [], []</span>
<span id="cb12-7"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> step <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">13</span>):</span>
<span id="cb12-8">    loss <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.functional.cross_entropy(</span>
<span id="cb12-9">        model(one_clip, one_target[:, :<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]).reshape(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, VOCAB), one_target[:, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>:].reshape(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>))</span>
<span id="cb12-10">    fine_tune.zero_grad()</span>
<span id="cb12-11">    loss.backward()</span>
<span id="cb12-12">    fine_tune.step()</span>
<span id="cb12-13"></span>
<span id="cb12-14">    steps.append(step)</span>
<span id="cb12-15">    losses.append(loss.item())</span>
<span id="cb12-16">    clean.append(accuracy())</span>
<span id="cb12-17">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> step <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>:</span>
<span id="cb12-18">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>step<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;5}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>loss<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>item()<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;10.5f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>spell(transcribe(one_clip)[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>].tolist())<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;8}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> "</span></span>
<span id="cb12-19">              <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>clean[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;15.1%}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code> step       loss    hears   clean accuracy
    0          -      adc           99.4%
    2    0.25263      acd           99.4%
    4    0.01363      acd           97.5%
    6    0.00430      acd           96.9%
    8    0.01027      acd           94.4%
   10    0.01837      acd           93.1%
   12    0.01824      acd           92.5%</code></pre>
</div>
</div>
<div id="cell-fig-forget" class="cell" data-execution_count="9">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb14" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb14-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> _style <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> figure</span>
<span id="cb14-2"></span>
<span id="cb14-3">fig, ax <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> figure(height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">4.0</span>)</span>
<span id="cb14-4">ax.plot(steps, losses, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb14-5">style_axes(ax, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Fine-tuning step on the single clip"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Loss on that clip"</span>)</span>
<span id="cb14-6">ax.annotate(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"loss on the one clip"</span>, xy<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(steps[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], losses[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]), xytext<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">14</span>),</span>
<span id="cb14-7">            textcoords<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"offset points"</span>, ha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"right"</span>, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>])</span>
<span id="cb14-8"></span>
<span id="cb14-9">right <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ax.twinx()</span>
<span id="cb14-10">right.plot(steps, clean, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>], linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb14-11">right.set_ylabel(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Accuracy on the other words"</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>)</span>
<span id="cb14-12">right.tick_params(colors<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, labelsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>, length<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb14-13">right.spines[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"top"</span>].set_visible(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>)</span>
<span id="cb14-14">right.annotate(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"everything else"</span>, xy<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(steps[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], clean[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]), xytext<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>, <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">16</span>),</span>
<span id="cb14-15">               textcoords<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"offset points"</span>, ha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"right"</span>, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>])</span>
<span id="cb14-16"></span>
<span id="cb14-17">fig.tight_layout()</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-display">
<div id="fig-forget" class="quarto-float quarto-figure quarto-figure-center anchored" alt="Two lines over twelve steps. The loss falls sharply to near zero within four steps. The clean accuracy line stays flat briefly and then declines steadily.">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-forget-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://roshbeed.com/posts/2026-09-18-knowing-when-to-stop/index_files/figure-html/fig-forget-output-1.png" alt="Two lines over twelve steps. The loss falls sharply to near zero within four steps. The clean accuracy line stays flat briefly and then declines steadily." width="658" height="373" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-forget-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;3: The fix lands almost immediately. Everything after that is the model memorising one clip at the expense of the other forty words.
</figcaption>
</figure>
</div>
</div>
</div>
</section>
<section id="what-that-shows" class="level2">
<h2 class="anchored" data-anchor-id="what-that-shows">What that shows</h2>
<p><strong>The fix is cheap.</strong> Two gradient steps and the model hears the word correctly. That’s the appealing part, and it’s real — on the actual project a handful of steps on one clip corrected a name the base model consistently got wrong.</p>
<p><strong>The loss goes to nearly zero, and that is not good news.</strong> One example, a loss of 0.004 — the model has memorised a single clip. Memorisation is precisely what you’re asking for here, and it’s also the thing that goes wrong when you scale this up without noticing.</p>
<p><strong>You pay for it somewhere you weren’t looking.</strong> Accuracy on the other 39 words drops steadily from step 4 onward, and by step 12 it has given up several points to keep improving on a clip it already got right. Nothing in the fine-tuning loop reports that. You have to go and measure it.</p>
<p>So the interesting question isn’t whether fine-tuning works. It’s <strong>when to stop</strong>, and the only way to answer it is to keep evaluating the thing you’re not training on.</p>
</section>
<section id="one-whisper-specific-trap" class="level2">
<h2 class="anchored" data-anchor-id="one-whisper-specific-trap">One Whisper-specific trap</h2>
<p>Worth writing down because it cost me time and produced no error at all.</p>
<p>Whisper’s tokenizer starts a transcript with a <code>&lt;|startoftranscript|&gt;</code> token. It is tempting to pass the tokenizer’s output straight through as the training labels. Don’t: the model prepends <code>decoder_start_token_id</code> itself when it shifts the labels right, and that token <strong>is</strong> start-of-transcript. Pass the tokenizer’s output unchanged and every position is off by one.</p>
<p>Nothing raises. The loss goes down. The model is simply learning a different task than the one you meant.</p>
<p>The full project is <a href="https://github.com/RoshBeed/ai-residency/tree/main/services/whisper-fine-tuning">on GitHub</a>.</p>
<hr>
<p>[1] Radford et al.&nbsp;<em>Robust Speech Recognition via Large-Scale Weak Supervision.</em> ICML 2023.</p>
<div id="ffe02431" class="cell" data-execution_count="10">
<div class="cell-output cell-output-stdout">
<pre><code>Built:      2026-09-18 02:23:57 UTC
Python:     3.13.15
matplotlib: 3.11.2
numpy:      2.5.3
torch:      2.14.0+cpu</code></pre>
</div>
</div>


</section>

 ]]></description>
  <category>audio</category>
  <category>speech</category>
  <category>fine-tuning</category>
  <category>week-5</category>
  <guid>https://roshbeed.com/posts/2026-09-18-knowing-when-to-stop/</guid>
  <pubDate>Fri, 18 Sep 2026 00:00:00 GMT</pubDate>
</item>
<item>
  <title>An image is not a sequence, so you make it one</title>
  <dc:creator>Rosh Beed</dc:creator>
  <link>https://roshbeed.com/posts/2026-09-18-image-is-not-a-sequence/</link>
  <description><![CDATA[ 




<p>Week 3: take the transformer, which was designed for text, and point it at images. No convolutions.</p>
<p>The awkward part is that a transformer eats a sequence of tokens, and an image isn’t one. It’s a grid of pixels with no natural order and far too many of them — 28×28 is 784 pixels, and attention costs grow with the square of the sequence length. On a real photograph you’d be computing attention over hundreds of thousands of positions.</p>
<p>The Vision Transformer paper [1] solves this in one move, in the first layer, and everything after it is the ordinary transformer with nothing changed: <strong>cut the image into fixed-size squares and treat each square as a word.</strong></p>
<div id="3a68183f" class="cell" data-execution_count="1">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> sys</span>
<span id="cb1-2"></span>
<span id="cb1-3">sys.path.insert(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".."</span>)</span>
<span id="cb1-4"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> numpy <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> np</span>
<span id="cb1-5"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> torch</span>
<span id="cb1-6"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> torch.nn <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> nn</span>
<span id="cb1-7"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> huggingface_hub <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> hf_hub_download</span>
<span id="cb1-8"></span>
<span id="cb1-9">REVISION <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"f705fed08827ff6c36e3b5329495c943a5e544e8"</span></span>
<span id="cb1-10">path <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> hf_hub_download(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"roshbeed/ai-residency-blog-data"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"mnist/mnist-small.npz"</span>,</span>
<span id="cb1-11">                       repo_type<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"dataset"</span>, revision<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>REVISION)</span>
<span id="cb1-12">data <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.load(path)</span>
<span id="cb1-13"></span>
<span id="cb1-14">x_train <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.from_numpy(data[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"x_train"</span>]).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>() <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">255.0</span></span>
<span id="cb1-15">y_train <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.from_numpy(data[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"y_train"</span>]).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">long</span>()</span>
<span id="cb1-16">x_test <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.from_numpy(data[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"x_test"</span>]).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>() <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">255.0</span></span>
<span id="cb1-17">y_test <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.from_numpy(data[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"y_test"</span>]).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">long</span>()</span>
<span id="cb1-18"></span>
<span id="cb1-19">PATCH <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">7</span></span>
<span id="cb1-20">PATCHES <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">28</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">//</span> PATCH) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span></span>
<span id="cb1-21"></span>
<span id="cb1-22"></span>
<span id="cb1-23"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> patchify(images):</span>
<span id="cb1-24">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""28x28 -&gt; 16 patches of 7x7, flattened. This is the whole trick."""</span></span>
<span id="cb1-25">    batch <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> images.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]</span>
<span id="cb1-26">    tiles <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> images.unfold(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, PATCH, PATCH).unfold(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, PATCH, PATCH)</span>
<span id="cb1-27">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> tiles.reshape(batch, PATCHES, PATCH <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> PATCH)</span>
<span id="cb1-28"></span>
<span id="cb1-29"></span>
<span id="cb1-30">baseline <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.bincount(y_test).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">max</span>().item() <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(y_test)</span>
<span id="cb1-31"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(x_train)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:,}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> training digits, </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(x_test)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:,}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> test digits"</span>)</span>
<span id="cb1-32"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"each image becomes </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>PATCHES<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> tokens of </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>PATCH <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> PATCH<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> numbers"</span>)</span>
<span id="cb1-33"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"always guessing the most common digit: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>baseline<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.4f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>6,000 training digits, 1,500 test digits
each image becomes 16 tokens of 49 numbers
always guessing the most common digit: 0.1173</code></pre>
</div>
</div>
<div id="cell-fig-patches" class="cell" data-execution_count="2">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb3-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> matplotlib.pyplot <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> plt</span>
<span id="cb3-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> _style <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> COLOURS, MUTED</span>
<span id="cb3-3"></span>
<span id="cb3-4">digit <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> x_train[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]</span>
<span id="cb3-5">tiles <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> patchify(digit.unsqueeze(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>))[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>].reshape(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>, PATCH, PATCH)</span>
<span id="cb3-6"></span>
<span id="cb3-7">fig <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> plt.figure(figsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">7.2</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">3.6</span>))</span>
<span id="cb3-8">outer <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> fig.add_gridspec(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, wspace<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.15</span>)</span>
<span id="cb3-9"></span>
<span id="cb3-10">whole <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> fig.add_subplot(outer[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>])</span>
<span id="cb3-11">whole.imshow(digit, cmap<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"gray_r"</span>)</span>
<span id="cb3-12">whole.set_title(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"the image"</span>, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, pad<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>)</span>
<span id="cb3-13">whole.axis(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"off"</span>)</span>
<span id="cb3-14"></span>
<span id="cb3-15">inner <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> outer[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>].subgridspec(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>, wspace<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.18</span>, hspace<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.18</span>)</span>
<span id="cb3-16"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> r <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>):</span>
<span id="cb3-17">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> c <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>):</span>
<span id="cb3-18">        ax <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> fig.add_subplot(inner[r, c])</span>
<span id="cb3-19">        ax.imshow(tiles[r, c], cmap<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"gray_r"</span>, vmin<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, vmax<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb3-20">        ax.set_xticks([])<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> ax.set_yticks([])</span>
<span id="cb3-21">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> s <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> ax.spines.values():</span>
<span id="cb3-22">            s.set_color(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#cfd4da"</span>)</span>
<span id="cb3-23">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> r <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">and</span> c <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>:</span>
<span id="cb3-24">            ax.set_title(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"sixteen tokens"</span>, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, pad<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>, loc<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"left"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-display">
<div id="fig-patches" class="quarto-float quarto-figure quarto-figure-center anchored" alt="On the left a handwritten digit. On the right the same digit split into a four-by-four grid of separated square tiles.">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-patches-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://roshbeed.com/posts/2026-09-18-image-is-not-a-sequence/index_files/figure-html/fig-patches-output-1.png" alt="On the left a handwritten digit. On the right the same digit split into a four-by-four grid of separated square tiles." width="555" height="303" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-patches-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;1: One digit as the model sees it: sixteen 7x7 patches, which become sixteen tokens in a sequence.
</figcaption>
</figure>
</div>
</div>
</div>
<p>Each patch gets projected to a vector by a single <code>Linear</code>, and from there the model has a sequence of 16 vectors — structurally identical to a sentence of 16 words. A <code>[CLS]</code> token is stuck on the front, exactly as BERT does, and its final representation is what the classifier reads.</p>
<p>The project version wrote the attention, the encoder block and the positional encodings by hand, with no <code>torch.nn.Transformer</code> anywhere. Here I use the built-in attention, because this post is about the patching idea rather than the arithmetic inside a head.</p>
<div id="67bc49fc" class="cell" data-execution_count="3">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb4-1">DIM, HEADS, LAYERS <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">64</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span></span>
<span id="cb4-2"></span>
<span id="cb4-3"></span>
<span id="cb4-4"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> Block(nn.Module):</span>
<span id="cb4-5">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>):</span>
<span id="cb4-6">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">super</span>().<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>()</span>
<span id="cb4-7">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.attention <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.MultiheadAttention(DIM, HEADS, batch_first<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span>
<span id="cb4-8">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.norm1, <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.norm2 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.LayerNorm(DIM), nn.LayerNorm(DIM)</span>
<span id="cb4-9">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.feedforward <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Sequential(nn.Linear(DIM, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> DIM), nn.GELU(),</span>
<span id="cb4-10">                                         nn.Linear(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> DIM, DIM))</span>
<span id="cb4-11"></span>
<span id="cb4-12">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> forward(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, x, want_weights<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>):</span>
<span id="cb4-13">        attended, weights <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.attention(x, x, x, need_weights<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>want_weights,</span>
<span id="cb4-14">                                           average_attn_weights<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span>
<span id="cb4-15">        x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.norm1(x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> attended)</span>
<span id="cb4-16">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.norm2(x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.feedforward(x)), weights</span>
<span id="cb4-17"></span>
<span id="cb4-18"></span>
<span id="cb4-19"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> VisionTransformer(nn.Module):</span>
<span id="cb4-20">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, use_positions<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>):</span>
<span id="cb4-21">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">super</span>().<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>()</span>
<span id="cb4-22">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.use_positions <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> use_positions</span>
<span id="cb4-23">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.project <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Linear(PATCH <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> PATCH, DIM)</span>
<span id="cb4-24">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.cls <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Parameter(torch.zeros(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, DIM))</span>
<span id="cb4-25">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.positions <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Parameter(torch.randn(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, PATCHES <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, DIM) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.02</span>)</span>
<span id="cb4-26">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.blocks <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.ModuleList([Block() <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> _ <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(LAYERS)])</span>
<span id="cb4-27">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.head <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Linear(DIM, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>)</span>
<span id="cb4-28"></span>
<span id="cb4-29">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> forward(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, images, want_weights<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>):</span>
<span id="cb4-30">        tokens <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.project(patchify(images))</span>
<span id="cb4-31">        tokens <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.cat([<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.cls.expand(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(images), <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>), tokens], dim<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb4-32">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.use_positions:</span>
<span id="cb4-33">            tokens <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> tokens <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.positions</span>
<span id="cb4-34"></span>
<span id="cb4-35">        weights <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span></span>
<span id="cb4-36">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i, block <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">enumerate</span>(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.blocks):</span>
<span id="cb4-37">            tokens, w <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> block(tokens, want_weights <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">and</span> i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> LAYERS <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb4-38">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> w <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">is</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>:</span>
<span id="cb4-39">                weights <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> w</span>
<span id="cb4-40">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.head(tokens[:, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]), weights  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># position 0 is the [CLS] token</span></span></code></pre></div></div>
</details>
</div>
<p>Drawn out, with the shapes the tensors actually take:</p>
<div id="cell-fig-arch" class="cell" data-execution_count="4">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb5-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> _arch <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> diagram</span>
<span id="cb5-2"></span>
<span id="cb5-3">diagram(VisionTransformer(), input_shape<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">28</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">28</span>), style<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"flow"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-display" data-execution_count="4">
<div id="fig-arch" class="quarto-float quarto-figure quarto-figure-center anchored" alt="A left-to-right row of coloured three-dimensional blocks. Two identical groups repeat in the middle, each containing an orange attention block and a taller blue and green pair, with thin outlined rectangles arching over them.">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-arch-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://roshbeed.com/posts/2026-09-18-image-is-not-a-sequence/index_files/figure-html/fig-arch-output-1.png" class="img-fluid figure-img" alt="A left-to-right row of coloured three-dimensional blocks. Two identical groups repeat in the middle, each containing an orange attention block and a taller blue and green pair, with thin outlined rectangles arching over them.">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-arch-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;2: The whole model, left to right. Each patch is projected to 64 numbers, two identical encoder blocks run over the 17 tokens, and the head reads position 0. The tall blue-and-green pairs are the feed-forward expansion to 256 inside each block; the outlines spanning the top are the residual connections.
</figcaption>
</figure>
</div>
</div>
</div>
<div id="a0056c2e" class="cell" data-execution_count="5">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb6-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> train(use_positions, epochs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">15</span>, batch<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">128</span>, seed<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>):</span>
<span id="cb6-2">    torch.manual_seed(seed)</span>
<span id="cb6-3">    model <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> VisionTransformer(use_positions)</span>
<span id="cb6-4">    optimiser <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.optim.AdamW(model.parameters(), lr<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">3e-3</span>, weight_decay<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.01</span>)</span>
<span id="cb6-5">    generator <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.Generator().manual_seed(seed)</span>
<span id="cb6-6"></span>
<span id="cb6-7">    accuracies <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []</span>
<span id="cb6-8">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> _ <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(epochs):</span>
<span id="cb6-9">        perm <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.randperm(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(x_train), generator<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>generator)</span>
<span id="cb6-10">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(perm) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> batch, batch):</span>
<span id="cb6-11">            b <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> perm[i:i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> batch]</span>
<span id="cb6-12">            loss <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.functional.cross_entropy(model(x_train[b])[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>], y_train[b])</span>
<span id="cb6-13">            optimiser.zero_grad()</span>
<span id="cb6-14">            loss.backward()</span>
<span id="cb6-15">            optimiser.step()</span>
<span id="cb6-16">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">with</span> torch.no_grad():</span>
<span id="cb6-17">            accuracies.append((model(x_test)[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>].argmax(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> y_test).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>().mean().item())</span>
<span id="cb6-18">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> model, accuracies</span>
<span id="cb6-19"></span>
<span id="cb6-20"></span>
<span id="cb6-21">model, with_positions <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> train(use_positions<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span>
<span id="cb6-22"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>(p.numel() <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> p <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> model.parameters())<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:,}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> parameters"</span>)</span>
<span id="cb6-23"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"best test accuracy: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">max</span>(with_positions)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.4f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">   (baseline </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>baseline<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.4f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">)"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>104,970 parameters
best test accuracy: 0.9393   (baseline 0.1173)</code></pre>
</div>
</div>
<p>About a hundred thousand parameters, six thousand training images, and a few seconds. The real project got 91.78% on the full MNIST test set after one epoch; this gets to roughly the same place on a fiftieth of the data.</p>
<section id="what-the-position-embeddings-are-for" class="level2">
<h2 class="anchored" data-anchor-id="what-the-position-embeddings-are-for">What the position embeddings are for</h2>
<p>Self-attention has no idea where anything is. It computes how much each token should attend to each other token, and that calculation is <strong>permutation invariant</strong> — shuffle the tokens and you get the same set of outputs back, shuffled. Without something to break that symmetry, a Vision Transformer sees a bag of patches, not a picture.</p>
<p>That is what the learned position embeddings do, and you can measure exactly what they are worth by removing them.</p>
<div id="5ffaadf6" class="cell" data-execution_count="6">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb8-1">_, without_positions <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> train(use_positions<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>)</span>
<span id="cb8-2"></span>
<span id="cb8-3"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"with position embeddings:    </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">max</span>(with_positions)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.4f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb8-4"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"without position embeddings: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">max</span>(without_positions)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.4f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb8-5"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"difference:                  </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">max</span>(with_positions) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">max</span>(without_positions)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:+.4f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>with position embeddings:    0.9393
without position embeddings: 0.8693
difference:                  +0.0700</code></pre>
</div>
</div>
<div id="cell-fig-positions" class="cell" data-execution_count="7">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb10" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb10-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> _style <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> figure, style_axes</span>
<span id="cb10-2"></span>
<span id="cb10-3">epochs <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(with_positions) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb10-4">fig, ax <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> figure(height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">3.8</span>)</span>
<span id="cb10-5">ax.axhline(baseline, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.2</span>, linestyle<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"--"</span>)</span>
<span id="cb10-6">ax.plot(epochs, with_positions, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>], linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb10-7">ax.plot(epochs, without_positions, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb10-8">ax.annotate(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"with position embeddings"</span>, xy<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(epochs[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], with_positions[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]),</span>
<span id="cb10-9">            xytext<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>), textcoords<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"offset points"</span>, ha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"right"</span>,</span>
<span id="cb10-10">            fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>])</span>
<span id="cb10-11">ax.annotate(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"without"</span>, xy<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(epochs[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], without_positions[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]), xytext<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>, <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">16</span>),</span>
<span id="cb10-12">            textcoords<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"offset points"</span>, ha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"right"</span>, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>])</span>
<span id="cb10-13">ax.annotate(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"always guess the most common digit"</span>, xy<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, baseline), xytext<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>),</span>
<span id="cb10-14">            textcoords<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"offset points"</span>, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED)</span>
<span id="cb10-15">ax.set_ylim(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.0</span>)</span>
<span id="cb10-16">style_axes(ax, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Epoch"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Test accuracy"</span>)</span>
<span id="cb10-17">fig.tight_layout()</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-display">
<div id="fig-positions" class="quarto-float quarto-figure quarto-figure-center anchored" alt="Two accuracy curves rising over fifteen epochs. The line with position embeddings settles noticeably above the line without, and both are far above a dashed baseline near the bottom.">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-positions-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://roshbeed.com/posts/2026-09-18-image-is-not-a-sequence/index_files/figure-html/fig-positions-output-1.png" alt="Two accuracy curves rising over fifteen epochs. The line with position embeddings settles noticeably above the line without, and both are far above a dashed baseline near the bottom." width="661" height="354" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-positions-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;3: The same model trained with and without position embeddings. Without them the model can still recognise which patches are present, just not where.
</figcaption>
</figure>
</div>
</div>
</div>
<p>It still works without them, which surprised me at first — and then made sense. A bag of 7×7 patches carries a lot about which digit it is, because a 0 and a 1 contain visibly different patches regardless of arrangement. The position embeddings are worth the last several points, which is where the distinctions that depend on layout live.</p>
</section>
<section id="what-the-cls-token-looks-at" class="level2">
<h2 class="anchored" data-anchor-id="what-the-cls-token-looks-at">What the CLS token looks at</h2>
<p>Since the classifier reads only position 0, the attention weights out of that position say which patches the model is actually using.</p>
<div id="cell-fig-attention" class="cell" data-execution_count="8">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb11" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb11-1"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">with</span> torch.no_grad():</span>
<span id="cb11-2">    logits, weights <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> model(x_test[:<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>], want_weights<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span>
<span id="cb11-3"></span>
<span id="cb11-4">cls_attention <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> weights[:, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>:].reshape(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>)  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># row 0 = the [CLS] token</span></span>
<span id="cb11-5"></span>
<span id="cb11-6">fig, axes <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> plt.subplots(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>, figsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">8.2</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2.4</span>))</span>
<span id="cb11-7"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> ax, image, attention, predicted <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">zip</span>(axes, x_test[:<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>], cls_attention, logits.argmax(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)):</span>
<span id="cb11-8">    ax.imshow(image, cmap<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"gray_r"</span>)</span>
<span id="cb11-9">    ax.imshow(attention, cmap<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"inferno"</span>, alpha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.45</span>, extent<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">28</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">28</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>),</span>
<span id="cb11-10">              interpolation<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bilinear"</span>)</span>
<span id="cb11-11">    ax.set_title(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"predicted </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>predicted<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>item()<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, pad<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>)</span>
<span id="cb11-12">    ax.axis(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"off"</span>)</span>
<span id="cb11-13">fig.tight_layout()</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-display">
<div id="fig-attention" class="quarto-float quarto-figure quarto-figure-center anchored" alt="Four handwritten digits in a row, each with a coarse four-by-four heat overlay concentrated on the strokes rather than the empty corners.">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-attention-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://roshbeed.com/posts/2026-09-18-image-is-not-a-sequence/index_files/figure-html/fig-attention-output-1.png" alt="Four handwritten digits in a row, each with a coarse four-by-four heat overlay concentrated on the strokes rather than the empty corners." width="778" height="215" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-attention-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;4: Attention from the [CLS] token in the final block, for four test digits. Brighter patches are the ones the classification actually rests on.
</figcaption>
</figure>
</div>
</div>
</div>
<p>The attention concentrates on the patches with ink in them and largely ignores the empty corners, which is the behaviour you’d want and nobody specified. Nothing in the loss says “look at the strokes” — it falls out of training the classifier.</p>
<p>I’d be careful about reading much more than that into it. Attention maps are suggestive, not explanatory, and with two layers and sixteen patches this one is very coarse.</p>
</section>
<section id="the-part-that-transfers" class="level2">
<h2 class="anchored" data-anchor-id="the-part-that-transfers">The part that transfers</h2>
<p>The reason this architecture mattered isn’t MNIST accuracy — a small CNN beats it easily at this scale, and the ViT paper is explicit that transformers only overtake convolutional networks with a lot of data behind them.</p>
<p>It’s that once an image is a sequence of tokens, it is the <em>same kind of object</em> as a sentence. That’s what makes it possible to feed images and text to one model, which is where week 4 goes.</p>
<p>The full project, with the attention and encoder blocks written from scratch, is <a href="https://github.com/RoshBeed/ai-residency/tree/main/services/vit-classifier">on GitHub</a>.</p>
<hr>
<p>[1] Dosovitskiy et al.&nbsp;<em>An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale.</em> ICLR 2021.</p>
<div id="cc74c236" class="cell" data-execution_count="9">
<div class="cell-output cell-output-stdout">
<pre><code>Built:      2026-09-18 02:24:25 UTC
Python:     3.13.15
matplotlib: 3.11.2
numpy:      2.5.3
torch:      2.14.0+cpu</code></pre>
</div>
</div>


</section>

 ]]></description>
  <category>vision</category>
  <category>transformers</category>
  <category>week-3</category>
  <guid>https://roshbeed.com/posts/2026-09-18-image-is-not-a-sequence/</guid>
  <pubDate>Fri, 18 Sep 2026 00:00:00 GMT</pubDate>
</item>
<item>
  <title>The loss went to zero and the model had learned nothing</title>
  <dc:creator>Rosh Beed</dc:creator>
  <link>https://roshbeed.com/posts/2026-09-18-loss-went-to-zero/</link>
  <description><![CDATA[ 




<p>Week 2: build the thing behind semantic search. Given a query, find the passage in a corpus that answers it.</p>
<p>The obvious approach is to take the query and a candidate passage, feed both into one model, and let it score the pair. That works well and is completely impractical, because you have to run it once for every passage in the corpus, for every query.</p>
<p>The dual encoder gets around it by refusing to let the query and the document meet. Two separate towers, one for each, both producing a vector in the same space. A query is relevant to a passage if their vectors are close.</p>
<p>That constraint is the whole point: because the document tower never sees the query, <strong>you can run it before any query exists.</strong> Embed the entire corpus once, offline, put the vectors in an index, and at query time you embed one short string and do a nearest-neighbour lookup.</p>
<div id="cell-fig-cost" class="cell" data-execution_count="1">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> sys</span>
<span id="cb1-2"></span>
<span id="cb1-3">sys.path.insert(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".."</span>)</span>
<span id="cb1-4"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> numpy <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> np</span>
<span id="cb1-5"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> _style <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> COLOURS, MUTED, figure, style_axes</span>
<span id="cb1-6"></span>
<span id="cb1-7">corpus <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.logspace(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">200</span>)</span>
<span id="cb1-8"></span>
<span id="cb1-9">fig, ax <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> figure(height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">3.8</span>)</span>
<span id="cb1-10">ax.plot(corpus, corpus, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>], linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb1-11">ax.plot(corpus, np.ones_like(corpus), color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb1-12">ax.annotate(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"cross-encoder: score every passage"</span>, xy<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2000</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2000</span>), xytext<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>),</span>
<span id="cb1-13">            textcoords<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"offset points"</span>, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>])</span>
<span id="cb1-14">ax.annotate(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"dual encoder: embed the query, then look it up"</span>, xy<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2000</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>),</span>
<span id="cb1-15">            xytext<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>), textcoords<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"offset points"</span>, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>])</span>
<span id="cb1-16">ax.set_xscale(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"log"</span>)</span>
<span id="cb1-17">ax.set_yscale(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"log"</span>)</span>
<span id="cb1-18">style_axes(ax, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Passages in the corpus"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Model runs per query"</span>)</span>
<span id="cb1-19">fig.tight_layout()</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-display">
<div id="fig-cost" class="quarto-float quarto-figure quarto-figure-center anchored" alt="A log-log chart. The cross-encoder line rises linearly with corpus size to a million model runs. The dual encoder line is flat at one.">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-cost-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://roshbeed.com/posts/2026-09-18-loss-went-to-zero/index_files/figure-html/fig-cost-output-1.png" alt="A log-log chart. The cross-encoder line rises linearly with corpus size to a million model runs. The dual encoder line is flat at one." width="661" height="354" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-cost-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;1: Model runs needed to answer one query. The cross-encoder has to score every passage; the dual encoder embeds the query once and lets an index do the rest.
</figcaption>
</figure>
</div>
</div>
</div>
<section id="training-it-triplet-loss" class="level2">
<h2 class="anchored" data-anchor-id="training-it-triplet-loss">Training it: triplet loss</h2>
<p>The towers start out random, so you have to teach them what close means. The signal used in the project is a triplet: a query, a passage that answers it, and a passage that doesn’t. Push the first pair together and the second apart, by at least a margin <img src="https://latex.codecogs.com/png.latex?m">:</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Cmathcal%7BL%7D%20=%20%5Cmax%5Cbig(0,%5C%20m%20-%20s(q,%20d%5E%7B+%7D)%20+%20s(q,%20d%5E%7B-%7D)%5Cbig)"></p>
<p>If the positive is already more similar than the negative by the margin, the loss is zero and nothing happens. Otherwise both towers get nudged.</p>
</section>
<section id="a-toy-corpus-with-free-labels" class="level2">
<h2 class="anchored" data-anchor-id="a-toy-corpus-with-free-labels">A toy corpus with free labels</h2>
<p>Real retrieval training needs query-passage pairs, which is the expensive part. There’s a standard trick for getting them free: take a document, cut a piece out, and use the piece as the query. It’s called the inverse cloze task [1], and it’s how several retrieval models get pre-trained.</p>
<p>Here I take Hacker News titles of at least eight words and cut them in half. The first half is the query, the second half is the document it should retrieve. No labelling, and the two halves are genuinely about the same thing.</p>
<div id="0c2b9dac" class="cell" data-execution_count="2">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb2-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> gzip</span>
<span id="cb2-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> json</span>
<span id="cb2-3"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> re</span>
<span id="cb2-4"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> zlib</span>
<span id="cb2-5"></span>
<span id="cb2-6"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> torch</span>
<span id="cb2-7"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> torch.nn <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> nn</span>
<span id="cb2-8"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> torch.nn.functional <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> F</span>
<span id="cb2-9"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> huggingface_hub <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> hf_hub_download</span>
<span id="cb2-10"></span>
<span id="cb2-11">REVISION <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"f705fed08827ff6c36e3b5329495c943a5e544e8"</span></span>
<span id="cb2-12">path <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> hf_hub_download(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"roshbeed/ai-residency-blog-data"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"hn/hn-sample.json.gz"</span>,</span>
<span id="cb2-13">                       repo_type<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"dataset"</span>, revision<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>REVISION)</span>
<span id="cb2-14">rows <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> json.load(gzip.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">open</span>(path, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"rt"</span>))</span>
<span id="cb2-15">titles <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [r[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"title"</span>] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> r <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> rows[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"train"</span>]] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> [r[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"title"</span>] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> r <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> rows[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"test"</span>]]</span>
<span id="cb2-16"></span>
<span id="cb2-17">tokenise <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">lambda</span> s: re.findall(<span class="vs" style="color: #20794D;
background-color: null;
font-style: inherit;">r"</span><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">[a-z0-9</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\+</span><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">#]</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span><span class="vs" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>, s.lower())</span>
<span id="cb2-18">pairs <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []</span>
<span id="cb2-19"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> title <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> titles:</span>
<span id="cb2-20">    words <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> tokenise(title)</span>
<span id="cb2-21">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(words) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>:</span>
<span id="cb2-22">        half <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(words) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">//</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span></span>
<span id="cb2-23">        pairs.append((words[:half], words[half:]))</span>
<span id="cb2-24"></span>
<span id="cb2-25"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(pairs)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:,}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> query/document pairs"</span>)</span>
<span id="cb2-26"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"  query:    </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">' '</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>join(pairs[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>][<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>])<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb2-27"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"  document: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">' '</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>join(pairs[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>][<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>])<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>79,009 query/document pairs
  query:    california governor to deploy 500 surveillance
  document: cameras to oakland to fight crime</code></pre>
</div>
</div>
<div id="9863ba9c" class="cell" data-execution_count="3">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb4-1">DIM <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">14</span></span>
<span id="cb4-2"></span>
<span id="cb4-3"></span>
<span id="cb4-4"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> encode(sequences):</span>
<span id="cb4-5">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""Bag of hashed words. crc32 rather than hash() — Python's is salted per process."""</span></span>
<span id="cb4-6">    X <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.zeros(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(sequences), DIM)</span>
<span id="cb4-7">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i, words <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">enumerate</span>(sequences):</span>
<span id="cb4-8">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> w <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> words:</span>
<span id="cb4-9">            X[i, zlib.crc32(w.encode()) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%</span> DIM] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.0</span></span>
<span id="cb4-10">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> words:</span>
<span id="cb4-11">            X[i] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(words)</span>
<span id="cb4-12">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> X</span>
<span id="cb4-13"></span>
<span id="cb4-14"></span>
<span id="cb4-15">rng <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.random.default_rng(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb4-16">order <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> rng.permutation(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(pairs))</span>
<span id="cb4-17">train_ids, test_ids <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> order[:<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">40_000</span>], order[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">40_000</span>:<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">45_000</span>]</span>
<span id="cb4-18"></span>
<span id="cb4-19">Q <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> encode([pairs[i][<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> train_ids])</span>
<span id="cb4-20">D <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> encode([pairs[i][<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> train_ids])</span>
<span id="cb4-21">Q_test <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> encode([pairs[i][<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> test_ids])</span>
<span id="cb4-22">D_test <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> encode([pairs[i][<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> test_ids])</span>
<span id="cb4-23"></span>
<span id="cb4-24"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(Q)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:,}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> training pairs, </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(D_test)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:,}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> documents to search at test time"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>40,000 training pairs, 5,000 documents to search at test time</code></pre>
</div>
</div>
<div id="bd7c772a" class="cell" data-execution_count="4">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb6-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> Tower(nn.Module):</span>
<span id="cb6-2">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""Text in, a unit vector out. Query and document get one of these each."""</span></span>
<span id="cb6-3"></span>
<span id="cb6-4">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, out<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">128</span>):</span>
<span id="cb6-5">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">super</span>().<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>()</span>
<span id="cb6-6">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.net <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Sequential(nn.Linear(DIM, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">256</span>), nn.ReLU(), nn.Linear(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">256</span>, out))</span>
<span id="cb6-7"></span>
<span id="cb6-8">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> forward(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, x):</span>
<span id="cb6-9">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> F.normalize(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.net(x), dim<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb6-10"></span>
<span id="cb6-11"></span>
<span id="cb6-12"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> recall_at_10(query_tower, doc_tower):</span>
<span id="cb6-13">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""How often the right document is in the top 10 of all 5,000."""</span></span>
<span id="cb6-14">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">with</span> torch.no_grad():</span>
<span id="cb6-15">        similarity <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> query_tower(Q_test) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@</span> doc_tower(D_test).T</span>
<span id="cb6-16">        top <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> similarity.topk(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>, dim<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>).indices</span>
<span id="cb6-17">        correct <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.arange(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(Q_test)).unsqueeze(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb6-18">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> (top <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> correct).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">any</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>().mean().item()</span>
<span id="cb6-19"></span>
<span id="cb6-20"></span>
<span id="cb6-21">CHANCE <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(D_test)</span>
<span id="cb6-22"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"chance recall@10 = </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>CHANCE<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.4f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>chance recall@10 = 0.0020</code></pre>
</div>
</div>
<div id="cell-fig-tower" class="cell" data-execution_count="5">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb8-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> _arch <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> diagram</span>
<span id="cb8-2"></span>
<span id="cb8-3">diagram(Tower(), input_shape<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, DIM))</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-display" data-execution_count="5">
<div id="fig-tower" class="quarto-float quarto-figure quarto-figure-center anchored" alt="A four-column neural network diagram: an input layer, two hidden layers and an output layer, each column drawn as a stack of circles fully connected to the next.">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-tower-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://roshbeed.com/posts/2026-09-18-loss-went-to-zero/index_files/figure-html/fig-tower-output-1.png" class="img-fluid figure-img" alt="A four-column neural network diagram: an input layer, two hidden layers and an output layer, each column drawn as a stack of circles fully connected to the next.">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-tower-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;2: One tower. Both the query and the document side are this same shape, with separate weights, and the output is normalised to unit length so a dot product is a cosine.
</figcaption>
</figure>
</div>
</div>
</div>
<div id="b682ca82" class="cell" data-execution_count="6">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb9-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> train(loss_fn, epochs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>, batch<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">256</span>, seed<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>):</span>
<span id="cb9-2">    torch.manual_seed(seed)</span>
<span id="cb9-3">    query_tower, doc_tower <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Tower(), Tower()</span>
<span id="cb9-4">    optimiser <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.optim.Adam([<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span>query_tower.parameters(), <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span>doc_tower.parameters()], lr<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1e-3</span>)</span>
<span id="cb9-5">    generator <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.Generator().manual_seed(seed)</span>
<span id="cb9-6"></span>
<span id="cb9-7">    losses, recalls <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [], []</span>
<span id="cb9-8">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> _ <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(epochs):</span>
<span id="cb9-9">        perm <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.randperm(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(Q), generator<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>generator)</span>
<span id="cb9-10">        total <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> steps <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span></span>
<span id="cb9-11">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(perm) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> batch, batch):</span>
<span id="cb9-12">            b <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> perm[i:i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> batch]</span>
<span id="cb9-13">            loss <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> loss_fn(query_tower, doc_tower, b, perm, generator)</span>
<span id="cb9-14">            optimiser.zero_grad()</span>
<span id="cb9-15">            loss.backward()</span>
<span id="cb9-16">            optimiser.step()</span>
<span id="cb9-17">            total <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> loss.item()</span>
<span id="cb9-18">            steps <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span>
<span id="cb9-19">        losses.append(total <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> steps)</span>
<span id="cb9-20">        recalls.append(recall_at_10(query_tower, doc_tower))</span>
<span id="cb9-21">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> losses, recalls</span>
<span id="cb9-22"></span>
<span id="cb9-23"></span>
<span id="cb9-24">MARGIN <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.2</span></span>
<span id="cb9-25"></span>
<span id="cb9-26"></span>
<span id="cb9-27"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> triplet(query_tower, doc_tower, b, perm, generator):</span>
<span id="cb9-28">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""The project's loss: one random passage as the negative."""</span></span>
<span id="cb9-29">    negatives <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> perm[torch.randperm(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(b), generator<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>generator)]</span>
<span id="cb9-30">    q <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> query_tower(Q[b])</span>
<span id="cb9-31">    positive <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (q <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> doc_tower(D[b])).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb9-32">    negative <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (q <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> doc_tower(D[negatives])).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb9-33">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> F.relu(MARGIN <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> positive <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> negative).mean()</span>
<span id="cb9-34"></span>
<span id="cb9-35"></span>
<span id="cb9-36">triplet_loss, triplet_recall <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> train(triplet)</span>
<span id="cb9-37"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> epoch, (l, r) <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">enumerate</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">zip</span>(triplet_loss, triplet_recall), <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>):</span>
<span id="cb9-38">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"epoch </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>epoch<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">: loss </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>l<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.4f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">   recall@10 </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>r<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.4f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">   (</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>r <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> CHANCE<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.1f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">x chance)"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>epoch 1: loss 0.0121   recall@10 0.0020   (1.0x chance)
epoch 2: loss 0.0093   recall@10 0.0022   (1.1x chance)
epoch 3: loss 0.0123   recall@10 0.0022   (1.1x chance)
epoch 4: loss 0.0199   recall@10 0.0020   (1.0x chance)
epoch 5: loss 0.0142   recall@10 0.0020   (1.0x chance)
epoch 6: loss 0.0201   recall@10 0.0018   (0.9x chance)
epoch 7: loss 0.0179   recall@10 0.0020   (1.0x chance)
epoch 8: loss 0.0178   recall@10 0.0018   (0.9x chance)</code></pre>
</div>
</div>
<p>The loss falls to near zero within one epoch, and <strong>recall never leaves chance.</strong> Out of 5,000 documents, the model finds the right one in its top ten about as often as picking ten at random would.</p>
<p>If you were watching the loss curve — which is the thing that’s easy to watch — you would conclude this was training beautifully.</p>
</section>
<section id="why-it-happens" class="level2">
<h2 class="anchored" data-anchor-id="why-it-happens">Why it happens</h2>
<p>A negative drawn at random from the corpus is about a different subject entirely. The towers only have to tell “machine learning” from “sourdough starter”, and they can do that almost immediately. Once the margin is satisfied, the loss is exactly zero, and a loss of zero has no gradient.</p>
<p>So the model stops learning while still being unable to do the thing you want, which is to tell the <em>right</em> passage from a thousand plausible ones.</p>
<p>The usual fix is to stop picking one negative and use the whole batch: every other document in the batch is a negative for this query, and the loss becomes a cross-entropy over which of them is correct. It’s a harder question, it stays hard as the model improves, and it costs nothing extra because those documents are already encoded.</p>
<div id="3003443d" class="cell" data-execution_count="7">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb11" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb11-1">TEMPERATURE <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.05</span></span>
<span id="cb11-2"></span>
<span id="cb11-3"></span>
<span id="cb11-4"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> in_batch(query_tower, doc_tower, b, perm, generator):</span>
<span id="cb11-5">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""Every other document in the batch is a negative for this query."""</span></span>
<span id="cb11-6">    q, docs <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> query_tower(Q[b]), doc_tower(D[b])</span>
<span id="cb11-7">    logits <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (q <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@</span> docs.T) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> TEMPERATURE</span>
<span id="cb11-8">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> F.cross_entropy(logits, torch.arange(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(b)))</span>
<span id="cb11-9"></span>
<span id="cb11-10"></span>
<span id="cb11-11">batch_loss, batch_recall <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> train(in_batch)</span>
<span id="cb11-12"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> epoch, (l, r) <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">enumerate</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">zip</span>(batch_loss, batch_recall), <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>):</span>
<span id="cb11-13">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"epoch </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>epoch<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">: loss </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>l<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.4f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">   recall@10 </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>r<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.4f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">   (</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>r <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> CHANCE<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.1f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">x chance)"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>epoch 1: loss 5.3608   recall@10 0.0098   (4.9x chance)
epoch 2: loss 4.8804   recall@10 0.0166   (8.3x chance)
epoch 3: loss 4.0087   recall@10 0.0252   (12.6x chance)
epoch 4: loss 2.4321   recall@10 0.0302   (15.1x chance)
epoch 5: loss 0.9204   recall@10 0.0334   (16.7x chance)
epoch 6: loss 0.3301   recall@10 0.0342   (17.1x chance)
epoch 7: loss 0.1374   recall@10 0.0362   (18.1x chance)
epoch 8: loss 0.0757   recall@10 0.0362   (18.1x chance)</code></pre>
</div>
</div>
<div id="cell-fig-recall" class="cell" data-execution_count="8">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb13" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb13-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> matplotlib.pyplot <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> plt</span>
<span id="cb13-2"></span>
<span id="cb13-3">epochs <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(batch_loss) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb13-4">fig, (left, right) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> plt.subplots(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, figsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">8.4</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">3.8</span>))</span>
<span id="cb13-5"></span>
<span id="cb13-6">left.plot(epochs, triplet_loss, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb13-7">left.plot(epochs, batch_loss, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>], linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb13-8">left.set_title(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"training loss"</span>, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, pad<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>)</span>
<span id="cb13-9">style_axes(left, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Epoch"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Loss"</span>)</span>
<span id="cb13-10"></span>
<span id="cb13-11">right.axhline(CHANCE, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.2</span>, linestyle<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"--"</span>)</span>
<span id="cb13-12">right.plot(epochs, triplet_recall, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb13-13">right.plot(epochs, batch_recall, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>], linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb13-14">right.annotate(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"in-batch negatives"</span>, xy<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(epochs[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], batch_recall[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]), xytext<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>, <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">14</span>),</span>
<span id="cb13-15">               textcoords<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"offset points"</span>, ha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"right"</span>, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>])</span>
<span id="cb13-16">right.annotate(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"one random negative"</span>, xy<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(epochs[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], triplet_recall[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]), xytext<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>),</span>
<span id="cb13-17">               textcoords<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"offset points"</span>, ha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"right"</span>, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>])</span>
<span id="cb13-18">right.annotate(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chance"</span>, xy<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, CHANCE), xytext<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>), textcoords<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"offset points"</span>,</span>
<span id="cb13-19">               fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED)</span>
<span id="cb13-20">right.set_title(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"recall@10 out of 5,000 documents"</span>, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, pad<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>)</span>
<span id="cb13-21">style_axes(right, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Epoch"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Recall@10"</span>)</span>
<span id="cb13-22"></span>
<span id="cb13-23">fig.tight_layout()</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-display">
<div id="fig-recall" class="quarto-float quarto-figure quarto-figure-center anchored" alt="Two panels. On the left, both loss curves fall, the triplet one almost immediately. On the right, in-batch recall climbs steadily while triplet recall stays flat on the chance line.">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-recall-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://roshbeed.com/posts/2026-09-18-loss-went-to-zero/index_files/figure-html/fig-recall-output-1.png" alt="Two panels. On the left, both loss curves fall, the triplet one almost immediately. On the right, in-batch recall climbs steadily while triplet recall stays flat on the chance line." width="796" height="354" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-recall-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;3: The same towers, the same data, the same number of steps. Only the choice of negatives differs.
</figcaption>
</figure>
</div>
</div>
</div>
<p>Same towers, same data, same number of gradient steps. The only change is which documents count as negatives, and it’s the difference between a model that works and a model that doesn’t.</p>
<p>Note what the loss curves do. The triplet loss ends <em>lower</em> than the in-batch loss. If you ranked these two runs by final training loss you would pick the one that learned nothing.</p>
</section>
<section id="why-this-matters-beyond-the-toy" class="level2">
<h2 class="anchored" data-anchor-id="why-this-matters-beyond-the-toy">Why this matters beyond the toy</h2>
<p>Negative sampling is the part of contrastive training that looks like a detail and isn’t. The architecture — two towers, a shared space, an index — is the easy half and mostly writes itself. What decides whether the thing retrieves anything is the question you ask it during training, and “is this passage more relevant than one picked at random” is not a hard enough question to learn from.</p>
<p>That’s why the literature moved to in-batch negatives and then to actively mined hard negatives: passages that look plausible for the query and aren’t. Each step makes the training question harder in the same direction.</p>
<p>The general lesson I took from this one, and now apply everywhere: <strong>report a metric next to the number a model that learned nothing would get.</strong> Chance recall@10 over 5,000 documents is 0.002. Any recall figure without that 0.002 beside it is unreadable — and a falling loss beside it is worth nothing at all.</p>
<p>The full project, with the GRU towers, the sweep and the Redis index, is <a href="https://github.com/RoshBeed/ai-residency/tree/main/services/vector-search-retrieval">on GitHub</a>.</p>
<hr>
<p>[1] Lee, Chang, Toutanova. <em>Latent Retrieval for Weakly Supervised Open Domain Question Answering.</em> ACL 2019.</p>
<div id="6771a90d" class="cell" data-execution_count="9">
<div class="cell-output cell-output-stdout">
<pre><code>Built:      2026-09-18 02:28:47 UTC
Python:     3.13.15
matplotlib: 3.11.2
numpy:      2.5.3
torch:      2.14.0+cpu</code></pre>
</div>
</div>


</section>

 ]]></description>
  <category>retrieval</category>
  <category>embeddings</category>
  <category>week-2</category>
  <guid>https://roshbeed.com/posts/2026-09-18-loss-went-to-zero/</guid>
  <pubDate>Fri, 18 Sep 2026 00:00:00 GMT</pubDate>
</item>
<item>
  <title>What the KL penalty is actually holding back</title>
  <dc:creator>Rosh Beed</dc:creator>
  <link>https://roshbeed.com/posts/2026-09-18-what-the-kl-penalty-holds-back/</link>
  <description><![CDATA[ 




<p>Week 6, the last one: reinforcement learning from human feedback. The loop that turned GPT-3 into InstructGPT [1], built from scratch — generation, KL penalty, reward, advantage estimation, and the clipped policy and value updates, one small module each.</p>
<p>The reason RL shows up here at all is that you can’t write down a loss for “a good summary”. What you can do is show people two summaries and ask which they prefer, train a model to predict those preferences, and then optimise the language model against <em>that</em>.</p>
<p>Which introduces the problem the whole method is organised around: <strong>you are now optimising a learned approximation of what you want.</strong> Push hard enough on any learned reward and you stop finding good outputs and start finding its mistakes.</p>
<section id="a-language-and-a-reward-model-with-a-weakness" class="level2">
<h2 class="anchored" data-anchor-id="a-language-and-a-reward-model-with-a-weakness">A language, and a reward model with a weakness</h2>
<p>To watch that happen I need something that has grammar, and a reward model that is slightly wrong about what’s good.</p>
<p>The language: eight tokens, where each one usually follows the one before it — token 4 tends to be followed by 5, then 6, and so on. A small bigram model trained on samples of it is the <strong>reference policy</strong>, standing in for the supervised model you start RLHF from.</p>
<p>The reward model: it likes token 3. That’s it. Think of it as a preference model that has correctly noticed people enjoy a particular thing, and has no opinion about anything else — which is roughly how real reward models fail.</p>
<div id="fe232eae" class="cell" data-execution_count="1">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> sys</span>
<span id="cb1-2"></span>
<span id="cb1-3">sys.path.insert(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".."</span>)</span>
<span id="cb1-4"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> numpy <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> np</span>
<span id="cb1-5"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> torch</span>
<span id="cb1-6"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> torch.nn <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> nn</span>
<span id="cb1-7"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> torch.nn.functional <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> F</span>
<span id="cb1-8"></span>
<span id="cb1-9">VOCAB, LENGTH, START <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span></span>
<span id="cb1-10"></span>
<span id="cb1-11">rng <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.random.default_rng(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb1-12">grammar <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.full((VOCAB, VOCAB), <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.02</span>)</span>
<span id="cb1-13"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(VOCAB):</span>
<span id="cb1-14">    grammar[i, (i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%</span> VOCAB] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.70</span>     <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># the usual next token</span></span>
<span id="cb1-15">    grammar[i, (i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%</span> VOCAB] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.16</span>     <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># sometimes it skips one</span></span>
<span id="cb1-16">grammar <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/=</span> grammar.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, keepdims<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span>
<span id="cb1-17"></span>
<span id="cb1-18"></span>
<span id="cb1-19"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> sample_corpus(n):</span>
<span id="cb1-20">    out <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.zeros((n, LENGTH), <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>)</span>
<span id="cb1-21">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(n):</span>
<span id="cb1-22">        previous <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> START</span>
<span id="cb1-23">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> t <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(LENGTH):</span>
<span id="cb1-24">            previous <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> rng.choice(VOCAB, p<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>grammar[previous])</span>
<span id="cb1-25">            out[i, t] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> previous</span>
<span id="cb1-26">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> torch.from_numpy(out)</span>
<span id="cb1-27"></span>
<span id="cb1-28"></span>
<span id="cb1-29"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> Bigram(nn.Module):</span>
<span id="cb1-30">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>):</span>
<span id="cb1-31">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">super</span>().<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>()</span>
<span id="cb1-32">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.logits <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Parameter(torch.zeros(VOCAB, VOCAB))</span>
<span id="cb1-33"></span>
<span id="cb1-34">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> forward(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, previous):</span>
<span id="cb1-35">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.logits[previous]</span>
<span id="cb1-36"></span>
<span id="cb1-37"></span>
<span id="cb1-38">corpus <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> sample_corpus(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4000</span>)</span>
<span id="cb1-39">torch.manual_seed(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb1-40">reference <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Bigram()</span>
<span id="cb1-41">optimiser <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.optim.Adam(reference.parameters(), lr<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.1</span>)</span>
<span id="cb1-42">shifted <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.cat([torch.full((<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(corpus), <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>), START), corpus[:, :<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]], dim<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb1-43"></span>
<span id="cb1-44"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> _ <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">400</span>):</span>
<span id="cb1-45">    loss <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> F.cross_entropy(reference(shifted).reshape(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, VOCAB), corpus.reshape(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>))</span>
<span id="cb1-46">    optimiser.zero_grad()</span>
<span id="cb1-47">    loss.backward()</span>
<span id="cb1-48">    optimiser.step()</span>
<span id="cb1-49"></span>
<span id="cb1-50"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> p <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> reference.parameters():</span>
<span id="cb1-51">    p.requires_grad_(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>)</span>
<span id="cb1-52"></span>
<span id="cb1-53">REWARDED <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span></span>
<span id="cb1-54"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"reference model trained, cross-entropy </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>loss<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>item()<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.4f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb1-55"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"the reward model gives one point per token </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>REWARDED<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">, so the maximum is </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>LENGTH<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>reference model trained, cross-entropy 1.0112
the reward model gives one point per token 3, so the maximum is 6</code></pre>
</div>
</div>
<div id="2a827cba" class="cell" data-execution_count="2">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb3-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> reward(sequences):</span>
<span id="cb3-2">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> (sequences <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> REWARDED).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>().<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb3-3"></span>
<span id="cb3-4"></span>
<span id="cb3-5"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@torch.no_grad</span>()</span>
<span id="cb3-6"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> generate(model, n, generator):</span>
<span id="cb3-7">    sequences <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.zeros(n, LENGTH, dtype<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>torch.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">long</span>)</span>
<span id="cb3-8">    log_probs <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.zeros(n, LENGTH)</span>
<span id="cb3-9">    previous <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.full((n,), START)</span>
<span id="cb3-10">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> t <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(LENGTH):</span>
<span id="cb3-11">        lp <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> F.log_softmax(model(previous), <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb3-12">        nxt <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.multinomial(lp.exp(), <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, generator<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>generator).squeeze(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb3-13">        sequences[:, t] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nxt</span>
<span id="cb3-14">        log_probs[:, t] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> lp.gather(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, nxt[:, <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>]).squeeze(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb3-15">        previous <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nxt</span>
<span id="cb3-16">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> sequences, log_probs</span>
<span id="cb3-17"></span>
<span id="cb3-18"></span>
<span id="cb3-19"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> log_prob_of(model, sequences):</span>
<span id="cb3-20">    previous <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.cat([torch.full((<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(sequences), <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>), START), sequences[:, :<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]], dim<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb3-21">    lp <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> F.log_softmax(model(previous), <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb3-22">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> lp.gather(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, sequences[..., <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>]).squeeze(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb3-23"></span>
<span id="cb3-24"></span>
<span id="cb3-25"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@torch.no_grad</span>()</span>
<span id="cb3-26"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> fluency(sequences):</span>
<span id="cb3-27">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""How likely these sequences are under the language the model started from."""</span></span>
<span id="cb3-28">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> log_prob_of(reference, sequences).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>).mean().item()</span>
<span id="cb3-29"></span>
<span id="cb3-30"></span>
<span id="cb3-31">generator <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.Generator().manual_seed(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>)</span>
<span id="cb3-32">samples, _ <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> generate(reference, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">512</span>, generator)</span>
<span id="cb3-33"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"reference: reward </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>reward(samples)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>mean()<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.2f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">, fluency </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>fluency(samples)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.2f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb3-34"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"  it says things like </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>samples[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>tolist()<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> and </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>samples[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>tolist()<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>reference: reward 0.84, fluency -6.19
  it says things like [4, 0, 1, 2, 6, 7] and [7, 0, 1, 2, 4, 5]</code></pre>
</div>
</div>
</section>
<section id="the-update" class="level2">
<h2 class="anchored" data-anchor-id="the-update">The update</h2>
<p>PPO’s headline is the clipped objective. Sample some sequences from the current policy, work out how much better than average each one turned out, and then push their probability up — but only so far.</p>
<p>The ratio <img src="https://latex.codecogs.com/png.latex?r%20=%20%5Cpi_%7B%5Ctext%7Bnew%7D%7D%20/%20%5Cpi_%7B%5Ctext%7Bold%7D%7D"> says how much the policy has moved on a sequence since it was sampled. Taking the minimum of <img src="https://latex.codecogs.com/png.latex?rA"> and a clipped <img src="https://latex.codecogs.com/png.latex?rA"> means that once the policy has moved far enough in the direction the advantage points, there’s no further gradient from that sample. You get to reuse a batch for several steps without the policy running away from the data it was collected from.</p>
<div id="cell-fig-clip" class="cell" data-execution_count="3">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb5-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> _style <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> COLOURS, MUTED, figure, style_axes</span>
<span id="cb5-2"></span>
<span id="cb5-3">ratio <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.linspace(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">400</span>)</span>
<span id="cb5-4">CLIP <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.2</span></span>
<span id="cb5-5"></span>
<span id="cb5-6">fig, ax <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> figure(height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">3.8</span>)</span>
<span id="cb5-7"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> advantage, colour, label <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> ((<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.0</span>, COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>], <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"a better-than-average sequence"</span>),</span>
<span id="cb5-8">                                 (<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.0</span>, COLOURS[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"a worse-than-average one"</span>)):</span>
<span id="cb5-9">    objective <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.minimum(ratio <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> advantage, np.clip(ratio, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> CLIP, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> CLIP) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> advantage)</span>
<span id="cb5-10">    ax.plot(ratio, objective, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>colour, linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb5-11">    ax.annotate(label, xy<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.75</span>, objective[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">40</span>]), xytext<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> advantage <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">18</span>),</span>
<span id="cb5-12">                textcoords<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"offset points"</span>, ha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"right"</span>, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>colour)</span>
<span id="cb5-13"></span>
<span id="cb5-14"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> edge <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> CLIP, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> CLIP):</span>
<span id="cb5-15">    ax.axvline(edge, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.8</span>, linestyle<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"--"</span>)</span>
<span id="cb5-16">ax.axhline(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>MUTED, linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.8</span>)</span>
<span id="cb5-17">style_axes(ax, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"How much the policy has moved on this sequence"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Objective"</span>)</span>
<span id="cb5-18">fig.tight_layout()</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-display">
<div id="fig-clip" class="quarto-float quarto-figure quarto-figure-center anchored" alt="Two lines against the policy ratio. For a positive advantage the line rises then flattens at 1.2; for a negative advantage it falls then flattens at 0.8.">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-clip-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://roshbeed.com/posts/2026-09-18-what-the-kl-penalty-holds-back/index_files/figure-html/fig-clip-output-1.png" alt="Two lines against the policy ratio. For a positive advantage the line rises then flattens at 1.2; for a negative advantage it falls then flattens at 0.8." width="660" height="353" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-clip-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;1: The clipped objective, as a function of how far the policy has moved. Once it has moved far enough in the useful direction, the line goes flat and there is nothing more to gain from that sample.
</figcaption>
</figure>
</div>
</div>
</div>
<p>Clipping keeps each <em>update</em> small. It says nothing at all about where the policy ends up after a hundred of them, and that’s the gap the next part lives in.</p>
<div id="9c463ccd" class="cell" data-execution_count="4">
<details class="code-fold">
<summary>Show the code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb6-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> train(kl_coefficient, clip<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.2</span>, iterations<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">120</span>, batch<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">512</span>, lr<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.05</span>):</span>
<span id="cb6-2">    torch.manual_seed(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb6-3">    policy <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Bigram()</span>
<span id="cb6-4">    policy.logits.data <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> reference.logits.data.clone()   <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># start from the reference</span></span>
<span id="cb6-5">    optimiser <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.optim.Adam(policy.parameters(), lr<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>lr)</span>
<span id="cb6-6">    generator <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.Generator().manual_seed(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb6-7"></span>
<span id="cb6-8">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> _ <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(iterations):</span>
<span id="cb6-9">        sequences, old_log_prob <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> generate(policy, batch, generator)</span>
<span id="cb6-10">        scores <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> reward(sequences)</span>
<span id="cb6-11">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">with</span> torch.no_grad():</span>
<span id="cb6-12">            reference_log_prob <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> log_prob_of(reference, sequences)</span>
<span id="cb6-13"></span>
<span id="cb6-14">        advantage <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> scores <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> scores.mean()</span>
<span id="cb6-15">        advantage <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> advantage <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> (advantage.std() <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1e-8</span>)</span>
<span id="cb6-16"></span>
<span id="cb6-17">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> _ <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>):        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># reuse the batch, which is what clipping makes safe</span></span>
<span id="cb6-18">            new_log_prob <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> log_prob_of(policy, sequences)</span>
<span id="cb6-19">            ratio <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (new_log_prob <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> old_log_prob).exp().<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb6-20">            kl <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (new_log_prob <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> reference_log_prob).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb6-21"></span>
<span id="cb6-22">            clipped <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">min</span>(ratio <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> advantage,</span>
<span id="cb6-23">                                ratio.clamp(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> clip, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> clip) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> advantage)</span>
<span id="cb6-24">            loss <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>(clipped <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> kl_coefficient <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> kl).mean()</span>
<span id="cb6-25"></span>
<span id="cb6-26">            optimiser.zero_grad()</span>
<span id="cb6-27">            loss.backward()</span>
<span id="cb6-28">            optimiser.step()</span>
<span id="cb6-29">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> policy</span>
<span id="cb6-30"></span>
<span id="cb6-31"></span>
<span id="cb6-32"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'KL coefficient'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;16}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'reward'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;8}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'fluency'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;9}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">   a sample"</span>)</span>
<span id="cb6-33"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> coefficient <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> (<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.0</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.0</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">3.0</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">6.0</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">20.0</span>):</span>
<span id="cb6-34">    policy <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> train(coefficient)</span>
<span id="cb6-35">    generator <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.Generator().manual_seed(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>)</span>
<span id="cb6-36">    samples, _ <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> generate(policy, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">512</span>, generator)</span>
<span id="cb6-37">    label <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"none"</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> coefficient <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>coefficient<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:g}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb6-38">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>label<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;16}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>reward(samples)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>mean()<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;8.2f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>fluency(samples)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;9.2f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">   "</span></span>
<span id="cb6-39">          <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>samples[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>tolist()<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb6-40"></span>
<span id="cb6-41">generator <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.Generator().manual_seed(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>)</span>
<span id="cb6-42">samples, _ <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> generate(reference, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">512</span>, generator)</span>
<span id="cb6-43"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'(reference)'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;16}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>reward(samples)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>mean()<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;8.2f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>fluency(samples)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;9.2f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">   "</span></span>
<span id="cb6-44">      <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>samples[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>tolist()<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>  KL coefficient   reward   fluency   a sample
            none     6.00    -23.08   [3, 3, 3, 3, 3, 3]
               1     6.00    -23.08   [3, 3, 3, 3, 3, 3]
               3     3.00    -13.73   [1, 3, 1, 3, 1, 3]
               6     2.00     -7.12   [1, 3, 4, 2, 3, 4]
              20     2.00     -5.75   [1, 2, 3, 1, 2, 3]
     (reference)     0.84     -6.19   [4, 0, 1, 2, 6, 7]</code></pre>
</div>
</div>
</section>
<section id="read-the-samples-not-the-reward" class="level2">
<h2 class="anchored" data-anchor-id="read-the-samples-not-the-reward">Read the samples, not the reward</h2>
<p>With no KL penalty the policy scores a perfect 6 out of 6. It does this by saying <code>3 3 3 3 3 3</code>.</p>
<p>That is the best possible output according to the reward model, and it is not a sentence. Fluency under the original language collapses from −7.5 to −23. The policy has found the reward model’s blind spot and moved in.</p>
<p><strong>Nothing in the reward number tells you this happened.</strong> Reward went up monotonically the whole way. If you were watching the metric the run looks like a complete success, and this is why RLHF papers report a KL-to-reference axis next to reward rather than reward alone.</p>
<p>Turn the penalty up and something more interesting than a compromise appears. The policy settles on <code>2 3 2 3 2 3</code> — half the maximum reward, and most of the fluency back.</p>
<p>Look at why that particular sequence. In this language, 2 is usually followed by 3. So the policy found a way to say the rewarded token as often as the <strong>grammar allows</strong>, rather than as often as arithmetic allows. It’s obeying the language and pursuing the reward at the same time.</p>
<p>That’s the whole objective of RLHF in one sequence: not maximum reward, and not the original model, but the best the reward can be served without leaving the distribution the language lives in.</p>
</section>
<section id="two-things-id-tell-myself-at-the-start" class="level2">
<h2 class="anchored" data-anchor-id="two-things-id-tell-myself-at-the-start">Two things I’d tell myself at the start</h2>
<p><strong>Clipping and the KL penalty solve different problems and people conflate them.</strong> Clipping bounds how far one update moves the policy from <em>the policy that collected the batch</em>. The KL penalty bounds how far training moves the policy from <em>the model you started with</em>. You can clip perfectly and still walk all the way to <code>3 3 3 3 3 3</code>, one small safe step at a time — which is exactly what the top row of that table is.</p>
<p><strong>The ratio must score token ids, never re-tokenized text.</strong> This one cost me real time. It is natural to record a generated state as its decoded string and re-encode it when computing the ratio, and it looks equivalent. It isn’t: about one in twenty states doesn’t survive decode-then-encode, sometimes coming back with a <em>different number of tokens</em>. The two sides of the ratio were scoring different sequences, so the ratio wasn’t 1 before any gradient step had been taken — and PPO’s entire safety argument rests on it being 1 there.</p>
<p>The full project, with the real reward model and LoRA, is <a href="https://github.com/RoshBeed/ai-residency/tree/main/services/rlhf-ppo">on GitHub</a>.</p>
<hr>
<p>[1] Ouyang et al.&nbsp;<em>Training language models to follow instructions with human feedback.</em> NeurIPS 2022. The PPO algorithm itself is Schulman et al., <em>Proximal Policy Optimization Algorithms</em>, 2017.</p>
<div id="77ebf304" class="cell" data-execution_count="5">
<div class="cell-output cell-output-stdout">
<pre><code>Built:      2026-09-18 02:28:55 UTC
Python:     3.13.15
matplotlib: 3.11.2
numpy:      2.5.3
torch:      2.14.0+cpu</code></pre>
</div>
</div>


</section>

 ]]></description>
  <category>rl</category>
  <category>rlhf</category>
  <category>language</category>
  <category>week-6</category>
  <guid>https://roshbeed.com/posts/2026-09-18-what-the-kl-penalty-holds-back/</guid>
  <pubDate>Fri, 18 Sep 2026 00:00:00 GMT</pubDate>
</item>
</channel>
</rss>
