<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Chauhan Balaji</title>
    <description>The latest articles on DEV Community by Chauhan Balaji (@balaji75).</description>
    <link>https://dev.to/balaji75</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2098484%2F69d8699d-3182-4edb-9646-ca48ea826cb9.png</url>
      <title>DEV Community: Chauhan Balaji</title>
      <link>https://dev.to/balaji75</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/balaji75"/>
    <language>en</language>
    <item>
      <title>I Built a Small LLM From Scratch — Here’s What I Finally Understood About Transformers</title>
      <dc:creator>Chauhan Balaji</dc:creator>
      <pubDate>Wed, 21 Oct 2026 05:29:10 +0000</pubDate>
      <link>https://dev.to/balaji75/i-built-a-small-llm-from-scratch-heres-what-i-finally-understood-about-transformers-4i92</link>
      <guid>https://dev.to/balaji75/i-built-a-small-llm-from-scratch-heres-what-i-finally-understood-about-transformers-4i92</guid>
      <description>&lt;h1&gt;
  
  
  How a Tiny LLM Works: Building a Language Model From Scratch in PyTorch
&lt;/h1&gt;

&lt;p&gt;Have you ever wondered what actually happens inside an LLM when you type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Hello, how are"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and it predicts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"you"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We often hear words like &lt;strong&gt;Transformer, Attention, Tokens, RoPE, GQA, RMSNorm, and SwiGLU&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But what do these things actually do?&lt;/p&gt;

&lt;p&gt;I recently worked through the implementation of a small language model from scratch, and I found that the easiest way to understand an LLM is to follow &lt;strong&gt;one piece of text through the entire model&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So in this post, we'll do exactly that.&lt;/p&gt;

&lt;p&gt;By the end, you should understand the basic flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Text
 ↓
Tokens
 ↓
Embeddings
 ↓
Transformer
 ↓
Attention
 ↓
Feed Forward Network
 ↓
Prediction
 ↓
Next Token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break it down step by step.&lt;/p&gt;




&lt;h1&gt;
  
  
  1. First: What Is a Language Model?
&lt;/h1&gt;

&lt;p&gt;A language model learns to predict what comes next.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input:
"The cat"

Prediction:
"sat"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"The cat sat"

Prediction:
"on"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"The cat sat on"

Prediction:
"the"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So at its simplest, a language model is doing:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Given the previous tokens, predict the next token.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's the central idea behind autoregressive language models.&lt;/p&gt;

&lt;p&gt;Our small model will learn this task using Shakespeare text.&lt;/p&gt;




&lt;h1&gt;
  
  
  2. Computers Don't Understand Text
&lt;/h1&gt;

&lt;p&gt;Before the model can learn anything, we have a problem.&lt;/p&gt;

&lt;p&gt;A neural network doesn't directly understand:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hello
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It needs numbers.&lt;/p&gt;

&lt;p&gt;So we convert characters into IDs.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;H → 20
e → 43
l → 50
l → 50
o → 53
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Hello"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;might become:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[20, 43, 50, 50, 53]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is called &lt;strong&gt;tokenization&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this particular project, we use a simple &lt;strong&gt;character-level tokenizer&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The basic idea is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;chars&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;char_to_idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;idx_to_char&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then encoding becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;char_to_idx&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And decoding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idx_to_char&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we now have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Human language
     ↓
Characters
     ↓
Numbers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the first step.&lt;/p&gt;




&lt;h1&gt;
  
  
  3. How Does the Model Know What to Predict?
&lt;/h1&gt;

&lt;p&gt;Suppose our text is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"To be or not to be"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can create training examples by shifting the sequence by one position.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input:  To be o
Target: o be or
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;Because each position teaches the model what comes next.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input seen        Target

T                  o
To                 [space]
To [space]         b
To [space] b       e
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the model repeatedly gets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Previous characters → Next character
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is called &lt;strong&gt;next-token prediction&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The training data in our implementation creates these shifted input/target sequences automatically.&lt;/p&gt;




&lt;h1&gt;
  
  
  4. Numbers Go Into an Embedding Layer
&lt;/h1&gt;

&lt;p&gt;We now have IDs such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[20, 43, 50, 50, 53]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But these IDs are just labels.&lt;/p&gt;

&lt;p&gt;The number &lt;code&gt;50&lt;/code&gt; isn't inherently more meaningful than &lt;code&gt;20&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So we convert each token ID into a vector.&lt;/p&gt;

&lt;p&gt;This is called a &lt;strong&gt;token embedding&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Think of it like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Token ID
   ↓
Embedding table
   ↓
Vector
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For our model, each token becomes a vector of size &lt;code&gt;256&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;50
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we might have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[0.12, -0.43, 0.87, ...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with 256 values.&lt;/p&gt;

&lt;p&gt;Now the neural network has a useful numerical representation to work with.&lt;/p&gt;




&lt;h1&gt;
  
  
  5. But How Does the Model Understand Relationships Between Tokens?
&lt;/h1&gt;

&lt;p&gt;This is where the &lt;strong&gt;Transformer&lt;/strong&gt; comes in.&lt;/p&gt;

&lt;p&gt;A Transformer processes the token representations and allows tokens to interact with one another.&lt;/p&gt;

&lt;p&gt;The most important component is:&lt;/p&gt;

&lt;h1&gt;
  
  
  Attention
&lt;/h1&gt;

&lt;p&gt;Let's use a simple example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"The dog chased the ball because it was moving."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What does &lt;code&gt;"it"&lt;/code&gt; refer to?&lt;/p&gt;

&lt;p&gt;A model needs to understand relationships between different words/tokens.&lt;/p&gt;

&lt;p&gt;Attention helps the model decide:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Which other tokens should I pay attention to when processing this token?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  6. Query, Key and Value
&lt;/h1&gt;

&lt;p&gt;Attention uses three representations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Q = Query
K = Key
V = Value
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A simple way to think about them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Query:&lt;/strong&gt; What information am I looking for?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key:&lt;/strong&gt; What information do I contain?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Value:&lt;/strong&gt; What information should I provide?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The model calculates how strongly a Query should attend to different Keys.&lt;/p&gt;

&lt;p&gt;The basic attention calculation is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Q × Kᵀ
   ↓
attention scores
   ↓
softmax
   ↓
attention weights
   ↓
weights × V
   ↓
attention output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In code, the core idea looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;scores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transpose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt;

&lt;span class="n"&gt;weights&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;softmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dim&lt;/span&gt;&lt;span class="o"&gt;=-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;weights&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But there is an important problem.&lt;/p&gt;

&lt;p&gt;During language generation, the model shouldn't be allowed to see future tokens.&lt;/p&gt;




&lt;h1&gt;
  
  
  7. Causal Attention: Don't Look Into the Future
&lt;/h1&gt;

&lt;p&gt;Suppose the sequence is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I love machine learning
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When predicting &lt;code&gt;"machine"&lt;/code&gt;, the model shouldn't already know &lt;code&gt;"learning"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So we use a &lt;strong&gt;causal mask&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It looks conceptually like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;       T0 T1 T2 T3

T0     ✓  ✗  ✗  ✗
T1     ✓  ✓  ✗  ✗
T2     ✓  ✓  ✓  ✗
T3     ✓  ✓  ✓  ✓
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;✓&lt;/code&gt; means the token can attend there.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;✗&lt;/code&gt; means it cannot.&lt;/p&gt;

&lt;p&gt;So each token can see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;itself + everything before it
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but never the future.&lt;/p&gt;

&lt;p&gt;That's what makes next-token prediction possible.&lt;/p&gt;

&lt;p&gt;The implementation applies a triangular causal mask before softmax.&lt;/p&gt;




&lt;h1&gt;
  
  
  8. Why Does the Model Need Positional Information?
&lt;/h1&gt;

&lt;p&gt;Imagine these two sentences:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dog bites man
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Man bites dog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same words are present, but their positions are different.&lt;/p&gt;

&lt;p&gt;A Transformer therefore needs positional information.&lt;/p&gt;

&lt;p&gt;One modern approach is:&lt;/p&gt;

&lt;h1&gt;
  
  
  RoPE — Rotary Positional Embeddings
&lt;/h1&gt;

&lt;p&gt;Instead of simply adding a position vector, RoPE rotates parts of the Query and Key representations.&lt;/p&gt;

&lt;p&gt;The important intuition is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Position 3 → Position 5
distance = 2

Position 7 → Position 9
distance = 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model can capture relationships based on relative position.&lt;/p&gt;

&lt;p&gt;RoPE applies rotations to pairs of dimensions in the Query and Key vectors.&lt;/p&gt;

&lt;p&gt;You don't need to memorize the rotation formula to understand the main idea:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;RoPE gives attention information about token positions and relative distances.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  9. Why Do We Need GQA?
&lt;/h1&gt;

&lt;p&gt;Now we have another engineering problem.&lt;/p&gt;

&lt;p&gt;Attention can become expensive, especially when models become large.&lt;/p&gt;

&lt;p&gt;This is where &lt;strong&gt;Grouped Query Attention (GQA)&lt;/strong&gt; can help.&lt;/p&gt;

&lt;p&gt;Normally, we might have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Q0 → K0, V0
Q1 → K1, V1
Q2 → K2, V2
Q3 → K3, V3
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With GQA, multiple Query heads share Key and Value heads.&lt;/p&gt;

&lt;p&gt;Our small model uses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;8 Query heads
2 KV heads
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Q0 ─┐
Q1 ─┤
Q2 ─┤──→ K0, V0
Q3 ─┘

Q4 ─┐
Q5 ─┤
Q6 ─┤──→ K1, V1
Q7 ─┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So four Query heads share the same KV head.&lt;/p&gt;

&lt;p&gt;Why is that useful?&lt;/p&gt;

&lt;p&gt;Because fewer Key/Value heads means a smaller KV cache and less memory usage during inference.&lt;/p&gt;

&lt;p&gt;The implementation expands the two KV heads to match the eight Query heads after applying RoPE.&lt;/p&gt;

&lt;p&gt;The important lesson:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Modern LLM architecture isn't only about making models powerful. It's also about making them efficient.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  10. What Happens After Attention?
&lt;/h1&gt;

&lt;p&gt;Attention isn't the only part of a Transformer block.&lt;/p&gt;

&lt;p&gt;After attention, we have a &lt;strong&gt;Feed-Forward Network (FFN)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In our model, we use:&lt;/p&gt;

&lt;h1&gt;
  
  
  SwiGLU
&lt;/h1&gt;

&lt;p&gt;A simple older FFN might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input
  ↓
Linear
  ↓
ReLU
  ↓
Linear
  ↓
Output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SwiGLU uses a gating mechanism.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;              ┌──→ SiLU ──┐
Input ────────┤            × ──→ Output
              └──→ Up ─────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code is essentially:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;gate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;silu&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;w_gate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;up&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;w_up&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;w_down&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gate&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;up&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The multiplication acts like a gate.&lt;/p&gt;

&lt;p&gt;It allows the network to learn which information should pass through more strongly.&lt;/p&gt;

&lt;p&gt;The implementation uses two parallel projections followed by element-wise multiplication and a final projection back to the model dimension.&lt;/p&gt;




&lt;h1&gt;
  
  
  11. Why RMSNorm?
&lt;/h1&gt;

&lt;p&gt;You'll notice another component appearing before attention and the FFN:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RMSNorm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Neural networks can become difficult to train when activations become poorly scaled.&lt;/p&gt;

&lt;p&gt;Normalization helps keep these values under control.&lt;/p&gt;

&lt;p&gt;RMSNorm works by calculating the root mean square of the values and scaling them.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input
 ↓
Calculate RMS
 ↓
Normalize
 ↓
Learnable scale
 ↓
Output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A simplified implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;rms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dim&lt;/span&gt;&lt;span class="o"&gt;=-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keepdim&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;eps&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;rms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;weight&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;RMSNorm is simpler than LayerNorm because it doesn't center the values by subtracting the mean.&lt;/p&gt;




&lt;h1&gt;
  
  
  12. Residual Connections: Why Do We Add the Input Back?
&lt;/h1&gt;

&lt;p&gt;Our Transformer block looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input
  │
  ├── RMSNorm
  ↓
Attention
  │
  ↓
+ ←──────── Input
  │
  ↓
  x'
  │
  ├── RMSNorm
  ↓
SwiGLU
  │
  ↓
+ ←──────── x'
  │
  ↓
Output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attention&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attn_norm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;rope_cos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;rope_sin&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ffn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ffn_norm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;x + ...&lt;/code&gt; part is the &lt;strong&gt;residual connection&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of forcing every layer to completely transform the representation, the layer can learn a useful change and add it to what was already there.&lt;/p&gt;

&lt;p&gt;This pattern helps make deeper networks easier to train.&lt;/p&gt;




&lt;h1&gt;
  
  
  13. Now We Can Build the Whole Transformer
&lt;/h1&gt;

&lt;p&gt;We can combine everything:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                Token IDs
                    ↓
              Token Embedding
                    ↓
          ┌─────────────────────┐
          │   Transformer       │
          │      Block           │
          │                     │
          │  RMSNorm             │
          │      ↓               │
          │  GQA + RoPE          │
          │      ↓               │
          │  Residual            │
          │      ↓               │
          │  RMSNorm             │
          │      ↓               │
          │  SwiGLU              │
          │      ↓               │
          │  Residual            │
          └─────────────────────┘
                    ↓
               × 4 Blocks
                    ↓
               Final RMSNorm
                    ↓
              Language Head
                    ↓
                 Logits
                    ↓
               Probabilities
                    ↓
              Next Character
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our implementation uses four Transformer blocks.&lt;/p&gt;

&lt;p&gt;The model configuration includes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Vocabulary:       65 characters
Embedding size:   256
Transformer blocks: 4
Query heads:      8
KV heads:         2
Context length:   256
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The complete model connects token embeddings, Transformer blocks, final normalization and the language-model head.&lt;/p&gt;




&lt;h1&gt;
  
  
  14. How Does the Model Actually Learn?
&lt;/h1&gt;

&lt;p&gt;Now we have a model.&lt;/p&gt;

&lt;p&gt;But initially, it knows nothing.&lt;/p&gt;

&lt;p&gt;So we train it.&lt;/p&gt;

&lt;p&gt;The basic training loop is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Get training data
        ↓
2. Forward pass
        ↓
3. Calculate loss
        ↓
4. Calculate gradients
        ↓
5. Update weights
        ↓
6. Repeat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;xb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;yb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_batch&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;

&lt;span class="n"&gt;logits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;yb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;optimizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zero_grad&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;loss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backward&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;optimizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our training setup uses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Batch size:     64
Learning rate:  3e-4
Optimizer:      AdamW
Training steps: 3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gradient clipping is also used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clip_grad_norm_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;max_norm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The purpose is to prevent very large gradients from destabilizing training.&lt;/p&gt;




&lt;h1&gt;
  
  
  15. What Is the Loss?
&lt;/h1&gt;

&lt;p&gt;The model outputs scores called &lt;strong&gt;logits&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For our character vocabulary, the model produces a score for each possible character.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a → 1.2
b → -0.8
c → 2.4
d → 0.1
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We convert these scores into probabilities.&lt;/p&gt;

&lt;p&gt;Then we compare the predicted probability distribution with the correct next character.&lt;/p&gt;

&lt;p&gt;This is where &lt;strong&gt;cross-entropy loss&lt;/strong&gt; is used.&lt;/p&gt;

&lt;p&gt;Simply put:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If the model gives a high probability to the correct answer, the loss is smaller.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If it gives a low probability to the correct answer, the loss is larger.&lt;/p&gt;

&lt;p&gt;The model then uses the loss to calculate gradients and update its weights.&lt;/p&gt;




&lt;h1&gt;
  
  
  16. Training vs Validation
&lt;/h1&gt;

&lt;p&gt;A model can memorize its training data.&lt;/p&gt;

&lt;p&gt;So we don't only look at training loss.&lt;/p&gt;

&lt;p&gt;We also evaluate on validation data.&lt;/p&gt;

&lt;p&gt;Think of it like studying for an exam:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Training data
= Questions you practiced

Validation data
= New questions you haven't practiced
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If training loss keeps decreasing but validation loss stops improving or gets worse, that can indicate &lt;strong&gt;overfitting&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;During evaluation, we disable dropout and don't calculate gradients:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@torch.no_grad&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;estimate_loss&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;train&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The implementation evaluates both training and validation splits to monitor the loss.&lt;/p&gt;




&lt;h1&gt;
  
  
  17. How Does Text Generation Work?
&lt;/h1&gt;

&lt;p&gt;After training, we can finally generate text.&lt;/p&gt;

&lt;p&gt;Suppose we give the model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ROMEO:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model predicts the next character.&lt;/p&gt;

&lt;p&gt;Maybe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ROMEO: W
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we feed the new sequence back into the model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ROMEO: W
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It predicts another character:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ROMEO: Wh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ROMEO: What
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And so on.&lt;/p&gt;

&lt;p&gt;So generation is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Prompt
 ↓
Predict next token
 ↓
Add token to prompt
 ↓
Predict again
 ↓
Add token
 ↓
Repeat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is called &lt;strong&gt;autoregressive generation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The implementation repeatedly takes the last-position logits, converts them to probabilities, samples the next token, and appends it to the sequence.&lt;/p&gt;




&lt;h1&gt;
  
  
  18. What Is Temperature?
&lt;/h1&gt;

&lt;p&gt;Suppose the model predicts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A → 70%
B → 20%
C → 8%
D → 2%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Should we always choose &lt;code&gt;A&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;Not necessarily.&lt;/p&gt;

&lt;p&gt;Temperature controls how sharp or random the probability distribution becomes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Low temperature
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;More predictable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Higher temperature
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;More varied
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0.3 → conservative
0.8 → balanced
1.5 → more creative
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So temperature doesn't change what the model learned.&lt;/p&gt;

&lt;p&gt;It changes &lt;strong&gt;how we sample from what the model learned&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  19. The Whole Process in One Picture
&lt;/h1&gt;

&lt;p&gt;If all of this feels like a lot, remember just this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 TEXT
                   ↓
              Tokenization
                   ↓
               Token IDs
                   ↓
             Embeddings
                   ↓
        ┌───────────────────┐
        │ Transformer Block │
        │                   │
        │ RMSNorm           │
        │ ↓                 │
        │ Attention         │
        │ ↓                 │
        │ Residual          │
        │ ↓                 │
        │ RMSNorm           │
        │ ↓                 │
        │ SwiGLU            │
        │ ↓                 │
        │ Residual          │
        └───────────────────┘
                   ↓
             More Blocks
                   ↓
             Final Norm
                   ↓
                Logits
                   ↓
             Probabilities
                   ↓
          Predict Next Token
                   ↓
          Add Token to Input
                   ↓
                Repeat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the basic idea behind an autoregressive Transformer language model.&lt;/p&gt;




&lt;h1&gt;
  
  
  20. What I Think Is Most Important to Remember
&lt;/h1&gt;

&lt;p&gt;If you're learning Transformers for the first time, &lt;strong&gt;don't try to memorize every equation immediately&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Start with these questions:&lt;/p&gt;

&lt;h3&gt;
  
  
  What is tokenization?
&lt;/h3&gt;

&lt;p&gt;Converts text into numbers.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are embeddings?
&lt;/h3&gt;

&lt;p&gt;Turns token IDs into useful vectors.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is attention?
&lt;/h3&gt;

&lt;p&gt;Allows tokens to use information from other tokens.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is causal masking?
&lt;/h3&gt;

&lt;p&gt;Prevents the model from looking at future tokens.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is RoPE?
&lt;/h3&gt;

&lt;p&gt;Provides positional information through rotations of Query and Key representations.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is GQA?
&lt;/h3&gt;

&lt;p&gt;Allows multiple Query heads to share Key/Value heads, reducing KV-cache size.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is SwiGLU?
&lt;/h3&gt;

&lt;p&gt;A gated feed-forward network used inside the Transformer block.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is RMSNorm?
&lt;/h3&gt;

&lt;p&gt;Normalizes activations using their root mean square.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is a residual connection?
&lt;/h3&gt;

&lt;p&gt;Adds the previous representation back to the transformed representation.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is training?
&lt;/h3&gt;

&lt;p&gt;Adjusting the model's weights to reduce prediction error.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is generation?
&lt;/h3&gt;

&lt;p&gt;Repeatedly predicting and adding the next token.&lt;/p&gt;

&lt;p&gt;If you understand these ten ideas, you already have a much stronger mental model of a Transformer.&lt;/p&gt;




&lt;h1&gt;
  
  
  21. The Most Useful Lesson I Got From Building It
&lt;/h1&gt;

&lt;p&gt;Before working through this implementation, I knew many of these terms individually.&lt;/p&gt;

&lt;p&gt;But knowing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RoPE
GQA
RMSNorm
SwiGLU
Attention
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;isn't enough.&lt;/p&gt;

&lt;p&gt;The important part is understanding how they work &lt;strong&gt;together&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A language model isn't one magical algorithm.&lt;/p&gt;

&lt;p&gt;It's a collection of components, each solving a particular problem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Tokenization
→ How do we represent text?

Embeddings
→ How do we represent tokens as vectors?

Attention
→ How do tokens interact?

Causal masking
→ How do we prevent cheating?

RoPE
→ How does the model know positions?

GQA
→ How can attention become more memory efficient?

SwiGLU
→ How does the network transform information?

RMSNorm
→ How do we keep activations stable?

Residual connections
→ How do we make deep networks easier to train?

Training
→ How does the model learn?

Generation
→ How does the model produce text?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you look at it this way, the architecture becomes much easier to understand.&lt;/p&gt;




&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;The easiest way I found to understand an LLM was not to start with:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Let's learn all the Transformer equations.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Instead, I started with a much simpler question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How does &lt;code&gt;"Hello"&lt;/code&gt; eventually become the next predicted token?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then I followed the data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hello
 ↓
Token IDs
 ↓
Embeddings
 ↓
Attention
 ↓
RoPE
 ↓
GQA
 ↓
SwiGLU
 ↓
Transformer blocks
 ↓
Logits
 ↓
Probability
 ↓
Next token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That made the architecture much less mysterious.&lt;/p&gt;

&lt;p&gt;I'm still learning, but building a small model forced me to stop treating LLMs as a black box and start thinking about &lt;strong&gt;data flow, tensor shapes, architecture, training, and inference&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And that's probably the most useful advice I can give to someone learning AI:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Don't just learn what a component is. Learn what problem it solves and follow the data through it.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you're learning Transformers too, I hope this makes the topic a little easier to understand.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next step:&lt;/strong&gt; take one of these components — attention, RoPE, GQA, or SwiGLU — and implement it yourself. That's where the concepts really start to stick.&lt;/p&gt;

</description>
      <category>deeplearning</category>
      <category>llm</category>
      <category>machinelearning</category>
      <category>python</category>
    </item>
    <item>
      <title>To Rephrase or Not to Rephrase: Will AI Change Its Answer If I Ask Differently?</title>
      <dc:creator>Chauhan Balaji</dc:creator>
      <pubDate>Fri, 09 Oct 2026 05:42:51 +0000</pubDate>
      <link>https://dev.to/balaji75/to-rephrase-or-not-to-rephrase-will-ai-change-its-answer-if-i-ask-differently-5b2p</link>
      <guid>https://dev.to/balaji75/to-rephrase-or-not-to-rephrase-will-ai-change-its-answer-if-i-ask-differently-5b2p</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/kaggle-2026-09-23"&gt;Kaggle Benchmarking Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  To Rephrase or Not to Rephrase: Will AI Change Its Answer If I Ask Differently?
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Kaggle Benchmarking Challenge submission&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Benchmark &amp;amp; Leaderboard:&lt;/strong&gt; &lt;a href="https://www.kaggle.com/benchmarks/chauhanbalaji/paraphrase-stability-benchmark" rel="noopener noreferrer"&gt;Paraphrase Stability Benchmark&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Task link:&lt;/strong&gt; &lt;a href="https://www.kaggle.com/benchmarks/tasks/chauhanbalaji/paraphrase-stability" rel="noopener noreferrer"&gt;Paraphrase Stability Task&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notebook &amp;amp; per-cell records:&lt;/strong&gt; &lt;a href="https://www.kaggle.com/code/chauhanbalaji/paraphrase-stability-benchmark" rel="noopener noreferrer"&gt;Public notebook&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I Benchmarked
&lt;/h2&gt;

&lt;p&gt;The idea is simple. Most leaderboards ask an AI a question &lt;strong&gt;once&lt;/strong&gt;, grade it, and move on. But real users never prompt the same way twice. So I took 32 multiple-choice questions and asked each one in &lt;strong&gt;5 semantically-equivalent phrasings&lt;/strong&gt; (canonical, reworded, terse, verbose, options-shuffled), &lt;strong&gt;twice each&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That is 32 items x 5 phrasings x 2 repeats = &lt;strong&gt;320 cells per model&lt;/strong&gt;. Across &lt;strong&gt;28 scored models&lt;/strong&gt; that is &lt;strong&gt;8,960 scored cells&lt;/strong&gt; — plus 2 runs that errored mid-grid, which I report instead of hiding.&lt;/p&gt;

&lt;p&gt;For example, the canonical version of one quantitative item looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A jacket is on sale for $80 after a 20% discount. What was the original price?

A. $96
B. $100
C. $104
D. $120

Select the correct answer and reply with just its letter (A, B, C, or D).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;em&gt;terse&lt;/em&gt; version of the same cell changes only the instruction line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A jacket is on sale for $80 after a 20% discount. What was the original price?

A. $96
B. $100
C. $104
D. $120

Answer:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A developer will immediately say &lt;strong&gt;$100&lt;/strong&gt;. But will the model still say $100 when the stem is reworded, when the prompt is stripped to &lt;code&gt;Answer:&lt;/code&gt;, or when the options are shuffled into a different order? And more importantly, what happens when the question is less obvious?&lt;/p&gt;

&lt;p&gt;Scoring is &lt;strong&gt;deterministic — no LLM judge anywhere&lt;/strong&gt;. A fixed parser extracts the chosen letter (bare letter, &lt;code&gt;answer is X&lt;/code&gt;, or option-text fallback); unparsable replies count as &lt;em&gt;wrong&lt;/em&gt; and are reported via parse-rate, never silently dropped. The headline metric is &lt;strong&gt;Stability-Adjusted Accuracy (SAA)&lt;/strong&gt;: an item scores 1 only if the model answered it correctly under &lt;strong&gt;all ten&lt;/strong&gt; framings.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Questions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Logic (8)&lt;/strong&gt; — syllogisms and paradoxes, including the knights-and-knaves puzzle where A says: &lt;em&gt;"We are both knaves."&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quantitative (8)&lt;/strong&gt; — multi-step word problems like the bat-and-ball trap and percentage-discount reversals.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Knowledge (8)&lt;/strong&gt; — verifiable facts (chemical symbols, capitals, dates).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Attention traps (8)&lt;/strong&gt; — "How many animals did Moses take on the ark?" (It was Noah.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every answer key is &lt;strong&gt;recomputed programmatically before any model is called&lt;/strong&gt; (bat-and-ball algebra, &lt;code&gt;1.5 * 0.5&lt;/code&gt;, day-of-week arithmetic). If the key mismatches, the harness refuses to run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Models Tested
&lt;/h2&gt;

&lt;p&gt;Picking the lineup was straightforward: I used Kaggle's &lt;em&gt;Evaluate More Models&lt;/em&gt; to span the whole field.&lt;/p&gt;

&lt;p&gt;First, the frontier flagships everyone benchmarks: &lt;code&gt;GPT-6 Sol&lt;/code&gt;, &lt;code&gt;GPT-5.6 Sol&lt;/code&gt;, &lt;code&gt;Claude Opus 5.5&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then the fast, cheap models I actually use day-to-day: &lt;code&gt;Gemini 3.7 Flash&lt;/code&gt;, &lt;code&gt;Gemini 3.1 Flash-Lite Preview&lt;/code&gt;, &lt;code&gt;GPT-6 Luna&lt;/code&gt;. From my experience these are efficient — but does efficiency cost &lt;em&gt;consistency&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;Then open-weights and mid-tier models mostly out of curiosity: &lt;code&gt;GLM-5&lt;/code&gt;, &lt;code&gt;Qwen 3 Coder 480B&lt;/code&gt;, &lt;code&gt;Gemma 4 26B A4B&lt;/code&gt;, &lt;code&gt;Qwen 3 Next 80B Instruct&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I also tried to include the new Grok models, but I immediately hit coverage failures with &lt;code&gt;Grok 4.20 (Non-Reasoning)&lt;/code&gt; and &lt;code&gt;Grok 4.6&lt;/code&gt;, so they are not in the scored table (the harness's &amp;gt;=90% coverage assertion failed loudly, exactly as designed). &lt;code&gt;Grok 4.20 Reasoning&lt;/code&gt; did finish — and we will talk about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Findings
&lt;/h2&gt;

&lt;p&gt;First, the scoreboard. Only &lt;strong&gt;4 of 28 models&lt;/strong&gt; survived all ten framings on every item:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;SAA&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GPT-5.6 Sol&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.00&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-6 Sol&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.00&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Opus 5.5&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.00&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GLM-5&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.00&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-6 Luna&lt;/td&gt;
&lt;td&gt;0.97&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini 3 Flash Preview&lt;/td&gt;
&lt;td&gt;0.97&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Sonnet 4.6&lt;/td&gt;
&lt;td&gt;0.97&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini 3.1 Flash-Lite Preview&lt;/td&gt;
&lt;td&gt;0.97&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemma 4 26B A4B&lt;/td&gt;
&lt;td&gt;0.97&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-6 Astra&lt;/td&gt;
&lt;td&gt;0.97&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Opus 4.8&lt;/td&gt;
&lt;td&gt;0.97&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-6.1 Sol&lt;/td&gt;
&lt;td&gt;0.96&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen 3 Coder 480B&lt;/td&gt;
&lt;td&gt;0.94&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini 3.8 Flash&lt;/td&gt;
&lt;td&gt;0.94&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini 3.7 Flash&lt;/td&gt;
&lt;td&gt;0.94&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini 3.6 Flash&lt;/td&gt;
&lt;td&gt;0.94&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Sonnet 5&lt;/td&gt;
&lt;td&gt;0.93&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini 2.5 Pro&lt;/td&gt;
&lt;td&gt;0.93&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen 3 Next 80B Instruct&lt;/td&gt;
&lt;td&gt;0.91&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Opus 5&lt;/td&gt;
&lt;td&gt;0.91&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini 3.5 Flash&lt;/td&gt;
&lt;td&gt;0.90&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini 3.1 Pro Preview&lt;/td&gt;
&lt;td&gt;0.90&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-5.4&lt;/td&gt;
&lt;td&gt;0.88&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini 2.5 Flash&lt;/td&gt;
&lt;td&gt;0.87&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Sonnet 5.5&lt;/td&gt;
&lt;td&gt;0.85&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini 3.5 Flash-Lite&lt;/td&gt;
&lt;td&gt;0.84&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-5.4 mini&lt;/td&gt;
&lt;td&gt;0.72&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grok 4.20 Reasoning&lt;/td&gt;
&lt;td&gt;0.66&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Most models score ~99% on &lt;strong&gt;raw accuracy&lt;/strong&gt;. The gap between 99% accuracy and 0.85 SAA is the entire point of this benchmark: the model &lt;em&gt;knows&lt;/em&gt; the answer, then loses it the moment you ask differently.&lt;/p&gt;

&lt;p&gt;When we look closer, the flips concentrate on the &lt;strong&gt;knights-and-knaves item (L3)&lt;/strong&gt; and the &lt;strong&gt;20%-discount item (Q2)&lt;/strong&gt;. The logic never changes between phrasings — only the words do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;canonical:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;"On an island, knights always tell the truth and knaves always
             lie. You meet A, who says: 'We are both knaves.' What is A?"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;reworded:&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="s2"&gt;"Knights on this island always tell the truth; knaves always
             lie. A tells you: 'Both of us are knaves.' What is A?"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same puzzle, same gold answer — and yet several models pick a different option under one of the two phrasings. A model that answers B, then C, then B again across repeats is &lt;em&gt;sampling&lt;/em&gt; an answer, not knowing one. And to be honest, I am glad most models fell into the paradox. A small win for the author, who can still trick AI sometimes.&lt;/p&gt;

&lt;h3&gt;
  
  
  A reference run in detail: Gemini 3.7 Flash
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Raw accuracy&lt;/td&gt;
&lt;td&gt;0.991 [95% CI 0.975, 1.000]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SAA&lt;/td&gt;
&lt;td&gt;0.938 [95% CI 0.844, 1.000]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flipped items&lt;/td&gt;
&lt;td&gt;L3, Q2 (2 of 32)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;canonical / reworded / verbose / shuffled&lt;/td&gt;
&lt;td&gt;100% / 100% / 100% / 100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;terse&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;95.3%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run instability (identical prompt, temp 0)&lt;/td&gt;
&lt;td&gt;0.6%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prompt instability (across phrasings)&lt;/td&gt;
&lt;td&gt;4.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Position bias (shuffled, by gold letter)&lt;/td&gt;
&lt;td&gt;A=1.00 B=1.00 C=1.00 D=1.00&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two things stand out. First, stripping the prompt to a bare &lt;code&gt;Answer:&lt;/code&gt; hurt more than any rewording — the model needs a little conversational room to behave. Second:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The phrasing moves models far more than the backend moves itself.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Run instability (same prompt twice, temperature 0) was 0.6%; prompt instability was 4.7%. That gap is the whole argument in one line: phrasing sensitivity is a real model behavior, not API noise.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "Newer Is Less Stable" Trap
&lt;/h2&gt;

&lt;p&gt;This is the finding I did not expect. Within model families, &lt;strong&gt;stability degrades as generations get newer&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claude Sonnet 4.6 -&amp;gt; 5 -&amp;gt; 5.5: &lt;strong&gt;0.97 -&amp;gt; 0.93 -&amp;gt; 0.85&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Gemini Flash-Lite 3.1 -&amp;gt; 3.5: &lt;strong&gt;0.97 -&amp;gt; 0.84&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We usually assume newer is strictly better. But as models are optimized for speed and single-shot benchmark accuracy, consistency under rephrasing can quietly regress. A leaderboard that asks each question once will never see this.&lt;/p&gt;

&lt;p&gt;Two more surprises:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Open weights can be perfectly stable.&lt;/strong&gt; &lt;code&gt;GLM-5&lt;/code&gt; ties &lt;code&gt;GPT-6 Sol&lt;/code&gt; and &lt;code&gt;Claude Opus 5.5&lt;/code&gt; at 1.00 — stability is trained in, not bought.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reasoning mode did not save Grok.&lt;/strong&gt; &lt;code&gt;Grok 4.20 Reasoning&lt;/code&gt; finished last at 0.66, and its non-reasoning sibling errored out entirely — so the clean reasoning-vs-non-reasoning ablation remains incomplete, and I would rather say so than paper over it.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  What I Learned
&lt;/h3&gt;

&lt;p&gt;When I designed the experiment, I worried the 32 questions would be too easy for today's models. Raw accuracy proved me right; &lt;strong&gt;SAA proved me wrong&lt;/strong&gt;. Yes, I could improve the benchmark — 32 items is a sample, not a census, and the CIs are wide on purpose (cluster bootstrap over items, 2000 resamples). But even frontier models flip on self-referential logic the moment the wording shifts. AI can absolutely help with hard questions — but if a harmless rephrase changes the answer, blindly trusting the first response in production is a risk. &lt;strong&gt;Consistency is a capability too.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Methodology &amp;amp; Limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No LLM judge.&lt;/strong&gt; Fixed letter-extraction parser; unparsable = wrong, reported via parse-rate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CIs are cluster bootstraps over the 32 items&lt;/strong&gt; — with 32 items, expect wide intervals; that honesty is the point.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SAA excludes incomplete items&lt;/strong&gt; and says so, so flaky APIs cannot inflate scores.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Errored runs are reported, not dropped&lt;/strong&gt; (Grok 4.20 Non-Reasoning, Grok 4.6).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MC-only, English-only.&lt;/strong&gt; Stability under free-form generation is strictly harder; this is the controlled version of the question.&lt;/li&gt;
&lt;li&gt;Don't rank two models on a 0.03 gap when their CIs overlap.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every run keeps its full per-cell transcript, so any flip quoted here is auditable — no judge, no vibes, just letter extraction.&lt;/p&gt;




</description>
      <category>devchallenge</category>
      <category>kagglechallenge</category>
      <category>ai</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>I Benchmarked 4 Frontier LLMs on Catching ML's "Silent Killers" — DeepSeek-R1 Missed the Most Basic Bug</title>
      <dc:creator>Chauhan Balaji</dc:creator>
      <pubDate>Sat, 03 Oct 2026 05:21:13 +0000</pubDate>
      <link>https://dev.to/balaji75/i-benchmarked-4-frontier-llms-on-catching-mls-silent-killers-deepseek-r1-missed-the-most-12a6</link>
      <guid>https://dev.to/balaji75/i-benchmarked-4-frontier-llms-on-catching-mls-silent-killers-deepseek-r1-missed-the-most-12a6</guid>
      <description>&lt;p&gt;Most public AI leaderboards test if a model can write code or pass a syntax check. But in real-world Machine Learning, the most dangerous code isn't syntactically broken—it's methodologically flawed. It passes unit tests, shows a green dashboard, and then dies silently in production.&lt;br&gt;
For the Kaggle Benchmarking Challenge, I built "The Silent Killer": an adversarial evaluation harness designed to test if frontier LLMs can actually audit ML pipelines and catch fatal data science mistakes.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What task(s) did you run?
I created a benchmark that feeds realistic, broken Heart Disease prediction pipelines to LLMs and asks them to identify the fatal flaw. I tested three specific "Silent Killers":
Data Leakage: Fitting a StandardScaler on the entire dataset before calling train_test_split (the scaler "sees" the test set, biasing the score).
The Wrong Metric: Using accuracy_score on a screening cohort that is 95% healthy and 5% sick (a model that just guesses "healthy" every time gets 95% accuracy).
Target Leakage: Using number_of_cardiology_visits as a predictor (a proxy feature that only exists after a patient is already diagnosed).
The Creative Approach (Dynamic Judge Rubric):
The hardest part of benchmarking LLMs is that they give plausible-sounding, generic advice to get partial credit. A static rubric fails because models will just list every ML best practice they know.
To solve this, I built a Dynamic Judge Rubric with a "No Misdiagnosis" Guard. The Judge LLM's grading criteria are assembled at runtime based on the specific bug in that row. If the model is reviewing the Data Leakage pipeline, but it complains about Class Imbalance instead, the Judge explicitly fails it.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Criterion 3: distractor guard -- the anti-"pattern match" check
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;rubric&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;criteria&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The response must NOT misdiagnose the flaw. It fails this check if it presents &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;any of the following as THE fatal flaw instead of the flaw in criterion 1: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rubric&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;distractors&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;. Strictness: briefly listing such issues as secondary or minor observations is &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;acceptable, but only if criterion 1 was satisfied.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This separates true methodological comprehension from simple pattern-matching.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Which models did you run it against?&lt;br&gt;
I used the Kaggle Model Proxy to test my harness against four frontier giants, chosen for their strong reasoning and coding capabilities:&lt;br&gt;
Gemini 3.7 Flash (Fast, highly capable baseline)&lt;br&gt;
Claude Sonnet 4.5 (Anthropic's flagship coding/reasoning model)&lt;br&gt;
Grok 4.20 Reasoning (xAI's deep reasoning model)&lt;br&gt;
DeepSeek-R1 (Famous for its deep, multi-step chain-of-thought reasoning)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;What are the main insights?&lt;br&gt;
The results were shocking. DeepSeek-R1, a model famous for its deep reasoning, completely missed the most fundamental ML bug of all time.&lt;br&gt;
| Model | Data Leakage | Wrong Metric | Target Leakage | Overall Score |&lt;br&gt;
|:------|:------------:|:------------:|:--------------:|:------------:|&lt;br&gt;
| &lt;strong&gt;Gemini 3.7 Flash&lt;/strong&gt; | ✅ Caught | ✅ Caught | ✅ Caught | &lt;strong&gt;100%&lt;/strong&gt; |&lt;br&gt;
| &lt;strong&gt;Claude Sonnet 4.5&lt;/strong&gt; | ✅ Caught | ✅ Caught | ✅ Caught | &lt;strong&gt;100%&lt;/strong&gt; |&lt;br&gt;
| &lt;strong&gt;Grok 4.20 Reasoning&lt;/strong&gt; | ✅ Caught | ✅ Caught | ✅ Caught | &lt;strong&gt;100%&lt;/strong&gt; |&lt;br&gt;
| &lt;strong&gt;DeepSeek-R1&lt;/strong&gt; | ❌ &lt;strong&gt;MISSED&lt;/strong&gt; | ✅ Caught | ✅ Caught | &lt;strong&gt;67%&lt;/strong&gt; |&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Where can we see it?&lt;br&gt;
You can view the full methodology, fork the code, and add your own models to the live leaderboard via the Kaggle Model Proxy here:&lt;br&gt;
👉 &lt;a href="https://www.kaggle.com/code/chauhanbalaji/the-silent-killer-ml-data-leakage-metric-detect" rel="noopener noreferrer"&gt;https://www.kaggle.com/code/chauhanbalaji/the-silent-killer-ml-data-leakage-metric-detect&lt;/a&gt;&lt;br&gt;
Evaluating AI isn't about keyword matching. It's about designing adversarial, dynamic tests that can't be gamed. What's the worst "silent killer" bug you've seen an AI write? Let me know in the comments!&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>llm</category>
    </item>
  </channel>
</rss>
