From eedb3f77418001bd28dc282545f11e897bac0d07 Mon Sep 17 00:00:00 2001 From: Filip Gralinski Date: Wed, 6 Jul 2022 08:14:22 +0200 Subject: [PATCH] Fix 11 --- wyk/08_Neuronowy_ngramowy_model.org | 219 --------- wyk/11_Worek_slow.ipynb | 414 ++++++++++++++++++ wyk/11_Worek_slow.org | 218 +++++++++ .../bow1.drawio | 0 .../bow1.drawio.png | Bin .../bow2.drawio | 0 .../bow2.drawio.png | Bin 7 files changed, 632 insertions(+), 219 deletions(-) create mode 100644 wyk/11_Worek_slow.ipynb create mode 100644 wyk/11_Worek_slow.org rename wyk/{08_Neuronowy_ngramowy_model => 11_Worek_slow}/bow1.drawio (100%) rename wyk/{08_Neuronowy_ngramowy_model => 11_Worek_slow}/bow1.drawio.png (100%) rename wyk/{08_Neuronowy_ngramowy_model => 11_Worek_slow}/bow2.drawio (100%) rename wyk/{08_Neuronowy_ngramowy_model => 11_Worek_slow}/bow2.drawio.png (100%) diff --git a/wyk/08_Neuronowy_ngramowy_model.org b/wyk/08_Neuronowy_ngramowy_model.org index ec88517..ff87376 100644 --- a/wyk/08_Neuronowy_ngramowy_model.org +++ b/wyk/08_Neuronowy_ngramowy_model.org @@ -185,222 +185,3 @@ Zauważmy, że względem $n$ oznacza to bardzo korzystną złożoność $O(n)$! Oznacza to, że nasz model może działać dla dużo większych wartości $n$ niż tradycyjny, statystyczny n-gramowy model języka (dla którego wartości $n > 5$ zazwyczaj nie mają sensu). - -** Model worka słów - -Jak stwierdziliśmy przed chwilą, dwuwarstwowy n-gramowy model języka -może działać dla stosunkowo dużego $n$. Zauważmy jednak, że istnieje -pewna słabość tego modelu. Otóż o ile intuicyjnie ma sens odróżniać -słowo poprzedzające, słowo występujące dwie pozycje wstecz i zapewne -trzy pozycje wstecz, a zatem uczyć się osobnych macierzy $C_{-1}$, -$C_{-2}$, $C_{-3}$ to różnica między wpływem słowa -występującego cztery pozycje wstecz i pięć pozycji wstecz jest już -raczej nieistotna; innymi słowy różnica między macierzami $C_{-4}$ i -$C_{-5}$ będzie raczej niewielka i sieć niepotrzebnie będzie uczyła -się dwukrotnie podobnych wag. Im dalej wstecz, tym różnica wpływu -będzie jeszcze mniej istotna, można np. przypuszczać, że różnica -między $C_{-10}$ i $C_{-13}$ nie powinna być duża. - -Spróbujmy najpierw zaproponować radykalne podejście, w którym nie -będziemy w ogóle uwzględniać pozycji słów (lub będziemy je uwzględniać -w niewielkim stopniu), później połączymy to z omówionym wcześniej -modelem $n$-gramowym. - -*** Agregacja wektorów - -Zamiast patrzeć na kilka poprzedzających słów, można przewidywać na -podstawie *całego* ciągu słów poprzedzających odgadywane słowo. Zauważmy jednak, że -sieć neuronowa musi mieć ustaloną strukturę, nie możemy zmieniać jej -rozmiaru. Musimy zatem najpierw zagregować cały ciąg do wektora o -*stałej* długości. Potrzebujemy zatem pewnej funkcji agregującej $A$, takiej by -$A(w_1,\dots,w_{i-1})$ było wektorem o stałej długości, niezależnie od $i$. - -*** Worek słów - -Najprostszą funkcją agregującą jest po prostu… suma. Dodajemy po -prostu zanurzenia słów: - -$$A(w_1,\dots,w_{i-1}) = E(w_1) + \dots + E(w_{i-1}) = \sum_{j=1}^{i-1} E(w_j).$$ - -*Uwaga*: zanurzenia słów nie zależą od pozycji słowa (podobnie było w wypadku n-gramowego modelu!). - -Jeśli rozmiar zanurzenia (embeddingu) wynosi $m$, wówczas rozmiar -wektora uzyskanego dla całego poprzedzającego tekstu wynosi również $m$. - -Proste dodawanie wydaje się bardzo „prostacką” metodą, a jednak -suma wektorów słów jest *zaskakująco skuteczną metodą zanurzenia -(embedowania) całych tekstów (doc2vec)*. Prostym wariantem dodawania jest obliczanie *średniej wektorów*: - -$$A(w_1,\dots,w_{i-1}) = \frac{E(w_1) + \dots + E(w_{i-1})}{i-1} = \frac{\sum_{j=1}^{i-1} E(w_j)}{i-1}.$$ - -Tak czy siak uzyskany wektor *nie zależy od kolejności słów* -(dodawanie jest przemienne i łączne!). Mówimy więc o *worku słów* -(/bag of words/, /BoW/) — co ma symbolizować fakt, że słowa są -przemieszane, niczym produkty w torbie na zakupy. - -**** Schemat graficzny modelu typu worek słów - -Po zanurzeniu całego poprzedzającego tekstu postępujemy podobnie jak w -modelu bigramowym — rzutujemy embedding na długi wektor wartości, na -którym stosujemy funkcję softmax: - -#+CAPTION: Model typu worek słów -[[./08_Neuronowy_ngramowy_model/bow1.drawio.png]] - -Odpowiada to wzorowi: - -$$y = \operatorname{softmax}(C\sum_{j=1}^{i-1} E(w_j)).$$ - -*** Jak traktować powtarzające się słowa? - -Według wzoru podanego wyżej, jeśli słowo w poprzedzającym tekście -pojawia się więcej niż raz, jego embedding zostanie zsumowany odpowiednią liczbę razy. -Na przykład embedding tekstu /to be or not to be/ będzie wynosił: - -$$E(\mathrm{to}) + E(\mathrm{be}) + E(\mathrm{or}) + E(\mathrm{not}) + E(\mathrm{to}) + E(\mathrm{be}) = 2E(\mathrm{to}) + 2E(\mathrm{be}) + E(\mathrm{or}) + E(\mathrm{not}).$$ - -Innymi słowy, choć w worku słów nie uwzględniamy kolejności słów, to -*liczba wystąpień* ma dla nas ciągle znaczenie. Można powiedzieć, że -traktujemy poprzedzający tekst jako *multizbiór* (struktura -matematyczna, w której nie uwzględnia się kolejności, choć zachowana -jest informacja o liczbie wystąpień). - -**** Zbiór słów - -Oczywiście moglibyśmy przy agregowaniu zanurzeń pomijać powtarzające -się słowa, a zatem zamiast multizbioru słów rozpatrywać po prostu ich zbiór: - -$$A(w_1,\dots,w_{i-1}) = \sum_{w \in \{w_1,\dots,w_{i-1}\}} E(w).$$ - -Jest kwestią dyskusyjną, czy to lepsze czy gorsze podejście — w końcu -liczba wystąpień np. słów /Ukraina/ czy /Polska/ może wpływać w jakimś -stopniu na prawdopodobieństwo kolejnego słowa (/Kijów/ czy -/Warszawa/?). - -*** Worek słów a wektoryzacja tf - -Wzór na sumę zanurzeń słów można przekształcić w taki sposób, by -sumować po wszystkich słowach ze słownika, zamiast po słowach rzeczywiście występujących w tekście: - -$$A(w_1,\dots,w_{i-1}) = \sum_{j=1}^{i-1} E(w_j) = \sum_{w \in V} \#wE(w)$$ - -gdzie $\#w$ to liczba wystąpień słowa $w$ w ciagu $w_1,\dots,w_{i-1}$ (w wielu przypadkach równa zero!). - -Jeśli teraz zanurzenia będziemy reprezentować jako macierz $E$ (por. poprzedni wykład), -wówczas sumę można przedstawić jako iloczyn macierzy $E$ i pewnego wektora: - -$$A(w_1,\dots,w_{i-1}) = E(w) [\#w^1,\dots,\#w^{|V|}]^T.$$ - -(Odróżniamy $w^i$ jako $i$-ty wyraz w słowniku $V$ od $w_i$ jako $i$-tego wyraz w rozpatrywanym ciągu). - -Zwróćmy uwagę, że wektor $[\#w_1,\dots,\#w_{|V|}]$ to po prostu -reprezentacja wektora poprzedzającego tekstu (tj. ciągu -$(w_1,\dots,w_{i-1})$) przy użyciu schematu wektoryzacji tf (/term -frequency/). Przypomnijmy, że tf to reprezentacja tekstu przy użyciu -wektorów o rozmiarze $|V|$ — na każdej pozycji odnotowujemy liczbę wystąpień. -Wektory tf są *rzadkie*, tj. na wielu pozycjach zawierają zera. - -Innymi słowy, nasz model języka /bag of words/ można przedstawić za pomocą wzoru: - -$$y = \operatorname{softmax}(C\operatorname{tf}(w_1,\dots,w_{i-1})),$$ - -co można zilustrować w następujący sposób: - -#+CAPTION: Model typu worek słów — alternatywna reprezentacja -[[./08_Neuronowy_ngramowy_model/bow2.drawio.png]] - -Można stwierdzić, że zanurzenie tekstu przekształca rzadki, długi wektor -tf w gęsty, krótki wektor. - -** Ważenie słów - -Czy wszystkie słowa są tak samo istotne? Rzecz jasna, nie: - -- jak już wiemy z naszych rozważań dotyczących n-gramowych modeli języka, słowa bezpośrednio - poprzedzające odgadywany wyraz mają większy wpływ niż słowa wcześniejsze; - intuicyjnie, wpływ słów stopniowo spada — tym bardziej, im bardziej słowo jest oddalone od słowa odgadywanego; -- jak wiemy z wyszukiwania informacji, słowa, które występują w wielu tekstach czy dokumentach, powinny mieć - mniejsze znaczenie, w skrajnym przypadku słowa występujące w prawie każdym tekście (/że/, /w/, /i/ itd.) powinny - być praktycznie pomijane jako /stop words/ (jeśli rozpatrywać je w „masie” worka słów — oczywiście - to, czy słowo poprzedzające odgadywane słowo to /że/, /w/ czy /i/ ma olbrzymie znaczenie!). - -Zamiast po prostu dodawać zanurzenia, można operować na sumie (bądź średniej) ważonej: - -$$\sum_{j=1}^{i-1} \omega(j, w_j)E(w_j),$$ - -gdzie $\omega(j, w_j)$ jest pewną wagą, która może zależeć od pozycji $j$ lub samego słowa $w_j$. - -*** Uwzględnienie pozycji - -Można w pewnym stopniu złamać „workowatość” naszej sieci przez proste -uwzględnienie pozycji słowa, np. w taki sposób: - -$$\omega(j, w_j) = \beta^{i-j-1},$$ - -dla pewnego hiperparametru $\beta$. Na przykład jeśli $\beta=0,9$, -wówczas słowo bezpośrednio poprzedzające dane słowo ma $1 / 0,9^9 \approx 2,58$ -większy wpływ niż słowo występujące 10 pozycji wstecz. - -*** Odwrócona częstość dokumentowa - -Aby większą wagę przykładać do słów występujących w mniejszej liczbie -dokumentów, możemy użyć, znanej z wyszukiwania informacji, -odwrotnej częstości dokumentowej (/inverted document frequency/, /idf/): - -$$\omega(j, w_j) = \operatorname{idf}_S(w_j) = \operatorname{log}\frac{|S|}{\operatorname{df}_S(w_j)},$$ - -gdzie: - -- $S$ jest pewną kolekcją dokumentów czy tekstów, z którego pochodzi przedmiotowy ciąg słów, -- $\operatorname{df}_S(w)$ to częstość dokumentowa słowa $w$ w kolekcji $S$, tzn. odpowiedź na pytanie, - w ilu dokumentach występuje $w$. - -Rzecz jasna, ten sposób ważenia oznacza tak naprawdę zastosowanie wektoryzacji tf-idf zamiast tf, -nasza sieć będzie dana zatem wzorem: - -$$y = \operatorname{softmax}(C\operatorname{tfidf}(w_1,\dots,w_{i-1})).$$ - -*** Bardziej skomplikowane sposoby ważenia słów - -Można oczywiście połączyć odwrotną częstość dokumentową z uwzględnieniem pozycji słowa: - -$$\omega(j, w_j) = \beta^{i-j-1}\operatorname{idf}_S(w_j).$$ - -*Uwaga*: „wagi” $\omega(j, w_j)$ nie są tak naprawdę wyuczalnymi -wagami (parametrami) naszej sieci neuronowej, terminologia może być -tutaj myląca. Z punktu widzenia sieci neuronowej $\omega(j, w_j)$ są -stałe i *nie* są optymalizowane w procesie propagacji wstecznej. Innymi -słowy, tak zdefiniowane $\omega(j, w_j)$ zależą tylko od: - -- hiperparametru $\beta$, który może być optymalizowany już poza siecią (w procesie *hiperoptymalizacji*), -- wartości $\operatorname{idf}_S(w_j)$ wyliczanych wcześniej na podstawie kolekcji $S$. - -*Pytanie*: czy wagi $\omega(j, w_j)$ mogłyby sensownie uwzględniać -jakieś parametry wyuczalne z całą siecią? - -** Modelowanie języka przy użyciu bardziej złożonych neuronowych sieci /feed-forward/ - -Można połączyć zalety obu ogólnych podejść (n-gramowego modelu i worka -słów) — można *równocześnie* traktować w specjalny sposób (na -przykład) dwa poprzedzające wyrazy, wszystkie zaś inne wyrazy -reprezentować jako „tło” modelowane za pomocą worka słów lub podobnej -reprezentacji. Osiągamy to poprzez konkatenację wektora -poprzedzającego słowa, słowa występującego dwie pozycje wstecz oraz -zagregowanego zanurzenia całego wcześniejszego tekstu: - -$$y = \operatorname{softmax}(C[E(w_{i-1}),E(w_{i-2}),A(w_1,\dots,w_{i-3})]),$$ - -czy lepiej z dodatkową warstwą ukrytą: - -$$y = \operatorname{softmax}(C\operatorname{tgh}(W[E(w_{i-1}),E(w_{i-2}),A(w_1,\dots,w_{i-3})])),$$ - -W tak uzyskanym dwuwarstwowym neuronowym modelu języka, łączącym model -trigramowy z workiem słów, macierz $W$ ma rozmiar $h \times 3m$. - -*Pytanie*: jakie mamy możliwości, jeśli zamiast przewidywać kolejne słowo, mamy za zadanie -odgadywać słowo w luce (jak w wyzwaniach typu /word gap/)? - -** Literatura - -Skuteczny n-gramowy neuronowy model języka opisano po raz pierwszy -w pracy [[https://www.jmlr.org/papers/volume3/bengio03a/bengio03a.pdf][A Neural Probabilistic Language Model]] autorstwa Yoshua Bengio i in. diff --git a/wyk/11_Worek_slow.ipynb b/wyk/11_Worek_slow.ipynb new file mode 100644 index 0000000..c40bbf9 --- /dev/null +++ b/wyk/11_Worek_slow.ipynb @@ -0,0 +1,414 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![Logo 1](https://git.wmi.amu.edu.pl/AITech/Szablon/raw/branch/master/Logotyp_AITech1.jpg)\n", + "
\n", + "

Modelowanie języka

\n", + "

11. Model worka słów w sieci feed-forward [wykład]

\n", + "

Filip Graliński (2022)

\n", + "
\n", + "\n", + "![Logo 2](https://git.wmi.amu.edu.pl/AITech/Szablon/raw/branch/master/Logotyp_AITech2.jpg)\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Model worka słów w sieci feed-forward\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Jak stwierdziliśmy w poprzednim wykładzie, dwuwarstwowy n-gramowy model języka\n", + "może działać dla stosunkowo dużego $n$. Zauważmy jednak, że istnieje\n", + "pewna słabość tego modelu. Otóż o ile intuicyjnie ma sens odróżniać\n", + "słowo poprzedzające, słowo występujące dwie pozycje wstecz i zapewne\n", + "trzy pozycje wstecz, a zatem uczyć się osobnych macierzy $C_{-1}$,\n", + "$C_{-2}$, $C_{-3}$ to różnica między wpływem słowa\n", + "występującego cztery pozycje wstecz i pięć pozycji wstecz jest już\n", + "raczej nieistotna; innymi słowy różnica między macierzami $C_{-4}$ i\n", + "$C_{-5}$ będzie raczej niewielka i sieć niepotrzebnie będzie uczyła\n", + "się dwukrotnie podobnych wag. Im dalej wstecz, tym różnica wpływu\n", + "będzie jeszcze mniej istotna, można np. przypuszczać, że różnica\n", + "między $C_{-10}$ i $C_{-13}$ nie powinna być duża.\n", + "\n", + "Spróbujmy najpierw zaproponować radykalne podejście, w którym nie\n", + "będziemy w ogóle uwzględniać pozycji słów (lub będziemy je uwzględniać\n", + "w niewielkim stopniu), później połączymy to z omówionym wcześniej\n", + "modelem $n$-gramowym.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Agregacja wektorów\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Zamiast patrzeć na kilka poprzedzających słów, można przewidywać na\n", + "podstawie **całego** ciągu słów poprzedzających odgadywane słowo. Zauważmy jednak, że\n", + "sieć neuronowa musi mieć ustaloną strukturę, nie możemy zmieniać jej\n", + "rozmiaru. Musimy zatem najpierw zagregować cały ciąg do wektora o\n", + "**stałej** długości. Potrzebujemy zatem pewnej funkcji agregującej $A$, takiej by\n", + "$A(w_1,\\dots,w_{i-1})$ było wektorem o stałej długości, niezależnie od $i$.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Worek słów\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Najprostszą funkcją agregującą jest po prostu… suma. Dodajemy po\n", + "prostu zanurzenia słów:\n", + "\n", + "$$A(w_1,\\dots,w_{i-1}) = E(w_1) + \\dots + E(w_{i-1}) = \\sum_{j=1}^{i-1} E(w_j).$$\n", + "\n", + "**Uwaga**: zanurzenia słów nie zależą od pozycji słowa (podobnie było w wypadku n-gramowego modelu!).\n", + "\n", + "Jeśli rozmiar zanurzenia (embeddingu) wynosi $m$, wówczas rozmiar\n", + "wektora uzyskanego dla całego poprzedzającego tekstu wynosi również $m$.\n", + "\n", + "Proste dodawanie wydaje się bardzo „prostacką” metodą, a jednak\n", + "suma wektorów słów jest **zaskakująco skuteczną metodą zanurzenia\n", + "(embedowania) całych tekstów (doc2vec)**. Prostym wariantem dodawania jest obliczanie **średniej wektorów**:\n", + "\n", + "$$A(w_1,\\dots,w_{i-1}) = \\frac{E(w_1) + \\dots + E(w_{i-1})}{i-1} = \\frac{\\sum_{j=1}^{i-1} E(w_j)}{i-1}.$$\n", + "\n", + "Tak czy siak uzyskany wektor **nie zależy od kolejności słów**\n", + "(dodawanie jest przemienne i łączne!). Mówimy więc o **worku słów**\n", + "(*bag of words*, *BoW*) — co ma symbolizować fakt, że słowa są\n", + "przemieszane, niczym produkty w torbie na zakupy.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Schemat graficzny modelu typu worek słów\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Po zanurzeniu całego poprzedzającego tekstu postępujemy podobnie jak w\n", + "modelu bigramowym — rzutujemy embedding na długi wektor wartości, na\n", + "którym stosujemy funkcję softmax:\n", + "\n", + "![img](./11_Worek_slow/bow1.drawio.png \"Model typu worek słów\")\n", + "\n", + "Odpowiada to wzorowi:\n", + "\n", + "$$y = \\operatorname{softmax}(C\\sum_{j=1}^{i-1} E(w_j)).$$\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Jak traktować powtarzające się słowa?\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Według wzoru podanego wyżej, jeśli słowo w poprzedzającym tekście\n", + "pojawia się więcej niż raz, jego embedding zostanie zsumowany odpowiednią liczbę razy.\n", + "Na przykład embedding tekstu *to be or not to be* będzie wynosił:\n", + "\n", + "$$E(\\mathrm{to}) + E(\\mathrm{be}) + E(\\mathrm{or}) + E(\\mathrm{not}) + E(\\mathrm{to}) + E(\\mathrm{be}) = 2E(\\mathrm{to}) + 2E(\\mathrm{be}) + E(\\mathrm{or}) + E(\\mathrm{not}).$$\n", + "\n", + "Innymi słowy, choć w worku słów nie uwzględniamy kolejności słów, to\n", + "**liczba wystąpień** ma dla nas ciągle znaczenie. Można powiedzieć, że\n", + "traktujemy poprzedzający tekst jako **multizbiór** (struktura\n", + "matematyczna, w której nie uwzględnia się kolejności, choć zachowana\n", + "jest informacja o liczbie wystąpień).\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Zbiór słów\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Oczywiście moglibyśmy przy agregowaniu zanurzeń pomijać powtarzające\n", + "się słowa, a zatem zamiast multizbioru słów rozpatrywać po prostu ich zbiór:\n", + "\n", + "$$A(w_1,\\dots,w_{i-1}) = \\sum_{w \\in \\{w_1,\\dots,w_{i-1}\\}} E(w).$$\n", + "\n", + "Jest kwestią dyskusyjną, czy to lepsze czy gorsze podejście — w końcu\n", + "liczba wystąpień np. słów *Ukraina* czy *Polska* może wpływać w jakimś\n", + "stopniu na prawdopodobieństwo kolejnego słowa (*Kijów* czy\n", + "*Warszawa*?).\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Worek słów a wektoryzacja tf\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Wzór na sumę zanurzeń słów można przekształcić w taki sposób, by\n", + "sumować po wszystkich słowach ze słownika, zamiast po słowach rzeczywiście występujących w tekście:\n", + "\n", + "$$A(w_1,\\dots,w_{i-1}) = \\sum_{j=1}^{i-1} E(w_j) = \\sum_{w \\in V} \\#wE(w)$$\n", + "\n", + "gdzie $\\#w$ to liczba wystąpień słowa $w$ w ciagu $w_1,\\dots,w_{i-1}$ (w wielu przypadkach równa zero!).\n", + "\n", + "Jeśli teraz zanurzenia będziemy reprezentować jako macierz $E$ (por. poprzedni wykład),\n", + "wówczas sumę można przedstawić jako iloczyn macierzy $E$ i pewnego wektora:\n", + "\n", + "$$A(w_1,\\dots,w_{i-1}) = E(w) [\\#w^1,\\dots,\\#w^{|V|}]^T.$$\n", + "\n", + "(Odróżniamy $w^i$ jako $i$-ty wyraz w słowniku $V$ od $w_i$ jako $i$-tego wyraz w rozpatrywanym ciągu).\n", + "\n", + "Zwróćmy uwagę, że wektor $[\\#w_1,\\dots,\\#w_{|V|}]$ to po prostu\n", + "reprezentacja wektora poprzedzającego tekstu (tj. ciągu\n", + "$(w_1,\\dots,w_{i-1})$) przy użyciu schematu wektoryzacji tf (*term\n", + "frequency*). Przypomnijmy, że tf to reprezentacja tekstu przy użyciu\n", + "wektorów o rozmiarze $|V|$ — na każdej pozycji odnotowujemy liczbę wystąpień.\n", + "Wektory tf są **rzadkie**, tj. na wielu pozycjach zawierają zera.\n", + "\n", + "Innymi słowy, nasz model języka *bag of words* można przedstawić za pomocą wzoru:\n", + "\n", + "$$y = \\operatorname{softmax}(C\\operatorname{tf}(w_1,\\dots,w_{i-1})),$$\n", + "\n", + "co można zilustrować w następujący sposób:\n", + "\n", + "![img](./11_Worek_slow/bow2.drawio.png \"Model typu worek słów — alternatywna reprezentacja\")\n", + "\n", + "Można stwierdzić, że zanurzenie tekstu przekształca rzadki, długi wektor\n", + "tf w gęsty, krótki wektor.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Ważenie słów\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Czy wszystkie słowa są tak samo istotne? Rzecz jasna, nie:\n", + "\n", + "- jak już wiemy z naszych rozważań dotyczących n-gramowych modeli języka, słowa bezpośrednio\n", + " poprzedzające odgadywany wyraz mają większy wpływ niż słowa wcześniejsze;\n", + " intuicyjnie, wpływ słów stopniowo spada — tym bardziej, im bardziej słowo jest oddalone od słowa odgadywanego;\n", + "- jak wiemy z wyszukiwania informacji, słowa, które występują w wielu tekstach czy dokumentach, powinny mieć\n", + " mniejsze znaczenie, w skrajnym przypadku słowa występujące w prawie każdym tekście (*że*, *w*, *i* itd.) powinny\n", + " być praktycznie pomijane jako *stop words* (jeśli rozpatrywać je w „masie” worka słów — oczywiście\n", + " to, czy słowo poprzedzające odgadywane słowo to *że*, *w* czy *i* ma olbrzymie znaczenie!).\n", + "\n", + "Zamiast po prostu dodawać zanurzenia, można operować na sumie (bądź średniej) ważonej:\n", + "\n", + "$$\\sum_{j=1}^{i-1} \\omega(j, w_j)E(w_j),$$\n", + "\n", + "gdzie $\\omega(j, w_j)$ jest pewną wagą, która może zależeć od pozycji $j$ lub samego słowa $w_j$.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Uwzględnienie pozycji\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Można w pewnym stopniu złamać „workowatość” naszej sieci przez proste\n", + "uwzględnienie pozycji słowa, np. w taki sposób:\n", + "\n", + "$$\\omega(j, w_j) = \\beta^{i-j-1},$$\n", + "\n", + "dla pewnego hiperparametru $\\beta$. Na przykład jeśli $\\beta=0,9$,\n", + "wówczas słowo bezpośrednio poprzedzające dane słowo ma $1 / 0,9^9 \\approx 2,58$\n", + "większy wpływ niż słowo występujące 10 pozycji wstecz.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Odwrócona częstość dokumentowa\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Aby większą wagę przykładać do słów występujących w mniejszej liczbie\n", + "dokumentów, możemy użyć, znanej z wyszukiwania informacji,\n", + "odwrotnej częstości dokumentowej (*inverted document frequency*, *idf*):\n", + "\n", + "$$\\omega(j, w_j) = \\operatorname{idf}_S(w_j) = \\operatorname{log}\\frac{|S|}{\\operatorname{df}_S(w_j)},$$\n", + "\n", + "gdzie:\n", + "\n", + "- $S$ jest pewną kolekcją dokumentów czy tekstów, z którego pochodzi przedmiotowy ciąg słów,\n", + "- $\\operatorname{df}_S(w)$ to częstość dokumentowa słowa $w$ w kolekcji $S$, tzn. odpowiedź na pytanie,\n", + " w ilu dokumentach występuje $w$.\n", + "\n", + "Rzecz jasna, ten sposób ważenia oznacza tak naprawdę zastosowanie wektoryzacji tf-idf zamiast tf,\n", + "nasza sieć będzie dana zatem wzorem:\n", + "\n", + "$$y = \\operatorname{softmax}(C\\operatorname{tfidf}(w_1,\\dots,w_{i-1})).$$\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Bardziej skomplikowane sposoby ważenia słów\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Można oczywiście połączyć odwrotną częstość dokumentową z uwzględnieniem pozycji słowa:\n", + "\n", + "$$\\omega(j, w_j) = \\beta^{i-j-1}\\operatorname{idf}_S(w_j).$$\n", + "\n", + "**Uwaga**: „wagi” $\\omega(j, w_j)$ nie są tak naprawdę wyuczalnymi\n", + "wagami (parametrami) naszej sieci neuronowej, terminologia może być\n", + "tutaj myląca. Z punktu widzenia sieci neuronowej $\\omega(j, w_j)$ są\n", + "stałe i **nie** są optymalizowane w procesie propagacji wstecznej. Innymi\n", + "słowy, tak zdefiniowane $\\omega(j, w_j)$ zależą tylko od:\n", + "\n", + "- hiperparametru $\\beta$, który może być optymalizowany już poza siecią (w procesie **hiperoptymalizacji**),\n", + "- wartości $\\operatorname{idf}_S(w_j)$ wyliczanych wcześniej na podstawie kolekcji $S$.\n", + "\n", + "**Pytanie**: czy wagi $\\omega(j, w_j)$ mogłyby sensownie uwzględniać\n", + "jakieś parametry wyuczalne z całą siecią?\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Modelowanie języka przy użyciu bardziej złożonych neuronowych sieci *feed-forward*\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Można połączyć zalety obu ogólnych podejść (n-gramowego modelu i worka\n", + "słów) — można **równocześnie** traktować w specjalny sposób (na\n", + "przykład) dwa poprzedzające wyrazy, wszystkie zaś inne wyrazy\n", + "reprezentować jako „tło” modelowane za pomocą worka słów lub podobnej\n", + "reprezentacji. Osiągamy to poprzez konkatenację wektora\n", + "poprzedzającego słowa, słowa występującego dwie pozycje wstecz oraz\n", + "zagregowanego zanurzenia całego wcześniejszego tekstu:\n", + "\n", + "$$y = \\operatorname{softmax}(C[E(w_{i-1}),E(w_{i-2}),A(w_1,\\dots,w_{i-3})]),$$\n", + "\n", + "czy lepiej z dodatkową warstwą ukrytą:\n", + "\n", + "$$y = \\operatorname{softmax}(C\\operatorname{tgh}(W[E(w_{i-1}),E(w_{i-2}),A(w_1,\\dots,w_{i-3})])),$$\n", + "\n", + "W tak uzyskanym dwuwarstwowym neuronowym modelu języka, łączącym model\n", + "trigramowy z workiem słów, macierz $W$ ma rozmiar $h \\times 3m$.\n", + "\n", + "**Pytanie**: jakie mamy możliwości, jeśli zamiast przewidywać kolejne słowo, mamy za zadanie\n", + "odgadywać słowo w luce (jak w wyzwaniach typu *word gap*)?\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Literatura\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Skuteczny n-gramowy neuronowy model języka opisano po raz pierwszy\n", + "w pracy [A Neural Probabilistic Language Model](https://www.jmlr.org/papers/volume3/bengio03a/bengio03a.pdf) autorstwa Yoshua Bengio i in.\n", + "\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.5" + }, + "org": null + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/wyk/11_Worek_slow.org b/wyk/11_Worek_slow.org new file mode 100644 index 0000000..d5230e7 --- /dev/null +++ b/wyk/11_Worek_slow.org @@ -0,0 +1,218 @@ +* Model worka słów w sieci feed-forward + +Jak stwierdziliśmy w poprzednim wykładzie, dwuwarstwowy n-gramowy model języka +może działać dla stosunkowo dużego $n$. Zauważmy jednak, że istnieje +pewna słabość tego modelu. Otóż o ile intuicyjnie ma sens odróżniać +słowo poprzedzające, słowo występujące dwie pozycje wstecz i zapewne +trzy pozycje wstecz, a zatem uczyć się osobnych macierzy $C_{-1}$, +$C_{-2}$, $C_{-3}$ to różnica między wpływem słowa +występującego cztery pozycje wstecz i pięć pozycji wstecz jest już +raczej nieistotna; innymi słowy różnica między macierzami $C_{-4}$ i +$C_{-5}$ będzie raczej niewielka i sieć niepotrzebnie będzie uczyła +się dwukrotnie podobnych wag. Im dalej wstecz, tym różnica wpływu +będzie jeszcze mniej istotna, można np. przypuszczać, że różnica +między $C_{-10}$ i $C_{-13}$ nie powinna być duża. + +Spróbujmy najpierw zaproponować radykalne podejście, w którym nie +będziemy w ogóle uwzględniać pozycji słów (lub będziemy je uwzględniać +w niewielkim stopniu), później połączymy to z omówionym wcześniej +modelem $n$-gramowym. + +** Agregacja wektorów + +Zamiast patrzeć na kilka poprzedzających słów, można przewidywać na +podstawie *całego* ciągu słów poprzedzających odgadywane słowo. Zauważmy jednak, że +sieć neuronowa musi mieć ustaloną strukturę, nie możemy zmieniać jej +rozmiaru. Musimy zatem najpierw zagregować cały ciąg do wektora o +*stałej* długości. Potrzebujemy zatem pewnej funkcji agregującej $A$, takiej by +$A(w_1,\dots,w_{i-1})$ było wektorem o stałej długości, niezależnie od $i$. + +** Worek słów + +Najprostszą funkcją agregującą jest po prostu… suma. Dodajemy po +prostu zanurzenia słów: + +$$A(w_1,\dots,w_{i-1}) = E(w_1) + \dots + E(w_{i-1}) = \sum_{j=1}^{i-1} E(w_j).$$ + +*Uwaga*: zanurzenia słów nie zależą od pozycji słowa (podobnie było w wypadku n-gramowego modelu!). + +Jeśli rozmiar zanurzenia (embeddingu) wynosi $m$, wówczas rozmiar +wektora uzyskanego dla całego poprzedzającego tekstu wynosi również $m$. + +Proste dodawanie wydaje się bardzo „prostacką” metodą, a jednak +suma wektorów słów jest *zaskakująco skuteczną metodą zanurzenia +(embedowania) całych tekstów (doc2vec)*. Prostym wariantem dodawania jest obliczanie *średniej wektorów*: + +$$A(w_1,\dots,w_{i-1}) = \frac{E(w_1) + \dots + E(w_{i-1})}{i-1} = \frac{\sum_{j=1}^{i-1} E(w_j)}{i-1}.$$ + +Tak czy siak uzyskany wektor *nie zależy od kolejności słów* +(dodawanie jest przemienne i łączne!). Mówimy więc o *worku słów* +(/bag of words/, /BoW/) — co ma symbolizować fakt, że słowa są +przemieszane, niczym produkty w torbie na zakupy. + +*** Schemat graficzny modelu typu worek słów + +Po zanurzeniu całego poprzedzającego tekstu postępujemy podobnie jak w +modelu bigramowym — rzutujemy embedding na długi wektor wartości, na +którym stosujemy funkcję softmax: + +#+CAPTION: Model typu worek słów +[[./11_Worek_slow/bow1.drawio.png]] + +Odpowiada to wzorowi: + +$$y = \operatorname{softmax}(C\sum_{j=1}^{i-1} E(w_j)).$$ + +** Jak traktować powtarzające się słowa? + +Według wzoru podanego wyżej, jeśli słowo w poprzedzającym tekście +pojawia się więcej niż raz, jego embedding zostanie zsumowany odpowiednią liczbę razy. +Na przykład embedding tekstu /to be or not to be/ będzie wynosił: + +$$E(\mathrm{to}) + E(\mathrm{be}) + E(\mathrm{or}) + E(\mathrm{not}) + E(\mathrm{to}) + E(\mathrm{be}) = 2E(\mathrm{to}) + 2E(\mathrm{be}) + E(\mathrm{or}) + E(\mathrm{not}).$$ + +Innymi słowy, choć w worku słów nie uwzględniamy kolejności słów, to +*liczba wystąpień* ma dla nas ciągle znaczenie. Można powiedzieć, że +traktujemy poprzedzający tekst jako *multizbiór* (struktura +matematyczna, w której nie uwzględnia się kolejności, choć zachowana +jest informacja o liczbie wystąpień). + +*** Zbiór słów + +Oczywiście moglibyśmy przy agregowaniu zanurzeń pomijać powtarzające +się słowa, a zatem zamiast multizbioru słów rozpatrywać po prostu ich zbiór: + +$$A(w_1,\dots,w_{i-1}) = \sum_{w \in \{w_1,\dots,w_{i-1}\}} E(w).$$ + +Jest kwestią dyskusyjną, czy to lepsze czy gorsze podejście — w końcu +liczba wystąpień np. słów /Ukraina/ czy /Polska/ może wpływać w jakimś +stopniu na prawdopodobieństwo kolejnego słowa (/Kijów/ czy +/Warszawa/?). + +** Worek słów a wektoryzacja tf + +Wzór na sumę zanurzeń słów można przekształcić w taki sposób, by +sumować po wszystkich słowach ze słownika, zamiast po słowach rzeczywiście występujących w tekście: + +$$A(w_1,\dots,w_{i-1}) = \sum_{j=1}^{i-1} E(w_j) = \sum_{w \in V} \#wE(w)$$ + +gdzie $\#w$ to liczba wystąpień słowa $w$ w ciagu $w_1,\dots,w_{i-1}$ (w wielu przypadkach równa zero!). + +Jeśli teraz zanurzenia będziemy reprezentować jako macierz $E$ (por. poprzedni wykład), +wówczas sumę można przedstawić jako iloczyn macierzy $E$ i pewnego wektora: + +$$A(w_1,\dots,w_{i-1}) = E(w) [\#w^1,\dots,\#w^{|V|}]^T.$$ + +(Odróżniamy $w^i$ jako $i$-ty wyraz w słowniku $V$ od $w_i$ jako $i$-tego wyraz w rozpatrywanym ciągu). + +Zwróćmy uwagę, że wektor $[\#w_1,\dots,\#w_{|V|}]$ to po prostu +reprezentacja wektora poprzedzającego tekstu (tj. ciągu +$(w_1,\dots,w_{i-1})$) przy użyciu schematu wektoryzacji tf (/term +frequency/). Przypomnijmy, że tf to reprezentacja tekstu przy użyciu +wektorów o rozmiarze $|V|$ — na każdej pozycji odnotowujemy liczbę wystąpień. +Wektory tf są *rzadkie*, tj. na wielu pozycjach zawierają zera. + +Innymi słowy, nasz model języka /bag of words/ można przedstawić za pomocą wzoru: + +$$y = \operatorname{softmax}(C\operatorname{tf}(w_1,\dots,w_{i-1})),$$ + +co można zilustrować w następujący sposób: + +#+CAPTION: Model typu worek słów — alternatywna reprezentacja +[[./11_Worek_slow/bow2.drawio.png]] + +Można stwierdzić, że zanurzenie tekstu przekształca rzadki, długi wektor +tf w gęsty, krótki wektor. + +** Ważenie słów + +Czy wszystkie słowa są tak samo istotne? Rzecz jasna, nie: + +- jak już wiemy z naszych rozważań dotyczących n-gramowych modeli języka, słowa bezpośrednio + poprzedzające odgadywany wyraz mają większy wpływ niż słowa wcześniejsze; + intuicyjnie, wpływ słów stopniowo spada — tym bardziej, im bardziej słowo jest oddalone od słowa odgadywanego; +- jak wiemy z wyszukiwania informacji, słowa, które występują w wielu tekstach czy dokumentach, powinny mieć + mniejsze znaczenie, w skrajnym przypadku słowa występujące w prawie każdym tekście (/że/, /w/, /i/ itd.) powinny + być praktycznie pomijane jako /stop words/ (jeśli rozpatrywać je w „masie” worka słów — oczywiście + to, czy słowo poprzedzające odgadywane słowo to /że/, /w/ czy /i/ ma olbrzymie znaczenie!). + +Zamiast po prostu dodawać zanurzenia, można operować na sumie (bądź średniej) ważonej: + +$$\sum_{j=1}^{i-1} \omega(j, w_j)E(w_j),$$ + +gdzie $\omega(j, w_j)$ jest pewną wagą, która może zależeć od pozycji $j$ lub samego słowa $w_j$. + +*** Uwzględnienie pozycji + +Można w pewnym stopniu złamać „workowatość” naszej sieci przez proste +uwzględnienie pozycji słowa, np. w taki sposób: + +$$\omega(j, w_j) = \beta^{i-j-1},$$ + +dla pewnego hiperparametru $\beta$. Na przykład jeśli $\beta=0,9$, +wówczas słowo bezpośrednio poprzedzające dane słowo ma $1 / 0,9^9 \approx 2,58$ +większy wpływ niż słowo występujące 10 pozycji wstecz. + +*** Odwrócona częstość dokumentowa + +Aby większą wagę przykładać do słów występujących w mniejszej liczbie +dokumentów, możemy użyć, znanej z wyszukiwania informacji, +odwrotnej częstości dokumentowej (/inverted document frequency/, /idf/): + +$$\omega(j, w_j) = \operatorname{idf}_S(w_j) = \operatorname{log}\frac{|S|}{\operatorname{df}_S(w_j)},$$ + +gdzie: + +- $S$ jest pewną kolekcją dokumentów czy tekstów, z którego pochodzi przedmiotowy ciąg słów, +- $\operatorname{df}_S(w)$ to częstość dokumentowa słowa $w$ w kolekcji $S$, tzn. odpowiedź na pytanie, + w ilu dokumentach występuje $w$. + +Rzecz jasna, ten sposób ważenia oznacza tak naprawdę zastosowanie wektoryzacji tf-idf zamiast tf, +nasza sieć będzie dana zatem wzorem: + +$$y = \operatorname{softmax}(C\operatorname{tfidf}(w_1,\dots,w_{i-1})).$$ + +*** Bardziej skomplikowane sposoby ważenia słów + +Można oczywiście połączyć odwrotną częstość dokumentową z uwzględnieniem pozycji słowa: + +$$\omega(j, w_j) = \beta^{i-j-1}\operatorname{idf}_S(w_j).$$ + +*Uwaga*: „wagi” $\omega(j, w_j)$ nie są tak naprawdę wyuczalnymi +wagami (parametrami) naszej sieci neuronowej, terminologia może być +tutaj myląca. Z punktu widzenia sieci neuronowej $\omega(j, w_j)$ są +stałe i *nie* są optymalizowane w procesie propagacji wstecznej. Innymi +słowy, tak zdefiniowane $\omega(j, w_j)$ zależą tylko od: + +- hiperparametru $\beta$, który może być optymalizowany już poza siecią (w procesie *hiperoptymalizacji*), +- wartości $\operatorname{idf}_S(w_j)$ wyliczanych wcześniej na podstawie kolekcji $S$. + +*Pytanie*: czy wagi $\omega(j, w_j)$ mogłyby sensownie uwzględniać +jakieś parametry wyuczalne z całą siecią? + +** Modelowanie języka przy użyciu bardziej złożonych neuronowych sieci /feed-forward/ + +Można połączyć zalety obu ogólnych podejść (n-gramowego modelu i worka +słów) — można *równocześnie* traktować w specjalny sposób (na +przykład) dwa poprzedzające wyrazy, wszystkie zaś inne wyrazy +reprezentować jako „tło” modelowane za pomocą worka słów lub podobnej +reprezentacji. Osiągamy to poprzez konkatenację wektora +poprzedzającego słowa, słowa występującego dwie pozycje wstecz oraz +zagregowanego zanurzenia całego wcześniejszego tekstu: + +$$y = \operatorname{softmax}(C[E(w_{i-1}),E(w_{i-2}),A(w_1,\dots,w_{i-3})]),$$ + +czy lepiej z dodatkową warstwą ukrytą: + +$$y = \operatorname{softmax}(C\operatorname{tgh}(W[E(w_{i-1}),E(w_{i-2}),A(w_1,\dots,w_{i-3})])),$$ + +W tak uzyskanym dwuwarstwowym neuronowym modelu języka, łączącym model +trigramowy z workiem słów, macierz $W$ ma rozmiar $h \times 3m$. + +*Pytanie*: jakie mamy możliwości, jeśli zamiast przewidywać kolejne słowo, mamy za zadanie +odgadywać słowo w luce (jak w wyzwaniach typu /word gap/)? + +** Literatura + +Skuteczny n-gramowy neuronowy model języka opisano po raz pierwszy +w pracy [[https://www.jmlr.org/papers/volume3/bengio03a/bengio03a.pdf][A Neural Probabilistic Language Model]] autorstwa Yoshua Bengio i in. diff --git a/wyk/08_Neuronowy_ngramowy_model/bow1.drawio b/wyk/11_Worek_slow/bow1.drawio similarity index 100% rename from wyk/08_Neuronowy_ngramowy_model/bow1.drawio rename to wyk/11_Worek_slow/bow1.drawio diff --git a/wyk/08_Neuronowy_ngramowy_model/bow1.drawio.png b/wyk/11_Worek_slow/bow1.drawio.png similarity index 100% rename from wyk/08_Neuronowy_ngramowy_model/bow1.drawio.png rename to wyk/11_Worek_slow/bow1.drawio.png diff --git a/wyk/08_Neuronowy_ngramowy_model/bow2.drawio b/wyk/11_Worek_slow/bow2.drawio similarity index 100% rename from wyk/08_Neuronowy_ngramowy_model/bow2.drawio rename to wyk/11_Worek_slow/bow2.drawio diff --git a/wyk/08_Neuronowy_ngramowy_model/bow2.drawio.png b/wyk/11_Worek_slow/bow2.drawio.png similarity index 100% rename from wyk/08_Neuronowy_ngramowy_model/bow2.drawio.png rename to wyk/11_Worek_slow/bow2.drawio.png