diff --git a/wyk/09_neurozoo.org b/wyk/09_neurozoo.org
index d95c66c..cc72bb5 100644
--- a/wyk/09_neurozoo.org
+++ b/wyk/09_neurozoo.org
@@ -375,13 +375,13 @@ Definicja w PyTorchu:
z_plus = torch.exp(z)
return z_plus / torch.sum(z_plus)
- softmax(torch.tensor([3., -1., 0., 5.]))
+ softmax(torch.tensor([3., 1., -1., 1.]))
#+END_SRC
#+RESULTS:
:results:
-# Out[75]:
-: tensor([0.1182, 0.0022, 0.0059, 0.8737])
+# Out[3]:
+: tensor([7.8678e-01, 1.0648e-01, 2.6393e-04, 1.0648e-01])
:end:
#+CAPTION: Softmax
diff --git a/wyk/11_rnn.ipynb b/wyk/11_rnn.ipynb
index aedf48a..89ec808 100644
--- a/wyk/11_rnn.ipynb
+++ b/wyk/11_rnn.ipynb
@@ -1 +1,522 @@
-{"cells":[{"cell_type":"markdown","metadata":{},"source":["## Rekurencyjne sieci neuronowe\n\n"]},{"cell_type":"markdown","metadata":{},"source":["### Inne spojrzenie na sieci przedstawione do tej pory\n\n"]},{"cell_type":"markdown","metadata":{},"source":["#### Regresja liniowa/logistyczna lub klasyfikacja wieloklasowa na całym tekście\n\n"]},{"cell_type":"markdown","metadata":{},"source":["W regresji liniowej czy logistycznej bądź w klasyfikacji wieloklasowej\n(z funkcją Softmax) stosowaliśmy następujący schemat:\n\nDo tej pory patrzyliśmy na to tak, że po prostu cały tekst jest od\nrazu przetwarzany przez (prostą) sieć neuronową, popatrzmy na ten\nprzypadek, jak na sytuację przetwarzania sekwencyjnego. Będzie to\ntrochę sztuczne, ale uogólnimy to potem w sensowny sposób.\n\n"]},{"cell_type":"markdown","metadata":{},"source":["##### Wektoryzacja\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Po pierwsze, zauważmy, że w wielu schematach wektoryzacji (np. tf), wektor\ndokumentów jest po prostu sumą wektorów poszczególnych składowych:\n\n$$\\vec{v}(d) = \\vec{v}(t^1,\\ldots,t^K) = \\vec{v}(t^1) + \\ldots + \\vec{v}(t^K) = \\sum_{k=1}^K \\vec{v}(t^i),$$\n\ngdzie w schemacie tf \\vec{v}(ti) to po prostu wektor *one-hot* dla słowa.\n\n**Pytanie** Jak postać przyjmie w \\vec{v}(ti) dla wektoryzacji tf-idf?\n\nWektory $\\vec{v}(t^k)$ mogą być również gęstymi wektorami\n($\\vec{v}(t^k) \\in \\mathcal{R}^n$, gdzie $n$ jest rzędu 10-1000), np.\nw modelu Word2vec albo mogą to być **wyuczalne** wektory (zanurzenia\nsłów, *embeddings*), tzn. wektory, które są parametrami uczonej sieci!\n\n**Pytanie** Ile wag (parametrów) wnoszą wyuczalne wektory do sieci?\n\n"]},{"cell_type":"markdown","metadata":{},"source":["##### Prosta wektoryzacja wyrażona w modelu sekwencyjnym\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Jak zapisać równoważnie powyższą wektoryzację w modelu **sekwencyjnym**, tj. przy założeniu, że\nprzetwarzamy wejście token po tokenie (a nie „naraz”)? Ogólnie wprowadzimy bardzo\nogólny model sieci **rekurencyjnej**.\n\nPo pierwsze zakładamy, że sieć ma pewien stan $\\vec{s^k} \\in\n\\mathcal{R}^m$ (stan jest wektorem o długości $m$), który może\nzmieniać się z każdym krokiem (przetwarzanym tokenem). Zmiana stanu\njest określona przez pewną funkcję $R : \\mathcal{R}^m \\times\n\\mathcal{R}^n \\rightarrow \\mathcal{R}^m$ ($n$ to rozmiar wektorów\n$\\vec{v}(t^k)$):\n\n$$\\vec{s^k} = R(\\vec{s^{k-1}}, \\vec{v}(t^k)).$$\n\nW przypadku wektoryzacji tf-idf mamy do czynienia z prostym\nsumowaniem, więc $R$ przyjmuje bardzo prostą postać:\n\n$$\\vec{s^0} = [0,\\dots,0],$$\n\n$$R(\\vec{s}, \\vec{x}) = \\vec{s} + \\vec{x}.$$\n\n"]},{"cell_type":"markdown","metadata":{},"source":["##### Wyjście z modelu\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Dla regresji liniowej/logistycznej, oprócz funkcji $R$, która określa\nzmianę stanu, potrzebujemy funkcji $O$, która określa wyjście systemu w każdym kroku.\n\n$$y^k = O(\\vec{s^k})$$\n\nW zadaniach klasyfikacji czy regresji, kiedy patrzymy na cały tekst w\nzasadzie wystarczy wziąć *ostatnią* wartość (tj. $y^K$). Można sobie\nwyobrazić sytuację, kiedy wartości $y^k$ dla $k < k$ również mogą być jakoś przydatne\n(np. klasyfikujemy na bieżąco tekst wpisywany przez użytkownika).\n\nW każdym razie dla regresji liniowej funkcja $O$ przyjmie postać:\n\n$$O(\\vec{s}) = \\vec{w}\\vec{s}$$,\n\ngdzie $\\vec{w}$ jest wektorem wyuczylnych wag, dla regresji zaś logistycznej:\n\n$$O(\\vec{s}) = \\operatorname{softmax}(\\vec{w}\\vec{s})$$\n\n**Pytanie**: jaką postać przyjmie $O$ dla klasyfikacji wieloklasowej\n\n"]},{"cell_type":"markdown","metadata":{},"source":["### Prosta sieć rekurencyjna\n\n"]},{"cell_type":"markdown","metadata":{},"source":["W najprostszej sieci rekurencyjnej (*Vanilla RNN*, sieć Elmana,\nczasami po prostu RNN) w każdym kroku oprócz właściwego wejścia\n($\\vec{v}(t^k)$) będziemy również podawać na wejściu poprzedni stan\nsieci ($\\vec{s^{k-1}}$).\n\nInnymi słowy, funkcje $R$ przyjmie następującą postać:\n\n$$s^k = \\sigma(W\\langle\\vec{v}(t^k), \\vec{s^{k-1}}\\rangle + \\vec{b}),$$\n\ngdzie:\n\n- $\\langle\\vec{x},\\vec{y}\\rangle$ to konkatenacja dwóch wektorów,\n- $W ∈ \\mathcal{R}m × \\mathcal{R}n+m $ — macierz wag,\n- $b \\in \\mathcal{R}^m$ — wektor obciążeń (*biases*).\n\nTaką sieć RNN można przedstawić schematycznie w następujący sposób:\n\n![img](./img-rnn.png)\n\nZauważmy, że zamiast macierzy $W$ działającej na konkatenacji wektorów można wprowadzić dwie\nmacierze $U$ i $V$ i tak zapisać wzór:\n\n$$s^k = \\sigma(U\\vec{v}(t^k) + V\\vec{s^{k-1}} + \\vec{b}).$$\n\nJeszcze inne spojrzenie na sieć RNN:\n\n![img](./rnn.png)\n\nPowyższy rysunek przedstawia pojedynczy krok sieci RNN. Dla całego\nwejścia (powiedzmy, 3-wyrazowego) możemy sieć rozwinąć (*unroll*):\n\n![img](./rnn-seq.png)\n\n"]},{"cell_type":"markdown","metadata":{},"source":["#### Zastosowanie sieci RNN do etykietowania sekwencji\n\n"]},{"cell_type":"markdown","metadata":{},"source":["#### Problemy z prostymi sieciami RNN\n\n"]},{"cell_type":"markdown","metadata":{},"source":["W praktyce proste sieci RNN są bardzo trudne w uczenia, zazwyczaj\npojawia się problem **zanikających** (rzadziej: **eksplodujących**)\ngradientów: w propagacji wstecznej błąd szybko zanika i nie jest w\nstanie dotrzeć do początkowych wejść.\n\n"]},{"cell_type":"markdown","metadata":{},"source":["### Sieci RNN z bramkami\n\n"]},{"cell_type":"markdown","metadata":{},"source":["W prostych sieciach RNN podstawowa trudność polega na tym, że mamy\nniewielką kontrolę nad tym jak pamięć (stan) jest aktualizowana. Aby\nzwiększyć tę kontrolę, potrzebujemy **bramek**.\n\n"]},{"cell_type":"markdown","metadata":{},"source":["#### Bramki\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Zazwyczaj do tej pory rozpatrywaliśmy iloczyn skalarny wektorów, w\nwyniku którego otrzymujemy liczbę (w PyTorchu wyrażany za pomocą operatora `@`), np.\n\n"]},{"cell_type":"code","execution_count":1,"metadata":{},"outputs":[{"name":"stdout","output_type":"stream","text":"# Out[2]:\ntensor(-5)"}],"source":["import torch\n\na = torch.tensor([-1, 0, 3])\nb = torch.tensor([2, 5, -1])\na @ b"]},{"cell_type":"markdown","metadata":{},"source":["Czasami przydatny jest **iloczyn Hadamarda**, czyli przemnożenie\nwektorów (albo macierzy) po współrzędnych. W PyTorchu taki iloczyn\nwyrażany jest za pomocą operatora `*`, w notacji matematycznej będziemy używali\nznaku $\\odot$.\n\n"]},{"cell_type":"code","execution_count":1,"metadata":{},"outputs":[{"name":"stdout","output_type":"stream","text":"# Out[3]:\ntensor([-2, 0, -3])"}],"source":["import torch\n\na = torch.tensor([-1, 0, 3])\nb = torch.tensor([2, 5, -1])\na * b"]},{"cell_type":"markdown","metadata":{},"source":["Zauważmy, że iloczyn Hadamarda przez wektor złożony z zer i jedynek daje nam *filtr*, możemy\nselektywnie wygaszać pozycje wektora, np. tutaj wyzerowaliśmy 2. i 5. pozycję wektora:\n\n"]},{"cell_type":"code","execution_count":1,"metadata":{},"outputs":[{"name":"stdout","output_type":"stream","text":"# Out[4]:\ntensor([1., 0., 3., 4., 0.])"}],"source":["import torch\n\na = torch.tensor([1., 2., 3., 4., 5.])\nb = torch.tensor([1., 0., 1., 1., 0.])\na * b"]},{"cell_type":"markdown","metadata":{},"source":["Co więcej, za pomocą bramki możemy selektywnie kontrolować, co\nzapamiętujemy, a co zapominamy. Rozpatrzmy mianowicie wektor zer i\njedynek $\\vec{g} \\in \\{0,1}^m$, dla stanu (pamięci) $\\vec{s}$ i nowej informacji\n$\\vec{x}$ możemy dokonywać aktualizacji w następujący sposób:\n\n$$\\vec{s} \\leftarrow \\vec{g} \\odot \\vec{x} + (1 - \\vec{g}) \\odot \\vec{s}$$\n\nNa przykład, za pomocą bramki można wpisać nową wartość na 2. i 5. pozycję wektora.\n\n"]},{"cell_type":"code","execution_count":1,"metadata":{},"outputs":[{"name":"stdout","output_type":"stream","text":"# Out[8]:\ntensor([ 1., 7., 3., 4., -8.])"}],"source":["import torch\n\ns = torch.tensor([1., 2., 3., 4., 5.])\nx = torch.tensor([8., 7., 15., -3., -8.])\n\ng = torch.tensor([0., 1., 0., 0., 1.])\n\ns = g * x + (1 - g) * s\ns"]},{"cell_type":"markdown","metadata":{},"source":["Wektor bramki nie musi być z góry określony, może być wyuczalny. Wtedy\njednak lepiej założyć, że bramka jest „miękka”, np. jej wartości\npochodzi z sigmoidy zastosowanej do jakiejś wcześniejszej warstwy.\n\n"]},{"cell_type":"code","execution_count":1,"metadata":{},"outputs":[{"name":"stdout","output_type":"stream","text":"# Out[14]:\ntensor([ 1.5310, 6.9998, 5.7777, 4.0000, -5.2159])"}],"source":["import torch\n\ns = torch.tensor([1., 2., 3., 4., 5.])\nx = torch.tensor([8., 7., 15., -3., -8.])\n\npre_g = torch.tensor([-2.5, 10.0, -1.2, -101., 1.3])\ng = torch.sigmoid(pre_g)\n\ns = g * x + (1 - g) * s\ns"]},{"cell_type":"markdown","metadata":{},"source":["**Pytanie:** dlaczego sigmoida zamiast tanh?\n\n"]},{"cell_type":"markdown","metadata":{},"source":["#### Sieć LSTM\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Architektura LSTM (*Long Short-Term Memory*) pozwala rozwiązać problem\nznikających gradientów — za cenę komplikacji obliczeń.\n\nW sieci LSTM stan $\\vec{s^k}$ ma dwie połówki, tj. $\\vec{s^k} =\n\\langle\\vec{c^k},\\vec{h^k}\\rangle$, gdzie\n\n- $\\vec{c^k}$ to **komórka pamięci**, która nie zmienia swojej, chyba że celowo zmodyfikujemy jej wartość\n za pomocą bramek,\n- $\\vec{h^k}$ to ukryty stan (przypominający $\\vec{s^k}$ ze zwykłej sieci RNN).\n\nSieć LSTM zawiera 3 bramki:\n\n- bramkę zapominania (*forget gate*), która steruje wymazywaniem informacji z komórki\n pamięci $\\vec{c^k}$,\n- bramkę wejścia (*input gate*), która steruje tym, na ile nowe informacje aktualizują\n komórkę pamięci $\\vec{c^k}$,\n- bramkę wyjścia (*output gate*), która steruje tym, co z komórki\n pamięci przekazywane jest na wyjście.\n\nWszystkie trzy bramki definiowane są za pomocą bardzo podobnego wzoru — warstwy liniowej na\npoprzedniej wartości warstwy ukrytej i bieżącego wejścia.\n\n$$\\vec{i} = \\sigma(W_i\\langle\\vec{v}(t^k),\\vec{h^{k-1}}\\rangle)$$\n\n$$\\vec{f} = \\sigma(W_f\\langle\\vec{v}(t^k),\\vec{h^{k-1}}\\rangle)$$\n\n$$\\vec{o} = \\sigma(W_f\\langle\\vec{v}(t^k),\\vec{h^{k-1}}\\rangle)$$\n\nJak widać, wzory różnią się tylko macierzami wag $W_*$.\n\nZmiana komórki pamięci jest zdefiniowana jak następuje:\n\n$$\\vec{c^k} = \\vec{f} \\odot \\vec{c^{k-1}} + \\vec{i} \\vec{z^k}$$,\n\ngdzie\n\n$$\\vec{z^k} = \\operatorname{tanh}(W_z\\langle\\vec{v}(t^k),\\vec{h^{k-1}}\\rangle)$$\n\nStan ukryty zmienia się w następujący sposób:\n\n$$\\vec{h^K} = \\vec{o} \\odot \\operatorname{tanh}(\\vec{c^k})$$.\n\nOstateczne wyjście może być wyliczane na podstawie wektora $\\vec{h^k}$:\n\n$$O(\\vec{s}) = O(\\langle\\vec{c},\\vec{h}\\rangle) = \\vec{h}$$\n\n**Pytanie**: Ile wag/parametrów ma sieć RNN o rozmiarze wejścia $n$ i\n\n"]},{"cell_type":"markdown","metadata":{},"source":["### Literatura\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Yoav Goldberg, *Neural Network Methods for Natural Language\nProcessing*, Morgan & Claypool Publishers, 2017\n\n"]}],"metadata":{"org":null,"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.5.2"}},"nbformat":4,"nbformat_minor":0}
\ No newline at end of file
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Rekurencyjne sieci neuronowe\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Inne spojrzenie na sieci przedstawione do tej pory\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Regresja liniowa/logistyczna lub klasyfikacja wieloklasowa na całym tekście\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "W regresji liniowej czy logistycznej bądź w klasyfikacji wieloklasowej\n",
+ "(z funkcją Softmax) stosowaliśmy następujący schemat:\n",
+ "\n",
+ "Do tej pory patrzyliśmy na to tak, że po prostu cały tekst jest od\n",
+ "razu przetwarzany przez (prostą) sieć neuronową, popatrzmy na ten\n",
+ "przypadek, jak na sytuację przetwarzania sekwencyjnego. Będzie to\n",
+ "trochę sztuczne, ale uogólnimy to potem w sensowny sposób.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "##### Wektoryzacja\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Po pierwsze, zauważmy, że w wielu schematach wektoryzacji (np. tf), wektor\n",
+ "dokumentów jest po prostu sumą wektorów poszczególnych składowych:\n",
+ "\n",
+ "$$\\vec{v}(d) = \\vec{v}(t^1,\\ldots,t^K) = \\vec{v}(t^1) + \\ldots + \\vec{v}(t^K) = \\sum_{k=1}^K \\vec{v}(t^i),$$\n",
+ "\n",
+ "gdzie w schemacie tf \\vec{v}(ti) to po prostu wektor *one-hot* dla słowa.\n",
+ "\n",
+ "**Pytanie** Jak postać przyjmie w \\vec{v}(ti) dla wektoryzacji tf-idf?\n",
+ "\n",
+ "Wektory $\\vec{v}(t^k)$ mogą być również gęstymi wektorami\n",
+ "($\\vec{v}(t^k) \\in \\mathcal{R}^n$, gdzie $n$ jest rzędu 10-1000), np.\n",
+ "w modelu Word2vec albo mogą to być **wyuczalne** wektory (zanurzenia\n",
+ "słów, *embeddings*), tzn. wektory, które są parametrami uczonej sieci!\n",
+ "\n",
+ "**Pytanie** Ile wag (parametrów) wnoszą wyuczalne wektory do sieci?\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "##### Prosta wektoryzacja wyrażona w modelu sekwencyjnym\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Jak zapisać równoważnie powyższą wektoryzację w modelu **sekwencyjnym**, tj. przy założeniu, że\n",
+ "przetwarzamy wejście token po tokenie (a nie „naraz”)? Ogólnie wprowadzimy bardzo\n",
+ "ogólny model sieci **rekurencyjnej**.\n",
+ "\n",
+ "Po pierwsze zakładamy, że sieć ma pewien stan $\\vec{s^k} \\in\n",
+ "\\mathcal{R}^m$ (stan jest wektorem o długości $m$), który może\n",
+ "zmieniać się z każdym krokiem (przetwarzanym tokenem). Zmiana stanu\n",
+ "jest określona przez pewną funkcję $R : \\mathcal{R}^m \\times\n",
+ "\\mathcal{R}^n \\rightarrow \\mathcal{R}^m$ ($n$ to rozmiar wektorów\n",
+ "$\\vec{v}(t^k)$):\n",
+ "\n",
+ "$$\\vec{s^k} = R(\\vec{s^{k-1}}, \\vec{v}(t^k)).$$\n",
+ "\n",
+ "W przypadku wektoryzacji tf-idf mamy do czynienia z prostym\n",
+ "sumowaniem, więc $R$ przyjmuje bardzo prostą postać:\n",
+ "\n",
+ "$$\\vec{s^0} = [0,\\dots,0],$$\n",
+ "\n",
+ "$$R(\\vec{s}, \\vec{x}) = \\vec{s} + \\vec{x}.$$\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "##### Wyjście z modelu\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Dla regresji liniowej/logistycznej, oprócz funkcji $R$, która określa\n",
+ "zmianę stanu, potrzebujemy funkcji $O$, która określa wyjście systemu w każdym kroku.\n",
+ "\n",
+ "$$y^k = O(\\vec{s^k})$$\n",
+ "\n",
+ "W zadaniach klasyfikacji czy regresji, kiedy patrzymy na cały tekst w\n",
+ "zasadzie wystarczy wziąć *ostatnią* wartość (tj. $y^K$). Można sobie\n",
+ "wyobrazić sytuację, kiedy wartości $y^k$ dla $k < k$ również mogą być jakoś przydatne\n",
+ "(np. klasyfikujemy na bieżąco tekst wpisywany przez użytkownika).\n",
+ "\n",
+ "W każdym razie dla regresji liniowej funkcja $O$ przyjmie postać:\n",
+ "\n",
+ "$$O(\\vec{s}) = \\vec{w}\\vec{s}$$,\n",
+ "\n",
+ "gdzie $\\vec{w}$ jest wektorem wyuczylnych wag, dla regresji zaś logistycznej:\n",
+ "\n",
+ "$$O(\\vec{s}) = \\operatorname{softmax}(\\vec{w}\\vec{s})$$\n",
+ "\n",
+ "**Pytanie**: jaką postać przyjmie $O$ dla klasyfikacji wieloklasowej\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Prosta sieć rekurencyjna\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "W najprostszej sieci rekurencyjnej (*Vanilla RNN*, sieć Elmana,\n",
+ "czasami po prostu RNN) w każdym kroku oprócz właściwego wejścia\n",
+ "($\\vec{v}(t^k)$) będziemy również podawać na wejściu poprzedni stan\n",
+ "sieci ($\\vec{s^{k-1}}$).\n",
+ "\n",
+ "Innymi słowy, funkcje $R$ przyjmie następującą postać:\n",
+ "\n",
+ "$$s^k = \\sigma(W\\langle\\vec{v}(t^k), \\vec{s^{k-1}}\\rangle + \\vec{b}),$$\n",
+ "\n",
+ "gdzie:\n",
+ "\n",
+ "- $\\langle\\vec{x},\\vec{y}\\rangle$ to konkatenacja dwóch wektorów,\n",
+ "- $W \\in \\mathcal{R}^m \\times \\mathcal{R}^{n+m}$ — macierz wag,\n",
+ "- $b \\in \\mathcal{R}^m$ — wektor obciążeń (*biases*).\n",
+ "\n",
+ "Taką sieć RNN można przedstawić schematycznie w następujący sposób:\n",
+ "\n",
+ "![img](./img-rnn.png)\n",
+ "\n",
+ "Zauważmy, że zamiast macierzy $W$ działającej na konkatenacji wektorów można wprowadzić dwie\n",
+ "macierze $U$ i $V$ i tak zapisać wzór:\n",
+ "\n",
+ "$$s^k = \\sigma(U\\vec{v}(t^k) + V\\vec{s^{k-1}} + \\vec{b}).$$\n",
+ "\n",
+ "Jeszcze inne spojrzenie na sieć RNN:\n",
+ "\n",
+ "![img](./rnn.png)\n",
+ "\n",
+ "Powyższy rysunek przedstawia pojedynczy krok sieci RNN. Dla całego\n",
+ "wejścia (powiedzmy, 3-wyrazowego) możemy sieć rozwinąć (*unroll*):\n",
+ "\n",
+ "![img](./rnn-seq.png)\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Zastosowanie sieci RNN do etykietowania sekwencji\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Problemy z prostymi sieciami RNN\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "W praktyce proste sieci RNN są bardzo trudne w uczenia, zazwyczaj\n",
+ "pojawia się problem **zanikających** (rzadziej: **eksplodujących**)\n",
+ "gradientów: w propagacji wstecznej błąd szybko zanika i nie jest w\n",
+ "stanie dotrzeć do początkowych wejść.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Sieci RNN z bramkami\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "W prostych sieciach RNN podstawowa trudność polega na tym, że mamy\n",
+ "niewielką kontrolę nad tym jak pamięć (stan) jest aktualizowana. Aby\n",
+ "zwiększyć tę kontrolę, potrzebujemy **bramek**.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Bramki\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Zazwyczaj do tej pory rozpatrywaliśmy iloczyn skalarny wektorów, w\n",
+ "wyniku którego otrzymujemy liczbę (w PyTorchu wyrażany za pomocą operatora `@`), np.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "# Out[2]:\n",
+ "tensor(-5)"
+ ]
+ }
+ ],
+ "source": [
+ "import torch\n",
+ "\n",
+ "a = torch.tensor([-1, 0, 3])\n",
+ "b = torch.tensor([2, 5, -1])\n",
+ "a @ b"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Czasami przydatny jest **iloczyn Hadamarda**, czyli przemnożenie\n",
+ "wektorów (albo macierzy) po współrzędnych. W PyTorchu taki iloczyn\n",
+ "wyrażany jest za pomocą operatora `*`, w notacji matematycznej będziemy używali\n",
+ "znaku $\\odot$.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "# Out[3]:\n",
+ "tensor([-2, 0, -3])"
+ ]
+ }
+ ],
+ "source": [
+ "import torch\n",
+ "\n",
+ "a = torch.tensor([-1, 0, 3])\n",
+ "b = torch.tensor([2, 5, -1])\n",
+ "a * b"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Zauważmy, że iloczyn Hadamarda przez wektor złożony z zer i jedynek daje nam *filtr*, możemy\n",
+ "selektywnie wygaszać pozycje wektora, np. tutaj wyzerowaliśmy 2. i 5. pozycję wektora:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "# Out[4]:\n",
+ "tensor([1., 0., 3., 4., 0.])"
+ ]
+ }
+ ],
+ "source": [
+ "import torch\n",
+ "\n",
+ "a = torch.tensor([1., 2., 3., 4., 5.])\n",
+ "b = torch.tensor([1., 0., 1., 1., 0.])\n",
+ "a * b"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Co więcej, za pomocą bramki możemy selektywnie kontrolować, co\n",
+ "zapamiętujemy, a co zapominamy. Rozpatrzmy mianowicie wektor zer i\n",
+ "jedynek $\\vec{g} \\in \\{0,1\\}^m$, dla stanu (pamięci) $\\vec{s}$ i nowej informacji\n",
+ "$\\vec{x}$ możemy dokonywać aktualizacji w następujący sposób:\n",
+ "\n",
+ "$$\\vec{s} \\leftarrow \\vec{g} \\odot \\vec{x} + (1 - \\vec{g}) \\odot \\vec{s}$$\n",
+ "\n",
+ "Na przykład, za pomocą bramki można wpisać nową wartość na 2. i 5. pozycję wektora.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "# Out[8]:\n",
+ "tensor([ 1., 7., 3., 4., -8.])"
+ ]
+ }
+ ],
+ "source": [
+ "import torch\n",
+ "\n",
+ "s = torch.tensor([1., 2., 3., 4., 5.])\n",
+ "x = torch.tensor([8., 7., 15., -3., -8.])\n",
+ "\n",
+ "g = torch.tensor([0., 1., 0., 0., 1.])\n",
+ "\n",
+ "s = g * x + (1 - g) * s\n",
+ "s"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Wektor bramki nie musi być z góry określony, może być wyuczalny. Wtedy\n",
+ "jednak lepiej założyć, że bramka jest „miękka”, np. jej wartości\n",
+ "pochodzi z sigmoidy zastosowanej do jakiejś wcześniejszej warstwy.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "# Out[14]:\n",
+ "tensor([ 1.5310, 6.9998, 5.7777, 4.0000, -5.2159])"
+ ]
+ }
+ ],
+ "source": [
+ "import torch\n",
+ "\n",
+ "s = torch.tensor([1., 2., 3., 4., 5.])\n",
+ "x = torch.tensor([8., 7., 15., -3., -8.])\n",
+ "\n",
+ "pre_g = torch.tensor([-2.5, 10.0, -1.2, -101., 1.3])\n",
+ "g = torch.sigmoid(pre_g)\n",
+ "\n",
+ "s = g * x + (1 - g) * s\n",
+ "s"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Pytanie:** dlaczego sigmoida zamiast tanh?\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Sieć LSTM\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Architektura LSTM (*Long Short-Term Memory*) pozwala rozwiązać problem\n",
+ "znikających gradientów — za cenę komplikacji obliczeń.\n",
+ "\n",
+ "W sieci LSTM stan $\\vec{s^k}$ ma dwie połówki, tj. $\\vec{s^k} =\n",
+ "\\langle\\vec{c^k},\\vec{h^k}\\rangle$, gdzie\n",
+ "\n",
+ "- $\\vec{c^k}$ to **komórka pamięci**, która nie zmienia swojej, chyba że celowo zmodyfikujemy jej wartość\n",
+ " za pomocą bramek,\n",
+ "- $\\vec{h^k}$ to ukryty stan (przypominający $\\vec{s^k}$ ze zwykłej sieci RNN).\n",
+ "\n",
+ "Sieć LSTM zawiera 3 bramki:\n",
+ "\n",
+ "- bramkę zapominania (*forget gate*), która steruje wymazywaniem informacji z komórki\n",
+ " pamięci $\\vec{c^k}$,\n",
+ "- bramkę wejścia (*input gate*), która steruje tym, na ile nowe informacje aktualizują\n",
+ " komórkę pamięci $\\vec{c^k}$,\n",
+ "- bramkę wyjścia (*output gate*), która steruje tym, co z komórki\n",
+ " pamięci przekazywane jest na wyjście.\n",
+ "\n",
+ "Wszystkie trzy bramki definiowane są za pomocą bardzo podobnego wzoru — warstwy liniowej na\n",
+ "poprzedniej wartości warstwy ukrytej i bieżącego wejścia.\n",
+ "\n",
+ "$$\\vec{i} = \\sigma(W_i\\langle\\vec{v}(t^k),\\vec{h^{k-1}}\\rangle)$$\n",
+ "\n",
+ "$$\\vec{f} = \\sigma(W_f\\langle\\vec{v}(t^k),\\vec{h^{k-1}}\\rangle)$$\n",
+ "\n",
+ "$$\\vec{o} = \\sigma(W_o\\langle\\vec{v}(t^k),\\vec{h^{k-1}}\\rangle)$$\n",
+ "\n",
+ "Jak widać, wzory różnią się tylko macierzami wag $W_*$.\n",
+ "\n",
+ "Zmiana komórki pamięci jest zdefiniowana jak następuje:\n",
+ "\n",
+ "$$\\vec{c^k} = \\vec{f} \\odot \\vec{c^{k-1}} + \\vec{i} \\vec{z^k}$$,\n",
+ "\n",
+ "gdzie\n",
+ "\n",
+ "$$\\vec{z^k} = \\operatorname{tanh}(W_z\\langle\\vec{v}(t^k),\\vec{h^{k-1}}\\rangle)$$\n",
+ "\n",
+ "Stan ukryty zmienia się w następujący sposób:\n",
+ "\n",
+ "$$\\vec{h^K} = \\vec{o} \\odot \\operatorname{tanh}(\\vec{c^k})$$.\n",
+ "\n",
+ "Ostateczne wyjście może być wyliczane na podstawie wektora $\\vec{h^k}$:\n",
+ "\n",
+ "$$O(\\vec{s}) = O(\\langle\\vec{c},\\vec{h}\\rangle) = \\vec{h}$$\n",
+ "\n",
+ "**Pytanie**: Ile wag/parametrów ma sieć RNN o rozmiarze wejścia $n$ i rozmiarze warstwy ukrytej $m$?\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Literatura\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Yoav Goldberg, *Neural Network Methods for Natural Language Processing*,\n",
+ "Morgan & Claypool Publishers, 2017\n",
+ "\n"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.9.2"
+ },
+ "org": null
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/wyk/11_rnn.org b/wyk/11_rnn.org
index 1efba6a..606a6eb 100644
--- a/wyk/11_rnn.org
+++ b/wyk/11_rnn.org
@@ -87,7 +87,7 @@ $$s^k = \sigma(W\langle\vec{v}(t^k), \vec{s^{k-1}}\rangle + \vec{b}),$$
gdzie:
- $\langle\vec{x},\vec{y}\rangle$ to konkatenacja dwóch wektorów,
-- $W \in \mathcal{R}^m \times \mathcal{R}^{n+m} $ — macierz wag,
+- $W \in \mathcal{R}^m \times \mathcal{R}^{n+m}$ — macierz wag,
- $b \in \mathcal{R}^m$ — wektor obciążeń (/biases/).
Taką sieć RNN można przedstawić schematycznie w następujący sposób:
@@ -181,7 +181,7 @@ selektywnie wygaszać pozycje wektora, np. tutaj wyzerowaliśmy 2. i 5. pozycję
Co więcej, za pomocą bramki możemy selektywnie kontrolować, co
zapamiętujemy, a co zapominamy. Rozpatrzmy mianowicie wektor zer i
-jedynek $\vec{g} \in \{0,1}^m$, dla stanu (pamięci) $\vec{s}$ i nowej informacji
+jedynek $\vec{g} \in \{0,1\}^m$, dla stanu (pamięci) $\vec{s}$ i nowej informacji
$\vec{x}$ możemy dokonywać aktualizacji w następujący sposób:
$$\vec{s} \leftarrow \vec{g} \odot \vec{x} + (1 - \vec{g}) \odot \vec{s}$$
@@ -259,7 +259,7 @@ $$\vec{i} = \sigma(W_i\langle\vec{v}(t^k),\vec{h^{k-1}}\rangle)$$
$$\vec{f} = \sigma(W_f\langle\vec{v}(t^k),\vec{h^{k-1}}\rangle)$$
-$$\vec{o} = \sigma(W_f\langle\vec{v}(t^k),\vec{h^{k-1}}\rangle)$$
+$$\vec{o} = \sigma(W_o\langle\vec{v}(t^k),\vec{h^{k-1}}\rangle)$$
Jak widać, wzory różnią się tylko macierzami wag $W_*$.
@@ -279,10 +279,10 @@ Ostateczne wyjście może być wyliczane na podstawie wektora $\vec{h^k}$:
$$O(\vec{s}) = O(\langle\vec{c},\vec{h}\rangle) = \vec{h}$$
-*Pytanie*: Ile wag/parametrów ma sieć RNN o rozmiarze wejścia $n$ i
+*Pytanie*: Ile wag/parametrów ma sieć RNN o rozmiarze wejścia $n$ i rozmiarze warstwy ukrytej $m$?
** Literatura
-Yoav Goldberg, /Neural Network Methods for Natural Language
-Processing/, Morgan & Claypool Publishers, 2017
+Yoav Goldberg, /Neural Network Methods for Natural Language Processing/,
+Morgan & Claypool Publishers, 2017
diff --git a/wyk/12_bpe.ipynb b/wyk/12_bpe.ipynb
new file mode 100644
index 0000000..0220f04
--- /dev/null
+++ b/wyk/12_bpe.ipynb
@@ -0,0 +1,839 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Podział na jednostki podwyrazowe\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Słownik nie może być za duży…\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Jeśli używamy wyuczalnych zanurzeń słów (embeddingów), wówczas musimy\n",
+ "je dopisać do listy parametrów całego modelu — jest to $|V|n$ wag,\n",
+ "gdzie $n$ to rozmiar embeddingów; w wypadku uczenia dodatkowo musimy\n",
+ "jeszcze pamiętać związane z embeddingami gradienty. Pamięć RAM karty\n",
+ "graficznej jest rzecz jasna ograniczona, słownik więc nie może być\n",
+ "dowolnie duży. Dla danego modelu karty graficznej dość łatwo ustalić\n",
+ "maksymalny rozmiar słownika — jest „twarde” ograniczenie, które musimy\n",
+ "spełnić.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Czy rzeczywiście słownik może być taki duży?\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Ile jest różnych form fleksyjnych w języku polskim? Zobaczmy w słowniku PoliMorf…\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "a\n",
+ "aa\n",
+ "AA\n",
+ "Aachen\n",
+ "Aalborg\n",
+ "Aalborgiem\n",
+ "Aalborgowi\n",
+ "Aalborgu\n",
+ "AAP\n",
+ "Aar\n",
+ "Aarem\n",
+ "Aarowi\n",
+ "Aaru\n",
+ "Aarze\n",
+ "Aara\n",
+ "Aarą\n",
+ "Aarę\n",
+ "Aaro\n",
+ "Aary\n",
+ "Aarze\n",
+ "uniq: błąd zapisu: Przerwany potok\n"
+ ]
+ }
+ ],
+ "source": [
+ "! wget -q 'http://zil.ipipan.waw.pl/PoliMorf?action=AttachFile&do=get&target=PoliMorf-0.6.7.tab.gz' -O - | zcat | cut -f 1 | uniq | head -n 20"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "3844535\n"
+ ]
+ }
+ ],
+ "source": [
+ "! wget -q 'http://zil.ipipan.waw.pl/PoliMorf?action=AttachFile&do=get&target=PoliMorf-0.6.7.tab.gz' -O - | zcat | cut -f 1 | sort -u | wc -l"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Pytanie** W którym języku europejskim wyrazów będzie jeszcze więcej niż języku polskim?\n",
+ "\n",
+ "Tak naprawdę form jest jeszcze więcej, oczywiście PoliMorf nie wyczerpuje zbioru…\n",
+ "\n",
+ "**Pytanie** Podaj przykłady „oczywistych” wyrazów, których nie ma w PoliMorfie. Jak w sposób systematyczny szukać takich wyrazów?\n",
+ "\n",
+ "Z drugiej strony, w PoliMorfie jest dużo dziwnych, „sztucznych” wyrazów.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "niebiałościenną\n",
+ "nieponadosobowości\n",
+ "nieknerający\n",
+ "inspektoratów\n",
+ "Korytkowskich\n",
+ "elektrostatyczności\n",
+ "Okola\n",
+ "bezsłowny\n",
+ "indygowcu\n",
+ "gadany\n",
+ "nieładowarkowościach\n",
+ "niepawężnicowate\n",
+ "Thom\n",
+ "poradlmy\n",
+ "olejący\n",
+ "Ziemianinów\n",
+ "stenotropizmami\n",
+ "wigiliowości\n",
+ "pognanej\n",
+ "niekinezyterapeutycznym\n"
+ ]
+ }
+ ],
+ "source": [
+ "! wget -q 'http://zil.ipipan.waw.pl/PoliMorf?action=AttachFile&do=get&target=PoliMorf-0.6.7.tab.gz' -O - | zcat | cut -f 1 | shuf -n 20"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Inaczej, zobaczmy, ile różnych wyrazów jest w jakimś rzeczywistym zbiorze tekstów, rozpatrzmy\n",
+ "teksty zebrane na potrzeby identyfikacji płci autora tekstu:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "# Out[7]:"
+ ]
+ }
+ ],
+ "source": [
+ "! git clone --single-branch --depth 1 git://gonito.net/petite-difference-challenge2"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "! xzcat petite-difference-challenge2/train/in.tsv.xz | perl -C -ne 'print \"$&\\n\" while/\\p{L}+/g;' | sort -u > vocab.txt"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "ˆ\n",
+ "ˇ\n",
+ "゚\n",
+ "a\n",
+ "A\n",
+ "á\n",
+ "Á\n",
+ "à\n",
+ "À\n",
+ "ă\n",
+ "Ă\n",
+ "â\n",
+ "Â\n",
+ "å\n",
+ "Å\n",
+ "ä\n",
+ "Ä\n",
+ "Ã\n",
+ "ā\n",
+ "aa\n",
+ "aA\n",
+ "Aa\n",
+ "AA\n",
+ "aĂ\n",
+ "AĂ\n",
+ "aâ\n",
+ "aÂ\n",
+ "Aâ\n",
+ "aÅ\n",
+ "aÄ\n",
+ "ª\n",
+ "aaa\n",
+ "aAa\n",
+ "Aaa\n",
+ "AaA\n",
+ "AAa\n",
+ "AAA\n",
+ "aaaa\n",
+ "aAaa\n",
+ "Aaaa\n",
+ "AaAa\n",
+ "AAaa\n",
+ "AAAa\n",
+ "AAAA\n",
+ "aaaaa\n",
+ "Aaaaa\n",
+ "AaaaA\n",
+ "AAaaa\n",
+ "AAAAA\n",
+ "aaaaaa\n"
+ ]
+ }
+ ],
+ "source": [
+ "! head -n 50 vocab.txt"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2974556 vocab.txt\n"
+ ]
+ }
+ ],
+ "source": [
+ "! wc -l vocab.txt"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Co gorsza, nawet jak weźmiemy cały taki słownik bez ograniczeń i tak\n",
+ "nie pokryje on sporej części tekstów przetwarzanych w czasie inferencji.\n",
+ "Zobaczmy, ilu wyrazów ze zbioru deweloperskiego nie będzie w słowniku.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "81380\n"
+ ]
+ }
+ ],
+ "source": [
+ "! cat petite-difference-challenge2/dev-0/in.tsv | perl -C -ne 'print \"$&\\n\" while/\\p{L}+/g;' | sort -u | comm vocab.txt - -13 | wc -l"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Takie wyrazy nazywamy wyrazami **OOV** (*out-of-vocabulary*).\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Obcięcie słownika\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Najprostszy sposób ograniczenia słownika to po prostu obcięcie do $N$ najczęstszych słów.\n",
+ "\n",
+ "Spróbujmy zastosować do korpusu „płci”:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "sort: błąd zapisu: 'standardowe wyjście': Przerwany potok\n",
+ "sort: błąd zapisu\n"
+ ]
+ }
+ ],
+ "source": [
+ "! xzcat petite-difference-challenge2/train/in.tsv.xz | perl -C -ne 'print \"$&\\n\" while/\\p{L}+/g;' | sort | uniq -c | sort -k 1rn | head -n 50000 | sort -k 2 > vocab50000.txt"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Daje to lepszy efekt niż można się spodziewać. Odrzucamy w ten sposób\n",
+ "tylko bardzo rzadkie słowa (albo takie, które wystąpiły tylko raz w\n",
+ "korpusie — tzw. *hapax legomena*), choć tych słów jest bardzo dużo.\n",
+ "\n",
+ "**Zagadka**: 50000 najczęstszych słów (1,9% **typów**) pokrywa jaki odsetek **wystąpień**?\n",
+ "\n",
+ "Rozkład normalny w języku nie jest… normalny — nie spotkamy się z nim\n",
+ "badając języki. W tekstach dominują „skrzywione” rozkłady z długimi,\n",
+ "„chudymi” ogonami.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "! xzcat petite-difference-challenge2/train/in.tsv.xz | perl -C -ne 'print \"$&\\n\" while/\\p{L}+/g;' | sort | uniq -c | sort -k 1rn | cut -f 1 > freqs.txt"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "'word-distribution.png'"
+ ]
+ },
+ "execution_count": 9,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ "