{
  "version": "https://jsonfeed.org/version/1",
  "title": "gruszka.dev",
  "home_page_url": "https://gruszka.dev/en",
  "feed_url": "https://gruszka.dev/en/tag-nlp.json",
  "description": "Things I would like to share",
  "items": [
    {
      "id": "https://gruszka.dev/en/how-computer-reads-text.html",
      "url": "https://gruszka.dev/en/how-computer-reads-text.html",
      "title": "How a computer reads text - from counting words to vectors",
      "content_html": "<div class=\"markdown-alert markdown-alert-tip\">\n<p class=\"markdown-alert-title\">Tip</p>\n<p><strong>Want to run this code yourself?</strong> All the examples from this post are in a ready-made Jupyter notebook: <a href=\"./how-computer-reads-text.ipynb\">how-computer-reads-text.ipynb</a></p>\n</div>\n<p>In the two previous posts we built a solid foundation. In the <a href=\"linguistic-features-and-llm.html\">first</a> we took language apart - phonetics, morphology, syntax, semantics, pragmatics. In the <a href=\"semiotics-and-llm.html\">second</a> we asked: &quot;OK, but does an LLM even understand what it generates?&quot; and concluded that an LLM is a machine of signs, not a mind.</p>\n<p>But one fundamental question remained unanswered: <strong>how does a computer even &quot;take&quot; text in?</strong></p>\n<p>Because a computer doesn't see letters. It doesn't see words. A computer sees <strong>numbers</strong>. So how is it that you type &quot;The cat sits on the mat&quot; and ChatGPT somehow processes it? What's the path from text to number, from number to &quot;understanding&quot;?</p>\n<p>That path is a fascinating story of evolution - from the simplest word counting, through ever smarter mathematical tricks, to vectors that can capture semantic similarities. Every step on that path was an answer to the limitations of the previous one.</p>\n<p>This is the <strong>third post in the &quot;Understanding LLM&quot; series</strong>. Today we switch the perspective from linguistic and philosophical to <strong>technical</strong>. But don't worry - there will still be plenty of examples, plenty of &quot;aha!&quot; moments, and zero formulas you can't understand ;-)</p>\n<div class=\"mermaid-diagram\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1319.9656\" height=\"96\" viewBox=\"0 0 1319.9656 96\"><rect x=\"0\" y=\"0\" width=\"1319.9656\" height=\"96\" fill=\"#FFFFFF\"/><defs><marker id=\"arrow-0\" viewBox=\"0 0 10 10\" refX=\"5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker><marker id=\"arrow-start-0\" viewBox=\"0 0 10 10\" refX=\"4.5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 5 L 10 10 L 10 0 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker></defs><path id=\"edge-0\" class=\"edgePath\" data-edge-id=\"edge-0\" d=\"M 232.384,44.000 L 282.384,44.000\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(282.38 44.00) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-1\" class=\"edgePath\" data-edge-id=\"edge-1\" d=\"M 498.117,44.000 L 548.117,44.000\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(548.12 44.00) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-2\" class=\"edgePath\" data-edge-id=\"edge-2\" d=\"M 763.849,44.000 L 813.849,44.000\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(813.85 44.00) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-3\" class=\"edgePath\" data-edge-id=\"edge-3\" d=\"M 1003.626,44.000 L 1053.626,44.000\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(1053.63 44.00) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><rect x=\"282.38\" y=\"8.00\" width=\"215.73\" height=\"72.00\" rx=\"3\" ry=\"3\" fill=\"#ffcc99\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"390.25\" y=\"37.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"390.25\" dy=\"0.00\">🔢 BoW / TF-IDF</tspan><tspan x=\"390.25\" dy=\"21.00\">&lt;i&gt;count words&lt;/i&gt;</tspan></text><rect x=\"548.12\" y=\"8.00\" width=\"215.73\" height=\"72.00\" rx=\"3\" ry=\"3\" fill=\"#ffff99\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"655.98\" y=\"37.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"655.98\" dy=\"0.00\">🔗 N-grams / Markov</tspan><tspan x=\"655.98\" dy=\"21.00\">&lt;i&gt;add context&lt;/i&gt;</tspan></text><rect x=\"813.85\" y=\"8.00\" width=\"189.78\" height=\"72.00\" rx=\"3\" ry=\"3\" fill=\"#ccff99\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"908.74\" y=\"37.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"908.74\" dy=\"0.00\">📊 Bayes</tspan><tspan x=\"908.74\" dy=\"21.00\">&lt;i&gt;classify&lt;/i&gt;</tspan></text><rect x=\"8.00\" y=\"8.00\" width=\"224.38\" height=\"72.00\" rx=\"3\" ry=\"3\" fill=\"#ff9999\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"120.19\" y=\"37.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"120.19\" dy=\"0.00\">✂️ Tokenization</tspan><tspan x=\"120.19\" dy=\"21.00\">&lt;i&gt;cut the text&lt;/i&gt;</tspan></text><rect x=\"1053.63\" y=\"8.00\" width=\"250.34\" height=\"72.00\" rx=\"3\" ry=\"3\" fill=\"#99ccff\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"1178.80\" y=\"37.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"1178.80\" dy=\"0.00\">📐 Word2Vec</tspan><tspan x=\"1178.80\" dy=\"21.00\">&lt;i&gt;meaning vectors&lt;/i&gt;</tspan></text></svg></div>\n<p>The narrative of this post: <strong>&quot;First you cut the text into pieces, then you count, then you understand.&quot;</strong> Simple? We'll see ;-) Let's start with the cutting.</p>\n<hr />\n<h2><a href=\"#tokenization---how-text-gets-split-into-pieces\" aria-hidden=\"true\" class=\"anchor\" id=\"tokenization---how-text-gets-split-into-pieces\"></a>Tokenization - how text gets split into pieces</h2>\n<h3><a href=\"#the-problem-a-computer-doesnt-see-words\" aria-hidden=\"true\" class=\"anchor\" id=\"the-problem-a-computer-doesnt-see-words\"></a>The problem: a computer doesn't see words</h3>\n<p>Imagine you're a computer. Someone shows you text:</p>\n<blockquote>\n<p>&quot;Alice has a cat.&quot;</p>\n</blockquote>\n<p>What do you see? Letters? Words? No. You see a sequence of bytes: <code>41 6c 69 63 65 20 68 61 73 20 61 20 63 61 74 2e</code>. Zero idea where one word starts and another ends. Zero idea that &quot;cat&quot; is an animal and not three random characters.</p>\n<p>For a computer to do anything with text, it first has to <strong>cut it into pieces</strong>. And that process is called <strong>tokenization</strong>.</p>\n<p>Tokenization is <strong>step zero</strong>. Without it there's no TF-IDF, no n-grams, no embeddings, no LLM. Everything starts with cutting.</p>\n<h3><a href=\"#the-naive-approach-split-on-spaces\" aria-hidden=\"true\" class=\"anchor\" id=\"the-naive-approach-split-on-spaces\"></a>The naive approach: split on spaces</h3>\n<p>The simplest idea: split the text on spaces.</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">&quot;Alice has a cat.&quot; -&gt; [&quot;Alice&quot;, &quot;has&quot;, &quot;a&quot;, &quot;cat.&quot;]\n</code></pre>\n<p>Did you notice? &quot;cat.&quot; has a period glued to it. That's not the word &quot;cat&quot; - it's the word &quot;cat&quot; with punctuation. And what about texts like these:</p>\n<ul>\n<li>&quot;un-believ-able&quot; - one word or three?</li>\n<li>&quot;Hi!&quot; - word + punctuation?</li>\n<li>&quot;New York&quot; - one word or two?</li>\n<li>&quot;:) &quot; - a word? a symbol? an emotion?</li>\n</ul>\n<p>In short: <strong>splitting on spaces doesn't work</strong>. The world is too complicated for such simple rules.</p>\n<h3><a href=\"#token--word--morpheme\" aria-hidden=\"true\" class=\"anchor\" id=\"token--word--morpheme\"></a>Token != word != morpheme</h3>\n<p>In the <a href=\"linguistic-features-and-llm.html\">first post</a> we met morphemes - the smallest meaningful units. &quot;Unhappiness&quot; is three morphemes: &quot;un-happy-ness&quot;. And now note: <strong>a token is not the same as a morpheme.</strong></p>\n<div class=\"mermaid-diagram\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"534.2058\" height=\"332.00266\" viewBox=\"0 0 534.2058 332.00266\"><rect x=\"0\" y=\"0\" width=\"534.2058\" height=\"332.00266\" fill=\"#FFFFFF\"/><defs><marker id=\"arrow-0\" viewBox=\"0 0 10 10\" refX=\"5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker><marker id=\"arrow-start-0\" viewBox=\"0 0 10 10\" refX=\"4.5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 5 L 10 10 L 10 0 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker></defs><rect x=\"8.00\" y=\"8.00\" width=\"484.25\" height=\"139.50\" rx=\"10\" ry=\"10\" fill=\"#F1F5F9\" stroke=\"#CBD5E1\" stroke-width=\"1\" /><text x=\"250.13\" y=\"35.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"250.13\" dy=\"0.00\">A linguist sees:</tspan></text><rect x=\"8.00\" y=\"197.50\" width=\"510.21\" height=\"118.50\" rx=\"10\" ry=\"10\" fill=\"#F1F5F9\" stroke=\"#CBD5E1\" stroke-width=\"1\" /><text x=\"263.10\" y=\"225.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"263.10\" dy=\"0.00\">GPT-4o sees (BPE tokens):</tspan></text><path id=\"edge-0\" class=\"edgePath\" data-edge-id=\"edge-0\" d=\"M 199.170,84.501 L 249.170,84.501\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(249.17 84.50) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-1\" class=\"edgePath\" data-edge-id=\"edge-1\" d=\"M 199.170,263.501 L 249.170,263.501\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(249.17 263.50) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><rect x=\"44.00\" y=\"59.00\" width=\"155.17\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"121.58\" y=\"88.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"121.58\" dy=\"0.00\">unhappiness</tspan></text><rect x=\"249.17\" y=\"48.50\" width=\"207.08\" height=\"72.00\" rx=\"3\" ry=\"3\" fill=\"#99ff99\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"352.71\" y=\"77.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"352.71\" dy=\"0.00\">un + happy + ness</tspan><tspan x=\"352.71\" dy=\"21.00\">&lt;i&gt;morphemes&lt;/i&gt;</tspan></text><rect x=\"44.00\" y=\"238.00\" width=\"155.17\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"121.58\" y=\"267.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"121.58\" dy=\"0.00\">unhappiness</tspan></text><rect x=\"249.17\" y=\"238.00\" width=\"233.04\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#ff9999\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"365.69\" y=\"267.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"365.69\" dy=\"0.00\">un + h + app + iness</tspan></text></svg></div>\n<p>A token is simply <strong>a piece of text</strong> determined by the tokenizer. It can be a whole word, a part of a word, or a single character. It depends on the algorithm.</p>\n<div class=\"markdown-alert markdown-alert-note\">\n<p class=\"markdown-alert-title\">Note</p>\n<p><strong>Three levels of cutting text:</strong></p>\n<ul>\n<li><strong>Word</strong> - the unit you intuitively &quot;feel&quot; (separated by spaces)</li>\n<li><strong>Morpheme</strong> - the smallest MEANINGFUL unit in a language (un-, happy-, -ness)</li>\n<li><strong>Token</strong> - a piece of text determined by a computer ALGORITHM (un, h, app, iness)</li>\n</ul>\n</div>\n<h3><a href=\"#bpe---byte-pair-encoding\" aria-hidden=\"true\" class=\"anchor\" id=\"bpe---byte-pair-encoding\"></a>BPE - Byte Pair Encoding</h3>\n<p>And here comes <strong>BPE (Byte Pair Encoding)</strong> - the most popular tokenization algorithm, used by GPT-2, GPT-3, GPT-4, Llama, and many other models.</p>\n<p>The idea of BPE can be reduced to one sentence: <strong>find the most frequent pair of characters and merge it into one token. And repeat.</strong></p>\n<p>Sounds simple? Because it is simple ;-) Let's see it on an example.</p>\n<h4><a href=\"#bpe-step-by-step\" aria-hidden=\"true\" class=\"anchor\" id=\"bpe-step-by-step\"></a>BPE step by step</h4>\n<p>We have a small corpus with five English words and their frequencies:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">&quot;low&quot;  ×10     &quot;lower&quot;  ×5      &quot;newest&quot;  ×12\n&quot;widest&quot;  ×4    &quot;new&quot;  ×5\n</code></pre>\n<p><strong>Step 0:</strong> We split everything into single characters:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">l o w       ×10     l o w e r       ×5      n e w e s t       ×12\nw i d e s t  ×4     n e w           ×5\n</code></pre>\n<p>Our starting dictionary is: <code>[&quot;d&quot;, &quot;e&quot;, &quot;i&quot;, &quot;l&quot;, &quot;n&quot;, &quot;o&quot;, &quot;r&quot;, &quot;s&quot;, &quot;t&quot;, &quot;w&quot;]</code> - simply all unique characters.</p>\n<p><strong>Step 1:</strong> We find the most frequent pair of adjacent characters:</p>\n<ul>\n<li>(e, s) appears in &quot;newest&quot; (12) and &quot;widest&quot; (4) = <strong>16 times</strong> &lt;- winner!</li>\n<li>(n, e) appears in &quot;newest&quot; (12) and &quot;new&quot; (5) = 17 times</li>\n<li>(l, o) appears in &quot;low&quot; (10) and &quot;lower&quot; (5) = 15 times</li>\n</ul>\n<p>Wait, let's recount. (n, e): &quot;newest&quot; (12) + &quot;new&quot; (5) = 17. Actually (n,e) = <strong>17 times</strong> &lt;- winner!</p>\n<p>Let's merge (n, e) -&gt; <strong>&quot;ne&quot;</strong>. Our dictionary grows by one token:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">Dictionary: [&quot;d&quot;, &quot;e&quot;, &quot;i&quot;, &quot;l&quot;, &quot;n&quot;, &quot;o&quot;, &quot;r&quot;, &quot;s&quot;, &quot;t&quot;, &quot;w&quot;, &quot;ne&quot;]\n\nl o w       ×10     l o w e r       ×5      ne w e s t        ×12\nw i d e s t  ×4     ne w            ×5\n</code></pre>\n<p><strong>Step 2:</strong> We look for the most frequent pair again:</p>\n<ul>\n<li>(ne, w) appears in &quot;newest&quot; (12) and &quot;new&quot; (5) = <strong>17 times</strong> &lt;- winner!</li>\n<li>(e, s) appears in &quot;newest&quot; (12) and &quot;widest&quot; (4) = 16 times</li>\n<li>(l, o) appears in &quot;low&quot; (10) and &quot;lower&quot; (5) = 15 times</li>\n</ul>\n<p>Merge (ne, w) -&gt; <strong>&quot;new&quot;</strong>:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">Dictionary: [..., &quot;ne&quot;, &quot;new&quot;]\n\nl o w       ×10     l o w e r       ×5      new e s t         ×12\nw i d e s t  ×4     new             ×5\n</code></pre>\n<p><strong>Step 3:</strong> The most frequent pair is now (e, s) = 16 times. Merge -&gt; <strong>&quot;es&quot;</strong>:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">Dictionary: [..., &quot;ne&quot;, &quot;new&quot;, &quot;es&quot;]\n\nl o w       ×10     l o w e r       ×5      new es t          ×12\nw i d es t   ×4     new             ×5\n</code></pre>\n<p><strong>Step 4:</strong> The most frequent pair is (l, o) = 15 times. Merge -&gt; <strong>&quot;lo&quot;</strong>:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">Dictionary: [..., &quot;ne&quot;, &quot;new&quot;, &quot;es&quot;, &quot;lo&quot;]\n\nlo w        ×10     lo w e r        ×5      new es t          ×12\nw i d es t   ×4     new             ×5\n</code></pre>\n<p><strong>Step 5:</strong> The most frequent pair is (lo, w) = 15 times. Merge -&gt; <strong>&quot;low&quot;</strong>:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">Dictionary: [..., &quot;ne&quot;, &quot;new&quot;, &quot;es&quot;, &quot;lo&quot;, &quot;low&quot;]\n\nlow         ×10     lo w e r        ×5      new es t          ×12\nw i d es t   ×4     new             ×5\n</code></pre>\n<p>And so on, until we reach the target vocabulary size. In real models:</p>\n<ul>\n<li><strong>GPT-2</strong> has a vocabulary of <strong>50 257</strong> tokens (256 bytes + 50 000 merges + 1 special)</li>\n<li><strong>GPT-4/o</strong> has a vocabulary of ~<strong>100 000</strong> tokens</li>\n<li><strong>Llama 3</strong> also uses around ~<strong>100 000</strong> tokens</li>\n</ul>\n<h3><a href=\"#when-is-the-tokenizer-used\" aria-hidden=\"true\" class=\"anchor\" id=\"when-is-the-tokenizer-used\"></a>When is the tokenizer used?</h3>\n<p>That's an important question, and the answer is: <strong>always. At both stages.</strong></p>\n<div class=\"mermaid-diagram\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"2403.962\" height=\"270.13962\" viewBox=\"0 0 2403.962 270.13962\"><rect x=\"0\" y=\"0\" width=\"2403.962\" height=\"270.13962\" fill=\"#FFFFFF\"/><defs><marker id=\"arrow-0\" viewBox=\"0 0 10 10\" refX=\"5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker><marker id=\"arrow-start-0\" viewBox=\"0 0 10 10\" refX=\"4.5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 5 L 10 10 L 10 0 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker></defs><rect x=\"1007.76\" y=\"108.81\" width=\"1380.20\" height=\"145.33\" rx=\"10\" ry=\"10\" fill=\"#F1F5F9\" stroke=\"#CBD5E1\" stroke-width=\"1\" /><text x=\"1697.86\" y=\"136.31\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"1697.86\" dy=\"0.00\">PROMPTING (inference)</tspan></text><rect x=\"8.00\" y=\"8.00\" width=\"919.76\" height=\"145.14\" rx=\"10\" ry=\"10\" fill=\"#F1F5F9\" stroke=\"#CBD5E1\" stroke-width=\"1\" /><text x=\"467.88\" y=\"35.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"467.88\" dy=\"0.00\">TRAINING</tspan></text><path id=\"edge-0\" class=\"edgePath\" data-edge-id=\"edge-0\" d=\"M 255.081,97.631 L 279.081,97.631\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(279.08 97.63) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-1\" class=\"edgePath\" data-edge-id=\"edge-1\" d=\"M 442.902,87.087 L 460.402,87.087 L 449.402,84.001 L 466.902,84.001\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(466.90 84.00) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-2\" class=\"edgePath\" data-edge-id=\"edge-2\" d=\"M 622.072,74.011 L 646.072,74.011\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(646.07 74.01) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-3\" class=\"edgePath\" data-edge-id=\"edge-3\" d=\"M 1220.233,198.631 L 1244.233,198.631\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(1244.23 198.63) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-4\" class=\"edgePath\" data-edge-id=\"edge-4\" d=\"M 1408.055,188.087 L 1425.555,188.087 L 1414.555,185.001 L 1432.055,185.001\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(1432.05 185.00) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-5\" class=\"edgePath\" data-edge-id=\"edge-5\" d=\"M 1587.224,175.011 L 1611.225,175.011\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(1611.22 175.01) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-6\" class=\"edgePath\" data-edge-id=\"edge-6\" d=\"M 1818.305,174.925 L 1842.305,174.925\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(1842.31 174.92) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-7\" class=\"edgePath\" data-edge-id=\"edge-7\" d=\"M 2032.082,174.836 L 2056.082,174.836\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(2056.08 174.84) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-8\" class=\"edgePath\" data-edge-id=\"edge-8\" d=\"M 2193.948,174.815 L 2217.948,174.815\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(2217.95 174.81) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-9\" class=\"edgePath\" data-edge-id=\"edge-9\" d=\"M 441.902,123.122 L 441.902,125.872 Q 441.902,128.122 444.152,128.122 L 1235.233,128.122 Q 1245.233,128.122 1245.233,138.122 L 1245.233,173.122\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"   stroke-dasharray=\"4 4\" stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(1245.23 173.12) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><rect data-edge-id=\"edge-9\" data-label-kind=\"center\" x=\"824.68\" y=\"130.12\" width=\"117.77\" height=\"28.40\" rx=\"2\" ry=\"2\" fill=\"#FFFFFF\" fill-opacity=\"0.00\" stroke=\"#94A3B8\" stroke-opacity=\"0.00\" stroke-width=\"0.8\"/><g class=\"edgeLabel\" data-edge-id=\"edge-9\" data-label-kind=\"center\"><text x=\"883.57\" y=\"147.82\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"883.57\" dy=\"0.00\">&quot;the same!&quot;</tspan></text></g><rect x=\"1047.76\" y=\"173.14\" width=\"172.47\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"1134.00\" y=\"202.14\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"1134.00\" dy=\"0.00\">💬 Your prompt</tspan></text><rect x=\"2056.08\" y=\"149.32\" width=\"137.87\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"2125.02\" y=\"178.32\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"2125.02\" dy=\"0.00\">📤 Decoder</tspan></text><rect x=\"2217.95\" y=\"149.31\" width=\"130.01\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"2282.96\" y=\"178.31\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"2282.96\" dy=\"0.00\">💬 Answer</tspan></text><rect x=\"1432.05\" y=\"149.52\" width=\"155.17\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"1509.64\" y=\"178.52\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"1509.64\" dy=\"0.00\">🔢 Token IDs</tspan></text><rect x=\"1611.22\" y=\"149.50\" width=\"207.08\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"1714.76\" y=\"178.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"1714.76\" dy=\"0.00\">🤖 Model generates</tspan></text><rect x=\"1842.31\" y=\"149.35\" width=\"189.78\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"1937.19\" y=\"178.35\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"1937.19\" dy=\"0.00\">🔢 New token IDs</tspan></text><rect x=\"1244.23\" y=\"173.12\" width=\"163.82\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#ff9999\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"1326.14\" y=\"202.12\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"1326.14\" dy=\"0.00\">✂️ Tokenizer</tspan></text><rect x=\"48.00\" y=\"72.14\" width=\"207.08\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"151.54\" y=\"101.14\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"151.54\" dy=\"0.00\">📚 Training corpus</tspan></text><rect x=\"466.90\" y=\"48.52\" width=\"155.17\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"544.49\" y=\"77.52\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"544.49\" dy=\"0.00\">🔢 Token IDs</tspan></text><rect x=\"646.07\" y=\"48.50\" width=\"241.69\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"766.92\" y=\"77.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"766.92\" dy=\"0.00\">🧠 You train the model</tspan></text><rect x=\"279.08\" y=\"72.12\" width=\"163.82\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#ff9999\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"360.99\" y=\"101.12\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"360.99\" dy=\"0.00\">✂️ Tokenizer</tspan></text></svg></div>\n<ol>\n<li>\n<p><strong>Training:</strong> first you train the tokenizer on a huge corpus (it learns which pairs to merge). Then you tokenize the ENTIRE training corpus into IDs. The model learns on those IDs.</p>\n</li>\n<li>\n<p><strong>Prompting:</strong> when you write to ChatGPT, your text goes through the <strong>SAME</strong> tokenizer -&gt; IDs -&gt; the model generates new IDs -&gt; a decoder turns them back into text.</p>\n</li>\n</ol>\n<p>The key point: <strong>the tokenizer is trained separately, before the model.</strong> Then it's &quot;frozen&quot; - it never changes again. GPT-2 has its tokenizer (a 50K vocabulary), GPT-4 has its own (a 100K vocabulary). And that's the reason Polish gets cut up worse in GPT-2 - because GPT-2's <strong>tokenizer</strong> was trained mainly on English, so few Polish character sequences got merged. The model itself might &quot;understand&quot; Polish better, but the tokenizer already cut the text into tiny pieces.</p>\n<div class=\"markdown-alert markdown-alert-tip\">\n<p class=\"markdown-alert-title\">Tip</p>\n<p><strong>An experiment for you:</strong> Go to <a href=\"https://tiktokenizer.vercel.app/\">tiktokenizer.vercel.app</a>, type some Polish text and see how the GPT-2 model cuts it into tokens. You'll see that Polish characters (a, e, l, s, c with diacritics) often take up 2-3 tokens! Because GPT-2 was trained mainly on English, Polish is &quot;exotic&quot; to it.</p>\n</div>\n<h3><a href=\"#different-models---different-cuts\" aria-hidden=\"true\" class=\"anchor\" id=\"different-models---different-cuts\"></a>Different models - different cuts</h3>\n<p>The key thing: <strong>every model has its own tokenizer</strong>. The same text can be cut completely differently:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-python\"><a-k>import</a-k> <a-v>tiktoken</a-v>\n\n<a-v>text</a-v> <a-o>=</a-o> <a-s>&quot;Incredibly happy&quot;</a-s>\n\n<a-v>gpt2</a-v> <a-o>=</a-o> <a-v>tiktoken</a-v>.<a-pr>get_encoding</a-pr>(<a-s>&quot;gpt2&quot;</a-s>)\n<a-v>gpt4</a-v> <a-o>=</a-o> <a-v>tiktoken</a-v>.<a-pr>get_encoding</a-pr>(<a-s>&quot;cl100k_base&quot;</a-s>)\n\n<a-f>print</a-f>(<a-s>&quot;GPT-2:&quot;</a-s>, <a-v>gpt2</a-v>.<a-pr>encode</a-pr>(<a-v>text</a-v>))\n<a-f>print</a-f>(<a-s>&quot;GPT-4:&quot;</a-s>, <a-v>gpt4</a-v>.<a-pr>encode</a-pr>(<a-v>text</a-v>))\n\n<a-f>print</a-f>(<a-s>&quot;GPT-2:&quot;</a-s>, [<a-v>gpt2</a-v>.<a-pr>decode</a-pr>([<a-v>t</a-v>]) <a-k>for</a-k> <a-v>t</a-v> <a-o>in</a-o> <a-v>gpt2</a-v>.<a-pr>encode</a-pr>(<a-v>text</a-v>)])\n<a-f>print</a-f>(<a-s>&quot;GPT-4:&quot;</a-s>, [<a-v>gpt4</a-v>.<a-pr>decode</a-pr>([<a-v>t</a-v>]) <a-k>for</a-k> <a-v>t</a-v> <a-o>in</a-o> <a-v>gpt4</a-v>.<a-pr>encode</a-pr>(<a-v>text</a-v>)])</code></pre>\n<p>GPT-2 will probably cut the text into many small fragments (because it doesn't &quot;know&quot; it well), and GPT-4 will do it much more efficiently (because it has seen more of this text).</p>\n<div class=\"mermaid-diagram\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1071.626\" height=\"360.5\" viewBox=\"0 0 1071.626 360.5\"><rect x=\"0\" y=\"0\" width=\"1071.626\" height=\"360.5\" fill=\"#FFFFFF\"/><defs><marker id=\"arrow-0\" viewBox=\"0 0 10 10\" refX=\"5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker><marker id=\"arrow-start-0\" viewBox=\"0 0 10 10\" refX=\"4.5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 5 L 10 10 L 10 0 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker></defs><rect x=\"8.00\" y=\"32.00\" width=\"330.29\" height=\"312.50\" rx=\"10\" ry=\"10\" fill=\"#F1F5F9\" stroke=\"#CBD5E1\" stroke-width=\"1\" /><text x=\"173.15\" y=\"59.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"173.15\" dy=\"0.00\">GPT-2 (English BPE):</tspan></text><rect x=\"388.29\" y=\"32.00\" width=\"312.99\" height=\"312.50\" rx=\"10\" ry=\"10\" fill=\"#F1F5F9\" stroke=\"#CBD5E1\" stroke-width=\"1\" /><text x=\"544.79\" y=\"59.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"544.79\" dy=\"0.00\">GPT-4o (multilingual BPE):</tspan></text><rect x=\"751.29\" y=\"8.00\" width=\"304.34\" height=\"315.50\" rx=\"10\" ry=\"10\" fill=\"#F1F5F9\" stroke=\"#CBD5E1\" stroke-width=\"1\" /><text x=\"903.46\" y=\"37.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"903.46\" dy=\"0.00\">Polish RoBERTa (Polish</tspan><tspan x=\"903.46\" dy=\"21.00\">SentencePiece):</tspan></text><path id=\"edge-0\" class=\"edgePath\" data-edge-id=\"edge-0\" d=\"M 173.162,144.500 L 173.162,194.500\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(173.16 194.50) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-1\" class=\"edgePath\" data-edge-id=\"edge-1\" d=\"M 544.790,144.500 L 544.790,194.500\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(544.79 194.50) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-2\" class=\"edgePath\" data-edge-id=\"edge-2\" d=\"M 903.443,144.500 L 903.443,194.500\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(903.44 194.50) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><rect x=\"69.64\" y=\"72.50\" width=\"207.08\" height=\"72.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"173.18\" y=\"101.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"173.18\" dy=\"0.00\">Nieprawdopodobnie</tspan><tspan x=\"173.18\" dy=\"21.00\">szczęśliwy</tspan></text><rect x=\"35.00\" y=\"194.50\" width=\"276.29\" height=\"114.00\" rx=\"3\" ry=\"3\" fill=\"#ff9999\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"173.15\" y=\"223.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"173.15\" dy=\"0.00\">N + ie + p + raw + d + op</tspan><tspan x=\"173.15\" dy=\"21.00\">+ od + ob + nie + s + z +</tspan><tspan x=\"173.15\" dy=\"21.00\">cz + ... + li + wy</tspan><tspan x=\"173.15\" dy=\"21.00\">&lt;i&gt;18 tokens!&lt;/i&gt;</tspan></text><rect x=\"441.25\" y=\"72.50\" width=\"207.08\" height=\"72.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"544.79\" y=\"101.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"544.79\" dy=\"0.00\">Nieprawdopodobnie</tspan><tspan x=\"544.79\" dy=\"21.00\">szczęśliwy</tspan></text><rect x=\"415.29\" y=\"194.50\" width=\"258.99\" height=\"114.00\" rx=\"3\" ry=\"3\" fill=\"#ffcc99\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"544.79\" y=\"223.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"544.79\" dy=\"0.00\">Nie + p + rawd + op +</tspan><tspan x=\"544.79\" dy=\"21.00\">odob + nie + szcz + ę +</tspan><tspan x=\"544.79\" dy=\"21.00\">śli + wy</tspan><tspan x=\"544.79\" dy=\"21.00\">&lt;i&gt;10 tokens&lt;/i&gt;</tspan></text><rect x=\"799.89\" y=\"72.50\" width=\"207.08\" height=\"72.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"903.43\" y=\"101.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"903.43\" dy=\"0.00\">Nieprawdopodobnie</tspan><tspan x=\"903.43\" dy=\"21.00\">szczęśliwy</tspan></text><rect x=\"778.29\" y=\"194.50\" width=\"250.34\" height=\"93.00\" rx=\"3\" ry=\"3\" fill=\"#99ff99\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"903.46\" y=\"223.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"903.46\" dy=\"0.00\">Nie + prawdopodobnie +</tspan><tspan x=\"903.46\" dy=\"21.00\">szczęśliwy</tspan><tspan x=\"903.46\" dy=\"21.00\">&lt;i&gt;3 tokens!&lt;/i&gt;</tspan></text></svg></div>\n<p>The example above uses Polish (&quot;Nieprawdopodobnie szczęśliwy&quot; = &quot;Incredibly happy&quot;) because it perfectly illustrates how badly a tokenizer trained on one language handles another.</p>\n<h3><a href=\"#what-about-polish-tokenizers\" aria-hidden=\"true\" class=\"anchor\" id=\"what-about-polish-tokenizers\"></a>What about Polish tokenizers?</h3>\n<p>There are models trained specifically on Polish text - and their tokenizers cut Polish <strong>much</strong> better:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-python\"><a-k>from</a-k> <a-v>transformers</a-v> <a-k>import</a-k> <a-cr>AutoTokenizer</a-cr>\n\n<a-v>text</a-v> <a-o>=</a-o> <a-s>&quot;Nieprawdopodobnie szczęśliwy&quot;</a-s>\n\n<a-v>herbert</a-v> <a-o>=</a-o> <a-cr>AutoTokenizer</a-cr>.<a-pr>from_pretrained</a-pr>(<a-s>&quot;allegro/herbert-base-cased&quot;</a-s>)\n<a-f>print</a-f>(<a-s>&quot;HerBERT (Allegro, Polish WordPiece):&quot;</a-s>, <a-v>herbert</a-v>.<a-pr>tokenize</a-pr>(<a-v>text</a-v>))\n\n<a-v>roberta</a-v> <a-o>=</a-o> <a-cr>AutoTokenizer</a-cr>.<a-pr>from_pretrained</a-pr>(<a-s>&quot;sdadas/polish-roberta-base-v2&quot;</a-s>)\n<a-f>print</a-f>(<a-s>&quot;Polish RoBERTa (Polish SentencePiece):&quot;</a-s>, <a-v>roberta</a-v>.<a-pr>tokenize</a-pr>(<a-v>text</a-v>))</code></pre>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">HerBERT (Allegro, Polish WordPiece):  ['Nie', 'prawdopodobnie&lt;/w&gt;', 'szczęśliwy&lt;/w&gt;']  -&gt; 3 tokens\nPolish RoBERTa (Polish SentencePiece): ['▁Nie', 'prawdopodobnie', '▁szczęśliwy']        -&gt; 3 tokens\n</code></pre>\n<p>Three tokens! &quot;prawdopodobnie&quot; and &quot;szczęśliwy&quot; are single tokens for a Polish tokenizer. Because that tokenizer &quot;saw&quot; these words so many times on a Polish corpus that it merged them into whole units.</p>\n<p>Two Polish tokenizers worth knowing:</p>\n<table>\n<thead>\n<tr>\n<th>Tokenizer</th>\n<th>Creator</th>\n<th>Algorithm</th>\n<th>Implemented in</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>HerBERT</strong></td>\n<td>Allegro</td>\n<td>WordPiece</td>\n<td>Polish BERT on the KGR10 corpus</td>\n</tr>\n<tr>\n<td><strong>Polish RoBERTa</strong></td>\n<td>sdadas</td>\n<td>SentencePiece (Unigram)</td>\n<td>Polish RoBERTa on a large corpus</td>\n</tr>\n</tbody>\n</table>\n<div class=\"markdown-alert markdown-alert-note\">\n<p class=\"markdown-alert-title\">Note</p>\n<p><strong>Why do Polish models cut better?</strong> Because their tokenizers were trained <strong>on Polish text</strong>. &quot;Prawdopodobnie&quot; appeared in the Polish corpus thousands of times, so BPE/SentencePiece merged it into one token. GPT-2 saw mostly English, so &quot;prawdopodobnie&quot; never got merged - and it cuts it into pieces. This is also why GPT-4o (trained on a much more multilingual corpus) cuts Polish better than GPT-2 - but still worse than typically Polish models.</p>\n</div>\n<h3><a href=\"#build-your-own-tokenizer\" aria-hidden=\"true\" class=\"anchor\" id=\"build-your-own-tokenizer\"></a>Build your own tokenizer!</h3>\n<p>Now that we understand how BPE works, let's try to train <strong>our own tokenizer</strong>. We'll use the <code>tokenizers</code> library from HuggingFace (not to be confused with <code>tiktoken</code>):</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-python\"><a-k>from</a-k> <a-v>tokenizers</a-v> <a-k>import</a-k> <a-cr>Tokenizer</a-cr>\n<a-k>from</a-k> <a-v>tokenizers</a-v>.<a-v>models</a-v> <a-k>import</a-k> <a-co>BPE</a-co>\n<a-k>from</a-k> <a-v>tokenizers</a-v>.<a-v>trainers</a-v> <a-k>import</a-k> <a-cr>BpeTrainer</a-cr>\n<a-k>from</a-k> <a-v>tokenizers</a-v>.<a-v>pre_tokenizers</a-v> <a-k>import</a-k> <a-cr>Whitespace</a-cr>\n\n<a-v>tokenizer</a-v> <a-o>=</a-o> <a-f>Tokenizer</a-f>(<a-f>BPE</a-f>(<a-v>unk_token</a-v><a-o>=</a-o><a-s>&quot;[UNK]&quot;</a-s>))\n<a-v>tokenizer</a-v>.<a-pr>pre_tokenizer</a-pr> <a-o>=</a-o> <a-f>Whitespace</a-f>()\n\n<a-v>trainer</a-v> <a-o>=</a-o> <a-f>BpeTrainer</a-f>(<a-v>vocab_size</a-v><a-o>=</a-o><a-n>300</a-n>, <a-v>special_tokens</a-v><a-o>=</a-o>[<a-s>&quot;[UNK]&quot;</a-s>])\n\n<a-v>corpus</a-v> <a-o>=</a-o> [\n    <a-s>&quot;Alice has a cat and a dog&quot;</a-s>,\n    <a-s>&quot;The cat sits on the mat&quot;</a-s>,\n    <a-s>&quot;The dog runs in the park&quot;</a-s>,\n    <a-s>&quot;Alice has a cat and a cat and once more a cat&quot;</a-s>,\n    <a-s>&quot;The cat likes milk and the cat likes to sleep&quot;</a-s>,\n    <a-s>&quot;The dog likes to run in the park and chase the cat&quot;</a-s>,\n    <a-s>&quot;A happy cat is a cat that has lots of milk&quot;</a-s>,\n    <a-s>&quot;An incredibly happy dog runs in the park&quot;</a-s>,\n    <a-s>&quot;Happy Alice has a cat and a dog and a happy home&quot;</a-s>,\n    <a-s>&quot;A home is a place where a happy family lives&quot;</a-s>,\n]\n\n<a-v>tokenizer</a-v>.<a-pr>train_from_iterator</a-pr>(<a-v>corpus</a-v>, <a-v>trainer</a-v>)\n<a-f>print</a-f>(<a-s>f&quot;Vocabulary size: </a-s><a-p>{</a-p><a-v>tokenizer</a-v><a-eb>.</a-eb><a-pr>get_vocab_size</a-pr><a-eb>()</a-eb><a-p>}</a-p><a-s>&quot;</a-s>)\n\n<a-v>test</a-v> <a-o>=</a-o> <a-s>&quot;Incredibly happy&quot;</a-s>\n<a-v>encoding</a-v> <a-o>=</a-o> <a-v>tokenizer</a-v>.<a-pr>encode</a-pr>(<a-v>test</a-v>)\n<a-f>print</a-f>(<a-s>f&#39;&quot;</a-s><a-p>{</a-p><a-v>test</a-v><a-p>}</a-p><a-s>&quot; -&gt; </a-s><a-p>{</a-p><a-v>encoding</a-v><a-eb>.</a-eb><a-pr>tokens</a-pr><a-p>}</a-p><a-s>&#39;</a-s>)</code></pre>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">Vocabulary size: 130\n&quot;Incredibly happy&quot; -&gt; ['Incredibly', 'happy']\n</code></pre>\n<p><strong>Two tokens!</strong> Because our small corpus is in English - and &quot;Incredibly&quot; and &quot;happy&quot; appeared often enough for BPE to merge them into single tokens.</p>\n<p>But wait - what do those parameters in the code actually mean?</p>\n<table>\n<thead>\n<tr>\n<th>Parameter</th>\n<th>What it does</th>\n<th>Example</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong><code>vocab_size</code></strong></td>\n<td>The maximum number of tokens in the vocabulary. BPE will merge pairs until the vocabulary reaches this size. The bigger, the longer the tokens (whole words). The smaller, the smaller the pieces (single letters).</td>\n<td>GPT-2: 50 257, ours: 300</td>\n</tr>\n<tr>\n<td><strong><code>special_tokens</code></strong></td>\n<td>Tokens with a special meaning for the model. They're always in the vocabulary, regardless of training.</td>\n<td><code>[UNK]</code>, <code>&lt;PAD&gt;</code>, <code>&lt;S&gt;</code> (start), <code>&lt;/S&gt;</code> (end)</td>\n</tr>\n<tr>\n<td><strong><code>unk_token</code></strong></td>\n<td>The &quot;unknown&quot; token - replaces every character the tokenizer can't recognize. E.g. if an emoji appears in the text and the tokenizer doesn't have it in the vocabulary, it inserts <code>[UNK]</code>.</td>\n<td><code>[UNK]</code> = &quot;I don't know what this is&quot;</td>\n</tr>\n</tbody>\n</table>\n<p>A simple analogy: <code>vocab_size</code> is the thickness of your dictionary - how many entries fit in it. <code>unk_token</code> is the entry meaning &quot;no such word&quot;. And <code>special_tokens</code> are &quot;reserved pages&quot; - they're always in the dictionary, regardless of what you teach the tokenizer.</p>\n<div class=\"markdown-alert markdown-alert-tip\">\n<p class=\"markdown-alert-title\">Tip</p>\n<p><strong>Experiment:</strong> Copy this code, change <code>vocab_size</code> to e.g. 50 and see what happens. You'll see that with a smaller vocabulary the tokenizer cuts words into smaller pieces - because it has less &quot;room&quot; for merges. That's exactly what we talked about: vocabulary size is a hyperparameter, and how finely the text gets cut depends on it.</p>\n</div>\n<div class=\"markdown-alert markdown-alert-warning\">\n<p class=\"markdown-alert-title\">Warning</p>\n<p><strong>Why does this matter?</strong> Because an LLM has a token limit in its context window. GPT-3 has 4K tokens, GPT-4 Turbo has 128K. But a &quot;token&quot; is not a &quot;word&quot;! In English ~1 token ~= 0.75 words. In a language poorly served by the tokenizer, it can be ~1 token ~= 0.4 words. So such text &quot;eats up&quot; more tokens and hits the limit faster.</p>\n</div>\n<h3><a href=\"#other-tokenization-algorithms\" aria-hidden=\"true\" class=\"anchor\" id=\"other-tokenization-algorithms\"></a>Other tokenization algorithms</h3>\n<p>BPE isn't the only player in town. Here's a quick comparison:</p>\n<table>\n<thead>\n<tr>\n<th>Algorithm</th>\n<th>Who uses it</th>\n<th>How it works</th>\n<th>Key difference</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>BPE</strong></td>\n<td>GPT, Llama, many others</td>\n<td>Merges the most frequent pair</td>\n<td>Simple, bottom-up</td>\n</tr>\n<tr>\n<td><strong>WordPiece</strong></td>\n<td>BERT, DistilBERT, Electra</td>\n<td>Merges the pair with the highest &quot;score&quot;</td>\n<td>Score = freq(pair) / (freq(a) x freq(b))</td>\n</tr>\n<tr>\n<td><strong>Unigram</strong></td>\n<td>T5, Pegasus, ALBERT</td>\n<td>Starts from a large vocabulary, removes the least useful</td>\n<td>Probabilistic, can yield different tokenizations</td>\n</tr>\n<tr>\n<td><strong>SentencePiece</strong></td>\n<td>Multilingual models</td>\n<td>BPE or Unigram on raw text (even without spaces!)</td>\n<td>Works with languages without spaces (Chinese, Japanese)</td>\n</tr>\n</tbody>\n</table>\n<p>The difference between BPE and WordPiece is subtle: BPE simply merges the <strong>most frequent</strong> pair. WordPiece merges the pair that is <strong>most surprising</strong> - i.e. one that appears together more often than would follow from the frequency of the individual elements. It's a bit like a relationship that is &quot;more than the sum of its parts&quot; ;-)</p>\n<div class=\"markdown-alert markdown-alert-note\">\n<p class=\"markdown-alert-title\">Note</p>\n<p><strong>A callback to post 1:</strong> Remember when we said Polish morphology is a nightmare for an LLM? 7 cases, inflection, lots of endings... Well, now you see why. The tokenizer doesn't &quot;know&quot; that &quot;domu&quot;, &quot;domowi&quot;, &quot;domem&quot; are inflections of the same word. To it they're just different character sequences. &quot;Understanding&quot; the relationship between forms - that's something the model has to learn on its own, during training.</p>\n</div>\n<h3><a href=\"#quiz-how-will-bpe-cut-this\" aria-hidden=\"true\" class=\"anchor\" id=\"quiz-how-will-bpe-cut-this\"></a>Quiz: how will BPE cut this?<sup class=\"footnote-ref\"><a href=\"#fn-1\" id=\"fnref-1\" data-footnote-ref>1</a></sup></h3>\n<p>We have our merges from the example above: n+e-&gt;ne, ne+w-&gt;new, e+s-&gt;es, l+o-&gt;lo, lo+w-&gt;low. How will BPE split these new words?</p>\n<ol>\n<li>&quot;lowest&quot; (assuming it wasn't in the corpus)</li>\n<li>&quot;newer&quot; (assuming it wasn't in the corpus)</li>\n<li>&quot;widower&quot; (a real English word!)</li>\n</ol>\n<hr />\n<h2><a href=\"#corpora-bag-of-words-and-tf-idf\" aria-hidden=\"true\" class=\"anchor\" id=\"corpora-bag-of-words-and-tf-idf\"></a>Corpora, Bag-of-Words, and TF-IDF</h2>\n<h3><a href=\"#what-is-a-corpus\" aria-hidden=\"true\" class=\"anchor\" id=\"what-is-a-corpus\"></a>What is a corpus?</h3>\n<p>OK, we have tokens. But how does the tokenizer know which character pairs to merge? From a <strong>corpus</strong>! BPE learns the most frequent combinations from text. And to do anything with tokens - count them, find patterns, train a model - we also need a corpus.</p>\n<p>A <strong>corpus</strong> is simply a collection of texts. It can be:</p>\n<ul>\n<li>the set of all Wikipedia articles</li>\n<li>a set of product reviews from Amazon</li>\n<li>the emails in your inbox</li>\n<li>a set of all posts on Reddit (knock on wood)</li>\n</ul>\n<p>Each individual text in a corpus we call a <strong>document</strong>. Simple?</p>\n<div class=\"mermaid-diagram\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"857.71515\" height=\"260\" viewBox=\"0 0 857.71515 260\"><rect x=\"0\" y=\"0\" width=\"857.71515\" height=\"260\" fill=\"#FFFFFF\"/><defs><marker id=\"arrow-0\" viewBox=\"0 0 10 10\" refX=\"5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker><marker id=\"arrow-start-0\" viewBox=\"0 0 10 10\" refX=\"4.5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 5 L 10 10 L 10 0 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker></defs><path id=\"edge-0\" class=\"edgePath\" data-edge-id=\"edge-0\" d=\"M 300.228,100.000 L 290.603,100.000 Q 282.728,100.000 278.523,106.658 L 254.090,145.342 Q 249.884,152.000 242.009,152.000 L 232.384,152.000\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(232.38 152.00) rotate(180.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-1\" class=\"edgePath\" data-edge-id=\"edge-1\" d=\"M 407.816,101.000 L 407.816,151.000\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(407.82 151.00) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-2\" class=\"edgePath\" data-edge-id=\"edge-2\" d=\"M 515.961,100.000 L 525.586,100.000 Q 533.461,100.000 537.566,106.720 L 561.119,145.280 Q 565.224,152.000 573.099,152.000 L 582.724,152.000\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(582.72 152.00) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><rect x=\"300.23\" y=\"8.00\" width=\"215.73\" height=\"93.00\" rx=\"3\" ry=\"3\" fill=\"#9999ff\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"408.09\" y=\"37.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#fff\"><tspan x=\"408.09\" dy=\"0.00\">📚 CORPUS</tspan><tspan x=\"408.09\" dy=\"21.00\">&lt;i&gt;a collection of</tspan><tspan x=\"408.09\" dy=\"21.00\">documents&lt;/i&gt;</tspan></text><rect x=\"8.00\" y=\"151.00\" width=\"224.38\" height=\"72.00\" rx=\"3\" ry=\"3\" fill=\"#ffcc99\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"120.19\" y=\"180.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"120.19\" dy=\"0.00\">📄 Document 1</tspan><tspan x=\"120.19\" dy=\"21.00\">This movie is great</tspan></text><rect x=\"282.38\" y=\"151.00\" width=\"250.34\" height=\"72.00\" rx=\"3\" ry=\"3\" fill=\"#ffcc99\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"407.55\" y=\"180.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"407.55\" dy=\"0.00\">📄 Document 2</tspan><tspan x=\"407.55\" dy=\"21.00\">This movie is terrible</tspan></text><rect x=\"582.72\" y=\"151.00\" width=\"258.99\" height=\"93.00\" rx=\"3\" ry=\"3\" fill=\"#ffcc99\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"712.22\" y=\"180.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"712.22\" dy=\"0.00\">📄 Document 3</tspan><tspan x=\"712.22\" dy=\"21.00\">Great book, I recommend</tspan><tspan x=\"712.22\" dy=\"21.00\">it</tspan></text></svg></div>\n<p>And these are exactly the datasets on which real models are trained - both tokenizers and the models themselves:</p>\n<table>\n<thead>\n<tr>\n<th>Corpus</th>\n<th>What it contains</th>\n<th>Size</th>\n<th>Fun fact</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>Wikitext</strong></td>\n<td>Articles from (English) Wikipedia</td>\n<td>~500 MB</td>\n<td>A standard benchmark for evaluating language models</td>\n</tr>\n<tr>\n<td><strong>Wiki-40B</strong></td>\n<td>Wikipedia in 59 languages (including Polish!)</td>\n<td>~40 GB</td>\n<td>The first step of many multilingual models</td>\n</tr>\n<tr>\n<td><strong>EuroParl</strong></td>\n<td>Transcriptions from the EU parliament (21 languages)</td>\n<td>~2 GB</td>\n<td>High-quality official texts, great for translation</td>\n</tr>\n<tr>\n<td><strong>Common Crawl</strong></td>\n<td>Dumps of web content</td>\n<td><strong>petabytes</strong> (PB!)</td>\n<td>The largest publicly available web corpus. Most LLMs use it</td>\n</tr>\n<tr>\n<td><strong>OpenWebText</strong></td>\n<td>A copy of Reddit links with &gt;3 upvotes</td>\n<td>~38 GB</td>\n<td>GPT-2 was trained on it. Filtered &quot;quality&quot; from Reddit</td>\n</tr>\n<tr>\n<td><strong>The Pile</strong></td>\n<td>A mix of 22 sources (arXiv, GitHub, Wikipedia, books...)</td>\n<td>~825 GB</td>\n<td>Created by EleutherAI as a &quot;do-everything&quot; dataset</td>\n</tr>\n<tr>\n<td><strong>RedPajama</strong></td>\n<td>An open replica of LLaMA's training data</td>\n<td>~1.2 TB</td>\n<td>That's why LLaMA is so good - trained on a huge, diverse set</td>\n</tr>\n<tr>\n<td><strong>OSCAR</strong></td>\n<td>Web dumps, filtered by language</td>\n<td>~6.5 TB</td>\n<td>Has separate subsets for each language, including Polish OSCAR</td>\n</tr>\n</tbody>\n</table>\n<p>And Polish corpora? Here are the most important ones:</p>\n<ul>\n<li><strong>NKJP</strong> (Narodowy Korpus Jezyka Polskiego / National Corpus of Polish) - millions of Polish texts, a balanced set from various fields</li>\n<li><strong>Polish-ROBERTa corpus</strong> - ~20 GB of Polish text from the web, Polish RoBERTa was trained on it</li>\n<li><strong>OSCAR (the Polish part)</strong> - Polish pages from Common Crawl, several hundred GB</li>\n<li><strong>Polish Wikipedia</strong> - ~2 GB of articles, often the starting point for Polish models</li>\n</ul>\n<div class=\"mermaid-diagram\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1194.7957\" height=\"741.5\" viewBox=\"0 0 1194.7957 741.5\"><rect x=\"0\" y=\"0\" width=\"1194.7957\" height=\"741.5\" fill=\"#FFFFFF\"/><defs><marker id=\"arrow-0\" viewBox=\"0 0 10 10\" refX=\"5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker><marker id=\"arrow-start-0\" viewBox=\"0 0 10 10\" refX=\"4.5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 5 L 10 10 L 10 0 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker></defs><rect x=\"8.00\" y=\"8.00\" width=\"1170.80\" height=\"717.50\" rx=\"10\" ry=\"10\" fill=\"#F1F5F9\" stroke=\"#CBD5E1\" stroke-width=\"1\" /><text x=\"593.40\" y=\"37.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"593.40\" dy=\"0.00\">Where LLM training data</tspan><tspan x=\"593.40\" dy=\"21.00\">comes from</tspan></text><path id=\"edge-0\" class=\"edgePath\" data-edge-id=\"edge-0\" d=\"M 289.688,363.000 L 299.313,363.000 Q 307.188,363.000 314.933,364.424 L 340.398,369.107 Q 348.143,370.531 356.018,370.531 L 365.643,370.531\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(365.64 370.53) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-1\" class=\"edgePath\" data-edge-id=\"edge-1\" d=\"M 220.473,460.500 L 230.098,460.500 Q 237.973,460.500 245.276,457.552 L 340.841,418.978 Q 348.143,416.031 356.018,416.031 L 365.643,416.031\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(365.64 416.03) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-2\" class=\"edgePath\" data-edge-id=\"edge-2\" d=\"M 263.732,143.500 L 330.643,143.500 Q 340.643,143.500 340.643,153.500 L 340.643,315.031 Q 340.643,325.031 350.643,325.031 L 365.643,325.031\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(365.64 325.03) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-3\" class=\"edgePath\" data-edge-id=\"edge-3\" d=\"M 281.036,603.500 L 355.643,603.500 Q 365.643,603.500 365.643,593.500 L 365.643,416.031\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(365.64 416.03) rotate(-90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-4\" class=\"edgePath\" data-edge-id=\"edge-4\" d=\"M 315.643,265.500 L 315.643,325.265 Q 315.643,335.265 325.643,335.265 L 361.038,335.265 Q 365.643,335.265 365.643,330.660 L 365.643,325.031\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(365.64 325.03) rotate(-90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-5\" class=\"edgePath\" data-edge-id=\"edge-5\" d=\"M 572.724,370.661 L 622.724,370.661\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(622.72 370.66) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-6\" class=\"edgePath\" data-edge-id=\"edge-6\" d=\"M 847.108,338.791 L 857.712,338.791 Q 864.608,338.791 871.358,340.203 L 872.858,340.516 Q 879.608,341.927 886.504,341.927 L 897.108,341.927\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(897.11 341.93) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-7\" class=\"edgePath\" data-edge-id=\"edge-7\" d=\"M 847.108,402.498 L 897.108,402.498\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(897.11 402.50) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><rect x=\"48.00\" y=\"194.50\" width=\"267.64\" height=\"72.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"181.82\" y=\"223.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"181.82\" dy=\"0.00\">📄 ArXiv</tspan><tspan x=\"181.82\" dy=\"21.00\">&lt;i&gt;scientific papers&lt;/i&gt;</tspan></text><rect x=\"48.00\" y=\"602.50\" width=\"233.04\" height=\"93.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"164.52\" y=\"631.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"164.52\" dy=\"0.00\">📖 Books</tspan><tspan x=\"164.52\" dy=\"21.00\">&lt;i&gt;Project Gutenberg</tspan><tspan x=\"164.52\" dy=\"21.00\">etc.&lt;/i&gt;</tspan></text><rect x=\"48.00\" y=\"316.50\" width=\"241.69\" height=\"93.00\" rx=\"3\" ry=\"3\" fill=\"#ff9999\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"168.84\" y=\"345.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"168.84\" dy=\"0.00\">🌐 Common Crawl</tspan><tspan x=\"168.84\" dy=\"21.00\">&lt;i&gt;petabytes from the</tspan><tspan x=\"168.84\" dy=\"21.00\">web&lt;/i&gt;</tspan></text><rect x=\"622.72\" y=\"324.29\" width=\"224.38\" height=\"93.00\" rx=\"3\" ry=\"3\" fill=\"#99ff99\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"734.92\" y=\"353.29\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"734.92\" dy=\"0.00\">📊 Clean corpus</tspan><tspan x=\"734.92\" dy=\"21.00\">&lt;i&gt;hundreds of GB -</tspan><tspan x=\"734.92\" dy=\"21.00\">TB&lt;/i&gt;</tspan></text><rect x=\"48.00\" y=\"72.50\" width=\"215.73\" height=\"72.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"155.87\" y=\"101.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"155.87\" dy=\"0.00\">💻 GitHub</tspan><tspan x=\"155.87\" dy=\"21.00\">&lt;i&gt;source code&lt;/i&gt;</tspan></text><rect x=\"897.11\" y=\"395.92\" width=\"198.43\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#9999ff\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"996.32\" y=\"424.92\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#fff\"><tspan x=\"996.32\" dy=\"0.00\">🧠 Model training</tspan></text><rect x=\"365.64\" y=\"324.03\" width=\"207.08\" height=\"93.00\" rx=\"3\" ry=\"3\" fill=\"#ffff99\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"469.18\" y=\"353.03\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"469.18\" dy=\"0.00\">🧹 Filtering</tspan><tspan x=\"469.18\" dy=\"21.00\">&lt;i&gt;removing spam,</tspan><tspan x=\"469.18\" dy=\"21.00\">duplicates&lt;/i&gt;</tspan></text><rect x=\"897.11\" y=\"294.92\" width=\"241.69\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#ffcc99\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"1017.95\" y=\"323.92\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"1017.95\" dy=\"0.00\">✂️ Tokenizer training</tspan></text><rect x=\"48.00\" y=\"459.50\" width=\"172.47\" height=\"93.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"134.24\" y=\"488.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"134.24\" dy=\"0.00\">📚 Wikipedia</tspan><tspan x=\"134.24\" dy=\"21.00\">&lt;i&gt;49 GB, 59</tspan><tspan x=\"134.24\" dy=\"21.00\">languages&lt;/i&gt;</tspan></text></svg></div>\n<p>Key observation: <strong>the same corpus is used to train both the tokenizer and the model.</strong> First you train the tokenizer on the corpus (it learns which character pairs to merge), then you tokenize the whole corpus with that tokenizer, and on those tokens you train the model.</p>\n<div class=\"markdown-alert markdown-alert-note\">\n<p class=\"markdown-alert-title\">Note</p>\n<p><strong>Why does this matter?</strong> If your corpus has little Polish text (e.g. GPT-2 trained mainly on English), the tokenizer won't merge Polish words, and the model won't learn Polish well. That's why Polish models (HerBERT, Polish RoBERTa) use corpora with a lot of Polish text - e.g. NKJP + Polish OSCAR + Polish Wikipedia.</p>\n</div>\n<h3><a href=\"#bag-of-words---a-bag-full-of-words\" aria-hidden=\"true\" class=\"anchor\" id=\"bag-of-words---a-bag-full-of-words\"></a>Bag-of-Words - a bag full of words</h3>\n<p>We have a corpus. Now we want to turn each document into <strong>numbers</strong> so the computer can do something with it.</p>\n<p>The simplest way: <strong>Bag-of-Words (BoW)</strong> - a bag of words. We count how many times each word appears in the document.</p>\n<p>Example. We have three short movie reviews:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">Document 1: &quot;This movie is great&quot;\nDocument 2: &quot;This movie is terrible&quot;\nDocument 3: &quot;Great movie I recommend&quot;\n</code></pre>\n<p>We build a vocabulary of all unique words:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">{this, movie, is, great, terrible, i, recommend}\n</code></pre>\n<p>And now we turn each document into a vector of counts:</p>\n<table>\n<thead>\n<tr>\n<th></th>\n<th>this</th>\n<th>movie</th>\n<th>is</th>\n<th>great</th>\n<th>terrible</th>\n<th>i</th>\n<th>recommend</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>Doc 1</strong></td>\n<td>1</td>\n<td>1</td>\n<td>1</td>\n<td>1</td>\n<td>0</td>\n<td>0</td>\n<td>0</td>\n</tr>\n<tr>\n<td><strong>Doc 2</strong></td>\n<td>1</td>\n<td>1</td>\n<td>1</td>\n<td>0</td>\n<td>1</td>\n<td>0</td>\n<td>0</td>\n</tr>\n<tr>\n<td><strong>Doc 3</strong></td>\n<td>0</td>\n<td>1</td>\n<td>0</td>\n<td>1</td>\n<td>0</td>\n<td>1</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>\n<p>Each document is now just a sequence of numbers. The computer is happy.</p>\n<p>But we're not entirely ;-). Because notice the problem:</p>\n<blockquote>\n<p>Document 1: &quot;This movie is great&quot; -&gt; [1, 1, 1, 1, 0, 0, 0]\nDocument 2: &quot;This movie is terrible&quot; -&gt; [1, 1, 1, 0, 1, 0, 0]</p>\n</blockquote>\n<p>These vectors are almost identical! They differ in one position. Yet one says &quot;great&quot; and the other &quot;terrible&quot; - completely different meanings.</p>\n<p>And there's a second problem: &quot;Dog bites man&quot; and &quot;Man bites dog&quot; will give <strong>exactly the same</strong> BoW vector. BoW completely <strong>ignores word order</strong>.</p>\n<div class=\"markdown-alert markdown-alert-note\">\n<p class=\"markdown-alert-title\">Note</p>\n<p><strong>In inflected languages things look different.</strong> In a language with cases, &quot;The dog bites the man&quot; and &quot;The man bites the dog&quot; can look different because the word forms change. But in English, &quot;dog&quot; is always &quot;dog&quot; regardless of its role in the sentence - which is exactly why BoW can't distinguish &quot;dog bites man&quot; from &quot;man bites dog&quot;. This is that morphology from the <a href=\"linguistic-features-and-llm.html\">first post</a> that sometimes helps us and sometimes gets in the way ;-)</p>\n</div>\n<div class=\"markdown-alert markdown-alert-warning\">\n<p class=\"markdown-alert-title\">Warning</p>\n<p><strong>Bag-of-Words in a nutshell:</strong></p>\n<ul>\n<li>Plus: trivially simple, works fast</li>\n<li>Minus: ignores order, ignores meaning, ignores context</li>\n<li>Metaphor: you throw all the words into a bag, shake it, and see what falls out. The bag doesn't know what was first and what was last.</li>\n</ul>\n</div>\n<h3><a href=\"#bag-of-words-in-code\" aria-hidden=\"true\" class=\"anchor\" id=\"bag-of-words-in-code\"></a>Bag-of-Words in code</h3>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-python\"><a-k>from</a-k> <a-v>sklearn</a-v>.<a-v>feature_extraction</a-v>.<a-v>text</a-v> <a-k>import</a-k> <a-cr>CountVectorizer</a-cr>\n<a-k>import</a-k> <a-v>pandas</a-v> <a-k>as</a-k> <a-v>pd</a-v>\n\n<a-v>corpus</a-v> <a-o>=</a-o> [\n    <a-s>&quot;This movie is great&quot;</a-s>,\n    <a-s>&quot;This movie is terrible&quot;</a-s>,\n    <a-s>&quot;Great movie I recommend&quot;</a-s>,\n]\n\n<a-v>vectorizer</a-v> <a-o>=</a-o> <a-f>CountVectorizer</a-f>()\n<a-co>X</a-co> <a-o>=</a-o> <a-v>vectorizer</a-v>.<a-pr>fit_transform</a-pr>(<a-v>corpus</a-v>)\n\n<a-v>df</a-v> <a-o>=</a-o> <a-v>pd</a-v>.<a-pr>DataFrame</a-pr>(\n    <a-co>X</a-co>.<a-pr>toarray</a-pr>(),\n    <a-v>columns</a-v><a-o>=</a-o><a-v>vectorizer</a-v>.<a-pr>get_feature_names_out</a-pr>(),\n    <a-v>index</a-v><a-o>=</a-o>[<a-s>&quot;Doc 1&quot;</a-s>, <a-s>&quot;Doc 2&quot;</a-s>, <a-s>&quot;Doc 3&quot;</a-s>]\n)\n<a-f>print</a-f>(<a-v>df</a-v>)</code></pre>\n<p>Result:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">        great  i  is  movie  recommend  terrible  this\nDoc 1      1  0   1      1          0         0     1\nDoc 2      0  0   1      1          0         1     1\nDoc 3      1  1   0      1          1         0     0\n</code></pre>\n<p>Exactly the same table as above - only now generated by code. Notice: &quot;movie&quot; is everywhere (value 1 in every row), and &quot;terrible&quot; only in Document 2. That's the whole magic of BoW - simple counting.</p>\n<div class=\"markdown-alert markdown-alert-important\">\n<p class=\"markdown-alert-title\">Important</p>\n<p><strong>Can BoW generate new texts?</strong> No. BoW is only a way of <strong>representing</strong> text (text -&gt; numbers). It's used to analyze, compare, and classify documents, but it doesn't generate anything new. It also can't predict what word should come after &quot;The cat sits on the...&quot;. To generate text, we need something that understands word <strong>order</strong> and can predict the next one. And that's exactly what we're getting to with n-grams and Markov chains ;-)</p>\n</div>\n<h3><a href=\"#tf-idf---what-if-not-all-words-are-equal\" aria-hidden=\"true\" class=\"anchor\" id=\"tf-idf---what-if-not-all-words-are-equal\"></a>TF-IDF - what if not all words are equal?</h3>\n<p>BoW treats every word the same. But <strong>not every word is equally important!</strong></p>\n<p>The word &quot;is&quot; appears in almost every English sentence. The word &quot;semiotics&quot; - rather rarely. Which is more informative? Obviously &quot;semiotics&quot; - because if you see it in a document, it tells you much more about its content than &quot;is&quot;.</p>\n<p>TF-IDF (Term Frequency - Inverse Document Frequency) is a way to capture that.</p>\n<p>Recall our corpus - three short movie reviews:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">Document 1: &quot;This movie is great&quot;          (4 words)\nDocument 2: &quot;This movie is terrible&quot;       (4 words)\nDocument 3: &quot;Great movie I recommend&quot;      (3 words)\n</code></pre>\n<p>Let's compute TF-IDF for the word <strong>&quot;great&quot;</strong> in Document 1:</p>\n<p><strong>TF (Term Frequency)</strong> - how often the word appears in one document:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">TF(&quot;great&quot;, Document 1) = 1 / 4 = 0.25\n</code></pre>\n<p>(1 occurrence in a 4-word document)</p>\n<p><strong>IDF (Inverse Document Frequency)</strong> - how &quot;rare&quot; the word is across the whole corpus:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">IDF(&quot;great&quot;) = log(3 / 2) = 0.176\n</code></pre>\n<p>(3 documents in the corpus, 2 of them contain &quot;great&quot;)</p>\n<p><strong>TF-IDF</strong> = TF x IDF:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">TF-IDF(&quot;great&quot;, Document 1) = 0.25 x 0.176 = 0.044\n</code></pre>\n<p>And the word &quot;is&quot;, which is EVERYWHERE?</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">IDF(&quot;is&quot;) = log(3 / 2) = 0.176  (also in 2 of 3 documents)\nIDF(&quot;movie&quot;) = log(3 / 3) = 0      (in ALL documents!)\n</code></pre>\n<p>&quot;Movie&quot; gets <strong>zero</strong> in IDF! Because if a word is in every document, it carries no information that would help tell one document apart from another.</p>\n<div class=\"markdown-alert markdown-alert-tip\">\n<p class=\"markdown-alert-title\">Tip</p>\n<p><strong>The intuition behind TF-IDF in one sentence:</strong> A word is important for a given document if it appears there often (high TF) but rarely in other documents (high IDF). In other words: &quot;Are you unique?&quot;</p>\n</div>\n<h3><a href=\"#tf-idf-in-code\" aria-hidden=\"true\" class=\"anchor\" id=\"tf-idf-in-code\"></a>TF-IDF in code</h3>\n<p>Try it yourself. Here's a complete example in Python:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-python\"><a-k>from</a-k> <a-v>sklearn</a-v>.<a-v>feature_extraction</a-v>.<a-v>text</a-v> <a-k>import</a-k> <a-cr>TfidfVectorizer</a-cr>\n<a-k>import</a-k> <a-v>pandas</a-v> <a-k>as</a-k> <a-v>pd</a-v>\n\n<a-v>corpus</a-v> <a-o>=</a-o> [\n    <a-s>&quot;This movie is great&quot;</a-s>,\n    <a-s>&quot;This movie is terrible&quot;</a-s>,\n    <a-s>&quot;Great movie I recommend&quot;</a-s>,\n]\n\n<a-v>vectorizer</a-v> <a-o>=</a-o> <a-f>TfidfVectorizer</a-f>()\n<a-v>tfidf_matrix</a-v> <a-o>=</a-o> <a-v>vectorizer</a-v>.<a-pr>fit_transform</a-pr>(<a-v>corpus</a-v>)\n\n<a-v>df</a-v> <a-o>=</a-o> <a-v>pd</a-v>.<a-pr>DataFrame</a-pr>(\n    <a-v>tfidf_matrix</a-v>.<a-pr>toarray</a-pr>(),\n    <a-v>columns</a-v><a-o>=</a-o><a-v>vectorizer</a-v>.<a-pr>get_feature_names_out</a-pr>(),\n    <a-v>index</a-v><a-o>=</a-o>[<a-s>&quot;Doc 1&quot;</a-s>, <a-s>&quot;Doc 2&quot;</a-s>, <a-s>&quot;Doc 3&quot;</a-s>]\n)\n<a-f>print</a-f>(<a-v>df</a-v>.<a-pr>round</a-pr>(<a-n>2</a-n>))</code></pre>\n<p>The result will look roughly like this:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">        great    i    is  movie  recommend  terrible  this\nDoc 1     0.41  0.0  0.49   0.37        0.0       0.0  0.49\nDoc 2     0.00  0.0  0.46   0.35        0.0       0.58  0.46\nDoc 3     0.41  0.54  0.0   0.41        0.54       0.0  0.00\n</code></pre>\n<p>Notice: &quot;movie&quot; has low values everywhere (because it's everywhere). &quot;terrible&quot; has a high value only in Document 2 (because it only appears there). TF-IDF works!</p>\n<div class=\"markdown-alert markdown-alert-tip\">\n<p class=\"markdown-alert-title\">Tip</p>\n<p><strong>An experiment for you:</strong> Think about your study (or work) notes. Which words would have high TF-IDF? Probably technical terms - &quot;recursion&quot;, &quot;entropy&quot;, &quot;backpropagation&quot;. And which would have low ones? &quot;And&quot;, &quot;is&quot;, &quot;on&quot;, &quot;that&quot; - because they're everywhere.</p>\n</div>\n<h3><a href=\"#tf-idf-as-a-search-engine\" aria-hidden=\"true\" class=\"anchor\" id=\"tf-idf-as-a-search-engine\"></a>TF-IDF as a search engine</h3>\n<p>TF-IDF has one super practical application: <strong>text search</strong>. This is basically how the first search engines worked.</p>\n<p>The idea is simple: you turn the user's query into a TF-IDF vector, and look for the document whose vector is <strong>most similar</strong> to it (so-called cosine similarity).</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-python\"><a-k>from</a-k> <a-v>sklearn</a-v>.<a-v>feature_extraction</a-v>.<a-v>text</a-v> <a-k>import</a-k> <a-cr>TfidfVectorizer</a-cr>\n<a-k>from</a-k> <a-v>sklearn</a-v>.<a-v>metrics</a-v>.<a-v>pairwise</a-v> <a-k>import</a-k> <a-v>cosine_similarity</a-v>\n<a-k>import</a-k> <a-v>numpy</a-v> <a-k>as</a-k> <a-v>np</a-v>\n\n<a-v>corpus</a-v> <a-o>=</a-o> [\n    <a-s>&quot;This movie is great&quot;</a-s>,\n    <a-s>&quot;This movie is terrible&quot;</a-s>,\n    <a-s>&quot;Great movie I recommend&quot;</a-s>,\n]\n\n<a-v>vectorizer</a-v> <a-o>=</a-o> <a-f>TfidfVectorizer</a-f>()\n<a-v>tfidf_matrix</a-v> <a-o>=</a-o> <a-v>vectorizer</a-v>.<a-pr>fit_transform</a-pr>(<a-v>corpus</a-v>)\n\n<a-v>query</a-v> <a-o>=</a-o> <a-s>&quot;great movie&quot;</a-s>\n<a-v>query_vec</a-v> <a-o>=</a-o> <a-v>vectorizer</a-v>.<a-pr>transform</a-pr>([<a-v>query</a-v>])\n\n<a-v>similarities</a-v> <a-o>=</a-o> <a-f>cosine_similarity</a-f>(<a-v>query_vec</a-v>, <a-v>tfidf_matrix</a-v>)[<a-n>0</a-n>]\n<a-v>ranked</a-v> <a-o>=</a-o> <a-f>sorted</a-f>(<a-f>zip</a-f>(<a-v>corpus</a-v>, <a-v>similarities</a-v>), <a-v>key</a-v><a-o>=</a-o><a-k>lambda</a-k> <a-v>x</a-v>: <a-o>-</a-o><a-v>x</a-v>[<a-n>1</a-n>])\n<a-k>for</a-k> <a-v>i</a-v>, (<a-v>doc</a-v>, <a-v>sim</a-v>) <a-o>in</a-o> <a-f>enumerate</a-f>(<a-v>ranked</a-v>, <a-n>1</a-n>):\n    <a-f>print</a-f>(<a-s>f&#39;</a-s><a-p>{</a-p><a-v>i</a-v><a-p>}</a-p><a-s>. &quot;</a-s><a-p>{</a-p><a-v>doc</a-v><a-p>}</a-p><a-s>&quot; -&gt; </a-s><a-p>{</a-p><a-v>sim</a-v><a-eb>:.3f</a-eb><a-p>}</a-p><a-s>&#39;</a-s>)</code></pre>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">Query: &quot;great movie&quot;\n\n1. &quot;Great movie I recommend&quot;        -&gt; 0.694  &lt;- best result!\n2. &quot;This movie is great&quot;            -&gt; 0.667\n3. &quot;This movie is terrible&quot;         -&gt; 0.229\n</code></pre>\n<p>Document 3 wins - because &quot;great&quot; and &quot;movie&quot; are high-TF-IDF keywords for it. Document 2 has &quot;movie&quot; but not &quot;great&quot; - so its similarity is low.</p>\n<div class=\"markdown-alert markdown-alert-note\">\n<p class=\"markdown-alert-title\">Note</p>\n<p><strong>Cosine similarity</strong> measures the angle between two vectors. If the vectors point in the same direction (similar proportions of words) - the similarity is close to 1. If in opposite directions - close to 0. Don't worry about the math - the intuition is simple: &quot;how similar are these two vectors to each other?&quot;</p>\n</div>\n<hr />\n<h2><a href=\"#n-grams---or-adding-context\" aria-hidden=\"true\" class=\"anchor\" id=\"n-grams---or-adding-context\"></a>N-grams - or, adding context</h2>\n<h3><a href=\"#the-problem-words-dont-live-in-a-vacuum\" aria-hidden=\"true\" class=\"anchor\" id=\"the-problem-words-dont-live-in-a-vacuum\"></a>The problem: words don't live in a vacuum</h3>\n<p>TF-IDF is better than pure BoW, because at least it weights words by importance. But it still treats every word <strong>separately</strong>. It doesn't know that &quot;do&quot; + &quot;not&quot; + &quot;like&quot; = something completely different than &quot;like&quot; alone.</p>\n<p>Or an English example that shows the problem perfectly:</p>\n<ul>\n<li>&quot;big red <strong>carpet</strong> and machine&quot; - we're talking about a carpet</li>\n<li>&quot;big red <strong>machine</strong> and carpet&quot; - we're talking about a machine</li>\n</ul>\n<p>The same words! But the order changes everything. BoW and TF-IDF don't see it.</p>\n<h3><a href=\"#what-are-n-grams\" aria-hidden=\"true\" class=\"anchor\" id=\"what-are-n-grams\"></a>What are n-grams?</h3>\n<p>An <strong>n-gram</strong> is simply a sequence of N consecutive elements (usually words or characters).</p>\n<p>Example with the sentence &quot;Alice has a cat&quot;:</p>\n<table>\n<thead>\n<tr>\n<th>Type</th>\n<th>N</th>\n<th>Result</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Unigrams</td>\n<td>1</td>\n<td>Alice, has, a, cat</td>\n</tr>\n<tr>\n<td>Bigrams</td>\n<td>2</td>\n<td>Alice has, has a, a cat</td>\n</tr>\n<tr>\n<td>Trigrams</td>\n<td>3</td>\n<td>Alice has a, has a cat</td>\n</tr>\n</tbody>\n</table>\n<p>And now the magic: instead of counting single words, we count <strong>pairs of words</strong> (bigrams). And suddenly:</p>\n<ul>\n<li>&quot;do not&quot; becomes a single entity (negation + verb = negative sense)</li>\n<li>&quot;big red&quot; and &quot;red carpet&quot; are different things</li>\n<li>&quot;New York&quot; is one unit, not two words</li>\n</ul>\n<p>You can also combine n-grams with TF-IDF! In sklearn it's enough to change one parameter:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-python\"><a-k>from</a-k> <a-v>sklearn</a-v>.<a-v>feature_extraction</a-v>.<a-v>text</a-v> <a-k>import</a-k> <a-cr>TfidfVectorizer</a-cr>\n<a-k>import</a-k> <a-v>pandas</a-v> <a-k>as</a-k> <a-v>pd</a-v>\n\n<a-v>corpus</a-v> <a-o>=</a-o> [\n    <a-s>&quot;This movie is great&quot;</a-s>,\n    <a-s>&quot;This movie is terrible&quot;</a-s>,\n    <a-s>&quot;Great movie I recommend&quot;</a-s>,\n]\n\n<a-v>vectorizer</a-v> <a-o>=</a-o> <a-f>TfidfVectorizer</a-f>(<a-v>ngram_range</a-v><a-o>=</a-o>(<a-n>1</a-n>, <a-n>2</a-n>))\n<a-v>tfidf_matrix</a-v> <a-o>=</a-o> <a-v>vectorizer</a-v>.<a-pr>fit_transform</a-pr>(<a-v>corpus</a-v>)\n\n<a-v>df</a-v> <a-o>=</a-o> <a-v>pd</a-v>.<a-pr>DataFrame</a-pr>(\n    <a-v>tfidf_matrix</a-v>.<a-pr>toarray</a-pr>(),\n    <a-v>columns</a-v><a-o>=</a-o><a-v>vectorizer</a-v>.<a-pr>get_feature_names_out</a-pr>(),\n    <a-v>index</a-v><a-o>=</a-o>[<a-s>&quot;Doc 1&quot;</a-s>, <a-s>&quot;Doc 2&quot;</a-s>, <a-s>&quot;Doc 3&quot;</a-s>]\n)\n<a-f>print</a-f>(<a-v>df</a-v>.<a-pr>round</a-pr>(<a-n>2</a-n>))</code></pre>\n<p>Look what happened - the feature vocabulary grew! Besides single words (&quot;movie&quot;, &quot;is&quot;, &quot;great&quot;) we now have <strong>pairs</strong>: &quot;movie is&quot;, &quot;is great&quot;, &quot;is terrible&quot;, &quot;great movie&quot;, &quot;this movie&quot;, &quot;movie recommend&quot;. Each pair is a separate column with its own TF-IDF.</p>\n<p>Thanks to this, the model sees that &quot;is great&quot; (Doc 1) and &quot;is terrible&quot; (Doc 2) are <strong>different things</strong> - because they're different bigrams with different TF-IDF values.</p>\n<div class=\"markdown-alert markdown-alert-note\">\n<p class=\"markdown-alert-title\">Note</p>\n<p><strong>N-grams are a trade-off.</strong> The bigger the N, the more context you capture, but the more data you need. With trigrams you have 3x more combinations than with unigrams. With 4-grams - even more. And you quickly get to the point where most n-grams appear in the corpus only once, which isn't useful.</p>\n</div>\n<h3><a href=\"#markov-chains---when-n-grams-start-predicting\" aria-hidden=\"true\" class=\"anchor\" id=\"markov-chains---when-n-grams-start-predicting\"></a>Markov chains - when n-grams start &quot;predicting&quot;</h3>\n<p>N-grams by themselves are just counts - like BoW, they don't generate text. But if we add <strong>transition probabilities</strong> to them, we suddenly get something that <strong>generates new text</strong>.</p>\n<p>The idea is simple: for each word we look at what words most often follow it. And we pick the next word probabilistically.</p>\n<p>Let's take a small corpus:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">&quot;alice has a cat alice has a dog the cat likes milk alice likes the cat&quot;\n</code></pre>\n<p>We count bigrams and transition probabilities:</p>\n<table>\n<thead>\n<tr>\n<th>Word</th>\n<th>Next word</th>\n<th>Probability</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>alice</strong></td>\n<td>has</td>\n<td>2/3 = <strong>67%</strong></td>\n</tr>\n<tr>\n<td><strong>alice</strong></td>\n<td>likes</td>\n<td>1/3 = 33%</td>\n</tr>\n<tr>\n<td><strong>has</strong></td>\n<td>a</td>\n<td>2/2 = 100%</td>\n</tr>\n<tr>\n<td><strong>a</strong></td>\n<td>cat</td>\n<td>2/3 = 67%</td>\n</tr>\n<tr>\n<td><strong>a</strong></td>\n<td>dog</td>\n<td>1/3 = 33%</td>\n</tr>\n<tr>\n<td><strong>the</strong></td>\n<td>cat</td>\n<td>1/1 = 100%</td>\n</tr>\n<tr>\n<td><strong>cat</strong></td>\n<td>likes</td>\n<td>1/1 = 100%</td>\n</tr>\n<tr>\n<td><strong>likes</strong></td>\n<td>milk</td>\n<td>1/2 = 50%</td>\n</tr>\n<tr>\n<td><strong>likes</strong></td>\n<td>the</td>\n<td>1/2 = 50%</td>\n</tr>\n</tbody>\n</table>\n<p>That is exactly a <strong>Markov chain</strong> - a model in which the probability of the next state depends <strong>only on the current state</strong> (or the last few).</p>\n<div class=\"mermaid-diagram\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"666.00635\" height=\"234.66414\" viewBox=\"0 0 666.00635 234.66414\"><rect x=\"0\" y=\"0\" width=\"666.00635\" height=\"234.66414\" fill=\"#FFFFFF\"/><defs><marker id=\"arrow-0\" viewBox=\"0 0 10 10\" refX=\"5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker><marker id=\"arrow-start-0\" viewBox=\"0 0 10 10\" refX=\"4.5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 5 L 10 10 L 10 0 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker></defs><path id=\"edge-0\" class=\"edgePath\" data-edge-id=\"edge-0\" d=\"M 111.259,103.674 L 122.407,103.674 Q 123.572,103.674 123.572,102.509 L 123.572,102.250 Q 123.572,101.085 124.737,101.085 L 192.793,101.085\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(192.79 101.08) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><rect data-edge-id=\"edge-0\" data-label-kind=\"center\" x=\"108.50\" y=\"71.07\" width=\"59.28\" height=\"28.40\" rx=\"2\" ry=\"2\" fill=\"#FFFFFF\" fill-opacity=\"0.00\" stroke=\"#94A3B8\" stroke-opacity=\"0.00\" stroke-width=\"0.8\"/><g class=\"edgeLabel\" data-edge-id=\"edge-0\" data-label-kind=\"center\"><text x=\"138.14\" y=\"88.77\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"138.14\" dy=\"0.00\">&quot;67%&quot;</tspan></text></g><path id=\"edge-1\" class=\"edgePath\" data-edge-id=\"edge-1\" d=\"M 111.259,144.174 L 118.031,144.174 Q 123.572,144.174 129.092,144.646 L 174.960,148.568 Q 180.480,149.040 186.021,149.040 L 192.793,149.040\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(192.79 149.04) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><rect data-edge-id=\"edge-1\" data-label-kind=\"center\" x=\"118.82\" y=\"113.96\" width=\"59.28\" height=\"28.40\" rx=\"2\" ry=\"2\" fill=\"#FFFFFF\" fill-opacity=\"0.00\" stroke=\"#94A3B8\" stroke-opacity=\"0.00\" stroke-width=\"0.8\"/><g class=\"edgeLabel\" data-edge-id=\"edge-1\" data-label-kind=\"center\"><text x=\"148.46\" y=\"131.66\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"148.46\" dy=\"0.00\">&quot;33%&quot;</tspan></text></g><path id=\"edge-2\" class=\"edgePath\" data-edge-id=\"edge-2\" d=\"M 278.748,76.769 L 278.748,88.979 Q 278.748,98.969 288.738,98.969 L 377.536,98.969 Q 387.526,98.969 387.526,88.979 L 387.526,76.769\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(387.53 76.77) rotate(-90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><rect data-edge-id=\"edge-2\" data-label-kind=\"center\" x=\"298.22\" y=\"68.57\" width=\"69.46\" height=\"28.40\" rx=\"2\" ry=\"2\" fill=\"#FFFFFF\" fill-opacity=\"0.00\" stroke=\"#94A3B8\" stroke-opacity=\"0.00\" stroke-width=\"0.8\"/><g class=\"edgeLabel\" data-edge-id=\"edge-2\" data-label-kind=\"center\"><text x=\"332.95\" y=\"86.27\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"332.95\" dy=\"0.00\">&quot;100%&quot;</tspan></text></g><path id=\"edge-3\" class=\"edgePath\" data-edge-id=\"edge-3\" d=\"M 456.178,100.590 L 462.950,100.590 Q 468.491,100.590 468.491,106.131 L 468.491,114.335 Q 468.491,124.335 478.491,124.335 L 554.373,124.335 Q 563.667,124.335 563.667,115.040 L 563.667,103.680\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(563.67 103.68) rotate(-90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><rect data-edge-id=\"edge-3\" data-label-kind=\"center\" x=\"492.40\" y=\"126.33\" width=\"59.28\" height=\"28.40\" rx=\"2\" ry=\"2\" fill=\"#FFFFFF\" fill-opacity=\"0.00\" stroke=\"#94A3B8\" stroke-opacity=\"0.00\" stroke-width=\"0.8\"/><g class=\"edgeLabel\" data-edge-id=\"edge-3\" data-label-kind=\"center\"><text x=\"522.05\" y=\"144.03\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"522.05\" dy=\"0.00\">&quot;67%&quot;</tspan></text></g><path id=\"edge-4\" class=\"edgePath\" data-edge-id=\"edge-4\" d=\"M 456.178,66.217 L 456.178,49.908 Q 456.178,39.908 466.178,39.908 L 543.213,39.908 Q 551.355,39.908 551.355,48.050 L 551.355,52.459 Q 551.355,58.000 556.895,58.000 L 563.667,58.000\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(563.67 58.00) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><rect data-edge-id=\"edge-4\" data-label-kind=\"center\" x=\"462.85\" y=\"9.51\" width=\"59.28\" height=\"28.40\" rx=\"2\" ry=\"2\" fill=\"#FFFFFF\" fill-opacity=\"0.00\" stroke=\"#94A3B8\" stroke-opacity=\"0.00\" stroke-width=\"0.8\"/><g class=\"edgeLabel\" data-edge-id=\"edge-4\" data-label-kind=\"center\"><text x=\"492.49\" y=\"27.21\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"492.49\" dy=\"0.00\">&quot;33%&quot;</tspan></text></g><path id=\"edge-5\" class=\"edgePath\" data-edge-id=\"edge-5\" d=\"M 296.052,170.513 L 302.824,170.513 Q 308.365,170.513 308.365,176.054 L 308.365,180.592 Q 308.365,188.839 316.612,188.839 L 377.526,188.839 Q 387.526,188.839 387.526,178.839 L 387.526,162.765\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(387.53 162.76) rotate(-90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><rect data-edge-id=\"edge-5\" data-label-kind=\"center\" x=\"316.02\" y=\"190.84\" width=\"59.28\" height=\"28.40\" rx=\"2\" ry=\"2\" fill=\"#FFFFFF\" fill-opacity=\"0.00\" stroke=\"#94A3B8\" stroke-opacity=\"0.00\" stroke-width=\"0.8\"/><g class=\"edgeLabel\" data-edge-id=\"edge-5\" data-label-kind=\"center\"><text x=\"345.66\" y=\"208.54\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"345.66\" dy=\"0.00\">&quot;50%&quot;</tspan></text></g><path id=\"edge-6\" class=\"edgePath\" data-edge-id=\"edge-6\" d=\"M 296.052,156.623 L 305.652,156.623 L 293.328,156.623 L 299.226,156.623 Q 304.052,156.623 304.052,161.449 L 304.052,186.264 Q 304.052,196.264 294.052,196.264 L 194.793,196.264 Q 184.793,196.264 184.793,186.264 L 184.793,129.680 Q 184.793,119.680 194.793,119.680 L 293.328,119.680 L 563.667,119.680\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(563.67 119.68) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><rect data-edge-id=\"edge-6\" data-label-kind=\"center\" x=\"208.63\" y=\"198.26\" width=\"59.28\" height=\"28.40\" rx=\"2\" ry=\"2\" fill=\"#FFFFFF\" fill-opacity=\"0.00\" stroke=\"#94A3B8\" stroke-opacity=\"0.00\" stroke-width=\"0.8\"/><g class=\"edgeLabel\" data-edge-id=\"edge-6\" data-label-kind=\"center\"><text x=\"238.27\" y=\"215.96\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"238.27\" dy=\"0.00\">&quot;50%&quot;</tspan></text></g><rect x=\"8.00\" y=\"94.17\" width=\"103.26\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#9999ff\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"59.63\" y=\"123.17\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#fff\"><tspan x=\"59.63\" dy=\"0.00\">alice</tspan></text><rect x=\"192.79\" y=\"51.08\" width=\"85.96\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#ffcc99\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"235.77\" y=\"80.08\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"235.77\" dy=\"0.00\">has</tspan></text><rect x=\"387.53\" y=\"51.09\" width=\"68.65\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"421.85\" y=\"80.09\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"421.85\" dy=\"0.00\">a</tspan></text><rect x=\"563.67\" y=\"94.18\" width=\"85.96\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#99ff99\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"606.65\" y=\"123.18\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"606.65\" dy=\"0.00\">cat</tspan></text><rect x=\"563.67\" y=\"8.00\" width=\"86.34\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"606.84\" y=\"37.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"606.84\" dy=\"0.00\">dog</tspan></text><rect x=\"192.79\" y=\"137.26\" width=\"103.26\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"244.42\" y=\"166.26\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"244.42\" dy=\"0.00\">likes</tspan></text><rect x=\"387.53\" y=\"137.26\" width=\"94.61\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"434.83\" y=\"166.26\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"434.83\" dy=\"0.00\">milk</tspan></text></svg></div>\n<p>&quot;alice&quot; -&gt; most often &quot;has&quot; (67%) -&gt; &quot;a&quot; -&gt; &quot;cat&quot; (67%) or &quot;dog&quot; (33%). So we generate e.g.: &quot;alice has a cat&quot;. Or &quot;alice has a dog&quot;. Or &quot;alice likes the cat&quot;. All these sentences are &quot;new&quot; - they didn't appear as wholes in the corpus - but the model stitched them together from transition probabilities.</p>\n<h3><a href=\"#how-it-works-under-the-hood---step-by-step\" aria-hidden=\"true\" class=\"anchor\" id=\"how-it-works-under-the-hood---step-by-step\"></a>How it works under the hood - step by step</h3>\n<p>Before we get to the code, let's trace the whole process on our fingers. We have the same corpus:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">&quot;alice has a cat alice has a dog the cat likes milk alice likes the cat&quot;\n</code></pre>\n<p><strong>Step 1: Build the trigram dictionary.</strong> We slide a &quot;window&quot; of 3 words and look at what follows:</p>\n<table>\n<thead>\n<tr>\n<th>Window (trigram)</th>\n<th>Next word</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>alice has <strong>a</strong></td>\n<td>cat</td>\n</tr>\n<tr>\n<td>has a <strong>cat</strong></td>\n<td>alice</td>\n</tr>\n<tr>\n<td>a cat <strong>alice</strong></td>\n<td>has</td>\n</tr>\n<tr>\n<td>cat alice <strong>has</strong></td>\n<td>a</td>\n</tr>\n<tr>\n<td>alice has <strong>a</strong></td>\n<td>dog</td>\n</tr>\n<tr>\n<td>has a <strong>dog</strong></td>\n<td>the</td>\n</tr>\n<tr>\n<td>a dog <strong>the</strong></td>\n<td>cat</td>\n</tr>\n<tr>\n<td>dog the <strong>cat</strong></td>\n<td>likes</td>\n</tr>\n<tr>\n<td>the cat <strong>likes</strong></td>\n<td>milk</td>\n</tr>\n<tr>\n<td>cat likes <strong>milk</strong></td>\n<td>alice</td>\n</tr>\n<tr>\n<td>likes milk <strong>alice</strong></td>\n<td>likes</td>\n</tr>\n<tr>\n<td>milk alice <strong>likes</strong></td>\n<td>the</td>\n</tr>\n</tbody>\n</table>\n<p>Each trigram has exactly one possible next word (because our corpus is tiny).</p>\n<p><strong>Step 2: Generate.</strong> We start from the starter trigram <code>(&quot;alice&quot;, &quot;has&quot;, &quot;a&quot;)</code>:</p>\n<div class=\"markdown-alert markdown-alert-important\">\n<p class=\"markdown-alert-title\">Important</p>\n<p><strong>This starter trigram is our &quot;prompt&quot;!</strong> Just like in ChatGPT you type text and the model continues - here we type &quot;alice has a&quot; and the generator picks the next words. The only difference is scale: ChatGPT has a context of thousands of tokens, and we have 3 words.</p>\n</div>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">Start:    alice has a\nStep 1:   alice has a [cat]     &lt;- after (alice,has,a) comes &quot;cat&quot;\nStep 2:   alice has a cat [alice]  &lt;- after (has,a,cat) comes &quot;alice&quot;\nStep 3:   alice has a cat alice [has] &lt;- after (a,cat,alice) comes &quot;has&quot;\nStep 4:   ... cat alice has [a]   &lt;- after (cat,alice,has) comes &quot;a&quot;\nStep 5:   ... alice has a [dog]   &lt;- after (alice,has,a) comes &quot;dog&quot;\nStep 6:   ... has a dog [the]     &lt;- after (has,a,dog) comes &quot;the&quot;\nStep 7:   ... a dog the [cat]     &lt;- after (a,dog,the) comes &quot;cat&quot;\nStep 8:   ... dog the cat [likes] &lt;- after (dog,the,cat) comes &quot;likes&quot;\nStep 9:   ... the cat likes [milk] &lt;- after (the,cat,likes) comes &quot;milk&quot;\nStep 10:  STOP &lt;- trigram (cat,likes,milk) -&gt; &quot;alice&quot;... it loops back\n</code></pre>\n<p>Result: <code>&quot;alice has a cat alice has a dog the cat likes milk&quot;</code> - basically our corpus, &quot;re-glued&quot; from the inside. On a larger corpus the result would be different every time.</p>\n<h3><a href=\"#a-simple-text-generator-from-n-grams\" aria-hidden=\"true\" class=\"anchor\" id=\"a-simple-text-generator-from-n-grams\"></a>A simple text generator from n-grams</h3>\n<p>Here's a complete (and genuinely short!) text generator in Python:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-python\"><a-k>import</a-k> <a-v>random</a-v>\n\n<a-v>corpus</a-v> <a-o>=</a-o> <a-s>&quot;alice has a cat alice has a dog the cat likes milk alice likes the cat&quot;</a-s>\n<a-v>tokens</a-v> <a-o>=</a-o> <a-v>corpus</a-v>.<a-pr>split</a-pr>()\n\n<a-v>trigrams</a-v> <a-o>=</a-o> {}\n<a-k>for</a-k> <a-v>i</a-v> <a-o>in</a-o> <a-f>range</a-f>(<a-f>len</a-f>(<a-v>tokens</a-v>) <a-o>-</a-o> <a-n>3</a-n>):\n    <a-v>key</a-v> <a-o>=</a-o> (<a-v>tokens</a-v>[<a-v>i</a-v>], <a-v>tokens</a-v>[<a-v>i</a-v> <a-o>+</a-o> <a-n>1</a-n>], <a-v>tokens</a-v>[<a-v>i</a-v> <a-o>+</a-o> <a-n>2</a-n>])\n    <a-v>next_word</a-v> <a-o>=</a-o> <a-v>tokens</a-v>[<a-v>i</a-v> <a-o>+</a-o> <a-n>3</a-n>]\n    <a-k>if</a-k> <a-v>key</a-v> <a-o>not in</a-o> <a-v>trigrams</a-v>:\n        <a-v>trigrams</a-v>[<a-v>key</a-v>] <a-o>=</a-o> []\n    <a-v>trigrams</a-v>[<a-v>key</a-v>].<a-pr>append</a-pr>(<a-v>next_word</a-v>)\n\n<a-v>current</a-v> <a-o>=</a-o> (<a-s>&quot;alice&quot;</a-s>, <a-s>&quot;has&quot;</a-s>, <a-s>&quot;a&quot;</a-s>)  <a-c># &lt;- this is our &quot;prompt&quot;!</a-c>\n<a-v>output</a-v> <a-o>=</a-o> <a-f>list</a-f>(<a-v>current</a-v>)\n\n<a-k>for</a-k> <a-v>_</a-v> <a-o>in</a-o> <a-f>range</a-f>(<a-n>10</a-n>):\n    <a-k>if</a-k> <a-f>tuple</a-f>(<a-v>output</a-v>[<a-o>-</a-o><a-n>3</a-n>:]) <a-o>not in</a-o> <a-v>trigrams</a-v>:\n        <a-k>break</a-k>\n    <a-v>possibilities</a-v> <a-o>=</a-o> <a-v>trigrams</a-v>[<a-f>tuple</a-f>(<a-v>output</a-v>[<a-o>-</a-o><a-n>3</a-n>:])]\n    <a-v>output</a-v>.<a-pr>append</a-pr>(<a-v>random</a-v>.<a-pr>choice</a-pr>(<a-v>possibilities</a-v>))\n\n<a-f>print</a-f>(<a-s>&quot; &quot;</a-s>.<a-pr>join</a-pr>(<a-v>output</a-v>))</code></pre>\n<p>Possible output: <code>&quot;alice has a cat alice has a dog the cat likes milk alice likes the cat&quot;</code></p>\n<div class=\"markdown-alert markdown-alert-note\">\n<p class=\"markdown-alert-title\">Note</p>\n<p><strong>Why does it come out the same every time?</strong> Because our corpus is so small that most trigrams have <strong>only one</strong> possible next word. <code>random.choice</code> has nothing to choose from! On a larger corpus (e.g. all of English Wikipedia) the same code would generate <strong>different text every time</strong> - because almost every trigram would have several possible continuations with different probabilities. And that's exactly the moment when generation becomes interesting.</p>\n</div>\n<p>Nothing spectacular, right? But that's because our corpus is microscopic. On a real corpus (e.g. all of Wikipedia) a trigram generator can produce sentences that sound sensible, even though they never appeared before.</p>\n<div class=\"markdown-alert markdown-alert-important\">\n<p class=\"markdown-alert-title\">Important</p>\n<p><strong>Markov chains are the first &quot;language models&quot;!</strong> Predicting the next word based on context - that is EXACTLY what ChatGPT does. Of course GPT uses a much more advanced method (Transformer + attention over thousands of context tokens), but <strong>the fundamental idea is the same</strong>: the probability of the next token. An LLM is a descendant of Markov chains. On steroids ;-)</p>\n</div>\n<hr />\n<h2><a href=\"#bayesian-techniques---or-classifying-text\" aria-hidden=\"true\" class=\"anchor\" id=\"bayesian-techniques---or-classifying-text\"></a>Bayesian techniques - or, classifying text</h2>\n<h3><a href=\"#what-if-we-dont-want-to-generate-but-classify\" aria-hidden=\"true\" class=\"anchor\" id=\"what-if-we-dont-want-to-generate-but-classify\"></a>What if we don't want to generate, but CLASSIFY?</h3>\n<p>Markov chains are great for generating text. But in practice we often want something else: <strong>assign a text to a category</strong>.</p>\n<ul>\n<li>Is this email <strong>spam</strong> or <strong>not-spam</strong>?</li>\n<li>Is this review <strong>positive</strong> or <strong>negative</strong>?</li>\n<li>Is this article about <strong>sports</strong>, <strong>politics</strong>, or <strong>technology</strong>?</li>\n</ul>\n<p>And here comes <strong>Naive Bayes</strong> - one of the simplest yet most useful text classification algorithms.</p>\n<h3><a href=\"#bayes-theorem---the-intuition\" aria-hidden=\"true\" class=\"anchor\" id=\"bayes-theorem---the-intuition\"></a>Bayes' theorem - the intuition</h3>\n<p>Without formulas, just an example:</p>\n<p>Imagine a colleague comes up to you and says: &quot;I'm coughing.&quot; What's the probability he has a cold?</p>\n<p>It depends! If it's November and everyone in the office is sick - high. If it's July and he coughs once - low.</p>\n<p>Bayes' theorem is a formal way of thinking about such situations: <strong>we update our knowledge based on new evidence.</strong></p>\n<p>And now let's translate that to text. The question is:</p>\n<blockquote>\n<p>&quot;If a document contains the word 'viagra', what's the probability it's spam?&quot;</p>\n</blockquote>\n<div class=\"mermaid-diagram\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1280.95\" height=\"281\" viewBox=\"0 0 1280.95 281\"><rect x=\"0\" y=\"0\" width=\"1280.95\" height=\"281\" fill=\"#FFFFFF\"/><defs><marker id=\"arrow-0\" viewBox=\"0 0 10 10\" refX=\"5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker><marker id=\"arrow-start-0\" viewBox=\"0 0 10 10\" refX=\"4.5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 5 L 10 10 L 10 0 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker></defs><path id=\"edge-0\" class=\"edgePath\" data-edge-id=\"edge-0\" d=\"M 266.991,131.248 L 316.991,131.248\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(316.99 131.25) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-1\" class=\"edgePath\" data-edge-id=\"edge-1\" d=\"M 541.376,102.579 L 552.027,102.579 Q 558.876,102.579 565.626,101.419 L 567.126,101.161 Q 573.876,100.000 580.725,100.000 L 591.376,100.000\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(591.38 100.00) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-2\" class=\"edgePath\" data-edge-id=\"edge-2\" d=\"M 541.376,166.249 L 551.008,166.249 Q 558.876,166.249 565.626,170.291 L 567.126,171.189 Q 573.876,175.231 581.743,175.231 L 591.376,175.231\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(591.38 175.23) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-3\" class=\"edgePath\" data-edge-id=\"edge-3\" d=\"M 867.671,100.000 L 877.296,100.000 Q 885.171,100.000 890.103,106.139 L 900.842,119.508 Q 905.774,125.647 913.649,125.647 L 923.274,125.647\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(923.27 125.65) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-4\" class=\"edgePath\" data-edge-id=\"edge-4\" d=\"M 833.063,174.776 L 842.688,174.776 Q 850.563,174.776 856.483,169.582 L 894.251,136.445 Q 900.171,131.251 908.046,131.251 L 917.671,131.251\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(917.67 131.25) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-5\" class=\"edgePath\" data-edge-id=\"edge-5\" d=\"M 1065.082,131.251 L 1144.387,131.251\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(1144.39 131.25) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><rect data-edge-id=\"edge-5\" data-label-kind=\"center\" x=\"1074.18\" y=\"100.85\" width=\"61.12\" height=\"28.40\" rx=\"2\" ry=\"2\" fill=\"#FFFFFF\" fill-opacity=\"0.00\" stroke=\"#94A3B8\" stroke-opacity=\"0.00\" stroke-width=\"0.8\"/><g class=\"edgeLabel\" data-edge-id=\"edge-5\" data-label-kind=\"center\"><text x=\"1104.73\" y=\"118.55\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"1104.73\" dy=\"0.00\">Spam!</tspan></text></g><polygon points=\"991.38,57.55 1065.08,131.25 991.38,204.96 917.67,131.25\" fill=\"#9999ff\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"991.38\" y=\"113.75\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#fff\"><tspan x=\"991.38\" dy=\"0.00\">Which</tspan><tspan x=\"991.38\" dy=\"21.00\">probability</tspan><tspan x=\"991.38\" dy=\"21.00\">is higher?</tspan></text><rect x=\"8.00\" y=\"95.25\" width=\"258.99\" height=\"72.00\" rx=\"3\" ry=\"3\" fill=\"#ffcc99\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"137.50\" y=\"124.25\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"137.50\" dy=\"0.00\">📧 New email:</tspan><tspan x=\"137.50\" dy=\"21.00\">&apos;Buy cheap viagra now!&apos;</tspan></text><rect x=\"591.38\" y=\"151.00\" width=\"241.69\" height=\"114.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"712.22\" y=\"180.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"712.22\" dy=\"0.00\">📊 P(not-spam) x</tspan><tspan x=\"712.22\" dy=\"21.00\">P(&apos;buy&apos;|not-spam) x</tspan><tspan x=\"712.22\" dy=\"21.00\">P(&apos;cheap&apos;|not-spam) x</tspan><tspan x=\"712.22\" dy=\"21.00\">P(&apos;viagra&apos;|not-spam)</tspan></text><rect x=\"316.99\" y=\"95.25\" width=\"224.38\" height=\"72.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"429.18\" y=\"124.25\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"429.18\" dy=\"0.00\">P(spam | &apos;buy cheap</tspan><tspan x=\"429.18\" dy=\"21.00\">viagra now&apos;) = ?</tspan></text><rect x=\"1144.39\" y=\"105.75\" width=\"120.56\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#ff9999\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"1204.67\" y=\"134.75\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"1204.67\" dy=\"0.00\">🗑️ SPAM</tspan></text><rect x=\"591.38\" y=\"8.00\" width=\"276.29\" height=\"93.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"729.52\" y=\"37.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"729.52\" dy=\"0.00\">📊 P(spam) x P(&apos;buy&apos;|spam)</tspan><tspan x=\"729.52\" dy=\"21.00\">x P(&apos;cheap&apos;|spam) x</tspan><tspan x=\"729.52\" dy=\"21.00\">P(&apos;viagra&apos;|spam)</tspan></text></svg></div>\n<h3><a href=\"#naive-bayes-in-action\" aria-hidden=\"true\" class=\"anchor\" id=\"naive-bayes-in-action\"></a>Naive Bayes in action</h3>\n<p>We have a small set of emails:</p>\n<table>\n<thead>\n<tr>\n<th>Email</th>\n<th>Content</th>\n<th>Label</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>1</td>\n<td>&quot;Buy cheap viagra now&quot;</td>\n<td>Spam</td>\n</tr>\n<tr>\n<td>2</td>\n<td>&quot;Free viagra offer&quot;</td>\n<td>Spam</td>\n</tr>\n<tr>\n<td>3</td>\n<td>&quot;Meeting tomorrow at 10&quot;</td>\n<td>Not-spam</td>\n</tr>\n<tr>\n<td>4</td>\n<td>&quot;Send me the report tomorrow&quot;</td>\n<td>Not-spam</td>\n</tr>\n</tbody>\n</table>\n<p>A new email arrives: <strong>&quot;Viagra meeting tomorrow&quot;</strong>. Spam or not?</p>\n<p>Naive Bayes computes:</p>\n<ol>\n<li>P(Spam) = 2/4 = 0.5, P(Not-spam) = 2/4 = 0.5</li>\n<li>P(&quot;viagra&quot; | Spam) = 2/2 = 1.0 (both spams have &quot;viagra&quot;)</li>\n<li>P(&quot;viagra&quot; | Not-spam) = 0/2 = 0.0 (no not-spam has &quot;viagra&quot;)</li>\n<li>P(&quot;tomorrow&quot; | Spam) = 0/2 = 0.0</li>\n<li>P(&quot;tomorrow&quot; | Not-spam) = 2/2 = 1.0</li>\n</ol>\n<p>P(Spam | &quot;viagra meeting tomorrow&quot;) ∝ 0.5 x 1.0 x 0.0 x ... = <strong>0</strong>!</p>\n<p>P(Not-spam | &quot;viagra meeting tomorrow&quot;) ∝ 0.5 x 0.0 x 1.0 x ... = <strong>0</strong>!</p>\n<p>Oops. The presence of &quot;tomorrow&quot; (a not-spam word) zeroed out spam, and the presence of &quot;viagra&quot; (a spam word) zeroed out not-spam. This particular email is tricky ;-)</p>\n<p>In practice, so-called <strong>Laplace smoothing</strong> is used (we add 1 to each count) to avoid zeroing:</p>\n<ul>\n<li>P(&quot;viagra&quot; | Spam) = (2 + 1) / (7 + 14) = 0.214 (we add 1, divide by all words in spam + unique words)</li>\n<li>P(&quot;viagra&quot; | Not-spam) = (0 + 1) / (7 + 14) = 0.048</li>\n</ul>\n<p>Now: P(Spam | email) ∝ 0.5 x 0.214 x 0.048 x 0.048 ≈ <strong>0.000246</strong>\nP(Not-spam | email) ∝ 0.5 x 0.048 x 0.143 x 0.143 ≈ <strong>0.000490</strong></p>\n<p>Not-spam wins! Because &quot;tomorrow&quot; and &quot;meeting&quot; strongly indicate a normal email.</p>\n<h3><a href=\"#why-naive\" aria-hidden=\"true\" class=\"anchor\" id=\"why-naive\"></a>Why &quot;Naive&quot;?</h3>\n<p>Because we assume words are <strong>independent</strong>. That is: the probability of &quot;viagra&quot; appearing doesn't depend on whether &quot;cheap&quot; also appears. Of course that's not true! &quot;Viagra&quot; and &quot;cheap&quot; appear together more often than by chance. But the model still works surprisingly well...</p>\n<p>It's a bit like assuming the weather in London doesn't depend on the weather in Paris. Of course it does a bit! But if you want to quickly estimate whether you need an umbrella, the independence assumption gives you a reasonably good answer ;-)</p>\n<h3><a href=\"#a-spam-classifier-in-python\" aria-hidden=\"true\" class=\"anchor\" id=\"a-spam-classifier-in-python\"></a>A spam classifier in Python</h3>\n<p>Now that we understand the math, let's do it in a few lines of code. The <code>scikit-learn</code> library has a ready Naive Bayes classifier:</p>\n<ul>\n<li><strong><code>CountVectorizer</code></strong> - turns text into a word-count vector (our BoW from the previous section)</li>\n<li><strong><code>MultinomialNB</code></strong> - that's Naive Bayes, the &quot;multinomial&quot; variant (because it counts word probabilities), with <code>alpha=1.0</code> i.e. Laplace smoothing</li>\n</ul>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-python\"><a-k>from</a-k> <a-v>sklearn</a-v>.<a-v>naive_bayes</a-v> <a-k>import</a-k> <a-cr>MultinomialNB</a-cr>\n<a-k>from</a-k> <a-v>sklearn</a-v>.<a-v>feature_extraction</a-v>.<a-v>text</a-v> <a-k>import</a-k> <a-cr>CountVectorizer</a-cr>\n\n<a-v>emails</a-v> <a-o>=</a-o> [\n    <a-s>&quot;Buy cheap viagra now&quot;</a-s>,\n    <a-s>&quot;Free viagra offer&quot;</a-s>,\n    <a-s>&quot;Meeting tomorrow at 10&quot;</a-s>,\n    <a-s>&quot;Send me the report tomorrow&quot;</a-s>,\n    <a-s>&quot;Cheap pills online buy now&quot;</a-s>,\n    <a-s>&quot;Report from yesterday send&quot;</a-s>,\n]\n<a-v>labels</a-v> <a-o>=</a-o> [<a-n>1</a-n>, <a-n>1</a-n>, <a-n>0</a-n>, <a-n>0</a-n>, <a-n>1</a-n>, <a-n>0</a-n>]  <a-c># 1=spam, 0=not-spam</a-c>\n\n<a-v>vectorizer</a-v> <a-o>=</a-o> <a-f>CountVectorizer</a-f>()\n<a-co>X</a-co> <a-o>=</a-o> <a-v>vectorizer</a-v>.<a-pr>fit_transform</a-pr>(<a-v>emails</a-v>)\n\n<a-v>classifier</a-v> <a-o>=</a-o> <a-f>MultinomialNB</a-f>(<a-v>alpha</a-v><a-o>=</a-o><a-n>1.0</a-n>)\n<a-v>classifier</a-v>.<a-pr>fit</a-pr>(<a-co>X</a-co>, <a-v>labels</a-v>)\n\n<a-v>new_email</a-v> <a-o>=</a-o> <a-v>vectorizer</a-v>.<a-pr>transform</a-pr>([<a-s>&quot;Viagra meeting tomorrow&quot;</a-s>])\n<a-v>prediction</a-v> <a-o>=</a-o> <a-v>classifier</a-v>.<a-pr>predict</a-pr>(<a-v>new_email</a-v>)\n<a-f>print</a-f>(<a-s>&quot;Spam!&quot;</a-s> <a-k>if</a-k> <a-v>prediction</a-v>[<a-n>0</a-n>] <a-o>==</a-o> <a-n>1</a-n> <a-k>else</a-k> <a-s>&quot;Not spam.&quot;</a-s>)</code></pre>\n<div class=\"markdown-alert markdown-alert-tip\">\n<p class=\"markdown-alert-title\">Tip</p>\n<p><strong>An experiment for you:</strong> Create 5 positive sentences about a movie (&quot;This movie was amazing!&quot;, &quot;Great action!&quot;, ...) and 5 negative ones (&quot;Boring as watching paint dry&quot;, &quot;Waste of time&quot;, ...). Write down the words that appear ONLY in positive and ONLY in negative. That's the intuition of Naive Bayes - every word &quot;votes&quot; for one of the categories.</p>\n</div>\n<div class=\"markdown-alert markdown-alert-warning\">\n<p class=\"markdown-alert-title\">Warning</p>\n<p><strong>Limitation:</strong> Naive Bayes sees words but not context. &quot;The movie is AMAZING... just kidding, awful&quot; - the model will see &quot;amazing&quot; and say &quot;positive&quot;. It doesn't get irony or complex constructions. We need something that understands <strong>relationships between words</strong>. And here we enter the world of vectors...</p>\n</div>\n<hr />\n<h2><a href=\"#word2vec---words-become-geometry\" aria-hidden=\"true\" class=\"anchor\" id=\"word2vec---words-become-geometry\"></a>Word2Vec - words become geometry</h2>\n<h3><a href=\"#the-eureka-moment\" aria-hidden=\"true\" class=\"anchor\" id=\"the-eureka-moment\"></a>The &quot;eureka&quot; moment</h3>\n<p>All the methods we've met so far have one problem in common: <strong>they treat words as discrete, independent entities</strong>. In BoW &quot;cat&quot; is at position 47 in the vector, &quot;dog&quot; at position 138. There's no relationship between them.</p>\n<p>But WE know that &quot;cat&quot; and &quot;dog&quot; are similar - both are pets. &quot;Cat&quot; and &quot;car&quot; - completely different. How do we make a computer &quot;know&quot; that too?</p>\n<p>The answer from 2013, from Tomáš Mikolov's team at Google: <strong>let's turn words into points in a high-dimensional space</strong>. Semantically similar words will be close to each other. Different words - far apart.</p>\n<p>That's <strong>Word2Vec</strong>. And it's a breakthrough.</p>\n<h3><a href=\"#one-hot-encoding-the-starting-point\" aria-hidden=\"true\" class=\"anchor\" id=\"one-hot-encoding-the-starting-point\"></a>One-hot encoding: the starting point</h3>\n<p>Before Word2Vec, the standard was so-called <strong>one-hot encoding</strong> - each word is a vector with a 1 at its position and 0 everywhere else:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">&quot;cat&quot;  -&gt; [0, 0, 0, 1, 0, 0, ...]  (1 at position 3)\n&quot;dog&quot;  -&gt; [0, 0, 1, 0, 0, 0, ...]  (1 at position 2)\n&quot;car&quot;  -&gt; [1, 0, 0, 0, 0, 0, ...]  (1 at position 0)\n</code></pre>\n<p>The problem? <strong>Every word is the same distance from every other word.</strong> The distance between &quot;cat&quot; and &quot;dog&quot; is the same as between &quot;cat&quot; and &quot;car&quot;. Zero information about meaning.</p>\n<div class=\"mermaid-diagram\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"437.8357\" height=\"309.5\" viewBox=\"0 0 437.8357 309.5\"><rect x=\"0\" y=\"0\" width=\"437.8357\" height=\"309.5\" fill=\"#FFFFFF\"/><defs><marker id=\"arrow-0\" viewBox=\"0 0 10 10\" refX=\"5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker><marker id=\"arrow-start-0\" viewBox=\"0 0 10 10\" refX=\"4.5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 5 L 10 10 L 10 0 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker></defs><rect x=\"8.00\" y=\"8.00\" width=\"413.84\" height=\"285.50\" rx=\"10\" ry=\"10\" fill=\"#F1F5F9\" stroke=\"#CBD5E1\" stroke-width=\"1\" /><text x=\"214.92\" y=\"37.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"214.92\" dy=\"0.00\">One-hot: everything is</tspan><tspan x=\"214.92\" dy=\"21.00\">equally far</tspan></text><path id=\"edge-0\" class=\"edgePath\" data-edge-id=\"edge-0\" d=\"M 181.866,148.885 L 191.491,148.885 Q 199.366,148.885 207.126,147.541 L 222.710,144.843 Q 230.470,143.500 238.345,143.500 L 247.970,143.500\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><rect data-edge-id=\"edge-0\" data-label-kind=\"center\" x=\"199.35\" y=\"113.51\" width=\"38.66\" height=\"28.40\" rx=\"2\" ry=\"2\" fill=\"#FFFFFF\" fill-opacity=\"0.00\" stroke=\"#94A3B8\" stroke-opacity=\"0.00\" stroke-width=\"0.8\"/><g class=\"edgeLabel\" data-edge-id=\"edge-0\" data-label-kind=\"center\"><text x=\"218.68\" y=\"131.21\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"218.68\" dy=\"0.00\">&quot;=&quot;</tspan></text></g><path id=\"edge-1\" class=\"edgePath\" data-edge-id=\"edge-1\" d=\"M 181.866,204.500 L 191.491,204.500 Q 199.366,204.500 207.126,205.843 L 222.710,208.541 Q 230.470,209.885 238.345,209.885 L 247.970,209.885\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><rect data-edge-id=\"edge-1\" data-label-kind=\"center\" x=\"199.35\" y=\"211.47\" width=\"38.66\" height=\"28.40\" rx=\"2\" ry=\"2\" fill=\"#FFFFFF\" fill-opacity=\"0.00\" stroke=\"#94A3B8\" stroke-opacity=\"0.00\" stroke-width=\"0.8\"/><g class=\"edgeLabel\" data-edge-id=\"edge-1\" data-label-kind=\"center\"><text x=\"218.68\" y=\"229.17\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"218.68\" dy=\"0.00\">&quot;=&quot;</tspan></text></g><rect x=\"44.00\" y=\"133.50\" width=\"137.87\" height=\"72.00\" rx=\"3\" ry=\"3\" fill=\"#9999ff\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"112.93\" y=\"162.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#fff\"><tspan x=\"112.93\" dy=\"0.00\">cat</tspan><tspan x=\"112.93\" dy=\"21.00\">[0,0,1,0]</tspan></text><rect x=\"247.97\" y=\"72.50\" width=\"137.87\" height=\"72.00\" rx=\"3\" ry=\"3\" fill=\"#ffcc99\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"316.90\" y=\"101.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"316.90\" dy=\"0.00\">dog</tspan><tspan x=\"316.90\" dy=\"21.00\">[0,1,0,0]</tspan></text><rect x=\"247.97\" y=\"194.50\" width=\"137.87\" height=\"72.00\" rx=\"3\" ry=\"3\" fill=\"#ff9999\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"316.90\" y=\"223.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"316.90\" dy=\"0.00\">car</tspan><tspan x=\"316.90\" dy=\"21.00\">[1,0,0,0]</tspan></text></svg></div>\n<h3><a href=\"#word2vec-show-me-your-neighbors-and-ill-tell-you-who-you-are\" aria-hidden=\"true\" class=\"anchor\" id=\"word2vec-show-me-your-neighbors-and-ill-tell-you-who-you-are\"></a>Word2Vec: &quot;Show me your neighbors and I'll tell you who you are&quot;</h3>\n<p>Word2Vec is based on a brilliant linguistic intuition: <strong>words that appear in similar contexts have similar meanings.</strong></p>\n<p>If you see: &quot;___ runs in the park and chases pigeons&quot;, what fits there? Dog? Cat? Rather not &quot;car&quot; or &quot;democracy&quot;. Context defines the word.</p>\n<div class=\"mermaid-diagram\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1425.8763\" height=\"502\" viewBox=\"0 0 1425.8763 502\"><rect x=\"0\" y=\"0\" width=\"1425.8763\" height=\"502\" fill=\"#FFFFFF\"/><defs><marker id=\"arrow-0\" viewBox=\"0 0 10 10\" refX=\"5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker><marker id=\"arrow-start-0\" viewBox=\"0 0 10 10\" refX=\"4.5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 5 L 10 10 L 10 0 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker></defs><rect x=\"8.13\" y=\"236.50\" width=\"1401.75\" height=\"249.50\" rx=\"10\" ry=\"10\" fill=\"#F1F5F9\" stroke=\"#CBD5E1\" stroke-width=\"1\" /><text x=\"709.00\" y=\"264.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"709.00\" dy=\"0.00\">Context of the word &apos;cat&apos;</tspan></text><rect x=\"8.00\" y=\"8.00\" width=\"1092.88\" height=\"148.50\" rx=\"10\" ry=\"10\" fill=\"#F1F5F9\" stroke=\"#CBD5E1\" stroke-width=\"1\" /><text x=\"554.44\" y=\"35.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"554.44\" dy=\"0.00\">Context of the word &apos;dog&apos;</tspan></text><path id=\"edge-0\" class=\"edgePath\" data-edge-id=\"edge-0\" d=\"M 242.081,90.000 L 251.706,90.000 Q 259.581,90.000 259.581,97.875 L 259.581,188.750 Q 259.581,198.750 249.581,198.750 L 27.629,198.750 Q 17.629,198.750 17.629,208.750 L 17.629,400.625 Q 17.629,408.500 25.504,408.500 L 35.129,408.500\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"   stroke-dasharray=\"4 4\" stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(35.13 408.50) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><rect data-edge-id=\"edge-0\" data-label-kind=\"center\" x=\"50.06\" y=\"168.35\" width=\"177.09\" height=\"28.40\" rx=\"2\" ry=\"2\" fill=\"#FFFFFF\" fill-opacity=\"0.00\" stroke=\"#94A3B8\" stroke-opacity=\"0.00\" stroke-width=\"0.8\"/><g class=\"edgeLabel\" data-edge-id=\"edge-0\" data-label-kind=\"center\"><text x=\"138.60\" y=\"186.05\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"138.60\" dy=\"0.00\">&quot;shared context!&quot;</tspan></text></g><rect x=\"1123.89\" y=\"277.00\" width=\"258.99\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"1253.38\" y=\"306.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"1253.38\" dy=\"0.00\">___ sleeps on the couch</tspan></text><rect x=\"566.46\" y=\"277.00\" width=\"224.38\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"678.66\" y=\"306.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"678.66\" dy=\"0.00\">My ___ catches mice</tspan></text><rect x=\"35.13\" y=\"399.00\" width=\"207.08\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#ffff99\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"138.67\" y=\"428.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"138.67\" dy=\"0.00\">I feed ___ kibble</tspan></text><rect x=\"840.85\" y=\"48.50\" width=\"233.04\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"957.37\" y=\"77.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"957.37\" dy=\"0.00\">___ runs in the park</tspan></text><rect x=\"292.08\" y=\"48.50\" width=\"224.38\" height=\"72.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"404.27\" y=\"77.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"404.27\" dy=\"0.00\">My ___ barks at the</tspan><tspan x=\"404.27\" dy=\"21.00\">mailman</tspan></text><rect x=\"35.00\" y=\"48.50\" width=\"207.08\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#ffff99\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"138.54\" y=\"77.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"138.54\" dy=\"0.00\">I feed ___ kibble</tspan></text></svg></div>\n<p>&quot;I feed ___ kibble&quot; - both dog and cat fit. That means the context of these words is partly shared. And that's exactly what Word2Vec exploits.</p>\n<p>Remember <strong>Saussure</strong> from the <a href=\"semiotics-and-llm.html\">second post</a>? Meaning is relational. &quot;Cat&quot; means what it means because it is NOT &quot;dog&quot;, it is NOT &quot;house&quot;. Word2Vec is the mathematical implementation of that idea!</p>\n<h3><a href=\"#cbow-and-skip-gram---two-sides-of-the-same-coin\" aria-hidden=\"true\" class=\"anchor\" id=\"cbow-and-skip-gram---two-sides-of-the-same-coin\"></a>CBOW and Skip-gram - two sides of the same coin</h3>\n<p>Word2Vec has two variants. Let's see both:</p>\n<p><strong>CBOW (Continuous Bag of Words):</strong> from context we guess the middle word.</p>\n<p><code>&quot;The cat ___ on the mat&quot; -&gt; sits? lies? sleeps?</code></p>\n<p><strong>Skip-gram:</strong> from the middle word we guess the context.</p>\n<p><code>&quot;sits&quot; -&gt; The? cat? on? the? mat?</code></p>\n<div class=\"mermaid-diagram\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1070.7925\" height=\"367.79407\" viewBox=\"0 0 1070.7925 367.79407\"><rect x=\"0\" y=\"0\" width=\"1070.7925\" height=\"367.79407\" fill=\"#FFFFFF\"/><defs><marker id=\"arrow-0\" viewBox=\"0 0 10 10\" refX=\"5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker><marker id=\"arrow-start-0\" viewBox=\"0 0 10 10\" refX=\"4.5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 5 L 10 10 L 10 0 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker></defs><rect x=\"8.00\" y=\"32.00\" width=\"613.68\" height=\"319.79\" rx=\"10\" ry=\"10\" fill=\"#F1F5F9\" stroke=\"#CBD5E1\" stroke-width=\"1\" /><text x=\"314.84\" y=\"59.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"314.84\" dy=\"0.00\">CBOW: context -&gt; word</tspan></text><rect x=\"654.33\" y=\"8.00\" width=\"400.46\" height=\"343.79\" rx=\"10\" ry=\"10\" fill=\"#F1F5F9\" stroke=\"#CBD5E1\" stroke-width=\"1\" /><text x=\"854.56\" y=\"37.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"854.56\" dy=\"0.00\">Skip-gram: word -&gt;</tspan><tspan x=\"854.56\" dy=\"21.00\">context</tspan></text><path id=\"edge-0\" class=\"edgePath\" data-edge-id=\"edge-0\" d=\"M 386.290,123.500 L 386.290,129.785 Q 386.290,134.926 381.368,136.415 L 358.821,143.232 Q 353.899,144.721 353.899,149.862 L 353.899,156.147\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(353.90 156.15) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-1\" class=\"edgePath\" data-edge-id=\"edge-1\" d=\"M 231.673,123.500 L 231.673,130.493 Q 231.673,134.926 232.153,139.334 L 232.260,140.313 Q 232.740,144.721 232.740,149.154 L 232.740,156.147\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(232.74 156.15) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-2\" class=\"edgePath\" data-edge-id=\"edge-2\" d=\"M 114.438,123.500 L 114.438,132.478 Q 114.438,139.824 121.784,139.824 L 180.112,139.824 Q 187.458,139.824 187.458,147.169 L 187.458,156.147\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(187.46 156.15) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-3\" class=\"edgePath\" data-edge-id=\"edge-3\" d=\"M 309.666,123.500 L 309.666,130.490 Q 309.666,134.926 309.155,139.334 L 309.042,140.313 Q 308.532,144.721 308.532,149.157 L 308.532,156.147\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(308.53 156.15) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-4\" class=\"edgePath\" data-edge-id=\"edge-4\" d=\"M 504.975,123.500 L 504.975,129.785 Q 504.975,134.926 499.834,134.926 L 398.339,134.926 Q 388.790,134.926 388.790,144.476 L 388.790,156.147\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(388.79 156.15) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-5\" class=\"edgePath\" data-edge-id=\"edge-5\" d=\"M 277.667,228.147 L 277.667,234.486 Q 277.667,239.573 275.125,243.981 L 274.560,244.960 Q 272.018,249.368 272.018,254.455 L 272.018,260.794\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(272.02 260.79) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-6\" class=\"edgePath\" data-edge-id=\"edge-6\" d=\"M 796.030,123.500 L 796.030,129.785 Q 796.030,134.926 799.284,138.908 L 800.781,140.739 Q 804.035,144.721 804.035,149.862 L 804.035,156.147\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(804.03 156.15) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-7\" class=\"edgePath\" data-edge-id=\"edge-7\" d=\"M 864.736,207.147 L 864.736,260.794\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(864.74 260.79) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-8\" class=\"edgePath\" data-edge-id=\"edge-8\" d=\"M 893.024,207.147 L 893.024,213.432 Q 893.024,218.573 897.026,221.803 L 927.184,246.139 Q 931.185,249.368 931.185,254.510 L 931.185,260.794\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(931.19 260.79) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-9\" class=\"edgePath\" data-edge-id=\"edge-9\" d=\"M 816.061,207.147 L 816.061,213.432 Q 816.061,218.573 811.766,221.401 L 773.579,246.540 Q 769.284,249.368 769.284,254.510 L 769.284,260.794\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(769.28 260.79) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><rect x=\"385.29\" y=\"72.50\" width=\"86.04\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"428.31\" y=\"101.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"428.31\" dy=\"0.00\">The</tspan></text><rect x=\"148.09\" y=\"72.50\" width=\"85.96\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"191.06\" y=\"101.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"191.06\" dy=\"0.00\">cat</tspan></text><rect x=\"38.00\" y=\"72.50\" width=\"77.44\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"76.72\" y=\"101.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"76.72\" dy=\"0.00\">on</tspan></text><rect x=\"266.69\" y=\"72.50\" width=\"85.96\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"309.67\" y=\"101.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"309.67\" dy=\"0.00\">the</tspan></text><rect x=\"503.98\" y=\"72.50\" width=\"87.71\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"547.83\" y=\"101.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"547.83\" dy=\"0.00\">mat</tspan></text><rect x=\"174.06\" y=\"156.15\" width=\"215.73\" height=\"72.00\" rx=\"3\" ry=\"3\" fill=\"#9999ff\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"281.92\" y=\"185.15\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#fff\"><tspan x=\"281.92\" dy=\"0.00\">🧠 Hidden layer</tspan><tspan x=\"281.92\" dy=\"21.00\">(averaged vectors)</tspan></text><rect x=\"754.09\" y=\"156.15\" width=\"181.13\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#ff9999\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"844.66\" y=\"185.15\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"844.66\" dy=\"0.00\">🧠 Hidden layer</tspan></text><rect x=\"213.28\" y=\"260.79\" width=\"111.91\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"269.24\" y=\"289.79\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"269.24\" dy=\"0.00\">sits ✅</tspan></text><rect x=\"802.93\" y=\"260.79\" width=\"94.61\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"850.23\" y=\"289.79\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"850.23\" dy=\"0.00\">The?</tspan></text><rect x=\"930.19\" y=\"260.79\" width=\"94.61\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"977.49\" y=\"289.79\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"977.49\" dy=\"0.00\">cat?</tspan></text><rect x=\"684.33\" y=\"260.79\" width=\"85.96\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"727.31\" y=\"289.79\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"727.31\" dy=\"0.00\">on?</tspan></text><rect x=\"704.39\" y=\"72.50\" width=\"94.61\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"751.69\" y=\"101.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"751.69\" dy=\"0.00\">sits</tspan></text></svg></div>\n<p>How does it work inside? A neural network with one hidden layer:</p>\n<ol>\n<li>Each word starts as a one-hot vector (e.g. [0, 0, 1, 0, ...])</li>\n<li>We multiply by a weight matrix <strong>W</strong> (size: vocabulary x embedding dimension, e.g. 50 000 x 300)</li>\n<li>The result is a vector of size <strong>E</strong> (e.g. 300 numbers) - that's our embedding!</li>\n<li>In CBOW: we average the context vectors and predict the middle. In Skip-gram: we take the middle and predict the neighbors.</li>\n<li>The network trains on millions of (word, context) pairs, updating the matrix <strong>W</strong></li>\n<li>After training, <strong>the row of the matrix W corresponding to a given word is its embedding</strong></li>\n</ol>\n<p>Sounds complicated? Let's do it in code - from scratch, without any ML libraries:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-python\"><a-k>import</a-k> <a-v>numpy</a-v> <a-k>as</a-k> <a-v>np</a-v>\n\n<a-v>np</a-v>.<a-pr>random</a-pr>.<a-pr>seed</a-pr>(<a-n>42</a-n>)\n\n<a-c># --- 1. Vocabulary and one-hot encoding ---</a-c>\n<a-v>corpus</a-v> <a-o>=</a-o> <a-s>&quot;cat sits on the mat and sleeps dog sits on the rug and sleeps cat runs around the room dog runs in the park&quot;</a-s>.<a-pr>split</a-pr>()\n<a-v>vocab</a-v> <a-o>=</a-o> <a-f>list</a-f>(<a-f>set</a-f>(<a-v>corpus</a-v>))\n<a-v>word2idx</a-v> <a-o>=</a-o> {<a-v>w</a-v>: <a-v>i</a-v> <a-k>for</a-k> <a-v>i</a-v>, <a-v>w</a-v> <a-o>in</a-o> <a-f>enumerate</a-f>(<a-v>vocab</a-v>)}\n<a-v>vocab_size</a-v> <a-o>=</a-o> <a-f>len</a-f>(<a-v>vocab</a-v>)\n\n<a-k>def</a-k> <a-f>one_hot</a-f>(<a-v>word</a-v>):\n    <a-v>vec</a-v> <a-o>=</a-o> <a-v>np</a-v>.<a-pr>zeros</a-pr>(<a-v>vocab_size</a-v>)\n    <a-v>vec</a-v>[<a-v>word2idx</a-v>[<a-v>word</a-v>]] <a-o>=</a-o> <a-n>1</a-n>\n    <a-k>return</a-k> <a-v>vec</a-v>\n\n<a-f>print</a-f>(<a-s>f&#39;One-hot &quot;cat&quot;: </a-s><a-p>{</a-p><a-f>one_hot</a-f><a-eb>(</a-eb><a-s>&quot;cat&quot;</a-s><a-eb>)</a-eb><a-p>}</a-p><a-s>&#39;</a-s>)\n<a-c># [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]  - only a single one!</a-c>\n\n<a-c># --- 2. Weight matrix (these will be our embeddings) ---</a-c>\n<a-v>embed_dim</a-v> <a-o>=</a-o> <a-n>5</a-n>  <a-c># in real Word2Vec this is 100-300</a-c>\n<a-cr>W1</a-cr> <a-o>=</a-o> <a-v>np</a-v>.<a-pr>random</a-pr>.<a-pr>randn</a-pr>(<a-v>vocab_size</a-v>, <a-v>embed_dim</a-v>) <a-o>*</a-o> <a-n>0.01</a-n>  <a-c># vocabulary -&gt; embedding</a-c>\n<a-cr>W2</a-cr> <a-o>=</a-o> <a-v>np</a-v>.<a-pr>random</a-pr>.<a-pr>randn</a-pr>(<a-v>embed_dim</a-v>, <a-v>vocab_size</a-v>) <a-o>*</a-o> <a-n>0.01</a-n>  <a-c># embedding -&gt; vocabulary</a-c>\n\n<a-c># --- 3. Training pairs (CBOW: context -&gt; middle) ---</a-c>\n<a-v>window</a-v> <a-o>=</a-o> <a-n>2</a-n>\n<a-v>pairs</a-v> <a-o>=</a-o> []\n<a-k>for</a-k> <a-v>i</a-v> <a-o>in</a-o> <a-f>range</a-f>(<a-v>window</a-v>, <a-f>len</a-f>(<a-v>corpus</a-v>) <a-o>-</a-o> <a-v>window</a-v>):\n    <a-v>context</a-v> <a-o>=</a-o> <a-v>corpus</a-v>[<a-v>i</a-v> <a-o>-</a-o> <a-v>window</a-v>:<a-v>i</a-v>] <a-o>+</a-o> <a-v>corpus</a-v>[<a-v>i</a-v> <a-o>+</a-o> <a-n>1</a-n>:<a-v>i</a-v> <a-o>+</a-o> <a-v>window</a-v> <a-o>+</a-o> <a-n>1</a-n>]\n    <a-v>target</a-v> <a-o>=</a-o> <a-v>corpus</a-v>[<a-v>i</a-v>]\n    <a-v>pairs</a-v>.<a-pr>append</a-pr>((<a-v>context</a-v>, <a-v>target</a-v>))\n\n<a-f>print</a-f>(<a-s>f&#39;Context: </a-s><a-p>{</a-p><a-v>pairs</a-v><a-eb>[</a-eb><a-n>0</a-n><a-eb>][</a-eb><a-n>0</a-n><a-eb>]</a-eb><a-p>}</a-p><a-s> -&gt; Target: </a-s><a-p>{</a-p><a-v>pairs</a-v><a-eb>[</a-eb><a-n>0</a-n><a-eb>][</a-eb><a-n>1</a-n><a-eb>]</a-eb><a-p>}</a-p><a-s>&#39;</a-s>)\n<a-c># Context: [&#39;cat&#39;, &#39;sits&#39;, &#39;the&#39;, &#39;mat&#39;] -&gt; Target: on</a-c>\n\n<a-c># --- 4. Training (gradient descent) ---</a-c>\n<a-k>def</a-k> <a-f>softmax</a-f>(<a-v>x</a-v>):\n    <a-v>e</a-v> <a-o>=</a-o> <a-v>np</a-v>.<a-pr>exp</a-pr>(<a-v>x</a-v> <a-o>-</a-o> <a-v>np</a-v>.<a-pr>max</a-pr>(<a-v>x</a-v>))\n    <a-k>return</a-k> <a-v>e</a-v> <a-o>/</a-o> <a-v>e</a-v>.<a-pr>sum</a-pr>()\n\n<a-k>for</a-k> <a-v>epoch</a-v> <a-o>in</a-o> <a-f>range</a-f>(<a-n>500</a-n>):\n    <a-v>loss</a-v> <a-o>=</a-o> <a-n>0</a-n>\n    <a-k>for</a-k> <a-v>context_words</a-v>, <a-v>target_word</a-v> <a-o>in</a-o> <a-v>pairs</a-v>:\n        <a-v>context_idx</a-v> <a-o>=</a-o> [<a-v>word2idx</a-v>[<a-v>w</a-v>] <a-k>for</a-k> <a-v>w</a-v> <a-o>in</a-o> <a-v>context_words</a-v>]\n        <a-v>target_idx</a-v> <a-o>=</a-o> <a-v>word2idx</a-v>[<a-v>target_word</a-v>]\n\n        <a-c># forward: averaged context embeddings -&gt; predict the middle</a-c>\n        <a-v>hidden</a-v> <a-o>=</a-o> <a-v>np</a-v>.<a-pr>mean</a-pr>(<a-cr>W1</a-cr>[<a-v>context_idx</a-v>], <a-v>axis</a-v><a-o>=</a-o><a-n>0</a-n>)\n        <a-v>output</a-v> <a-o>=</a-o> <a-f>softmax</a-f>(<a-v>hidden</a-v> @ <a-cr>W2</a-cr>)\n        <a-v>loss</a-v> <a-o>-=</a-o> <a-v>np</a-v>.<a-pr>log</a-pr>(<a-v>output</a-v>[<a-v>target_idx</a-v>] <a-o>+</a-o> <a-n>1e-8</a-n>)\n\n        <a-c># backward: update the weights</a-c>\n        <a-v>grad</a-v> <a-o>=</a-o> <a-v>output</a-v>.<a-pr>copy</a-pr>()\n        <a-v>grad</a-v>[<a-v>target_idx</a-v>] <a-o>-=</a-o> <a-n>1</a-n>\n        <a-cr>W2</a-cr> <a-o>-=</a-o> <a-n>0.05</a-n> <a-o>*</a-o> <a-v>np</a-v>.<a-pr>outer</a-pr>(<a-v>hidden</a-v>, <a-v>grad</a-v>)\n        <a-v>grad_hidden</a-v> <a-o>=</a-o> <a-v>grad</a-v> @ <a-cr>W2</a-cr>.<a-pr>T</a-pr>\n        <a-k>for</a-k> <a-v>idx</a-v> <a-o>in</a-o> <a-v>context_idx</a-v>:\n            <a-cr>W1</a-cr>[<a-v>idx</a-v>] <a-o>-=</a-o> <a-n>0.05</a-n> <a-o>*</a-o> <a-v>grad_hidden</a-v> <a-o>/</a-o> <a-f>len</a-f>(<a-v>context_idx</a-v>)\n\n<a-c># --- 5. Result: embeddings! ---</a-c>\n<a-k>for</a-k> <a-v>word</a-v> <a-o>in</a-o> [<a-s>&quot;cat&quot;</a-s>, <a-s>&quot;dog&quot;</a-s>, <a-s>&quot;sits&quot;</a-s>, <a-s>&quot;runs&quot;</a-s>]:\n    <a-f>print</a-f>(<a-s>f&quot;</a-s><a-p>{</a-p><a-v>word</a-v><a-p>}</a-p><a-s>: </a-s><a-p>{</a-p><a-cr>W1</a-cr><a-eb>[</a-eb><a-v>word2idx</a-v><a-eb>[</a-eb><a-v>word</a-v><a-eb>]].</a-eb><a-pr>round</a-pr><a-eb>(</a-eb><a-n>3</a-n><a-eb>)</a-eb><a-p>}</a-p><a-s>&quot;</a-s>)\n\n<a-c># --- 6. Cosine similarity ---</a-c>\n<a-k>def</a-k> <a-f>cosine</a-f>(<a-v>a</a-v>, <a-v>b</a-v>):\n    <a-k>return</a-k> <a-v>np</a-v>.<a-pr>dot</a-pr>(<a-v>a</a-v>, <a-v>b</a-v>) <a-o>/</a-o> (<a-v>np</a-v>.<a-pr>linalg</a-pr>.<a-pr>norm</a-pr>(<a-v>a</a-v>) <a-o>*</a-o> <a-v>np</a-v>.<a-pr>linalg</a-pr>.<a-pr>norm</a-pr>(<a-v>b</a-v>) <a-o>+</a-o> <a-n>1e-8</a-n>)\n\n<a-f>print</a-f>(<a-s>f&#39;cat &lt;-&gt; dog: </a-s><a-p>{</a-p><a-f>cosine</a-f><a-eb>(</a-eb><a-cr>W1</a-cr><a-eb>[</a-eb><a-v>word2idx</a-v><a-eb>[</a-eb><a-s>&quot;cat&quot;</a-s><a-eb>]], </a-eb><a-cr>W1</a-cr><a-eb>[</a-eb><a-v>word2idx</a-v><a-eb>[</a-eb><a-s>&quot;dog&quot;</a-s><a-eb>]]):.3f</a-eb><a-p>}</a-p><a-s>&#39;</a-s>)\n<a-f>print</a-f>(<a-s>f&#39;cat &lt;-&gt; sits: </a-s><a-p>{</a-p><a-f>cosine</a-f><a-eb>(</a-eb><a-cr>W1</a-cr><a-eb>[</a-eb><a-v>word2idx</a-v><a-eb>[</a-eb><a-s>&quot;cat&quot;</a-s><a-eb>]], </a-eb><a-cr>W1</a-cr><a-eb>[</a-eb><a-v>word2idx</a-v><a-eb>[</a-eb><a-s>&quot;sits&quot;</a-s><a-eb>]]):.3f</a-eb><a-p>}</a-p><a-s>&#39;</a-s>)\n<a-f>print</a-f>(<a-s>f&#39;cat &lt;-&gt; runs: </a-s><a-p>{</a-p><a-f>cosine</a-f><a-eb>(</a-eb><a-cr>W1</a-cr><a-eb>[</a-eb><a-v>word2idx</a-v><a-eb>[</a-eb><a-s>&quot;cat&quot;</a-s><a-eb>]], </a-eb><a-cr>W1</a-cr><a-eb>[</a-eb><a-v>word2idx</a-v><a-eb>[</a-eb><a-s>&quot;runs&quot;</a-s><a-eb>]]):.3f</a-eb><a-p>}</a-p><a-s>&#39;</a-s>)</code></pre>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">One-hot &quot;cat&quot;: [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]\nContext: ['cat', 'sits', 'the', 'mat'] -&gt; Target: on\ncat: [-2.866 -0.264 -0.225 -1.822  2.783]\ndog: [-1.133  0.678 -1.537  1.989  2.241]\nsits: [ 1.524 -0.023  1.804 -2.355 -0.788]\nruns: [ 2.     4.098 -0.315  2.971  0.815]\ncat &lt;-&gt; dog: 0.378\ncat &lt;-&gt; sits: -0.177\ncat &lt;-&gt; runs: -0.407\n</code></pre>\n<p>Notice: <strong>cat and dog</strong> (0.378) are more similar than <strong>cat and runs</strong> (-0.407). The network discovered on its own that &quot;cat&quot; and &quot;dog&quot; are animals, because they appear in similar contexts. Zero labels, zero supervision - just text and math.</p>\n<div class=\"markdown-alert markdown-alert-note\">\n<p class=\"markdown-alert-title\">Note</p>\n<p>This is <strong>all of Word2Vec in ~30 lines</strong>. Real Word2Vec adds negative sampling (because softmax over 50 000 words is slow) and optimizations, but the principle is exactly the same.</p>\n</div>\n<div class=\"markdown-alert markdown-alert-note\">\n<p class=\"markdown-alert-title\">Note</p>\n<p><strong>The &quot;fake task&quot;:</strong> What we care about is not what the network predicts, but the <strong>hidden layer weights</strong>. We train the network on an &quot;artificial&quot; task of predicting context, but what we want to extract is the matrix W - our embeddings. It's a bit like training someone to solve crosswords not so they solve crosswords well, but so they expand their vocabulary ;-)</p>\n</div>\n<h3><a href=\"#cbow-vs-skip-gram\" aria-hidden=\"true\" class=\"anchor\" id=\"cbow-vs-skip-gram\"></a>CBOW vs Skip-gram</h3>\n<table>\n<thead>\n<tr>\n<th></th>\n<th>CBOW</th>\n<th>Skip-gram</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>Direction</strong></td>\n<td>Context -&gt; Middle word</td>\n<td>Middle word -&gt; Context</td>\n</tr>\n<tr>\n<td><strong>Speed</strong></td>\n<td>Faster</td>\n<td>Slower</td>\n</tr>\n<tr>\n<td><strong>Rare words</strong></td>\n<td>Handles worse</td>\n<td>Handles better</td>\n</tr>\n<tr>\n<td><strong>Frequent words</strong></td>\n<td>Handles better</td>\n<td>Handles worse</td>\n</tr>\n<tr>\n<td><strong>When to use</strong></td>\n<td>Large corpus, frequent words</td>\n<td>Small corpus, rare words</td>\n</tr>\n</tbody>\n</table>\n<p>According to Mikolov et al.'s original paper: <strong>Skip-gram is better for rare words and small datasets. CBOW is faster and better for frequent words.</strong></p>\n<h3><a href=\"#the-magical-vector-space\" aria-hidden=\"true\" class=\"anchor\" id=\"the-magical-vector-space\"></a>The magical vector space</h3>\n<p>After training Word2Vec, each word is a vector (e.g. 300 numbers). And this space has remarkable properties:</p>\n<p><strong>Similar words are close:</strong></p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">distance(&quot;cat&quot;, &quot;dog&quot;) &lt; distance(&quot;cat&quot;, &quot;car&quot;)\ndistance(&quot;France&quot;, &quot;Germany&quot;) &lt; distance(&quot;France&quot;, &quot;banana&quot;)\n</code></pre>\n<p><strong>Analogies work like adding and subtracting vectors:</strong></p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">vector(&quot;king&quot;) - vector(&quot;man&quot;) + vector(&quot;woman&quot;) ≈ vector(&quot;queen&quot;)\n</code></pre>\n<p>Why? Because the &quot;king&quot; vector encodes many semantic dimensions - in one of them is information about &quot;gender&quot; (man/woman), in another about &quot;power&quot; (monarchy). By subtracting &quot;man&quot; and adding &quot;woman&quot;, we change the gender dimension while keeping the rest.</p>\n<p>Other analogy examples:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">France - Paris + Berlin ≈ Germany\nsmall - smaller + big ≈ bigger\nboat - water + air ≈ airplane\n</code></pre>\n<div class=\"mermaid-diagram\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"781.35175\" height=\"369\" viewBox=\"0 0 781.35175 369\"><rect x=\"0\" y=\"0\" width=\"781.35175\" height=\"369\" fill=\"#FFFFFF\"/><defs><marker id=\"arrow-0\" viewBox=\"0 0 10 10\" refX=\"5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker><marker id=\"arrow-start-0\" viewBox=\"0 0 10 10\" refX=\"4.5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 5 L 10 10 L 10 0 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker></defs><path id=\"edge-0\" class=\"edgePath\" data-edge-id=\"edge-0\" d=\"M 437.818,80.000 L 437.818,89.625 Q 437.818,97.500 445.365,99.748 L 494.066,114.252 Q 501.614,116.500 501.614,124.375 L 501.614,134.000\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(501.61 134.00) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><rect data-edge-id=\"edge-0\" data-label-kind=\"center\" x=\"317.20\" y=\"113.46\" width=\"167.20\" height=\"28.40\" rx=\"2\" ry=\"2\" fill=\"#FFFFFF\" fill-opacity=\"0.00\" stroke=\"#94A3B8\" stroke-opacity=\"0.00\" stroke-width=\"0.8\"/><g class=\"edgeLabel\" data-edge-id=\"edge-0\" data-label-kind=\"center\"><text x=\"400.81\" y=\"131.16\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"400.81\" dy=\"0.00\">&quot;subtract &apos;man&apos;&quot;</tspan></text></g><path id=\"edge-1\" class=\"edgePath\" data-edge-id=\"edge-1\" d=\"M 580.776,227.000 L 580.776,236.625 Q 580.776,244.500 582.486,252.187 L 583.292,255.813 Q 585.002,263.500 585.002,271.375 L 585.002,281.000\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(585.00 281.00) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><rect data-edge-id=\"edge-1\" data-label-kind=\"center\" x=\"446.68\" y=\"237.68\" width=\"137.54\" height=\"28.40\" rx=\"2\" ry=\"2\" fill=\"#FFFFFF\" fill-opacity=\"0.95\" stroke=\"#94A3B8\" stroke-opacity=\"0.45\" stroke-width=\"0.8\"/><g class=\"edgeLabel\" data-edge-id=\"edge-1\" data-label-kind=\"center\"><text x=\"515.45\" y=\"255.38\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"515.45\" dy=\"0.00\">&quot;add &apos;woman&apos;&quot;</tspan></text></g><path id=\"edge-2\" class=\"edgePath\" data-edge-id=\"edge-2\" d=\"M 265.081,44.000 L 215.081,44.000\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"   stroke-dasharray=\"4 4\" stroke-linecap=\"round\" stroke-linejoin=\"round\" /><path id=\"edge-3\" class=\"edgePath\" data-edge-id=\"edge-3\" d=\"M 633.002,281.000 L 633.002,271.375 Q 633.002,263.500 640.877,263.500 L 755.352,263.500 Q 765.352,263.500 765.352,253.500 L 765.352,90.000 Q 765.352,80.000 755.352,80.000 L 625.701,80.000\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"   stroke-dasharray=\"4 4\" stroke-linecap=\"round\" stroke-linejoin=\"round\" /><rect x=\"265.08\" y=\"8.00\" width=\"207.08\" height=\"72.00\" rx=\"3\" ry=\"3\" fill=\"#ff9999\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"368.62\" y=\"37.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"368.62\" dy=\"0.00\">👑 king</tspan><tspan x=\"368.62\" dy=\"21.00\">[0.50, 0.68, ...]</tspan></text><rect x=\"440.83\" y=\"134.00\" width=\"258.99\" height=\"93.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"570.33\" y=\"163.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"570.33\" dy=\"0.00\">[0.35, 0.55, ...]</tspan><tspan x=\"570.33\" dy=\"21.00\">&lt;i&gt;monarch, but without</tspan><tspan x=\"570.33\" dy=\"21.00\">gender&lt;/i&gt;</tspan></text><rect x=\"8.00\" y=\"8.00\" width=\"207.08\" height=\"72.00\" rx=\"3\" ry=\"3\" fill=\"#ffcc99\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"111.54\" y=\"37.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"111.54\" dy=\"0.00\">👨 man</tspan><tspan x=\"111.54\" dy=\"21.00\">[0.15, 0.13, ...]</tspan></text><rect x=\"497.46\" y=\"281.00\" width=\"207.08\" height=\"72.00\" rx=\"3\" ry=\"3\" fill=\"#99ccff\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"601.00\" y=\"310.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"601.00\" dy=\"0.00\">👸 queen</tspan><tspan x=\"601.00\" dy=\"21.00\">[0.38, 0.64, ...]</tspan></text><rect x=\"522.16\" y=\"8.00\" width=\"207.08\" height=\"72.00\" rx=\"3\" ry=\"3\" fill=\"#ccff99\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"625.70\" y=\"37.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"625.70\" dy=\"0.00\">👩 woman</tspan><tspan x=\"625.70\" dy=\"21.00\">[0.12, 0.20, ...]</tspan></text></svg></div>\n<p>This is what we talked about in the <a href=\"linguistic-features-and-llm.html\">first post</a> on the occasion of semantics. Now you see <strong>how it works under the hood</strong>.</p>\n<h3><a href=\"#word2vec-in-code\" aria-hidden=\"true\" class=\"anchor\" id=\"word2vec-in-code\"></a>Word2Vec in code</h3>\n<p>First let's train our own model on a small corpus to see how it works from scratch:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-python\"><a-k>from</a-k> <a-v>gensim</a-v>.<a-v>models</a-v> <a-k>import</a-k> <a-cr>Word2Vec</a-cr>\n\n<a-v>corpus</a-v> <a-o>=</a-o> [\n    [<a-s>&quot;cat&quot;</a-s>, <a-s>&quot;sits&quot;</a-s>, <a-s>&quot;on&quot;</a-s>, <a-s>&quot;the&quot;</a-s>, <a-s>&quot;mat&quot;</a-s>, <a-s>&quot;and&quot;</a-s>, <a-s>&quot;sleeps&quot;</a-s>],\n    [<a-s>&quot;dog&quot;</a-s>, <a-s>&quot;sits&quot;</a-s>, <a-s>&quot;on&quot;</a-s>, <a-s>&quot;the&quot;</a-s>, <a-s>&quot;rug&quot;</a-s>, <a-s>&quot;and&quot;</a-s>, <a-s>&quot;sleeps&quot;</a-s>],\n    [<a-s>&quot;cat&quot;</a-s>, <a-s>&quot;runs&quot;</a-s>, <a-s>&quot;around&quot;</a-s>, <a-s>&quot;the&quot;</a-s>, <a-s>&quot;room&quot;</a-s>],\n    [<a-s>&quot;dog&quot;</a-s>, <a-s>&quot;runs&quot;</a-s>, <a-s>&quot;in&quot;</a-s>, <a-s>&quot;the&quot;</a-s>, <a-s>&quot;park&quot;</a-s>],\n    [<a-s>&quot;cat&quot;</a-s>, <a-s>&quot;and&quot;</a-s>, <a-s>&quot;dog&quot;</a-s>, <a-s>&quot;play&quot;</a-s>, <a-s>&quot;in&quot;</a-s>, <a-s>&quot;the&quot;</a-s>, <a-s>&quot;garden&quot;</a-s>],\n    [<a-s>&quot;cat&quot;</a-s>, <a-s>&quot;eats&quot;</a-s>, <a-s>&quot;kibble&quot;</a-s>, <a-s>&quot;from&quot;</a-s>, <a-s>&quot;the&quot;</a-s>, <a-s>&quot;bowl&quot;</a-s>],\n    [<a-s>&quot;dog&quot;</a-s>, <a-s>&quot;eats&quot;</a-s>, <a-s>&quot;kibble&quot;</a-s>, <a-s>&quot;from&quot;</a-s>, <a-s>&quot;the&quot;</a-s>, <a-s>&quot;bowl&quot;</a-s>],\n    [<a-s>&quot;cat&quot;</a-s>, <a-s>&quot;catches&quot;</a-s>, <a-s>&quot;a&quot;</a-s>, <a-s>&quot;mouse&quot;</a-s>, <a-s>&quot;in&quot;</a-s>, <a-s>&quot;the&quot;</a-s>, <a-s>&quot;house&quot;</a-s>],\n    [<a-s>&quot;dog&quot;</a-s>, <a-s>&quot;chases&quot;</a-s>, <a-s>&quot;the&quot;</a-s>, <a-s>&quot;cat&quot;</a-s>, <a-s>&quot;in&quot;</a-s>, <a-s>&quot;the&quot;</a-s>, <a-s>&quot;garden&quot;</a-s>],\n    [<a-s>&quot;bird&quot;</a-s>, <a-s>&quot;sits&quot;</a-s>, <a-s>&quot;on&quot;</a-s>, <a-s>&quot;a&quot;</a-s>, <a-s>&quot;branch&quot;</a-s>, <a-s>&quot;of&quot;</a-s>, <a-s>&quot;the&quot;</a-s>, <a-s>&quot;tree&quot;</a-s>],\n    [<a-s>&quot;fish&quot;</a-s>, <a-s>&quot;swims&quot;</a-s>, <a-s>&quot;in&quot;</a-s>, <a-s>&quot;the&quot;</a-s>, <a-s>&quot;aquarium&quot;</a-s>],\n    [<a-s>&quot;car&quot;</a-s>, <a-s>&quot;drives&quot;</a-s>, <a-s>&quot;on&quot;</a-s>, <a-s>&quot;the&quot;</a-s>, <a-s>&quot;road&quot;</a-s>],\n    [<a-s>&quot;bike&quot;</a-s>, <a-s>&quot;rides&quot;</a-s>, <a-s>&quot;on&quot;</a-s>, <a-s>&quot;the&quot;</a-s>, <a-s>&quot;path&quot;</a-s>],\n    [<a-s>&quot;cat&quot;</a-s>, <a-s>&quot;purrs&quot;</a-s>, <a-s>&quot;when&quot;</a-s>, <a-s>&quot;you&quot;</a-s>, <a-s>&quot;pet&quot;</a-s>, <a-s>&quot;it&quot;</a-s>],\n    [<a-s>&quot;dog&quot;</a-s>, <a-s>&quot;wags&quot;</a-s>, <a-s>&quot;its&quot;</a-s>, <a-s>&quot;tail&quot;</a-s>, <a-s>&quot;when&quot;</a-s>, <a-s>&quot;it&quot;</a-s>, <a-s>&quot;sees&quot;</a-s>, <a-s>&quot;its&quot;</a-s>, <a-s>&quot;owner&quot;</a-s>],\n]\n\n<a-v>model</a-v> <a-o>=</a-o> <a-f>Word2Vec</a-f>(\n    <a-v>sentences</a-v><a-o>=</a-o><a-v>corpus</a-v>,\n    <a-v>vector_size</a-v><a-o>=</a-o><a-n>10</a-n>,   <a-c># vector dimension (small = educational)</a-c>\n    <a-v>window</a-v><a-o>=</a-o><a-n>3</a-n>,         <a-c># context window size</a-c>\n    <a-v>min_count</a-v><a-o>=</a-o><a-n>1</a-n>,      <a-c># ignore words rarer than N</a-c>\n    <a-v>sg</a-v><a-o>=</a-o><a-n>0</a-n>,             <a-c># 0 = CBOW, 1 = Skip-gram</a-c>\n    <a-v>epochs</a-v><a-o>=</a-o><a-n>200</a-n>,       <a-c># how many passes over the data</a-c>\n)\n\n<a-f>print</a-f>(<a-v>model</a-v>.<a-pr>wv</a-pr>.<a-pr>most_similar</a-pr>(<a-s>&quot;cat&quot;</a-s>, <a-v>topn</a-v><a-o>=</a-o><a-n>3</a-n>))\n<a-c># [(&#39;dog&#39;, 0.96), (&#39;sleeps&#39;, 0.95), (&#39;on&#39;, 0.95)]</a-c>\n\n<a-f>print</a-f>(<a-v>model</a-v>.<a-pr>wv</a-pr>.<a-pr>similarity</a-pr>(<a-s>&quot;cat&quot;</a-s>, <a-s>&quot;dog&quot;</a-s>))    <a-c># ~0.96</a-c>\n<a-f>print</a-f>(<a-v>model</a-v>.<a-pr>wv</a-pr>.<a-pr>similarity</a-pr>(<a-s>&quot;cat&quot;</a-s>, <a-s>&quot;car&quot;</a-s>))    <a-c># ~0.83</a-c></code></pre>\n<div class=\"markdown-alert markdown-alert-note\">\n<p class=\"markdown-alert-title\">Note</p>\n<p>Notice: our corpus is TINY (15 sentences), so the results are far from ideal - &quot;cat&quot; and &quot;car&quot; have as much as 0.83 similarity, which makes no sense. But the model correctly puts &quot;dog&quot; closest to &quot;cat&quot;! On real corpora (billions of sentences) these vectors become very accurate.</p>\n</div>\n<p>And here's how we use a <strong>pretrained model</strong> trained on billions of words:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-python\"><a-k>from</a-k> <a-v>gensim</a-v>.<a-v>downloader</a-v> <a-k>import</a-k> <a-v>load</a-v>\n\n<a-v>model</a-v> <a-o>=</a-o> <a-f>load</a-f>(<a-s>&quot;glove-wiki-gigaword-50&quot;</a-s>)\n\n<a-v>result</a-v> <a-o>=</a-o> <a-v>model</a-v>.<a-pr>most_similar</a-pr>(\n    <a-v>positive</a-v><a-o>=</a-o>[<a-s>&quot;king&quot;</a-s>, <a-s>&quot;woman&quot;</a-s>],\n    <a-v>negative</a-v><a-o>=</a-o>[<a-s>&quot;man&quot;</a-s>],\n    <a-v>topn</a-v><a-o>=</a-o><a-n>5</a-n>\n)\n\n<a-k>for</a-k> <a-v>word</a-v>, <a-v>score</a-v> <a-o>in</a-o> <a-v>result</a-v>:\n    <a-f>print</a-f>(<a-s>f&quot;</a-s><a-p>{</a-p><a-v>word</a-v><a-p>}</a-p><a-s>: </a-s><a-p>{</a-p><a-v>score</a-v><a-eb>:.3f</a-eb><a-p>}</a-p><a-s>&quot;</a-s>)\n\n<a-c># queen: 0.852</a-c>\n<a-c># throne: 0.737</a-c>\n<a-c># ...</a-c></code></pre>\n<p>You can also check the similarity between words:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-python\"><a-f>print</a-f>(<a-v>model</a-v>.<a-pr>similarity</a-pr>(<a-s>&quot;cat&quot;</a-s>, <a-s>&quot;dog&quot;</a-s>))    <a-c># ~0.92</a-c>\n<a-f>print</a-f>(<a-v>model</a-v>.<a-pr>similarity</a-pr>(<a-s>&quot;cat&quot;</a-s>, <a-s>&quot;car&quot;</a-s>))    <a-c># ~0.15</a-c></code></pre>\n<p>&quot;Cat&quot; and &quot;dog&quot; - similarity 0.92. &quot;Cat&quot; and &quot;car&quot; - 0.15. The model &quot;knows&quot; that a cat is closer to a dog than to a car.</p>\n<div class=\"markdown-alert markdown-alert-tip\">\n<p class=\"markdown-alert-title\">Tip</p>\n<p><strong>Experiment:</strong> Go to the <a href=\"https://projector.tensorflow.org/\">TensorFlow Embedding Projector</a> - it's an interactive visualization of the vector space. You can type words and see what's close. It's ONE of the most beautiful visualizations in all of ML. Seriously, check it out!</p>\n</div>\n<div class=\"markdown-alert markdown-alert-warning\">\n<p class=\"markdown-alert-title\">Warning</p>\n<p><strong>Watch out for bias:</strong> Word2Vec learns from data. If in the training data &quot;programmer&quot; appears more often near &quot;man&quot; than &quot;woman&quot; - the model will pick that up. A famous example: <code>vector(&quot;programmer&quot;) - vector(&quot;man&quot;) + vector(&quot;woman&quot;) ≈ &quot;homemaker&quot;</code>. That's a bias hidden in the data that the model unreflectively reproduces. Remember the semiosphere from the <a href=\"semiotics-and-llm.html\">second post</a>? The semiosphere isn't neutral - and neither is the training data.</p>\n</div>\n<h3><a href=\"#from-sparse-vectors-to-dense\" aria-hidden=\"true\" class=\"anchor\" id=\"from-sparse-vectors-to-dense\"></a>From sparse vectors to dense</h3>\n<p>Let's make the comparison again, to make it stick:</p>\n<div class=\"mermaid-diagram\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"741.849\" height=\"461\" viewBox=\"0 0 741.849 461\"><rect x=\"0\" y=\"0\" width=\"741.849\" height=\"461\" fill=\"#FFFFFF\"/><defs><marker id=\"arrow-0\" viewBox=\"0 0 10 10\" refX=\"5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker><marker id=\"arrow-start-0\" viewBox=\"0 0 10 10\" refX=\"4.5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 5 L 10 10 L 10 0 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker></defs><rect x=\"8.00\" y=\"8.00\" width=\"717.85\" height=\"205.50\" rx=\"10\" ry=\"10\" fill=\"#F1F5F9\" stroke=\"#CBD5E1\" stroke-width=\"1\" /><text x=\"366.92\" y=\"37.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"366.92\" dy=\"0.00\">BoW / TF-IDF: sparse</tspan><tspan x=\"366.92\" dy=\"21.00\">vector</tspan></text><rect x=\"8.00\" y=\"263.50\" width=\"674.59\" height=\"181.50\" rx=\"10\" ry=\"10\" fill=\"#F1F5F9\" stroke=\"#CBD5E1\" stroke-width=\"1\" /><text x=\"345.29\" y=\"291.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"345.29\" dy=\"0.00\">Word2Vec: dense vector</tspan></text><path id=\"edge-0\" class=\"edgePath\" data-edge-id=\"edge-0\" d=\"M 320.295,129.508 L 370.295,129.508\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(370.29 129.51) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-1\" class=\"edgePath\" data-edge-id=\"edge-1\" d=\"M 147.259,360.992 L 370.295,360.992\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(370.29 360.99) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><rect x=\"44.00\" y=\"335.48\" width=\"103.26\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"95.63\" y=\"364.48\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"95.63\" dy=\"0.00\">&apos;cat&apos;</tspan></text><rect x=\"370.29\" y=\"304.00\" width=\"276.29\" height=\"114.00\" rx=\"3\" ry=\"3\" fill=\"#99ff99\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"508.44\" y=\"333.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"508.44\" dy=\"0.00\">[0.23, -0.45, 0.67, 0.12,</tspan><tspan x=\"508.44\" dy=\"21.00\">-0.89, ...]</tspan><tspan x=\"508.44\" dy=\"21.00\">Length = 300</tspan><tspan x=\"508.44\" dy=\"21.00\">All values NON-ZERO</tspan></text><rect x=\"44.00\" y=\"104.02\" width=\"276.29\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"182.15\" y=\"133.02\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"182.15\" dy=\"0.00\">&apos;The cat sits on the mat&apos;</tspan></text><rect x=\"370.29\" y=\"72.50\" width=\"319.55\" height=\"114.00\" rx=\"3\" ry=\"3\" fill=\"#ff9999\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"530.07\" y=\"101.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"530.07\" dy=\"0.00\">[0, 0, 1, 0, 0, 1, 0, 0, 1, 0,</tspan><tspan x=\"530.07\" dy=\"21.00\">0, 1, ...]</tspan><tspan x=\"530.07\" dy=\"21.00\">Length = vocabulary size</tspan><tspan x=\"530.07\" dy=\"21.00\">Mostly ZEROS</tspan></text></svg></div>\n<table>\n<thead>\n<tr>\n<th></th>\n<th>BoW / TF-IDF</th>\n<th>Word2Vec</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>Type</strong></td>\n<td>Sparse</td>\n<td>Dense</td>\n</tr>\n<tr>\n<td><strong>Vector length</strong></td>\n<td>= vocabulary size (can be 100 000+)</td>\n<td>= embedding dimension (usually 100-300)</td>\n</tr>\n<tr>\n<td><strong>Values</strong></td>\n<td>Mostly zeros</td>\n<td>All non-zero</td>\n</tr>\n<tr>\n<td><strong>Similarity</strong></td>\n<td>Hard to capture</td>\n<td>Cosine similarity works great</td>\n</tr>\n<tr>\n<td><strong>Context</strong></td>\n<td>None</td>\n<td>Captured through the context window</td>\n</tr>\n<tr>\n<td><strong>Analogies</strong></td>\n<td>None</td>\n<td>Work (king - man + woman ≈ queen)</td>\n</tr>\n</tbody>\n</table>\n<h3><a href=\"#can-word2vec-generate-text\" aria-hidden=\"true\" class=\"anchor\" id=\"can-word2vec-generate-text\"></a>Can Word2Vec generate text?</h3>\n<p>No. Word2Vec creates a <strong>map of meanings</strong> - it tells you that &quot;cat&quot; is close to &quot;dog&quot;, but it can't compose a sentence out of that. It's like a synonym dictionary: you know what's similar, but you won't write a poem.</p>\n<p>But... what if we combine Word2Vec with Markov chains? A Markov chain picks the next word based on frequency. And if instead of drawing equally, we make words more similar to the context have a higher chance?</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-python\"><a-k>from</a-k> <a-v>gensim</a-v>.<a-v>models</a-v> <a-k>import</a-k> <a-cr>Word2Vec</a-cr>\n<a-k>import</a-k> <a-v>numpy</a-v> <a-k>as</a-k> <a-v>np</a-v>\n<a-k>from</a-k> <a-v>collections</a-v> <a-k>import</a-k> <a-v>defaultdict</a-v>\n<a-k>import</a-k> <a-v>random</a-v>\n\n<a-v>corpus</a-v> <a-o>=</a-o> [\n    <a-s>&quot;cat sits on the mat and sleeps&quot;</a-s>,\n    <a-s>&quot;dog sits on the rug and sleeps&quot;</a-s>,\n    <a-s>&quot;cat runs around the room&quot;</a-s>,\n    <a-s>&quot;dog runs in the park&quot;</a-s>,\n    <a-s>&quot;cat and dog play in the garden&quot;</a-s>,\n    <a-s>&quot;cat eats kibble from the bowl&quot;</a-s>,\n    <a-s>&quot;dog eats kibble from the bowl&quot;</a-s>,\n    <a-s>&quot;cat catches a mouse in the house&quot;</a-s>,\n    <a-s>&quot;dog chases the cat in the garden&quot;</a-s>,\n    <a-s>&quot;bird sits on a branch of the tree&quot;</a-s>,\n    <a-s>&quot;cat looks through the window and purrs&quot;</a-s>,\n    <a-s>&quot;dog barks at the mailman&quot;</a-s>,\n    <a-s>&quot;cat sleeps all day on the couch&quot;</a-s>,\n]\n\n<a-v>tokenized</a-v> <a-o>=</a-o> [<a-v>sentence</a-v>.<a-pr>split</a-pr>() <a-k>for</a-k> <a-v>sentence</a-v> <a-o>in</a-o> <a-v>corpus</a-v>]\n\n<a-v>w2v_model</a-v> <a-o>=</a-o> <a-f>Word2Vec</a-f>(<a-v>sentences</a-v><a-o>=</a-o><a-v>tokenized</a-v>, <a-v>vector_size</a-v><a-o>=</a-o><a-n>10</a-n>, <a-v>window</a-v><a-o>=</a-o><a-n>3</a-n>, <a-v>min_count</a-v><a-o>=</a-o><a-n>1</a-n>, <a-v>epochs</a-v><a-o>=</a-o><a-n>200</a-n>)\n\n<a-c># build the transition matrix (like in Markov chains)</a-c>\n<a-v>transitions</a-v> <a-o>=</a-o> <a-f>defaultdict</a-f>(<a-v>list</a-v>)\n<a-k>for</a-k> <a-v>sentence</a-v> <a-o>in</a-o> <a-v>tokenized</a-v>:\n    <a-k>for</a-k> <a-v>i</a-v> <a-o>in</a-o> <a-f>range</a-f>(<a-f>len</a-f>(<a-v>sentence</a-v>) <a-o>-</a-o> <a-n>1</a-n>):\n        <a-v>transitions</a-v>[<a-v>sentence</a-v>[<a-v>i</a-v>]].<a-pr>append</a-pr>(<a-v>sentence</a-v>[<a-v>i</a-v> <a-o>+</a-o> <a-n>1</a-n>])\n\n<a-k>def</a-k> <a-f>generate</a-f>(<a-v>start</a-v>, <a-v>length</a-v><a-o>=</a-o><a-n>6</a-n>):\n    <a-v>words</a-v> <a-o>=</a-o> [<a-v>start</a-v>]\n    <a-k>for</a-k> <a-v>_</a-v> <a-o>in</a-o> <a-f>range</a-f>(<a-v>length</a-v>):\n        <a-v>current</a-v> <a-o>=</a-o> <a-v>words</a-v>[<a-o>-</a-o><a-n>1</a-n>]\n        <a-k>if</a-k> <a-v>current</a-v> <a-o>not in</a-o> <a-v>transitions</a-v>:\n            <a-k>break</a-k>\n\n        <a-v>candidates</a-v> <a-o>=</a-o> <a-v>transitions</a-v>[<a-v>current</a-v>]\n\n        <a-c># averaged vector of the last words = context</a-c>\n        <a-v>context_vector</a-v> <a-o>=</a-o> <a-v>np</a-v>.<a-pr>mean</a-pr>(\n            [<a-v>w2v_model</a-v>.<a-pr>wv</a-pr>[<a-v>w</a-v>] <a-k>for</a-k> <a-v>w</a-v> <a-o>in</a-o> <a-v>words</a-v>[<a-o>-</a-o><a-n>3</a-n>:] <a-k>if</a-k> <a-v>w</a-v> <a-o>in</a-o> <a-v>w2v_model</a-v>.<a-pr>wv</a-pr>],\n            <a-v>axis</a-v><a-o>=</a-o><a-n>0</a-n>,\n        )\n\n        <a-c># score candidates by similarity to the context</a-c>\n        <a-v>scored</a-v> <a-o>=</a-o> []\n        <a-k>for</a-k> <a-v>candidate</a-v> <a-o>in</a-o> <a-v>candidates</a-v>:\n            <a-k>if</a-k> <a-v>candidate</a-v> <a-o>in</a-o> <a-v>w2v_model</a-v>.<a-pr>wv</a-pr>:\n                <a-v>similarity</a-v> <a-o>=</a-o> <a-v>np</a-v>.<a-pr>dot</a-pr>(<a-v>context_vector</a-v>, <a-v>w2v_model</a-v>.<a-pr>wv</a-pr>[<a-v>candidate</a-v>]) <a-o>/</a-o> (\n                    <a-v>np</a-v>.<a-pr>linalg</a-pr>.<a-pr>norm</a-pr>(<a-v>context_vector</a-v>) <a-o>*</a-o> <a-v>np</a-v>.<a-pr>linalg</a-pr>.<a-pr>norm</a-pr>(<a-v>w2v_model</a-v>.<a-pr>wv</a-pr>[<a-v>candidate</a-v>]) <a-o>+</a-o> <a-n>1e-8</a-n>\n                )\n                <a-v>scored</a-v>.<a-pr>append</a-pr>((<a-v>candidate</a-v>, <a-v>similarity</a-v>))\n\n        <a-k>if</a-k> <a-v>scored</a-v>:\n            <a-c># draw, but with weights - similar words have a higher chance</a-c>\n            <a-v>weights</a-v> <a-o>=</a-o> [<a-v>score</a-v> <a-o>+</a-o> <a-n>1</a-n> <a-k>for</a-k> <a-v>_</a-v>, <a-v>score</a-v> <a-o>in</a-o> <a-v>scored</a-v>]\n            <a-v>chosen</a-v> <a-o>=</a-o> <a-v>random</a-v>.<a-pr>choices</a-pr>([<a-v>w</a-v> <a-k>for</a-k> <a-v>w</a-v>, <a-v>_</a-v> <a-o>in</a-o> <a-v>scored</a-v>], <a-v>weights</a-v><a-o>=</a-o><a-v>weights</a-v>, <a-v>k</a-v><a-o>=</a-o><a-n>1</a-n>)[<a-n>0</a-n>]\n            <a-v>words</a-v>.<a-pr>append</a-pr>(<a-v>chosen</a-v>)\n        <a-k>else</a-k>:\n            <a-v>words</a-v>.<a-pr>append</a-pr>(<a-v>random</a-v>.<a-pr>choice</a-pr>(<a-v>candidates</a-v>))\n    <a-k>return</a-k> <a-s>&quot; &quot;</a-s>.<a-pr>join</a-pr>(<a-v>words</a-v>)\n\n<a-k>for</a-k> <a-v>_</a-v> <a-o>in</a-o> <a-f>range</a-f>(<a-n>5</a-n>):\n    <a-f>print</a-f>(<a-f>generate</a-f>(<a-s>&quot;cat&quot;</a-s>))</code></pre>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">cat sits on the mat and sleeps\ncat looks through the window and purrs\ncat eats kibble from the bowl\ncat sleeps all day on the couch\ncat catches a mouse in the house\n</code></pre>\n<p>It's not Shakespeare, but the texts are more coherent than pure random Markov. The idea is simple:</p>\n<ol>\n<li>The Markov chain gives <strong>candidates</strong> for the next word</li>\n<li>Word2Vec <strong>scores</strong> the candidates based on similarity to the context</li>\n<li>We pick randomly, but with a <strong>bias</strong> - words that fit better have a higher chance</li>\n</ol>\n<p>This is a tiny step toward what today's LLMs do. They also predict the next word, but instead of a simple transition matrix they have multi-layer Transformer networks with an attention mechanism. There, &quot;scoring candidates&quot; is much, much more advanced.</p>\n<div class=\"markdown-alert markdown-alert-important\">\n<p class=\"markdown-alert-title\">Important</p>\n<p><strong>The key difference:</strong> Word2Vec gives each word ONE vector. But &quot;bank&quot; in &quot;bank account&quot; and &quot;bank&quot; of a river are completely different meanings! Word2Vec doesn't distinguish them - that's why in the next posts we get into Transformers, where <strong>context changes the meaning</strong> of every word.</p>\n</div>\n<hr />\n<h2><a href=\"#summary---the-whole-path-in-one-place\" aria-hidden=\"true\" class=\"anchor\" id=\"summary---the-whole-path-in-one-place\"></a>Summary - the whole path in one place</h2>\n<p>Here's our roadmap, from cutting text to the geometry of meanings:</p>\n<table>\n<thead>\n<tr>\n<th>Method</th>\n<th>What it does</th>\n<th>Context?</th>\n<th>What it can't do</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>Tokenization (BPE)</strong></td>\n<td>Cuts text into pieces</td>\n<td>No</td>\n<td>Doesn't understand what it cuts</td>\n</tr>\n<tr>\n<td><strong>BoW / TF-IDF</strong></td>\n<td>Counts words, weights by rarity</td>\n<td>No</td>\n<td>Ignores order</td>\n</tr>\n<tr>\n<td><strong>N-grams</strong></td>\n<td>Looks at word sequences</td>\n<td>Local (2-3 words)</td>\n<td>Short context, fast-growing vocabulary</td>\n</tr>\n<tr>\n<td><strong>Naive Bayes</strong></td>\n<td>Classifies based on probability</td>\n<td>No (words &quot;independent&quot;)</td>\n<td>Doesn't capture dependencies between words</td>\n</tr>\n<tr>\n<td><strong>Word2Vec</strong></td>\n<td>Turns words into meaning vectors</td>\n<td>Yes (context window)</td>\n<td>One word = one vector (ignores polysemy)</td>\n</tr>\n</tbody>\n</table>\n<div class=\"mermaid-diagram\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"368.94678\" height=\"893.5\" viewBox=\"0 0 368.94678 893.5\"><rect x=\"0\" y=\"0\" width=\"368.94678\" height=\"893.5\" fill=\"#FFFFFF\"/><defs><marker id=\"arrow-0\" viewBox=\"0 0 10 10\" refX=\"5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker><marker id=\"arrow-start-0\" viewBox=\"0 0 10 10\" refX=\"4.5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 5 L 10 10 L 10 0 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker></defs><rect x=\"8.00\" y=\"8.00\" width=\"344.95\" height=\"869.50\" rx=\"10\" ry=\"10\" fill=\"#F1F5F9\" stroke=\"#CBD5E1\" stroke-width=\"1\" /><text x=\"180.47\" y=\"37.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"180.47\" dy=\"0.00\">Evolution: from counting</tspan><tspan x=\"180.47\" dy=\"21.00\">to understanding</tspan></text><path id=\"edge-0\" class=\"edgePath\" data-edge-id=\"edge-0\" d=\"M 180.474,144.500 L 180.474,198.500\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(180.47 198.50) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><rect data-edge-id=\"edge-0\" data-label-kind=\"center\" x=\"62.74\" y=\"110.56\" width=\"157.32\" height=\"28.40\" rx=\"2\" ry=\"2\" fill=\"#FFFFFF\" fill-opacity=\"0.00\" stroke=\"#94A3B8\" stroke-opacity=\"0.00\" stroke-width=\"0.8\"/><g class=\"edgeLabel\" data-edge-id=\"edge-0\" data-label-kind=\"center\"><text x=\"141.39\" y=\"128.26\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"141.39\" dy=\"0.00\">&quot;how to count?&quot;</tspan></text></g><path id=\"edge-1\" class=\"edgePath\" data-edge-id=\"edge-1\" d=\"M 180.474,291.500 L 180.473,345.500\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(180.47 345.50) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><rect data-edge-id=\"edge-1\" data-label-kind=\"center\" x=\"177.84\" y=\"303.04\" width=\"137.54\" height=\"28.40\" rx=\"2\" ry=\"2\" fill=\"#FFFFFF\" fill-opacity=\"0.95\" stroke=\"#94A3B8\" stroke-opacity=\"0.45\" stroke-width=\"0.8\"/><g class=\"edgeLabel\" data-edge-id=\"edge-1\" data-label-kind=\"center\"><text x=\"246.61\" y=\"320.74\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"246.61\" dy=\"0.00\">&quot;no context!&quot;</tspan></text></g><path id=\"edge-2\" class=\"edgePath\" data-edge-id=\"edge-2\" d=\"M 180.473,417.500 L 180.473,471.500\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(180.47 471.50) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><rect data-edge-id=\"edge-2\" data-label-kind=\"center\" x=\"67.21\" y=\"381.04\" width=\"226.53\" height=\"28.40\" rx=\"2\" ry=\"2\" fill=\"#FFFFFF\" fill-opacity=\"0.00\" stroke=\"#94A3B8\" stroke-opacity=\"0.00\" stroke-width=\"0.8\"/><g class=\"edgeLabel\" data-edge-id=\"edge-2\" data-label-kind=\"center\"><text x=\"180.47\" y=\"398.74\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"180.47\" dy=\"0.00\">&quot;we want to classify!&quot;</tspan></text></g><path id=\"edge-3\" class=\"edgePath\" data-edge-id=\"edge-3\" d=\"M 180.473,543.500 L 180.473,597.500\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(180.47 597.50) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><rect data-edge-id=\"edge-3\" data-label-kind=\"center\" x=\"62.26\" y=\"507.04\" width=\"236.42\" height=\"28.40\" rx=\"2\" ry=\"2\" fill=\"#FFFFFF\" fill-opacity=\"0.00\" stroke=\"#94A3B8\" stroke-opacity=\"0.00\" stroke-width=\"0.8\"/><g class=\"edgeLabel\" data-edge-id=\"edge-3\" data-label-kind=\"center\"><text x=\"180.47\" y=\"524.74\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"180.47\" dy=\"0.00\">&quot;words aren&apos;t numbers!&quot;</tspan></text></g><path id=\"edge-4\" class=\"edgePath\" data-edge-id=\"edge-4\" d=\"M 196.473,690.500 L 196.473,744.500\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(196.47 744.50) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><rect data-edge-id=\"edge-4\" data-label-kind=\"center\" x=\"86.15\" y=\"654.04\" width=\"147.43\" height=\"28.40\" rx=\"2\" ry=\"2\" fill=\"#FFFFFF\" fill-opacity=\"0.00\" stroke=\"#94A3B8\" stroke-opacity=\"0.00\" stroke-width=\"0.8\"/><g class=\"edgeLabel\" data-edge-id=\"edge-4\" data-label-kind=\"center\"><text x=\"159.87\" y=\"671.74\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"159.87\" dy=\"0.00\">&quot;what&apos;s next?&quot;</tspan></text></g><rect x=\"89.91\" y=\"198.50\" width=\"181.13\" height=\"93.00\" rx=\"3\" ry=\"3\" fill=\"#ffcc99\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"180.47\" y=\"227.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"180.47\" dy=\"0.00\">🔢 BoW / TF-IDF</tspan><tspan x=\"180.47\" dy=\"21.00\">&lt;i&gt;pieces -&gt;</tspan><tspan x=\"180.47\" dy=\"21.00\">numbers&lt;/i&gt;</tspan></text><rect x=\"46.65\" y=\"744.50\" width=\"267.64\" height=\"93.00\" rx=\"3\" ry=\"3\" fill=\"#9999ff\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"180.47\" y=\"773.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#fff\"><tspan x=\"180.47\" dy=\"0.00\">🤖 Transformers / LLM</tspan><tspan x=\"180.47\" dy=\"21.00\">&lt;i&gt;context + attention +</tspan><tspan x=\"180.47\" dy=\"21.00\">scale&lt;/i&gt;</tspan></text><rect x=\"68.28\" y=\"345.50\" width=\"224.38\" height=\"72.00\" rx=\"3\" ry=\"3\" fill=\"#ffff99\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"180.47\" y=\"374.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"180.47\" dy=\"0.00\">🔗 N-grams / Markov</tspan><tspan x=\"180.47\" dy=\"21.00\">&lt;i&gt;adding order&lt;/i&gt;</tspan></text><rect x=\"38.00\" y=\"471.50\" width=\"284.95\" height=\"72.00\" rx=\"3\" ry=\"3\" fill=\"#ccff99\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"180.47\" y=\"500.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"180.47\" dy=\"0.00\">📊 Naive Bayes</tspan><tspan x=\"180.47\" dy=\"21.00\">&lt;i&gt;class probabilities&lt;/i&gt;</tspan></text><rect x=\"59.63\" y=\"72.50\" width=\"241.69\" height=\"72.00\" rx=\"3\" ry=\"3\" fill=\"#ff9999\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"180.47\" y=\"101.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"180.47\" dy=\"0.00\">✂️ Tokenization</tspan><tspan x=\"180.47\" dy=\"21.00\">&lt;i&gt;text -&gt; pieces&lt;/i&gt;</tspan></text><rect x=\"89.91\" y=\"597.50\" width=\"181.13\" height=\"93.00\" rx=\"3\" ry=\"3\" fill=\"#99ccff\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"180.47\" y=\"626.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"180.47\" dy=\"0.00\">📐 Word2Vec</tspan><tspan x=\"180.47\" dy=\"21.00\">&lt;i&gt;geometry of</tspan><tspan x=\"180.47\" dy=\"21.00\">meanings&lt;/i&gt;</tspan></text></svg></div>\n<p>And the key perspective: <strong>from each of these methods, the LLM took something for itself.</strong></p>\n<ul>\n<li><strong>Tokenization (BPE)</strong> is the first step of every LLM's pipeline. ChatGPT cuts your text into tokens BEFORE it does anything with it.</li>\n<li><strong>TF-IDF and BoW</strong> are the foundations of thinking about text as numbers. Without that idea, that words can be &quot;counted&quot;, there would be no representation learning.</li>\n<li><strong>N-grams and Markov chains</strong> are the prototype of next-token prediction. That's exactly what an LLM does - except an LLM has a context of thousands of tokens, not 2-3.</li>\n<li><strong>Naive Bayes</strong> showed that a probabilistic approach to text works surprisingly well. An LLM is also a probabilistic model - it predicts the probability of the next token.</li>\n<li><strong>Word2Vec</strong> is the ancestor of what we today call the &quot;embedding layer&quot; in Transformers. GPT no longer uses Word2Vec as a separate step, but its embedding layer realizes the same idea: token -&gt; vector.</li>\n</ul>\n<p><strong>The LLM didn't fall from the sky. It stands on the shoulders of giants.</strong></p>\n<div class=\"markdown-alert markdown-alert-important\">\n<p class=\"markdown-alert-title\">Important</p>\n<p><strong>What's in the next post?</strong> We have word vectors - but how do we build something out of them that understands <strong>whole sentences</strong>? In the <a href=\"from-neurons-to-memory.html\">next post</a> we'll walk the whole path from a single neuron (perceptron, 1958), through MLP, RNN with the forgetting problem, to LSTM with memory gates. The evolution of architectures that prepared the ground for <strong>Transformers</strong> and ChatGPT. Stay tuned!</p>\n</div>\n<hr />\n<p>I know this post is a lot, but I wanted the path from &quot;cutting text&quot; to &quot;meaning vectors&quot; to be complete and understandable.</p>\n<p>I hope you'll say &quot;aha!&quot; at least a few times. If anything is unclear - <strong>let me know in the comments</strong>, I'll try to explain. And if you have better Word2Vec analogy examples - even more reason to let me know.</p>\n<p>Which method surprised you the most? Did you know that ChatGPT &quot;thinks&quot; in tokens, not words? And that its &quot;thinking&quot; is really a descendant of Markov chains?</p>\n<p>See you next time!</p>\n<hr />\n<p><strong>Sources and interesting links:</strong></p>\n<p>If you want to go deeper, here are the materials I used:</p>\n<ul>\n<li><a href=\"https://huggingface.co/learn/llm-course/chapter6/5\">Byte-Pair Encoding tokenization - Hugging Face</a> - a great, step-by-step introduction to BPE with code</li>\n<li><a href=\"https://huggingface.co/docs/transformers/en/tokenizer_summary\">Tokenization algorithms - Hugging Face</a> - an overview of BPE, WordPiece, Unigram, SentencePiece</li>\n<li><a href=\"https://sebastianraschka.com/blog/2025/bpe-from-scratch.html\">BPE Tokenizer From Scratch - Sebastian Raschka</a> - a BPE implementation from scratch in Python, very educational</li>\n<li><a href=\"https://codesignal.com/learn/courses/foundations-of-nlp-data-processing-2/lessons/introduction-to-tf-idf-vectorization-in-nlp\">Introduction to TF-IDF Vectorization in NLP - CodeSignal</a> - a readable introduction to TF-IDF with examples</li>\n<li><a href=\"https://stackabuse.com/python-for-nlp-developing-an-automatic-text-filler-using-n-grams/\">Python for NLP: Developing an Automatic Text Filler using N-grams - StackAbuse</a> - a great article on n-grams with a text generator</li>\n<li><a href=\"https://medium.com/in-pursuit-of-artificial-intelligence/brief-introduction-to-n-gram-and-tf-idf-tokenization-e58d22555bab\">Brief Introduction to N-gram and TF-IDF - Medium</a> - a comparison of TF-IDF vs n-grams with code</li>\n<li><a href=\"https://www.baeldung.com/cs/word-embeddings-cbow-vs-skip-gram\">Word Embeddings: CBOW vs Skip-Gram - Baeldung</a> - a clear comparison of both Word2Vec architectures</li>\n<li><a href=\"https://medium.com/data-science/nlp-101-word2vec-skip-gram-and-cbow-93512ee24314\">NLP 101: Word2Vec - Skip-gram and CBOW - Medium</a> - good pictures and intuition for Word2Vec</li>\n<li><a href=\"https://aclanthology.org/2020.aespen-1.6/\">TF-IDF Character N-grams versus Word Embedding-based Models - ACL Anthology</a> - a contrast between classic features and embeddings</li>\n<li><a href=\"https://projector.tensorflow.org/\">TensorFlow Embedding Projector</a> - an interactive visualization of the vector space (mandatory!)</li>\n</ul>\n<section class=\"footnotes\" data-footnotes>\n<ol>\n<li id=\"fn-1\">\n<p>Tokenization quiz answers: 1) &quot;lowest&quot; -&gt; [&quot;low&quot;, &quot;est&quot;] (l+o-&gt;lo, lo+w-&gt;low; &quot;e&quot; and &quot;s&quot; might be merged to &quot;es&quot;, then &quot;est&quot;? depends on the merges - with our merges &quot;es&quot; exists, so &quot;lowest&quot; -&gt; [&quot;low&quot;, &quot;es&quot;, &quot;t&quot;]). 2) &quot;newer&quot; -&gt; [&quot;new&quot;, &quot;er&quot;] (n+e-&gt;ne, ne+w-&gt;new; &quot;er&quot; is not merged in our dictionary). 3) &quot;widower&quot; -&gt; [&quot;w&quot;, &quot;i&quot;, &quot;d&quot;, &quot;o&quot;, &quot;w&quot;, &quot;er&quot;] or similar - &quot;widow&quot; isn't a merge we have, so it gets split (w+i+d+ow... but &quot;ow&quot; isn't merged either, only &quot;lo&quot;). The exact split depends on which merges made it into the dictionary. <a href=\"#fnref-1\" class=\"footnote-backref\" data-footnote-backref data-footnote-backref-idx=\"1\" aria-label=\"Back to reference 1\">↩</a></p>\n</li>\n</ol>\n</section>\n",
      "summary": "\"From tokenization through TF-IDF and Markov chains, to Word2Vec. How a computer turns text into numbers so that language models can process it.\"",
      "date_published": "2026-06-09T00:00:00-00:00",
      "image": "",
      "authors": [
        {
          "name": "Blazej Gruszka",
          "url": "https://www.linkedin.com/in/blazejgruszka/",
          "avatar": "https://github.com/bgruszka.png"
        }
      ],
      "tags": [
        "llm",
        "ai",
        "nlp",
        "tokenization",
        "word2vec",
        "embeddings",
        "tf-idf",
        "markov",
        "bayes",
        "language-models"
      ],
      "language": "en"
    },
    {
      "id": "https://gruszka.dev/en/linguistic-features-and-llm.html",
      "url": "https://gruszka.dev/en/linguistic-features-and-llm.html",
      "title": "Linguistic features - what you need to know before you understand how an LLM thinks",
      "content_html": "<p>LLMs (Large Language Models, i.e. ChatGPT, Claude, Gemini and friends) have been with us for a while now, and I decided to dig deeper into the topic, and one big question came up: <strong>but how does it even work that a model &quot;understands&quot; language?</strong> After all, underneath it's just &quot;math and statistics&quot;, right? Well... yes and no ;-)</p>\n<p>And then I stumbled into linguistics. And it turned out that to truly understand what an LLM does with language, you first need to understand what language is made of. And since a programmer trying to read about linguistics is a rather funny picture, I thought: <strong>I'll share it with you</strong> ;-)</p>\n<p>This is the <strong>first post in a series</strong> in which we'll survey different levels of language analysis and see how it all connects to LLMs. In today's post we'll build the foundation - we'll get to know the five main layers of language and see why each of them matters for language models.</p>\n<hr />\n<h2><a href=\"#language-is-like-an-onion---layer-upon-layer\" aria-hidden=\"true\" class=\"anchor\" id=\"language-is-like-an-onion---layer-upon-layer\"></a>Language is like an onion - layer upon layer</h2>\n<p>Before we get into the details, imagine language as a sort of... onion. Or a layer cake, if you prefer sweet metaphors :D</p>\n<p>Each layer is a different level on which language &quot;works&quot;. From the lowest - sounds - to the highest - what we <em>really</em> meant to say, even when we said something completely different.</p>\n<div class=\"mermaid-diagram\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"282.9914\" height=\"647\" viewBox=\"0 0 282.9914 647\"><rect x=\"0\" y=\"0\" width=\"282.9914\" height=\"647\" fill=\"#FFFFFF\"/><defs><marker id=\"arrow-0\" viewBox=\"0 0 10 10\" refX=\"5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker><marker id=\"arrow-start-0\" viewBox=\"0 0 10 10\" refX=\"4.5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 5 L 10 10 L 10 0 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker></defs><path id=\"edge-0\" class=\"edgePath\" data-edge-id=\"edge-0\" d=\"M 137.494,101.000 L 137.494,151.000\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(137.49 151.00) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-1\" class=\"edgePath\" data-edge-id=\"edge-1\" d=\"M 137.490,223.000 L 137.490,273.000\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(137.49 273.00) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-2\" class=\"edgePath\" data-edge-id=\"edge-2\" d=\"M 137.486,366.000 L 137.486,416.000\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(137.49 416.00) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-3\" class=\"edgePath\" data-edge-id=\"edge-3\" d=\"M 137.484,488.000 L 137.484,538.000\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(137.48 538.00) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><rect x=\"8.00\" y=\"8.00\" width=\"258.99\" height=\"93.00\" rx=\"3\" ry=\"3\" fill=\"#ff9999\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"137.50\" y=\"37.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"137.50\" dy=\"0.00\">🔊 Phonetics &amp; phonology</tspan><tspan x=\"137.50\" dy=\"21.00\">&lt;i&gt;sounds and the</tspan><tspan x=\"137.50\" dy=\"21.00\">system of sounds&lt;/i&gt;</tspan></text><rect x=\"16.65\" y=\"151.00\" width=\"241.69\" height=\"72.00\" rx=\"3\" ry=\"3\" fill=\"#ffcc99\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"137.49\" y=\"180.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"137.49\" dy=\"0.00\">🧩 Morphology</tspan><tspan x=\"137.49\" dy=\"21.00\">&lt;i&gt;word structure&lt;/i&gt;</tspan></text><rect x=\"51.25\" y=\"273.00\" width=\"172.47\" height=\"93.00\" rx=\"3\" ry=\"3\" fill=\"#ffff99\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"137.49\" y=\"302.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"137.49\" dy=\"0.00\">📐 Syntax</tspan><tspan x=\"137.49\" dy=\"21.00\">&lt;i&gt;sentence</tspan><tspan x=\"137.49\" dy=\"21.00\">structure&lt;/i&gt;</tspan></text><rect x=\"46.92\" y=\"416.00\" width=\"181.13\" height=\"72.00\" rx=\"3\" ry=\"3\" fill=\"#99ff99\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"137.48\" y=\"445.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"137.48\" dy=\"0.00\">💡 Semantics</tspan><tspan x=\"137.48\" dy=\"21.00\">&lt;i&gt;meaning&lt;/i&gt;</tspan></text><rect x=\"51.25\" y=\"538.00\" width=\"172.47\" height=\"93.00\" rx=\"3\" ry=\"3\" fill=\"#9999ff\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"137.48\" y=\"567.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"137.48\" dy=\"0.00\">🎭 Pragmatics</tspan><tspan x=\"137.48\" dy=\"21.00\">&lt;i&gt;meaning in</tspan><tspan x=\"137.48\" dy=\"21.00\">context&lt;/i&gt;</tspan></text></svg></div>\n<p>Five layers, five ways in which language gets &quot;analyzed&quot;. And note - <strong>an LLM has to handle each of these levels</strong> to come across as a sensible conversational partner. Of course it doesn't do this consciously - but the structures it learns during training somehow &quot;catch&quot; these layers.</p>\n<div class=\"markdown-alert markdown-alert-note\">\n<p class=\"markdown-alert-title\">Note</p>\n<p><strong>The five layers of language in a nutshell:</strong></p>\n<ol>\n<li><strong>Phonetics &amp; phonology</strong> - sounds and how they're organized into a system</li>\n<li><strong>Morphology</strong> - how words are built from smaller pieces</li>\n<li><strong>Syntax</strong> - the rules of word order in a sentence</li>\n<li><strong>Semantics</strong> - the meaning of words and sentences</li>\n<li><strong>Pragmatics</strong> - how context changes the meaning of an utterance</li>\n</ol>\n</div>\n<p>OK, let's get to it. We start at the bottom of our onion ;-)</p>\n<hr />\n<h2><a href=\"#phonetics-and-phonology---the-sounds-an-llm-never-hears\" aria-hidden=\"true\" class=\"anchor\" id=\"phonetics-and-phonology---the-sounds-an-llm-never-hears\"></a>Phonetics and phonology - the sounds an LLM never hears</h2>\n<h3><a href=\"#what-is-phonetics\" aria-hidden=\"true\" class=\"anchor\" id=\"what-is-phonetics\"></a>What is phonetics?</h3>\n<p><strong>Phonetics</strong> studies speech sounds as physical phenomena. Put simply: how we produce sounds, how they travel through the air, and how we receive them.</p>\n<p>It has three branches:</p>\n<table>\n<thead>\n<tr>\n<th>Branch</th>\n<th>What it studies</th>\n<th>Example</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>Articulatory</strong></td>\n<td>How the speech organs produce sounds</td>\n<td>Where is your tongue when you say &quot;sh&quot;?</td>\n</tr>\n<tr>\n<td><strong>Acoustic</strong></td>\n<td>The physical properties of sound waves</td>\n<td>Frequency, amplitude</td>\n</tr>\n<tr>\n<td><strong>Auditory</strong></td>\n<td>How the ear and brain receive sounds</td>\n<td>How the cochlea turns waves into a nerve signal</td>\n</tr>\n</tbody>\n</table>\n<h3><a href=\"#and-phonology\" aria-hidden=\"true\" class=\"anchor\" id=\"and-phonology\"></a>And phonology?</h3>\n<p><strong>Phonology</strong> is a level up. It's not interested in sounds as physical phenomena, but in <strong>how sounds are organized in a particular language</strong>.</p>\n<p>Key concept: the <strong>phoneme</strong> - the smallest unit of sound that distinguishes the meaning of words.</p>\n<p>A simple example in English:</p>\n<ul>\n<li><strong>pat</strong> vs <strong>bat</strong> - the words differ by one sound (/p/ vs /b/), and the meaning is completely different</li>\n<li><strong>rat</strong> vs <strong>mat</strong> - /r/ vs /m/ again changes everything</li>\n</ul>\n<p>Or in Polish:</p>\n<ul>\n<li><strong>kot</strong> (cat) vs <strong>lot</strong> (flight) - /k/ vs /l/ and we have a totally different word</li>\n</ul>\n<div class=\"markdown-alert markdown-alert-tip\">\n<p class=\"markdown-alert-title\">Tip</p>\n<p><strong>Experiment:</strong> Say &quot;cat&quot; out loud, once as a plain statement (&quot;My cat sleeps.&quot;), and once as a surprised question (&quot;Cat?!&quot; with raised eyebrows). The same word, but intonation, stress, and melody completely change what the listener &quot;hears&quot;. That is phonology in action - sounds + their organization in a system.</p>\n</div>\n<h3><a href=\"#why-does-this-matter-for-an-llm\" aria-hidden=\"true\" class=\"anchor\" id=\"why-does-this-matter-for-an-llm\"></a>Why does this matter for an LLM?</h3>\n<p>Right - <strong>an LLM gets text, not sound</strong>. It has never heard pronunciation. And yet...</p>\n<ul>\n<li>it knows that &quot;cat&quot; and &quot;hat&quot; rhyme (because it saw it in texts)</li>\n<li>it handles puns and wordplay based on sounds</li>\n<li>it can write rhyming poetry</li>\n</ul>\n<p>So the model somehow <strong>absorbs phonological information</strong> from text data alone. A bit like never having seen the ocean, but having read so many books about it that you can describe it ;-)</p>\n<p>But of course there are limitations. A text-based LLM can't distinguish homophones (words that sound the same but mean different things) based on phonetics - it handles them purely at the semantic level (more on that in a moment).</p>\n<p>And a fun fact: models like <strong>Whisper</strong> (speech-to-text from OpenAI) already connect phonetics with text. But that's a topic for a separate post in this series ;-)</p>\n<div class=\"markdown-alert markdown-alert-warning\">\n<p class=\"markdown-alert-title\">Warning</p>\n<p><strong>Paradox:</strong> An LLM has never heard a single sound, yet from training data it &quot;knows&quot; about rhymes, wordplay, and sound patterns. It's a bit like a person deaf from birth who can write rhyming poetry - because they've read millions of them.</p>\n</div>\n<hr />\n<h2><a href=\"#morphology---how-words-are-built-from-blocks\" aria-hidden=\"true\" class=\"anchor\" id=\"morphology---how-words-are-built-from-blocks\"></a>Morphology - how words are built from blocks</h2>\n<h3><a href=\"#what-is-morphology\" aria-hidden=\"true\" class=\"anchor\" id=\"what-is-morphology\"></a>What is morphology?</h3>\n<p><strong>Morphology</strong> studies the structure of words - how they're assembled from smaller, meaningful pieces called <strong>morphemes</strong>.</p>\n<p>A morpheme is the <strong>smallest unit of language that carries meaning</strong>. It can't be divided into anything smaller that would still carry meaning.</p>\n<p>An example from English, often used in textbooks:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">unhappiness = un + happy + ness\n               ↑       ↑       ↑\n            &quot;not&quot;   &quot;happy&quot;   makes a noun\n</code></pre>\n<p>Or in Polish:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">nieszczęśliwy = nie + szczęśliw + y\n                   ↑        ↑       ↑\n               &quot;not&quot;  &quot;happy/fortune&quot;  masculine gender\n</code></pre>\n<p>Or even more:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">nieprzystojny = nie + przy + stoj + ny\n              &quot;not&quot; + &quot;at&quot; + &quot;stand&quot; + adjective suffix\n</code></pre>\n<p>Each of these pieces has its own meaning. Those are morphemes.</p>\n<h3><a href=\"#two-kinds-of-morphemes\" aria-hidden=\"true\" class=\"anchor\" id=\"two-kinds-of-morphemes\"></a>Two kinds of morphemes</h3>\n<table>\n<thead>\n<tr>\n<th>Type</th>\n<th>Description</th>\n<th>Examples</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>Free</strong></td>\n<td>Can stand alone as a word</td>\n<td>cat, house, runs</td>\n</tr>\n<tr>\n<td><strong>Bound</strong></td>\n<td>Must be attached to another morpheme</td>\n<td>un-, -ness, -s, -ed</td>\n</tr>\n</tbody>\n</table>\n<p>And among bound morphemes we have <strong>affixes</strong>:</p>\n<ul>\n<li><strong>Prefixes</strong> (before the root): <em>un-</em>, <em>re-</em>, <em>pre-</em>, <em>dis-</em></li>\n<li><strong>Suffixes</strong> (after the root): <em>-ness</em>, <em>-ful</em>, <em>-ize</em>, <em>-ed</em></li>\n<li><strong>Infixes</strong> (inside the root): practically absent in English, but present in other languages! E.g. in Tagalog (Philippines): <em>sulat</em> (to write) -&gt; <em>s<strong>um</strong>ulat</em> (to write something) - the morpheme <em>-um-</em> is inserted into the middle of the root. Or think of the English <em>abso-bloody-lutely</em> ;-)</li>\n</ul>\n<h3><a href=\"#inflection-vs-derivation\" aria-hidden=\"true\" class=\"anchor\" id=\"inflection-vs-derivation\"></a>Inflection vs derivation</h3>\n<p>This is an important distinction:</p>\n<p><strong>Inflection</strong> - changes the grammatical form, but doesn't create a new word:</p>\n<ul>\n<li>house -&gt; house<strong>s</strong> (plural)</li>\n<li>run -&gt; ran (past tense)</li>\n</ul>\n<p><strong>Derivation</strong> (word formation) - creates a new word, often changing the part of speech:</p>\n<ul>\n<li>teach -&gt; <strong>teacher</strong> (verb -&gt; noun)</li>\n<li>house -&gt; <strong>household</strong> (noun -&gt; adjective/noun)</li>\n</ul>\n<div class=\"markdown-alert markdown-alert-warning\">\n<p class=\"markdown-alert-title\">Warning</p>\n<p><strong>Polish morphology is a nightmare for an LLM!</strong> The Polish language is highly inflectional - we have 7 cases, 2 numbers, 3 genders, verb aspects, and tons of exceptions. For comparison: in English, declining a noun means at most adding <em>-s</em>. In Polish? &quot;Dom, domu, domowi, dom, domem, domu, domie, domy, domow...&quot; and so on :D</p>\n</div>\n<h3><a href=\"#how-does-an-llm-handle-morphology\" aria-hidden=\"true\" class=\"anchor\" id=\"how-does-an-llm-handle-morphology\"></a>How does an LLM handle morphology?</h3>\n<p>Here's a fun fact. An LLM doesn't &quot;know&quot; what morphemes are. It uses <strong>BPE tokenization</strong> (Byte Pair Encoding), which splits text into tokens - but tokens are <strong>not the same</strong> as morphemes.</p>\n<div class=\"mermaid-diagram\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"542.85754\" height=\"335\" viewBox=\"0 0 542.85754 335\"><rect x=\"0\" y=\"0\" width=\"542.85754\" height=\"335\" fill=\"#FFFFFF\"/><defs><marker id=\"arrow-0\" viewBox=\"0 0 10 10\" refX=\"5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker><marker id=\"arrow-start-0\" viewBox=\"0 0 10 10\" refX=\"4.5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 5 L 10 10 L 10 0 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker></defs><rect x=\"8.00\" y=\"176.50\" width=\"518.86\" height=\"142.50\" rx=\"10\" ry=\"10\" fill=\"#F1F5F9\" stroke=\"#CBD5E1\" stroke-width=\"1\" /><text x=\"267.43\" y=\"205.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"267.43\" dy=\"0.00\">GPT-4o sees (BPE</tspan><tspan x=\"267.43\" dy=\"21.00\">tokenization):</tspan></text><rect x=\"8.00\" y=\"8.00\" width=\"492.90\" height=\"118.50\" rx=\"10\" ry=\"10\" fill=\"#F1F5F9\" stroke=\"#CBD5E1\" stroke-width=\"1\" /><text x=\"254.45\" y=\"35.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"254.45\" dy=\"0.00\">A linguist sees:</tspan></text><path id=\"edge-0\" class=\"edgePath\" data-edge-id=\"edge-0\" d=\"M 207.822,74.000 L 257.822,74.000\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(257.82 74.00) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-1\" class=\"edgePath\" data-edge-id=\"edge-1\" d=\"M 199.170,266.500 L 257.822,266.500\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(257.82 266.50) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><rect x=\"44.00\" y=\"48.50\" width=\"163.82\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"125.91\" y=\"77.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"125.91\" dy=\"0.00\">un-happiness</tspan></text><rect x=\"257.82\" y=\"48.50\" width=\"207.08\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#99ff99\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"361.36\" y=\"77.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"361.36\" dy=\"0.00\">un + happy + ness</tspan></text><rect x=\"44.00\" y=\"241.00\" width=\"155.17\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"121.58\" y=\"270.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"121.58\" dy=\"0.00\">unhappiness</tspan></text><rect x=\"257.82\" y=\"241.00\" width=\"233.04\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#ff9999\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"374.34\" y=\"270.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"374.34\" dy=\"0.00\">un + h + app + iness</tspan></text></svg></div>\n<p>The BPE tokenizer splits text based on <strong>frequency of occurrence</strong> in the training data, not on linguistic structure. Sometimes that overlaps with morphemes, sometimes it doesn't.</p>\n<p>That means the model doesn't learn morphology &quot;directly&quot; - it learns it indirectly, through statistics. And somehow it works ;-)</p>\n<div class=\"markdown-alert markdown-alert-tip\">\n<p class=\"markdown-alert-title\">Tip</p>\n<p><strong>A challenge for you:</strong> Can you break these words into morphemes?</p>\n<ul>\n<li><em>unbelievable</em></li>\n<li><em>antidisestablishmentarianism</em></li>\n<li><em>reimplementation</em></li>\n</ul>\n</div>\n<hr />\n<h2><a href=\"#syntax---the-rules-of-the-sentence-arranging-game\" aria-hidden=\"true\" class=\"anchor\" id=\"syntax---the-rules-of-the-sentence-arranging-game\"></a>Syntax - the rules of the sentence-arranging game</h2>\n<h3><a href=\"#what-is-syntax\" aria-hidden=\"true\" class=\"anchor\" id=\"what-is-syntax\"></a>What is syntax?</h3>\n<p><strong>Syntax</strong> is the set of rules that determine how words are combined into sentences. Thanks to syntax we know that &quot;The cat sits on the mat&quot; is a valid sentence, and &quot;The cat the on mat sits&quot; is gibberish.</p>\n<p>Simple? Well... more or less ;-)</p>\n<h3><a href=\"#word-order\" aria-hidden=\"true\" class=\"anchor\" id=\"word-order\"></a>Word order</h3>\n<p>Different languages have different word-order rules:</p>\n<table>\n<thead>\n<tr>\n<th>Language</th>\n<th>Basic order</th>\n<th>Example</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>English</td>\n<td>SVO (Subject-Verb-Object)</td>\n<td>The cat sits on the mat</td>\n</tr>\n<tr>\n<td>Polish</td>\n<td>&quot;Flexible&quot; SVO, but...</td>\n<td>Kot siedzi na macie / Na macie siedzi kot / Siedzi kot na macie</td>\n</tr>\n<tr>\n<td>Japanese</td>\n<td>SOV</td>\n<td>猫がマットの上に座っている</td>\n</tr>\n<tr>\n<td>Latin</td>\n<td>Free (because endings say everything)</td>\n<td>Felix in tapete sedet</td>\n</tr>\n</tbody>\n</table>\n<p>Polish is nice because we have quite a lot of freedom in word order. While in English &quot;On the mat sits the cat&quot; sounds poetic or unnatural, for us it's a normal sentence ;-)</p>\n<h3><a href=\"#the-syntax-tree\" aria-hidden=\"true\" class=\"anchor\" id=\"the-syntax-tree\"></a>The syntax tree</h3>\n<p>Sentences have a hierarchical structure - words group into phrases, and phrases into larger phrases. You can draw it as a tree:</p>\n<div class=\"mermaid-diagram\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"791.7824\" height=\"479\" viewBox=\"0 0 791.7824 479\"><rect x=\"0\" y=\"0\" width=\"791.7824\" height=\"479\" fill=\"#FFFFFF\"/><defs><marker id=\"arrow-0\" viewBox=\"0 0 10 10\" refX=\"5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker><marker id=\"arrow-start-0\" viewBox=\"0 0 10 10\" refX=\"4.5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 5 L 10 10 L 10 0 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker></defs><path id=\"edge-0\" class=\"edgePath\" data-edge-id=\"edge-0\" d=\"M 223.401,59.000 L 223.401,68.625 Q 223.401,76.500 215.799,78.553 L 175.463,89.447 Q 167.860,91.500 167.860,99.375 L 167.860,109.000\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(167.86 109.00) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-1\" class=\"edgePath\" data-edge-id=\"edge-1\" d=\"M 343.582,59.000 L 343.582,68.625 Q 343.582,76.500 351.163,78.631 L 389.363,89.369 Q 396.945,91.500 396.945,99.375 L 396.945,109.000\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(396.94 109.00) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-2\" class=\"edgePath\" data-edge-id=\"edge-2\" d=\"M 104.330,160.000 L 104.330,170.286 Q 104.330,177.500 101.785,184.250 L 101.219,185.750 Q 98.674,192.500 98.674,199.714 L 98.674,210.000\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(98.67 210.00) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-3\" class=\"edgePath\" data-edge-id=\"edge-3\" d=\"M 424.479,160.000 L 424.479,169.625 Q 424.479,177.500 417.210,180.528 L 395.732,189.472 Q 388.462,192.500 388.462,200.375 L 388.462,210.000\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(388.46 210.00) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-4\" class=\"edgePath\" data-edge-id=\"edge-4\" d=\"M 504.072,160.000 L 504.072,169.625 Q 504.072,177.500 511.094,181.065 L 526.591,188.935 Q 533.612,192.500 533.612,200.375 L 533.612,210.000\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(533.61 210.00) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-5\" class=\"edgePath\" data-edge-id=\"edge-5\" d=\"M 541.823,261.000 L 541.823,270.625 Q 541.823,278.500 535.962,283.760 L 530.969,288.240 Q 525.108,293.500 525.108,301.375 L 525.108,311.000\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(525.11 311.00) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-6\" class=\"edgePath\" data-edge-id=\"edge-6\" d=\"M 629.612,261.000 L 629.612,270.625 Q 629.612,278.500 636.207,282.804 L 645.999,289.196 Q 652.594,293.500 652.594,301.375 L 652.594,311.000\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(652.59 311.00) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-7\" class=\"edgePath\" data-edge-id=\"edge-7\" d=\"M 676.678,362.000 L 676.678,412.000\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(676.68 412.00) rotate(90.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><rect x=\"35.51\" y=\"210.00\" width=\"120.56\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"95.79\" y=\"239.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"95.79\" dy=\"0.00\">The cat</tspan></text><rect x=\"616.51\" y=\"412.00\" width=\"120.56\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"676.79\" y=\"441.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"676.79\" dy=\"0.00\">the mat</tspan></text><rect x=\"8.00\" y=\"109.00\" width=\"198.43\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#99ff99\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"107.21\" y=\"138.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"107.21\" dy=\"0.00\">NP (noun phrase)</tspan></text><rect x=\"577.35\" y=\"311.00\" width=\"198.43\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"676.57\" y=\"340.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"676.57\" dy=\"0.00\">NP (noun phrase)</tspan></text><rect x=\"448.67\" y=\"311.00\" width=\"77.44\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"487.39\" y=\"340.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"487.39\" dy=\"0.00\">on</tspan></text><rect x=\"443.46\" y=\"210.00\" width=\"276.29\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#ff9999\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"581.61\" y=\"239.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"581.61\" dy=\"0.00\">PP (prepositional phrase)</tspan></text><rect x=\"202.14\" y=\"8.00\" width=\"163.82\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#9999ff\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"284.05\" y=\"37.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#fff\"><tspan x=\"284.05\" dy=\"0.00\">S (sentence)</tspan></text><rect x=\"296.78\" y=\"210.00\" width=\"94.61\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"344.09\" y=\"239.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"344.09\" dy=\"0.00\">sits</tspan></text><rect x=\"357.26\" y=\"109.00\" width=\"198.43\" height=\"51.00\" rx=\"3\" ry=\"3\" fill=\"#ffcc99\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"456.48\" y=\"138.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#000\"><tspan x=\"456.48\" dy=\"0.00\">VP (verb phrase)</tspan></text></svg></div>\n<p>That's the simple sentence &quot;The cat sits on the mat&quot; and it already has its hierarchy! Now imagine sentences with subordinate clauses, participles, adverbials...</p>\n<h3><a href=\"#colorless-green-ideas-sleep-furiously\" aria-hidden=\"true\" class=\"anchor\" id=\"colorless-green-ideas-sleep-furiously\"></a>&quot;Colorless green ideas sleep furiously&quot;</h3>\n<p>This is a famous example from Noam Chomsky.</p>\n<p>The sentence is <strong>syntactically correct</strong> - it has a subject, a predicate, a proper structure. But <strong>semantically</strong> it's complete nonsense - ideas can't be green or sleep.</p>\n<p>And that's exactly the proof that <strong>syntax and semantics are two different levels</strong>. You can have perfect syntax and zero sense ;-)</p>\n<div class=\"markdown-alert markdown-alert-important\">\n<p class=\"markdown-alert-title\">Important</p>\n<p><strong>Syntax is an LLM's superpower.</strong> The model learns to predict the next token based on a huge amount of text data. In the process it absorbs syntactic patterns - it knows that after &quot;The cat sits on...&quot; we expect a noun, and after &quot;Quickly...&quot; a verb. This is the foundation of why an LLM generates grammatically correct sentences.</p>\n</div>\n<h3><a href=\"#quiz-correct-or-not\" aria-hidden=\"true\" class=\"anchor\" id=\"quiz-correct-or-not\"></a>Quiz: correct or not?</h3>\n<p>Test yourself - which sentences are syntactically correct?<sup class=\"footnote-ref\"><a href=\"#fn-1\" id=\"fnref-1\" data-footnote-ref>1</a></sup></p>\n<ol>\n<li>The dog barks at the mailman.</li>\n<li>At the mailman barks the dog.</li>\n<li>Barks the dog at the mailman.</li>\n<li>The dog the mailman at barks.</li>\n<li>Does the dog bark at the mailman?</li>\n</ol>\n<hr />\n<h2><a href=\"#semantics---what-does-meaning-even-mean\" aria-hidden=\"true\" class=\"anchor\" id=\"semantics---what-does-meaning-even-mean\"></a>Semantics - what does &quot;meaning&quot; even mean?</h2>\n<h3><a href=\"#what-is-semantics\" aria-hidden=\"true\" class=\"anchor\" id=\"what-is-semantics\"></a>What is semantics?</h3>\n<p>If syntax asks &quot;how is this built?&quot;, semantics asks: <strong>&quot;what does it mean?&quot;</strong></p>\n<p>Semantics studies the meaning of words, phrases, and sentences. And right away it turns out this isn't simple at all.</p>\n<h3><a href=\"#polysemy---one-word-many-meanings\" aria-hidden=\"true\" class=\"anchor\" id=\"polysemy---one-word-many-meanings\"></a>Polysemy - one word, many meanings</h3>\n<p>A classic English example: <strong>bank</strong></p>\n<ul>\n<li>Bank - a financial institution (&quot;I deposited money at the bank&quot;)</li>\n<li>Bank - the side of a river (&quot;The river bank was muddy&quot;)</li>\n<li>Bank - a row of similar things (&quot;A bank of computers&quot;)</li>\n<li>Bank - to tilt an aircraft (&quot;The plane banked to the left&quot;)</li>\n</ul>\n<p>The same word, four completely different meanings. How does an LLM know which one is meant? <strong>From context.</strong> And that's exactly what it does well - because it has seen millions of sentences where &quot;bank&quot; appeared in different contexts.</p>\n<p>Or another classic - polysemy within a single sentence:</p>\n<blockquote>\n<p>&quot;I can can the canning of cans.&quot;</p>\n</blockquote>\n<p>The word &quot;can&quot; three times, three different functions: <strong>can</strong> (be able to), <strong>can</strong> (the verb, to put in a can), <strong>can</strong> (the noun, a container). Context changes everything ;-)</p>\n<h3><a href=\"#synonyms---are-big-large-and-huge-the-same\" aria-hidden=\"true\" class=\"anchor\" id=\"synonyms---are-big-large-and-huge-the-same\"></a>Synonyms - are &quot;big&quot;, &quot;large&quot;, and &quot;huge&quot; the same?</h3>\n<p>Almost. But not quite:</p>\n<ul>\n<li><strong>A big house</strong> - normal, just sizable</li>\n<li><strong>A large house</strong> - sounds a bit more formal</li>\n<li><strong>A huge house</strong> - now that's a mansion :D</li>\n</ul>\n<p>Semantics studies these subtle differences - both <strong>denotation</strong> (literal meaning) and <strong>connotation</strong> (associations, emotional coloring).</p>\n<h3><a href=\"#how-does-an-llm-understand-meaning\" aria-hidden=\"true\" class=\"anchor\" id=\"how-does-an-llm-understand-meaning\"></a>How does an LLM &quot;understand&quot; meaning?</h3>\n<p>Here enters the concept of <strong>word embeddings</strong>. In broad terms: each word is represented as a vector - a sequence of numbers - in a high-dimensional space. Words with similar meanings are &quot;close&quot; to each other in that space.</p>\n<p>The famous example you've probably seen:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">vector(&quot;king&quot;) - vector(&quot;man&quot;) + vector(&quot;woman&quot;) ≈ vector(&quot;queen&quot;)\n</code></pre>\n<p>Meaning: the model &quot;knows&quot; that the relationship between &quot;king&quot; and &quot;man&quot; is analogous to the relationship between &quot;queen&quot; and &quot;woman&quot;. And it got that purely from data - nobody taught it that!</p>\n<p>We can even show this in code. Here's a simple example with the <code>gensim</code> library in Python:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-python\"><a-k>from</a-k> <a-v>gensim</a-v>.<a-v>downloader</a-v> <a-k>import</a-k> <a-v>load</a-v>\n\n<a-v>model</a-v> <a-o>=</a-o> <a-f>load</a-f>(<a-s>&quot;glove-wiki-gigaword-50&quot;</a-s>)\n\n<a-v>result</a-v> <a-o>=</a-o> <a-v>model</a-v>.<a-pr>most_similar</a-pr>(\n    <a-v>positive</a-v><a-o>=</a-o>[<a-s>&quot;king&quot;</a-s>, <a-s>&quot;woman&quot;</a-s>],\n    <a-v>negative</a-v><a-o>=</a-o>[<a-s>&quot;man&quot;</a-s>],\n    <a-v>topn</a-v><a-o>=</a-o><a-n>3</a-n>\n)\n\n<a-f>print</a-f>(<a-v>result</a-v>)\n<a-c># [(&#39;queen&#39;, 0.85), ...]  -- in first place: queen!</a-c></code></pre>\n<div class=\"markdown-alert markdown-alert-tip\">\n<p class=\"markdown-alert-title\">Tip</p>\n<p><strong>If you want to test this yourself:</strong> install <code>gensim</code> (<code>pip install gensim</code>) and run the code above. Of course you'll need some RAM - the GloVe model isn't small. But the result is genuinely satisfying ;-)</p>\n</div>\n<h3><a href=\"#compositionality\" aria-hidden=\"true\" class=\"anchor\" id=\"compositionality\"></a>Compositionality</h3>\n<p>One of the most important concepts in semantics: <strong>the meaning of a sentence is the result of the meanings of the individual words + the rules for combining them.</strong></p>\n<p>So: &quot;The red cat sits on the mat&quot; = [red] + [cat] + [sits] + [on] + [the mat] + syntactic rules.</p>\n<p>Sounds trivial, but it's a powerful principle. Thanks to it we can understand an infinite number of sentences, even ones we've never heard before. And thanks to it an LLM can generate new, never-before-seen sentences that still make sense.</p>\n<div class=\"markdown-alert markdown-alert-warning\">\n<p class=\"markdown-alert-title\">Warning</p>\n<p><strong>But careful:</strong> &quot;I understood that you didn't understand that it wasn't understandable.&quot; - each word is simple, but the sentence as a whole requires analyzing it layer by layer. Compositionality is a powerful tool, but it has its limits.</p>\n</div>\n<hr />\n<h2><a href=\"#pragmatics---what-i-really-meant\" aria-hidden=\"true\" class=\"anchor\" id=\"pragmatics---what-i-really-meant\"></a>Pragmatics - what I really meant</h2>\n<h3><a href=\"#what-is-pragmatics\" aria-hidden=\"true\" class=\"anchor\" id=\"what-is-pragmatics\"></a>What is pragmatics?</h3>\n<p>And so we arrive at the most interesting layer (in my opinion). <strong>Pragmatics</strong> studies <strong>how context influences the meaning of an utterance</strong> - what we <em>really</em> want to say, often by saying something completely different.</p>\n<p>If semantics asks &quot;what does this mean?&quot;, pragmatics asks: <strong>&quot;what did the author mean in this particular situation?&quot;</strong></p>\n<h3><a href=\"#scenes-from-life\" aria-hidden=\"true\" class=\"anchor\" id=\"scenes-from-life\"></a>Scenes from life</h3>\n<p><strong>Scene 1: &quot;It's cold in here.&quot;</strong></p>\n<p>Semantically: information about the temperature in the room.\nPragmatically: <strong>&quot;Close the window!&quot;</strong> or <strong>&quot;Turn up the heating!&quot;</strong> or <strong>&quot;Give me a blanket.&quot;</strong></p>\n<p>Every one of you understands that when someone says &quot;It's cold in here&quot; while standing next to an open window - you don't ask about degrees Celsius, you just close the window.</p>\n<p><strong>Scene 2: &quot;Could you pass the salt?&quot;</strong></p>\n<p>Semantically: a question about your <em>ability</em> to pass the salt.\nPragmatically: <strong>a request to pass the salt.</strong></p>\n<p>Answering &quot;Yes, I could&quot; (and nothing more) is semantically correct, but pragmatically... well, you're being a bit rude ;-)</p>\n<p><strong>Scene 3: Irony</strong></p>\n<blockquote>\n<p>&quot;Well congratulations, you broke it again.&quot;</p>\n</blockquote>\n<p>Semantically: congratulations.\nPragmatically: <strong>irony, criticism, disappointment.</strong></p>\n<p>Does an LLM catch irony? Sometimes yes, sometimes no. It's still an open research problem.</p>\n<h3><a href=\"#speech-act-theory\" aria-hidden=\"true\" class=\"anchor\" id=\"speech-act-theory\"></a>Speech act theory</h3>\n<p>The philosopher J.L. Austin came up with something brilliant: <strong>utterances don't just describe reality, they also <em>do</em> things.</strong></p>\n<table>\n<thead>\n<tr>\n<th>Type of speech act</th>\n<th>Description</th>\n<th>Example</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>Locutionary</strong></td>\n<td>The utterance itself</td>\n<td>You say &quot;I'm closing the window&quot;</td>\n</tr>\n<tr>\n<td><strong>Illocutionary</strong></td>\n<td>The intention of the utterance</td>\n<td>A promise to close the window</td>\n</tr>\n<tr>\n<td><strong>Perlocutionary</strong></td>\n<td>The effect on the listener</td>\n<td>The listener feels relieved</td>\n</tr>\n</tbody>\n</table>\n<p>Other examples of speech acts:</p>\n<ul>\n<li><strong>A promise:</strong> &quot;I promise I'll do it.&quot; - by saying it, you <em>perform</em> the act of promising</li>\n<li><strong>A declaration:</strong> &quot;I now pronounce you married.&quot; - someone must have the authority for it, but it works!</li>\n<li><strong>An apology:</strong> &quot;I'm sorry.&quot; - that's not a description of a state of affairs, it <em>is</em> the act of apologizing</li>\n</ul>\n<h3><a href=\"#grices-implicatures\" aria-hidden=\"true\" class=\"anchor\" id=\"grices-implicatures\"></a>Grice's implicatures</h3>\n<p>Paul Grice, another philosopher of language, noticed that conversation follows a <strong>cooperative principle</strong> - we say things that are relevant, true, clear, and on topic.</p>\n<p>When someone breaks that principle, we look for an <strong>implicature</strong> - a hidden meaning.</p>\n<p>Example:</p>\n<blockquote>\n<p>A: &quot;Are you going to the party?&quot;\nB: &quot;I have an exam tomorrow.&quot;</p>\n</blockquote>\n<p>B didn't answer &quot;yes&quot; or &quot;no&quot;. But from the context A understands: <strong>I'm not going, because I have to study.</strong> That's a conversational implicature.</p>\n<h3><a href=\"#how-does-an-llm-handle-pragmatics\" aria-hidden=\"true\" class=\"anchor\" id=\"how-does-an-llm-handle-pragmatics\"></a>How does an LLM handle pragmatics?</h3>\n<div class=\"markdown-alert markdown-alert-warning\">\n<p class=\"markdown-alert-title\">Warning</p>\n<p><strong>This is the hardest layer for an LLM.</strong> And that's not just my opinion - researchers all over the world are working on benchmarks that test the pragmatics of language models.</p>\n</div>\n<p>The problems:</p>\n<ul>\n<li><strong>Irony and sarcasm</strong> - the model often takes it literally</li>\n<li><strong>Implicatures</strong> - it doesn't always &quot;catch&quot; what's between the lines</li>\n<li><strong>Speech acts</strong> - it may not recognize whether someone is promising, asking, or threatening</li>\n<li><strong>Cultural context</strong> - what's polite in one culture is rude in another</li>\n</ul>\n<p>But on the other hand - GPT-4 and newer models are getting better and better. Why? Because <strong>the enormous amount of training data contains pragmatics in practice</strong> - dialogues from movies, books, internet forums. The model has &quot;seen&quot; millions of examples of irony, politeness, requests.</p>\n<div class=\"markdown-alert markdown-alert-tip\">\n<p class=\"markdown-alert-title\">Tip</p>\n<p><strong>An experiment for you:</strong> Open ChatGPT (or Claude, or whatever you have at hand) and run this short test:</p>\n<p>Tell the model:</p>\n<p><em>&quot;I just hammered my finger.&quot;</em></p>\n<p>Then, in a new conversation:</p>\n<p><em>&quot;Well congratulations, I hammered my finger again!&quot;</em></p>\n<p>See whether the model notices that in the second case &quot;congratulations&quot; is irony, or whether it starts calling an ambulance?</p>\n</div>\n<hr />\n<h2><a href=\"#summary---the-whole-onion-in-one-place\" aria-hidden=\"true\" class=\"anchor\" id=\"summary---the-whole-onion-in-one-place\"></a>Summary - the whole onion in one place</h2>\n<p>Here are our five layers, in a nutshell:</p>\n<table>\n<thead>\n<tr>\n<th>Layer</th>\n<th>What it studies</th>\n<th>Example</th>\n<th>How the LLM handles it</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>Phonetics &amp; phonology</strong></td>\n<td>Sounds and the system of sounds</td>\n<td>/kat/ vs /hat/</td>\n<td>Never hears them, but &quot;knows&quot; from text data</td>\n</tr>\n<tr>\n<td><strong>Morphology</strong></td>\n<td>Word structure from morphemes</td>\n<td>un-happy-ness</td>\n<td>BPE tokenization != morphemes, but it works somehow</td>\n</tr>\n<tr>\n<td><strong>Syntax</strong></td>\n<td>Word order in a sentence</td>\n<td>The cat sits on the mat</td>\n<td>The model's superpower - handles grammar great</td>\n</tr>\n<tr>\n<td><strong>Semantics</strong></td>\n<td>Meaning of words and sentences</td>\n<td>&quot;bank&quot; - which one?</td>\n<td>Word embeddings + context</td>\n</tr>\n<tr>\n<td><strong>Pragmatics</strong></td>\n<td>Meaning in context</td>\n<td>&quot;It's cold&quot; = close the window</td>\n<td>The hardest layer, still an open problem</td>\n</tr>\n</tbody>\n</table>\n<div class=\"mermaid-diagram\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1209.4475\" height=\"211.5\" viewBox=\"0 0 1209.4475 211.5\"><rect x=\"0\" y=\"0\" width=\"1209.4475\" height=\"211.5\" fill=\"#FFFFFF\"/><defs><marker id=\"arrow-0\" viewBox=\"0 0 10 10\" refX=\"5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker><marker id=\"arrow-start-0\" viewBox=\"0 0 10 10\" refX=\"4.5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M 0 5 L 10 10 L 10 0 z\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"1\" stroke-dasharray=\"1,0\"/></marker></defs><rect x=\"8.00\" y=\"8.00\" width=\"1185.45\" height=\"187.50\" rx=\"10\" ry=\"10\" fill=\"#F1F5F9\" stroke=\"#CBD5E1\" stroke-width=\"1\" /><text x=\"600.72\" y=\"37.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"600.72\" dy=\"0.00\">How an LLM processes</tspan><tspan x=\"600.72\" dy=\"21.00\">language</tspan></text><path id=\"edge-0\" class=\"edgePath\" data-edge-id=\"edge-0\" d=\"M 220.473,135.000 L 244.473,135.000\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(244.47 135.00) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-1\" class=\"edgePath\" data-edge-id=\"edge-1\" d=\"M 494.813,135.000 L 518.813,135.000\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(518.81 135.00) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-2\" class=\"edgePath\" data-edge-id=\"edge-2\" d=\"M 673.983,135.000 L 697.983,135.000\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(697.98 135.00) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><path id=\"edge-3\" class=\"edgePath\" data-edge-id=\"edge-3\" d=\"M 956.974,135.000 L 980.974,135.000\" fill=\"none\" stroke=\"#64748B\" stroke-width=\"2\"    stroke-linecap=\"round\" stroke-linejoin=\"round\" /><g transform=\"translate(980.97 135.00) rotate(0.00)\"><polygon points=\"0,0 -7.50,3.90 -7.50,-3.90\" fill=\"#64748B\" stroke=\"#64748B\" stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></g><rect x=\"48.00\" y=\"83.00\" width=\"172.47\" height=\"72.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"134.24\" y=\"112.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"134.24\" dy=\"0.00\">Phonetics</tspan><tspan x=\"134.24\" dy=\"21.00\">❌ never hears</tspan></text><rect x=\"244.47\" y=\"72.50\" width=\"250.34\" height=\"93.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"369.64\" y=\"101.50\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"369.64\" dy=\"0.00\">Morphology</tspan><tspan x=\"369.64\" dy=\"21.00\">⚠️ through the lens of</tspan><tspan x=\"369.64\" dy=\"21.00\">tokens</tspan></text><rect x=\"980.97\" y=\"83.00\" width=\"172.47\" height=\"72.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"1067.21\" y=\"112.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"1067.21\" dy=\"0.00\">Pragmatics</tspan><tspan x=\"1067.21\" dy=\"21.00\">❓ the hardest</tspan></text><rect x=\"518.81\" y=\"83.00\" width=\"155.17\" height=\"72.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"596.40\" y=\"112.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"596.40\" dy=\"0.00\">Syntax</tspan><tspan x=\"596.40\" dy=\"21.00\">✅ very well</tspan></text><rect x=\"697.98\" y=\"83.00\" width=\"258.99\" height=\"72.00\" rx=\"3\" ry=\"3\" fill=\"#F8FAFC\" stroke=\"#94A3B8\" stroke-width=\"1\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><text x=\"827.48\" y=\"112.00\" text-anchor=\"middle\" font-family=\"Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,DejaVu Sans,Liberation Sans,sans-serif,Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji\" font-size=\"14\" fill=\"#0F172A\"><tspan x=\"827.48\" dy=\"0.00\">Semantics</tspan><tspan x=\"827.48\" dy=\"21.00\">✅ well, with exceptions</tspan></text></svg></div>\n<h3><a href=\"#whats-next\" aria-hidden=\"true\" class=\"anchor\" id=\"whats-next\"></a>What's next?</h3>\n<p>In the next post we change perspective - instead of looking at the layers of language, we look at the very nature of signs and meaning:</p>\n<ul>\n<li><strong><a href=\"semiotics-and-llm.html\">Semiotics - why an LLM doesn't &quot;think&quot;, yet still means something</a></strong></li>\n</ul>\n<hr />\n<p>If you made it all the way here - <strong>thanks!</strong> ;-) I really appreciate that you took the time to read this monster.</p>\n<p>I hope I brought the topic a little closer to you. If anything is unclear - <strong>let me know in the comments</strong>, I'll try to explain. And if you have better examples (and you surely do!) - even more reason to let me know.</p>\n<p>Which layer surprised you the most? Which do you find the most interesting in the context of AI?</p>\n<p>See you in the next post!</p>\n<hr />\n<p><strong>Sources and interesting links:</strong></p>\n<p>If you want to go deeper, here are the materials I used when writing this post:</p>\n<ul>\n<li><a href=\"https://fiveable.me/introduction-study-language/unit-1/branches-linguistics/study-guide/Bbhz9eKIobWh0O9F\">Branches of linguistics - Fiveable</a> - a great overview of linguistics subfields with examples</li>\n<li><a href=\"https://fiveable.me/lists/levels-of-linguistic-analysis\">Levels of Linguistic Analysis - Fiveable</a> - a synthesis of the levels of language analysis</li>\n<li><a href=\"https://relay.libguides.com/science-of-teaching-reading-resource-guide/five-language-domains\">The Five Language Domains - Relay Graduate School</a> - a simple, didactic take on the five language domains</li>\n<li><a href=\"https://www.zenml.io/llmops-database/linguistic-informed-approach-to-production-llm-systems\">Linguistic-Informed Approach to Production LLM Systems - ZenML</a> - a bridge between linguistics and LLM practice</li>\n<li><a href=\"https://www.stat.lmu.de/soda/en/research/research-projects/evaluating-large-language-models-on-linguistic-competence/\">Evaluating Large Language Models on Linguistic Competence - LMU Munich</a> - how the linguistic competence of models is studied</li>\n<li><a href=\"https://arxiv.org/abs/2502.12378\">Pragmatics in the Era of LLMs: A Survey - arXiv</a> - a survey of research on pragmatics in LLMs</li>\n</ul>\n<section class=\"footnotes\" data-footnotes>\n<ol>\n<li id=\"fn-1\">\n<p>Quiz answers: sentences 1 and 5 are correct. Sentence 1 has standard SVO order. Sentence 5 is a grammatical question formed with the auxiliary &quot;does&quot;. Sentences 2 and 3 are understandable but sound archaic or poetic - English allows such inversions only in limited stylistic contexts, unlike Polish where flexible word order is the norm. Sentence 4 (&quot;The dog the mailman at barks&quot;) is syntactically incorrect - the word order is so jumbled it violates the rules of English grammar. <a href=\"#fnref-1\" class=\"footnote-backref\" data-footnote-backref data-footnote-backref-idx=\"1\" aria-label=\"Back to reference 1\">↩</a></p>\n</li>\n</ol>\n</section>\n",
      "summary": "\"Five layers of language - phonetics, morphology, syntax, semantics, pragmatics - and how an LLM handles each of them.\"",
      "date_published": "2026-06-06T00:00:00-00:00",
      "image": "",
      "authors": [
        {
          "name": "Blazej Gruszka",
          "url": "https://www.linkedin.com/in/blazejgruszka/",
          "avatar": "https://github.com/bgruszka.png"
        }
      ],
      "tags": [
        "llm",
        "ai",
        "nlp",
        "linguistics",
        "language-models",
        "chatgpt"
      ],
      "language": "en"
    }
  ]
}