{
  "version": "https://jsonfeed.org/version/1",
  "title": "gruszka.dev",
  "home_page_url": "https://gruszka.dev",
  "feed_url": "https://gruszka.dev/tag-language-models.json",
  "description": "Things I would like to share",
  "items": [
    {
      "id": "https://gruszka.dev/jak-komputer-czyta-tekst.html",
      "url": "https://gruszka.dev/jak-komputer-czyta-tekst.html",
      "title": "Jak komputer czyta tekst - od liczenia słów do wektorów",
      "content_html": "<div class=\"markdown-alert markdown-alert-tip\">\n<p class=\"markdown-alert-title\">Tip</p>\n<p><strong>Chcesz uruchomić ten kod samemu?</strong> Wszystkie przykłady z tego posta znajdziesz w gotowym notatniku Jupyter: <a href=\"./jak-komputer-czyta-tekst.ipynb\">jak-komputer-czyta-tekst.ipynb</a></p>\n</div>\n<p>W dwóch poprzednich wpisach zbudowaliśmy sobie solidny fundament. W <a href=\"cechy-jezykowe-a-llm.html\">pierwszym</a> rozłożyliśmy język na czynniki pierwsze - fonetyka, morfologia, składnia, semantyka, pragmatyka. W <a href=\"semiotyka-a-llm.html\">drugim</a> zapytaliśmy: &quot;OK, ale czy LLM w ogóle rozumie to, co generuje?&quot; i doszliśmy do wniosku, że LLM jest maszyną znaków, nie umysłem.</p>\n<p>Ale zostało mi jedno fundamentalne pytanie, na które nie odpowiedziałem: <strong>jak komputer w ogóle &quot;bierze&quot; tekst do środka?</strong></p>\n<p>Bo przecież komputer nie widzi liter. Nie widzi słów. Komputer widzi <strong>liczby</strong>. Więc jak to się dzieje, że wpisujesz &quot;Kot siedzi na macie&quot; i ChatGPT jakoś to przetwarza? Jaka jest droga od tekstu do liczby, od liczby do &quot;rozumienia&quot;?</p>\n<p>Ta droga to fascynująca historia ewolucji - od najprostszego liczenia słów, przez coraz sprytniejsze triki matematyczne, aż po wektory, które potrafią uchwycić podobieństwa znaczeniowe. Każdy krok na tej drodze był odpowiedzią na ograniczenia poprzedniego.</p>\n<p>To jest <strong>trzeci wpis z serii &quot;zrozumiec LLM&quot;</strong>. Dzisiaj zmieniamy perspektywę z lingwistycznej i filozoficznej na <strong>techniczną</strong>. Ale bez obaw - dalej będzie dużo przykładów, dużo &quot;aha!&quot; i zero wzorów, których nie da się zrozumieć ;-)</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph LR\n    T[&quot;✂️ Tokenizacja&lt;br/&gt;&lt;i&gt;tniemy tekst&lt;/i&gt;&quot;] --&gt; B[&quot;🔢 BoW / TF-IDF&lt;br/&gt;&lt;i&gt;liczymy słowa&lt;/i&gt;&quot;]\n    B --&gt; N[&quot;🔗 N-gramy / Markow&lt;br/&gt;&lt;i&gt;dodajemy kontekst&lt;/i&gt;&quot;]\n    N --&gt; NB[&quot;📊 Bayes&lt;br/&gt;&lt;i&gt;klasyfikujemy&lt;/i&gt;&quot;]\n    NB --&gt; W[&quot;📐 Word2Vec&lt;br/&gt;&lt;i&gt;wektory znaczeń&lt;/i&gt;&quot;]\n    \n    style T fill:#ff9999,color:#000\n    style B fill:#ffcc99,color:#000\n    style N fill:#ffff99,color:#000\n    style NB fill:#ccff99,color:#000\n    style W fill:#99ccff,color:#000\n</code></pre>\n<p>Narracja tego wpisu: <strong>&quot;Najpierw tniesz tekst na kawałki, potem liczysz, potem rozumiesz.&quot;</strong> Proste? Zobaczymy ;-) Zaczynamy od cięcia.</p>\n<hr />\n<h2><a href=\"#tokenizacja---jak-tekst-jest-dzielony-na-kawałki\" aria-hidden=\"true\" class=\"anchor\" id=\"tokenizacja---jak-tekst-jest-dzielony-na-kawałki\"></a>Tokenizacja - jak tekst jest dzielony na kawałki</h2>\n<h3><a href=\"#problem-komputer-nie-widzi-słów\" aria-hidden=\"true\" class=\"anchor\" id=\"problem-komputer-nie-widzi-słów\"></a>Problem: komputer nie widzi słów</h3>\n<p>Wyobraźcie sobie, że jesteście komputerem. Ktoś wam pokazuje tekst:</p>\n<blockquote>\n<p>&quot;Ala ma kota.&quot;</p>\n</blockquote>\n<p>Co widzicie? Literki? Słowa? Nie. Widzicie ciąg bajtów: <code>65 6c 61 20 6d 61 20 6b 6f 74 61 2e</code>. Zero pojęcia, gdzie zaczyna się jedno słowo, a kończy drugie. Zero pojęcia, że &quot;kot&quot; to zwierzak, a nie trzy losowe znaki.</p>\n<p>Żeby komputer cokolwiek mógł zrobić z tekstem, musi go najpierw <strong>pociąć na kawałki</strong>. I ten proces nazywa się <strong>tokenizacją</strong>.</p>\n<p>Tokenizacja to <strong>krok zerowy</strong>. Bez niej nie ma TF-IDF, nie ma n-gramów, nie ma embeddingów, nie ma LLM. Wszystko zaczyna się od cięcia.</p>\n<h3><a href=\"#naiwne-podejście-tnij-po-spacjach\" aria-hidden=\"true\" class=\"anchor\" id=\"naiwne-podejście-tnij-po-spacjach\"></a>Naiwne podejście: tnij po spacjach</h3>\n<p>Najprostszy pomysł: dzielimy tekst po spacjach.</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">&quot;Ala ma kota.&quot; → [&quot;Ala&quot;, &quot;ma&quot;, &quot;kota.&quot;]\n</code></pre>\n<p>Zauważyliście? &quot;kota.&quot; ma kropkę przyklejoną. To nie jest słowo &quot;kot&quot; w dopełniaczu - to jest słowo &quot;kota&quot; z interpunkcją. A co z takimi tekstami:</p>\n<ul>\n<li>&quot;nie-przy-stoj-ny&quot; → jedno słowo czy pięć?</li>\n<li>&quot;Cześć!&quot; → słowo + interpunkcja?</li>\n<li>&quot;New York&quot; → jedno słowo czy dwa?</li>\n<li>&quot;:) &quot; → słowo? znak? emocja?</li>\n</ul>\n<p>Krótko: <strong>cięcie po spacjach nie działa</strong>. Świat jest za skomplikowany na tak proste reguły.</p>\n<h3><a href=\"#token--słowo--morfem\" aria-hidden=\"true\" class=\"anchor\" id=\"token--słowo--morfem\"></a>Token ≠ słowo ≠ morfem</h3>\n<p>W <a href=\"cechy-jezykowe-a-llm.html\">pierwszym poście</a> poznaliśmy morfemy - najmniejsze jednostki znaczące. &quot;Nieszczęśliwy&quot; to trzy morfemy: &quot;nie-szczęśliw-y&quot;. A teraz uwaga: <strong>token to nie to samo co morfem.</strong></p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph LR\n    subgraph &quot;Lingwista widzi:&quot;\n        A[&quot;nieszczęśliwy&quot;] --&gt; A1[&quot;nie + szczęśliw + y&lt;br/&gt;&lt;i&gt;morfemy&lt;/i&gt;&quot;]\n    end\n    subgraph &quot;GPT-4o widzi (tokeny BPE):&quot;\n        B[&quot;nieszczęśliwy&quot;] --&gt; B1[&quot;nie + -s + zcz + ę + śli + wy&quot;]\n    end\n    \n    style A1 fill:#99ff99,color:#000\n    style B1 fill:#ff9999,color:#000\n</code></pre>\n<p>Token to po prostu <strong>kawałek tekstu</strong> wyznaczony przez tokenizator. Może być całym słowem, może być częścią słowa, może być pojedynczym znakiem. Zależy od algorytmu.</p>\n<div class=\"markdown-alert markdown-alert-note\">\n<p class=\"markdown-alert-title\">Note</p>\n<p><strong>Trzy poziomy cięcia tekstu:</strong></p>\n<ul>\n<li><strong>Słowo</strong> - jednostka, którą intuicyjnie &quot;czujesz&quot; (oddzielona spacjami)</li>\n<li><strong>Morfem</strong> - najmniejsza jednostka ZNACZĄCA w języku (nie-, szczęśliw-, -y)</li>\n<li><strong>Token</strong> - kawałek tekstu wyznaczony przez ALGORYTM komputerowy (nie, -s, zcz, ę, śli, wy)</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>I tu wchodzi <strong>BPE (Byte Pair Encoding)</strong> - najpopularniejszy algorytm tokenizacji, używany przez GPT-2, GPT-3, GPT-4, Llama, i wiele innych modeli.</p>\n<p>Ideę BPE można sprowadzić do jednego zdania: <strong>szukamy najczęstszej pary znaków i łączymy ją w jeden token. I powtarzamy.</strong></p>\n<p>Brzmi prosto? Bo jest proste ;-) Zobaczmy to na przykładzie.</p>\n<h4><a href=\"#bpe-krok-po-kroku\" aria-hidden=\"true\" class=\"anchor\" id=\"bpe-krok-po-kroku\"></a>BPE krok po kroku</h4>\n<p>Mamy mały korpus z pięcioma polskimi słowami i ich częstotliwościami:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">&quot;las&quot;  ×10     &quot;lak&quot;  ×5      &quot;lat&quot;  ×12\n&quot;bat&quot;  ×4      &quot;lasy&quot; ×5\n</code></pre>\n<p>(&quot;las&quot; = las, &quot;lak&quot; = lak do pieczęci, &quot;lat&quot; = lat (od &quot;rok&quot;), &quot;bat&quot; = bicz, &quot;lasy&quot; = po prostu lasy)</p>\n<p><strong>Krok 0:</strong> Dzielimy wszystko na pojedyncze znaki:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">l a s    ×10     l a k    ×5      l a t    ×12\nb a t    ×4      l a s y  ×5\n</code></pre>\n<p>Nasz początkowy słownik to: <code>[&quot;a&quot;, &quot;b&quot;, &quot;k&quot;, &quot;l&quot;, &quot;s&quot;, &quot;t&quot;, &quot;y&quot;]</code> - po prostu wszystkie unikalne znaki.</p>\n<p><strong>Krok 1:</strong> Znajdujemy najczęstszą parę sąsiadujących znaków:</p>\n<ul>\n<li>(l, a) występuje w &quot;las&quot; (10), &quot;lak&quot; (5), &quot;lat&quot; (12) i &quot;lasy&quot; (5) = <strong>32 razy</strong> ← zwycięzca!</li>\n<li>(a, t) występuje w &quot;lat&quot; (12) i &quot;bat&quot; (4) = 16 razy</li>\n<li>(a, s) występuje w &quot;las&quot; (10) i &quot;lasy&quot; (5) = 15 razy</li>\n</ul>\n<p>Łączymy (l, a) → <strong>&quot;la&quot;</strong>. Nasz słownik rośnie o jeden token:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">Słownik: [&quot;a&quot;, &quot;b&quot;, &quot;k&quot;, &quot;l&quot;, &quot;s&quot;, &quot;t&quot;, &quot;y&quot;, &quot;la&quot;]\n\nla s     ×10     la k     ×5      la t     ×12\nb a t    ×4      la s y   ×5\n</code></pre>\n<p><strong>Krok 2:</strong> Znowu szukamy najczęstszej pary:</p>\n<ul>\n<li>(la, s) występuje w &quot;las&quot; (10) i &quot;lasy&quot; (5) = <strong>15 razy</strong> ← zwycięzca!</li>\n<li>(la, t) występuje w &quot;lat&quot; (12) = 12 razy</li>\n<li>(la, k) występuje w &quot;lak&quot; (5) = 5 razy</li>\n</ul>\n<p>Łączymy (la, s) → <strong>&quot;las&quot;</strong>:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">Słownik: [&quot;a&quot;, &quot;b&quot;, &quot;k&quot;, &quot;l&quot;, &quot;s&quot;, &quot;t&quot;, &quot;y&quot;, &quot;la&quot;, &quot;las&quot;]\n\nlas      ×10     la k     ×5      la t     ×12\nb a t    ×4      las y    ×5\n</code></pre>\n<p><strong>Krok 3:</strong> Najczęstsza para to teraz (la, t) = 12 razy. Łączymy → <strong>&quot;lat&quot;</strong>:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">Słownik: [..., &quot;la&quot;, &quot;las&quot;, &quot;lat&quot;]\n\nlas      ×10     la k     ×5      lat      ×12\nb a t    ×4      las y    ×5\n</code></pre>\n<p><strong>Krok 4:</strong> Najczęstsza para to (la, k) = 5 razy. Łączymy → <strong>&quot;lak&quot;</strong>:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">Słownik: [..., &quot;la&quot;, &quot;las&quot;, &quot;lat&quot;, &quot;lak&quot;]\n\nlas      ×10     lak      ×5      lat      ×12\nb a t    ×4      las y    ×5\n</code></pre>\n<p><strong>Krok 5:</strong> Najczęstsza para to (las, y) = 5 razy. Łączymy → <strong>&quot;lasy&quot;</strong>:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">Słownik: [..., &quot;la&quot;, &quot;las&quot;, &quot;lat&quot;, &quot;lak&quot;, &quot;lasy&quot;]\n\nlas      ×10     lak      ×5      lat      ×12\nb a t    ×4      lasy     ×5\n</code></pre>\n<p>I tak dalej, aż osiągniemy docelowy rozmiar słownika. W prawdziwych modelach:</p>\n<ul>\n<li><strong>GPT-2</strong> ma słownik <strong>50 257</strong> tokenów (256 bajtów + 50 000 scalenia + 1 specjalny)</li>\n<li><strong>GPT-4/o</strong> ma słownik ~<strong>100 000</strong> tokenów</li>\n<li><strong>Llama 3</strong> też używa około ~<strong>100 000</strong> tokenów</li>\n</ul>\n<h3><a href=\"#kiedy-tokenizer-jest-używany\" aria-hidden=\"true\" class=\"anchor\" id=\"kiedy-tokenizer-jest-używany\"></a>Kiedy tokenizer jest używany?</h3>\n<p>To ważne pytanie, bo odpowiedź brzmi: <strong>zawsze. Na obu etapach.</strong></p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph LR\n    subgraph &quot;TRENING&quot;\n        TC[&quot;📚 Korpus treningowy&quot;] --&gt; TT[&quot;✂️ Tokenizer&quot;] --&gt; TI[&quot;🔢 Token IDs&quot;]\n        TI --&gt; TM[&quot;🧠 Trenujesz model&quot;]\n    end\n    subgraph &quot;PROMPTOWANIE (inferencja)&quot;\n        PC[&quot;💬 Twój prompt&quot;] --&gt; PT[&quot;✂️ Tokenizer&quot;] --&gt; PI[&quot;🔢 Token IDs&quot;]\n        PI --&gt; PM[&quot;🤖 Model generuje&quot;] --&gt; PO[&quot;🔢 Nowe token IDs&quot;]\n        PO --&gt; PD[&quot;📤 Decoder&quot;] --&gt; PF[&quot;💬 Odpowiedź&quot;]\n    end\n    \n    TT -.-&gt;|&quot;ten sam!&quot;| PT\n    style TT fill:#ff9999,color:#000\n    style PT fill:#ff9999,color:#000\n</code></pre>\n<ol>\n<li>\n<p><strong>Trening:</strong> najpierw trenujesz tokenizer na ogromnym korpusie (uczy się, które pary scalać). Potem tokenizujesz CAŁY korpus treningowy na ID-ki. Model uczy się na tych ID-kach.</p>\n</li>\n<li>\n<p><strong>Promptowanie:</strong> kiedy piszesz do ChatGPT, twój tekst przechodzi przez ten <strong>SAM tokenizer</strong> → ID-ki → model generuje nowe ID-ki → decoder zamienia z powrotem na tekst.</p>\n</li>\n</ol>\n<p>Kluczowe: <strong>tokenizer jest trenowany osobno, przed modelem.</strong> Potem jest &quot;zamrożony&quot; - nie zmienia się już nigdy. GPT-2 ma swój tokenizer (słownik 50K), GPT-4 ma swój (słownik 100K). I to jest powód, dla którego polski jest cięty gorzej w GPT-2 - bo <strong>tokenizer</strong> GPT-2 był trenowany głównie na angielskim, więc mało polskich ciągów znaków zostało scalonych. Model sam mógłby &quot;rozumieć&quot; polski lepiej, ale tokenizer już mu pociął tekst na drobne kawałki.</p>\n<div class=\"markdown-alert markdown-alert-tip\">\n<p class=\"markdown-alert-title\">Tip</p>\n<p><strong>Eksperyment dla was:</strong> Wejdźcie na <a href=\"https://tiktokenizer.vercel.app/\">tiktokenizer.vercel.app</a>, wpiszcie jakiś tekst po polsku i zobaczcie, jak model GPT-2 tnie go na tokeny. Zobaczycie, że polskie znaki (ą, ę, ł, ś, ć) często zajmują po 2-3 tokeny! Bo GPT-2 był trenowany głównie na angielskim, więc polski jest dla niego &quot;egzotyczny&quot;.</p>\n</div>\n<h3><a href=\"#różne-modele---różne-cięcie\" aria-hidden=\"true\" class=\"anchor\" id=\"różne-modele---różne-cięcie\"></a>Różne modele - różne cięcie</h3>\n<p>Kluczowa rzecz: <strong>każdy model ma swój własny tokenizator</strong>. Ten sam tekst może być pocięty zupełnie inaczej:</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;Nieprawdopodobnie szczęśliwy&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 prawdopodobnie potnie polski tekst na mnóstwo małych fragmentów (bo nie &quot;zna&quot; polskiego dobrze), a GPT-4 zrobi to o wiele efektywniej (bo widział więcej polskiego tekstu).</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph TD\n    subgraph &quot;GPT-2 (angielski BPE):&quot;\n        G2[&quot;Nieprawdopodobnie szczęśliwy&quot;] --&gt; G2T[&quot;N + ie + p + raw + d + op + od + ob + nie + s + z + cz + ⍰ + ⍰ + ⍰ + ⍰ + li + wy&lt;br/&gt;&lt;i&gt;18 tokenów!&lt;/i&gt;&quot;]\n    end\n    subgraph &quot;GPT-4o (multilingual BPE):&quot;\n        G4[&quot;Nieprawdopodobnie szczęśliwy&quot;] --&gt; G4T[&quot;Nie + p + rawd + op + odob + nie + szcz + ę + śli + wy&lt;br/&gt;&lt;i&gt;10 tokenów&lt;/i&gt;&quot;]\n    end\n    subgraph &quot;Polish RoBERTa (polski SentencePiece):&quot;\n        PR[&quot;Nieprawdopodobnie szczęśliwy&quot;] --&gt; PRT[&quot;Nie + prawdopodobnie + szczęśliwy&lt;br/&gt;&lt;i&gt;3 tokeny!&lt;/i&gt;&quot;]\n    end\n    \n    style G2T fill:#ff9999,color:#000\n    style G4T fill:#ffcc99,color:#000\n    style PRT fill:#99ff99,color:#000\n</code></pre>\n<h3><a href=\"#a-co-z-polskimi-tokenizatorami\" aria-hidden=\"true\" class=\"anchor\" id=\"a-co-z-polskimi-tokenizatorami\"></a>A co z polskimi tokenizatorami?</h3>\n<p>Istnieją modele trenowane specjalnie na polskim tekście - i ich tokenizatory tną polski <strong>znacznie</strong> lepiej:</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, polski 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 (polski 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, polski WordPiece):  ['Nie', 'prawdopodobnie&lt;/w&gt;', 'szczęśliwy&lt;/w&gt;']  → 3 tokeny\nPolish RoBERTa (polski SentencePiece): ['▁Nie', 'prawdopodobnie', '▁szczęśliwy']        → 3 tokeny\n</code></pre>\n<p>Trzy tokeny! &quot;prawdopodobnie&quot; i &quot;szczęśliwy&quot; to dla polskiego tokenizera pojedyncze tokeny. Bo ten tokenizer &quot;widział&quot; te słowa na polskim korpusie tyle razy, że scalił je w całe jednostki.</p>\n<p>Dwa polskie tokenizery, które warto znać:</p>\n<table>\n<thead>\n<tr>\n<th>Tokenizer</th>\n<th>Twórca</th>\n<th>Algorytm</th>\n<th>Zaimplementowany w</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>polski BERT na KGR10 korpusie</td>\n</tr>\n<tr>\n<td><strong>Polish RoBERTa</strong></td>\n<td>sdadas</td>\n<td>SentencePiece (Unigram)</td>\n<td>polski RoBERTa na dużym korpusie</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>Dlaczego polskie modele tną lepiej?</strong> Bo ich tokenizery były trenowane <strong>na polskim tekście</strong>. &quot;prawdopodobnie&quot; występowało w polskim korpusie tysiące razy, więc BPE/SentencePiece scalił je w jeden token. GPT-2 widział głównie angielski, więc &quot;prawdopodobnie&quot; mu się nie scaliło - i tnie je na kawałki. To jest też powód, dla którego GPT-4o (trenowany na dużo bardziej wielojęzycznym korpusie) tnie polski lepiej niż GPT-2 - ale nadal gorzej niż typowo polskie modele.</p>\n</div>\n<h3><a href=\"#stwórz-własny-polski-tokenizer\" aria-hidden=\"true\" class=\"anchor\" id=\"stwórz-własny-polski-tokenizer\"></a>Stwórz własny polski tokenizer!</h3>\n<p>Skoro już rozumiemy, jak BPE działa, spróbujmy wytrenować <strong>własny tokenizer</strong> na polskim tekście. Użyjemy biblioteki <code>tokenizers</code> od HuggingFace (nie mylić z <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;Ala ma kota i psa&quot;</a-s>,\n    <a-s>&quot;Kot siedzi na macie&quot;</a-s>,\n    <a-s>&quot;Pies biega po parku&quot;</a-s>,\n    <a-s>&quot;Ala ma kota i kota i jeszcze raz kota&quot;</a-s>,\n    <a-s>&quot;Kot lubi mleko i kot lubi spać&quot;</a-s>,\n    <a-s>&quot;Pies lubi biegać po parku i gonić kota&quot;</a-s>,\n    <a-s>&quot;Szczęśliwy kot to kot który ma dużo mleka&quot;</a-s>,\n    <a-s>&quot;Nieprawdopodobnie szczęśliwy pies biega po parku&quot;</a-s>,\n    <a-s>&quot;Szczęśliwy Ala ma kota i psa i szczęśliwy dom&quot;</a-s>,\n    <a-s>&quot;Dom to miejsce gdzie mieszka szczęśliwa rodzina&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;Rozmiar słownika: </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;Nieprawdopodobnie szczęśliwy&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; → </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\">Rozmiar słownika: 130\n&quot;Nieprawdopodobnie szczęśliwy&quot; → ['Nieprawdopodobnie', 'szczęśliwy']\n</code></pre>\n<p><strong>Dwa tokeny!</strong> Bo nasz mały korpus jest po polsku - i &quot;Nieprawdopodobnie&quot; oraz &quot;szczęśliwy&quot; wystąpiły wystarczająco często, żeby BPE scalił je w pojedyncze tokeny.</p>\n<p>Ale chwila - co właściwie znaczą te parametry w kodzie?</p>\n<table>\n<thead>\n<tr>\n<th>Parametr</th>\n<th>Co robi</th>\n<th>Przykład</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong><code>vocab_size</code></strong></td>\n<td>Maksymalna liczba tokenów w słowniku. BPE będzie scalał pary, aż słownik osiągnie ten rozmiar. Im większy, tym dłuższe tokeny (całe słowa). Im mniejszy, tym mniejsze kawałki (pojedyncze litery).</td>\n<td>GPT-2: 50 257, nasz: 300</td>\n</tr>\n<tr>\n<td><strong><code>special_tokens</code></strong></td>\n<td>Tokeny o specjalnym znaczeniu dla modelu. Zawsze są w słowniku, niezależnie od treningu.</td>\n<td><code>[UNK]</code>, <code>&lt;PAD&gt;</code>, <code>&lt;S&gt;</code> (start), <code>&lt;/S&gt;</code> (koniec)</td>\n</tr>\n<tr>\n<td><strong><code>unk_token</code></strong></td>\n<td>Token &quot;nieznany&quot; - zastępuje każdy znak, którego tokenizer nie potrafi rozpoznać. Np. jeśli w tekście pojawi się emoji, a tokenizer nie ma go w słowniku, wstawi <code>[UNK]</code>.</td>\n<td><code>[UNK]</code> = &quot;nie wiem co to jest&quot;</td>\n</tr>\n</tbody>\n</table>\n<p>Prosta analogia: <code>vocab_size</code> to grubość waszego słownika - ile haseł się w nim zmieści. <code>unk_token</code> to hasło oznaczające &quot;nie ma takiego słowa&quot;. A <code>special_tokens</code> to &quot;strony zarezerwowane&quot; - zawsze są w słowniku, niezależnie od tego, czego nauczysz tokenizer.</p>\n<div class=\"markdown-alert markdown-alert-tip\">\n<p class=\"markdown-alert-title\">Tip</p>\n<p><strong>Eksperyment:</strong> Skopiujcie ten kod, zmieńcie <code>vocab_size</code> na np. 50 i zobaczcie, co się stanie. Zobaczycie, że przy mniejszym słowniku tokenizer tnie słowa na mniejsze kawałki - bo ma mniej &quot;miejsca&quot; na scalenia. To jest dokładnie to, o czym mówiliśmy: rozmiar słownika to hiperparametr, i to od niego zależy, jak drobno tekst jest cięty.</p>\n</div>\n<div class=\"markdown-alert markdown-alert-warning\">\n<p class=\"markdown-alert-title\">Warning</p>\n<p><strong>Dlaczego to ma znaczenie?</strong> Bo LLM ma limit tokenów w oknie kontekstowym. GPT-3 ma 4K tokenów, GPT-4 Turbo ma 128K. Ale &quot;token&quot; to nie &quot;słowo&quot;! W angielskim ~1 token ≈ 0.75 słowa. W polskim, zwłaszcza z GPT-2, to może być ~1 token ≈ 0.4 słowa. Czyli polski tekst &quot;zjada&quot; więcej tokenów i szybciej wyczerpuje limit.</p>\n</div>\n<h3><a href=\"#inne-algorytmy-tokenizacji\" aria-hidden=\"true\" class=\"anchor\" id=\"inne-algorytmy-tokenizacji\"></a>Inne algorytmy tokenizacji</h3>\n<p>BPE to nie jedyny gracz w mieście. Oto krótkie zestawienie:</p>\n<table>\n<thead>\n<tr>\n<th>Algorytm</th>\n<th>Kto używa</th>\n<th>Jak działa</th>\n<th>Kluczowa różnica</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>BPE</strong></td>\n<td>GPT, Llama, many others</td>\n<td>Scala najczęstszą parę</td>\n<td>Prosty, od dołu</td>\n</tr>\n<tr>\n<td><strong>WordPiece</strong></td>\n<td>BERT, DistilBERT, Electra</td>\n<td>Scala parę z najwyższym &quot;score&quot;</td>\n<td>Score = freq(pary) / (freq(a) × freq(b))</td>\n</tr>\n<tr>\n<td><strong>Unigram</strong></td>\n<td>T5, Pegasus, ALBERT</td>\n<td>Zaczyna od dużego słownika, usuwa najmniej przydatne</td>\n<td>Probabilistyczny, może dawać różne tokenizacje</td>\n</tr>\n<tr>\n<td><strong>SentencePiece</strong></td>\n<td>Wielojęzyczne modele</td>\n<td>BPE lub Unigram na surowym tekście (nawet bez spacji!)</td>\n<td>Działa z językami bez spacji (chiński, japoński)</td>\n</tr>\n</tbody>\n</table>\n<p>Różnica między BPE a WordPiece jest subtelna: BPE scala po prostu <strong>najczęstszą</strong> parę. WordPiece scala parę, która jest <strong>najbardziej zaskakująca</strong> - czyli występuje razem częściej niż by wynikało z częstotliwości poszczególnych elementów. To trochę jak związek, który jest &quot;bardziej niż suma części&quot; ;-)</p>\n<div class=\"markdown-alert markdown-alert-note\">\n<p class=\"markdown-alert-title\">Note</p>\n<p><strong>Nawiązanie do posta 1:</strong> Pamiętacie, jak mówiliśmy, że polska morfologia to koszmar dla LLM? 7 przypadków, fleksja, mnóstwo końcówek... No to teraz widzicie dlaczego. Tokenizator nie &quot;wie&quot;, że &quot;domu&quot;, &quot;domowi&quot;, &quot;domem&quot; to odmiany tego samego słowa. Dla niego to po prostu różne sekwencje znaków. To &quot;zrozumienie&quot; relacji między formami - tego model musi się nauczyć sam, w trakcie treningu.</p>\n</div>\n<h3><a href=\"#quiz-jak-bpe-to-potnie\" aria-hidden=\"true\" class=\"anchor\" id=\"quiz-jak-bpe-to-potnie\"></a>Quiz: jak BPE to potnie?<sup class=\"footnote-ref\"><a href=\"#fn-1\" id=\"fnref-1\" data-footnote-ref>1</a></sup></h3>\n<p>Mamy nasze scalenia z powyższego przykładu: l+a→la, la+s→las, la+t→lat, la+k→lak, las+y→lasy. Jak BPE podzieli te nowe słowa?</p>\n<ol>\n<li>&quot;klasa&quot; (zakładając, że nie było w korpusie)</li>\n<li>&quot;lata&quot; (zakładając, że nie było w korpusie)</li>\n<li>&quot;laska&quot; (popularne polskie słowo!)</li>\n</ol>\n<hr />\n<h2><a href=\"#korpusy-bag-of-words-i-tf-idf\" aria-hidden=\"true\" class=\"anchor\" id=\"korpusy-bag-of-words-i-tf-idf\"></a>Korpusy, Bag-of-Words i TF-IDF</h2>\n<h3><a href=\"#co-to-jest-korpus\" aria-hidden=\"true\" class=\"anchor\" id=\"co-to-jest-korpus\"></a>Co to jest korpus?</h3>\n<p>OK, mamy tokeny. Ale skąd tokenizator wie, które pary znaków scalać? Z <strong>korpusu</strong>! BPE uczy się najczęstszych połączeń z tekstu. I żeby zrobić cokolwiek z tokenami - policzyć je, znaleźć wzorce, wytrenować model - też potrzebujemy korpusu.</p>\n<p><strong>Korpus</strong> to po prostu zbiór tekstów. Może to być:</p>\n<ul>\n<li>zbiór wszystkich artykułów z Wikipedii</li>\n<li>zbiór recenzji produktów z Allegro</li>\n<li>zbiór maili w Twojej skrzynce</li>\n<li>zbiór wszystkich wpisów na Wykopie (odpukać)</li>\n</ul>\n<p>Każdy pojedynczy tekst w korpusie nazywamy <strong>dokumentem</strong>. Proste?</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph TD\n    C[&quot;📚 KORPUS&lt;br/&gt;&lt;i&gt;zbiór dokumentów&lt;/i&gt;&quot;] --&gt; D1[&quot;📄 Dokument 1&lt;br/&gt;Ten film jest świetny&quot;]\n    C --&gt; D2[&quot;📄 Dokument 2&lt;br/&gt;Ten film jest beznadziejny&quot;]\n    C --&gt; D3[&quot;📄 Dokument 3&lt;br/&gt;Świetna książka, polecam&quot;]\n    \n    style C fill:#9999ff,color:#fff\n    style D1 fill:#ffcc99,color:#000\n    style D2 fill:#ffcc99,color:#000\n    style D3 fill:#ffcc99,color:#000\n</code></pre>\n<p>I to są właśnie zbiory danych, na których trenuje się prawdziwe modele - i tokenizery, i same modele:</p>\n<table>\n<thead>\n<tr>\n<th>Korpus</th>\n<th>Co zawiera</th>\n<th>Rozmiar</th>\n<th>Ciekawostka</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>Wikitext</strong></td>\n<td>Artykuły z Wikipedii (angielskiej)</td>\n<td>~500 MB</td>\n<td>Standardowy benchmark do ewaluacji modeli językowych</td>\n</tr>\n<tr>\n<td><strong>Wiki-40B</strong></td>\n<td>Wikipedia w 59 językach (w tym polskim!)</td>\n<td>~40 GB</td>\n<td>Pierwszy krok wielu modeli wielojęzycznych</td>\n</tr>\n<tr>\n<td><strong>EuroParl</strong></td>\n<td>Transkrypcje z parlamentu UE (21 języków)</td>\n<td>~2 GB</td>\n<td>Wysokiej jakości teksty oficjalne, świetny do tłumaczeń</td>\n</tr>\n<tr>\n<td><strong>Common Crawl</strong></td>\n<td>Zrzuty treści ze stron internetowych</td>\n<td><strong>petabajty</strong> (PB!)</td>\n<td>Największy publicznie dostępny korpus webowy. Większość LLM-ów z niego korzysta</td>\n</tr>\n<tr>\n<td><strong>OpenWebText</strong></td>\n<td>Kopia linków z Reddita z &gt;3 punktami oceny</td>\n<td>~38 GB</td>\n<td>Z niego trenowano GPT-2. Filtrowana &quot;jakość&quot; z Reddita</td>\n</tr>\n<tr>\n<td><strong>The Pile</strong></td>\n<td>Mix 22 źródeł (arXiv, GitHub, Wikipedia, książki...)</td>\n<td>~825 GB</td>\n<td>Stworzony przez EleutherAI jako zbiór &quot;do wszystkiego&quot;</td>\n</tr>\n<tr>\n<td><strong>RedPajama</strong></td>\n<td>Otwarta replika danych treningowych LLaMA</td>\n<td>~1.2 TB</td>\n<td>To dlatego LLaMA jest tak dobra - trenowana na ogromnym, różnorodnym zbiorze</td>\n</tr>\n<tr>\n<td><strong>OSCAR</strong></td>\n<td>Zrzuty z internetu, filtrowane po językach</td>\n<td>~6.5 TB</td>\n<td>Ma osobne podzbiory dla każdego języka, w tym polski OSCAR</td>\n</tr>\n</tbody>\n</table>\n<p>A polskie korpusy? Oto najważniejsze:</p>\n<ul>\n<li><strong>NKJP</strong> (Narodowy Korpus Języka Polskiego) - miliony polskich tekstów, zrównoważony zbiór z różnych dziedzin</li>\n<li><strong>Polish-ROBERTa corpus</strong> - ~20 GB polskiego tekstu z internetu, na nim trenowano Polish RoBERTa</li>\n<li><strong>OSCAR (polska część)</strong> - polskie strony z Common Crawl, kilkaset GB</li>\n<li><strong>Wikipedia po polsku</strong> - ~2 GB artykułów, często punkt wyjścia do polskich modeli</li>\n</ul>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph LR\n    subgraph &quot;Skąd biorą się dane treningowe LLM&quot;\n        CC[&quot;🌐 Common Crawl&lt;br/&gt;&lt;i&gt;petabajty z internetu&lt;/i&gt;&quot;] --&gt; MIX\n        WP[&quot;📚 Wikipedia&lt;br/&gt;&lt;i&gt;49 GB, 59 języków&lt;/i&gt;&quot;] --&gt; MIX\n        GH[&quot;💻 GitHub&lt;br/&gt;&lt;i&gt;kod źródłowy&lt;/i&gt;&quot;] --&gt; MIX\n        BK[&quot;📖 Książki&lt;br/&gt;&lt;i&gt;Project Gutenberg etc.&lt;/i&gt;&quot;] --&gt; MIX\n        AX[&quot;📄 ArXiv&lt;br/&gt;&lt;i&gt;publikacje naukowe&lt;/i&gt;&quot;] --&gt; MIX\n        \n        MIX[&quot;🧹 Filtrowanie&lt;br/&gt;&lt;i&gt;usuwanie spamu, duplikatów&lt;/i&gt;&quot;] --&gt; CORPUS[&quot;📊 Czysty korpus&lt;br/&gt;&lt;i&gt;setki GB - TB&lt;/i&gt;&quot;]\n        CORPUS --&gt; TOK[&quot;✂️ Trening tokenizera&quot;]\n        CORPUS --&gt; LLM[&quot;🧠 Trening modelu&quot;]\n    end\n    \n    style CC fill:#ff9999,color:#000\n    style MIX fill:#ffff99,color:#000\n    style CORPUS fill:#99ff99,color:#000\n    style TOK fill:#ffcc99,color:#000\n    style LLM fill:#9999ff,color:#fff\n</code></pre>\n<p>Kluczowa obserwacja: <strong>ten sam korpus służy do trenowania tokenizera i modelu.</strong> Najpierw trenujesz tokenizer na korpusie (uczy się, które pary znaków scalać), potem tokenizujesz cały korpus za pomocą tego tokenizera, i na tych tokenach trenujesz model.</p>\n<div class=\"markdown-alert markdown-alert-note\">\n<p class=\"markdown-alert-title\">Note</p>\n<p><strong>Dlaczego to jest ważne?</strong> Jeśli twój korpus ma mało polskiego tekstu (np. GPT-2 trenowany głównie na angielskim), to tokenizer nie scali polskich słów, a model nie nauczy się polskiego dobrze. Dlatego polskie modele (HerBERT, Polish RoBERTa) używają korpusów z dużą ilością polskiego tekstu - np. NKJP + polski OSCAR + polską Wikipedię.</p>\n</div>\n<h3><a href=\"#bag-of-words---torba-pełna-słów\" aria-hidden=\"true\" class=\"anchor\" id=\"bag-of-words---torba-pełna-słów\"></a>Bag-of-Words - torba pełna słów</h3>\n<p>Mamy korpus. Teraz chcemy zamienić każdy dokument na <strong>liczby</strong>, żeby komputer mógł z tym coś zrobić.</p>\n<p>Najprostszy sposób: <strong>Bag-of-Words (BoW)</strong> - torba słów. Liczymy, ile razy każde słowo występuje w dokumencie.</p>\n<p>Przykład. Mamy trzy krótkie recenzje filmu:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">Dokument 1: &quot;Ten film jest świetny&quot;\nDokument 2: &quot;Ten film jest beznadziejny&quot;\nDokument 3: &quot;Świetny film polecam&quot;\n</code></pre>\n<p>Budujemy słownik wszystkich unikalnych słów:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">{ten, film, jest, świetny, beznadziejny, polecam}\n</code></pre>\n<p>I teraz każdy dokument zamieniamy na wektor zliczeń:</p>\n<table>\n<thead>\n<tr>\n<th></th>\n<th>ten</th>\n<th>film</th>\n<th>jest</th>\n<th>świetny</th>\n<th>beznadziejny</th>\n<th>polecam</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>Dok 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</tr>\n<tr>\n<td><strong>Dok 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</tr>\n<tr>\n<td><strong>Dok 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</tr>\n</tbody>\n</table>\n<p>Każdy dokument to teraz po prostu ciąg liczb. Komputer jest happy.</p>\n<p>Ale my nie do końca ;-) Bo zauważcie problem:</p>\n<blockquote>\n<p>Dokument 1: &quot;Ten film jest świetny&quot; → [1, 1, 1, 1, 0, 0]<br />\nDokument 2: &quot;Ten film jest beznadziejny&quot; → [1, 1, 1, 0, 1, 0]</p>\n</blockquote>\n<p>Te wektory są prawie identyczne! Różnią się na jednej pozycji. A przecież jeden mówi &quot;świetny&quot;, a drugi &quot;beznadziejny&quot; - zupełnie inne znaczenie.</p>\n<p>I jest drugi problem: &quot;Dog bites man&quot; i &quot;Man bites dog&quot; dadzą <strong>dokładnie ten sam</strong> wektor BoW. BoW kompletnie <strong>ignoruje kolejność słów</strong>.</p>\n<div class=\"markdown-alert markdown-alert-note\">\n<p class=\"markdown-alert-title\">Note</p>\n<p><strong>Dlaczego po polsku to nie działa tak samo?</strong> Bo polski ma przypadki! &quot;Pies gryzie człowieka&quot; i &quot;Człowiek gryzie psa&quot; to dla BoW <strong>różne</strong> zdania - bo &quot;pies&quot; ≠ &quot;psa&quot; i &quot;człowieka&quot; ≠ &quot;człowiek&quot;. To są inne słowa. Angielski nie ma przypadków, więc &quot;dog&quot; to zawsze &quot;dog&quot; niezależnie od roli w zdaniu - i dlatego BoW nie odróżnia &quot;dog bites man&quot; od &quot;man bites dog&quot;. To jest właśnie ta polska morfologia z <a href=\"cechy-jezykowe-a-llm.html\">pierwszego posta</a>, która czasem nam pomaga, a czasem przeszkadza ;-)</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 w pigułce:</strong></p>\n<ul>\n<li>Plus: banalnie prosty, działa szybko</li>\n<li>Minus: ignoruje kolejność, ignoruje znaczenie, ignoruje kontekst</li>\n<li>Metafora: wrzucasz wszystkie słowa do torby, potrząsasz, i patrzysz co wypadnie. Torba nie wie, co było pierwsze, a co ostatnie.</li>\n</ul>\n</div>\n<h3><a href=\"#bag-of-words-w-kodzie\" aria-hidden=\"true\" class=\"anchor\" id=\"bag-of-words-w-kodzie\"></a>Bag-of-Words w kodzie</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;Ten film jest świetny&quot;</a-s>,\n    <a-s>&quot;Ten film jest beznadziejny&quot;</a-s>,\n    <a-s>&quot;Świetny film polecam&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;Dok 1&quot;</a-s>, <a-s>&quot;Dok 2&quot;</a-s>, <a-s>&quot;Dok 3&quot;</a-s>]\n)\n<a-f>print</a-f>(<a-v>df</a-v>)</code></pre>\n<p>Wynik:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">        beznadziejny  film  jest  polecam  świetny  ten\nDok 1              0     1     1        0        1    1\nDok 2              1     1     1        0        0    1\nDok 3              0     1     0        1        1    0\n</code></pre>\n<p>Dokładnie ta sama tabela, co wyżej - tylko teraz wygenerowana przez kod. Zauważcie: &quot;film&quot; jest wszędzie (wartość 1 w każdym wierszu), a &quot;beznadziejny&quot; tylko w Dokumencie 2. To jest cała magia BoW - proste zliczanie.</p>\n<div class=\"markdown-alert markdown-alert-important\">\n<p class=\"markdown-alert-title\">Important</p>\n<p><strong>Czy BoW potrafi generować nowe teksty?</strong> Nie. BoW to tylko sposób <strong>reprezentacji</strong> tekstu (tekst → liczby). Służy do analizy, porównywania i klasyfikacji dokumentów, ale nie generuje nic nowego. Nie przewiduje też, jakie słowo powinno pojść po &quot;Kot siedzi na...&quot;. Żeby generować tekst, potrzebujemy czegoś, co rozumie <strong>kolejność</strong> słów i potrafi przewidywać następne. I to jest dokładnie to, do czego dochodzimy za chwilę przy n-gramach i łańcuchach Markowa ;-)</p>\n</div>\n<h3><a href=\"#tf-idf---a-co-jeśli-nie-wszystkie-słowa-są-równe\" aria-hidden=\"true\" class=\"anchor\" id=\"tf-idf---a-co-jeśli-nie-wszystkie-słowa-są-równe\"></a>TF-IDF - a co jeśli nie wszystkie słowa są równe?</h3>\n<p>BoW traktuje każde słowo tak samo. Ale przecież <strong>nie każde słowo jest tak samo ważne</strong>!</p>\n<p>Słowo &quot;jest&quot; występuje w niemal każdym polskim zdaniu. Słowo &quot;semiotyka&quot; - raczej rzadko. Które jest bardziej informatywne? Oczywiście &quot;semiotyka&quot; - bo jeśli widzisz to słowo w dokumencie, to dużo więcej mówi ci o jego treści niż &quot;jest&quot;.</p>\n<p>TF-IDF (Term Frequency - Inverse Document Frequency) to sposób, żeby to uchwycić.</p>\n<p>Przypomnijmy nasz korpus - trzy krótkie recenzje filmu:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">Dokument 1: &quot;Ten film jest świetny&quot;         (4 słowa)\nDokument 2: &quot;Ten film jest beznadziejny&quot;     (4 słowa)\nDokument 3: &quot;Świetny film polecam&quot;            (3 słowa)\n</code></pre>\n<p>Policzmy TF-IDF dla słowa <strong>&quot;świetny&quot;</strong> w Dokumencie 1:</p>\n<p><strong>TF (Term Frequency)</strong> - jak często słowo występuje w jednym dokumencie:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">TF(&quot;świetny&quot;, Dokument 1) = 1 / 4 = 0.25\n</code></pre>\n<p>(1 wystąpienie w 4-słowowym dokumencie)</p>\n<p><strong>IDF (Inverse Document Frequency)</strong> - jak &quot;rzadkie&quot; jest słowo w całym korpusie:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">IDF(&quot;świetny&quot;) = log(3 / 2) = 0.176\n</code></pre>\n<p>(3 dokumenty w korpusie, 2 z nich zawierają &quot;świetny&quot;)</p>\n<p><strong>TF-IDF</strong> = TF × IDF:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">TF-IDF(&quot;świetny&quot;, Dokument 1) = 0.25 × 0.176 = 0.044\n</code></pre>\n<p>A słowo &quot;jest&quot;, które jest WSZĘDZIE?</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">IDF(&quot;jest&quot;) = log(3 / 2) = 0.176  (też w 2 z 3 dokumentów)\nIDF(&quot;film&quot;) = log(3 / 3) = 0      (we WSZYSTKICH dokumentach!)\n</code></pre>\n<p>&quot;Film&quot; dostaje <strong>zero</strong> w IDF! Bo jeśli słowo jest w każdym dokumencie, to nie wnosi żadnej informacji, która pomogłaby odróżnić jeden dokument od drugiego.</p>\n<div class=\"markdown-alert markdown-alert-tip\">\n<p class=\"markdown-alert-title\">Tip</p>\n<p><strong>Intuicja za TF-IDF w jednym zdaniu:</strong> Słowo jest ważne dla danego dokumentu, jeśli często tam występuje (wysokie TF), ale rzadko w innych dokumentach (wysokie IDF). Czyli: &quot;Czy jesteś wyjątkowy?&quot;</p>\n</div>\n<h3><a href=\"#tf-idf-w-kodzie\" aria-hidden=\"true\" class=\"anchor\" id=\"tf-idf-w-kodzie\"></a>TF-IDF w kodzie</h3>\n<p>Spróbujcie sami. Oto kompletny przykład w Pythonie:</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;Ten film jest świetny&quot;</a-s>,\n    <a-s>&quot;Ten film jest beznadziejny&quot;</a-s>,\n    <a-s>&quot;Świetny film polecam&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;Dok 1&quot;</a-s>, <a-s>&quot;Dok 2&quot;</a-s>, <a-s>&quot;Dok 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>Wynik będzie wyglądał mniej więcej tak:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">       beznadziejny  film   jest   polecam  świetny   ten\nDok 1         0.00  0.37  0.49     0.00     0.37   0.49\nDok 2         0.58  0.35  0.46     0.00     0.00   0.46\nDok 3         0.00  0.41  0.00     0.54     0.41   0.00\n</code></pre>\n<p>Zauważcie: &quot;film&quot; ma niskie wartości wszędzie (bo jest wszędzie). &quot;beznadziejny&quot; ma wysoką wartość tylko w Dokumencie 2 (bo tylko tam występuje). TF-IDF działa!</p>\n<div class=\"markdown-alert markdown-alert-tip\">\n<p class=\"markdown-alert-title\">Tip</p>\n<p><strong>Eksperyment dla was:</strong> Pomyślcie o waszych notatkach ze studiów (albo z pracy). Jakie słowa miałyby wysokie TF-IDF? Prawdopodobnie terminy fachowe - &quot;rekurencja&quot;, &quot;entropy&quot;, &quot;backpropagation&quot;. A jakie miałyby niskie? &quot;I&quot;, &quot;jest&quot;, &quot;na&quot;, &quot;że&quot; - bo są wszędzie.</p>\n</div>\n<h3><a href=\"#tf-idf-jako-wyszukiwarka\" aria-hidden=\"true\" class=\"anchor\" id=\"tf-idf-jako-wyszukiwarka\"></a>TF-IDF jako wyszukiwarka</h3>\n<p>TF-IDF ma jedno super praktyczne zastosowanie: <strong>przeszukiwanie tekstu</strong>. To jest właściwie to, jak działały pierwsze wyszukiwarki internetowe.</p>\n<p>Idea jest prosta: zamieniasz zapytanie użytkownika na wektor TF-IDF, i szukasz dokumentu, którego wektor jest do niego <strong>najbardziej podobny</strong> (tzw. podobieństwo kosinusowe).</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;Ten film jest świetny&quot;</a-s>,\n    <a-s>&quot;Ten film jest beznadziejny&quot;</a-s>,\n    <a-s>&quot;Świetny film polecam&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;świetny film&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; → </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\">Zapytanie: &quot;świetny film&quot;\n\n1. &quot;Świetny film polecam&quot;         → 0.694  ← najlepszy wynik!\n2. &quot;Ten film jest świetny&quot;       → 0.667\n3. &quot;Ten film jest beznadziejny&quot;   → 0.229\n</code></pre>\n<p>Dokument 3 wygrywa - bo &quot;świetny&quot; i &quot;film&quot; to dla niego słowa-klucze o wysokim TF-IDF. Dokument 2 ma &quot;film&quot;, ale nie ma &quot;świetny&quot; - więc jego podobieństwo jest niskie.</p>\n<div class=\"markdown-alert markdown-alert-note\">\n<p class=\"markdown-alert-title\">Note</p>\n<p><strong>Podobieństwo kosinusowe</strong> mierzy kąt między dwoma wektorami. Jeśli wektory wskazują w tym samym kierunku (podobne proporcje słów) - podobieństwo jest bliskie 1. Jeśli w przeciwnych - bliskie 0. Nie przejmujcie się matematyką - intuicja jest prosta: &quot;jak bardzo te dwa wektory są do siebie podobne?&quot;</p>\n</div>\n<hr />\n<h2><a href=\"#n-gramy---czyli-dodajemy-kontekst\" aria-hidden=\"true\" class=\"anchor\" id=\"n-gramy---czyli-dodajemy-kontekst\"></a>N-gramy - czyli dodajemy kontekst</h2>\n<h3><a href=\"#problem-słowa-nie-żyją-w-próżni\" aria-hidden=\"true\" class=\"anchor\" id=\"problem-słowa-nie-żyją-w-próżni\"></a>Problem: słowa nie żyją w próżni</h3>\n<p>TF-IDF jest lepszy niż czysty BoW, bo przynajmniej waży słowa według ważności. Ale nadal traktuje każde słowo <strong>osobno</strong>. Nie wie, że &quot;nie&quot; + &quot;lubię&quot; = coś zupełnie innego niż &quot;lubię&quot; samo.</p>\n<p>Albo przykład z angielskiego, który świetnie pokazuje problem:</p>\n<ul>\n<li>&quot;big red <strong>machine</strong> and carpet&quot; - mówimy o maszynie</li>\n<li>&quot;big red <strong>carpet</strong> and machine&quot; - mówimy o dywanie</li>\n</ul>\n<p>Same słowa są te same! Ale kolejność zmienia wszystko. BoW i TF-IDF tego nie widzą.</p>\n<h3><a href=\"#czym-są-n-gramy\" aria-hidden=\"true\" class=\"anchor\" id=\"czym-są-n-gramy\"></a>Czym są n-gramy?</h3>\n<p><strong>N-gram</strong> to po prostu ciąg N kolejnych elementów (zwykle słów albo znaków).</p>\n<p>Przykład ze zdaniem &quot;Ala ma kota&quot;:</p>\n<table>\n<thead>\n<tr>\n<th>Typ</th>\n<th>N</th>\n<th>Wynik</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Unigramy</td>\n<td>1</td>\n<td>Ala, ma, kota</td>\n</tr>\n<tr>\n<td>Bigramy</td>\n<td>2</td>\n<td>Ala ma, ma kota</td>\n</tr>\n<tr>\n<td>Trigramy</td>\n<td>3</td>\n<td>Ala ma kota</td>\n</tr>\n</tbody>\n</table>\n<p>I teraz magia: zamiast liczyć pojedyncze słowa, liczymy <strong>pary słów</strong> (bigramy). I nagle:</p>\n<ul>\n<li>&quot;nie lubię&quot; staje się jednym bytem (negacja + czasownik = negatywny sens)</li>\n<li>&quot;big red&quot; i &quot;red carpet&quot; to różne rzeczy</li>\n<li>&quot;New York&quot; to jedna jednostka, nie dwa słowa</li>\n</ul>\n<p>Można też łączyć n-gramy z TF-IDF! W sklearn wystarczy zmienić jeden parametr:</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;Ten film jest świetny&quot;</a-s>,\n    <a-s>&quot;Ten film jest beznadziejny&quot;</a-s>,\n    <a-s>&quot;Świetny film polecam&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;Dok 1&quot;</a-s>, <a-s>&quot;Dok 2&quot;</a-s>, <a-s>&quot;Dok 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<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">       beznadziejny  film  film jest  film polecam  jest  jest beznadziejny  jest świetny  polecam   ten  ten film  świetny  świetny film\nDok 1          0.00  0.29       0.37           0.0  0.37               0.00          0.49      0.0  0.37      0.37     0.37           0.0\nDok 2          0.46  0.27       0.35           0.0  0.35               0.46          0.00      0.0  0.35      0.35     0.00           0.0\nDok 3          0.00  0.30       0.00           0.5  0.00               0.00          0.00      0.5  0.00      0.00     0.38           0.5\n</code></pre>\n<p>Zobaczcie, co się stało - słownik cech urósł! Oprócz pojedynczych słów (&quot;film&quot;, &quot;jest&quot;, &quot;świetny&quot;) mamy teraz <strong>pary</strong>: &quot;film jest&quot;, &quot;jest świetny&quot;, &quot;jest beznadziejny&quot;, &quot;świetny film&quot;, &quot;ten film&quot;, &quot;film polecam&quot;. Każda para to osobna kolumna z własnym TF-IDF.</p>\n<p>Dzięki temu model widzi, że &quot;jest świetny&quot; (Dok 1) i &quot;jest beznadziejny&quot; (Dok 2) to <strong>różne rzeczy</strong> - bo to różne bigramy z różnymi wartościami TF-IDF.</p>\n<div class=\"markdown-alert markdown-alert-note\">\n<p class=\"markdown-alert-title\">Note</p>\n<p><strong>N-gramy to kompromis.</strong> Im większe N, tym więcej kontekstu łapiesz, ale tym więcej danych potrzebujesz. Z trigramami masz 3× więcej kombinacji niż z unigramami. Z 4-gramami - jeszcze więcej. I szybko dochodzisz do momentu, gdzie większość n-gramów występuje w korpusie tylko raz, co nie jest przydatne.</p>\n</div>\n<h3><a href=\"#łańcuchy-markowa---gdy-n-gramy-zaczynają-przewidywać\" aria-hidden=\"true\" class=\"anchor\" id=\"łańcuchy-markowa---gdy-n-gramy-zaczynają-przewidywać\"></a>Łańcuchy Markowa - gdy n-gramy zaczynają &quot;przewidywać&quot;</h3>\n<p>Same n-gramy to tylko zliczenia - tak jak BoW, nie generują tekstu. Ale jeśli dodamy do nich <strong>prawdopodobieństwa przejść</strong>, nagle zyskujemy coś, co <strong>generuje nowy tekst</strong>.</p>\n<p>Pomysł jest prosty: dla każdego słowa patrzymy, jakie słowa najczęściej po nim następują. I wybieramy następne słowo probabilistycznie.</p>\n<p>Weźmy mały korpus:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">&quot;ala ma kota ala ma psa kot lubi mleko ala lubi kota&quot;\n</code></pre>\n<p>Liczymy bigramy i prawdopodobieństwa przejść:</p>\n<table>\n<thead>\n<tr>\n<th>Słowo</th>\n<th>Następne słowo</th>\n<th>Prawdopodobieństwo</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>ala</strong></td>\n<td>ma</td>\n<td>2/3 = <strong>67%</strong></td>\n</tr>\n<tr>\n<td><strong>ala</strong></td>\n<td>lubi</td>\n<td>1/3 = 33%</td>\n</tr>\n<tr>\n<td><strong>ma</strong></td>\n<td>kota</td>\n<td>1/2 = 50%</td>\n</tr>\n<tr>\n<td><strong>ma</strong></td>\n<td>psa</td>\n<td>1/2 = 50%</td>\n</tr>\n<tr>\n<td><strong>kot</strong></td>\n<td>lubi</td>\n<td>1/1 = 100%</td>\n</tr>\n<tr>\n<td><strong>lubi</strong></td>\n<td>mleko</td>\n<td>1/2 = 50%</td>\n</tr>\n<tr>\n<td><strong>lubi</strong></td>\n<td>kota</td>\n<td>1/2 = 50%</td>\n</tr>\n</tbody>\n</table>\n<p>To jest właśnie <strong>łańcuch Markowa</strong> - model, w którym prawdopodobieństwo następnego stanu zależy <strong>tylko od obecnego stanu</strong> (albo kilku ostatnich).</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph LR\n    A[&quot;ala&quot;] --&gt;|&quot;67%&quot;| B[&quot;ma&quot;]\n    A --&gt;|&quot;33%&quot;| L[&quot;lubi&quot;]\n    B --&gt;|&quot;50%&quot;| E[&quot;kota&quot;]\n    B --&gt;|&quot;50%&quot;| F[&quot;psa&quot;]\n    L --&gt;|&quot;50%&quot;| M[&quot;mleko&quot;]\n    L --&gt;|&quot;50%&quot;| E\n    \n    style A fill:#9999ff,color:#fff\n    style B fill:#ffcc99,color:#000\n    style E fill:#99ff99,color:#000\n</code></pre>\n<p>&quot;ala&quot; → najczęściej &quot;ma&quot; (67%) → &quot;kota&quot; albo &quot;psa&quot; (po 50%). Czyli generujemy np.: &quot;ala ma kota&quot;. Albo &quot;ala ma psa&quot;. Albo &quot;ala lubi kota&quot;. Wszystkie te zdania są &quot;nowe&quot; - nie wystąpiły jako całości w korpusie - ale model skleił je z prawdopodobieństw przejść.</p>\n<h3><a href=\"#jak-to-działa-pod-spodem---krok-po-kroku\" aria-hidden=\"true\" class=\"anchor\" id=\"jak-to-działa-pod-spodem---krok-po-kroku\"></a>Jak to działa pod spodem - krok po kroku</h3>\n<p>Zanim przejdziemy do kodu, prześledźmy cały proces na palcach. Mamy ten sam korpus:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">&quot;ala ma kota ala ma psa kot lubi mleko ala lubi kota&quot;\n</code></pre>\n<p><strong>Krok 1: Budujemy słownik trigramów.</strong> Przesuwamy &quot;okienko&quot; o 3 słowa i patrzymy, co jest za nim:</p>\n<table>\n<thead>\n<tr>\n<th>Okno (trigram)</th>\n<th>Następne słowo</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>ala ma <strong>kota</strong></td>\n<td>ala</td>\n</tr>\n<tr>\n<td>ma kota <strong>ala</strong></td>\n<td>ma</td>\n</tr>\n<tr>\n<td>kota ala <strong>ma</strong></td>\n<td>psa</td>\n</tr>\n<tr>\n<td>ala ma <strong>psa</strong></td>\n<td>kot</td>\n</tr>\n<tr>\n<td>ma psa <strong>kot</strong></td>\n<td>lubi</td>\n</tr>\n<tr>\n<td>psa kot <strong>lubi</strong></td>\n<td>mleko</td>\n</tr>\n<tr>\n<td>kot lubi <strong>mleko</strong></td>\n<td>ala</td>\n</tr>\n<tr>\n<td>lubi mleko <strong>ala</strong></td>\n<td>lubi</td>\n</tr>\n<tr>\n<td>mleko ala <strong>lubi</strong></td>\n<td>kota</td>\n</tr>\n</tbody>\n</table>\n<p>Każdy trigram ma dokładnie jedno możliwe następne słowo (bo nasz korpus jest malutki).</p>\n<p><strong>Krok 2: Generujemy.</strong> Zaczynamy od trigramu startowego <code>(&quot;ala&quot;, &quot;ma&quot;, &quot;kota&quot;)</code>:</p>\n<div class=\"markdown-alert markdown-alert-important\">\n<p class=\"markdown-alert-title\">Important</p>\n<p><strong>Ten trigram startowy to nasz &quot;prompt&quot;!</strong> Tak samo jak w ChatGPT wpisujesz tekst i model kontynuuje - tu wpisujemy &quot;ala ma kota&quot; i generator dobiera kolejne słowa. Jedyna różnica to skala: ChatGPT ma kontekst na tysiące tokenów, a my na 3 słowa.</p>\n</div>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">Start:    ala ma kota\nKrok 1:   ala ma kota [ala]     ← po (ala,ma,kota) następuje &quot;ala&quot;\nKrok 2:   ala ma kota ala [ma]  ← po (ma,kota,ala) następuje &quot;ma&quot;\nKrok 3:   ala ma kota ala ma [psa]   ← po (kota,ala,ma) następuje &quot;psa&quot;\nKrok 4:   ... ma psa [kot]      ← po (ala,ma,psa) następuje &quot;kot&quot;\nKrok 5:   ... psa kot [lubi]    ← po (ma,psa,kot) następuje &quot;lubi&quot;\nKrok 6:   ... kot lubi [mleko]  ← po (psa,kot,lubi) następuje &quot;mleko&quot;\nKrok 7:   ... lubi mleko [ala]  ← po (kot,lubi,mleko) następuje &quot;ala&quot;\nKrok 8:   ... mleko ala [lubi]  ← po (lubi,mleko,ala) następuje &quot;lubi&quot;\nKrok 9:   ... ala lubi [kota]   ← po (mleko,ala,lubi) następuje &quot;kota&quot;\nKrok 10:  STOP ← trigram (ala,lubi,kota) nie jest w słowniku\n</code></pre>\n<p>Wynik: <code>&quot;ala ma kota ala ma psa kot lubi mleko ala lubi kota&quot;</code> - dokładnie nasz korpus, tylko &quot;przeklejony&quot; od środka. Na większym korpusie wynik byłby inny za każdym razem.</p>\n<h3><a href=\"#prosty-generator-tekstu-z-n-gramów\" aria-hidden=\"true\" class=\"anchor\" id=\"prosty-generator-tekstu-z-n-gramów\"></a>Prosty generator tekstu z n-gramów</h3>\n<p>Oto kompletny (i naprawdę krótki!) generator tekstu w Pythonie:</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;ala ma kota ala ma psa kot lubi mleko ala lubi kota&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;ala&quot;</a-s>, <a-s>&quot;ma&quot;</a-s>, <a-s>&quot;kota&quot;</a-s>)  <a-c># ← to jest nasz &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>Możliwy wynik: <code>&quot;ala ma kota ala ma psa kot lubi mleko ala ma kota ala ma psa&quot;</code></p>\n<div class=\"markdown-alert markdown-alert-note\">\n<p class=\"markdown-alert-title\">Note</p>\n<p><strong>Dlaczego za każdym razem wychodzi to samo?</strong> Bo nasz korpus jest tak mały, że większość trigramów ma <strong>tylko jedno</strong> możliwe następne słowo. <code>random.choice</code> nie ma z czego wybierać! Na większym korpusie (np. całej polskiej Wikipedii) ten sam kod generowałby <strong>za każdym razem inny tekst</strong> - bo prawie każdy trigram miałby kilka możliwych kontynuacji o różnych prawdopodobieństwach. I to jest właśnie moment, kiedy generowanie staje się interesujące.</p>\n</div>\n<p>Nic wielkiego, prawda? Ale to dlatego, że nasz korpus jest mikroskopijny. Na prawdziwym korpusie (np. całej Wikipedii) generator trigramowy potrafi produkować zdania, które brzmią sensownie, choć nie wystąpiły nigdy wcześniej.</p>\n<div class=\"markdown-alert markdown-alert-important\">\n<p class=\"markdown-alert-title\">Important</p>\n<p><strong>Łańcuchy Markowa to pierwsze &quot;modele językowe&quot;!</strong> Przewidywanie następnego słowa na podstawie kontekstu - to jest DOKŁADNIE to, co robi ChatGPT. Oczywiście GPT używa o wiele bardziej zaawansowanej metody (Transformer + attention na tysiącach tokenów kontekstu), ale <strong>fundamentalna idea jest ta sama</strong>: prawdopodobieństwo następnego tokenu. LLM to potomek łańcuchów Markowa. Na sterydach ;-)</p>\n</div>\n<hr />\n<h2><a href=\"#techniki-bayesowskie---czyli-klasyfikujemy-tekst\" aria-hidden=\"true\" class=\"anchor\" id=\"techniki-bayesowskie---czyli-klasyfikujemy-tekst\"></a>Techniki bayesowskie - czyli klasyfikujemy tekst</h2>\n<h3><a href=\"#a-gdybyśmy-chcieli-nie-generować-ale-klasyfikować\" aria-hidden=\"true\" class=\"anchor\" id=\"a-gdybyśmy-chcieli-nie-generować-ale-klasyfikować\"></a>A gdybyśmy chcieli nie generować, ale KLASYFIKOWAĆ?</h3>\n<p>Łańcuchy Markowa są super do generowania tekstu. Ale w praktyce często chcemy coś innego: <strong>przypisać tekst do kategorii</strong>.</p>\n<ul>\n<li>Czy ten mail to <strong>spam</strong> czy <strong>nie-spam</strong>?</li>\n<li>Czy ta recenzja jest <strong>pozytywna</strong> czy <strong>negatywna</strong>?</li>\n<li>Czy ten artykuł jest o <strong>sporcie</strong>, <strong>polityce</strong> czy <strong>technologii</strong>?</li>\n</ul>\n<p>I tu wchodzi <strong>Naive Bayes</strong> - jeden z najprostszych, a zarazem najużyteczniejszych algorytmów klasyfikacji tekstu.</p>\n<h3><a href=\"#twierdzenie-bayesa---intuicja\" aria-hidden=\"true\" class=\"anchor\" id=\"twierdzenie-bayesa---intuicja\"></a>Twierdzenie Bayesa - intuicja</h3>\n<p>Bez wzorów, samym przykładem:</p>\n<p>Wyobraźcie sobie, że idzie do was kolega i mówi: &quot;Kaszlę.&quot; Jakie jest prawdopodobieństwo, że ma przeziębienie?</p>\n<p>Zależy! Jeśli jest listopad i wszyscy w biurze chorują - wysokie. Jeśli jest lipiec i kaszle raz - niskie.</p>\n<p>Twierdzenie Bayesa to formalny sposób myślenia o takich sytuacjach: <strong>aktualizujemy naszą wiedzę na podstawie nowych dowodów.</strong></p>\n<p>A teraz przełóżmy to na tekst. Pytanie brzmi:</p>\n<blockquote>\n<p>&quot;Jeśli dokument zawiera słowo 'viagra', jakie jest prawdopodobieństwo, że to spam?&quot;</p>\n</blockquote>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph LR\n    D[&quot;📧 Nowy mail:&lt;br/&gt;'Kup viagra tanio!'&quot;] --&gt; Q[&quot;P(spam | 'kup viagra tanio') = ?&quot;]\n    Q --&gt; S[&quot;📊 P(spam) × P('kup'|spam) × P('viagra'|spam) × P('tanio'|spam)&quot;]\n    Q --&gt; H[&quot;📊 P(nie-spam) × P('kup'|nie-spam) × P('viagra'|nie-spam) × P('tanio'|nie-spam)&quot;]\n    S --&gt; C{&quot;Które&lt;br/&gt;prawdopodobieństwo&lt;br/&gt;jest większe?&quot;}\n    H --&gt; C\n    C --&gt;|Spam!| R[&quot;🗑️ SPAM&quot;]\n    \n    style D fill:#ffcc99,color:#000\n    style C fill:#9999ff,color:#fff\n    style R fill:#ff9999,color:#000\n</code></pre>\n<h3><a href=\"#naive-bayes-w-akcji\" aria-hidden=\"true\" class=\"anchor\" id=\"naive-bayes-w-akcji\"></a>Naive Bayes w akcji</h3>\n<p>Mamy mały zbiór maili:</p>\n<table>\n<thead>\n<tr>\n<th>Mail</th>\n<th>Treść</th>\n<th>Etykieta</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>1</td>\n<td>&quot;Kup viagra tanio teraz&quot;</td>\n<td>Spam</td>\n</tr>\n<tr>\n<td>2</td>\n<td>&quot;Viagra darmowa oferta&quot;</td>\n<td>Spam</td>\n</tr>\n<tr>\n<td>3</td>\n<td>&quot;Spotkanie jutro o 10&quot;</td>\n<td>Nie-spam</td>\n</tr>\n<tr>\n<td>4</td>\n<td>&quot;Prześlij mi raport jutro&quot;</td>\n<td>Nie-spam</td>\n</tr>\n</tbody>\n</table>\n<p>Przychodzi nowy mail: <strong>&quot;Viagra jutro spotkanie&quot;</strong>. Spam czy nie?</p>\n<p>Naive Bayes liczy:</p>\n<ol>\n<li>P(Spam) = 2/4 = 0.5, P(Nie-spam) = 2/4 = 0.5</li>\n<li>P(&quot;viagra&quot; | Spam) = 2/2 = 1.0 (oba spamy mają &quot;viagra&quot;)</li>\n<li>P(&quot;viagra&quot; | Nie-spam) = 0/2 = 0.0 (żaden nie-spam nie ma &quot;viagra&quot;)</li>\n<li>P(&quot;jutro&quot; | Spam) = 0/2 = 0.0</li>\n<li>P(&quot;jutro&quot; | Nie-spam) = 2/2 = 1.0</li>\n</ol>\n<p>P(Spam | &quot;viagra jutro spotkanie&quot;) ∝ 0.5 × 1.0 × 0.0 × ... = <strong>0</strong>!</p>\n<p>P(Nie-spam | &quot;viagra jutro spotkanie&quot;) ∝ 0.5 × 0.0 × 1.0 × ... = <strong>0</strong>!</p>\n<p>Ups. Obecność &quot;jutro&quot; (słowo z nie-spamu) wyzerowało spam, a obecność &quot;viagra&quot; (słowo spamowe) wyzerowało nie-spam. Ten konkretny mail jest trudny ;-)</p>\n<p>W praktyce stosuje się tzw. <strong>Laplace smoothing</strong> (dodajemy 1 do każdego licznika), żeby uniknąć zerowania:</p>\n<ul>\n<li>P(&quot;viagra&quot; | Spam) = (2 + 1) / (7 + 14) = 0.214 (dodajemy 1, dzielimy przez wszystkie słowa w spamie + unikalne słowa)</li>\n<li>P(&quot;viagra&quot; | Nie-spam) = (0 + 1) / (7 + 14) = 0.048</li>\n</ul>\n<p>Teraz: P(Spam | mail) ∝ 0.5 × 0.214 × 0.048 × 0.048 ≈ <strong>0.000246</strong>\nP(Nie-spam | mail) ∝ 0.5 × 0.048 × 0.143 × 0.143 ≈ <strong>0.000490</strong></p>\n<p>Nie-spam wygrywa! Bo &quot;jutro&quot; i &quot;spotkanie&quot; silnie wskazują na normalny mail.</p>\n<h3><a href=\"#dlaczego-naive\" aria-hidden=\"true\" class=\"anchor\" id=\"dlaczego-naive\"></a>Dlaczego &quot;Naive&quot;?</h3>\n<p>Bo zakładamy, że słowa są <strong>niezależne</strong>. Czyli: prawdopodobieństwo wystąpienia &quot;viagra&quot; nie zależy od tego, czy &quot;tanie&quot; też występuje. Oczywiście to nie jest prawda! &quot;Viagra&quot; i &quot;tanie&quot; występują razem częściej niż przypadkiem. Ale model i tak działa zaskakująco dobrze...</p>\n<p>To jest trochę jak założenie, że pogoda w Warszawie nie zależy od pogody w Krakowie. Oczywiście trochę zależy! Ale jeśli chcesz szybko oszacować, czy potrzebujesz parasola, to założenie niezależności daje ci w miarę dobrą odpowiedź ;-)</p>\n<h3><a href=\"#klasyfikator-spamu-w-pythonie\" aria-hidden=\"true\" class=\"anchor\" id=\"klasyfikator-spamu-w-pythonie\"></a>Klasyfikator spamu w Pythonie</h3>\n<p>Skoro już rozumiemy matematykę, zróbmy to w kilku linijkach kodu. Biblioteka <code>scikit-learn</code> ma gotowy klasyfikator Naive Bayes:</p>\n<ul>\n<li><strong><code>CountVectorizer</code></strong> - zamienia tekst na wektor zliczeń słów (czyli nasz BoW z poprzedniej sekcji)</li>\n<li><strong><code>MultinomialNB</code></strong> - to jest właśnie Naive Bayes, tzw. &quot;wielomianowy&quot; (bo liczy prawdopodobieństwa słów), z <code>alpha=1.0</code> czyli 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;Kup viagra tanio teraz&quot;</a-s>,\n    <a-s>&quot;Viagra darmowa oferta&quot;</a-s>,\n    <a-s>&quot;Spotkanie jutro o 10&quot;</a-s>,\n    <a-s>&quot;Prześlij mi raport jutro&quot;</a-s>,\n    <a-s>&quot;Tanie leki online kup teraz&quot;</a-s>,\n    <a-s>&quot;Raport z wczoraj prześlij&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=nie-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 jutro spotkanie&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;Nie-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>Eksperyment dla was:</strong> Stwórzcie 5 pozytywnych zdań o filmie (&quot;Ten film był niesamowity!&quot;, &quot;Wspaniała akcja!&quot;, ...) i 5 negatywnych (&quot;Nudny jak flaki z olejem&quot;, &quot;Strata czasu&quot;, ...). Wypiszcie słowa, które występują TYLKO w pozytywnych i TYLKO w negatywnych. To jest intuicja Naive Bayes - każde słowo &quot;głosuje&quot; na jedną z kategorii.</p>\n</div>\n<div class=\"markdown-alert markdown-alert-warning\">\n<p class=\"markdown-alert-title\">Warning</p>\n<p><strong>Ograniczenie:</strong> Naive Bayes widzi słowa, ale nie kontekst. &quot;Film jest NIESAMOWITY... żesz beznadziejny&quot; - model zobaczy &quot;niesamowity&quot; i powie &quot;pozytywny&quot;. Nie ogarnia ironii ani złożonych konstrukcji. Potrzebujemy czegoś, co rozumie <strong>relacje między słowami</strong>. I tu wchodzimy w świat wektorów...</p>\n</div>\n<hr />\n<h2><a href=\"#word2vec---słowa-stają-się-geometrią\" aria-hidden=\"true\" class=\"anchor\" id=\"word2vec---słowa-stają-się-geometrią\"></a>Word2Vec - słowa stają się geometrią</h2>\n<h3><a href=\"#moment-eureka\" aria-hidden=\"true\" class=\"anchor\" id=\"moment-eureka\"></a>Moment &quot;eureka&quot;</h3>\n<p>Wszystkie metody, które dotąd poznaliśmy, mają jeden wspólny problem: <strong>traktują słowa jako dyskretne, niezależne byty</strong>. W BoW &quot;kot&quot; jest na pozycji 47 w wektorze, &quot;pies&quot; na pozycji 138. Nie ma między nimi żadnej relacji.</p>\n<p>A przecież MY wiemy, że &quot;kot&quot; i &quot;pies&quot; są do siebie podobne - oba to zwierzęta domowe. &quot;Kot&quot; i &quot;samochód&quot; - zupełnie różne. Jak sprawić, żeby komputer też to &quot;wiedział&quot;?</p>\n<p>Odpowiedź z 2013 roku, od zespołu Tomáša Mikolova w Google: <strong>zamieńmy słowa w punkty w przestrzeni wielowymiarowej</strong>. Słowa podobne znaczeniowo będą blisko siebie. Słowa różne - daleko.</p>\n<p>To jest <strong>Word2Vec</strong>. I to jest przełom.</p>\n<h3><a href=\"#one-hot-encoding-punkt-wyjścia\" aria-hidden=\"true\" class=\"anchor\" id=\"one-hot-encoding-punkt-wyjścia\"></a>One-hot encoding: punkt wyjścia</h3>\n<p>Zanim Word2Vec, standardem było tzw. <strong>one-hot encoding</strong> - każde słowo to wektor z jedynką na swojej pozycji i zerami wszędzie indziej:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">&quot;kot&quot;  → [0, 0, 0, 1, 0, 0, ...]  (1 na pozycji 3)\n&quot;pies&quot; → [0, 0, 1, 0, 0, 0, ...]  (1 na pozycji 2)\n&quot;samochód&quot; → [1, 0, 0, 0, 0, 0, ...] (1 na pozycji 0)\n</code></pre>\n<p>Problem? <strong>Każde słowo jest w takiej samej odległości od każdego innego.</strong> Odległość między &quot;kot&quot; a &quot;pies&quot; jest taka sama jak między &quot;kot&quot; a &quot;samochód&quot;. Zero informacji o znaczeniu.</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph LR\n    subgraph &quot;One-hot: wszystko jest tak samo daleko&quot;\n        O1[&quot;kot&lt;br/&gt;[0,0,1,0]&quot;] ---|&quot;=&quot;| O2[&quot;pies&lt;br/&gt;[0,1,0,0]&quot;]\n        O1 ---|&quot;=&quot;| O3[&quot;samochód&lt;br/&gt;[1,0,0,0]&quot;]\n    end\n    \n    style O1 fill:#9999ff,color:#fff\n    style O2 fill:#ffcc99,color:#000\n    style O3 fill:#ff9999,color:#000\n</code></pre>\n<h3><a href=\"#word2vec-pokaż-mi-twoich-sąsiadów-a-powiem-ci-kim-jesteś\" aria-hidden=\"true\" class=\"anchor\" id=\"word2vec-pokaż-mi-twoich-sąsiadów-a-powiem-ci-kim-jesteś\"></a>Word2Vec: &quot;Pokaż mi twoich sąsiadów, a powiem ci, kim jesteś&quot;</h3>\n<p>Word2Vec opiera się na genialnej intuicji lingwistycznej: <strong>słowa, które występują w podobnym kontekście, mają podobne znaczenie.</strong></p>\n<p>Jeśli widzisz: &quot;___ biega po parku i goni gołębie&quot;, to co tam wpadnie? Pies? Kot? Raczej nie &quot;samochód&quot; ani &quot;demokracja&quot;. Kontekst definiuje słowo.</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph TD\n    subgraph &quot;Kontekst słowa 'pies'&quot;\n        P1[&quot;___ biega po parku&quot;]\n        P2[&quot;Mój ___ szczeka na listonosza&quot;]\n        P3[&quot;Karmię ___ karmą&quot;]\n    end\n    subgraph &quot;Kontekst słowa 'kot'&quot;\n        K1[&quot;___ śpi na kanapie&quot;]\n        K2[&quot;Mój ___ łapie myszy&quot;]\n        K3[&quot;Karmię ___ karmą&quot;]\n    end\n    \n    P3 -.-&gt;|&quot;wspólny kontekst!&quot;| K3\n    \n    style P3 fill:#ffff99,color:#000\n    style K3 fill:#ffff99,color:#000\n</code></pre>\n<p>&quot;Karmię ___ karmą&quot; - zarówno pies, jak i kot pasują. To znaczy, że kontekst tych słów jest częściowo wspólny. I to jest właśnie to, co Word2Vec wykorzystuje.</p>\n<p>Pamiętacie <strong>Saussure'a</strong> z <a href=\"semiotyka-a-llm.html\">drugiego posta</a>? Znaczenie jest relacyjne. &quot;Kot&quot; znaczy to, co znaczy, bo NIE jest &quot;psem&quot;, NIE jest &quot;domem&quot;. Word2Vec to matematyczna implementacja tej idei!</p>\n<h3><a href=\"#cbow-i-skip-gram---dwie-strony-jednej-monety\" aria-hidden=\"true\" class=\"anchor\" id=\"cbow-i-skip-gram---dwie-strony-jednej-monety\"></a>CBOW i Skip-gram - dwie strony jednej monety</h3>\n<p>Word2Vec ma dwa warianty. Zobaczmy oba:</p>\n<p><strong>CBOW (Continuous Bag of Words):</strong> z kontekstu zgadujemy słowo środkowe.</p>\n<p><code>&quot;Kot ___ na macie&quot; → siedzi? leży? śpi?</code></p>\n<p><strong>Skip-gram:</strong> ze słowa środkowego zgadujemy kontekst.</p>\n<p><code>&quot;siedzi&quot; → kot? na? macie?</code></p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph TD\n    subgraph &quot;CBOW: kontekst → słowo&quot;\n        C1[&quot;Kot&quot;] --&gt; H1[&quot;🧠 Ukryta warstwa&lt;br/&gt;(uśrednione wektory)&quot;]\n        C2[&quot;na&quot;] --&gt; H1\n        C3[&quot;macie&quot;] --&gt; H1\n        H1 --&gt; O1[&quot;siedzi ✅&quot;]\n    end\n    \n    subgraph &quot;Skip-gram: słowo → kontekst&quot;\n        S1[&quot;siedzi&quot;] --&gt; H2[&quot;🧠 Ukryta warstwa&quot;]\n        H2 --&gt; O2[&quot;Kot?&quot;]\n        H2 --&gt; O3[&quot;na?&quot;]\n        H2 --&gt; O4[&quot;macie?&quot;]\n    end\n    \n    style H1 fill:#9999ff,color:#fff\n    style H2 fill:#ff9999,color:#000\n</code></pre>\n<p>Jak to działa wewnątrz? Sieć neuronowa z jedną ukrytą warstwą:</p>\n<ol>\n<li>Każde słowo zaczyna jako one-hot wektor (np. [0, 0, 1, 0, ...])</li>\n<li>Mnożymy przez macierz wag <strong>W</strong> (rozmiar: słownik × wymiar embeddingu, np. 50 000 × 300)</li>\n<li>Wynik to wektor o rozmiarze <strong>E</strong> (np. 300 liczb) - to jest nasz embedding!</li>\n<li>W CBOW: uśredniamy wektory kontekstu i przewidujemy środek. W Skip-gram: bierzemy środek i przewidujemy sąsiadów.</li>\n<li>Sieć trenuje się na milionach par (słowo, kontekst), aktualizując macierz <strong>W</strong></li>\n<li>Po treningu, <strong>wiersz macierzy W odpowiadający danemu słowu to jego embedding</strong></li>\n</ol>\n<p>Brzmi skomplikowanie? Zróbmy to w kodzie - od zera, bez żadnych bibliotek ML:</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. Słownik i one-hot encoding ---</a-c>\n<a-v>corpus</a-v> <a-o>=</a-o> <a-s>&quot;kot siedzi na macie i śpi pies siedzi na dywanie i śpi kot biega po pokoju pies biega po parku&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;kot&quot;: </a-s><a-p>{</a-p><a-f>one_hot</a-f><a-eb>(</a-eb><a-s>&quot;kot&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.]  - tylko jedna jedynka!</a-c>\n\n<a-c># --- 2. Macierz wag (to będą nasze embeddingi) ---</a-c>\n<a-v>embed_dim</a-v> <a-o>=</a-o> <a-n>5</a-n>  <a-c># w prawdziwym Word2Vec to 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># słownik → 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 → słownik</a-c>\n\n<a-c># --- 3. Pary treningowe (CBOW: kontekst → środek) ---</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;Kontekst: </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> → Cel: </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># Kontekst: [&#39;kot&#39;, &#39;siedzi&#39;, &#39;macie&#39;, &#39;i&#39;] → Cel: na</a-c>\n\n<a-c># --- 4. Trening (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: uśrednione embeddingi kontekstu → przewidujemy środek</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: aktualizujemy wagi</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. Wynik: embeddingi! ---</a-c>\n<a-k>for</a-k> <a-v>word</a-v> <a-o>in</a-o> [<a-s>&quot;kot&quot;</a-s>, <a-s>&quot;pies&quot;</a-s>, <a-s>&quot;siedzi&quot;</a-s>, <a-s>&quot;biega&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. Podobieństwo kosinusowe ---</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;kot ↔ pies: </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;kot&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;pies&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;kot ↔ siedzi: </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;kot&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;siedzi&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;kot ↔ biega: </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;kot&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;biega&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;kot&quot;: [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]\nKontekst: ['kot', 'siedzi', 'macie', 'i'] → Cel: na\nkot: [-2.866 -0.264 -0.225 -1.822  2.783]\npies: [-1.133  0.678 -1.537  1.989  2.241]\nsiedzi: [ 1.524 -0.023  1.804 -2.355 -0.788]\nbiega: [ 2.     4.098 -0.315  2.971  0.815]\nkot ↔ pies: 0.378\nkot ↔ siedzi: -0.177\nkot ↔ biega: -0.407\n</code></pre>\n<p>Zauważcie: <strong>kot i pies</strong> (0.378) są bardziej podobne niż <strong>kot i biega</strong> (-0.407). Sieć sama odkryła, że &quot;kot&quot; i &quot;pies&quot; to zwierzęta, bo występują w podobnym kontekście. Zero etykiet, zero nadzoru - tylko tekst i matematyka.</p>\n<div class=\"markdown-alert markdown-alert-note\">\n<p class=\"markdown-alert-title\">Note</p>\n<p>To jest <strong>cały Word2Vec w ~30 linijkach</strong>. Prawdziwy Word2Vec dodaje jeszcze negative sampling (bo softmax na 50 000 słów jest wolny) i optymalizacje, ale zasada jest dokładnie ta sama.</p>\n</div>\n<div class=\"markdown-alert markdown-alert-note\">\n<p class=\"markdown-alert-title\">Note</p>\n<p><strong>&quot;Fake task&quot;</strong>: Interesuje nas nie to, co sieć przewiduje, ale <strong>wagi ukrytej warstwy</strong>. Sieć trenujemy na &quot;sztucznym&quot; zadaniu przewidywania kontekstu, ale to, co chcemy wyciągnąć, to macierz W - nasze embeddingi. To trochę jak trenowanie kogoś do rozwiązywania krzyżówek nie po to, żeby dobrze rozwiązywał krzyżówki, ale po to, żeby poszerzył słownik ;-)</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>Kierunek</strong></td>\n<td>Kontekst → Słowo środkowe</td>\n<td>Słowo środkowe → Kontekst</td>\n</tr>\n<tr>\n<td><strong>Szybkość</strong></td>\n<td>Szybszy</td>\n<td>Wolniejszy</td>\n</tr>\n<tr>\n<td><strong>Rzadkie słowa</strong></td>\n<td>Radzi sobie gorzej</td>\n<td>Radzi sobie lepiej</td>\n</tr>\n<tr>\n<td><strong>Częste słowa</strong></td>\n<td>Radzi sobie lepiej</td>\n<td>Radzi sobie gorzej</td>\n</tr>\n<tr>\n<td><strong>Kiedy użyć</strong></td>\n<td>Duży korpus, częste słowa</td>\n<td>Mały korpus, rzadkie słowa</td>\n</tr>\n</tbody>\n</table>\n<p>Według oryginalnej pracy Mikolova et al.: <strong>Skip-gram jest lepszy dla rzadkich słów i małych zbiorów danych. CBOW jest szybszy i lepszy dla częstych słów.</strong></p>\n<h3><a href=\"#magiczna-przestrzeń-wektorowa\" aria-hidden=\"true\" class=\"anchor\" id=\"magiczna-przestrzeń-wektorowa\"></a>Magiczna przestrzeń wektorowa</h3>\n<p>Po wytrenowaniu Word2Vec, każde słowo to wektor (np. 300 liczb). I ta przestrzeń ma niezwykłe właściwości:</p>\n<p><strong>Słowa podobne są blisko:</strong></p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">odległość(&quot;kot&quot;, &quot;pies&quot;) &lt; odległość(&quot;kot&quot;, &quot;samochód&quot;)\nodległość(&quot;Polska&quot;, &quot;Niemcy&quot;) &lt; odległość(&quot;Polska&quot;, &quot;banan&quot;)\n</code></pre>\n<p><strong>Analogie działają jak dodawanie i odejmowanie wektorów:</strong></p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">wektor(&quot;król&quot;) - wektor(&quot;mężczyzna&quot;) + wektor(&quot;kobieta&quot;) ≈ wektor(&quot;królowa&quot;)\n</code></pre>\n<p>Dlaczego? Bo wektor &quot;króla&quot; koduje wiele wymiarów znaczeniowych - w jednym z nich jest informacja o &quot;rodzaju&quot; (mężczyzna/kobieta), w innym o &quot;władzy&quot; (monarchia). Odejmując &quot;mężczyznę&quot; i dodając &quot;kobietę&quot;, zmieniamy wymiar rodzaju, zachowując pozostałe.</p>\n<p>Inne przykłady analogii:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">Polska - Warszawa + Berlin ≈ Niemcy\nmały - mniejszy + duży ≈ większy\nłódź - woda + powietrze ≈ samolot\n</code></pre>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph TD\n    K[&quot;👑 król&lt;br/&gt;[0.50, 0.68, ...]&quot;] --&gt;|&quot;odjąć 'mężczyzna'&quot;| KM[&quot;[0.35, 0.55, ...]&lt;br/&gt;&lt;i&gt;monarcha, ale bez rodzaju&lt;/i&gt;&quot;]\n    M[&quot;👨 mężczyzna&lt;br/&gt;[0.15, 0.13, ...]&quot;]\n    W[&quot;👩 kobieta&lt;br/&gt;[0.12, 0.20, ...]&quot;]\n    KM --&gt;|&quot;dodać 'kobieta'&quot;| Q[&quot;👸 królowa&lt;br/&gt;[0.38, 0.64, ...]&quot;]\n    \n    K -.- M\n    Q -.- W\n    \n    style K fill:#ff9999,color:#000\n    style M fill:#ffcc99,color:#000\n    style W fill:#ccff99,color:#000\n    style Q fill:#99ccff,color:#000\n</code></pre>\n<p>To jest to, o czym mówiliśmy w <a href=\"cechy-jezykowe-a-llm.html\">pierwszym poście</a> przy okazji semantyki. Teraz widzicie, <strong>jak to działa pod spodem</strong>.</p>\n<h3><a href=\"#word2vec-w-kodzie\" aria-hidden=\"true\" class=\"anchor\" id=\"word2vec-w-kodzie\"></a>Word2Vec w kodzie</h3>\n<p>Najpierw wytrenujmy własny model na małym korpusie, żeby zobaczyć jak to działa od zera:</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;kot&quot;</a-s>, <a-s>&quot;siedzi&quot;</a-s>, <a-s>&quot;na&quot;</a-s>, <a-s>&quot;macie&quot;</a-s>, <a-s>&quot;i&quot;</a-s>, <a-s>&quot;śpi&quot;</a-s>],\n    [<a-s>&quot;pies&quot;</a-s>, <a-s>&quot;siedzi&quot;</a-s>, <a-s>&quot;na&quot;</a-s>, <a-s>&quot;dywanie&quot;</a-s>, <a-s>&quot;i&quot;</a-s>, <a-s>&quot;śpi&quot;</a-s>],\n    [<a-s>&quot;kot&quot;</a-s>, <a-s>&quot;biega&quot;</a-s>, <a-s>&quot;po&quot;</a-s>, <a-s>&quot;pokoju&quot;</a-s>],\n    [<a-s>&quot;pies&quot;</a-s>, <a-s>&quot;biega&quot;</a-s>, <a-s>&quot;po&quot;</a-s>, <a-s>&quot;parku&quot;</a-s>],\n    [<a-s>&quot;kot&quot;</a-s>, <a-s>&quot;i&quot;</a-s>, <a-s>&quot;pies&quot;</a-s>, <a-s>&quot;bawią&quot;</a-s>, <a-s>&quot;się&quot;</a-s>, <a-s>&quot;w&quot;</a-s>, <a-s>&quot;ogrodzie&quot;</a-s>],\n    [<a-s>&quot;kot&quot;</a-s>, <a-s>&quot;je&quot;</a-s>, <a-s>&quot;karmę&quot;</a-s>, <a-s>&quot;z&quot;</a-s>, <a-s>&quot;miski&quot;</a-s>],\n    [<a-s>&quot;pies&quot;</a-s>, <a-s>&quot;je&quot;</a-s>, <a-s>&quot;karmę&quot;</a-s>, <a-s>&quot;z&quot;</a-s>, <a-s>&quot;miski&quot;</a-s>],\n    [<a-s>&quot;kot&quot;</a-s>, <a-s>&quot;łapie&quot;</a-s>, <a-s>&quot;mysz&quot;</a-s>, <a-s>&quot;w&quot;</a-s>, <a-s>&quot;domu&quot;</a-s>],\n    [<a-s>&quot;pies&quot;</a-s>, <a-s>&quot;goni&quot;</a-s>, <a-s>&quot;kota&quot;</a-s>, <a-s>&quot;po&quot;</a-s>, <a-s>&quot;ogrodzie&quot;</a-s>],\n    [<a-s>&quot;ptak&quot;</a-s>, <a-s>&quot;siedzi&quot;</a-s>, <a-s>&quot;na&quot;</a-s>, <a-s>&quot;gałęzi&quot;</a-s>, <a-s>&quot;drzewa&quot;</a-s>],\n    [<a-s>&quot;ryba&quot;</a-s>, <a-s>&quot;pływa&quot;</a-s>, <a-s>&quot;w&quot;</a-s>, <a-s>&quot;akwarium&quot;</a-s>],\n    [<a-s>&quot;samochód&quot;</a-s>, <a-s>&quot;jeździ&quot;</a-s>, <a-s>&quot;po&quot;</a-s>, <a-s>&quot;drodze&quot;</a-s>],\n    [<a-s>&quot;rower&quot;</a-s>, <a-s>&quot;jeździ&quot;</a-s>, <a-s>&quot;po&quot;</a-s>, <a-s>&quot;ścieżce&quot;</a-s>],\n    [<a-s>&quot;kot&quot;</a-s>, <a-s>&quot;mruczy&quot;</a-s>, <a-s>&quot;gdy&quot;</a-s>, <a-s>&quot;głaszczesz&quot;</a-s>, <a-s>&quot;go&quot;</a-s>],\n    [<a-s>&quot;pies&quot;</a-s>, <a-s>&quot;merda&quot;</a-s>, <a-s>&quot;ogonem&quot;</a-s>, <a-s>&quot;gdy&quot;</a-s>, <a-s>&quot;widzi&quot;</a-s>, <a-s>&quot;pana&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># wymiar wektora (mały = edukacyjny)</a-c>\n    <a-v>window</a-v><a-o>=</a-o><a-n>3</a-n>,         <a-c># rozmiar okna kontekstowego</a-c>\n    <a-v>min_count</a-v><a-o>=</a-o><a-n>1</a-n>,      <a-c># ignoruj słowa rzadsze niż 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># ile przejść po danych</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;kot&quot;</a-s>, <a-v>topn</a-v><a-o>=</a-o><a-n>3</a-n>))\n<a-c># [(&#39;pies&#39;, 0.96), (&#39;śpi&#39;, 0.95), (&#39;na&#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;kot&quot;</a-s>, <a-s>&quot;pies&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;kot&quot;</a-s>, <a-s>&quot;samochód&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>Zauważcie: nasz korpus jest MALUTKI (15 zdań), więc wyniki są dalekie od idealnych — &quot;kot&quot; i &quot;samochód&quot; mają aż 0.83 podobieństwa, co jest bez sensu. Ale model poprawnie stawia &quot;pies&quot; najbliżej &quot;kot&quot;! Na prawdziwych korpusach (miliardy zdań) te wektory stają się bardzo dokładne.</p>\n</div>\n<p>A tak korzystamy z <strong>gotowego modelu</strong> wytrenowanego na miliardach słów:</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>Możecie też sprawdzić podobieństwo między słowami:</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; i &quot;dog&quot; - podobieństwo 0.92. &quot;Cat&quot; i &quot;car&quot; - 0.15. Model &quot;wie&quot;, że kot jest bliżej psa niż samochodu.</p>\n<div class=\"markdown-alert markdown-alert-tip\">\n<p class=\"markdown-alert-title\">Tip</p>\n<p><strong>Eksperyment:</strong> Wejdźcie na <a href=\"https://projector.tensorflow.org/\">TensorFlow Embedding Projector</a> - to interaktywna wizualizacja przestrzeni wektorowej. Możecie wpisywać słowa i widzieć, co jest blisko. To jest JEDNA z najpiękniejszych wizualizacji w całym ML. Serio, sprawdźcie!</p>\n</div>\n<div class=\"markdown-alert markdown-alert-warning\">\n<p class=\"markdown-alert-title\">Warning</p>\n<p><strong>Uwaga na uprzedzenia:</strong> Word2Vec uczy się z danych. Jeśli w danych treningowych &quot;programista&quot; częściej występuje blisko &quot;mężczyzna&quot; niż &quot;kobieta&quot; - to model to wyłapie. Słynny przykład: <code>wektor(&quot;programista&quot;) - wektor(&quot;mężczyzna&quot;) + wektor(&quot;kobieta&quot;) ≈ &quot;gospodyni domowa&quot;</code>. To jest uprzedzenie ukryte w danych, które model bezrefleksyjnie reprodukuje. Pamiętacie semiosferę z <a href=\"semiotyka-a-llm.html\">drugiego posta</a>? Semiosfera nie jest neutralna - i dane treningowe też nie są.</p>\n</div>\n<h3><a href=\"#od-rzadkich-wektorów-do-gęstych\" aria-hidden=\"true\" class=\"anchor\" id=\"od-rzadkich-wektorów-do-gęstych\"></a>Od rzadkich wektorów do gęstych</h3>\n<p>Zróbmy jeszcze raz porównanie, żeby to sobie utrwalić:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph LR\n    subgraph &quot;BoW / TF-IDF: rzadki wektor&quot;\n        R[&quot;'Kot siedzi na macie'&quot;] --&gt; RV[&quot;[0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, ...]&lt;br/&gt;Długość = rozmiar słownika&lt;br/&gt;Większość to ZERA&quot;]\n    end\n    subgraph &quot;Word2Vec: gęsty wektor&quot;\n        D[&quot;'kot'&quot;] --&gt; DV[&quot;[0.23, -0.45, 0.67, 0.12, -0.89, ...]&lt;br/&gt;Długość = 300&lt;br/&gt;Wszystkie wartości NIEZEROWE&quot;]\n    end\n    \n    style RV fill:#ff9999,color:#000\n    style DV fill:#99ff99,color:#000\n</code></pre>\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>Typ</strong></td>\n<td>Rzadki (sparse)</td>\n<td>Gęsty (dense)</td>\n</tr>\n<tr>\n<td><strong>Długość wektora</strong></td>\n<td>= rozmiar słownika (może być 100 000+)</td>\n<td>= wymiar embeddingu (zwykle 100-300)</td>\n</tr>\n<tr>\n<td><strong>Wartości</strong></td>\n<td>Głównie zera</td>\n<td>Wszystkie niezerowe</td>\n</tr>\n<tr>\n<td><strong>Podobieństwo</strong></td>\n<td>Trudno uchwycić</td>\n<td>Kosinusowe podobieństwo działa super</td>\n</tr>\n<tr>\n<td><strong>Kontekst</strong></td>\n<td>Brak</td>\n<td>Uchwycony przez okno kontekstowe</td>\n</tr>\n<tr>\n<td><strong>Analogie</strong></td>\n<td>Nie ma</td>\n<td>Działają (król - mężczyzna + kobieta ≈ królowa)</td>\n</tr>\n</tbody>\n</table>\n<h3><a href=\"#czy-word2vec-potrafi-generować-tekst\" aria-hidden=\"true\" class=\"anchor\" id=\"czy-word2vec-potrafi-generować-tekst\"></a>Czy Word2Vec potrafi generować tekst?</h3>\n<p>Nie. Word2Vec tworzy <strong>mapę znaczeń</strong> — mówi ci, że &quot;kot&quot; jest blisko &quot;pies&quot;, ale nie potrafi ułożyć z tego zdania. To jak słownik synonimów: wiesz co jest podobne, ale nie napiszesz wiersza.</p>\n<p>Ale... co jeśli połączymy Word2Vec z łańcuchami Markowa? Łańcuch Markowa wybiera następne słowo na podstawie częstotliwości. A jeśli zamiast losować równie, sprawimy, że słowa bardziej podobne do kontekstu będą miały większą szansę?</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;kot siedzi na macie i śpi&quot;</a-s>,\n    <a-s>&quot;pies siedzi na dywanie i śpi&quot;</a-s>,\n    <a-s>&quot;kot biega po pokoju&quot;</a-s>,\n    <a-s>&quot;pies biega po parku&quot;</a-s>,\n    <a-s>&quot;kot i pies bawią się w ogrodzie&quot;</a-s>,\n    <a-s>&quot;kot je karmę z miski&quot;</a-s>,\n    <a-s>&quot;pies je karmę z miski&quot;</a-s>,\n    <a-s>&quot;kot łapie mysz w domu&quot;</a-s>,\n    <a-s>&quot;pies goni kota po ogrodzie&quot;</a-s>,\n    <a-s>&quot;ptak siedzi na gałęzi drzewa&quot;</a-s>,\n    <a-s>&quot;kot patrzy przez okno i mruczy&quot;</a-s>,\n    <a-s>&quot;pies szczeka na listonosza&quot;</a-s>,\n    <a-s>&quot;kot śpi cały dzień na kanapie&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># budujemy macierz przejść (jak w łańcuchach Markowa)</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># uśredniony wektor ostatnich słów = kontekst</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># punktujemy kandydatów za podobieństwo do kontekstu</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># losujemy, ale z wagami — podobne słowa mają większą szansę</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;kot&quot;</a-s>))</code></pre>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">kot siedzi na macie i śpi\nkot patrzy przez okno i mruczy\nkot je karmę z miski\nkot śpi cały dzień na kanapie\nkot łapie mysz w domu\n</code></pre>\n<p>Nie jest to Szekspir, ale teksty są bardziej spójne niż czysty losowy Markow. Idea jest prosta:</p>\n<ol>\n<li>Łańcuch Markowa daje <strong>kandydatów</strong> na następne słowo</li>\n<li>Word2Vec <strong>punktuje</strong> kandydatów na podstawie podobieństwa do kontekstu</li>\n<li>Wybieramy losowo, ale z <strong>obciążeniem</strong> — słowa bardziej pasujące mają większą szansę</li>\n</ol>\n<p>To jest malutki krok w kierunku tego, co robią dzisiejsze LLM-y. One też przewidują następne słowo, ale zamiast prostej macierzy przejścia mają wielowarstwowe sieci Transformer z mechanizmem uwagi. Tam &quot;punktowanie kandydatów&quot; jest o wiele, wiele bardziej zaawansowane.</p>\n<div class=\"markdown-alert markdown-alert-important\">\n<p class=\"markdown-alert-title\">Important</p>\n<p><strong>Kluczowa różnica:</strong> Word2Vec daje każdemu słowu JEDEN wektor. Ale &quot;zamek&quot; w &quot;zamek w drzwiach&quot; i &quot;zamek na wzgórzu&quot; to zupełnie inne znaczenia! Word2Vec nie rozróżnia — dlatego w kolejnych wpisach wejdziemy w Transformer-y, gdzie <strong>kontekst zmienia znaczenie</strong> każdego słowa.</p>\n</div>\n<hr />\n<h2><a href=\"#podsumowanie---cała-droga-w-jednym-miejscu\" aria-hidden=\"true\" class=\"anchor\" id=\"podsumowanie---cała-droga-w-jednym-miejscu\"></a>Podsumowanie - cała droga w jednym miejscu</h2>\n<p>Oto nasza mapa drogowa, od cięcia tekstu do geometrii znaczeń:</p>\n<table>\n<thead>\n<tr>\n<th>Metoda</th>\n<th>Co robi</th>\n<th>Kontekst?</th>\n<th>Czego nie umie</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>Tokenizacja (BPE)</strong></td>\n<td>Tnie tekst na kawałki</td>\n<td>Nie</td>\n<td>Nie rozumie, co tnie</td>\n</tr>\n<tr>\n<td><strong>BoW / TF-IDF</strong></td>\n<td>Liczy słowa, waży rzadkością</td>\n<td>Nie</td>\n<td>Ignoruje kolejność</td>\n</tr>\n<tr>\n<td><strong>N-gramy</strong></td>\n<td>Patrzy na sekwencje słów</td>\n<td>Lokalny (2-3 słowa)</td>\n<td>Krótki kontekst, szybko rosnący słownik</td>\n</tr>\n<tr>\n<td><strong>Naive Bayes</strong></td>\n<td>Klasyfikuje na podstawie prawdopodobieństwa</td>\n<td>Nie (słowa &quot;niezależne&quot;)</td>\n<td>Nie łapie zależności między słowami</td>\n</tr>\n<tr>\n<td><strong>Word2Vec</strong></td>\n<td>Zamienia słowa w wektory znaczeniowe</td>\n<td>Tak (okno kontekstowe)</td>\n<td>Jedno słowo = jeden wektor (ignoruje wieloznaczność)</td>\n</tr>\n</tbody>\n</table>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph TD\n    subgraph &quot;Ewolucja: od liczenia do rozumienia&quot;\n        T[&quot;✂️ Tokenizacja&lt;br/&gt;&lt;i&gt;tekst → kawałki&lt;/i&gt;&quot;] --&gt;|&quot;jak policzyć?&quot;| B[&quot;🔢 BoW / TF-IDF&lt;br/&gt;&lt;i&gt;kawałki → liczby&lt;/i&gt;&quot;]\n        B --&gt;|&quot;brak kontekstu!&quot;| N[&quot;🔗 N-gramy / Markow&lt;br/&gt;&lt;i&gt;dodajemy kolejność&lt;/i&gt;&quot;]\n        N --&gt;|&quot;chcemy klasyfikować!&quot;| NB[&quot;📊 Naive Bayes&lt;br/&gt;&lt;i&gt;prawdopodobieństwo klas&lt;/i&gt;&quot;]\n        NB --&gt;|&quot;słowa to nie liczby!&quot;| W[&quot;📐 Word2Vec&lt;br/&gt;&lt;i&gt;geometria znaczeń&lt;/i&gt;&quot;]\n        W --&gt;|&quot;co dalej?&quot;| LLM[&quot;🤖 Transformers / LLM&lt;br/&gt;&lt;i&gt;kontekst + attention + skala&lt;/i&gt;&quot;]\n    end\n    \n    style T fill:#ff9999,color:#000\n    style B fill:#ffcc99,color:#000\n    style N fill:#ffff99,color:#000\n    style NB fill:#ccff99,color:#000\n    style W fill:#99ccff,color:#000\n    style LLM fill:#9999ff,color:#fff\n</code></pre>\n<p>I kluczowa perspektywa: <strong>z każdej z tych metod LLM wziął coś dla siebie.</strong></p>\n<ul>\n<li><strong>Tokenizacja (BPE)</strong> to pierwszy krok pipeline'u każdego LLM. ChatGPT tnie wasz tekst na tokeny ZANIM cokolwiek z nim zrobi.</li>\n<li><strong>TF-IDF i BoW</strong> to fundamenty myślenia o tekście jako o liczbach. Bez tego pomysłu, że słowa można &quot;policzyć&quot;, nie byłoby uczenia reprezentacji (representation learning).</li>\n<li><strong>N-gramy i łańcuchy Markowa</strong> to pierwowzór przewidywania następnego tokena. To jest dokładnie to, co robi LLM - tylko że LLM ma kontekst na tysiące tokenów, nie na 2-3.</li>\n<li><strong>Naive Bayes</strong> pokazał, że probabilistyczne podejście do tekstu działa zaskakująco dobrze. LLM też jest modelem probabilistycznym - przewiduje prawdopodobieństwo następnego tokena.</li>\n<li><strong>Word2Vec</strong> to przodek tego, co dziś nazywamy &quot;embedding layer&quot; w Transformerach. GPT nie używa już Word2Vec jako osobnego kroku, ale jego warstwa embeddingowa realizuje tę samą ideę: token → wektor.</li>\n</ul>\n<p><strong>LLM nie spadł z nieba. Stoi na barkach gigantów.</strong></p>\n<div class=\"markdown-alert markdown-alert-important\">\n<p class=\"markdown-alert-title\">Important</p>\n<p><strong>Co w następnym wpisie?</strong> Wejdziemy w to, co dzieje się, gdy te wszystkie pomysły połączymy z mechanizmem <strong>uwagi (attention)</strong> i sieciami <strong>Transformer</strong>. Bo od Word2Vec (2013) do &quot;Attention Is All You Need&quot; (2017) do ChatGPT (2022) jest &quot;tylko&quot; kilkanaście lat i kilka przełomowych pomysłów ;-)</p>\n</div>\n<hr />\n<p>Wiem, że tego posta jest dużo, ale chciałem, żeby ta droga od &quot;tnięcia tekstu&quot; do &quot;wektorów znaczeń&quot; była pełna i zrozumiała.</p>\n<p>Mam nadzieję, że chociaż kilka razy powiecie &quot;aha!&quot;. Jeśli coś jest niejasne - <strong>napiszcie w komentarzach</strong>, postaram się wyjaśnić. A jeśli macie lepsze przykłady analogii Word2Vec - tym bardziej dajcie znać.</p>\n<p>Która metoda was najbardziej zaskoczyła? Czy wiedzieliście, że ChatGPT &quot;myśli&quot; w tokenach, nie w słowach? I że jego &quot;myślenie&quot; to tak naprawdę potomek łańcuchów Markowa?</p>\n<p>Do następnego!</p>\n<hr />\n<p><strong>Źródła i ciekawe linki:</strong></p>\n<p>Jeśli chcecie wejść głębiej, oto materiały, z których korzystałem:</p>\n<ul>\n<li><a href=\"https://huggingface.co/learn/llm-course/chapter6/5\">Byte-Pair Encoding tokenization — Hugging Face</a> - świetne, krokowe wprowadzenie do BPE z kodem</li>\n<li><a href=\"https://huggingface.co/docs/transformers/en/tokenizer_summary\">Tokenization algorithms — Hugging Face</a> - przegląd 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> - implementacja BPE od zera w Python, bardzo edukacyjna</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> - czytelne wprowadzenie do TF-IDF z przykładami</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> - super artykuł o n-gramach z generatorem tekstu</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> - porównanie TF-IDF vs n-gramy z kodem</li>\n<li><a href=\"https://www.baeldung.com/cs/word-embeddings-cbow-vs-skip-gram\">Word Embeddings: CBOW vs Skip-Gram — Baeldung</a> - klarowne porównanie obu architektur Word2Vec</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> - dobre obrazki i intuicja dla 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> - kontrast między klasycznymi cechami a embeddingami</li>\n<li><a href=\"https://projector.tensorflow.org/\">TensorFlow Embedding Projector</a> - interaktywna wizualizacja przestrzeni wektorowej (obowiązkowe!)</li>\n</ul>\n<section class=\"footnotes\" data-footnotes>\n<ol>\n<li id=\"fn-1\">\n<p>Odpowiedzi do quizu tokenizacyjnego: 1) &quot;klasa&quot; → [&quot;k&quot;, &quot;las&quot;, &quot;a&quot;] (l+a→la, la+s→las, ale &quot;k&quot; nie pasuje do żadnego scalenia) 2) &quot;lata&quot; → [&quot;lat&quot;, &quot;a&quot;] (l+a→la, la+t→lat) 3) &quot;laska&quot; → [&quot;las&quot;, &quot;k&quot;, &quot;a&quot;] (l+a→la, la+s→las, ale &quot;las&quot;+&quot;k&quot; nie ma w scaleniach, więc zostaje podzielone). <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": "",
      "date_published": "2026-06-09T00:00:00-00:00",
      "image": "",
      "authors": [
        {
          "name": "Błażej Gruszka",
          "url": "https://www.linkedin.com/in/blazejgruszka/",
          "avatar": "https://github.com/bgruszka.png"
        }
      ],
      "tags": [
        "llm",
        "ai",
        "nlp",
        "tokenizacja",
        "word2vec",
        "embeddings",
        "tf-idf",
        "markow",
        "bayes",
        "language-models"
      ],
      "language": "pl"
    },
    {
      "id": "https://gruszka.dev/semiotyka-a-llm.html",
      "url": "https://gruszka.dev/semiotyka-a-llm.html",
      "title": "Semiotyka - dlaczego LLM nie \"myśli\", ale jednak coś znaczy",
      "content_html": "<p>W poprzednim wpisie zbudowaliśmy naszą językową cebulkę - pięć warstw, od fonetyki po pragmatykę, i zobaczyliśmy, jak LLM radzi sobie z każdą z nich. Ale po napisaniu tamtego posta zostało mi jedno wielkie &quot;ale...&quot; w głowie.</p>\n<p>Bo przecież - <strong>czy LLM w ogóle &quot;rozumie&quot; to, co generuje?</strong> Czy ma jakiś wewnętrzny model świata? Czy myśli?</p>\n<p>I wtedy wpadłem na semiotykę. I okazuje się, że semiotyka daje nam genialną ramę do myślenia o LLM - nie jako o sztucznym umyśle, ale jako o <strong>maszynie znaków</strong>. I nagle wszystko zaczyna mieć sens. Albo przynajmniej ma bardziej sens niż wcześniej ;-)</p>\n<p>To jest <strong>drugi wpis z serii &quot;zrozumiec LLM&quot;</strong>. Dzisiaj zmieniamy perspektywę: zamiast patrzeć na <em>warstwy języka</em>, patrzymy na samą naturę tego, czym są <strong>znaki</strong> i jak <strong>znaczenie</strong> w ogóle powstaje. I dlaczego to jest kluczowe do zrozumienia, czym LLM jest - i czym nie jest.</p>\n<hr />\n<h2><a href=\"#czym-jest-semiotyka\" aria-hidden=\"true\" class=\"anchor\" id=\"czym-jest-semiotyka\"></a>Czym jest semiotyka?</h2>\n<p>Zanim wejdziemy w LLM, musimy załatwić podstawy. Bo semiotyka to jedno z tych słów, które brzmi mądrze, ale co właściwie znaczy?</p>\n<p><strong>Semiotyka</strong> to nauka o znakach i o tym, jak znaki tworzą znaczenie. To wszystko. Nie brzmi już tak strasznie, prawda? ;-)</p>\n<p>A &quot;znak&quot; w semiotyce to wszystko, co <em>coś oznacza</em>. Coś, co stoi za czymś innym. Proste przykłady:</p>\n<ul>\n<li>🔴 Czerwone światło na skrzyżowaniu = <strong>STOP</strong></li>\n<li>😂 Emoji z łzami = <strong>śmieję się</strong> (albo: <em>umieram ze śmiechu</em>)</li>\n<li>💨 Zapach dymu = <strong>ogień gdzieś w pobliżu</strong></li>\n<li>🐾 Ślady łap na śniegu = <strong>tu przeszedł pies</strong> (albo wilk, albo... better not think about it :D)</li>\n</ul>\n<p>Każdy z tych znaków <em>reprezentuje coś innego</em>. I to &quot;reprezentowanie&quot; to jest właśnie to, co semiotyka bada.</p>\n<div class=\"markdown-alert markdown-alert-tip\">\n<p class=\"markdown-alert-title\">Tip</p>\n<p><strong>Eksperyment:</strong> Rozejrzyjcie się wokół siebie - ile znaków widzicie w tej chwili? Ja w tym momencie widzę: ikonę WiFi (mam internet), powiadomienie na telefonie (ktoś napisał), logo na kubku z kawą (marka). Trzy znaki i nawet nie wstałem z krzesła.</p>\n</div>\n<h3><a href=\"#semiotyka-vs-semantyka\" aria-hidden=\"true\" class=\"anchor\" id=\"semiotyka-vs-semantyka\"></a>Semiotyka vs semantyka</h3>\n<p>W poprzednim wpisie mieliśmy semantykę - badanie znaczenia słów i zdań. Więc czym semiotyka się różni?</p>\n<p>Krótko:</p>\n<table>\n<thead>\n<tr>\n<th></th>\n<th>Co pyta</th>\n<th>Przykład</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>Semantyka</strong></td>\n<td>&quot;Co to znaczy?&quot;</td>\n<td>Co znaczy słowo &quot;zamek&quot;?</td>\n</tr>\n<tr>\n<td><strong>Semiotyka</strong></td>\n<td>&quot;Jak ten znak w ogóle działa?&quot;</td>\n<td>Jak to się dzieje, że czerwone światło OZNACZA &quot;stop&quot;?</td>\n</tr>\n</tbody>\n</table>\n<p>Semantyka pyta o konkretne znaczenia. Semiotyka pyta o <strong>mechanizm znaczenia</strong> - jak to się dzieje, że cokolwiek cokolwiek oznacza.</p>\n<p>I właśnie dlatego semiotyka jest tak ważna dla zrozumienia LLM. Bo pytanie nie brzmi &quot;co LLM znaczy&quot;, ale &quot;<strong>jak LLM operuje na znakach</strong>&quot;.</p>\n<hr />\n<h2><a href=\"#dwóch-gigantów-saussure-vs-peirce\" aria-hidden=\"true\" class=\"anchor\" id=\"dwóch-gigantów-saussure-vs-peirce\"></a>Dwóch gigantów: Saussure vs Peirce</h2>\n<p>W semiotyce są dwie główne tradycje, które musicie znać. Dwa podejścia, dwa sposoby myślenia o znakach. I uwaga - oba są ważne dla zrozumienia LLM, ale każdy z innej strony.</p>\n<h3><a href=\"#ferdinand-de-saussure-znak-jako-para\" aria-hidden=\"true\" class=\"anchor\" id=\"ferdinand-de-saussure-znak-jako-para\"></a>Ferdinand de Saussure: znak jako para</h3>\n<p>Saussure (szwajcarski lingwista, żył na przełomie XIX i XX wieku) powiedział: <strong>znak składa się z dwóch części</strong>.</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph LR\n    Z[&quot;SIGN&lt;br/&gt;&lt;i&gt;znak&lt;/i&gt;&quot;] --&gt; SI[&quot;SIGNIFIANT&lt;br/&gt;&lt;i&gt;znaczące&lt;/i&gt;&lt;br/&gt;forma: dźwięk, tekst&quot;]\n    Z --&gt; SE[&quot;SIGNIFIE&lt;br/&gt;&lt;i&gt;znaczone&lt;/i&gt;&lt;br/&gt;koncept, pojęcie&quot;]\n    \n    style Z fill:#9999ff,color:#fff\n    style SI fill:#ffcc99,color:#000\n    style SE fill:#99ff99,color:#000\n</code></pre>\n<ul>\n<li><strong>Znaczące</strong> (signifiant) - forma znaku. To, co widzisz, słyszysz, dotykasz. Np. ciąg liter &quot;k-o-t&quot; albo dźwięk /kot/.</li>\n<li><strong>Znaczone</strong> (signifié) - koncept, pojęcie, które ta forma wywołuje w twojej głowie. Np. futrzasty zwierzak, który miauczy i ignoruje cię przez większość dnia :D</li>\n</ul>\n<p>I kluczowa rzecz: <strong>relacja między znaczącym a znaczonym jest arbitralna</strong>. Nie ma żadnego logicznego powodu, dla którego ciąg liter &quot;k-o-t&quot; oznacza właśnie tego zwierzaka. Po prostu... tak się przyjęło. W angielskim to &quot;cat&quot;, w niemieckim &quot;Katze&quot;, w japońskim &quot;猫&quot; (neko) - każdy język ma inny ciąg dźwięków/liter na to samo pojęcie.</p>\n<p>Ale Saussure mówi coś jeszcze ważniejszego: <strong>znaczenie słowa wynika z jego relacji do innych słów w systemie</strong>. &quot;Kot&quot; znaczy to, co znaczy, bo NIE jest &quot;psem&quot;, NIE jest &quot;domem&quot;, NIE jest &quot;samochodem&quot;. Znaczenie jest <strong>różnicowe</strong> - wynika z różnicy.</p>\n<blockquote>\n<p>To brzmi abstrakcyjnie, ale za chwilę zobaczycie, że to jest <strong>dokładnie</strong> to, co robią embeddingi w LLM. Naprawdę ;-)</p>\n</blockquote>\n<h3><a href=\"#charles-sanders-peirce-znak-jako-proces\" aria-hidden=\"true\" class=\"anchor\" id=\"charles-sanders-peirce-znak-jako-proces\"></a>Charles Sanders Peirce: znak jako proces</h3>\n<p>Peirce (amerykański filozof, trochę wcześniej niż Saussure, ale mniej więcej w tym samym czasie) miał inne podejście. Dla niego znak nie jest statyczną parą, ale <strong>dynamicznym procesem</strong>.</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph TD\n    R[&quot;REPRESENTAMEN&lt;br/&gt;&lt;i&gt;znak&lt;/i&gt;&lt;br/&gt;forma, którą widzisz&quot;] --&gt; O[&quot;OBJECT&lt;br/&gt;&lt;i&gt;obiekt&lt;/i&gt;&lt;br/&gt;to, do czego znak się odnosi&quot;]\n    R --&gt; I[&quot;INTERPRETANT&lt;br/&gt;&lt;i&gt;interpretacja&lt;/i&gt;&lt;br/&gt;efekt w umyśle odbiorcy&quot;]\n    I -.-&gt;|&quot;tworzy nowy znak&quot;| R2[&quot;NOWY REPRESENTAMEN&quot;]\n    \n    style R fill:#ff9999,color:#000\n    style O fill:#99ff99,color:#000\n    style I fill:#9999ff,color:#fff\n</code></pre>\n<p>Triada Peirce'a:</p>\n<ul>\n<li><strong>Representamen</strong> - sam znak, forma (odpowiednik &quot;znaczącego&quot; u Saussure'a)</li>\n<li><strong>Obiekt</strong> - to, do czego znak się odnosi (rzecz w świecie, koncept)</li>\n<li><strong>Interpretant</strong> - efekt, który znak wywołuje w umyśle odbiorcy. I uwaga: ten interpretant SAM staje się nowym znakiem, który znowu ma swój interpretant, i tak dalej... <strong>nieskończony łańcuch interpretacji</strong>.</li>\n</ul>\n<p>I to jest kluczowa różnica: u Saussure'a znak jest statyczny (para), u Peirce'a - <strong>dynamiczny, żywy, będący procesem</strong>. Znaczenie nie jest &quot;zawarte&quot; w znaku - ono powstaje w procesie interpretacji.</p>\n<h3><a href=\"#trzy-rodzaje-znaków-według-peircea\" aria-hidden=\"true\" class=\"anchor\" id=\"trzy-rodzaje-znaków-według-peircea\"></a>Trzy rodzaje znaków według Peirce'a</h3>\n<p>Peirce podzielił znaki na trzy kategorie, które są super intuicyjne:</p>\n<table>\n<thead>\n<tr>\n<th>Typ</th>\n<th>Opis</th>\n<th>Przykłady</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>Ikona</strong></td>\n<td>Podobieństwo między znakiem a obiektem</td>\n<td>Portret, mapa, emoji 😺 (wygląda trochę jak kot), ikona folderu na komputerze</td>\n</tr>\n<tr>\n<td><strong>Indeks</strong></td>\n<td>Związek przyczynowo-skutkowy lub fizyczny</td>\n<td>Ślady stóp na piasku (ktoś tu przeszedł), dym (ogień), kaszel (choroba), termometr (temperatura)</td>\n</tr>\n<tr>\n<td><strong>Symbol</strong></td>\n<td>Konwencja, umowa społeczna</td>\n<td>Słowo &quot;kot&quot;, flaga państwowa, czerwone światło = stop, matematyczne &quot;=&quot;</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>Słowo &quot;symbol&quot; w codziennym języku znaczy coś innego niż w semiotyce Peirce'a! W semiotyce symbol to znak oparty <strong>wyłącznie na konwencji</strong> - nie przypomina obiektu (jak ikona) i nie jest z nim fizycznie związany (jak indeks). Flaga Polski nie &quot;przypomina&quot; Polski i nie jest z nią fizycznie połączona - po prostu się umówiliśmy, że te kolory oznaczają to państwo.</p>\n</div>\n<p>Sprawdźcie się - jaki to typ znaku?<sup class=\"footnote-ref\"><a href=\"#fn-1\" id=\"fnref-1\" data-footnote-ref>1</a></sup></p>\n<ol>\n<li>🌡️ Termometr pokazujący 37°C</li>\n<li>📸 Zdjęcie twojego psa</li>\n<li>🟢 Zielone światło = &quot;jedź&quot;</li>\n<li>🐾 Ślady butów na śniegu</li>\n<li>♿ Ikona dostępności</li>\n</ol>\n<hr />\n<h2><a href=\"#hall-of-mirrors---czyli-llm-utknął-w-lustrach\" aria-hidden=\"true\" class=\"anchor\" id=\"hall-of-mirrors---czyli-llm-utknął-w-lustrach\"></a>Hall of Mirrors - czyli LLM utknął w lustrach</h2>\n<p>David Manheim, badacz AI, użył pięknej metafory pochodzącej od semiotyki Peirce'a. Nazwał to <strong>&quot;Hall of Mirrors Problem&quot;</strong> - problemem hali luster.</p>\n<p>Wyobraźcie sobie: jesteście w pokoju pełnym luster. Widzicie odbicia odbić odbić... i nigdzie nie ma okna na zewnątrz. Nie widzicie prawdziwego świata - widzicie tylko... więcej luster.</p>\n<p>To jest dokładnie to, co robi LLM.</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph LR\n    W[&quot;🌍 Świat&lt;br/&gt;&lt;i&gt;rzeczywistość&lt;/i&gt;&quot;] --&gt;|&quot;doświadczenie&quot;| C[&quot;🧑 Ludzie&lt;br/&gt;&lt;i&gt;piszą teksty&lt;/i&gt;&quot;]\n    C --&gt;|&quot;tworzą&quot;| T[&quot;📝 Teksty&lt;br/&gt;&lt;i&gt;dane treningowe&lt;/i&gt;&quot;]\n    T --&gt;|&quot;uczy się na&quot;| LLM[&quot;🤖 LLM&quot;]\n    LLM --&gt;|&quot;generuje&quot;| T2[&quot;📝 Nowe teksty&quot;]\n    T2 --&gt;|&quot;trafiają do&quot;| T\n    \n    style W fill:#99ff99,color:#000\n    style C fill:#ffcc99,color:#000\n    style T fill:#ffff99,color:#000\n    style LLM fill:#ff9999,color:#000\n    style T2 fill:#ffff99,color:#000\n</code></pre>\n<p>Spójrzmy na to przez pryzmat triady Peirce'a:</p>\n<ul>\n<li><strong>Representamen</strong> (znak) - tekst, tokeny, słowa - ✅ LLM ma dostęp</li>\n<li><strong>Obiekt</strong> (rzeczywistość) - świat, doświadczenie, fizyka - ❌ LLM nie ma dostępu</li>\n<li><strong>Interpretant</strong> (interpretacja) - zrozumienie - ❓ LLM generuje coś, co <em>wygląda</em> jak interpretacja</li>\n</ul>\n<p>LLM nigdy nie widział świata. Nigdy nie poczuł smaku truskawki, nie dotknął lodu, nie usłyszał śmiechu. Całą swoją &quot;wiedzę&quot; o świecie czerpie z tekstu - z tego, co <strong>inni ludzie napisali</strong> o świecie.</p>\n<p>Więc kiedy LLM pisze &quot;truskawki są słodkie&quot; - on nie <em>wie</em>, że są słodkie. On wie, że w tekstach, na których był trenowany, słowo &quot;truskawki&quot; często występuje blisko słowa &quot;słodkie&quot;. To jest różnica. I to jest właśnie <strong>semiotyczna</strong> różnica.</p>\n<div class=\"markdown-alert markdown-alert-warning\">\n<p class=\"markdown-alert-title\">Warning</p>\n<p><strong>Paradoks:</strong> LLM potrafi napisać piękny opis zachodu słońca, choć nigdy nie widział słońca. Ale potrafi też napisać piękny opis zachodu słońca <strong>na Marsie</strong> - choć tam jeszcze nikt nie widział zachodu słońca. Skąd &quot;wie&quot;? Z tekstu science fiction. Czyli: znaki odnoszą się do znaków, które odnoszą się do znaków... hala luster ;-)</p>\n</div>\n<hr />\n<h2><a href=\"#saussure-w-kodzie-embeddingi-jako-system-znaków\" aria-hidden=\"true\" class=\"anchor\" id=\"saussure-w-kodzie-embeddingi-jako-system-znaków\"></a>Saussure w kodzie: embeddingi jako system znaków</h2>\n<p>OK, teraz to, co obiecałem - zobaczmy, jak teoria Saussure'a realizuje się w kodzie. Bo to jest <strong>naprawdę</strong> fascynujące.</p>\n<p>Pamiętacie, co mówił Saussure? <strong>Znaczenie słowa wynika z jego relacji do innych słów</strong>. &quot;Kot&quot; znaczy to, co znaczy, bo nie jest &quot;psem&quot;, nie jest &quot;domem&quot; itd. Znaczenie jest relacyjne.</p>\n<p>A teraz pomyślcie o <strong>word embeddings</strong> (osadzeniach słów) - które poznaliśmy w poprzednim wpisie. Każde słowo jest reprezentowane jako wektor w wielowymiarowej przestrzeni. I słowa o podobnym znaczeniu są <strong>blisko</strong> siebie w tej przestrzeni.</p>\n<p>To jest <strong>dokładnie</strong> relacyjna teoria znaku Saussure'a, tylko zaimplementowana w matematyce!</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>king</a-v> <a-o>=</a-o> <a-v>model</a-v>[<a-s>&quot;king&quot;</a-s>]\n<a-v>queen</a-v> <a-o>=</a-o> <a-v>model</a-v>[<a-s>&quot;queen&quot;</a-s>]\n<a-v>man</a-v> <a-o>=</a-o> <a-v>model</a-v>[<a-s>&quot;man&quot;</a-s>]\n<a-v>woman</a-v> <a-o>=</a-o> <a-v>model</a-v>[<a-s>&quot;woman&quot;</a-s>]\n\n<a-v>result</a-v> <a-o>=</a-o> <a-v>king</a-v> <a-o>-</a-o> <a-v>man</a-v> <a-o>+</a-o> <a-v>woman</a-v>\n\n<a-k>from</a-k> <a-v>gensim</a-v>.<a-v>models</a-v> <a-k>import</a-k> <a-cr>KeyedVectors</a-cr>\n<a-v>similarities</a-v> <a-o>=</a-o> <a-v>model</a-v>.<a-pr>cosine_similarities</a-pr>(<a-v>result</a-v>, [<a-v>queen</a-v>])\n<a-f>print</a-f>(<a-s>f&quot;Podobieństwo do &#39;queen&#39;: </a-s><a-p>{</a-p><a-v>similarities</a-v><a-eb>[</a-eb><a-n>0</a-n><a-eb>]:.3f</a-eb><a-p>}</a-p><a-s>&quot;</a-s>)</code></pre>\n<p>Wypisze coś w stylu: <code>Podobieństwo do 'queen': 0.850</code></p>\n<p>Ale spójrzcie na to z perspektywy Saussure'a:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph TD\n    subgraph &quot;Saussure: znak = pozycja w systemie relacji&quot;\n        S1[&quot;'king' nie jest 'queen'&lt;br/&gt;'king' nie jest 'man'&lt;br/&gt;'king' nie jest 'woman'&quot;] --&gt; S2[&quot;znaczenie = różnica&quot;]\n    end\n    subgraph &quot;Word2Vec: słowo = wektor w przestrzeni relacyjnej&quot;\n        V1[&quot;king = [0.50, 0.68, ...]&lt;br/&gt;queen = [0.38, 0.64, ...]&lt;br/&gt;man = [0.31, 0.43, ...]&quot;] --&gt; V2[&quot;znaczenie = pozycja wektora&lt;br/&gt;względem innych wektorów&quot;]\n    end\n    \n    S2 -.-&gt;|&quot;to samo!&quot;| V2\n    \n    style S2 fill:#9999ff,color:#fff\n    style V2 fill:#ff9999,color:#000\n</code></pre>\n<p>Zarówno Saussure, jak i Word2Vec mówią to samo: <strong>znaczenie nie jest w samym znaku - jest w jego relacji do innych znaków</strong>. Saussure wymyślił to jako teorię języka. Programiści Google wymyślili Word2Vec jako algorytm. I doszli do tego samego wniosku.</p>\n<p>No... prawie. Bo jest jeden haczyk. Saussure zakładał, że za znaczącym (forma) stoi <strong>znaczone</strong> (koncept). W embeddingach mamy tylko pozycję w przestrzeni - mamy relacje, ale czy mamy &quot;koncept&quot;? Czy wektor <code>[0.50, 0.68, ...]</code> <strong>jest</strong> konceptem &quot;króla&quot;?</p>\n<p>To jest właśnie to pytanie, które doprowadza nas do kolejnego semiotyka...</p>\n<hr />\n<h2><a href=\"#derrida-pismo-jako-fundament\" aria-hidden=\"true\" class=\"anchor\" id=\"derrida-pismo-jako-fundament\"></a>Derrida: pismo jako fundament</h2>\n<p>Jacques Derrida (francuski filozof, lata 60. i 70. XX wieku) zrobił coś odważnego. Spojrzał na całego Saussure'a i powiedział: <strong>&quot;Chwila. A dlaczego uważacie, że mowa jest ważniejsza od pisma?&quot;</strong></p>\n<p>Saussure (i cała zachodnia tradycja filozoficzna) traktował mowę jako &quot;pierwotną&quot; - bliższą myśli, bliższą znaczeniu. Pismo było &quot;pochodne&quot; - tylko zapis mowy, &quot;znak znaku&quot;. Derrida nazwał to <strong>logocentryzmem</strong> - przekonaniem, że na końcu łańcucha znaków jest jakaś &quot;obecność&quot;, &quot;myśl&quot;, &quot;intencja&quot;, która nadaje znaczenie.</p>\n<p>I Derrida odwrócił to do góry nogami: <strong>pismo nie jest podrzędne wobec mowy. Pismo jest systemem samym w sobie.</strong></p>\n<p>Dlaczego to jest ważne dla LLM? Bo Elad Vromen w swoim artykule &quot;Language Models as Semiotic Machines&quot; zauważył coś genialnego:</p>\n<blockquote>\n<p>LLM trenuje na <strong>piśmie</strong> (tekst). Tworzy model <strong>pisma</strong>. Generuje nowe <strong>pismo</strong>. Nigdzie w tym procesie nie ma &quot;mowy&quot;, &quot;umysłu&quot; ani &quot;intencji&quot;. Cała hierarchia Saussure'a - mowa &gt; pismo - zostaje odwrócona. Pismo jest jedyną rzeczywistością, jaką LLM zna.</p>\n</blockquote>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph LR\n    subgraph &quot;Tradycyjny model (logocentryzm)&quot;\n        A1[&quot;🌍 Świat&quot;] --&gt; A2[&quot;🧠 Myśl&quot;]\n        A2 --&gt; A3[&quot;🗣️ Mowa&quot;]\n        A3 --&gt; A4[&quot;📝 Pismo&quot;]\n    end\n    \n    subgraph &quot;Model LLM (odwrócenie Derridy)&quot;\n        B1[&quot;📝 Pismo&lt;br/&gt;&lt;i&gt;dane treningowe&lt;/i&gt;&quot;] --&gt; B2[&quot;🧮 Model&lt;br/&gt;&lt;i&gt;statystyka znaków&lt;/i&gt;&quot;]\n        B2 --&gt; B3[&quot;📝 Nowe pismo&lt;br/&gt;&lt;i&gt;output LLM&lt;/i&gt;&quot;]\n    end\n    \n    style A4 fill:#ff9999,color:#000\n    style B1 fill:#99ff99,color:#000\n    style B2 fill:#9999ff,color:#fff\n    style B3 fill:#99ff99,color:#000\n</code></pre>\n<p>Więc kiedy pytamy &quot;czy LLM rozumie język?&quot; - zadajemy <strong>złe pytanie</strong>. To jak pytanie &quot;czy książka rozumie to, co jest w niej zapisane?&quot; Książka nie &quot;rozumie&quot; - ale <strong>zawiera znaki</strong>, które my - czytelnicy - interpretujemy. LLM jest czymś pośrednim: nie jest książką (bo generuje nowy tekst), ale nie jest też umysłem (bo nie ma dostępu do znaczeń poza tekstem).</p>\n<div class=\"markdown-alert markdown-alert-important\">\n<p class=\"markdown-alert-title\">Important</p>\n<p><strong>Derrida w pigułce dla LLM:</strong> LLM nie modeluje &quot;umysłu&quot; ani &quot;świata&quot;. LLM modeluje <strong>pismo</strong> - system znaków, który ma własną logikę, własne reguły, własną spójność. I to pismo jest wystarczające, żeby generować tekst, który <em>dla nas</em> ma znaczenie. Ale to <strong>my</strong> nadajemy mu znaczenie - nie model.</p>\n</div>\n<p>To też wyjaśnia pewien fenomen, który pewnie zauważyliście: LLM czasami mówi rzeczy, które są <strong>statystycznie poprawne, ale nonsensowne</strong>. Bo w systemie pisma, w którym operuje model, te słowa dobrze pasują do siebie. Ale my - jako ludzie z dostępem do świata (obiektów w sensie Peirce'a) - widzimy, że to nie ma sensu. Model nie ma tego &quot;zakotwiczenia&quot; w rzeczywistości.</p>\n<details>\n<summary>Dla chętnych: Derrida i iterowalność</summary>\n<p>Derrida w swoim słynnym eseju &quot;Signature Event Context&quot; mówił o <strong>iterowalności</strong> znaków - o tym, że znak może być powtórzony w nowym kontekście i nabierać nowego znaczenia. Słowo &quot;dobry&quot; może być komplementem, ironią albo frazą w &quot;dobry wieczór&quot; - kontekst zmienia wszystko.</p>\n<p>I to jest właśnie to, co widzimy w LLM: ten sam prompt w innym kontekście daje inną odpowiedź. Model nie &quot;rozumie&quot; kontekstu - ale <strong>statystycznie</strong> wyłapuje wzorce kontekstowe z danych treningowych. Czyli: iterowalność znaków w czystej, matematycznej postaci.</p>\n</details>\n<hr />\n<h2><a href=\"#prompt-jako-akt-semiotyczny\" aria-hidden=\"true\" class=\"anchor\" id=\"prompt-jako-akt-semiotyczny\"></a>Prompt jako akt semiotyczny</h2>\n<p>Teraz wchodzimy na bardzo praktyczny grunt. Bo jeśli LLM jest maszyną znaków, to <strong>prompt</strong> - to, co do niego wpisujecie - jest <strong>aktem semiotycznym</strong>. Nie po prostu &quot;komendą&quot;. Ale aktem, który tworzy ramę dla znaczenia.</p>\n<h3><a href=\"#triada-peircea-w-praktyce\" aria-hidden=\"true\" class=\"anchor\" id=\"triada-peircea-w-praktyce\"></a>Triada Peirce'a w praktyce</h3>\n<p>Spójrzmy na interakcję z LLM przez pryzmat triady Peirce'a:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph TD\n    P[&quot;📝 PROMPT&lt;br/&gt;&lt;i&gt;representamen&lt;/i&gt;&lt;br/&gt;znak wejściowy&quot;] --&gt; LLM[&quot;🤖 LLM&lt;br/&gt;&lt;i&gt;przetwarzanie znaków&lt;/i&gt;&quot;]\n    LLM --&gt; O[&quot;💬 ODPOWIEDŹ&lt;br/&gt;&lt;i&gt;nowy representamen&lt;/i&gt;&quot;]\n    O --&gt; U[&quot;🧑 UŻYTKOWNIK&lt;br/&gt;&lt;i&gt;interpretant&lt;/i&gt;&lt;br/&gt;nadaje znaczenie&quot;]\n    U -.-&gt;|&quot;nowy prompt&quot;| P\n    \n    style P fill:#ff9999,color:#000\n    style LLM fill:#ffcc99,color:#000\n    style O fill:#ffff99,color:#000\n    style U fill:#9999ff,color:#fff\n</code></pre>\n<p>Czyli:</p>\n<ol>\n<li><strong>Wy</strong> tworzycie prompt (representamen)</li>\n<li><strong>LLM</strong> przetwarza znaki i generuje odpowiedź (nowy representamen)</li>\n<li><strong>Wy</strong> interpretujecie odpowiedź (stajecie się interpretantem)</li>\n<li>Wasza interpretacja prowadzi do nowego promptu... i cykl się powtarza</li>\n</ol>\n<p>Zauważcie: <strong>znaczenie powstaje dopiero w kroku 3</strong>. LLM generuje ciąg tokenów, ale to Wy nadajecie mu sens. To jest dokładnie to, o czym mówił Umberto Eco z koncepcją <strong>&quot;dzieła otwartego&quot;</strong> - tekst nie ma jednego, ustalonego znaczenia. Tekst jest &quot;ramą&quot;, którą czytelnik wypełnia interpretacją.</p>\n<h3><a href=\"#eksperyment-jak-prompt-zmienia-ramę-semiotyczną\" aria-hidden=\"true\" class=\"anchor\" id=\"eksperyment-jak-prompt-zmienia-ramę-semiotyczną\"></a>Eksperyment: jak prompt zmienia ramę semiotyczną</h3>\n<p>Spróbujcie sami. Odpalcie ChatGPT (albo Claude, Gemini - co macie) i wyślijcie te trzy prompty, każdy w <strong>nowej rozmowie</strong>:</p>\n<ol>\n<li><code>&quot;Wyjaśnij, czym jest grawitacja.&quot;</code></li>\n<li><code>&quot;Wyjaśnij grawitację pięciolatkowi.&quot;</code></li>\n<li><code>&quot;Wyjaśnij grawitację w stylu sonetu Szekspira.&quot;</code></li>\n</ol>\n<p>Każdy prompt dotyczy tego samego tematu (grawitacja). Ale każdy <strong>zmienia ramę semiotyczną</strong> - zmienia ton, rejestr, gatunek, oczekiwaną formę odpowiedzi.</p>\n<table>\n<thead>\n<tr>\n<th>Prompt</th>\n<th>Zmienia się...</th>\n<th>Rama semiotyczna</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>&quot;Wyjaśnij grawitację&quot;</td>\n<td>-</td>\n<td>Neutralna, encyklopedyczna</td>\n</tr>\n<tr>\n<td>&quot;...pięciolatkowi&quot;</td>\n<td>Odbiorca, prostota języka</td>\n<td>Edukacyjna, dostosowana do wieku</td>\n</tr>\n<tr>\n<td>&quot;...w stylu Szekspira&quot;</td>\n<td>Gatunek, forma, styl</td>\n<td>Literacka, artystyczna</td>\n</tr>\n</tbody>\n</table>\n<p>To jest dokładnie to, co Picca nazywa <strong>&quot;semiotyczną umową&quot;</strong> (semiotic contract). Kiedy tworzycie prompt, nie &quot;prosicie o informację&quot; - <strong>ustanawiacie warunki, w jakich znaczenie będzie konstruowane</strong>. Prosicie o grawitację w trybie Szekspira? Otrzymujecie hybrydę fizyki i poezji. To nie jest &quot;prawdziwa&quot; grawitacja ani &quot;prawdziwy&quot; Szekspir - to jest <strong>semiotyczny kolaż</strong>, nowa konfiguracja znaków.</p>\n<div class=\"markdown-alert markdown-alert-tip\">\n<p class=\"markdown-alert-title\">Tip</p>\n<p><strong>Eksperyment bonusowy:</strong> Spróbujcie: <em>&quot;Wyjaśnij pojęcie entropii używając metafor z bajek.&quot;</em> Zobaczycie, jak LLM łączy dwie zupełnie różne strefy semiotyczne - fizykę i baśnie. To jest właśnie to, co semiotyka nazywa <strong>translacją między kodami kulturowymi</strong>.</p>\n</div>\n<hr />\n<h2><a href=\"#semiosfera---ekologia-znaków\" aria-hidden=\"true\" class=\"anchor\" id=\"semiosfera---ekologia-znaków\"></a>Semiosfera - ekologia znaków</h2>\n<p>Jeszcze jedno pojęcie, które warto znać. Jurij Lotman, rosyjski semiotyk, wymyślił koncept <strong>semiosfery</strong>.</p>\n<p>Semiosfera to przestrzeń, w której żyją znaki. To ekologia znaczeń - sieć kodów kulturowych, gatunków, dyskursów, ideologii, które wchodzą ze sobą w interakcje. Podobnie jak biosfera to przestrzeń, w której żyją organizmy, semiosfera to przestrzeń, w której żyją znaki.</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph TD\n    subgraph &quot;Semiosfera&quot;\n        N[&quot;📰 Media&lt;br/&gt;&lt;i&gt;news, artykuły&lt;/i&gt;&quot;]\n        L[&quot;📚 Literatura&lt;br/&gt;&lt;i&gt;powieści, poezja&lt;/i&gt;&quot;]\n        S[&quot;🔬 Nauka&lt;br/&gt;&lt;i&gt;publikacje, podręczniki&lt;/i&gt;&quot;]\n        I[&quot;💬 Internet&lt;br/&gt;&lt;i&gt;fora, social media&lt;/i&gt;&quot;]\n        K[&quot;🎭 Kultura&lt;br/&gt;&lt;i&gt;film, sztuka, muzyka&lt;/i&gt;&quot;]\n        P[&quot;⚖️ Prawo&lt;br/&gt;&lt;i&gt;ustawy, orzeczenia&lt;/i&gt;&quot;]\n    end\n    \n    D[&quot;📊 Dane treningowe LLM&quot;] -.-&gt; N\n    D -.-&gt; L\n    D -.-&gt; S\n    D -.-&gt; I\n    D -.-&gt; K\n    D -.-&gt; P\n    \n    LLM2[&quot;🤖 LLM&quot;] --&gt;|&quot;nawiguje po&lt;br/&gt;strefach semiosfery&quot;| D\n    \n    style D fill:#ffff99,color:#000\n    style LLM2 fill:#ff9999,color:#000\n</code></pre>\n<p>Dane treningowe LLM są niczym innym jak <strong>gigantycznym przekrojem semiosfery</strong>. Kiedy model czyta Wikipedię, twittera, książki, artykuły naukowe, kody źródłowe - przyswaja (statystycznie!) całą tę różnorodność kodów kulturowych.</p>\n<p>I dlatego potrafi pisać w stylu Szekspira, tłumaczyć z niemieckiego, żartować jak komik i cytować przepisy prawa - bo wszystko to jest w semiosferze, a model &quot;widział&quot; próbki z każdej strefy.</p>\n<p>Ale jest i druga strona medalu: model przyswaja też <strong>uprzedzenia, stereotypy i dominujące narracje</strong> zawarte w semiosferze. Bo semiosfera nie jest neutralna - to przestrzeń kulturowa z historią, z władzą, z ideologią. I LLM, operując na znakach z tej przestrzeni, reprodukuje je w swoich outputach.</p>\n<div class=\"markdown-alert markdown-alert-warning\">\n<p class=\"markdown-alert-title\">Warning</p>\n<p><strong>Dlaczego LLM czasem mówi głupoty?</strong> Bo semiosfera jest pełna sprzeczności. Na jednej stronie internetu &quot;ziemia jest okrągła&quot;, na innej &quot;ziemia jest płaska&quot;. Model widzi oba znaki i nie ma dostępu do &quot;obiektu&quot; (rzeczywistej ziemi), żeby zdecydować, który znak jest prawdziwy. Operuje w hali luster - znaki odnoszą się do znaków, a nie do rzeczywistości.</p>\n</div>\n<hr />\n<h2><a href=\"#quiz-saussure-czy-peirce\" aria-hidden=\"true\" class=\"anchor\" id=\"quiz-saussure-czy-peirce\"></a>Quiz: Saussure czy Peirce?</h2>\n<p>Sprawdźcie, który semiotyk lepiej wyjaśnia te zjawiska LLM. Przypominam:</p>\n<ul>\n<li><strong>Saussure</strong>: znak = para (znaczące + znaczone), znaczenie relacyjne, system statyczny</li>\n<li><strong>Peirce</strong>: znak = triada (representamen + obiekt + interpretant), proces interpretacji, dynamika</li>\n</ul>\n<p>Kto lepiej wyjaśnia, że...?<sup class=\"footnote-ref\"><a href=\"#fn-2\" id=\"fnref-2\" data-footnote-ref>2</a></sup></p>\n<ol>\n<li><strong>LLM potrafi pisać wiersze, choć nigdy nie czuł poezji</strong> - Saussure czy Peirce?</li>\n<li><strong>Embedding &quot;król&quot; - &quot;mężczyzna&quot; + &quot;kobieta&quot; = &quot;królowa&quot;</strong> - Saussure czy Peirce?</li>\n<li><strong>Ten sam prompt daje różne odpowiedzi w zależności od kontekstu rozmowy</strong> - Saussure czy Peirce?</li>\n<li><strong>LLM nie ma dostępu do świata, tylko do tekstu</strong> - Saussure czy Peirce?</li>\n<li><strong>Znamy kogoś, kto zapytał ChatGPT o diagnozę medyczną i ją dostał</strong> - Saussure czy Peirce?</li>\n</ol>\n<hr />\n<h2><a href=\"#podsumowanie---mapa-semiotyczna-llm\" aria-hidden=\"true\" class=\"anchor\" id=\"podsumowanie---mapa-semiotyczna-llm\"></a>Podsumowanie - mapa semiotyczna LLM</h2>\n<p>Oto nasza semiotyczna mapa w pigułce:</p>\n<table>\n<thead>\n<tr>\n<th>Semiotyk</th>\n<th>Kluczowy koncept</th>\n<th>Co to mówi o LLM</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>Saussure</strong></td>\n<td>Znaczenie = relacja między znakami</td>\n<td>Embeddingi realizują relacyjną koncepcję znaku</td>\n</tr>\n<tr>\n<td><strong>Peirce</strong></td>\n<td>Triada znak-obiekt-interpretant</td>\n<td>LLM nie ma dostępu do obiektu, tylko do znaków (hala luster)</td>\n</tr>\n<tr>\n<td><strong>Derrida</strong></td>\n<td>Pismo jako system sam w sobie</td>\n<td>LLM modeluje pismo, nie umysł; dane treningowe = jedyne źródło</td>\n</tr>\n<tr>\n<td><strong>Lotman</strong></td>\n<td>Semiosfera - ekologia znaków</td>\n<td>Dane treningowe to przekrój semiosfery; LLM nawiguje po strefach</td>\n</tr>\n<tr>\n<td><strong>Eco</strong></td>\n<td>Dzieło otwarte</td>\n<td>Output LLM nie ma jednego znaczenia - wymaga Waszej interpretacji</td>\n</tr>\n</tbody>\n</table>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph TD\n    subgraph &quot;Semiotyczna perspektywa na LLM&quot;\n        SAU[&quot;Saussure&lt;br/&gt; znaczenie = relacja&quot;] --&gt; EMB[&quot;Embeddingi&lt;br/&gt;wektory = pozycje relacyjne&quot;]\n        PEI[&quot;Peirce&lt;br/&gt;znak → obiekt → interpretant&quot;] --&gt; HOM[&quot;Hall of Mirrors&lt;br/&gt;brak dostępu do obiektu&quot;]\n        DER[&quot;Derrida&lt;br/&gt;pismo jest fundamentem&quot;] --&gt; DAT[&quot;Dane treningowe&lt;br/&gt;= pismo, nie umysł&quot;]\n        LOT[&quot;Lotman&lt;br/&gt;semiosfera&quot;] --&gt; NAV[&quot;LLM nawiguje&lt;br/&gt;po strefach semiosfery&quot;]\n        ECO[&quot;Eco&lt;br/&gt;dzieło otwarte&quot;] --&gt; INT[&quot;Output wymaga&lt;br/&gt;interpretacji człowieka&quot;]\n    end\n    \n    style SAU fill:#ff9999,color:#000\n    style PEI fill:#ffcc99,color:#000\n    style DER fill:#ffff99,color:#000\n    style LOT fill:#99ff99,color:#000\n    style ECO fill:#9999ff,color:#fff\n</code></pre>\n<p>Więc: <strong>LLM nie ma umysłu.</strong> Ale to nie znaczy, że nie robi nic interesującego. LLM operuje na znakach - rekonfiguruje je, łączy strefy semiosfery, tworzy nowe konfiguracje znaków. I te nowe konfiguracje <strong>dla nas - jako interpretatorów - mają znaczenie</strong>.</p>\n<p>To jest semiotyczna odpowiedź na pytanie &quot;czy LLM rozumie?&quot;. LLM nie &quot;rozumie&quot; w ludzkim sensie. Ale generuje znaki, które wchodzą w naszą semiosferę i stają się częścią naszego procesu interpretacji. I ten proces jest prawdziwy, ważny i potężny.</p>\n<hr />\n<p>Wiem, że semiotyka na początku brzmi jak coś z innej planety, ale mam nadzieję, że teraz widzicie, jak to się łączy z LLM.</p>\n<p>Kórą koncepcję semiotyczną uważacie za najbardziej przydatną do zrozumienia LLM? Saussure i jego relacyjność? Peirce i jego hala luster? A może Derrida i jego &quot;pismo&quot;?</p>\n<p>Bo szczerze mówiąc - ja się dopiero uczę tej perspektywy. Ale czuję, że to jest ten moment, gdzie lingwistyka, filozofia i programowanie spotykają się w jednym miejscu i tworzą coś naprawdę fascynującego.</p>\n<blockquote>\n<p><strong>Co w następnym wpisie?</strong> Przechodzimy od teorii do praktyki: <a href=\"jak-komputer-czyta-tekst.html\">Jak komputer czyta tekst - od liczenia słów do wektorów</a>. Zobaczymy jak tokenizacja, TF-IDF, łańcuchy Markowa i Word2Vec realizują w kodzie to, o czym mówili Saussure (znaczenie jest relacyjne!) i Derrida (pismo jest systemem samym w sobie!).</p>\n</blockquote>\n<p>Do następnego!</p>\n<hr />\n<p><strong>Źródła i ciekawe linki:</strong></p>\n<p>Jeśli chcecie wejść głębiej, oto materiały, z których korzystałem:</p>\n<ul>\n<li><a href=\"https://arxiv.org/abs/2505.17080\">Davide Picca, &quot;Not Minds, but Signs: Reframing LLMs through Semiotics&quot; - arXiv</a> - główna inspiracja tego wpisu; propozycja patrzenia na LLM jako maszyny semiotycznej</li>\n<li><a href=\"https://arxiv.org/abs/2410.13065\">Elad Vromen, &quot;Language Models as Semiotic Machines&quot; - arXiv</a> - genialne połączenie Saussure'a, Derridy i embeddingów</li>\n<li><a href=\"https://philpapers.org/rec/MANLMH-2\">David Manheim, &quot;Language Models' Hall of Mirrors Problem&quot; - PhilPapers</a> - źródło metafory &quot;hali luster&quot;</li>\n<li><a href=\"https://arxiv.org/pdf/2509.14250.pdf\">&quot;Semiotic reflections and modelling&quot; - arXiv</a> - prompty jako zjawiska semiotyczne</li>\n<li><a href=\"https://www.emerald.com/jd/article/doi/10.1108/JD-03-2026-0140/1367688/The-meaning-of-prompts-a-semiotic-approach-to\">&quot;The meaning of prompts: a semiotic approach to human-LLM interaction&quot; - Emerald</a> - prompty jako akty znaczeniotwórcze</li>\n<li><a href=\"https://www.turtlesai.it/it/pages-391/a_semiotic_perspective_on_generative_ai_and_llms\">&quot;A Semiotic Perspective on Generative AI and LLMs&quot; - TurtlesAI</a> - przystępne wprowadzenie do semiotyki generatywnej AI</li>\n<li><a href=\"https://www.sciencedirect.com/science/article/pii/S1877042814057139\">&quot;The Semiotic Perspectives of Peirce and Saussure: A Brief Comparative Study&quot; - ScienceDirect</a> - porównanie dwóch głównych tradycji semiotycznych</li>\n<li><a href=\"https://www.linkedin.com/posts/ceperez_the-difference-between-semantics-and-semiotics-activity-7358534695254388737-gebY\">Carlos E. Perez, &quot;Semantics vs Semiotics: How LLMs Should Be Understood&quot; - LinkedIn</a> - krótki post o tym, dlaczego semiotyka &gt; semantyka dla LLM</li>\n</ul>\n<section class=\"footnotes\" data-footnotes>\n<ol>\n<li id=\"fn-1\">\n<p>Odpowiedzi: 1) indeks (przyczynowo-skutkowy - temperatura powoduje rozszerzenie cieczy), 2) ikona (podobieństwo do psa), 3) symbol (czysta konwencja), 4) indeks (fizyczny ślad kogoś, kto tu przeszedł), 5) ikona (podobieństwo do osoby na wózku). <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<li id=\"fn-2\">\n<p>Moje odpowiedzi: 1) <strong>Peirce</strong> - model nie ma dostępu do obiektu (doświadczenia poezji), ale generuje representameny (tekst), które interpretujemy. 2) <strong>Saussure</strong> - to jest czysta relacyjność! Król jest definiowany przez to, czym nie jest. 3) <strong>Peirce</strong> - kontekst zmienia interpretację, a interpretant tworzy nowy znak. 4) <strong>Peirce</strong> - brak obiektu w triadzie. 5) <strong>Peirce + Lotman</strong> - model reprodukuje znaki z semiosfery medycznej bez dostępu do rzeczywistego obiektu (ciała pacjenta). Diagnoza jest znakiem bez zakotwiczenia. <a href=\"#fnref-2\" class=\"footnote-backref\" data-footnote-backref data-footnote-backref-idx=\"2\" aria-label=\"Back to reference 2\">↩</a></p>\n</li>\n</ol>\n</section>\n",
      "summary": "",
      "date_published": "2026-06-07T00:00:00-00:00",
      "image": "",
      "authors": [
        {
          "name": "Błażej Gruszka",
          "url": "https://www.linkedin.com/in/blazejgruszka/",
          "avatar": "https://github.com/bgruszka.png"
        }
      ],
      "tags": [
        "llm",
        "ai",
        "semiotyka",
        "znaki",
        "linguistics",
        "language-models",
        "saussure",
        "peirce",
        "derrida"
      ],
      "language": "pl"
    },
    {
      "id": "https://gruszka.dev/cechy-jezykowe-a-llm.html",
      "url": "https://gruszka.dev/cechy-jezykowe-a-llm.html",
      "title": "Cechy językowe - co musisz wiedzieć, zanim zrozumiesz, jak myśli LLM",
      "content_html": "<p>LLM (Large Language Models, czyli takie ChatGPT, Claude, Gemini i spółka) jest już z nami od jakiegoś czasu i stwierdziłem, że zacznę głębiej wchodzić w ten temat i pojawiło się jedno wielkie pytanie: <strong>ale jak to w ogóle działa, że model &quot;rozumie&quot; język?</strong> Bo przecież pod spodem to tylko &quot;matematyka i statystyka&quot;, prawda? No... tak i nie ;-)</p>\n<p>I wtedy wpadłem na lingwistykę. I okazało się, że żeby naprawdę zrozumieć, co LLM robi z językiem, trzeba najpierw zrozumieć, z czego ten język się składa. A że programista próbujący czytać o lingwistyce to dość zabawny obrazek, to pomyślałem: <strong>podzielę się tym z wami</strong> ;-)</p>\n<p>To jest <strong>pierwszy wpis z serii</strong>, w której będziemy przeglądać różne poziomy analizy języka i patrzeć, jak to wszystko łączy się z LLM. W dzisiejszym wpisie zbudujemy fundament - poznamy pięć głównych warstw języka i zobaczymy, dlaczego każda z nich jest ważna dla modeli językowych.</p>\n<hr />\n<h2><a href=\"#język-jak-cebulka---warstwa-na-warstwie\" aria-hidden=\"true\" class=\"anchor\" id=\"język-jak-cebulka---warstwa-na-warstwie\"></a>Język jak cebulka - warstwa na warstwie</h2>\n<p>Zanim wejdziemy w szczegóły, wyobraźcie sobie język jako taką... cebulę. Albo tort warstwowy, jeśli wolicie słodkie metafory :D</p>\n<p>Każda warstwa to inny poziom, na którym język &quot;działa&quot;. Od najniższej - dźwięków - do najwyższej - tego, co <em>naprawdę</em> chcieliśmy powiedzieć, nawet jeśli powiedzieliśmy coś zupełnie innego.</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph TD\n    A[&quot;🔊 Fonetyka i fonologia&lt;br/&gt;&lt;i&gt;dźwięki i system dźwięków&lt;/i&gt;&quot;] --&gt; B[&quot;🧩 Morfologia&lt;br/&gt;&lt;i&gt;budowa słów&lt;/i&gt;&quot;]\n    B --&gt; C[&quot;📐 Składnia&lt;br/&gt;&lt;i&gt;budowa zdań&lt;/i&gt;&quot;]\n    C --&gt; D[&quot;💡 Semantyka&lt;br/&gt;&lt;i&gt;znaczenie&lt;/i&gt;&quot;]\n    D --&gt; E[&quot;🎭 Pragmatyka&lt;br/&gt;&lt;i&gt;znaczenie w kontekście&lt;/i&gt;&quot;]\n    \n    style A fill:#ff9999,color:#000\n    style B fill:#ffcc99,color:#000\n    style C fill:#ffff99,color:#000\n    style D fill:#99ff99,color:#000\n    style E fill:#9999ff,color:#000\n</code></pre>\n<p>Pięć warstw, pięć sposobów, na jakie język jest &quot;analizowany&quot;. I uwaga - <strong>LLM musi sobie z każdym z tych poziomów radzić</strong>, żeby wydawać się sensownym rozmówcą. Oczywiście nie robi tego świadomie - ale struktury, których uczy się w trakcie treningu, jakoś te warstwy &quot;łapią&quot;.</p>\n<div class=\"markdown-alert markdown-alert-note\">\n<p class=\"markdown-alert-title\">Note</p>\n<p><strong>Pięć warstw języka w pigułce:</strong></p>\n<ol>\n<li><strong>Fonetyka i fonologia</strong> - dźwięki i jak są zorganizowane w system</li>\n<li><strong>Morfologia</strong> - jak słowa są budowane z mniejszych kawałków</li>\n<li><strong>Składnia</strong> - zasady porządku słów w zdaniu</li>\n<li><strong>Semantyka</strong> - znaczenie słów i zdań</li>\n<li><strong>Pragmatyka</strong> - jak kontekst zmienia znaczenie wypowiedzi</li>\n</ol>\n</div>\n<p>OK, przejdźmy do rzeczy. Zaczynamy od dołu naszej cebulki ;-)</p>\n<hr />\n<h2><a href=\"#fonetyka-i-fonologia---dźwięki-których-llm-nie-słyszy\" aria-hidden=\"true\" class=\"anchor\" id=\"fonetyka-i-fonologia---dźwięki-których-llm-nie-słyszy\"></a>Fonetyka i fonologia - dźwięki, których LLM nie słyszy</h2>\n<h3><a href=\"#czym-jest-fonetyka\" aria-hidden=\"true\" class=\"anchor\" id=\"czym-jest-fonetyka\"></a>Czym jest fonetyka?</h3>\n<p><strong>Fonetyka</strong> bada dźwięki mowy jako zjawiska fizyczne. Mówiąc najprościej: jak produkujemy dźwięki, jak one podróżują przez powietrze i jak je odbieramy.</p>\n<p>Ma trzy gałęzie:</p>\n<table>\n<thead>\n<tr>\n<th>Gałąź</th>\n<th>Co bada</th>\n<th>Przykład</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>Artykulacyjna</strong></td>\n<td>Jak narządy mowy produkują dźwięki</td>\n<td>Gdzie jest język, gdy mówisz &quot;sz&quot;?</td>\n</tr>\n<tr>\n<td><strong>Akustyczna</strong></td>\n<td>Fizyczne właściwości fal dźwiękowych</td>\n<td>Częstotliwość, amplituda</td>\n</tr>\n<tr>\n<td><strong>Audytywna</strong></td>\n<td>Jak ucho i mózg odbierają dźwięki</td>\n<td>Jak ślimak przetwarza fale na sygnał nerwowy</td>\n</tr>\n</tbody>\n</table>\n<h3><a href=\"#a-fonologia\" aria-hidden=\"true\" class=\"anchor\" id=\"a-fonologia\"></a>A fonologia?</h3>\n<p><strong>Fonologia</strong> to poziom wyżej. Nie interesują jej same dźwięki jako fizyczne zjawiska, ale <strong>jak dźwięki są zorganizowane w konkretnym języku</strong>.</p>\n<p>Kluczowe pojęcie: <strong>fonem</strong> - to najmniejsza jednostka dźwiękowa, która odróżnia znaczenie słów.</p>\n<p>Prosty przykład po polsku:</p>\n<ul>\n<li><strong>kot</strong> vs <strong>lot</strong> - słowa różnią się jednym dźwiękiem (/k/ vs /l/), a znaczenie jest zupełnie inne</li>\n<li><strong>ras</strong> vs <strong>las</strong> - /r/ vs /l/ znowu zmienia wszystko</li>\n</ul>\n<p>Albo po angielsku:</p>\n<ul>\n<li><strong>pat</strong> vs <strong>bat</strong> - /p/ vs /b/ i mamy zupełnie inne słowo</li>\n</ul>\n<div class=\"markdown-alert markdown-alert-tip\">\n<p class=\"markdown-alert-title\">Tip</p>\n<p><strong>Eksperyment:</strong> Wymów na głos &quot;kot&quot; raz jako zwykłe zdanie (&quot;Mój kot śpi.&quot;), a raz jako pytanie z zaskoczeniem (&quot;Kot?!&quot; z uniesionymi brwami). Ten sam wyraz, ale intonacja, akcent i melodia całkowicie zmieniają to, co &quot;słyszy&quot; odbiorca. To jest właśnie fonologia w działaniu - dźwięki + ich organizacja w system.</p>\n</div>\n<h3><a href=\"#dlaczego-to-ważne-dla-llm\" aria-hidden=\"true\" class=\"anchor\" id=\"dlaczego-to-ważne-dla-llm\"></a>Dlaczego to ważne dla LLM?</h3>\n<p>No właśnie - <strong>LLM dostaje tekst, nie dźwięk</strong>. Nigdy nie usłyszał wymowy. A mimo to...</p>\n<ul>\n<li>wie, że &quot;kot&quot; i &quot;lot&quot; rymują się (bo widział to w tekstach)</li>\n<li>radzi sobie z kalamburami i grami słowymi opartymi na dźwiękach</li>\n<li>potrafi pisać wiersze z rymami</li>\n</ul>\n<p>Czyli model jakoś <strong>przyswaja informacje fonologiczne</strong> z samych danych tekstowych. Trochę jakbyś nigdy nie widział oceanu, ale przeczytał o nim tyle książek, że potrafisz go opisać ;-)</p>\n<p>No ale oczywiście są ograniczenia. LLM tekstowy nie rozróżnia homofonów (słów, które brzmią tak samo, ale znaczą co innego) na podstawie fonetyki - radzi sobie z nimi czysto na poziomie semantycznym (o czym za chwilę).</p>\n<p>I tu ciekawostka: modele typu <strong>Whisper</strong> (speech-to-text od OpenAI) już łączą fonetykę z tekstem. Ale to temat na osobny wpis w tej serii ;-)</p>\n<div class=\"markdown-alert markdown-alert-warning\">\n<p class=\"markdown-alert-title\">Warning</p>\n<p><strong>Paradoks:</strong> LLM nigdy nie słyszał żadnego dźwięku, ale z danych treningowych &quot;wie&quot; o rymach, grach słownych i wzorcach brzmieniowych. To trochę jak osoba głucha od urodzenia, która potrafi pisać wiersze z rymami - bo przeczytała ich miliony.</p>\n</div>\n<hr />\n<h2><a href=\"#morfologia---jak-słowa-są-budowane-z-klocków\" aria-hidden=\"true\" class=\"anchor\" id=\"morfologia---jak-słowa-są-budowane-z-klocków\"></a>Morfologia - jak słowa są budowane z klocków</h2>\n<h3><a href=\"#czym-jest-morfologia\" aria-hidden=\"true\" class=\"anchor\" id=\"czym-jest-morfologia\"></a>Czym jest morfologia?</h3>\n<p><strong>Morfologia</strong> bada budowę słów - jak są one składane z mniejszych, sensownych kawałków zwanych <strong>morfemami</strong>.</p>\n<p>Morfem to <strong>najmniejsza jednostka języka, która niesie ze sobą znaczenie</strong>. Nie da się jej podzielić na coś jeszcze mniejszego, co miałoby znaczenie.</p>\n<p>Przykład z angielskiego, często używany w podręcznikach:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">unhappiness = un + happy + ness\n              ↑       ↑       ↑\n           „nie&quot;   „szczęśliwy&quot;  rzeczownik\n</code></pre>\n<p>Albo po polsku:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">nieszczęśliwy = nie + szczęśliw + y\n                  ↑        ↑       ↑\n              „nie&quot;  „szczęście&quot;   rodzaj męski\n</code></pre>\n<p>Albo jeszcze bardziej:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">nieprzystojny = nie + przy + stoj + ny\n</code></pre>\n<p>Każdy z tych kawałków ma swoje znaczenie. To są właśnie morfemy.</p>\n<h3><a href=\"#dwa-rodzaje-morfemów\" aria-hidden=\"true\" class=\"anchor\" id=\"dwa-rodzaje-morfemów\"></a>Dwa rodzaje morfemów</h3>\n<table>\n<thead>\n<tr>\n<th>Typ</th>\n<th>Opis</th>\n<th>Przykłady</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>Wolne</strong></td>\n<td>Mogą stać samodzielnie jako słowo</td>\n<td>kot, dom, biegnie</td>\n</tr>\n<tr>\n<td><strong>Związane</strong></td>\n<td>Muszą być dołączone do innego morfemu</td>\n<td>nie-, -ek, -owie, -ować</td>\n</tr>\n</tbody>\n</table>\n<p>A wśród morfemów związanych mamy <strong>afiksy</strong>:</p>\n<ul>\n<li><strong>Prefiksy</strong> (przed rdzeniem): <em>nie-</em>, <em>prze-</em>, <em>od-</em>, <em>naj-</em></li>\n<li><strong>Sufiksy</strong> (za rdzeniem): <em>-ek</em>, <em>-owie</em>, <em>-ować</em>, <em>-ny</em></li>\n<li><strong>Infiksy</strong> (wewnątrz rdzenia): w polskim praktycznie nie występują, ale w innych językach tak! Np. w tagalskim (Filipiny): <em>sulat</em> (pisać) -&gt; <em>s<strong>um</strong>ulat</em> (napisać) - morfem <em>-um-</em> jest wstawiony w środek rdzenia. Albo pomyślcie o angielskim <em>abso-bloody-lutely</em> ;-)</li>\n</ul>\n<h3><a href=\"#fleksja-vs-słowotwórstwo\" aria-hidden=\"true\" class=\"anchor\" id=\"fleksja-vs-słowotwórstwo\"></a>Fleksja vs słowotwórstwo</h3>\n<p>To ważne rozróżnienie:</p>\n<p><strong>Fleksja</strong> (odmiana) - zmienia formę gramatyczną, ale nie tworzy nowego słowa:</p>\n<ul>\n<li>dom -&gt; dom<strong>y</strong> (liczba mnoga)</li>\n<li>biegnie -&gt; bieg<strong>ł</strong> (czas przeszły)</li>\n</ul>\n<p><strong>Słowotwórstwo</strong> (derywacja) - tworzy nowe słowo, często zmieniając część mowy:</p>\n<ul>\n<li>uczyć -&gt; <strong>uczeń</strong> (czasownik -&gt; rzeczownik)</li>\n<li>dom -&gt; <strong>domowy</strong> (rzeczownik -&gt; przymiotnik)</li>\n</ul>\n<div class=\"markdown-alert markdown-alert-warning\">\n<p class=\"markdown-alert-title\">Warning</p>\n<p><strong>Polska morfologia to koszmar dla LLM!</strong> Język polski jest silnie fleksyjny - mamy 7 przypadków, 2 liczby, 3 rodzaje, aspekty czasowników, i mnóstwo wyjątków. Dla porównania: w angielskim odmiana rzeczownika to max. dodanie <em>-s</em>. W polskim? &quot;Dom, domu, domowi, dom, domem, domu, domie, domy, domów...&quot; i tak dalej :D</p>\n</div>\n<h3><a href=\"#jak-llm-radzi-sobie-z-morfologią\" aria-hidden=\"true\" class=\"anchor\" id=\"jak-llm-radzi-sobie-z-morfologią\"></a>Jak LLM radzi sobie z morfologią?</h3>\n<p>Tu jest ciekawostka. LLM nie &quot;wie&quot; czym są morfemy. Używa <strong>tokenizacji BPE</strong> (Byte Pair Encoding), która dzieli tekst na tokeny - ale tokeny to <strong>nie to samo</strong> co morfemy.</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph LR\n    subgraph &quot;Lingwista widzi:&quot;\n        A[&quot;nie-szczęśliw-y&quot;] --&gt; A1[&quot;nie + szczęśliw + y&quot;]\n    end\n    subgraph &quot;GPT-4o widzi (tokenizacja BPE):&quot;\n        B[&quot;nie-szczęśliw-y&quot;] --&gt; B1[&quot;nie + -s + zcz + ę + śli + wy&quot;]\n    end\n    \n    style A1 fill:#99ff99,color:#000\n    style B1 fill:#ff9999,color:#000\n</code></pre>\n<p>Tokenizator BPE dzieli tekst na podstawie <strong>częstotliwości występowania</strong> w danych treningowych, a nie na podstawie struktury językowej. Czasem to się pokrywa z morfemami, a czasem nie.</p>\n<p>To znaczy, że model nie uczy się morfologii &quot;od strony&quot; - on uczy się jej pośrednio, przez statystykę. I jakoś mu to działa ;-)</p>\n<div class=\"markdown-alert markdown-alert-tip\">\n<p class=\"markdown-alert-title\">Tip</p>\n<p><strong>Zabawa dla was:</strong> Potraficie rozbić te słowa na morfemy?</p>\n<ul>\n<li><em>nieprawdopodobny</em></li>\n<li><em>najpiękniejszy</em></li>\n<li><em>przeczytaliśmy</em></li>\n</ul>\n</div>\n<hr />\n<h2><a href=\"#składnia---zasady-gry-w-układanie-zdań\" aria-hidden=\"true\" class=\"anchor\" id=\"składnia---zasady-gry-w-układanie-zdań\"></a>Składnia - zasady gry w układanie zdań</h2>\n<h3><a href=\"#czym-jest-składnia\" aria-hidden=\"true\" class=\"anchor\" id=\"czym-jest-składnia\"></a>Czym jest składnia?</h3>\n<p><strong>Składnia</strong> to zbiór reguł, które określają, jak słowa są łączone w zdania. To dzięki składni wiemy, że &quot;Kot siedzi na macie&quot; to poprawne zdanie, a &quot;Kot macie na siedzi&quot; to bełkot.</p>\n<p>Proste? No... bardziej albo mniej ;-)</p>\n<h3><a href=\"#szyk-zdania\" aria-hidden=\"true\" class=\"anchor\" id=\"szyk-zdania\"></a>Szyk zdania</h3>\n<p>W różnych językach obowiązują różne zasady porządku słów:</p>\n<table>\n<thead>\n<tr>\n<th>Język</th>\n<th>Podstawowy szyk</th>\n<th>Przykład</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Angielski</td>\n<td>SVO (Subject-Verb-Object)</td>\n<td>The cat sits on the mat</td>\n</tr>\n<tr>\n<td>Polski</td>\n<td>&quot;Elastyczny&quot; SVO, ale...</td>\n<td>Kot siedzi na macie / Na macie siedzi kot / Siedzi kot na macie</td>\n</tr>\n<tr>\n<td>Japoński</td>\n<td>SOV</td>\n<td>猫がマットの上に座っている</td>\n</tr>\n<tr>\n<td>Łacina</td>\n<td>Wolny (bo końcówki mówią wszystko)</td>\n<td>Felix in tapete sedet</td>\n</tr>\n</tbody>\n</table>\n<p>Polski jest fajny, bo mamy dość dużą swobodę w szyku zdania. O ile w angielskim &quot;On the mat sits the cat&quot; brzmi poetycko albo nienaturalnie, o tyle u nas to normalne zdanie ;-)</p>\n<h3><a href=\"#drzewo-składniowe\" aria-hidden=\"true\" class=\"anchor\" id=\"drzewo-składniowe\"></a>Drzewo składniowe</h3>\n<p>Zdania mają strukturę hierarchiczną - słowa grupują się w frazy, a frazy w większe frazy. Można to narysować jako drzewo:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph TD\n    S[&quot;S (zdanie)&quot;] --&gt; NP1[&quot;NP (fraza rzeczownikowa)&quot;]\n    S --&gt; VP[&quot;VP (fraza czasownikowa)&quot;]\n    NP1 --&gt; N1[&quot;Kot&quot;]\n    VP --&gt; V[&quot;siedzi&quot;]\n    VP --&gt; PP[&quot;PP (fraza przyimkowa)&quot;]\n    PP --&gt; P[&quot;na&quot;]\n    PP --&gt; NP2[&quot;NP (fraza rzeczownikowa)&quot;]\n    NP2 --&gt; N2[&quot;macie&quot;]\n    \n    style S fill:#9999ff,color:#fff\n    style NP1 fill:#99ff99,color:#000\n    style VP fill:#ffcc99,color:#000\n    style PP fill:#ff9999,color:#000\n</code></pre>\n<p>To jest proste zdanie &quot;Kot siedzi na macie&quot; i już ma swoją hierarchię! A wyobraźcie sobie zdania z podrzędnymi, imiesłowami, okolicznikami...</p>\n<h3><a href=\"#bezbarwne-zielone-idee-śpią-zaciekawione\" aria-hidden=\"true\" class=\"anchor\" id=\"bezbarwne-zielone-idee-śpią-zaciekawione\"></a>&quot;Bezbarwne zielone idee śpią zaciekawione&quot;</h3>\n<p>To słynny przykład Noama Chomsky'ego (w oryginale: <em>&quot;Colorless green ideas sleep furiously&quot;</em>).</p>\n<p>Zdanie jest <strong>składniowo poprawne</strong> - ma podmiot, orzeczenie, prawidłową strukturę. Ale <strong>semantycznie</strong> to kompletny absurd - idee nie mogą być zielone ani spać.</p>\n<p>I to jest właśnie dowód na to, że <strong>składnia i semantyka to dwa różne poziomy</strong>. Można mieć perfekt składnię i zero sensu ;-)</p>\n<div class=\"markdown-alert markdown-alert-important\">\n<p class=\"markdown-alert-title\">Important</p>\n<p><strong>Składnia to supermoc LLM.</strong> Model uczy się przewidywać następny token na podstawie ogromnej ilości danych tekstowych. W trakcie tego procesu przyswaja wzorce składniowe - wie, że po &quot;Kot siedzi na...&quot; spodziewamy się rzeczownika, a po &quot;Szybko...&quot; czasownika. To jest fundament tego, dlaczego LLM generuje poprawne gramatycznie zdania.</p>\n</div>\n<h3><a href=\"#quiz-poprawne-czy-nie\" aria-hidden=\"true\" class=\"anchor\" id=\"quiz-poprawne-czy-nie\"></a>Quiz: poprawne czy nie?</h3>\n<p>Sprawdźcie się - które zdania są składniowo poprawne?<sup class=\"footnote-ref\"><a href=\"#fn-1\" id=\"fnref-1\" data-footnote-ref>1</a></sup></p>\n<ol>\n<li>Pies szczeka na listonosza.</li>\n<li>Na listonosza szczeka pies.</li>\n<li>Szczeka pies na listonosza.</li>\n<li>Pies listonosza na szczeka.</li>\n<li>Czy pies szczeka na listonosza?</li>\n</ol>\n<hr />\n<h2><a href=\"#semantyka---co-to-w-ogóle-znaczy-znaczenie\" aria-hidden=\"true\" class=\"anchor\" id=\"semantyka---co-to-w-ogóle-znaczy-znaczenie\"></a>Semantyka - co to w ogóle znaczy, &quot;znaczenie&quot;?</h2>\n<h3><a href=\"#czym-jest-semantyka\" aria-hidden=\"true\" class=\"anchor\" id=\"czym-jest-semantyka\"></a>Czym jest semantyka?</h3>\n<p>Jeśli składnia pyta &quot;jak to jest zbudowane?&quot;, to semantyka pyta: <strong>&quot;co to znaczy?&quot;</strong></p>\n<p>Semantyka bada znaczenie słów, fraz i zdań. I od razu się okazuje, że to wcale nie jest takie proste.</p>\n<h3><a href=\"#wieloznaczność---jedno-słowo-wiele-znaczeń\" aria-hidden=\"true\" class=\"anchor\" id=\"wieloznaczność---jedno-słowo-wiele-znaczeń\"></a>Wieloznaczność - jedno słowo, wiele znaczeń</h3>\n<p>Klasyczny polski przykład: <strong>zamek</strong></p>\n<ul>\n<li>Zamek - budowla (&quot;Zamek Królewski w Warszawie&quot;)</li>\n<li>Zamek - do drzwi (&quot;Zepsuł mi się zamek w drzwiach&quot;)</li>\n<li>Zamek - do ubrania (&quot;Rozpiął zamek w kurtce&quot;)</li>\n<li>Zamek - w karabinie (&quot;Zamek w karabinie się zaciął&quot;)</li>\n</ul>\n<p>To samo słowo, cztery zupełnie różne znaczenia. Jak LLM wie, o który chodzi? <strong>Z kontekstu.</strong> I to jest właśnie to, co robi dobrze - bo widział miliony zdań, gdzie &quot;zamek&quot; występował w różnych kontekstach.</p>\n<p>Albo inny klasyk - wieloznaczność w jednym zdaniu:</p>\n<blockquote>\n<p>&quot;Moja droga, ta droga jest bardzo droga.&quot;</p>\n</blockquote>\n<p>Trzy razy to samo słowo, trzy różne znaczenia: <strong>droga</strong> (zwrócenie się do kogoś), <strong>droga</strong> (ulica/szlak), <strong>droga</strong> (tania -&gt; droga, przymiotnik). Kontekst zmienia wszystko ;-)</p>\n<h3><a href=\"#synonimy---czy-duży-wielki-i-ogromny-to-to-samo\" aria-hidden=\"true\" class=\"anchor\" id=\"synonimy---czy-duży-wielki-i-ogromny-to-to-samo\"></a>Synonimy - czy &quot;duży&quot;, &quot;wielki&quot; i &quot;ogromny&quot; to to samo?</h3>\n<p>Prawie. Ale nie do końca:</p>\n<ul>\n<li><strong>Duży dom</strong> - normalny, po prostu spory</li>\n<li><strong>Wielki dom</strong> - brzmi bardziej oficjalnie/poetycko</li>\n<li><strong>Ogromny dom</strong> - no, to już jest rezydencja :D</li>\n</ul>\n<p>Semantyka bada te subtelne różnice - zarówno <strong>denotację</strong> (dosłowne znaczenie), jak i <strong>konotację</strong> (skojarzenia, nacechowanie emocjonalne).</p>\n<h3><a href=\"#jak-llm-rozumie-znaczenie\" aria-hidden=\"true\" class=\"anchor\" id=\"jak-llm-rozumie-znaczenie\"></a>Jak LLM &quot;rozumie&quot; znaczenie?</h3>\n<p>Tu wchodzi koncept <strong>word embeddings</strong> (osadzeń słów). W dużym uproszczeniu: każde słowo jest reprezentowane jako wektor - ciąg liczb - w wielowymiarowej przestrzeni. Słowa o podobnym znaczeniu są &quot;blisko&quot; siebie w tej przestrzeni.</p>\n<p>Słynny przykład, który prawdopodobnie widzieliście:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">wektor(&quot;król&quot;) - wektor(&quot;mężczyzna&quot;) + wektor(&quot;kobieta&quot;) ≈ wektor(&quot;królowa&quot;)\n</code></pre>\n<p>Czyli: model &quot;wie&quot;, że relacja między &quot;król&quot; a &quot;mężczyzna&quot; jest analogiczna do relacji między &quot;królowa&quot; a &quot;kobieta&quot;. I to z samych danych, nikt go tego nie uczył!</p>\n<p>Możemy to nawet pokazać w kodzie. Oto prosty przykład z biblioteką <code>gensim</code> w Pythonie:</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), ...]  -- na pierwszym miejscu: queen!</a-c></code></pre>\n<div class=\"markdown-alert markdown-alert-tip\">\n<p class=\"markdown-alert-title\">Tip</p>\n<p><strong>Jeśli chcecie to przetestować sami:</strong> zainstalujcie <code>gensim</code> (<code>pip install gensim</code>) i odpalcie powyższy kod. Oczywiście potrzebujecie trochę RAM-u - model GloVe nie jest mały. Ale efekt jest naprawdę satysfakcjonujący ;-)</p>\n</div>\n<h3><a href=\"#kompozycyjność\" aria-hidden=\"true\" class=\"anchor\" id=\"kompozycyjność\"></a>Kompozycyjność</h3>\n<p>Jedno z najważniejszych pojęć w semantyce: <strong>znaczenie zdania jest wynikiem znaczeń poszczególnych słów + zasad ich łączenia.</strong></p>\n<p>Czyli: &quot;Czerwony kot siedzi na macie&quot; = [czerwony] + [kot] + [siedzi] + [na] + [macie] + zasady składniowe.</p>\n<p>Brzmi banalne, ale to potężna zasada. Dzięki niej potrafimy zrozumieć nieskończoną liczbę zdań, nawet takich, których nigdy wcześniej nie słyszeliśmy. I dzięki niej LLM potrafi generować nowe, niespotykane zdania, które i tak mają sens.</p>\n<div class=\"markdown-alert markdown-alert-warning\">\n<p class=\"markdown-alert-title\">Warning</p>\n<p><strong>Ale uwaga:</strong> &quot;Zrozumiałem, że nie zrozumiałeś, że to nie było zrozumiałe.&quot; - każde słowo jest proste, ale zdanie jako całość wymaga przeanalizowania warstwy po warstwie. Kompozycyjność to potężne narzędzie, ale ma swoje granice.</p>\n</div>\n<hr />\n<h2><a href=\"#pragmatyka---co-naprawdę-miałem-na-myśli\" aria-hidden=\"true\" class=\"anchor\" id=\"pragmatyka---co-naprawdę-miałem-na-myśli\"></a>Pragmatyka - co naprawdę miałem na myśli</h2>\n<h3><a href=\"#czym-jest-pragmatyka\" aria-hidden=\"true\" class=\"anchor\" id=\"czym-jest-pragmatyka\"></a>Czym jest pragmatyka?</h3>\n<p>I tak dochodzimy do najciekawszej (według mnie) warstwy. <strong>Pragmatyka</strong> bada to, <strong>jak kontekst wpływa na znaczenie wypowiedzi</strong> - co <em>naprawdę</em> chcemy powiedzieć, często mówiąc coś zupełnie innego.</p>\n<p>Jeśli semantyka pyta &quot;co to znaczy?&quot;, pragmatyka pyta: <strong>&quot;co autor miał na myśli w tej konkretnej sytuacji?&quot;</strong></p>\n<h3><a href=\"#scenki-z-życia-wzięte\" aria-hidden=\"true\" class=\"anchor\" id=\"scenki-z-życia-wzięte\"></a>Scenki z życia wzięte</h3>\n<p><strong>Scenka 1: &quot;Zimno tu.&quot;</strong></p>\n<p>Semantycznie: informacja o temperaturze w pomieszczeniu.<br />\nPragmatycznie: <strong>&quot;Zamknij okno!&quot;</strong> albo <strong>&quot;Zwiększ ogrzewanie!&quot;</strong> albo <strong>&quot;Daj mi koc.&quot;</strong></p>\n<p>Każdy z was zrozumie, że kiedy ktoś mówi &quot;Zimno tu&quot; w momencie, gdy stoi przy otwartym oknie - nie pytasz o stopnie Celsjusza, tylko zamykasz okno.</p>\n<p><strong>Scenka 2: &quot;Mógłbyś podać sól?&quot;</strong></p>\n<p>Semantycznie: pytanie o twoją <em>zdolność</em> podania soli.<br />\nPragmatycznie: <strong>prośba o podanie soli.</strong></p>\n<p>Odpowiedź &quot;Tak, mógłbym&quot; (i nic więcej) jest semantycznie poprawna, ale pragmatycznie... no, jesteś trochę niemiły ;-)</p>\n<p><strong>Scenka 3: Ironia</strong></p>\n<blockquote>\n<p>&quot;No gratulacje, znowu to zepsułeś.&quot;</p>\n</blockquote>\n<p>Semantycznie: gratulacje.<br />\nPragmatycznie: <strong>ironia, krytyka, rozczarowanie.</strong></p>\n<p>Czy LLM łapie ironię? Czasem tak, czasem nie. To wciąż otwarty problem badawczy.</p>\n<h3><a href=\"#teoria-aktów-mowy\" aria-hidden=\"true\" class=\"anchor\" id=\"teoria-aktów-mowy\"></a>Teoria aktów mowy</h3>\n<p>Filozof J.L. Austin wymyślił coś genialnego: <strong>wypowiedzi nie tylko opisują rzeczywistość, ale też coś <em>robią</em>.</strong></p>\n<table>\n<thead>\n<tr>\n<th>Typ aktu mowy</th>\n<th>Opis</th>\n<th>Przykład</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>Lokucyjny</strong></td>\n<td>Samo wypowiedzenie</td>\n<td>Mówisz &quot;Zamykam okno&quot;</td>\n</tr>\n<tr>\n<td><strong>Illokucyjny</strong></td>\n<td>Intencja wypowiedzi</td>\n<td>Obiecanie, że zamkniesz okno</td>\n</tr>\n<tr>\n<td><strong>Perlokucyjny</strong></td>\n<td>Efekt na odbiorcę</td>\n<td>Słuchacz czuje ulgę</td>\n</tr>\n</tbody>\n</table>\n<p>Inne przykłady aktów mowy:</p>\n<ul>\n<li><strong>Obietnica:</strong> &quot;Obiecuję, że to zrobię.&quot; - mówiąc to, <em>wykonujesz</em> akt obietnicy</li>\n<li><strong>Oświadczenie:</strong> &quot;Oświadczam was mężem i żoną.&quot; - ktoś musi mieć do tego władzę, ale działa!</li>\n<li><strong>Przeprosiny:</strong> &quot;Przepraszam.&quot; - to nie jest opis stanu rzeczy, to <em>jest</em> akt przeprosin</li>\n</ul>\n<h3><a href=\"#implikatury-gricea\" aria-hidden=\"true\" class=\"anchor\" id=\"implikatury-gricea\"></a>Implikatury Grice'a</h3>\n<p>Paul Grice, inny filozof języka, zauważył, że w rozmowie obowiązuje <strong>zasada współpracy</strong> - mówimy rzeczy, które są istotne, prawdziwe, jasne i na temat.</p>\n<p>Kiedy ktoś tę zasadę łamie, szukamy <strong>implikatury</strong> - ukrytego znaczenia.</p>\n<p>Przykład:</p>\n<blockquote>\n<p>A: &quot;Idziesz na imprezę?&quot;<br />\nB: &quot;Mam egzamin jutro.&quot;</p>\n</blockquote>\n<p>B nie odpowiedział &quot;tak&quot; ani &quot;nie&quot;. Ale z kontekstu A rozumie: <strong>nie idę, bo muszę się uczyć.</strong> To jest implikatura konwersacyjna.</p>\n<h3><a href=\"#jak-llm-radzi-sobie-z-pragmatyką\" aria-hidden=\"true\" class=\"anchor\" id=\"jak-llm-radzi-sobie-z-pragmatyką\"></a>Jak LLM radzi sobie z pragmatyką?</h3>\n<div class=\"markdown-alert markdown-alert-warning\">\n<p class=\"markdown-alert-title\">Warning</p>\n<p><strong>To jest najtrudniejsza warstwa dla LLM.</strong> I to nie jest tylko moje zdanie - badacze z całego świata pracują nad benchmarkami testującymi pragmatykę modeli językowych.</p>\n</div>\n<p>Problemy:</p>\n<ul>\n<li><strong>Ironia i sarkazm</strong> - model często bierze dosłownie</li>\n<li><strong>Implikatury</strong> - nie zawsze &quot;wyłapuje&quot; to, co jest między wierszami</li>\n<li><strong>Akt mowy</strong> - może nie rozpoznać, czy ktoś obiecuje, pyta czy grozi</li>\n<li><strong>Kontekst kulturowy</strong> - co jest grzeczne w jednej kulturze, jest niegrzeczne w innej</li>\n</ul>\n<p>Ale z drugiej strony - GPT-4 i nowsze modele radzą sobie coraz lepiej. Dlaczego? Bo <strong>ogromna ilość danych treningowych zawiera pragmatykę w praktyce</strong> - dialogi z filmów, książek, forów internetowych. Model &quot;widział&quot; miliony przykładów ironii, grzeczności, próśb.</p>\n<div class=\"markdown-alert markdown-alert-tip\">\n<p class=\"markdown-alert-title\">Tip</p>\n<p><strong>Eksperyment dla was:</strong> Otwórzcie ChatGPT (albo Claude, albo co tam macie pod ręką) i przeprowadźcie ten krótki test:</p>\n<p>Napiszcie modelowi:</p>\n<p><em>&quot;Właśnie strzeliłem sobie w palca gwoździem.&quot;</em></p>\n<p>A potem od nowej rozmowy:</p>\n<p><em>&quot;No gratulacje, znowu strzeliłem sobie w palca gwoździem!&quot;</em></p>\n<p>Zobaczcie, czy model zauważy, że w drugim przypadku &quot;gratulacje&quot; to ironia, czy jednak zacznie wzywać pogotowie?</p>\n</div>\n<hr />\n<h2><a href=\"#podsumowanie---cała-cebulka-w-jednym-miejscu\" aria-hidden=\"true\" class=\"anchor\" id=\"podsumowanie---cała-cebulka-w-jednym-miejscu\"></a>Podsumowanie - cała cebulka w jednym miejscu</h2>\n<p>Oto nasze pięć warstw, w pigułce:</p>\n<table>\n<thead>\n<tr>\n<th>Warstwa</th>\n<th>Co bada</th>\n<th>Przykład</th>\n<th>Jak radzi sobie LLM</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>Fonetyka i fonologia</strong></td>\n<td>Dźwięki i system dźwięków</td>\n<td>/kot/ vs /lot/</td>\n<td>Nie słyszy, ale &quot;wie&quot; z danych tekstowych</td>\n</tr>\n<tr>\n<td><strong>Morfologia</strong></td>\n<td>Budowa słów z morfemów</td>\n<td>nie-szczęśliw-y</td>\n<td>Tokenizacja BPE ≠ morfemy, ale jakoś działa</td>\n</tr>\n<tr>\n<td><strong>Składnia</strong></td>\n<td>Porządek słów w zdaniu</td>\n<td>Kot siedzi na macie</td>\n<td>Supermoc modelu - świetnie radzi sobie z gramatyką</td>\n</tr>\n<tr>\n<td><strong>Semantyka</strong></td>\n<td>Znaczenie słów i zdań</td>\n<td>&quot;zamek&quot; - który?</td>\n<td>Word embeddings + kontekst</td>\n</tr>\n<tr>\n<td><strong>Pragmatyka</strong></td>\n<td>Znaczenie w kontekście</td>\n<td>&quot;Zimno tu&quot; = zamknij okno</td>\n<td>Najtrudniejsza warstwa, wciąż otwarty problem</td>\n</tr>\n</tbody>\n</table>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph LR\n    subgraph &quot;Jak LLM przetwarza język&quot;\n        F[&quot;Fonetyka&lt;br/&gt;❌ nie słyszy&quot;] --&gt; M[&quot;Morfologia&lt;br/&gt;⚠️ przez pryzmat tokenów&quot;]\n        M --&gt; S[&quot;Składnia&lt;br/&gt;✅ bardzo dobrze&quot;]\n        S --&gt; Sem[&quot;Semantyka&lt;br/&gt;✅ dobrze, z wyjątkami&quot;]\n        Sem --&gt; P[&quot;Pragmatyka&lt;br/&gt;❓ najtrudniejsza&quot;]\n    end\n</code></pre>\n<h3><a href=\"#co-dalej\" aria-hidden=\"true\" class=\"anchor\" id=\"co-dalej\"></a>Co dalej?</h3>\n<p>W kolejnym wpisie zmieniamy perspektywę - zamiast patrzeć na warstwy języka, patrzymy na samą naturę znaków i znaczenia:</p>\n<ul>\n<li><strong><a href=\"semiotyka-a-llm.html\">Semiotyka - dlaczego LLM nie &quot;myśli&quot;, ale jednak coś znaczy</a></strong></li>\n</ul>\n<!--W kolejnych wpisach tej serii wejdziemy głębiej w każdą z tych warstw:\n\n- **Fonetyka i LLM** - gdzie kończy się tekst, a zaczyna mowa? Jak modele speech (Whisper, TTS) łączą dźwięk z językiem?\n- **Morfologia i składnia w LLM** - czy model naprawdę rozpoznaje strukturę, czy tylko statystykę?\n- **Semantyka w LLM** - znaczenie, reprezentacja wektorowa i granice \"rozumienia\"\n- **Pragmatyka w LLM** - kontekst, implikatury i dlaczego model czasem \"nie ogarnia\"\n- **Reprezentacje lingwistyczne w LLM** - czy wewnątrz modelu można znaleźć \"neurony\" odpowiadające za konkretne cechy języka?-->\n<hr />\n<p>Jeśli dotarliście aż tu - <strong>dzięki!</strong> ;-) Naprawdę doceniam, że poświęciliście czas na przeczytanie tego potworka.</p>\n<p>Mam nadzieję, że choć trochę przybliżyłem wam temat. Jeśli coś jest niejasne - <strong>napiszcie w komentarzach</strong>, postaram się wyjaśnić. A jeśli macie lepsze przykłady (bo na pewno macie!) - tym bardziej dajcie znać.</p>\n<p>Która warstwa was najbardziej zaskoczyła? Która jest waszym zdaniem najciekawsza w kontekście AI?</p>\n<p>Do następnego wpisu!</p>\n<hr />\n<p><strong>Źródła i ciekawe linki:</strong></p>\n<p>Jeśli chcecie wejść głębiej, oto materiały, z których korzystałem przy pisaniu tego wpisu:</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> - świetny przegląd działów lingwistyki z przykładami</li>\n<li><a href=\"https://fiveable.me/lists/levels-of-linguistic-analysis\">Levels of Linguistic Analysis - Fiveable</a> - synteza poziomów analizy językowej</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> - proste, dydaktyczne ujęcie pięciu domen języka</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> - pomost między lingwistyką a praktyką LLM</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> - jak bada się kompetencję językową modeli</li>\n<li><a href=\"https://arxiv.org/abs/2502.12378\">Pragmatics in the Era of LLMs: A Survey - arXiv</a> - przegląd badań nad pragmatyką w LLM</li>\n</ul>\n<section class=\"footnotes\" data-footnotes>\n<ol>\n<li id=\"fn-1\">\n<p>Odpowiedzi do quizu: zdania 1-3 i 5 są poprawne (różnią się szykiem, ale polski na to pozwala; 5 to pytanie z słówkiem &quot;czy&quot;). Zdanie 4 (&quot;Pies listonosza na szczeka&quot;) jest niepoprawne składniowo - szyk jest tak zaburzony, że nie spełnia zasad polskiej gramatyki. <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": "",
      "date_published": "2026-06-06T00:00:00-00:00",
      "image": "",
      "authors": [
        {
          "name": "Błażej Gruszka",
          "url": "https://www.linkedin.com/in/blazejgruszka/",
          "avatar": "https://github.com/bgruszka.png"
        }
      ],
      "tags": [
        "llm",
        "ai",
        "jezykoznawstwo",
        "nlp",
        "linguistics",
        "language-models",
        "chatgpt"
      ],
      "language": "pl"
    }
  ]
}