diff --git a/wyk/07_Zanurzenia_slow.org b/wyk/07_Zanurzenia_slow.org new file mode 100644 index 0000000..f4c6a42 --- /dev/null +++ b/wyk/07_Zanurzenia_slow.org @@ -0,0 +1,338 @@ +* Zanurzenia słów + +W praktyce stosowalność słowosieci okazała się zaskakująco +ograniczona. Większy przełom w przetwarzaniu języka naturalnego przyniosły +wielowymiarowe reprezentacje słów, inaczej: zanurzenia słów. + +** „Wymiary” słów + +Moglibyśmy zanurzyć (ang. /embed/) w wielowymiarowej przestrzeni, tzn. zdefiniować odwzorowanie +$E \colon V \rightarrow \mathcal{R}^m$ dla pewnego $m$ i określić taki sposób estymowania +prawdopodobieństw $P(u|v)$, by dla par $E(v)$ i $E(v')$ oraz $E(u)$ i $E(u')$ znajdujących się w pobliżu +(według jakiejś metryki odległości, na przykład zwykłej odległości euklidesowej): + +$$P(u|v) \approx P(u'|v').$$ + +$E(u)$ nazywamy zanurzeniem (embeddingiem) słowa. + +*** Wymiary określone z góry? + +Można by sobie wyobrazić, że $m$ wymiarów mogłoby być z góry +określonych przez lingwistę. Wymiary te byłyby związane z typowymi +„osiami” rozpatrywanymi w językoznawstwie, na przykład: + +- czy słowo jest wulgarne, pospolite, potoczne, neutralne czy książkowe? +- czy słowo jest archaiczne, wychodzące z użycia czy jest neologizmem? +- czy słowo dotyczy kobiet, czy mężczyzn (w sensie rodzaju gramatycznego i/lub + socjolingwistycznym)? +- czy słowo jest w liczbie pojedynczej czy mnogiej? +- czy słowo jest rzeczownikiem czy czasownikiem? +- czy słowo jest rdzennym słowem czy zapożyczeniem? +- czy słowo jest nazwą czy słowem pospolitym? +- czy słowo opisuje konkretną rzecz czy pojęcie abstrakcyjne? +- … + +W praktyce okazało się jednak, że lepiej, żeby komputer uczył się sam +możliwych wymiarów — z góry określamy tylko $m$ (liczbę wymiarów). + +** Bigramowy model języka oparty na zanurzeniach + +Zbudujemy teraz najprostszy model język oparty na zanurzeniach. Będzie to właściwie najprostszy +*neuronowy model języka*, jako że zbudowany model można traktować jako prostą sieć neuronową. + +*** Słownik + +W typowym neuronowym modelu języka rozmiar słownika musi być z góry +ograniczony. Zazwyczaj jest to liczba rzędu kilkudziesięciu wyrazów — +po prostu będziemy rozpatrywać $|V|$ najczęstszych wyrazów, pozostałe zamienimy +na specjalny token ~~ reprezentujący nieznany (/unknown/) wyraz. + +Aby utworzyć taki słownik użyjemy gotowej klasy ~Vocab~ z pakietu torchtext: + +#+BEGIN_SRC python :session mysession :exports both :results raw drawer + from itertools import islice + import regex as re + import sys + from torchtext.vocab import build_vocab_from_iterator + + + def get_words_from_line(line): + line = line.rstrip() + yield '' + for m in re.finditer(r'[\p{L}0-9\*]+|\p{P}+', line): + yield m.group(0).lower() + yield '' + + + def get_word_lines_from_file(file_name): + with open(file_name, 'r') as fh: + for line in fh: + yield get_words_from_line(line) + + vocab_size = 20000 + + vocab = build_vocab_from_iterator( + get_word_lines_from_file('opensubtitlesA.pl.txt'), + max_tokens = vocab_size, + specials = ['']) + + vocab['jest'] +#+END_SRC + +#+RESULTS: +:results: +16 +:end: + +#+BEGIN_SRC python :session mysession :exports both :results raw drawer +vocab.lookup_tokens([0, 1, 2, 10, 12345]) +#+END_SRC + +#+RESULTS: +:results: +['', '', '', 'w', 'wierzyli'] +:end: + +*** Definicja sieci + +Naszą prostą sieć neuronową zaimplementujemy używając frameworku PyTorch. + +#+BEGIN_SRC python :session mysession :exports both :results raw drawer + from torch import nn + import torch + + embed_size = 100 + + class SimpleBigramNeuralLanguageModel(nn.Module): + def __init__(self, vocabulary_size, embedding_size): + super(SimpleBigramNeuralLanguageModel, self).__init__() + self.model = nn.Sequential( + nn.Embedding(vocabulary_size, embedding_size), + nn.Linear(embedding_size, vocabulary_size), + nn.Softmax() + ) + + def forward(self, x): + return self.model(x) + + model = SimpleBigramNeuralLanguageModel(vocab_size, embed_size) + + vocab.set_default_index(vocab['']) + ixs = torch.tensor(vocab.forward(['pies'])) + out[0][vocab['jest']] +#+END_SRC + +#+RESULTS: +:results: +:end: + +Teraz wyuczmy model. Wpierw tylko potasujmy nasz plik: + +#+BEGIN_SRC +shuf < opensubtitlesA.pl.txt > opensubtitlesA.pl.shuf.txt +#+END_SRC + +#+BEGIN_SRC python :session mysession :exports both :results raw drawer + from torch.utils.data import IterableDataset + import itertools + + def look_ahead_iterator(gen): + prev = None + for item in gen: + if prev is not None: + yield (prev, item) + prev = item + + class Bigrams(IterableDataset): + def __init__(self, text_file, vocabulary_size): + self.vocab = build_vocab_from_iterator( + get_word_lines_from_file(text_file), + max_tokens = vocabulary_size, + specials = ['']) + self.vocab.set_default_index(self.vocab['']) + self.vocabulary_size = vocabulary_size + self.text_file = text_file + + def __iter__(self): + return look_ahead_iterator( + (self.vocab[t] for t in itertools.chain.from_iterable(get_word_lines_from_file(self.text_file)))) + + train_dataset = Bigrams('opensubtitlesA.pl.shuf.txt', vocab_size) +#+END_SRC + +#+RESULTS: +:results: +:end: + +#+BEGIN_SRC python :session mysession :exports both :results raw drawer + from torch.utils.data import DataLoader + + next(iter(train_dataset)) +#+END_SRC + +#+RESULTS: +:results: +(2, 5) +:end: + +#+BEGIN_SRC python :session mysession :exports both :results raw drawer + from torch.utils.data import DataLoader + + next(iter(DataLoader(train_dataset, batch_size=5))) +#+END_SRC + +#+RESULTS: +:results: +[tensor([ 2, 5, 51, 3481, 231]), tensor([ 5, 51, 3481, 231, 4])] +:end: + +#+BEGIN_SRC python :session mysession :exports both :results raw drawer + device = 'cuda' + model = SimpleBigramNeuralLanguageModel(vocab_size, embed_size).to(device) + data = DataLoader(train_dataset, batch_size=5000) + optimizer = torch.optim.Adam(model.parameters()) + criterion = torch.nn.NLLLoss() + + model.train() + step = 0 + for x, y in data: + x = x.to(device) + y = y.to(device) + optimizer.zero_grad() + ypredicted = model(x) + loss = criterion(torch.log(ypredicted), y) + if step % 100 == 0: + print(step, loss) + step += 1 + loss.backward() + optimizer.step() + + torch.save(model.state_dict(), 'model1.bin') +#+END_SRC + +#+RESULTS: +:results: +None +:end: + +Policzmy najbardziej prawdopodobne kontynuację dla zadanego słowa: + +#+BEGIN_SRC python :session mysession :exports both :results raw drawer + device = 'cuda' + model = SimpleBigramNeuralLanguageModel(vocab_size, embed_size).to(device) + model.load_state_dict(torch.load('model1.bin')) + model.eval() + + ixs = torch.tensor(vocab.forward(['dla'])).to(device) + + out = model(ixs) + top = torch.topk(out[0], 10) + top_indices = top.indices.tolist() + top_probs = top.values.tolist() + top_words = vocab.lookup_tokens(top_indices) + list(zip(top_words, top_indices, top_probs)) +#+END_SRC + +#+RESULTS: +:results: +[('ciebie', 73, 0.1580502986907959), ('mnie', 26, 0.15395283699035645), ('', 0, 0.12862136960029602), ('nas', 83, 0.0410110242664814), ('niego', 172, 0.03281523287296295), ('niej', 245, 0.02104802615940571), ('siebie', 181, 0.020788608118891716), ('którego', 365, 0.019379809498786926), ('was', 162, 0.013852755539119244), ('wszystkich', 235, 0.01381855271756649)] +:end: + +Teraz zbadajmy najbardziej podobne zanurzenia dla zadanego słowa: + +#+BEGIN_SRC python :session mysession :exports both :results raw drawer + vocab = train_dataset.vocab + ixs = torch.tensor(vocab.forward(['kłopot'])).to(device) + + out = model(ixs) + top = torch.topk(out[0], 10) + top_indices = top.indices.tolist() + top_probs = top.values.tolist() + top_words = vocab.lookup_tokens(top_indices) + list(zip(top_words, top_indices, top_probs)) +#+END_SRC + +#+RESULTS: +:results: +[('.', 3, 0.404473215341568), (',', 4, 0.14222915470600128), ('z', 14, 0.10945753753185272), ('?', 6, 0.09583134204149246), ('w', 10, 0.050338443368673325), ('na', 12, 0.020703863352537155), ('i', 11, 0.016762692481279373), ('', 0, 0.014571071602404118), ('...', 15, 0.01453721895813942), ('', 1, 0.011769450269639492)] +:end: + +#+BEGIN_SRC python :session mysession :exports both :results raw drawer + cos = nn.CosineSimilarity(dim=1, eps=1e-6) + + embeddings = model.model[0].weight + + vec = embeddings[vocab['poszedł']] + + similarities = cos(vec, embeddings) + + top = torch.topk(similarities, 10) + + top_indices = top.indices.tolist() + top_probs = top.values.tolist() + top_words = vocab.lookup_tokens(top_indices) + list(zip(top_words, top_indices, top_probs)) +#+END_SRC + +#+RESULTS: +:results: +[('poszedł', 1087, 1.0), ('idziesz', 1050, 0.4907470941543579), ('przyjeżdża', 4920, 0.45242372155189514), ('pojechałam', 12784, 0.4342481195926666), ('wrócił', 1023, 0.431664377450943), ('dobrać', 10351, 0.4312002956867218), ('stałeś', 5738, 0.4258835017681122), ('poszła', 1563, 0.41979148983955383), ('trafiłam', 18857, 0.4109022617340088), ('jedzie', 1674, 0.4091658890247345)] +:end: + +*** Zapis przy użyciu wzoru matematycznego + +Powyżej zaprogramowaną sieć neuronową można opisać następującym wzorem: + +$$\vec{y} = \operatorname{softmax}(CE(w_{i-1}),$$ + +gdzie: + +- $w_{i-1}$ to pierwszy wyraz w bigramie (poprzedzający wyraz), +- $E(w)$ to zanurzenie (embedding) wyrazy $w$ — wektor o rozmiarze $m$, +- $C$ to macierz o rozmiarze $|V| \times m$, która rzutuje wektor zanurzenia w wektor o rozmiarze słownika, +- $\vec{y}$ to wyjściowy wektor prawdopodobieństw o rozmiarze $|V|$. + +**** Hiperparametry + +Zauważmy, że nasz model ma dwa hiperparametry: + +- $m$ — rozmiar zanurzenia, +- $|V|$ — rozmiar słownika, jeśli zakładamy, że możemy sterować + rozmiarem słownika (np. przez obcinanie słownika do zadanej liczby + najczęstszych wyrazów i zamiany pozostałych na specjalny token, powiedzmy, ~~. + +Oczywiście możemy próbować manipulować wartościami $m$ i $|V|$ w celu +polepszenia wyników naszego modelu. + +*Pytanie*: dlaczego nie ma sensu wartość $m \approx |V|$ ? dlaczego nie ma sensu wartość $m = 1$? + +*** Diagram sieci + +Jako że mnożenie przez macierz ($C$) oznacza po prostu zastosowanie +warstwy liniowej, naszą sieć możemy interpretować jako jednowarstwową +sieć neuronową, co można zilustrować za pomocą następującego diagramu: + +#+CAPTION: Diagram prostego bigramowego neuronowego modelu języka +[[./07_Zanurzenia_slow/bigram1.drawio.png]] + +*** Zanurzenie jako mnożenie przez macierz + +Uzyskanie zanurzenia ($E(w)$) zazwyczaj realizowane jest na zasadzie +odpytania (_look-up_). Co ciekawe, zanurzenie można intepretować jako +mnożenie przez macierz zanurzeń (embeddingów) $E$ o rozmiarze $m \times |V|$ — jeśli słowo będziemy na wejściu kodowali przy użyciu +wektora z gorącą jedynką (_one-hot encoding_), tzn. słowo $w$ zostanie +podane na wejściu jako wektor $\vec{1_V}(w) = [0,\ldots,0,1,0\ldots,0]$ o rozmiarze $|V|$ +złożony z samych zer z wyjątkiem jedynki na pozycji odpowiadającej indeksowi wyrazu $w$ w słowniku $V$. + +Wówczas wzór przyjmie postać: + +$$\vec{y} = \operatorname{softmax}(CE\vec{1_V}(w_{i-1})),$$ + +gdzie $E$ będzie tym razem macierzą $m \times |V|$. + +*Pytanie*: czy $\vec{1_V}(w)$ intepretujemy jako wektor wierszowy czy kolumnowy? + +W postaci diagramu można tę interpretację zilustrować w następujący sposób: + +#+CAPTION: Diagram prostego bigramowego neuronowego modelu języka z wejściem w postaci one-hot +[[./07_Zanurzenia_slow/bigram2.drawio.png]] diff --git a/wyk/08_Neuronowy_ngramowy_model.org b/wyk/08_Neuronowy_ngramowy_model.org new file mode 100644 index 0000000..2c95c29 --- /dev/null +++ b/wyk/08_Neuronowy_ngramowy_model.org @@ -0,0 +1,403 @@ +* Neuronowy n-gramowy model języka + +Omówiony w poprzedniej części neuronowy bigramowy model języka +warunkuje kolejny wyraz jedynie względem bezpośrednio poprzedzającego +— jak w każdym bigramowym modelu przyjmujemy założenie, że $w_i$ +zależy tylko od $w_{i-1}$. Rzecz jasna jest to bardzo duże +ograniczenie, w rzeczywiście bardzo często prawdopodobieństwo +kolejnego wyrazu zależy od wyrazu dwie, trzy, cztery itd. pozycje +wstecz czy w ogólności od wszystkich wyrazów poprzedzających (bez +względu na ich pozycje). + +*Pytanie*: Wskaż zależności o zasięgu większym niż 1 wyraz w zdaniu +_Zatopieni w kłębach dymu cygar i pochyleni nad butelkami z ciemnego +szkła obywatele tej dzielnicy, jedni zakładali się o wygranę lub +przegranę Anglii, drudzy o bankructwo Wokulskiego; jedni nazywali +geniuszem Bismarcka, drudzy — awanturnikiem Wokulskiego; jedni +krytykowali postępowanie prezydenta MacMahona, inni twierdzili, że +Wokulski jest zdecydowanym wariatem, jeżeli nie czymś gorszym…_ + +** Trigramowy neuronowy model języka + +Spróbujmy najpierw rozszerzyć nasz model na trigramy, to znaczy +będziemy przewidywać słowo $w_i$ na podstawie słów $w_{i-2}$ i +$w_{i-1}$. + +Najprostsze rozwiązanie polegałoby na zanurzeniu pary $(w_{i-2}, +w_{i-1})$ w całości i postępowaniu jak w przypadku modelu bigramowego. +Byłoby to jednak zupełnie niepraktyczne, jako że: + +- liczba zanurzeń do wyuczenia byłaby olbrzymia ($|V|^2$ — byłoby to + ewentualnie akceptowalne dla modeli operujących na krótszych + jednostkach niż słowno, np. na znakach), +- w szczególności zanurzenia dla par $(v, u)$, $(u, v)$, $(u, u)$ i + $(v, v)$ nie miałyby ze sobą nic wspólnego. + +*** Konketanacja zanurzeń + +Właściwsze rozwiązanie polega na zanurzenia dalej pojedynczych słów i +następnie ich *konkatenowaniu*. + +Przypomnijmy, że konkatenacja wektorów $\vec{x_1}$ i $\vec{x_2}$ to wektor o rozmiarze +$|\vec{x_1}| + |\vec{x_2}|$ powstały ze „sklejania” wektorów $\vec{x_1}$ i $\vec{x_2}$. +Konkatenację wektorów $\vec{x_1}$ i $\vec{x_2}$ będziemy oznaczać za pomocą $[\vec{x_1}, \vec{x_2}]$. + +Przykład: jeśli $\vec{x_1} = [-1, 2, 0]$ i $\vec{x_2} = [3, -3]$ +wówczas $[\vec{x_1}, \vec{x_2}] = [-1, 2, 0, 3, -3]$ + +Oznacza to, że nasza macierz „kontekstowa” $C$ powinna mieć w modelu trigramowy rozmiar nie +$|V| \times m$, lecz $|V| \times m+m$ = $|V| \times 2m$ i wyjście będę zdefiniowane za pomocą wzoru: + +$$\vec{y} = \operatorname{softmax}(C[E(w_{i-2}),E(w_{i-1})]),$$ + +co można przedstawić za pomocą następującego schematu: + +#+CAPTION: Diagram prostego bigramowego neuronowego modelu języka +[[./08_Neuronowy_ngramowy_model/trigram1.drawio.png]] + +**** Rozbicie macierzy $C$ + +Zamiast mnożyć macierz $C$ przez konkatenację dwóch wektorów, można +rozbić macierz $C$ na dwie, powiedzmy $C_{-2}$ i $C_{-1}$, przemnażać +je osobno przez odpowiadający im wektory i następnie _dodać_ macierze, +tak aby: + +$$C[E(w_{i-2}),E(w_{i-1})] = C_{-2}E(w_{i-2}) + C_{-1}E(w_{i-1}).$$ + +Macierze $C_{-2}$ i $C_{-1}$ będą miały rozmiar $|V| \times m$. + +Przy tym podejściu możemy powiedzieć, że ostatni i przedostatni wyraz +mają swoje osobne macierze o potencjalnie różnych wagach — co ma sens, +jako że na inne aspekty zwracamy uwagę przewidując kolejne słowo na +podstawie wyrazu bezpośrednio poprzedzającego, a na inne — na +podstawie słowa występującego dwie pozycje wcześniej. + +** Uogólnienie na $n$-gramowy model języka dla dowolnego $n$ + +Łatwo uogólnić opisany wyżej trigramowy model języka dla dowolnego $n$. +Uogólniony model można przedstawić za pomocą wzoru: + +$$\vec{y} = \operatorname{softmax}(C[E(w_{i-n+1}),\dots,E(w_{i-1})]),$$ + +gdzie macierz $C$ ma rozmiar $|V| \times nm$ lub za pomocą wzoru: + +$$\vec{y} = \operatorname{softmax}(C_{-(i-n+1)}E(w_{i-n+1}) + \dots + C_{-1}E(w_{i-1}),$$ + +gdzie macierze $C_{-(i-n+1)$, \ldots, $C_{-1}$ mają rozmiary $|V| \times m$. + +Por. diagram: + +#+CAPTION: Diagram prostego n-gramowego neuronowego modelu języka +[[./08_Neuronowy_ngramowy_model/ngram.drawio.png]] + +** Dodanie kolejnej warstwy + +W wypadku trigramowego czy — ogólniej — n-gramowego modelu języka dla +$n \geq 3$ warto dodać kolejną (*ukrytą*) warstwę, na którą będziemy rzutować +skonkatenowane embeddingi, zanim zrzutujemy je do długiego wektora +prawdopodobieństw. + +Zakładamy, że warstwa ukryta zawiera $h$ neuronów. Wartość $h$ powinna być mniejsza +niż $nm$ (a może nawet od $m$). + +*Pytanie*: Dlaczego wartość $h > nm$ nie jest racjonalnym wyborem? + +*Pytanie*: Dlaczego dodanie kolejnej warstwy nie ma sensu dla modelu bigramowego? + +*** Funkcja aktywacji + +Aby warstwa ukryta wnosiła coś nowego, na wyjściu z tej funkcji musimy +zastosować nieliniową *funkcji aktywacji*. Zazwyczaj jako funkcji +aktywacji w sieciach neuronowych używa się funkcji ReLU albo funkcji +sigmoidalnej. W prostych neuronowych modelach języka sprawdza się też +*tangens hiperboliczny* (tgh, w literaturze anglojęzycznej tanh): + +$$\operatorname{tgh}(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}}.$$ + +#+BEGIN_SRC python :session mysession :results file +import matplotlib.pyplot as plt +import torch +import torch.nn as nn + +x = torch.linspace(-5,5,100) +plt.xlabel("x") +plt.ylabel("y") +a = torch.Tensor(x.size()[0]).fill_(2.) +m = torch.stack([x, a]) +plt.plot(x, nn.functional.tanh(m)[0]) +fname = '08_Neuronowy_ngramowy_model/tanh.png' +plt.savefig(fname) +fname +#+END_SRC + +#+RESULTS: +[[file:08_Neuronowy_ngramowy_model/tanh.png]] + +**** Tangens hiperboliczny zastosowany dla wektora + +Tangens hiperboliczny wektora będzie po prostu wektorem tangensów +hiperbolicznych poszczególnych wartości. + +#+BEGIN_SRC python :session mysession :results file + import torch + import torch.nn as nn + + v = torch.Tensor([-100, -2.0, 0.0, 0.5, 1000.0]) + nn.functional.tanh(v) +#+END_SRC + +#+RESULTS: +[[file:tensor([-1.0000, -0.9640, 0.0000, 0.4621, 1.0000])]] + +*** Wzór i schemat dwuwarstwowego n-gramowego neuronowego modelu języka + +Dwuwarstwowy model języka będzie określony następującym wzorem: + +$$\vec{y} = \operatorname{softmax}(C\operatorname{tgh}(W[E(w_{i-n+1}),\dots,E(w_{i-1})])),$$ + +gdzie: + +- $W$ jest wyuczalną macierzą wag o rozmiarze $h \times nm$, +- $C$ będzie macierzą o rozmiarze $|V| \times h$. + +Zmodyfikowaną się można przedstawić za pomocą następującego schematu: + +#+CAPTION: Dwuwarstwowy n-gramowy neuronowy model języka +[[./08_Neuronowy_ngramowy_model/ngram-tgh.drawio.png]] + +*** Liczba wag w modelu dwuwarstwowym + +Na wagi w modelu dwuwarstwowym składają się: + +- zanurzenia: $m|V|$, +- wagi warstwy ukrytej: $hnm$, +- wagi warstwy wyjściowej: $|V|h$, + +a zatem łącznie: + +$$m|V| + hnm + |V|h$$ + +Jeśli $h \approx m$ (co jest realistyczną opcją), wówczas otrzymamy oszacowanie: + +$$O(m|V| + nm^2).$$ + +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 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-gramowym modelu 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 zapewne 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 +cały ciąg słów poprzedzających przewidywane 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 +dodawanie 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 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 wielu przypadkach równa zero!). + +Jeśli teraz zanurzenia będzie 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$-ty wyraz w 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 w wyszukiwaniu 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 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 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, zaś wszystkie 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})]),$$ + +lepiej z dodatkową warstwą ukrytą: + +$$y = \operatorname{softmax}(C\operatorname{tg}(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$. + +** 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/08_Neuronowy_ngramowy_model/bow1.drawio new file mode 100644 index 0000000..0d30d50 --- /dev/null +++ b/wyk/08_Neuronowy_ngramowy_model/bow1.drawio @@ -0,0 +1 @@ +7VpZd9o4GP01PJLjDWM/BgKZmaaTtjmnU+alR8XCVjCWI4sY8utHQjLexNKwk3kB6Wr/tvvJdsPsTmb3BMTBZ+zBsGFo3qxh3jUMw7V09suBuQB027UE4hPkSSwHntAblKAm0SnyYFLqSDEOKYrL4BBHERzSEgYIwWm52wiH5VVj4MMa8DQEYR39B3k0EKhjtHP8D4j8IFuZHVC0TEDWWU6RBMDDqYAWhzN7DbNLMKaiNJl1YciFl8lFSKC/onW5MQIjutWAbu/xCwqcTz//fvZerAc4TN6aUhkJnWcHhh47v6xiQgPs4wiEvRztEDyNPMhn1Vgt7/OAccxAnYHPkNK5VCaYUsyggE5C2QpniP7gw29asjaQk/Hy3axYmWeViJL5j2JlkM/Aq/mwRS0bN8IRlRvhIu+I8/JDrhRjpi88JUPZ6+vj5/Txfjy+f/gz+vbS96nuPDQNaY6A+JCukbG+VDbzEognkO2PjSMwBBS9lvcBpLn6y365RllBKlWt4HWbfAXhVK5U03iuT66cNEAUPsVgcfSUOXVZdyvF9QoJhbMCVD+xbDU06dsyJpiurKe5g+mmxIKCc2XY3oVkKoRkh2zVjodeWdGni5MzZ7HSn412BzGxtu8EkPVkC5c6V4TMZEPLkkwowWPYxSEmDIlwxH1rhMKwAoEQ+RGrDpnEIcM7XNKIRahb2TBBnrdwTJXqys66B+1ZFe1ZTl179jGV515KCHt/pLHrkUbZz1FGt1OFHvv8Qo/ulo1Xb9WNd2nQR7Fe/QIZOCfdQTbfGgZ+v9k7W5q9bp6V3Tvnb/enN3ttJeWijENDjMfNaVwgWHT99GrrFXq1Tkyvej2DTPCITsDsepVgVZSga4oMVTuqFrZKUQl+Gze6rYZjAI/LgIDUwzH+heACtRKaAn4ZD/nvGHCw0/Wgjxv87ijSW8TLrS6K2P/39SmuIkUW0C9SRT5Kdmw4BmPHou0sQ13Bdgy3dRjbUd4/zYvLMJYpxTHv+EPsPPe/uoM++itwv7eM3sTrZLI7mzv+uk2eU8LR0k94x1cK6ePm2ftLi+XQLxixJXJdt91yytKu6FB4mRxVfEK5aSKjMpFww9pEt4SAeaFbzDskv7FhS/u9fZX7s4LYQW6cS+G+315Vt+f1z6T0D/tMqmW1K6x7zKdSSkKoh+ArCzf7Id30SXvRSWwPevff/gWfsNW3ps3sPc+5kO66TZ4T6Zp22QmOSrpKIV1e6nkppGsZbjXkvZd2FVMdiHgVK22g3o0jdiZfpd3Wyffm5uZ6+dPcIls/FHsq5d9WBFeR6ehZhnOtujDM02UySl04/8fwA8XwpR53jeC1iQ4Uv2vrbIjeG/rvHLuVWWHrUux1Pxn0QTLjFWRsVmhC287M9qVZVcotLrpclCWd2y9TnDU0k4WQb1kHZuKzvDG/GBefU4u5VlyZ2S5RnKwig4JBgCQWn6ON0IwbVmcFDyj0u/VrCdOsU4OloAbrUJfc9qW42mkvuUrZbftNh72jK++k4MN8wrGTE1QvuUb7iJdcpZDq7/tTQOSrthBFCPPSGaeru8UkqxyTDMXrrn29KmXV/HNZQSr5R8dm7z8= \ No newline at end of file diff --git a/wyk/08_Neuronowy_ngramowy_model/bow2.drawio b/wyk/08_Neuronowy_ngramowy_model/bow2.drawio new file mode 100644 index 0000000..3d99bdb --- /dev/null +++ b/wyk/08_Neuronowy_ngramowy_model/bow2.drawio @@ -0,0 +1 @@ +5Vpdc+I2FP01nmkfkkGyAftxcYC2m22ym5lt05cdrS1sgbGIEDHw6ythGX/IEDYECOQBkI6vJOueo6srDYbpjud9hibhF+rjyIANf26YNwaEjgXEtwQWKQBajpUiASO+wnLggSyxAhsKnREfT0uGnNKIk0kZ9GgcY4+XMMQYTcpmAxqVR52gAGvAg4ciHf2H+DxMURu2c/wPTIIwG1lMMH0yRpmx6mIaIp8mKbSanNk1TJdRytPSeO7iSDov80vqgd6Gp+sXYzjmuzT4evclueuPRv3bP+NvT72AA/v2ykx7eUbRTE3YgK1I9NfxybMoBrJoQMGZlfww2h1yBY32TQpklmLIkvFqsnyReZDjucRDPo4EAERxyhkdYZdGlAkkprGw7AxIFFUgFJEgFlVPzBALvPOMGSeCm0/qwZj4vhymk4SE44cJ8uSYiVCiwBidxT6Wk2+sX6voLeVA2SeeFyDlvT6mY8zZQpiop1ZLaVJp2bJVPcmFkZmEBU2YCkNKisG655wtUVCE/QJ5juZr7AvxqiplPKQBjVHUzdGKV3KbW0onip4h5nyhViKacVomD88J/1c2v26q2qPqTJZv5sXK4iXnT+mMeXjLFFtqySMWYL7Fzk7t5Py3UslwhDh5Li/uNyemVbOqKkzlPIAN+i34/A3EC5yyeEFTF+9a0EdRL7DORb7C62xRaCSrj1l/spI3W9X2l729o+yB+a50b79/3Z9e9lBz0pQO+BjNL3jnBJXg09BZWGPHYWGnxIfR5chwm4YNkS99wFDi0wn9SfAKtaY8QTK5jeT3CEmw4/o4oIbMxdKkichy0yWx+P2+PXGqSbxS6CerIh8l5xKZtoi5Re1cAV070GkeRjsetYe9r85jj/wVOt+bsDv2O7W7+/acGXzYnLlptSv8HTNrTh4aT4BNWo/d/rf/0Gdq9axZduQ5q7TDcpxfSTxWtXvMiPCYFMF2NivJQzntAG737p6E9ucffw/9J+sWe9NlevDYI8lQTe8pEa+S7xLQqWqlXRFBmjipdsWz7stdwUpX6SS1rj4xhhYFs4k0mGqiW8/69TrUw8j19fXlRgKzkgOYzonjQLsmjKcxG2Sx+lK5gObpYnJtQGmeS0we0JirDqWDOxsi6AtnvmMGVbOy7Bq7xcGXO6qKYUNsfkW4rHVP+1wk8trbggNKa9dbtNaeEtwrBhzm0kwLijUe3LxntcpxErZrzq3mMQOlfsOSIKaOoRGJCV2VfnN/f9eb116kWFY5EMGaAyGAxyTF0UgRI7nFD6jU5QdusKmzPdQnG2uVebqNky+46m2dWZOYHOy6rpbbTFxnlKbvF/Iq2/zJU0MANAISPOKULZbIG8poxweXS4el35+YOiEHuzutZ0S/wV6ieMaWOCZY4EM0khegtRtT9wNtTGYNU/C4TOm33GyJ/JG8k04X0eWyUT2u1MSxIy8bSyMjMFzLcBzh/o9HSG0u/UaEiGr+v5r0AJr/O8ns/g8= \ No newline at end of file diff --git a/wyk/08_Neuronowy_ngramowy_model/bow2.drawio.png b/wyk/08_Neuronowy_ngramowy_model/bow2.drawio.png new file mode 100644 index 0000000..4debc2e Binary files /dev/null and b/wyk/08_Neuronowy_ngramowy_model/bow2.drawio.png differ diff --git a/wyk/08_Neuronowy_ngramowy_model/ngram-tgh.drawio b/wyk/08_Neuronowy_ngramowy_model/ngram-tgh.drawio new file mode 100644 index 0000000..099793b --- /dev/null +++ b/wyk/08_Neuronowy_ngramowy_model/ngram-tgh.drawio @@ -0,0 +1 @@ +7Vpbc5s4FP41PCZjxMXwWBzb6W4y3TQz3c2+dFSjgGoZUVkOdn99pSDMTb6kviVxZzKxdDi68J2PcxEYVm8yHzKYxrc0RMQAnXBuWFcGAKYNOuJHShZK4ttWLokYDpWsFNzjn0gJ1cBohkM0rSlySgnHaV04okmCRrwmg4zRrK72SEl91RRGqCW4H0HSlv6LQx7nUg90S/k1wlFcrGy6fn5lAgtlNcU0hiHNctHzzVl9w+oxSnnemsx7iEj0ClxyBAYrri43xlDCtxlw9+k2+zQcj4c3H5PPPwYRN72bC5DP8gTJTN2w2ixfFAgwOktCJCcxDSvIYszRfQpH8momjC5kMZ8Qdbm9KbXPJ8Q4mldEapNDRCeIs4VQUVeBpUyvKANs1c9K/M1CJ65gX8igMnm0nLpERTQUMC8AydKA5BKxahDiJ9GM+POd2+Iv+2p0Ayxg7V7lgkJTLFxTboAssOF1JKec0THqUUKZkCQ0EZrBIyakIYIER4nojgTiSMgDiTQWBP6gLkxwGMpltKYrjdvZj/WWT7yynmW1rece03h+C2sUiidcdSnjMY1oAkm/lDZQKXVuKE2Veb4jzhfKXcEZp3XjoTnm/8nhl47qPajJZPtqXu0sNoE/pTM2Qmtu0VV+EbII8TV6Xq4n73+tKRkikOOnugfcu2HcV+h6OnXyXjht8treMdlr2m+FvgJ1tqgMkt2HYj7ZKYc993anvbcl7U3rVfHeewO8N09P/M7KoIuLKEooHV/M0kqIxe8/wLp2Iz3yTxxgTbNlqQyyKc+gEBKcYCpb79UcdjNb7Wiy1c5R7dHO6af0kU/g/B1bATQd2OnNsFXVwOjPsdFzDA/AUILAYBbSlH7D6Flqq8coJPL/GEph0AtRRA15x3nFgWXb6eFE/H5ZX3VoqpZc9I01JedSsAAfiISlRp7l4UOFPMB3DkOeEfW+D+78hwH+K/a/OKA/CQNtxXniHMEx6ygdtSzXgnS+qfH+Mlk19B+KxRKlrb1GEQ8aNsxzczWqeua0YSLQbUyUJ++tiT4wBhcVtVQqTLffcJEUbb2vur5o5DsoybkE9/f5qit41x8jmWd7jOQ4DQYe8RiJumlggX7wlc5ur3vh3dXH61FxUHoqZwOO5W0armVjZZ7dd36YLHUf+sPP/8O/qT2wZxfFQX2lMteCeqw6fN0mX1OMtbwTxlgtSNafGHugGGvbfiPv/O0o257qUHFWs9KGSLtxxM6xVsvbdqy9vLx8v+HSah4KaRzHocKlFv/ui3MdUb0G55vvALfhC46Y72gN6P1x/Ady/MsD213dfnOiQzn91jobXP4G/Z0dvjaV7J4HX1+anq9Ou7dIz50d+b9TwXWY9LzlkTUIro6yTt1Jm5pzQrN7zKrUfSus3yN7nTZ7V39fcCr2Oi328ihuG4sQnE5XJRkVyOE0zT9ue8Rzabp9sLn5zkT3ylfDZftQXG6/F5+N2YLLFx9v633iblaxukW9VPUqzn68iuiWHxzmobf8btPq/wI= \ No newline at end of file diff --git a/wyk/08_Neuronowy_ngramowy_model/ngram-tgh.drawio.png b/wyk/08_Neuronowy_ngramowy_model/ngram-tgh.drawio.png new file mode 100644 index 0000000..c5407b2 Binary files /dev/null and b/wyk/08_Neuronowy_ngramowy_model/ngram-tgh.drawio.png differ diff --git a/wyk/08_Neuronowy_ngramowy_model/ngram.drawio b/wyk/08_Neuronowy_ngramowy_model/ngram.drawio new file mode 100644 index 0000000..0c0042b --- /dev/null +++ b/wyk/08_Neuronowy_ngramowy_model/ngram.drawio @@ -0,0 +1 @@ +7VpZc9owEP41fkwGn9iPhQA90mmTzKRNXzoqVmwV4XWFiKG/vhKW4zMYypUmfQHp0+r6dtlDg2b2p4sRQ3H4EXxMNaPjLzTzQjMMz9LFpwSWKaA7npUiASO+wnLghvzGCuwodE58PCsJcgDKSVwGxxBFeMxLGGIMkrLYPdDyrjEKcA24GSNaR78Qn4cp6hrdHH+LSRBmO4sLpiNTlAmrJWYh8iFJodXlzIFm9hkAT1vTRR9TSV7GS8rA8InRx4MxHPFNJlx9+ph8Gk0mo8t30fWvYcB19/LMSFd5QHSuLqwOy5cZAwzmkY/lIrpm9pKQcHwTo7EcTYTOBRbyKVXD9UOpcz5gxvGiAKlDjjBMMWdLIaJGDVOpXpmMYal+kvOvZzJhgfsMQ0rlwePSOSuioYjZgiSzgSSHil17PnkQzYCvbi4M20q+a90eEbR2L1IgkxQbl4QrJAtueJnJGWcwwX2gwAQSQSQke/eE0gqEKAki0R0LxrHAe5JpIgz4jRqYEt+X2zSqLlduZz/as4yy9kyzrj3nmMrzalxjX/zCVRcYDyGACNFBjlZYyWUuAWKlnp+Y86VyV2jOoaw8vCD8q5x+bqvenVpMti8Wxc6yjfwZzNkYr7mio/wiYgHma+TcVE7ef60qGaaIk4eyB9y7Ypzn53p0r2y8ul03Xss9pvXq1r9ivoJ1tixMkt27bD3Zyaeterubvbuh2evms7J79/nb/enNvvNkyCVZDKUAk7N5XAiw5OWHV8eqJEfeicOrrtc0lSA24wkSICURAdl6qeqwqrlqpyFX7RxVH/WMfgb3fIoWL1gLeiVsn14LG5UMDH5PtL6tuQbyJQcMJT7E8IPgFWqpX5FP5ecESbDX93EAmkyy03KDyLbdJ5H4vl1fcjSULCn0g1WR11KtiEJeZCtF23kMPQXbMTz7MLYzBvfn8Mq7G5L3oXdrG4Op32usNk+cH9h6maSjluSNJL3etHh/Waya+hmI2CLXtVsp4I2KDtO8XM0qvje1LGR0KwuliXttoTeMoWVBLJYCs80PnKVEG5+rLC8a6Qly43wk9+/ttanYXf+EpL/aJyTbrljgEZ+QkpvOL53Fzt1gdP0NfQBraM3P7NM6G2PbN6SKi2itrhsvnT22t1XXzo5eaCfN1IuPfcTKmqk2MPik9ZruCWNlI0nm64iV7Va/91hpWV4lffzraFlf6lDxsmGnlojZOmPnmNlot/WYeX5+/qzD3m6Oo/q00+A4jhr2ulvnLKII7f3TectOCjScii84dd7i/nf8B3L8hrenIqm60KGcfm2fFpffIr+twxfd/I8OqXj+dxFz8Ac= \ No newline at end of file diff --git a/wyk/08_Neuronowy_ngramowy_model/ngram.drawio.png b/wyk/08_Neuronowy_ngramowy_model/ngram.drawio.png new file mode 100644 index 0000000..3686ee8 Binary files /dev/null and b/wyk/08_Neuronowy_ngramowy_model/ngram.drawio.png differ diff --git a/wyk/08_Neuronowy_ngramowy_model/tanh.png b/wyk/08_Neuronowy_ngramowy_model/tanh.png new file mode 100644 index 0000000..9da7579 Binary files /dev/null and b/wyk/08_Neuronowy_ngramowy_model/tanh.png differ diff --git a/wyk/08_Neuronowy_ngramowy_model/trigram1.drawio b/wyk/08_Neuronowy_ngramowy_model/trigram1.drawio new file mode 100644 index 0000000..7b43c21 --- /dev/null +++ b/wyk/08_Neuronowy_ngramowy_model/trigram1.drawio @@ -0,0 +1 @@ +5VldU+IwFP01fdTpJ5RHQXB3R8d1ndldn3YiiW0k7WVDsOCv34SmtKWBRVERfYHk5Puec3OT1PJ6yeyMo3F8AZgwy7XxzPJOLdft+I78VcA8B5xWx8+RiFOssRK4po9Eg7ZGpxSTSa2iAGCCjuvgENKUDEUNQ5xDVq92B6w+6hhFpAFcDxFror8oFnGOhm67xL8QGsXFyHKBeUmCisq6i0mMMGQ5tFic17e8HgcQeSqZ9QhTxivskltgsKZ0OTFOUrFNgyGE94Orzs2Afos7PwO3n+DukV7GRMyLBRMs16+zwEUMEaSI9Uu0y2GaYqJ6tWWurHMOMJagI8F7IsRck4mmAiQUi4TpUjKj4nclfaO7UunTWTUzLzKp4HPVxD4OiuxN0YPKlM0WuaJdvjq1pLVGK9iBKR/qWleXF9nl2Wh0dv41/fF3EAknPD9ytfgQj4jYUK+1pFb6BIGEyPnIdpwwJOhDfR5IizNa1iv5kwlNoZnOTZN8QGyqR2rwW7KnjJfFVJDrMVosPZMuXGdqrbkeCBdkVoGaK9alnq09We8Arq/zWelOjqexuOJKBbaLkYyab9rkDTWvBXwoqve2VL27o+oXTU84R/NKhTHQVEwqPX9XQEVaraAmLa++A8pE3mMpleXUnu9insHFWkzapovpg0xGYsGAjHJ+9sdqd6m0Tfs0B4qacuBa5RU5Ss8SdfVMBIcR6QEDLpEUUqXJO8rYCoQYjVKZHUqiicS7yk+pjGYnuiChGC8EbXL8ushfwvd920hQ1fdbr+T6RvI6h+L6z/fY1pYeGxp3iX0Frtb7C1zFeaoQrxM0xeu7b6lexz8U+e4WuZ4j+3BL2Tveu9J9+P51v3/Z22tDLi1iKAMYHU3HlQBLP354DZyVo3W45/DqOA2mMsQnIkMSZDSloFIflQ7fW6HDNtx07Dflo3kfnMCdSNDsA7Ow4hTO/lnY6srA4XFk9QIrdBFWNuAowzCGW0oWqK+9CDP1O0IK7PYwicBSm3F+3aAqHfRoKv9/br5yGK4sOXTLV5HPcltxQ/e4fqFchp6KdtxO8DraMb5VmKSz+/mgYROD5bbf5vb+oPNJjsX/f9DZ9QGm8cLih/6x+Y1l2Ul+MtftSiK36Mptr3SVH94bXT31WcgwUsd+6tzqLXZ+SjLq1nTp3fyU5BzyU9Juu06wRocvf9aV2fKrTM51+W3L6/8D \ No newline at end of file diff --git a/wyk/08_Neuronowy_ngramowy_model/trigram1.drawio.png b/wyk/08_Neuronowy_ngramowy_model/trigram1.drawio.png new file mode 100644 index 0000000..13d6e9c Binary files /dev/null and b/wyk/08_Neuronowy_ngramowy_model/trigram1.drawio.png differ