This commit is contained in:
Filip Gralinski 2022-07-06 08:28:27 +02:00
parent b343653f5e
commit 1e50331206
4 changed files with 411 additions and 46 deletions

Binary file not shown.

View File

@ -7,7 +7,7 @@
"![Logo 1](https://git.wmi.amu.edu.pl/AITech/Szablon/raw/branch/master/Logotyp_AITech1.jpg)\n", "![Logo 1](https://git.wmi.amu.edu.pl/AITech/Szablon/raw/branch/master/Logotyp_AITech1.jpg)\n",
"<div class=\"alert alert-block alert-info\">\n", "<div class=\"alert alert-block alert-info\">\n",
"<h1> Modelowanie języka</h1>\n", "<h1> Modelowanie języka</h1>\n",
"<h2> 7. <i>Zanurzenia słów</i> [wykład]</h2> \n", "<h2> 09. <i>Zanurzenia słów (Word2vec)</i> [wykład]</h2> \n",
"<h3> Filip Graliński (2022)</h3>\n", "<h3> Filip Graliński (2022)</h3>\n",
"</div>\n", "</div>\n",
"\n", "\n",
@ -19,7 +19,7 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"## Zanurzenia słów\n", "## Zanurzenia słów (Word2vec)\n",
"\n" "\n"
] ]
}, },
@ -122,7 +122,7 @@
"po prostu będziemy rozpatrywać $|V|$ najczęstszych wyrazów, pozostałe zamienimy\n", "po prostu będziemy rozpatrywać $|V|$ najczęstszych wyrazów, pozostałe zamienimy\n",
"na specjalny token `<unk>` reprezentujący nieznany (*unknown*) wyraz.\n", "na specjalny token `<unk>` reprezentujący nieznany (*unknown*) wyraz.\n",
"\n", "\n",
"Aby utworzyć taki słownik użyjemy gotowej klasy `Vocab` z pakietu torchtext:\n", "Aby utworzyć taki słownik, użyjemy gotowej klasy `Vocab` z pakietu torchtext:\n",
"\n" "\n"
] ]
}, },
@ -313,33 +313,48 @@
"next(iter(DataLoader(train_dataset, batch_size=5)))" "next(iter(DataLoader(train_dataset, batch_size=5)))"
] ]
}, },
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"None"
]
}
],
"source": [
"device = 'cuda'\n",
"model = SimpleBigramNeuralLanguageModel(vocab_size, embed_size).to(device)\n",
"data = DataLoader(train_dataset, batch_size=5000)\n",
"optimizer = torch.optim.Adam(model.parameters())\n",
"criterion = torch.nn.NLLLoss()\n",
"\n",
"model.train()\n",
"step = 0\n",
"for x, y in data:\n",
" x = x.to(device)\n",
" y = y.to(device)\n",
" optimizer.zero_grad()\n",
" ypredicted = model(x)\n",
" loss = criterion(torch.log(ypredicted), y)\n",
" if step % 100 == 0:\n",
" print(step, loss)\n",
" step += 1\n",
" loss.backward()\n",
" optimizer.step()\n",
"\n",
"torch.save(model.state_dict(), 'model1.bin')"
]
},
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
" device = 'cuda'\n", "Policzmy najbardziej prawdopodobne kontynuacje dla zadanego słowa:\n",
" model = SimpleBigramNeuralLanguageModel(vocab_size, embed_size).to(device)\n",
" data = DataLoader(train_dataset, batch_size=5000)\n",
" optimizer = torch.optim.Adam(model.parameters())\n",
" criterion = torch.nn.NLLLoss()\n",
" \n",
" model.train()\n",
" step = 0\n",
" for x, y in data:\n",
" x = x.to(device)\n",
" y = y.to(device)\n",
" optimizer.zero_grad()\n",
" ypredicted = model(x)\n",
" loss = criterion(torch.log(ypredicted), y)\n",
" if step % 100 == 0:\n",
" print(step, loss)\n",
" step += 1\n",
" loss.backward()\n",
" optimizer.step()\n",
" \n",
" torch.save(model.state_dict(), 'model1.bin')\n",
"\n",
"Policzmy najbardziej prawdopodobne kontynuację dla zadanego słowa:\n",
"\n" "\n"
] ]
}, },
@ -502,7 +517,7 @@
"warstwy liniowej, naszą sieć możemy interpretować jako jednowarstwową\n", "warstwy liniowej, naszą sieć możemy interpretować jako jednowarstwową\n",
"sieć neuronową, co można zilustrować za pomocą następującego diagramu:\n", "sieć neuronową, co można zilustrować za pomocą następującego diagramu:\n",
"\n", "\n",
"![img](./07_Zanurzenia_slow/bigram1.drawio.png \"Diagram prostego bigramowego neuronowego modelu języka\")\n", "![img](./09_Zanurzenia_slow/bigram1.drawio.png \"Diagram prostego bigramowego neuronowego modelu języka\")\n",
"\n" "\n"
] ]
}, },
@ -535,7 +550,7 @@
"\n", "\n",
"W postaci diagramu można tę interpretację zilustrować w następujący sposób:\n", "W postaci diagramu można tę interpretację zilustrować w następujący sposób:\n",
"\n", "\n",
"![img](./07_Zanurzenia_slow/bigram2.drawio.png \"Diagram prostego bigramowego neuronowego modelu języka z wejściem w postaci one-hot\")\n", "![img](./09_Zanurzenia_slow/bigram2.drawio.png \"Diagram prostego bigramowego neuronowego modelu języka z wejściem w postaci one-hot\")\n",
"\n" "\n"
] ]
} }
@ -556,7 +571,7 @@
"name": "python", "name": "python",
"nbconvert_exporter": "python", "nbconvert_exporter": "python",
"pygments_lexer": "ipython3", "pygments_lexer": "ipython3",
"version": "3.10.2" "version": "3.10.5"
}, },
"org": null "org": null
}, },

View File

@ -1,4 +1,4 @@
* Zanurzenia słów * Zanurzenia słów (Word2vec)
W praktyce stosowalność słowosieci okazała się zaskakująco W praktyce stosowalność słowosieci okazała się zaskakująco
ograniczona. Większy przełom w przetwarzaniu języka naturalnego przyniosły ograniczona. Większy przełom w przetwarzaniu języka naturalnego przyniosły
@ -47,9 +47,9 @@ ograniczony. Zazwyczaj jest to liczba rzędu kilkudziesięciu wyrazów —
po prostu będziemy rozpatrywać $|V|$ najczęstszych wyrazów, pozostałe zamienimy po prostu będziemy rozpatrywać $|V|$ najczęstszych wyrazów, pozostałe zamienimy
na specjalny token ~<unk>~ reprezentujący nieznany (/unknown/) wyraz. na specjalny token ~<unk>~ reprezentujący nieznany (/unknown/) wyraz.
Aby utworzyć taki słownik użyjemy gotowej klasy ~Vocab~ z pakietu torchtext: Aby utworzyć taki słownik, użyjemy gotowej klasy ~Vocab~ z pakietu torchtext:
#+BEGIN_SRC python :session mysession :exports both :results raw drawer #+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
from itertools import islice from itertools import islice
import regex as re import regex as re
import sys import sys
@ -84,7 +84,7 @@ Aby utworzyć taki słownik użyjemy gotowej klasy ~Vocab~ z pakietu torchtext:
16 16
:end: :end:
#+BEGIN_SRC python :session mysession :exports both :results raw drawer #+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
vocab.lookup_tokens([0, 1, 2, 10, 12345]) vocab.lookup_tokens([0, 1, 2, 10, 12345])
#+END_SRC #+END_SRC
@ -97,7 +97,7 @@ vocab.lookup_tokens([0, 1, 2, 10, 12345])
Naszą prostą sieć neuronową zaimplementujemy używając frameworku PyTorch. Naszą prostą sieć neuronową zaimplementujemy używając frameworku PyTorch.
#+BEGIN_SRC python :session mysession :exports both :results raw drawer #+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
from torch import nn from torch import nn
import torch import torch
@ -132,7 +132,7 @@ Teraz wyuczmy model. Wpierw tylko potasujmy nasz plik:
shuf < opensubtitlesA.pl.txt > opensubtitlesA.pl.shuf.txt shuf < opensubtitlesA.pl.txt > opensubtitlesA.pl.shuf.txt
#+END_SRC #+END_SRC
#+BEGIN_SRC python :session mysession :exports both :results raw drawer #+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
from torch.utils.data import IterableDataset from torch.utils.data import IterableDataset
import itertools import itertools
@ -164,7 +164,7 @@ shuf < opensubtitlesA.pl.txt > opensubtitlesA.pl.shuf.txt
:results: :results:
:end: :end:
#+BEGIN_SRC python :session mysession :exports both :results raw drawer #+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
from torch.utils.data import DataLoader from torch.utils.data import DataLoader
next(iter(train_dataset)) next(iter(train_dataset))
@ -175,7 +175,7 @@ shuf < opensubtitlesA.pl.txt > opensubtitlesA.pl.shuf.txt
(2, 5) (2, 5)
:end: :end:
#+BEGIN_SRC python :session mysession :exports both :results raw drawer #+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
from torch.utils.data import DataLoader from torch.utils.data import DataLoader
next(iter(DataLoader(train_dataset, batch_size=5))) next(iter(DataLoader(train_dataset, batch_size=5)))
@ -186,7 +186,7 @@ shuf < opensubtitlesA.pl.txt > opensubtitlesA.pl.shuf.txt
[tensor([ 2, 5, 51, 3481, 231]), tensor([ 5, 51, 3481, 231, 4])] [tensor([ 2, 5, 51, 3481, 231]), tensor([ 5, 51, 3481, 231, 4])]
:end: :end:
#+BEGIN_SRC python :session mysession :exports both :results raw drawer #+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
device = 'cuda' device = 'cuda'
model = SimpleBigramNeuralLanguageModel(vocab_size, embed_size).to(device) model = SimpleBigramNeuralLanguageModel(vocab_size, embed_size).to(device)
data = DataLoader(train_dataset, batch_size=5000) data = DataLoader(train_dataset, batch_size=5000)
@ -215,9 +215,9 @@ shuf < opensubtitlesA.pl.txt > opensubtitlesA.pl.shuf.txt
None None
:end: :end:
Policzmy najbardziej prawdopodobne kontynuację dla zadanego słowa: Policzmy najbardziej prawdopodobne kontynuacje dla zadanego słowa:
#+BEGIN_SRC python :session mysession :exports both :results raw drawer #+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
device = 'cuda' device = 'cuda'
model = SimpleBigramNeuralLanguageModel(vocab_size, embed_size).to(device) model = SimpleBigramNeuralLanguageModel(vocab_size, embed_size).to(device)
model.load_state_dict(torch.load('model1.bin')) model.load_state_dict(torch.load('model1.bin'))
@ -240,7 +240,7 @@ Policzmy najbardziej prawdopodobne kontynuację dla zadanego słowa:
Teraz zbadajmy najbardziej podobne zanurzenia dla zadanego słowa: Teraz zbadajmy najbardziej podobne zanurzenia dla zadanego słowa:
#+BEGIN_SRC python :session mysession :exports both :results raw drawer #+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
vocab = train_dataset.vocab vocab = train_dataset.vocab
ixs = torch.tensor(vocab.forward(['kłopot'])).to(device) ixs = torch.tensor(vocab.forward(['kłopot'])).to(device)
@ -257,7 +257,7 @@ Teraz zbadajmy najbardziej podobne zanurzenia dla zadanego słowa:
[('.', 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), ('<unk>', 0, 0.014571071602404118), ('...', 15, 0.01453721895813942), ('</s>', 1, 0.011769450269639492)] [('.', 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), ('<unk>', 0, 0.014571071602404118), ('...', 15, 0.01453721895813942), ('</s>', 1, 0.011769450269639492)]
:end: :end:
#+BEGIN_SRC python :session mysession :exports both :results raw drawer #+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
cos = nn.CosineSimilarity(dim=1, eps=1e-6) cos = nn.CosineSimilarity(dim=1, eps=1e-6)
embeddings = model.model[0].weight embeddings = model.model[0].weight
@ -313,7 +313,7 @@ warstwy liniowej, naszą sieć możemy interpretować jako jednowarstwową
sieć neuronową, co można zilustrować za pomocą następującego diagramu: sieć neuronową, co można zilustrować za pomocą następującego diagramu:
#+CAPTION: Diagram prostego bigramowego neuronowego modelu języka #+CAPTION: Diagram prostego bigramowego neuronowego modelu języka
[[./07_Zanurzenia_slow/bigram1.drawio.png]] [[./09_Zanurzenia_slow/bigram1.drawio.png]]
*** Zanurzenie jako mnożenie przez macierz *** Zanurzenie jako mnożenie przez macierz
@ -335,4 +335,4 @@ gdzie $E$ będzie tym razem macierzą $m \times |V|$.
W postaci diagramu można tę interpretację zilustrować w następujący sposób: 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 #+CAPTION: Diagram prostego bigramowego neuronowego modelu języka z wejściem w postaci one-hot
[[./07_Zanurzenia_slow/bigram2.drawio.png]] [[./09_Zanurzenia_slow/bigram2.drawio.png]]

File diff suppressed because one or more lines are too long