{
  "version": "https://jsonfeed.org/version/1",
  "title": "gruszka.dev",
  "home_page_url": "https://gruszka.dev",
  "feed_url": "https://gruszka.dev/tag-lstm.json",
  "description": "Things I would like to share",
  "items": [
    {
      "id": "https://gruszka.dev/od-prostych-neuronow-do-pamieci.html",
      "url": "https://gruszka.dev/od-prostych-neuronow-do-pamieci.html",
      "title": "Od prostych neuronów do pamięci – ewolucja modeli językowych",
      "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 (perceptron, RNN, LSTM) znajdziesz w gotowym notatniku Jupyter: <a href=\"./od-prostych-neuronow-do-pamieci.ipynb\">od-prostych-neuronow-do-pamieci.ipynb</a></p>\n</div>\n<p>W <a href=\"jak-komputer-czyta-tekst.html\">poprzednim wpisie</a> przeszliśmy całą drogę od cięcia tekstu na tokeny, przez liczenie słów (TF-IDF), łańcuchy Markowa, aż po Word2Vec i wektory znaczeń. I na koniec pojawiło się jedno fundamentalne pytanie...</p>\n<p><strong>Mamy wektory słów. Super. Ale jak z nich zbudować coś, co rozumie zdanie? Albo cały paragraf? Albo książkę?</strong></p>\n<p>Bo przecież &quot;Dog bites man&quot; to nie jest po prostu &quot;dog&quot; + &quot;bites&quot; + &quot;man&quot;. To jest <strong>sekwencja</strong> - słowa następują po sobie w określonym porządku i ten porządek <strong>zmienia znaczenie</strong>. &quot;Dog bites man&quot; i &quot;Man bites dog&quot; to te same słowa, ale zupełnie inne historie :D</p>\n<p>A Word2Vec? Word2Vec patrzy na słowa <strong>pojedynczo</strong>. Nie ma pojęcia kolejności. Nie wie, że &quot;nie&quot; przed &quot;lubie&quot; zmienia wszystko.</p>\n<p>Więc potrzebujemy czegoś, co potrafi przetwarzać <strong>sekwencje</strong>. Coś, co czyta tekst w kolejności i buduje zrozumienie krok po kroku.</p>\n<p>I tu zaczyna się fascynująca historia - bo rozwiązanie tego problemu nie pojawiło się z dnia na dzień. To była ewolucja, która trwała kilkadziesiąt lat. Od jednego, prostego neuronu, do skomplikowanych komórek pamięci. I każda generacja rozwiązywała problem poprzedniej, ale tworzyła nowy.</p>\n<p>To jest <strong>czwarty wpis z serii &quot;zrozumiec LLM&quot;</strong>. Dzisiaj idziemy głębiej w architekturę sieci neuronowych - ale spokojnie, dalej bez wzorów, których nie da się zrozumieć ;-) Zamiast tego będzie dużo metafor, diagramów i przykładów z życia.</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">timeline\n    title Ewolucja architektur neuronowych\n    1943 : McCulloch &amp; Pitts&lt;br/&gt;model matematyczny neuronu\n    1958 : Rosenblatt&lt;br/&gt;perceptron\n    1986 : Rumelhart et al.&lt;br/&gt;backpropagation + MLP\n    1986 : RNN&lt;br/&gt;sieci rekurencyjne\n    1997 : Hochreiter &amp; Schmidhuber&lt;br/&gt;LSTM\n    2017 : Vaswani et al.&lt;br/&gt;Transformer (cliffhanger!)\n</code></pre>\n<p>Narracja dzisiejszego wpisu: <strong>&quot;Od jednej komórki do pamięci - i dlaczego to wciąż nie wystarczyło.&quot;</strong> Zaczynamy od podstaw.</p>\n<hr />\n<h2><a href=\"#neuron-biologiczny-vs-sztuczny---skąd-w-ogóle-ten-pomysł\" aria-hidden=\"true\" class=\"anchor\" id=\"neuron-biologiczny-vs-sztuczny---skąd-w-ogóle-ten-pomysł\"></a>Neuron biologiczny vs sztuczny - skąd w ogóle ten pomysł?</h2>\n<p>Zanim wejdziemy w architektury, szybko załatwmy jedno pytanie, które pewnie sobie zadajecie: <strong>dlaczego w ogóle nazywa się to &quot;siecią neuronową&quot;? Co to ma wspólnego z mózgiem?</strong></p>\n<p>Odpowiedź: i dużo, i mało ;-) Na początku była inspiracja biologią. Spójrzcie:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph LR\n    BD[&quot;🌿 Dendryty&lt;br/&gt;&lt;i&gt;odbierają sygnały&lt;/i&gt;&quot;] --&gt; BS[&quot;🧫 Soma&lt;br/&gt;&lt;i&gt;przetwarza&lt;/i&gt;&quot;]\n    BS --&gt; BA[&quot;⚡ Akson&lt;br/&gt;&lt;i&gt;przekazuje dalej&lt;/i&gt;&quot;]\n</code></pre>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph LR\n    SD[&quot;📥 Wejścia x₁, x₂, x₃&lt;br/&gt;&lt;i&gt;liczby&lt;/i&gt;&quot;] --&gt; SS[&quot;➕ Suma ważona&lt;br/&gt;&lt;i&gt;w₁x₁ + w₂x₂ + w₃x₃ + b&lt;/i&gt;&quot;]\n    SS --&gt; SA[&quot;📤 Wyjście&lt;br/&gt;&lt;i&gt;aktywacja(suma)&lt;/i&gt;&quot;]\n</code></pre>\n<p><strong>Pierwszy diagram</strong> - prawdziwy neuron. Ma <strong>dendryty</strong> (odbierają sygnały od innych neuronów), <strong>somę</strong> (ciało komórki - decyduje czy &quot;odpalić&quot;), i <strong>akson</strong> (przekazuje sygnał dalej).</p>\n<p><strong>Drugi diagram</strong> - sztuczny neuron. Ma <strong>wejścia</strong> (liczby), <strong>sumę ważoną</strong> (mnoży każde wejście przez jego &quot;ważność&quot; i dodaje wszystko), i <strong>wyjście</strong> (wynik przepuszczony przez funkcję aktywacji).</p>\n<p>Podobieństwo jest... luźne. Biologiczny neuron jest niewyobrażalnie bardziej skomplikowany. Ale <strong>inspiracja</strong> była ważna - Warren McCulloch i Walter Pitts w 1943 roku pokazali, że taki uproszczony model neuronu potrafi realizować podstawowe operacje logiczne (AND, OR, NOT). A to oznaczało, że sieć takich neuronów mogłaby teoretycznie obliczać <strong>cokolwiek</strong>.</p>\n<div class=\"markdown-alert markdown-alert-note\">\n<p class=\"markdown-alert-title\">Note</p>\n<p><strong>Czy LLM &quot;myśli&quot; jak mózg?</strong> Nie. Neuron sztuczny to matematyczna abstrakcja, nie model biologiczny. Biologiczny neuron używa impulsów elektrycznych, neuroprzekaźników, ma tysiące połączeń i dynamikę, której nie da się sprowadzić do <code>w₁x₁ + w₂x₂</code>. Ale inspiracja była punktem wyjścia - i to ważny punkt.</p>\n</div>\n<p>OK, to tyle biologii. Przejdźmy do tego, co z tego wynikło ;-)</p>\n<hr />\n<h2><a href=\"#perceptron---najprostsza-decyzja-świata\" aria-hidden=\"true\" class=\"anchor\" id=\"perceptron---najprostsza-decyzja-świata\"></a>Perceptron - najprostsza decyzja świata</h2>\n<p>W 1958 roku Frank Rosenblatt stworzył <strong>perceptron</strong> - pierwszy algorytm, który potrafił się <strong>uczyć</strong> na podstawie danych. To był kamień milowy.</p>\n<p>Perceptron działa banalnie prosto. Wyobraźcie sobie, że decydujecie, czy wyjść na spacer:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph TD\n    S[&quot;☀️ Słonecznie?&quot;] --&gt;|&quot;w = +5&quot;| SUM[&quot;➕ SUMA&quot;]\n    D[&quot;🌧️ Pada deszcz?&quot;] --&gt;|&quot;w = -8&quot;| SUM\n    T[&quot;🌡️ Temperatura &gt; 20°C?&quot;] --&gt;|&quot;w = +3&quot;| SUM\n    BIAS[&quot;⚙️ Próg (bias) = 0&quot;] --&gt; SUM\n    SUM --&gt; DEC{&quot;Suma &gt;= próg?&lt;br/&gt;(próg = 0)&quot;}\n    DEC --&gt;|&quot;Tak&quot;| Y[&quot;✅ Wychodzę na spacer!&quot;]\n    DEC --&gt;|&quot;Nie&quot;| N[&quot;❌ Zostaję w domu&quot;]\n    \n    style S fill:#ffcc99,color:#000\n    style D fill:#9999ff,color:#fff\n    style T fill:#ff9999,color:#000\n    style SUM fill:#99ff99,color:#000\n    style Y fill:#66cc66,color:#fff\n    style N fill:#cc6666,color:#fff\n</code></pre>\n<p>Mechanika jest prosta:</p>\n<ol>\n<li>Każde wejście ma swoją <strong>wagę</strong> (importance) - słońce ma wagę +5, deszcz -8, temperatura +3</li>\n<li>Perceptron <strong>mnoży</strong> każde wejście przez jego wagę i <strong>dodaje wszystko</strong> (to jest ta &quot;suma ważona&quot;)</li>\n<li>Jeśli suma jest większa lub równa <strong>progowi</strong> (w naszym przypadku próg = 0) - odpala &quot;tak&quot;. Inaczej - &quot;nie&quot;.</li>\n</ol>\n<p>Przykład:</p>\n<table>\n<thead>\n<tr>\n<th>Sytuacja</th>\n<th>Słońce (+5)</th>\n<th>Deszcz (-8)</th>\n<th>Temp &gt;20° (+3)</th>\n<th>Suma</th>\n<th>Decyzja</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Słonecznie, ciepło, bez deszczu</td>\n<td>1</td>\n<td>0</td>\n<td>1</td>\n<td>5 + 0 + 3 = <strong>8</strong></td>\n<td>✅ Wychodzę</td>\n</tr>\n<tr>\n<td>Pada, zimno</td>\n<td>0</td>\n<td>1</td>\n<td>0</td>\n<td>0 - 8 + 0 = <strong>-8</strong></td>\n<td>❌ Zostaję</td>\n</tr>\n<tr>\n<td>Pochmurno i pada, ale ciepło</td>\n<td>0</td>\n<td>1</td>\n<td>1</td>\n<td>0 - 8 + 3 = <strong>-5</strong></td>\n<td>❌ Zostaję</td>\n</tr>\n</tbody>\n</table>\n<p>I - najważniejsze - perceptron potrafi <strong>uczyć się</strong> tych wag. Pokazujecie mu 100 przykładów &quot;wyszedłem / nie wyszedłem&quot; i on stopniowo dostosowuje wagi, żeby jego decyzje pasowały do waszych.</p>\n<p>Genialne w swojej prostocie, prawda?</p>\n<h3><a href=\"#ale-perceptron-ma-jeden-wielki-problem\" aria-hidden=\"true\" class=\"anchor\" id=\"ale-perceptron-ma-jeden-wielki-problem\"></a>Ale perceptron ma jeden wielki problem...</h3>\n<p>Perceptron potrafi narysować <strong>jedną prostą linię</strong> dzielącą dane na dwie grupy. Co jest super, jeśli dane da się tak podzielić:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">⚫ ⚫ ⚫    │    ⚪ ⚪ ⚪\n⚫ ⚫ ⚫    │    ⚪ ⚪ ⚪\n          ↑\n     jedna linia dzieli je ✅\n</code></pre>\n<p>Ale co jeśli dane wyglądają tak?</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">⚪  ⚫\n\n⚫  ⚪\n</code></pre>\n<p>To jest słynny <strong>problem XOR</strong> (exclusive OR). Wyjście jest &quot;prawdziwe&quot; tylko wtedy, gdy dokładnie <strong>jedno</strong> wejście jest prawdziwe - nie oba i nie żadne. I nie da się narysować jednej prostej linii, która by to oddzieliła.</p>\n<p>Ten problem <strong>zatrzymał rozwój sieci neuronowych na prawie 20 lat</strong>. Serio. Badacze powiedzieli: &quot;no dobra, perceptron jest fajny, ale skoro nie radzi sobie z czymś tak prostym jak XOR, to po co to w ogóle?&quot; Ten okres nazywa się <strong>&quot;AI Winter&quot;</strong> (zima AI).</p>\n<div class=\"markdown-alert markdown-alert-warning\">\n<p class=\"markdown-alert-title\">Warning</p>\n<p><strong>Dlaczego XOR był taki ważny?</strong> Bo pokazał, że pojedynczy perceptron jest ograniczony do problemów <strong>liniowo separowalnych</strong>. A prawdziwy świat rzadko jest liniowy. Język na pewno nie jest.</p>\n</div>\n<p>I wtedy ktoś powiedział: <strong>a co jeśli połączymy kilka perceptronów razem?</strong></p>\n<hr />\n<h2><a href=\"#mlp---zespół-który-potrafi-więcej-niż-jednostka\" aria-hidden=\"true\" class=\"anchor\" id=\"mlp---zespół-który-potrafi-więcej-niż-jednostka\"></a>MLP - zespół, który potrafi więcej niż jednostka</h2>\n<p><strong>Multilayer Perceptron (MLP)</strong> to sieć złożona z wielu warstw neuronów. Zamiast jednej warstwy decyzyjnej, mamy:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph LR\n    subgraph &quot;Warstwa wejściowa&quot;\n        X1[&quot;x₁&quot;]\n        X2[&quot;x₂&quot;]\n        X3[&quot;x₃&quot;]\n    end\n    subgraph &quot;Warstwa ukryta 1&quot;\n        H1[&quot;h₁&lt;br/&gt;&lt;i&gt;proste cechy&lt;/i&gt;&quot;]\n        H2[&quot;h₂&quot;]\n        H3[&quot;h₃&quot;]\n        H4[&quot;h₄&quot;]\n    end\n    subgraph &quot;Warstwa ukryta 2&quot;\n        G1[&quot;g₁&lt;br/&gt;&lt;i&gt;kombinacje&lt;/i&gt;&quot;]\n        G2[&quot;g₂&quot;]\n        G3[&quot;g₃&quot;]\n    end\n    subgraph &quot;Warstwa wyjściowa&quot;\n        Y1[&quot;y₁&lt;br/&gt;&lt;i&gt;decyzja&lt;/i&gt;&quot;]\n        Y2[&quot;y₂&quot;]\n    end\n    \n    X1 --&gt; H1 &amp; H2 &amp; H3 &amp; H4\n    X2 --&gt; H1 &amp; H2 &amp; H3 &amp; H4\n    X3 --&gt; H1 &amp; H2 &amp; H3 &amp; H4\n    H1 --&gt; G1 &amp; G2 &amp; G3\n    H2 --&gt; G1 &amp; G2 &amp; G3\n    H3 --&gt; G1 &amp; G2 &amp; G3\n    H4 --&gt; G1 &amp; G2 &amp; G3\n    G1 --&gt; Y1 &amp; Y2\n    G2 --&gt; Y1 &amp; Y2\n    G3 --&gt; Y1 &amp; Y2\n    \n    style X1 fill:#ff9999,color:#000\n    style X2 fill:#ff9999,color:#000\n    style X3 fill:#ff9999,color:#000\n    style H1 fill:#ffcc99,color:#000\n    style H2 fill:#ffcc99,color:#000\n    style H3 fill:#ffcc99,color:#000\n    style H4 fill:#ffcc99,color:#000\n    style G1 fill:#99ff99,color:#000\n    style G2 fill:#99ff99,color:#000\n    style G3 fill:#99ff99,color:#000\n    style Y1 fill:#9999ff,color:#fff\n    style Y2 fill:#9999ff,color:#fff\n</code></pre>\n<p>Każdy neuron w jednej warstwie jest połączony z <strong>każdym</strong> neuronem w warstwie następnej (dlatego to się nazywa &quot;fully connected&quot; albo &quot;dense&quot;).</p>\n<p>Metafora, która mi najbardziej pasuje:</p>\n<p>Wyobraźcie sobie <strong>firmę</strong>. Analitycy (warstwa 1) patrzą na surowe dane i wyłapują proste wzorce. Menedżerowie (warstwa 2) łączą te wzorce w coś bardziej sensownego. Dyrektor (warstwa wyjściowa) podejmuje ostateczną decyzję. Żadna z tych osób nie rozumie pełnego obrazu sama - ale razem tworzą <strong>kaskadę abstrakcji</strong>.</p>\n<p>I właśnie to rozwiązuje problem XOR! Pierwsza warstwa rysuje <strong>dwie linie</strong>. Druga warstwa łączy je w <strong>jeden obszar</strong>. Nagle nieliniowy problem staje się rozwiązywalny.</p>\n<h3><a href=\"#ale-uwaga---jest-haczyk\" aria-hidden=\"true\" class=\"anchor\" id=\"ale-uwaga---jest-haczyk\"></a>Ale uwaga - jest haczyk!</h3>\n<p>Dodanie warstw ukrytych <strong>samo w sobie</strong> nic nie daje. Dlaczego?</p>\n<p>Bo jeśli każda warstwa robi tylko <code>suma = wagi × wejścia + bias</code>, to dwie warstwy takiej operacji to nadal... <strong>jedna wielka operacja liniowa</strong>. Pokażę na liczbach:</p>\n<p><strong>Warstwa 1:</strong> <code>y = 2·x + 1</code>\n<strong>Warstwa 2:</strong> <code>z = 3·y + 2</code></p>\n<p>Podstawiamy pierwsze do drugiego:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">z = 3·(2·x + 1) + 2\nz = 6·x + 3 + 2\nz = 6·x + 5    ← jedna operacja, tylko z innymi liczbami\n</code></pre>\n<p>Dwie warstwy złożyły się w jedną. Zamiast dwóch transformacji, dostajecie jedną - tylko z innymi współczynnikami. To jakbyście zamiast dwóch filtrów do kawy użyli jednego, ale dwa razy grubszego. Efekt ten sam.</p>\n<p>Więc potrzebujemy <strong>czegoś nieliniowego</strong> pomiędzy warstwami. I tu wkracza...</p>\n<h3><a href=\"#funkcja-aktywacji---nieoceniony-bohater-deep-learningu\" aria-hidden=\"true\" class=\"anchor\" id=\"funkcja-aktywacji---nieoceniony-bohater-deep-learningu\"></a>Funkcja aktywacji - nieoceniony bohater deep learningu</h3>\n<p>Funkcja aktywacji to taka &quot;niestandardowa przetwórnia&quot;, którą przepuszczamy wynik sumy. Najpopularniejsza dziś to <strong>ReLU</strong> (Rectified Linear Unit):</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-python\"><a-k>def</a-k> <a-f>relu</a-f>(<a-v>x</a-v>):\n    <a-k>if</a-k> <a-v>x</a-v> <a-o>&gt;</a-o> <a-n>0</a-n>:\n        <a-k>return</a-k> <a-v>x</a-v>\n    <a-k>else</a-k>:\n        <a-k>return</a-k> <a-n>0</a-n></code></pre>\n<p>To wszystko. <strong>Jeśli wartość jest dodatnia - zostaw. Jeśli ujemna - wyzeruj.</strong></p>\n<p>Brzmi banalnie? Jest banalnie. Ale ta jedna prosta operacja <strong>łamie liniowość</strong>.</p>\n<p>Wracamy do naszego przykładu z liczbami. Bez ReLU dwie warstwy złożyły się w jedną (<code>z = 6·x + 5</code>). Ale teraz wstawmy ReLU między nimi:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner\">Warstwa 1:  y = ReLU(2·x + 1)\nWarstwa 2:  z = ReLU(3·y + 2)\n</code></pre>\n<p>Sprawdźmy dla trzech wartości <code>x</code>:</p>\n<table>\n<thead>\n<tr>\n<th>x</th>\n<th>2·x + 1</th>\n<th>y (po ReLU)</th>\n<th>3·y + 2</th>\n<th>po ReLU</th>\n<th>Bez ReLU (6·x + 5)</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>-2</strong></td>\n<td>-3</td>\n<td><strong>0</strong> ⬅ wyzerowane!</td>\n<td>2</td>\n<td>2</td>\n<td>-7</td>\n</tr>\n<tr>\n<td><strong>0</strong></td>\n<td>1</td>\n<td>1</td>\n<td>5</td>\n<td>5</td>\n<td>5</td>\n</tr>\n<tr>\n<td><strong>1</strong></td>\n<td>3</td>\n<td>3</td>\n<td>11</td>\n<td>11</td>\n<td>11</td>\n</tr>\n</tbody>\n</table>\n<p>Bez ReLU wynik zmienia się liniowo (-7, 5, 11 - stały przyrost). Z ReLU nagle dla <code>x = -2</code> dostajemy <strong>2 zamiast -7</strong>. Ta relacja nie jest już prostą linią. <strong>Wyzerowanie ujemnych wartości jest czymś, czego nie da się wyrazić jako <code>a·x + b</code></strong> - i właśnie dlatego warstwy przestają się sklejać w jedną.</p>\n<p>Analogia: wyobraź sobie <strong>żaluzje w oknie</strong>. Liniowość to przezroczysta szyba - przepuszcza wszystko, lekko przyciemnione. ReLU to żaluzje, które całkowicie blokują światło poniżej pewnego kąta. Nie da się symulować &quot;całkowitego zablokowania&quot; przez &quot;mocniejsze przyciemnienie szyby&quot;. To <strong>jakościowo inna operacja</strong>.</p>\n<p>Dzięki ReLU każda warstwa robi coś <strong>innego</strong> niż poprzednia. I nagle sieć z wielu warstw staje się potężna - potrafi modelować nieliniowe, skomplikowane relacje w danych.</p>\n<div class=\"markdown-alert markdown-alert-important\">\n<p class=\"markdown-alert-title\">Important</p>\n<p><strong>ReLU w pigułce:</strong> Bez funkcji aktywacji (nieliniowej) między warstwami, sieć z 100 warstwami jest równie ekspresywna jak sieć z 1 warstwą. ReLU (i jego kuzyni: sigmoid, tanh, GELU) to <strong>składnik, który sprawia, że głębokość ma sens.</strong></p>\n</div>\n<p>Inne popularne funkcje aktywacji:</p>\n<table>\n<thead>\n<tr>\n<th>Funkcja</th>\n<th>Wzór (w uproszczeniu)</th>\n<th>Zakres</th>\n<th>Gdzie używana</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>ReLU</strong></td>\n<td>max(0, x)</td>\n<td>[0, +∞)</td>\n<td>Warstwy ukryte (standard)</td>\n</tr>\n<tr>\n<td><strong>Sigmoid</strong></td>\n<td>1 / (1 + e⁻ˣ)</td>\n<td>(0, 1)</td>\n<td>Bramki LSTM, output binarny</td>\n</tr>\n<tr>\n<td><strong>Tanh</strong></td>\n<td>(eˣ - e⁻ˣ) / (eˣ + e⁻ˣ)</td>\n<td>(-1, 1)</td>\n<td>Bramki LSTM, hidden states</td>\n</tr>\n<tr>\n<td><strong>Softmax</strong></td>\n<td>eˣⁱ / Σeˣʲ</td>\n<td>(0, 1), suma = 1</td>\n<td>Warstwa wyjściowa (klasyfikacja)</td>\n</tr>\n</tbody>\n</table>\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 Python i odpalcie to:</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-k>def</a-k> <a-f>relu</a-f>(<a-v>x</a-v>):\n    <a-k>return</a-k> <a-v>np</a-v>.<a-pr>maximum</a-pr>(<a-n>0</a-n>, <a-v>x</a-v>)\n\n<a-v>x</a-v> <a-o>=</a-o> <a-v>np</a-v>.<a-pr>array</a-pr>([<a-o>-</a-o><a-n>3</a-n>, <a-o>-</a-o><a-n>1</a-n>, <a-n>0</a-n>, <a-n>2</a-n>, <a-n>5</a-n>])\n<a-f>print</a-f>(<a-s>f&quot;Wejście: </a-s><a-p>{</a-p><a-v>x</a-v><a-p>}</a-p><a-s>&quot;</a-s>)\n<a-f>print</a-f>(<a-s>f&quot;Po ReLU: </a-s><a-p>{</a-p><a-f>relu</a-f><a-eb>(</a-eb><a-v>x</a-v><a-eb>)</a-eb><a-p>}</a-p><a-s>&quot;</a-s>)</code></pre>\n<p>Zobaczycie jak ReLU &quot;odcina&quot; wszystko, co jest poniżej zera. Ta prosta operacja pozwala sieci &quot;wybierać&quot;, które cechy są aktywne, a które ignorować.</p>\n</div>\n<h3><a href=\"#mini-quiz-liniowe-czy-nie\" aria-hidden=\"true\" class=\"anchor\" id=\"mini-quiz-liniowe-czy-nie\"></a>Mini-quiz: liniowe czy nie?<sup class=\"footnote-ref\"><a href=\"#fn-1\" id=\"fnref-1\" data-footnote-ref>1</a></sup></h3>\n<ol>\n<li>Zwykła regresja liniowa (y = ax + b) - liniowa czy nieliniowa?</li>\n<li>Perceptron z funkcją step (0 lub 1) - liniowy czy nieliniowy?</li>\n<li>MLP bez funkcji aktywacji między warstwami - liniowy czy nieliniowy?</li>\n</ol>\n<hr />\n<h2><a href=\"#problem-mlp-nie-rozumie-kolejności\" aria-hidden=\"true\" class=\"anchor\" id=\"problem-mlp-nie-rozumie-kolejności\"></a>Problem: MLP nie rozumie kolejności</h2>\n<p>OK, mamy MLP. Potrafi modelować nieliniowe relacje. Super. Ale jest jeden <strong>fundamentalny</strong> problem, który blokuje nas przed używaniem MLP do języka.</p>\n<p>MLP widzi <strong>wszystkie cechy naraz</strong>. Nie ma pojęcia kolejności.</p>\n<p>Weźmy klasyczny angielski przykład. Dwa zdania z <strong>dokładnie tymi samymi słowami</strong>, ale w innej kolejności:</p>\n<table>\n<thead>\n<tr>\n<th>Zdanie</th>\n<th>dog</th>\n<th>bites</th>\n<th>man</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>&quot;Dog bites man&quot;</td>\n<td>1</td>\n<td>1</td>\n<td>1</td>\n</tr>\n<tr>\n<td>&quot;Man bites dog&quot;</td>\n<td>1</td>\n<td>1</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>\n<p><strong>Identyczny wektor.</strong> A znaczenie? Pierwsze to nudna wiadomość. Drugie to sensacja w gazecie :D</p>\n<p>Dla MLP oba zdania to <strong>dokładnie to samo</strong> - bo MLP dostaje ten sam wektor i nie ma pojęcia kolejności.</p>\n<div class=\"markdown-alert markdown-alert-note\">\n<p class=\"markdown-alert-title\">Note</p>\n<p><strong>A co z polskim?</strong> Polski jest tu podchwytliwy - przez fleksję (7 przypadków!) rzadko mamy dwa zdania z <strong>dokładnie tymi samymi</strong> formami słów. &quot;Pies gryzie człowieka&quot; vs &quot;Człowiek gryzie psa&quot; to różne formy (pies→psa, człowieka→człowiek). Ale angielski, z minimalną odmianą, idealnie pokazuje ten problem. I właśnie dlatego przykłady z bag-of-words często używają angielskiego ;-)</p>\n</div>\n<p>To jest problem <strong>permutacji</strong> - MLP nie odróżnia <code>[A, B, C]</code> od <code>[C, B, A]</code>. A w języku kolejność jest <strong>wszystkim</strong>. &quot;Nie lubię&quot; vs &quot;Lubię nie&quot; - same słowa, zupełnie inne znaczenie.</p>\n<p>Można by pomyśleć: &quot;OK, to dodajmy pozycję słowa jako cechę&quot;. Ale to nie rozwiązuje problemu - MLP nadal nie rozumie, że słowo na pozycji 1 ma <strong>związek</strong> ze słowem na pozycji 5. Każda pozycja to po prostu kolejna liczba na wejściu.</p>\n<p>Więc potrzebujemy czegoś, co przetwarza dane <strong>krok po kroku</strong>, <strong>w kolejności</strong>, i buduje zrozumienie <strong>sekwencyjnie</strong>. Tak jak my czytamy książkę - zdanie po zdaniu, słowo po słowie.</p>\n<p>I tu wkracza RNN.</p>\n<hr />\n<h2><a href=\"#rnn---czyta-tekst-po-kolei\" aria-hidden=\"true\" class=\"anchor\" id=\"rnn---czyta-tekst-po-kolei\"></a>RNN - &quot;czyta tekst po kolei&quot;</h2>\n<p><strong>Recurrent Neural Network (RNN)</strong> to sieć, która ma coś, czego MLP nie ma: <strong>pamięć</strong>. No... jako tako ;-)</p>\n<p>Wyobraźcie sobie, że czytacie książkę. Nie czytacie całej strony naraz (jak robiłby MLP). Czytacie <strong>słowo po słowie</strong>, <strong>zdanie po zdaniu</strong>. I przy każdym nowym słowie <strong>aktualizujecie swoje zrozumienie</strong> tego, co się dzieje.</p>\n<p>Dokładnie to robi RNN. Ma <strong>hidden state</strong> (ukryty stan) - to jest jego &quot;rozumienie tekstu do tej pory&quot;. Przy każdym nowym słowie:</p>\n<ol>\n<li>Bierze nowe słowo (x_t)</li>\n<li>Bierze swoje dotychczasowe zrozumienie (h_{t-1})</li>\n<li>Łączy je i tworzy <strong>nowe</strong> zrozumienie (h_t)</li>\n</ol>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph LR\n    subgraph &quot;RNN czyta: 'Kot siedzi na macie'&quot;\n        X1[&quot;📖 'Kot'&quot;] --&gt; RNN1[&quot;🔄 RNN&lt;br/&gt;h₁&quot;]\n        RNN1 --&gt; |&quot;rozumie:&lt;br/&gt;'mowa o kocie'&quot;| RNN2[&quot;🔄 RNN&lt;br/&gt;h₂&quot;]\n        X2[&quot;📖 'siedzi'&quot;] --&gt; RNN2\n        RNN2 --&gt; |&quot;rozumie:&lt;br/&gt;'kot coś robi'&quot;| RNN3[&quot;🔄 RNN&lt;br/&gt;h₃&quot;]\n        X3[&quot;📖 'na'&quot;] --&gt; RNN3\n        RNN3 --&gt; RNN4[&quot;🔄 RNN&lt;br/&gt;h₄&quot;]\n        X4[&quot;📖 'macie'&quot;] --&gt; RNN4\n        RNN4 --&gt; |&quot;rozumie:&lt;br/&gt;'kot siedzi na macie'&quot;| OUT[&quot;✅ Pełne zrozumienie&quot;]\n    end\n    \n    style X1 fill:#ffcc99,color:#000\n    style X2 fill:#ffcc99,color:#000\n    style X3 fill:#ffcc99,color:#000\n    style X4 fill:#ffcc99,color:#000\n    style RNN1 fill:#9999ff,color:#fff\n    style RNN2 fill:#9999ff,color:#fff\n    style RNN3 fill:#9999ff,color:#fff\n    style RNN4 fill:#9999ff,color:#fff\n    style OUT fill:#66cc66,color:#fff\n</code></pre>\n<p>Ten diagram to jest właśnie <strong>unrolling</strong> (rozwinięcie w czasie). W rzeczywistości to <strong>ten sam</strong> neuron RNN - ale &quot;odwijamy&quot; go w czasie, żeby pokazać, jak przetwarza kolejne słowa. Zauważcie: strzałka idzie od lewej do prawej - informacja płynie <strong>sekwencyjnie</strong>.</p>\n<p>To jest genialne w swojej prostocie. RNN &quot;czyta&quot; tekst tak jak my - po kolei, budując zrozumienie krok po kroku.</p>\n<h3><a href=\"#rnn-w-kodzie---minimalistycznie\" aria-hidden=\"true\" class=\"anchor\" id=\"rnn-w-kodzie---minimalistycznie\"></a>RNN w kodzie - minimalistycznie</h3>\n<p>Żeby to naprawdę poczuć, napiszmy najprostszy możliwy RNN. Bez PyTorcha, bez TensorFlow - sam NumPy:</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-k>def</a-k> <a-f>simple_rnn</a-f>(<a-v>word_vectors</a-v>, <a-v>hidden_size</a-v><a-o>=</a-o><a-n>4</a-n>):\n    <a-v>words</a-v>, <a-v>dim</a-v> <a-o>=</a-o> <a-v>word_vectors</a-v>.<a-pr>shape</a-pr>\n    <a-v>np</a-v>.<a-pr>random</a-pr>.<a-pr>seed</a-pr>(<a-n>42</a-n>)\n    \n    <a-cr>W_h</a-cr> <a-o>=</a-o> <a-v>np</a-v>.<a-pr>random</a-pr>.<a-pr>randn</a-pr>(<a-v>hidden_size</a-v>, <a-v>hidden_size</a-v>) <a-o>*</a-o> <a-n>0.01</a-n>\n    <a-cr>W_x</a-cr> <a-o>=</a-o> <a-v>np</a-v>.<a-pr>random</a-pr>.<a-pr>randn</a-pr>(<a-v>hidden_size</a-v>, <a-v>dim</a-v>) <a-o>*</a-o> <a-n>0.01</a-n>\n    <a-v>bias</a-v> <a-o>=</a-o> <a-v>np</a-v>.<a-pr>zeros</a-pr>(<a-v>hidden_size</a-v>)\n    \n    <a-v>h</a-v> <a-o>=</a-o> <a-v>np</a-v>.<a-pr>zeros</a-pr>(<a-v>hidden_size</a-v>)\n    \n    <a-k>for</a-k> <a-v>t</a-v>, <a-v>word</a-v> <a-o>in</a-o> <a-f>enumerate</a-f>(<a-v>word_vectors</a-v>):\n        <a-v>h</a-v> <a-o>=</a-o> <a-v>np</a-v>.<a-pr>tanh</a-pr>(<a-cr>W_h</a-cr> @ <a-v>h</a-v> <a-o>+</a-o> <a-cr>W_x</a-cr> @ <a-v>word</a-v> <a-o>+</a-o> <a-v>bias</a-v>)\n        <a-f>print</a-f>(<a-s>f&quot;  Krok </a-s><a-p>{</a-p><a-v>t</a-v><a-o>+</a-o><a-n>1</a-n><a-p>}</a-p><a-s>: hidden state = </a-s><a-p>{</a-p><a-v>np</a-v><a-eb>.</a-eb><a-pr>round</a-pr><a-eb>(</a-eb><a-v>h</a-v><a-eb>, </a-eb><a-n>2</a-n><a-eb>)</a-eb><a-p>}</a-p><a-s>&quot;</a-s>)\n    \n    <a-k>return</a-k> <a-v>h</a-v>\n\n<a-v>kot</a-v> <a-o>=</a-o> <a-v>np</a-v>.<a-pr>array</a-pr>([<a-n>1.0</a-n>, <a-n>0.0</a-n>])\n<a-v>siedzi</a-v> <a-o>=</a-o> <a-v>np</a-v>.<a-pr>array</a-pr>([<a-n>0.0</a-n>, <a-n>1.0</a-n>])\n<a-v>na</a-v> <a-o>=</a-o> <a-v>np</a-v>.<a-pr>array</a-pr>([<a-n>0.3</a-n>, <a-n>0.3</a-n>])\n<a-v>macie</a-v> <a-o>=</a-o> <a-v>np</a-v>.<a-pr>array</a-pr>([<a-n>0.8</a-n>, <a-n>0.2</a-n>])\n\n<a-v>sentence</a-v> <a-o>=</a-o> <a-v>np</a-v>.<a-pr>array</a-pr>([<a-v>kot</a-v>, <a-v>siedzi</a-v>, <a-v>na</a-v>, <a-v>macie</a-v>])\n<a-f>print</a-f>(<a-s>&quot;Przetwarzam: &#39;Kot siedzi na macie&#39;&quot;</a-s>)\n<a-v>final</a-v> <a-o>=</a-o> <a-f>simple_rnn</a-f>(<a-v>sentence</a-v>)\n<a-f>print</a-f>(<a-s>f&quot;\\nFinalny hidden state: </a-s><a-p>{</a-p><a-v>np</a-v><a-eb>.</a-eb><a-pr>round</a-pr><a-eb>(</a-eb><a-v>final</a-v><a-eb>, </a-eb><a-n>2</a-n><a-eb>)</a-eb><a-p>}</a-p><a-s>&quot;</a-s>)</code></pre>\n<p>Uruchomcie to! Zobaczycie, jak przy każdym słowie <code>hidden state</code> się zmienia. Ostatni hidden state to jest &quot;rozumienie&quot; całego zdania przez nasz prosty RNN.</p>\n<div class=\"markdown-alert markdown-alert-tip\">\n<p class=\"markdown-alert-title\">Tip</p>\n<p><strong>Kluczowa intuicja:</strong> Zauważcie linijkę <code>h = np.tanh(W_h @ h + W_x @ word + bias)</code>. To jest serce RNN.</p>\n<p><strong>Dwie części tej linijki:</strong></p>\n<ul>\n<li><code>W_h @ h + W_x @ word + bias</code> — to jest ta sama &quot;suma ważona&quot; co w perceptronie i MLP. Tyle że teraz sumuje <strong>dwa źródła</strong>: poprzedni hidden state (<code>h</code>) i nowe słowo (<code>word</code>).</li>\n<li><code>tanh(...)</code> — to jest <strong>funkcja aktywacji</strong>, dokładnie ta sama rodzina co ReLU z poprzedniej sekcji. Zamiast &quot;odcinać poniżej zera&quot;, tanh ściska wszystko do zakresu (-1, 1). Ale cel jest ten sam: <strong>złamać liniowość</strong>.</li>\n</ul>\n<p>Nowy hidden state zależy od <strong>dwóch rzeczy</strong>: poprzedniego hidden state (<code>h</code>) i nowego słowa (<code>word</code>). To jest dokładnie to, co robimy, czytając tekst - łączymy to, co już wiemy, z tym, co właśnie przeczytaliśmy.</p>\n</div>\n<h3><a href=\"#ale-rnn-ma-jeden-wielki-problem\" aria-hidden=\"true\" class=\"anchor\" id=\"ale-rnn-ma-jeden-wielki-problem\"></a>Ale RNN ma jeden wielki problem...</h3>\n<p>I tu dochodzimy do tematu, który zmienił wszystko.</p>\n<hr />\n<h2><a href=\"#problem-złotej-rybki---dlaczego-rnn-zapomina\" aria-hidden=\"true\" class=\"anchor\" id=\"problem-złotej-rybki---dlaczego-rnn-zapomina\"></a>Problem złotej rybki - dlaczego RNN zapomina</h2>\n<p>RNN ma pamięć, tak. Ale to jest pamięć <strong>złotej rybki</strong>.</p>\n<p>Wyobraźcie sobie to zdanie:</p>\n<blockquote>\n<p>&quot;To jest <strong>Jan</strong> i ma 30 lat. Lubi chodzić po górach, programować w Pythonie i grać na gitarze. Jego ulubionym kolorem jest zielony. Kiedyś chciał zostać astronautą, ale potem odkrył, że (...) [tutaj 50 słów o różnych rzeczach] (...) i dlatego <strong>___</strong> poszedł do szkoły muzycznej.&quot;</p>\n</blockquote>\n<p>Jaki ma być &quot;___&quot;? <strong>Jan.</strong> Ale żeby to wiedzieć, RNN musi pamiętać imię z początku zdania. I tu pojawia się problem: po 50+ słowach, sygnał z pierwszego słowa jest tak <strong>rozwodniony</strong>, że RNN go praktycznie nie pamięta.</p>\n<p>To jest słynny problem <strong>vanishing gradient</strong> (zanikający gradient).</p>\n<p>Metafora: <strong>głuchy telefon</strong>. Gracie w głuchy telefon - pierwsza osoba szepcze wiadomość drugiej, ta trzeciej, itd. Po 10 osobach wiadomość jest trochę zniekształcona. Po 50 osobach - kompletnie nieczytelna. Po 100 osobach - ktoś mówi &quot;kup mleko&quot;, a ostatnia osoba słyszy &quot;łap chleb&quot;.</p>\n<p>W RNN gradient (sygnał uczący) przepływa przez sieć dokładnie tak samo - <strong>krok po kroku</strong>. Przy każdym kroku jest mnożony przez jakieś wagi. Jeśli te wagi są mniejsze niż 1, to po wielu mnożeniach gradient <strong>zanika do zera</strong>. Sieć przestaje się uczyć z odległych słów.</p>\n<p>A czasami jest odwrotnie - wagi są większe niż 1 i gradient <strong>rośnie eksponencjalnie</strong> (exploding gradient). Sieć &quot;oszalała&quot; i wartości lecą w kosmos.</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph LR\n    G1[&quot;Krok 1&lt;br/&gt;gradient = 1.0&quot;] --&gt;|&quot;×0.7&quot;| G2[&quot;Krok 2&lt;br/&gt;0.7&quot;]\n    G2 --&gt;|&quot;×0.7&quot;| G3[&quot;Krok 3&lt;br/&gt;0.49&quot;]\n    G3 --&gt;|&quot;×0.7&quot;| G4[&quot;Krok 4&lt;br/&gt;0.34&quot;]\n    G4 --&gt;|&quot;...&quot;| G5[&quot;Krok 20&lt;br/&gt;0.001&quot;]\n    G5 --&gt;|&quot;...&quot;| G6[&quot;Krok 50&lt;br/&gt;≈ 0&quot;]\n    \n    style G1 fill:#66cc66,color:#fff\n    style G2 fill:#99cc66,color:#000\n    style G3 fill:#cccc66,color:#000\n    style G4 fill:#cc9966,color:#000\n    style G5 fill:#cc6666,color:#fff\n    style G6 fill:#993333,color:#fff\n</code></pre>\n<div class=\"markdown-alert markdown-alert-warning\">\n<p class=\"markdown-alert-title\">Warning</p>\n<p><strong>Dlaczego to jest takie ważne?</strong> Bo język jest pełen <strong>długich zależności</strong>. &quot;The <strong>cat</strong>, which already ate all the fish in the fridge and then slept on the couch for three hours, was <strong>happy</strong>.&quot; - żeby połączyć &quot;cat&quot; z &quot;was happy&quot;, model musi przeskoczyć kilkanaście słów. RNN tego nie potrafi.</p>\n</div>\n<p>I wtedy w 1997 roku Sepp Hochreiter i Jürgen Schmidhuber powiedzieli: <strong>a gdyby tak dać sieci explicite mechanizm pamięci?</strong></p>\n<hr />\n<h2><a href=\"#lstm---sieć-z-notatnikiem\" aria-hidden=\"true\" class=\"anchor\" id=\"lstm---sieć-z-notatnikiem\"></a>LSTM - sieć z notatnikiem</h2>\n<p><strong>Long Short-Term Memory (LSTM)</strong> to RNN na sterydach. Zamiast jednego prostego hidden state, LSTM ma <strong>komórkę pamięci</strong> (cell state) i <strong>trzy bramki</strong> (gates), które decydują, co z tą pamięcią zrobić.</p>\n<p>Metafora, która najlepiej działa: <strong>LSTM to uczeń z notatnikiem i trzema zasadami</strong>.</p>\n<h3><a href=\"#bramka-forget-zapomnij---wyrzuć-stare-notatki\" aria-hidden=\"true\" class=\"anchor\" id=\"bramka-forget-zapomnij---wyrzuć-stare-notatki\"></a>Bramka forget (zapomnij) - &quot;wyrzuć stare notatki&quot;</h3>\n<p>Na początku każdego kroku LSTM pyta: <strong>&quot;co z dotychczasowej pamięci jest już nieaktualne i mogę wyrzucić?&quot;</strong></p>\n<p>Jak czytacie nowy rozdział książki, to nie potrzebujecie pamiętać, co jedli bohaterowie na śniadanie trzy rozdziały temu. Zapominacie nieistotne detale, żeby zrobić miejsce na nowe.</p>\n<p>W LSTM: forget gate przepuszcza każdy element pamięci przez <strong>sigmoidę</strong> (wartości 0-1). Blisko 0 = &quot;zapomnij&quot;. Blisko 1 = &quot;zatrzymaj&quot;.</p>\n<h3><a href=\"#bramka-input-zapamiętaj---zapisz-to-to-jest-ważne\" aria-hidden=\"true\" class=\"anchor\" id=\"bramka-input-zapamiętaj---zapisz-to-to-jest-ważne\"></a>Bramka input (zapamiętaj) - &quot;zapisz to, to jest ważne&quot;</h3>\n<p>Potem LSTM pyta: <strong>&quot;co z nowego słowa jest warte zapisania?&quot;</strong></p>\n<p>Kiedy czytacie &quot;Mam na imię <strong>Jan</strong>&quot; - macie refleks: &quot;OK, to jest ważne, muszę to zapamiętać&quot;. Nie zapamiętujecie każdego słowa dosłownie, ale <strong>wybieracie</strong> to, co istotne.</p>\n<p>W LSTM: input gate decyduje, które nowe informacje przepuścić do cell state.</p>\n<h3><a href=\"#bramka-output-wyeksponuj---pokaż-mi-co-teraz-potrzebuję\" aria-hidden=\"true\" class=\"anchor\" id=\"bramka-output-wyeksponuj---pokaż-mi-co-teraz-potrzebuję\"></a>Bramka output (wyeksponuj) - &quot;pokaż mi, co teraz potrzebuję&quot;</h3>\n<p>Na koniec LSTM pyta: <strong>&quot;co z mojej pamięci jest teraz istotne dla tego, co robię?&quot;</strong></p>\n<p>Jak ktoś was pyta &quot;Jak ma na imię ta postać, o której właśnie czytaliśmy?&quot; - sięgacie do notatnika i wyciągacie konkretne informacje. Nie pokazujecie całego notatnika - tylko to, co jest potrzebne <strong>teraz</strong>.</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph TD\n    subgraph &quot;LSTM - komórka pamięci&quot;\n        CS_IN[&quot;📥 Cell state (wejście)&lt;br/&gt;&lt;i&gt;taśma z informacjami&lt;/i&gt;&quot;] --&gt; MUL1[&quot;✖️ × forget gate&quot;]\n        FG[&quot;🗑️ Forget gate&lt;br/&gt;&lt;i&gt;co zapomnieć?&lt;/i&gt;&quot;] --&gt; MUL1\n        MUL1 --&gt; ADD[&quot;➕ + nowa informacja&quot;]\n        NEW[&quot;📝 Nowa informacja&lt;br/&gt;&lt;i&gt;input gate × kandydat&lt;/i&gt;&quot;] --&gt; ADD\n        IG[&quot;📥 Input gate&lt;br/&gt;&lt;i&gt;co zapisać?&lt;/i&gt;&quot;] --&gt; NEW\n        ADD --&gt; CS_OUT[&quot;📤 Cell state (wyjście)&lt;br/&gt;&lt;i&gt;zaktualizowana taśma&lt;/i&gt;&quot;]\n        CS_OUT --&gt; TANH[&quot;tanh&quot;]\n        TANH --&gt; MUL2[&quot;✖️ × output gate&quot;]\n        OG[&quot;📤 Output gate&lt;br/&gt;&lt;i&gt;co pokazać?&lt;/i&gt;&quot;] --&gt; MUL2\n        MUL2 --&gt; HS[&quot;🧠 Hidden state&lt;br/&gt;&lt;i&gt;to, co 'myśli' teraz&lt;/i&gt;&quot;]\n    end\n    \n    X[&quot;📖 Nowe słowo (x_t)&quot;] --&gt; FG &amp; IG &amp; OG\n    HS_PREV[&quot;🧠 Poprzedni hidden state (h_{t-1})&quot;] --&gt; FG &amp; IG &amp; OG\n    \n    style CS_IN fill:#ffcc99,color:#000\n    style CS_OUT fill:#ffcc99,color:#000\n    style FG fill:#ff6666,color:#fff\n    style IG fill:#66cc66,color:#fff\n    style OG fill:#6666ff,color:#fff\n    style HS fill:#cc99ff,color:#000\n    style X fill:#ffcc99,color:#000\n</code></pre>\n<p>Kluczowa rzecz: <strong>cell state</strong> to jest ta &quot;taśma produkcyjna&quot; (conveyor belt). Informacja na niej może płynąć <strong>prawie niezmieniona</strong> przez wiele kroków - bramki decydują tylko, co przepuścić, co dodać i co usunąć. To rozwiązuje problem vanishing gradient, bo informacja nie musi być &quot;mnożona&quot; na każdym kroku - może po prostu przepłynąć.</p>\n<div class=\"markdown-alert markdown-alert-note\">\n<p class=\"markdown-alert-title\">Note</p>\n<p><strong>RNN vs LSTM w jednym zdaniu:</strong> RNN próbuje pamiętać <strong>wszystko</strong>, ale szybko zapomina. LSTM jest <strong>selektywne</strong> - decyduje co zachować, co zaktualizować i co pokazać. To jest różnica między &quot;próbą zapamiętania całego wykładu słowo w słowo&quot; a &quot;robieniem notatek z najważniejszymi punktami&quot;.</p>\n</div>\n<details>\n<summary>Dla chętnych: matematyka bramek LSTM</summary>\n<p>Jeśli chcecie zobaczyć wzory, oto one (nie musicie ich zapamiętywać, ale warto zobaczyć, że to nie jest czarna magia):</p>\n<p><strong>Forget gate:</strong>\n$$f_t = \\sigma(W_f \\cdot [h_{t-1}, x_t] + b_f)$$</p>\n<p><strong>Input gate:</strong>\n$$i_t = \\sigma(W_i \\cdot [h_{t-1}, x_t] + b_i)$$</p>\n<p><strong>Kandydat do cell state:</strong>\n$$\\tilde{C}<em>t = \\tanh(W_c \\cdot [h</em>{t-1}, x_t] + b_c)$$</p>\n<p><strong>Aktualizacja cell state:</strong>\n$$C_t = f_t \\odot C_{t-1} + i_t \\odot \\tilde{C}_t$$</p>\n<p><strong>Output gate:</strong>\n$$o_t = \\sigma(W_o \\cdot [h_{t-1}, x_t] + b_o)$$</p>\n<p><strong>Hidden state:</strong>\n$$h_t = o_t \\odot \\tanh(C_t)$$</p>\n<p>Gdzie $\\sigma$ to sigmoid (wgniata wartości do 0-1), $\\odot$ to mnożenie element-po-elemencie, a $[h_{t-1}, x_t]$ to połączenie poprzedniego hidden state z nowym wejściem.</p>\n</details>\n<h3><a href=\"#eksperyment-dla-was\" aria-hidden=\"true\" class=\"anchor\" id=\"eksperyment-dla-was\"></a>Eksperyment dla was</h3>\n<p>Spróbujcie sami. Odpalcie ChatGPT (albo Claude, Gemini - co macie) i wyślijcie ten prompt:</p>\n<blockquote>\n<p>&quot;Pola na imieniu miała rzadką zdolność do zapamiętywania cyfr. Jej ulubioną liczbą było 42. Lubiła też longboarding, malowanie akwarelami i granie w szachy. Jej kot wabił się Mruczka. (...) <em>[tutaj wklejcie 3-4 akapity jakiegokolwiek tekstu - przepis na naleśniki, opis pogody, cokolwiek]</em> (...) Mimo wszystko, to właśnie ___ postanowiła wziąć udział w konkursie matematycznym.&quot;</p>\n</blockquote>\n<p>Zobaczcie, czy model wypełni lukę poprawnie (<strong>Pola</strong>). LSTM by sobie z tym poradził. Zwykły RNN - prawdopodobnie nie. A dlaczego? Bo LSTM ma mechanizm, który mówi &quot;zapamiętaj imię <strong>Pola</strong> - to może być ważne później&quot;. RNN po prostu... zapomniałby ;-)</p>\n<hr />\n<h2><a href=\"#dlaczego-lstm-wciąż-nie-wystarczyło\" aria-hidden=\"true\" class=\"anchor\" id=\"dlaczego-lstm-wciąż-nie-wystarczyło\"></a>Dlaczego LSTM wciąż nie wystarczyło</h2>\n<p>OK, więc LSTM rozwiązało problem pamięci. Przynajmniej częściowo. Ale pojawiły się nowe problemy, które okazały się <strong>fundamentalne</strong>.</p>\n<h3><a href=\"#problem-1-nawet-lstm-ma-swoje-granice\" aria-hidden=\"true\" class=\"anchor\" id=\"problem-1-nawet-lstm-ma-swoje-granice\"></a>Problem 1: Nawet LSTM ma swoje granice</h3>\n<p>LSTM rozwiązało vanishing gradient <strong>w dużej mierze</strong>, ale nie <strong>całkowicie</strong>. Gdy odległość między powiązanymi słowami rośnie do setek lub tysięcy słów, nawet LSTM zaczyna gubić nić narracji. Badacze Bengio et al. (1994) pokazali, że sieci rekurencyjne - nawet z bramkami - wciąż mają problem z podtrzymaniem użytecznych gradientów na bardzo długich dystansach.</p>\n<p>Czyli: zamiast zapominać po 10 słowach (RNN), LSTM zapomina po... 100? 200? Lepiej, ale wciąż nie idealnie.</p>\n<h3><a href=\"#problem-2-sekwencyjność--wąskie-gardło\" aria-hidden=\"true\" class=\"anchor\" id=\"problem-2-sekwencyjność--wąskie-gardło\"></a>Problem 2: Sekwencyjność = wąskie gardło</h3>\n<p>I to jest <strong>najważniejszy</strong> problem.</p>\n<p>RNN i LSTM przetwarzają dane <strong>krok po kroku</strong>. Krok 2 musi poczekać na krok 1. Krok 3 na krok 2. I tak dalej.</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph LR\n    S1[&quot;Krok 1&quot;] --&gt; S2[&quot;Krok 2&quot;]\n    S2 --&gt; S3[&quot;Krok 3&quot;]\n    S3 --&gt; S4[&quot;Krok 4&quot;]\n    S4 --&gt; S5[&quot;...&quot;]\n    S5 --&gt; S6[&quot;Krok 1000&quot;]\n</code></pre>\n<p>A teraz pomyślcie o GPU. GPU to jest maszynka, która <strong>uwielbia</strong> robić wiele rzeczy naraz (parallel processing). Tysiące rdzeni pracujących równocześnie.</p>\n<p>Ale RNN/LSTM mówi GPU: &quot;nie, nie, poczekaj. Najpierw skończmy krok 1, <strong>dopiero potem</strong> zacznij krok 2&quot;. GPU płacze.</p>\n<p>To jest jak książka z 10 000 rozdziałów, gdzie <strong>każdy rozdział musi być przeczytany po poprzednim</strong>. Nie możecie przeczytać rozdziału 500, dopóki nie skończycie 499. Nie da się tego zrównoleglić. Przy krótkich tekstach to nie problem, ale przy trenowaniu modelu na <strong>miliardach</strong> słów? To jest koszmar.</p>\n<p>LSTM ma dodatkowo <strong>4x więcej parametrów</strong> niż prosty RNN (trzy bramki + cell state = cztery zestawy wag na każdy neuron). Więc trenuje się wolniej i zużywa więcej pamięci.</p>\n<h3><a href=\"#problem-3-black-box\" aria-hidden=\"true\" class=\"anchor\" id=\"problem-3-black-box\"></a>Problem 3: &quot;Black box&quot;</h3>\n<p>LSTM (jak większość modeli deep learning) jest trudny do interpretacji. Model może generować poprawne wyniki, ale <strong>trudno zrozumieć, dlaczego</strong> podjął taką a nie inną decyzję. W zastosowaniach takich jak medycyna czy finanse, gdzie interpretowalność jest kluczowa, to jest poważny problem.</p>\n<h3><a href=\"#podsumowanie-ewolucji---co-rozwiązało-co\" aria-hidden=\"true\" class=\"anchor\" id=\"podsumowanie-ewolucji---co-rozwiązało-co\"></a>Podsumowanie ewolucji - co rozwiązało co</h3>\n<table>\n<thead>\n<tr>\n<th>Architektura</th>\n<th>Rok</th>\n<th>Rozwiązuje...</th>\n<th>Ale nie potrafi...</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>Perceptron</strong></td>\n<td>1958</td>\n<td>Klasyfikacja liniowa</td>\n<td>XOR, nieliniowość</td>\n</tr>\n<tr>\n<td><strong>MLP</strong></td>\n<td>1986</td>\n<td>Nieliniowe relacje</td>\n<td>Rozumieć kolejność słów</td>\n</tr>\n<tr>\n<td><strong>RNN</strong></td>\n<td>1986</td>\n<td>Przetwarzać sekwencje krok po kroku</td>\n<td>Pamiętać długie zależności</td>\n</tr>\n<tr>\n<td><strong>LSTM</strong></td>\n<td>1997</td>\n<td>Pamiętać dłużej dzięki bramkom</td>\n<td>Przetwarzać równolegle, bardzo długie teksty</td>\n</tr>\n</tbody>\n</table>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph LR\n    P[&quot;Perceptron&lt;br/&gt;✅ klasyfikuje&lt;br/&gt;❌ tylko liniowo&quot;] --&gt;|&quot;dodaj warstwy&quot;| MLP\n    MLP[&quot;MLP&lt;br/&gt;✅ nieliniowość&lt;br/&gt;❌ nie widzi kolejności&quot;] --&gt;|&quot;dodaj rekurencję&quot;| RNN\n    RNN[&quot;RNN&lt;br/&gt;✅ sekwencje&lt;br/&gt;❌ zapomina&quot;] --&gt;|&quot;dodaj bramki&quot;| LSTM\n    LSTM[&quot;LSTM&lt;br/&gt;✅ dłuższa pamięć&lt;br/&gt;❌ sekwencyjny = wolny&quot;] --&gt;|&quot;???&quot;| Q[&quot;&lt;b&gt;???&lt;/b&gt;&quot;]\n    \n    style P fill:#ff9999,color:#000\n    style MLP fill:#ffcc99,color:#000\n    style RNN fill:#99ccff,color:#000\n    style LSTM fill:#cc99ff,color:#000\n    style Q fill:#ff6666,color:#fff\n</code></pre>\n<p>Każda generacja rozwiązywała problem poprzedniej, ale tworzyła <strong>nowy</strong>. To jest jak gra w whack-a-mole - uderzasz jednego kreta, wyskakuje następny.</p>\n<p>I wtedy w 2017 roku ktoś zadał szalone pytanie...</p>\n<hr />\n<h2><a href=\"#a-co-jeśli-przestaniemy-czytać-po-kolei\" aria-hidden=\"true\" class=\"anchor\" id=\"a-co-jeśli-przestaniemy-czytać-po-kolei\"></a>&quot;A co jeśli przestaniemy czytać po kolei?&quot;</h2>\n<p>Metafora, która zmienia wszystko.</p>\n<p>Wyobraźcie sobie, że czytacie powieść. Teraz macie trzy sposoby na to:</p>\n<ul>\n<li><strong>RNN</strong> - pamiętacie ostatnie kilka zdań. Reszta się zaciera.</li>\n<li><strong>LSTM</strong> - zatrzymujecie główną fabułę, zapominacie małe detale. Lepiej, ale wciąż czytacie liniowo.</li>\n<li><strong>???</strong> - zamiast czytać słowo po słowie, <strong>patrzycie na całą stronę naraz</strong>. Wasza uwaga skacze do kluczowych postaci, ważnych momentów fabularnych, nieoczekiwanych zwrotów akcji. <strong>Nie czytacie po kolei - rozumiecie całość jednocześnie!</strong></li>\n</ul>\n<p>W 2017 roku grupa badaczy z Google opublikowała artykuł ze skromnym tytułem: <strong>&quot;Attention Is All You Need&quot;</strong>. Ich szalona idea? <strong>A co jeśli w ogóle przestaniemy czytać po kolei?</strong> A co jeśli każde słowo będzie mogło &quot;zobaczyć&quot; <strong>wszystkie inne słowa naraz</strong>?</p>\n<p>I <strong>nastąpiła rewolucja</strong>. Bo jeśli każde słowo patrzy na każde inne <strong>jednocześnie</strong> (nie czekając na swoją kolej), to:</p>\n<ul>\n<li>Nie ma sekwencyjnego wąskiego gardła - wszystko dzieje się <strong>równolegle</strong></li>\n<li>GPU jest wniebowzięte - tysiące rdzeni pracujących naraz</li>\n<li>Długie zależności? Nie ma problemu - słowo nr 1 i słowo nr 1000 &quot;widzą&quot; się <strong>bezpośrednio</strong>, bez 999 kroków pośrednich</li>\n</ul>\n<p>Ta architektura nazywa się <strong>Transformer</strong>. I to na niej są zbudowane ChatGPT, Claude, Gemini i wszystkie LLM, które znacie.</p>\n<hr />\n<h2><a href=\"#podsumowanie---cała-ewolucja-w-jednym-miejscu\" aria-hidden=\"true\" class=\"anchor\" id=\"podsumowanie---cała-ewolucja-w-jednym-miejscu\"></a>Podsumowanie - cała ewolucja w jednym miejscu</h2>\n<table>\n<thead>\n<tr>\n<th>Architektura</th>\n<th>Metafora</th>\n<th>Co potrafi</th>\n<th>Czego nie umie?</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>Perceptron</strong></td>\n<td>Linijka</td>\n<td>Rysuje jedną linię, klasyfikuje na 2 grupy</td>\n<td>XOR, nieliniowość, cokolwiek złożonego</td>\n</tr>\n<tr>\n<td><strong>MLP</strong></td>\n<td>Zespół analityków</td>\n<td>Modeluje nieliniowe relacje, &quot;głębokość&quot;</td>\n<td>Nie rozumie kolejności, widzi wszystko naraz</td>\n</tr>\n<tr>\n<td><strong>RNN</strong></td>\n<td>Czytelnik słowo po słowie</td>\n<td>Przetwarza sekwencje, ma hidden state</td>\n<td>Krótka pamięć (vanishing gradient)</td>\n</tr>\n<tr>\n<td><strong>LSTM</strong></td>\n<td>Uczeń z notatnikiem i bramkami</td>\n<td>Selekrewna pamięć, trzy bramki kontroli</td>\n<td>Sekwencyjny = wolny, brak parallelizacji</td>\n</tr>\n<tr>\n<td><strong>Transformer</strong></td>\n<td>Cała strona naraz</td>\n<td>Równoległość, uwaga na wszystko</td>\n<td>...do odkrycia w następnych wpisach...</td>\n</tr>\n</tbody>\n</table>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-mermaid\">graph TD\n    subgraph &quot;Od neuronu do LLM - droga, którą przeszliśmy&quot;\n        P[&quot;Perceptron 1958&lt;br/&gt;liniowa klasyfikacja&quot;] --&gt; MLP\n        MLP[&quot;MLP 1986&lt;br/&gt;nieliniowość + warstwy&quot;] --&gt; RNN\n        RNN[&quot;RNN 1986&lt;br/&gt;sekwencje + hidden state&quot;] --&gt; LSTM\n        LSTM[&quot;LSTM 1997&lt;br/&gt;pamięć z bramkami&quot;] --&gt; T\n        T[&quot;Transformer 2017&lt;br/&gt;uwaga na wszystko naraz&quot;]\n    end\n    \n    style P fill:#ff9999,color:#000\n    style MLP fill:#ffcc99,color:#000\n    style RNN fill:#99ccff,color:#000\n    style LSTM fill:#cc99ff,color:#000\n    style T fill:#ff6666,color:#fff\n</code></pre>\n<h3><a href=\"#quiz-końcowy-dopasuj-architekturę\" aria-hidden=\"true\" class=\"anchor\" id=\"quiz-końcowy-dopasuj-architekturę\"></a>Quiz końcowy: dopasuj architekturę<sup class=\"footnote-ref\"><a href=\"#fn-2\" id=\"fnref-2\" data-footnote-ref>2</a></sup></h3>\n<ol>\n<li>Chcesz przewidzieć cenę domu na podstawie metrażu, liczby pokoi i dzielnicy - jaka architektura wystarczy?</li>\n<li>Musisz przetworzyć zdanie 50-słowowe i określić jego sentyment (pozytywny/negatywny) - co wybierasz?</li>\n<li>Trenujesz model na tekście 10 000 słów i każde słowo musi &quot;widzieć&quot; każde inne - RNN, LSTM, czy coś innego?</li>\n<li>Chcesz klasyfikować punkty na płaszczyźnie na dwie grupy, ale są ułożone w kształt X (XOR) - pojedynczy perceptron wystarczy?</li>\n</ol>\n<hr />\n<p>Ten wpis jest dość długi, ale czułem, że ta ewolucja od perceptronu do LSTM zasługuje na pełną, opowiedzianą historię.</p>\n<p>Jeśli coś jest niejasne - <strong>napiszcie w komentarzach</strong>, postaram się wyjaśnić.</p>\n<p>Która architektura was najbardziej zaskoczyła? Czy wiedzieliście, że &quot;AI Winter&quot; był spowodowany czymś tak &quot;prostym&quot; jak XOR?</p>\n<blockquote>\n<p><strong>Co w następnym wpisie?</strong> Wchodzimy w <strong>Transformer</strong> - architekturę, która zmieniła wszystko. Attention, self-attention, positional encoding, multi-head attention - wszystko to, co sprawiło, że ChatGPT w ogóle istnieje. Stay tuned!</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://d2l.ai/chapter_multilayer-perceptrons/mlp.html\">Multilayer Perceptrons — d2l.ai</a> - doskonałe, wizualne wprowadzenie do MLP: warstwy, funkcje aktywacji, przejście od regresji liniowej do sieci wielowarstwowych</li>\n<li><a href=\"https://en.wikipedia.org/wiki/Multilayer_perceptron\">Multilayer perceptron — Wikipedia</a> - definicje, rysunki sieci, timeline historii od 1943 do współczesności</li>\n<li><a href=\"https://dev.to/zeromathai/multilayer-perceptron-mlp-a-practical-way-to-understand-neural-networks-3hic\">Multilayer Perceptron (MLP): A Practical Way to Understand Neural Networks — dev.to</a> - świeży, bardzo intuicyjny artykuł z metaforami i porównaniem MLP vs CNN vs Transformer</li>\n<li><a href=\"https://ai.plainenglish.io/deep-learning-specialization-perceptron-explained-building-blocks-working-limitations-c1e3b0815f08\">Perceptron Explained — PlainEnglish</a> - perceptron jako &quot;cegiełka&quot; sieci, komponenty, intuicja geometryczna, ograniczenia</li>\n<li><a href=\"https://www.geeksforgeeks.org/deep-learning/multi-layer-perceptron-learning-in-tensorflow/\">Multi-Layer Perceptron Learning in TensorFlow — GeeksforGeeks</a> - przejrzyste diagramy, forward propagation, backpropagation z implementacją</li>\n<li><a href=\"https://www.geeksforgeeks.org/deep-learning/deep-learning-introduction-to-long-short-term-memory/\">Introduction to Long Short Term Memory — GeeksforGeeks</a> - przystępne wprowadzenie do LSTM: komórka pamięci, bramki, problem long-term dependencies</li>\n<li><a href=\"https://papers.ssrn.com/sol3/JELJOUR_Results.cfm?form_name=journalBrowse&amp;journal_id=3747444\">Drawbacks of LSTM Algorithm: A Case Study — SSRN</a> - wady LSTM: złożoność obliczeniowa, overfitting, brak parallelizacji, wrażliwość na hiperparametry</li>\n<li><a href=\"https://dev.to/sreeni5018/understanding-transformer-model-types-the-evolution-from-rnn-to-modern-ai-1j4i\">Understanding Transformer Model Types: The Evolution from RNN to Modern AI — dev.to</a> - narracyjny opis przejścia od RNN do Transformerów, typy encoder/decoder</li>\n<li><a href=\"https://www.youtube.com/watch?v=gNusDo8D8gk\">LLM: Large Language Models Evolution — YouTube</a> - film opowiadający linię rozwoju od modeli sekwencyjnych do architektur opartych na attention</li>\n</ul>\n<section class=\"footnotes\" data-footnotes>\n<ol>\n<li id=\"fn-1\">\n<p>Odpowiedzi do mini-quizu: 1) Liniowa - y = ax + b to funkcja liniowa. 2) Nieliniowy - funkcja step &quot;łamie&quot; liniowość. 3) Liniowy! Bez aktywacji nieliniowej, MLP z 100 warstwami = jedna wielka transformacja liniowa. To jest właśnie powód, dla którego funkcje aktywacji są niezbędne. <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) Wystarczy prosty <strong>MLP</strong> - dane tabelaryczne, brak sekwencji. 2) <strong>LSTM</strong> - sekwencja o umiarkowanej długości, LSTM sobie poradzi. 3) <strong>Transformer</strong> - 10 000 słów to za dużo nawet dla LSTM. Transformer z self-attention pozwala każdemu słowu &quot;widzieć&quot; każde inne bez czekania. 4) <strong>Nie</strong> - pojedynczy perceptron nie rozwiąże XOR. Trzeba minimum MLP (2 warstwy). <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": "\"Od pojedynczego neuronu w 1958 roku, przez MLP i RNN z problemem zapominania, aż po LSTM z bramkami pamięci. Ewolucja architektur, która doprowadziła do Transformerów.\"",
      "date_published": "2026-06-17T00: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",
        "neural-networks",
        "rnn",
        "lstm",
        "perceptron",
        "mlp",
        "language-models",
        "deep-learning"
      ],
      "language": "pl"
    }
  ]
}