<?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: onlydev</title>
    <description>The latest articles on DEV Community by onlydev (@onlydev75).</description>
    <link>https://dev.to/onlydev75</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%2F4140404%2F38e9b191-0d2b-4da4-83b6-51c89ff2a251.jpg</url>
      <title>DEV Community: onlydev</title>
      <link>https://dev.to/onlydev75</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/onlydev75"/>
    <language>en</language>
    <item>
      <title>I Built a Small LLM From Scratch — Here’s What I Finally Understood About Transformer</title>
      <dc:creator>onlydev</dc:creator>
      <pubDate>Wed, 21 Oct 2026 05:13:19 +0000</pubDate>
      <link>https://dev.to/onlydev75/i-built-a-small-llm-from-scratch-heres-what-i-finally-understood-about-transformer-2h2o</link>
      <guid>https://dev.to/onlydev75/i-built-a-small-llm-from-scratch-heres-what-i-finally-understood-about-transformer-2h2o</guid>
      <description>&lt;p&gt;I used to understand Large Language Models mostly from the outside.&lt;/p&gt;

&lt;p&gt;I knew terms like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Transformer&lt;/li&gt;
&lt;li&gt;Attention&lt;/li&gt;
&lt;li&gt;Embeddings&lt;/li&gt;
&lt;li&gt;Tokens&lt;/li&gt;
&lt;li&gt;Fine-tuning&lt;/li&gt;
&lt;li&gt;GPT&lt;/li&gt;
&lt;li&gt;LLM&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But knowing the terminology is very different from understanding what actually happens inside a model.&lt;/p&gt;

&lt;p&gt;So instead of only reading about LLMs, I decided to &lt;strong&gt;build a small language model from scratch using PyTorch&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The goal wasn't to build the next ChatGPT.&lt;/p&gt;

&lt;p&gt;The goal was much more important for me as a learner:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt; &lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




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

&lt;p&gt;The model starts with Shakespeare text and learns to predict the next character.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Text
  ↓
Character Tokenization
  ↓
Token Embeddings
  ↓
Transformer Blocks
  ↓
RMSNorm
  ↓
Grouped Query Attention + RoPE
  ↓
SwiGLU Feed Forward Network
  ↓
Final Normalization
  ↓
Language Model Head
  ↓
Next-token prediction
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model configuration I worked with was:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;The important thing is that these aren't just names in a diagram. I implemented the pieces and followed the tensors through the model.&lt;/p&gt;




&lt;h1&gt;
  
  
  1. First Problem: How Does Text Become Numbers?
&lt;/h1&gt;

&lt;p&gt;Neural networks don't understand strings directly.&lt;/p&gt;

&lt;p&gt;So the first step was tokenization.&lt;/p&gt;

&lt;p&gt;For this project, I used a &lt;strong&gt;character-level tokenizer&lt;/strong&gt;.&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;"Hello"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;can become something like:&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;The tokenizer maintains two mappings:&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;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;character&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;integer&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;integer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;character&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&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 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;s&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;s&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;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;The important lesson for me was simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Before a model can learn language, language has to become numerical data.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The training examples are created by shifting the sequence by one position.&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:  To be o
Target: o be or
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the model is continuously learning:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Given previous characters → predict the next character
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This turns one sequence of length &lt;code&gt;N&lt;/code&gt; into &lt;code&gt;N&lt;/code&gt; next-token prediction examples.&lt;/p&gt;




&lt;h1&gt;
  
  
  2. RMSNorm — A Simpler Way to Normalize Activations
&lt;/h1&gt;

&lt;p&gt;One component I learned about was &lt;strong&gt;RMSNorm (Root Mean Square Normalization)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Traditional LayerNorm performs operations involving the mean and standard deviation.&lt;/p&gt;

&lt;p&gt;RMSNorm simplifies this.&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;x
 ↓
calculate RMS
 ↓
normalize x
 ↓
learnable scale
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The implementation is roughly:&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;The interesting part is that RMSNorm only needs a learnable scale vector.&lt;/p&gt;

&lt;p&gt;My takeaway:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Normalization isn't just a mathematical trick. It helps keep the values flowing through a deep network under control.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The material compares RMSNorm with LayerNorm and notes its use in modern architectures such as LLaMA, Mistral, and Gemma.&lt;/p&gt;




&lt;h1&gt;
  
  
  3. RoPE — How Does a Transformer Know Where a Token Is?
&lt;/h1&gt;

&lt;p&gt;This was one of the concepts I found most interesting.&lt;/p&gt;

&lt;p&gt;A transformer needs positional information.&lt;/p&gt;

&lt;p&gt;One approach is to add positional embeddings.&lt;/p&gt;

&lt;p&gt;RoPE — &lt;strong&gt;Rotary Positional Embeddings&lt;/strong&gt; — takes a different approach.&lt;/p&gt;

&lt;p&gt;Instead of simply adding a position vector, RoPE rotates the Query and Key vectors.&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;RoPE allows the attention mechanism to capture this kind of &lt;strong&gt;relative positional relationship&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;At a high level:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Query + position
Key + position
       ↓
rotations
       ↓
attention
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rotation is applied to pairs of dimensions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(d0, d1)
(d2, d3)
(d4, d5)
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and each pair gets its own rotation frequency.&lt;/p&gt;

&lt;p&gt;The biggest lesson I took from this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Position isn't just “where is this token?” — attention can benefit from understanding how far apart tokens are.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  4. GQA — Reducing Attention Memory
&lt;/h1&gt;

&lt;p&gt;Next came &lt;strong&gt;Grouped Query Attention (GQA)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This was especially useful because it showed me that modern LLM architecture isn't only about making models bigger.&lt;/p&gt;

&lt;p&gt;It's also about making them &lt;strong&gt;more efficient&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of giving every Query head its own Key and Value heads, GQA allows multiple Query heads to share KV heads.&lt;/p&gt;

&lt;p&gt;In my model:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;So four Query heads share each KV head.&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;Q0 ─┐
Q1 ─┤
Q2 ─┤──→ K0, V0
Q3 ─┘

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

&lt;/div&gt;



&lt;p&gt;This significantly reduces the size of the KV cache.&lt;/p&gt;

&lt;p&gt;The implementation first projects Q, K and V, reshapes them into heads, applies RoPE to Q and K, and then expands the KV heads to match the Query heads.&lt;/p&gt;

&lt;p&gt;This taught me an important engineering lesson:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;AI engineering isn't only about model accuracy. Memory usage and inference efficiency matter too.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  5. Causal Attention — Why Can't the Model See the Future?
&lt;/h1&gt;

&lt;p&gt;During training, the model shouldn't be allowed to cheat.&lt;/p&gt;

&lt;p&gt;Suppose we're predicting:&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
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When predicting the next token after &lt;code&gt;"The"&lt;/code&gt;, the model shouldn't be able to look at &lt;code&gt;"cat"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That's why causal masking is used.&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;       T0 T1 T2 T3

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

&lt;/div&gt;



&lt;p&gt;Each token can attend to itself and previous tokens, but not future tokens.&lt;/p&gt;

&lt;p&gt;The attention computation looks roughly 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;scores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;masked_fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-inf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&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;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;This made the phrase &lt;strong&gt;“next-token prediction”&lt;/strong&gt; much more concrete for me.&lt;/p&gt;




&lt;h1&gt;
  
  
  6. SwiGLU — The Feed-Forward Part Isn't Just ReLU
&lt;/h1&gt;

&lt;p&gt;Another component I implemented was &lt;strong&gt;SwiGLU&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A traditional feed-forward network can 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 introduces a gating mechanism.&lt;/p&gt;

&lt;p&gt;It has two paths:&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 ───────┤            × ──→ Linear ──→ Output
             └──→ Up ────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The core operation 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;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 gate learns which information should be emphasized or suppressed.&lt;/p&gt;

&lt;p&gt;The training material describes this as a gated dual-path feed-forward network, with SiLU on the gate path and element-wise multiplication controlling information flow.&lt;/p&gt;




&lt;h1&gt;
  
  
  7. Putting Everything Together
&lt;/h1&gt;

&lt;p&gt;A transformer block in my implementation follows this pattern:&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
  │
  ├── GQA + RoPE
  │
  └── Residual Add
          ↓
        x'
          │
          ├── RMSNorm
          │
          ├── SwiGLU
          │
          └── Residual Add
                 ↓
               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;This is a &lt;strong&gt;pre-norm + residual&lt;/strong&gt; architecture.&lt;/p&gt;

&lt;p&gt;The residual connection means the block learns a change to the existing representation instead of having to completely rebuild it at every layer.&lt;/p&gt;




&lt;h1&gt;
  
  
  8. The Complete Mini LLM
&lt;/h1&gt;

&lt;p&gt;The complete model then becomes:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;The model uses weight tying between the token embedding and language-model head.&lt;/p&gt;

&lt;p&gt;The complete architecture in the project uses 4 transformer blocks, 8 Query heads, 2 KV heads, and a 256-dimensional representation.&lt;/p&gt;




&lt;h1&gt;
  
  
  9. Training the Model
&lt;/h1&gt;

&lt;p&gt;Once the architecture was ready, the next question was:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How does it actually learn?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The training loop follows the familiar deep-learning pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Get batch
   ↓
Forward pass
   ↓
Calculate loss
   ↓
Zero gradients
   ↓
Backward pass
   ↓
Gradient clipping
   ↓
Optimizer step
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The project used:&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;The training code follows:&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;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;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;Gradient clipping is used to reduce the risk of exploding gradients during training.&lt;/p&gt;




&lt;h1&gt;
  
  
  10. Training Loss vs Validation Loss
&lt;/h1&gt;

&lt;p&gt;One thing I don't want to overlook when building ML models is evaluation.&lt;/p&gt;

&lt;p&gt;A model getting better on training data doesn't automatically mean it's generalizing well.&lt;/p&gt;

&lt;p&gt;So we can compare:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Training loss
      vs
Validation loss
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A growing gap between them can be a signal of overfitting.&lt;/p&gt;

&lt;p&gt;During evaluation, dropout is disabled and gradients aren't needed:&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 training material uses the train/validation loss gap as an indicator for overfitting.&lt;/p&gt;




&lt;h1&gt;
  
  
  11. Finally: Generating Text
&lt;/h1&gt;

&lt;p&gt;After training, the model can generate new text.&lt;/p&gt;

&lt;p&gt;The process is autoregressive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Prompt
  ↓
Model
  ↓
Predict next token
  ↓
Append token
  ↓
Model again
  ↓
Predict next token
  ↓
Repeat...
&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;"ROMEO:"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;gets encoded into token IDs.&lt;/p&gt;

&lt;p&gt;The model predicts the next character, adds it to the sequence, and repeats the process.&lt;/p&gt;

&lt;p&gt;The generation code takes the logits from the &lt;strong&gt;last position&lt;/strong&gt;, converts them into probabilities using softmax, samples the next token, and appends it to the sequence.&lt;/p&gt;




&lt;h1&gt;
  
  
  12. Temperature Controls Randomness
&lt;/h1&gt;

&lt;p&gt;Another concept that became much clearer after implementing generation was &lt;strong&gt;temperature&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Lower temperature:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Higher temperature:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;More diverse
More random
&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;T = 0.3 → conservative
T = 0.8 → balanced
T = 1.5 → creative
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So temperature doesn't make the model “smarter”.&lt;/p&gt;

&lt;p&gt;It changes how we sample from its probability distribution.&lt;/p&gt;




&lt;h1&gt;
  
  
  What I Actually Learned From This Project
&lt;/h1&gt;

&lt;p&gt;The biggest lesson wasn't memorizing what RMSNorm, RoPE, GQA and SwiGLU stand for.&lt;/p&gt;

&lt;p&gt;It was understanding how the pieces connect.&lt;/p&gt;

&lt;p&gt;Before this project, I could say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Transformers use attention.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now I can explain:&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
 ↓
Q / K / V
 ↓
RoPE
 ↓
Causal Attention
 ↓
GQA
 ↓
Residual Connection
 ↓
SwiGLU
 ↓
Repeated Transformer Blocks
 ↓
Logits
 ↓
Probability Distribution
 ↓
Next Token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That difference is important.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Knowing the vocabulary is not the same as understanding the system.&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Why I Built This Instead of Only Using an LLM API
&lt;/h1&gt;

&lt;p&gt;As someone preparing for an AI/ML engineering role, I think there is value in going one layer deeper.&lt;/p&gt;

&lt;p&gt;Using:&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is useful.&lt;/p&gt;

&lt;p&gt;But understanding what happens underneath gives me a stronger foundation for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;debugging ML systems&lt;/li&gt;
&lt;li&gt;reading research papers&lt;/li&gt;
&lt;li&gt;understanding model architectures&lt;/li&gt;
&lt;li&gt;optimizing inference&lt;/li&gt;
&lt;li&gt;experimenting with transformers&lt;/li&gt;
&lt;li&gt;explaining technical decisions during interviews&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I don't expect a small Shakespeare model to qualify me for an LLM engineering role by itself.&lt;/p&gt;

&lt;p&gt;But building it gave me something more valuable:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A mental model of how modern language models work.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  My Biggest Takeaways
&lt;/h1&gt;

&lt;h3&gt;
  
  
  1. Build, don't just watch
&lt;/h3&gt;

&lt;p&gt;Reading about transformers is useful.&lt;/p&gt;

&lt;p&gt;Implementing one forces you to understand the tensor shapes and data flow.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Understand the “why”
&lt;/h3&gt;

&lt;p&gt;Don't memorize:&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
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Why is it there?
What problem does it solve?
What happens if I remove it?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Follow the tensors
&lt;/h3&gt;

&lt;p&gt;One of the best ways I found to understand deep learning code is to constantly ask:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;What is the shape here?
&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;[b, seq, 256]
      ↓
[b, 8, seq, 32]
      ↓
attention
      ↓
[b, seq, 256]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. AI engineering is more than model training
&lt;/h3&gt;

&lt;p&gt;A good ML engineer needs to think about:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Data
↓
Architecture
↓
Training
↓
Evaluation
↓
Inference
↓
Efficiency
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Projects become much more valuable when you can explain them
&lt;/h3&gt;

&lt;p&gt;This is probably the biggest career lesson I took from this.&lt;/p&gt;

&lt;p&gt;Don't just put:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Built an LLM using PyTorch”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;on a resume.&lt;/p&gt;

&lt;p&gt;Be able to explain:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I implemented character tokenization, causal next-token prediction, RMSNorm, RoPE, GQA with shared KV heads, SwiGLU, residual transformer blocks, AdamW training, validation loss evaluation, and autoregressive generation.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That demonstrates actual understanding.&lt;/p&gt;




&lt;h1&gt;
  
  
  What's Next?
&lt;/h1&gt;

&lt;p&gt;This project gave me a foundation.&lt;/p&gt;

&lt;p&gt;My next step is to move from:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;“I understand how the architecture works.”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;to:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;“I can build useful AI systems with it.”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That means exploring things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fine-tuning&lt;/li&gt;
&lt;li&gt;embeddings&lt;/li&gt;
&lt;li&gt;RAG&lt;/li&gt;
&lt;li&gt;vector databases&lt;/li&gt;
&lt;li&gt;evaluation&lt;/li&gt;
&lt;li&gt;inference optimization&lt;/li&gt;
&lt;li&gt;model deployment&lt;/li&gt;
&lt;li&gt;AI agents&lt;/li&gt;
&lt;li&gt;production ML pipelines&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal isn't to learn every AI buzzword.&lt;/p&gt;

&lt;p&gt;The goal is to &lt;strong&gt;build things, understand them, and document what I learn.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;I'm learning that the fastest way to improve as an AI/ML engineer isn't to wait until I know everything.&lt;/p&gt;

&lt;p&gt;It's to:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Learn → Build → Break → Debug → Explain → Share → Repeat.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This small LLM project was one step in that process.&lt;/p&gt;

&lt;p&gt;And I'm going to keep documenting the journey.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Tech Stack:&lt;/strong&gt; Python · PyTorch · Deep Learning · Transformers · LLMs&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What would you recommend I build next — a RAG system, fine-tuned model, or an AI agent?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>deeplearning</category>
      <category>llm</category>
      <category>machinelearning</category>
      <category>python</category>
    </item>
  </channel>
</rss>
