AI-For-Beginners/translations/lt/lessons/5-NLP/14-Embeddings/EmbeddingsPyTorch.ipynb

724 lines
28 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Įterpimai\n",
"\n",
"Ankstesniame pavyzdyje dirbome su aukštos dimensijos žodžių maišo vektoriais, kurių ilgis yra `vocab_size`, ir aiškiai konvertavome iš žemos dimensijos pozicinių reprezentacijų vektorių į retą vieno elemento reprezentaciją. Ši vieno elemento reprezentacija nėra efektyvi atminties požiūriu, be to, kiekvienas žodis yra traktuojamas nepriklausomai nuo kitų, t. y. vieno elemento užkoduoti vektoriai neišreiškia jokio semantinio panašumo tarp žodžių.\n",
"\n",
"Šiame skyriuje toliau nagrinėsime **News AG** duomenų rinkinį. Pradėkime įkeldami duomenis ir pasinaudodami kai kuriomis ankstesnio užrašų knygelės apibrėžtimis.\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Loading dataset...\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"d:\\WORK\\ai-for-beginners\\5-NLP\\14-Embeddings\\data\\train.csv: 29.5MB [00:01, 18.8MB/s] \n",
"d:\\WORK\\ai-for-beginners\\5-NLP\\14-Embeddings\\data\\test.csv: 1.86MB [00:00, 11.2MB/s] \n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Building vocab...\n",
"Vocab size = 95812\n"
]
}
],
"source": [
"import torch\n",
"import torchtext\n",
"import numpy as np\n",
"from torchnlp import *\n",
"train_dataset, test_dataset, classes, vocab = load_dataset()\n",
"vocab_size = len(vocab)\n",
"print(\"Vocab size = \",vocab_size)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Kas yra įterpimas?\n",
"\n",
"Įterpimo (**embedding**) idėja yra atvaizduoti žodžius mažesnės dimensijos tankiais vektoriais, kurie tam tikru būdu atspindi žodžio semantinę reikšmę. Vėliau aptarsime, kaip sukurti prasmingus žodžių įterpimus, tačiau šiuo metu tiesiog galvokime apie įterpimus kaip apie būdą sumažinti žodžio vektoriaus dimensiją.\n",
"\n",
"Taigi, įterpimo sluoksnis priims žodį kaip įvestį ir pateiks išvesties vektorių su nurodytu `embedding_size`. Tam tikra prasme, tai labai panašu į `Linear` sluoksnį, tačiau vietoj vieno karšto kodavimo vektoriaus jis galės priimti žodžio numerį kaip įvestį.\n",
"\n",
"Naudodami įterpimo sluoksnį kaip pirmąjį sluoksnį mūsų tinkle, galime pereiti nuo žodžių maišo (bag-of-words) prie **įterpimo maišo** (embedding bag) modelio, kuriame pirmiausia kiekvieną žodį mūsų tekste paverčiame atitinkamu įterpimu, o tada apskaičiuojame tam tikrą agregavimo funkciją visiems tiems įterpimams, pvz., `sum`, `average` arba `max`.\n",
"\n",
"![Vaizdas, rodantis įterpimo klasifikatorių penkiems sekos žodžiams.](../../../../../lessons/5-NLP/14-Embeddings/images/embedding-classifier-example.png)\n",
"\n",
"Mūsų klasifikatoriaus neuroninis tinklas prasidės įterpimo sluoksniu, tada agregavimo sluoksniu ir lineariu klasifikatoriumi viršuje:\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"class EmbedClassifier(torch.nn.Module):\n",
" def __init__(self, vocab_size, embed_dim, num_class):\n",
" super().__init__()\n",
" self.embedding = torch.nn.Embedding(vocab_size, embed_dim)\n",
" self.fc = torch.nn.Linear(embed_dim, num_class)\n",
"\n",
" def forward(self, x):\n",
" x = self.embedding(x)\n",
" x = torch.mean(x,dim=1)\n",
" return self.fc(x)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Darbas su kintamu sekos dydžiu\n",
"\n",
"Dėl šios architektūros mūsų tinklui reikės sukurti minibatch'us tam tikru būdu. Ankstesniame skyriuje, naudojant žodžių maišo (BoW) metodą, visi BoW tensoriai minibatch'e turėjo vienodą dydį `vocab_size`, nepaisant tikrojo mūsų teksto sekos ilgio. Kai pereiname prie žodžių įterpimų (word embeddings), kiekviename teksto pavyzdyje turėsime skirtingą žodžių skaičių, o jungiant šiuos pavyzdžius į minibatch'us reikės taikyti tam tikrą užpildymą (padding).\n",
"\n",
"Tai galima padaryti naudojant tą pačią techniką, pateikiant `collate_fn` funkciją duomenų šaltiniui:\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"def padify(b):\n",
" # b is the list of tuples of length batch_size\n",
" # - first element of a tuple = label, \n",
" # - second = feature (text sequence)\n",
" # build vectorized sequence\n",
" v = [encode(x[1]) for x in b]\n",
" # first, compute max length of a sequence in this minibatch\n",
" l = max(map(len,v))\n",
" return ( # tuple of two tensors - labels and features\n",
" torch.LongTensor([t[0]-1 for t in b]),\n",
" torch.stack([torch.nn.functional.pad(torch.tensor(t),(0,l-len(t)),mode='constant',value=0) for t in v])\n",
" )\n",
"\n",
"train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=16, collate_fn=padify, shuffle=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Mokymas įterpimo klasifikatoriaus\n",
"\n",
"Dabar, kai apibrėžėme tinkamą duomenų įkroviklį, galime treniruoti modelį naudodami mokymo funkciją, kurią apibrėžėme ankstesniame skyriuje:\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3200: acc=0.6415625\n",
"6400: acc=0.6865625\n",
"9600: acc=0.7103125\n",
"12800: acc=0.726953125\n",
"16000: acc=0.739375\n",
"19200: acc=0.75046875\n",
"22400: acc=0.7572321428571429\n"
]
},
{
"data": {
"text/plain": [
"(0.889799795315499, 0.7623160588611644)"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"net = EmbedClassifier(vocab_size,32,len(classes)).to(device)\n",
"train_epoch(net,train_loader, lr=1, epoch_size=25000)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> **Pastaba**: Čia mes treniruojame tik 25 tūkst. įrašų (mažiau nei vieną pilną epochą) dėl laiko taupymo, tačiau galite tęsti treniravimą, parašyti funkciją treniruoti kelias epochas ir eksperimentuoti su mokymosi tempo parametru, kad pasiektumėte didesnį tikslumą. Turėtumėte sugebėti pasiekti apie 90% tikslumą.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### EmbeddingBag sluoksnis ir kintamo ilgio sekų reprezentacija\n",
"\n",
"Ankstesnėje architektūroje reikėjo visas sekas užpildyti iki vienodo ilgio, kad jos tilptų į mini paketą. Tai nėra pats efektyviausias būdas reprezentuoti kintamo ilgio sekas kitas požiūris būtų naudoti **poslinkio** vektorių, kuris saugotų visų sekų poslinkius viename dideliame vektoriuje.\n",
"\n",
"![Vaizdas, rodantis poslinkio sekos reprezentaciją](../../../../../lessons/5-NLP/14-Embeddings/images/offset-sequence-representation.png)\n",
"\n",
"> **Note**: Aukščiau pateiktame paveikslėlyje rodoma simbolių seka, tačiau mūsų pavyzdyje dirbame su žodžių sekų reprezentacija. Vis dėlto bendras principas, kaip sekas reprezentuoti naudojant poslinkio vektorių, išlieka tas pats.\n",
"\n",
"Norėdami dirbti su poslinkio reprezentacija, naudojame [`EmbeddingBag`](https://pytorch.org/docs/stable/generated/torch.nn.EmbeddingBag.html) sluoksnį. Jis panašus į `Embedding`, tačiau kaip įvestį naudoja turinio vektorių ir poslinkio vektorių. Be to, jis apima vidurkinimo sluoksnį, kuris gali būti `mean`, `sum` arba `max`.\n",
"\n",
"Štai modifikuotas tinklas, kuris naudoja `EmbeddingBag`:\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"class EmbedClassifier(torch.nn.Module):\n",
" def __init__(self, vocab_size, embed_dim, num_class):\n",
" super().__init__()\n",
" self.embedding = torch.nn.EmbeddingBag(vocab_size, embed_dim)\n",
" self.fc = torch.nn.Linear(embed_dim, num_class)\n",
"\n",
" def forward(self, text, off):\n",
" x = self.embedding(text, off)\n",
" return self.fc(x)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Norėdami paruošti duomenų rinkinį mokymui, turime pateikti konversijos funkciją, kuri paruoš poslinkio vektorių:\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"def offsetify(b):\n",
" # first, compute data tensor from all sequences\n",
" x = [torch.tensor(encode(t[1])) for t in b]\n",
" # now, compute the offsets by accumulating the tensor of sequence lengths\n",
" o = [0] + [len(t) for t in x]\n",
" o = torch.tensor(o[:-1]).cumsum(dim=0)\n",
" return ( \n",
" torch.LongTensor([t[0]-1 for t in b]), # labels\n",
" torch.cat(x), # text \n",
" o\n",
" )\n",
"\n",
"train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=16, collate_fn=offsetify, shuffle=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Atkreipkite dėmesį, kad, skirtingai nei visuose ankstesniuose pavyzdžiuose, mūsų tinklas dabar priima du parametrus: duomenų vektorių ir poslinkio vektorių, kurie yra skirtingo dydžio. Panašiai, mūsų duomenų įkroviklis taip pat pateikia mums 3 reikšmes vietoj 2: tiek teksto, tiek poslinkio vektoriai pateikiami kaip ypatybės. Todėl turime šiek tiek pakoreguoti savo mokymo funkciją, kad tai būtų tinkamai apdorota:\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3200: acc=0.6153125\n",
"6400: acc=0.6615625\n",
"9600: acc=0.6932291666666667\n",
"12800: acc=0.715078125\n",
"16000: acc=0.7270625\n",
"19200: acc=0.7382291666666667\n",
"22400: acc=0.7486160714285715\n"
]
},
{
"data": {
"text/plain": [
"(22.771553103007037, 0.7551983365323096)"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"net = EmbedClassifier(vocab_size,32,len(classes)).to(device)\n",
"\n",
"def train_epoch_emb(net,dataloader,lr=0.01,optimizer=None,loss_fn = torch.nn.CrossEntropyLoss(),epoch_size=None, report_freq=200):\n",
" optimizer = optimizer or torch.optim.Adam(net.parameters(),lr=lr)\n",
" loss_fn = loss_fn.to(device)\n",
" net.train()\n",
" total_loss,acc,count,i = 0,0,0,0\n",
" for labels,text,off in dataloader:\n",
" optimizer.zero_grad()\n",
" labels,text,off = labels.to(device), text.to(device), off.to(device)\n",
" out = net(text, off)\n",
" loss = loss_fn(out,labels) #cross_entropy(out,labels)\n",
" loss.backward()\n",
" optimizer.step()\n",
" total_loss+=loss\n",
" _,predicted = torch.max(out,1)\n",
" acc+=(predicted==labels).sum()\n",
" count+=len(labels)\n",
" i+=1\n",
" if i%report_freq==0:\n",
" print(f\"{count}: acc={acc.item()/count}\")\n",
" if epoch_size and count>epoch_size:\n",
" break\n",
" return total_loss.item()/count, acc.item()/count\n",
"\n",
"\n",
"train_epoch_emb(net,train_loader, lr=4, epoch_size=25000)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Semantiniai įterpiniai: Word2Vec\n",
"\n",
"Mūsų ankstesniame pavyzdyje modelio įterpimo sluoksnis išmoko susieti žodžius su vektorinėmis reprezentacijomis, tačiau ši reprezentacija neturėjo daug semantinės prasmės. Būtų naudinga išmokti tokią vektorinę reprezentaciją, kurioje panašūs žodžiai ar sinonimai atitiktų vektorius, esančius arti vienas kito pagal tam tikrą vektorinį atstumą (pvz., euklidinį atstumą).\n",
"\n",
"Tam reikia iš anksto apmokyti mūsų įterpimo modelį naudojant didelę tekstų kolekciją specifiniu būdu. Vienas iš pirmųjų būdų mokyti semantinius įterpinius vadinamas [Word2Vec](https://en.wikipedia.org/wiki/Word2vec). Jis pagrįstas dviem pagrindinėmis architektūromis, kurios naudojamos žodžių paskirstytai reprezentacijai kurti:\n",
"\n",
" - **Nuolatinis maišo žodžių modelis** (CBoW) — šioje architektūroje modelis mokomas numatyti žodį iš aplinkinio konteksto. Turint ngramą $(W_{-2},W_{-1},W_0,W_1,W_2)$, modelio tikslas yra numatyti $W_0$ iš $(W_{-2},W_{-1},W_1,W_2)$.\n",
" - **Nuolatinis skip-gram modelis** yra priešingas CBoW. Modelis naudoja aplinkinį kontekstinių žodžių langą, kad numatytų dabartinį žodį.\n",
"\n",
"CBoW yra greitesnis, o skip-gram yra lėtesnis, tačiau geriau reprezentuoja retus žodžius.\n",
"\n",
"![Vaizdas, rodantis tiek CBoW, tiek Skip-Gram algoritmus, skirtus žodžiams paversti vektoriais.](../../../../../lessons/5-NLP/14-Embeddings/images/example-algorithms-for-converting-words-to-vectors.png)\n",
"\n",
"Norėdami eksperimentuoti su Word2Vec įterpimu, iš anksto apmokytu naudojant Google News duomenų rinkinį, galime naudoti **gensim** biblioteką. Žemiau pateikiame žodžius, labiausiai panašius į 'neural'.\n",
"\n",
"> **Note:** Kai pirmą kartą kuriate žodžių vektorius, jų atsisiuntimas gali užtrukti!\n"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"import gensim.downloader as api\n",
"w2v = api.load('word2vec-google-news-300')"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"neuronal -> 0.7804799675941467\n",
"neurons -> 0.7326500415802002\n",
"neural_circuits -> 0.7252851724624634\n",
"neuron -> 0.7174385190010071\n",
"cortical -> 0.6941086649894714\n",
"brain_circuitry -> 0.6923246383666992\n",
"synaptic -> 0.6699118614196777\n",
"neural_circuitry -> 0.6638563275337219\n",
"neurochemical -> 0.6555314064025879\n",
"neuronal_activity -> 0.6531826257705688\n"
]
}
],
"source": [
"for w,p in w2v.most_similar('neural'):\n",
" print(f\"{w} -> {p}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Mes taip pat galime apskaičiuoti vektorių įterpimus iš žodžio, kurie bus naudojami klasifikavimo modelio mokymui (aiškumo dėlei rodome tik pirmąsias 20 vektoriaus komponentų):\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([ 0.01226807, 0.06225586, 0.10693359, 0.05810547, 0.23828125,\n",
" 0.03686523, 0.05151367, -0.20703125, 0.01989746, 0.10058594,\n",
" -0.03759766, -0.1015625 , -0.15820312, -0.08105469, -0.0390625 ,\n",
" -0.05053711, 0.16015625, 0.2578125 , 0.10058594, -0.25976562],\n",
" dtype=float32)"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"w2v.word_vec('play')[:20]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Puikus dalykas apie semantinius įterpimus yra tai, kad galite manipuliuoti vektoriaus kodavimu, kad pakeistumėte semantiką. Pavyzdžiui, galime paprašyti surasti žodį, kurio vektorinė reprezentacija būtų kuo artimesnė žodžiams *karalius* ir *moteris*, ir kuo toliau nuo žodžio *vyras*:\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"('queen', 0.7118192911148071)"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"w2v.most_similar(positive=['king','woman'],negative=['man'])[0]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Tiek CBoW, tiek Skip-Grams yra „prognozuojančios“ įterptys, nes jos atsižvelgia tik į vietinius kontekstus. Word2Vec nepasinaudoja globaliu kontekstu.\n",
"\n",
"**FastText** remiasi Word2Vec, mokydamas vektorių reprezentacijas kiekvienam žodžiui ir simbolių n-gramas, esančias žodyje. Šių reprezentacijų reikšmės kiekviename mokymo žingsnyje yra vidurkinamos į vieną vektorių. Nors tai prideda daug papildomų skaičiavimų priešmokymio metu, tai leidžia žodžių įterptims koduoti subžodžių informaciją.\n",
"\n",
"Kitas metodas, **GloVe**, pasinaudoja koegzistavimo matricos idėja, naudodamas neuroninius metodus, kad išskaidytų koegzistavimo matricą į išraiškingesnius ir nelinijinius žodžių vektorius.\n",
"\n",
"Galite eksperimentuoti su pavyzdžiu, keisdami įterptis į FastText ir GloVe, nes gensim palaiko kelis skirtingus žodžių įterpimo modelius.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Naudojant iš anksto apmokytus įterpimus PyTorch\n",
"\n",
"Galime pakeisti aukščiau pateiktą pavyzdį, kad iš anksto užpildytume matricą mūsų įterpimo sluoksnyje semantiniais įterpimais, tokiais kaip Word2Vec. Turime atsižvelgti į tai, kad iš anksto apmokytų įterpimų ir mūsų teksto korpuso žodynai greičiausiai nesutaps, todėl trūkstamų žodžių svorius inicializuosime atsitiktinėmis reikšmėmis:\n"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Embedding size: 300\n",
"Populating matrix, this will take some time...Done, found 41080 words, 54732 words missing\n"
]
}
],
"source": [
"embed_size = len(w2v.get_vector('hello'))\n",
"print(f'Embedding size: {embed_size}')\n",
"\n",
"net = EmbedClassifier(vocab_size,embed_size,len(classes))\n",
"\n",
"print('Populating matrix, this will take some time...',end='')\n",
"found, not_found = 0,0\n",
"for i,w in enumerate(vocab.get_itos()):\n",
" try:\n",
" net.embedding.weight[i].data = torch.tensor(w2v.get_vector(w))\n",
" found+=1\n",
" except:\n",
" net.embedding.weight[i].data = torch.normal(0.0,1.0,(embed_size,))\n",
" not_found+=1\n",
"\n",
"print(f\"Done, found {found} words, {not_found} words missing\")\n",
"net = net.to(device)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Dabar treniruokime mūsų modelį. Atkreipkite dėmesį, kad modelio treniravimas užtrunka žymiai ilgiau nei ankstesniame pavyzdyje, dėl didesnio įterpimo sluoksnio dydžio ir daug didesnio parametrų skaičiaus. Taip pat dėl to gali prireikti treniruoti modelį su daugiau pavyzdžių, jei norime išvengti per didelio pritaikymo.\n"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3200: acc=0.6359375\n",
"6400: acc=0.68109375\n",
"9600: acc=0.7067708333333333\n",
"12800: acc=0.723671875\n",
"16000: acc=0.73625\n",
"19200: acc=0.7463541666666667\n",
"22400: acc=0.7560714285714286\n"
]
},
{
"data": {
"text/plain": [
"(214.1013875559821, 0.7626759436980166)"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"train_epoch_emb(net,train_loader, lr=4, epoch_size=25000)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Mūsų atveju nematome didelio tikslumo padidėjimo, greičiausiai dėl labai skirtingų žodynų. \n",
"Norint išspręsti skirtingų žodynų problemą, galime naudoti vieną iš šių sprendimų: \n",
"* Iš naujo apmokyti word2vec modelį pagal mūsų žodyną \n",
"* Įkelti mūsų duomenų rinkinį su žodynu iš iš anksto apmokyto word2vec modelio. Žodyną, naudojamą duomenų rinkiniui įkelti, galima nurodyti įkėlimo metu. \n",
"\n",
"Pastarasis metodas atrodo paprastesnis, ypač todėl, kad PyTorch `torchtext` sistema turi integruotą palaikymą įterpimams. Pavyzdžiui, galime sukurti žodyną, pagrįstą GloVe, tokiu būdu: \n"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"100%|█████████▉| 399999/400000 [00:15<00:00, 25411.14it/s]\n"
]
}
],
"source": [
"vocab = torchtext.vocab.GloVe(name='6B', dim=50)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Įkeltas žodynas turi šias pagrindines operacijas:\n",
"* `vocab.stoi` žodynas leidžia mums konvertuoti žodį į jo indeksą žodyne\n",
"* `vocab.itos` atlieka priešingą veiksmą - konvertuoja skaičių į žodį\n",
"* `vocab.vectors` yra įterptųjų vektorių masyvas, todėl norint gauti žodžio `s` įterptį, turime naudoti `vocab.vectors[vocab.stoi[s]]`\n",
"\n",
"Štai pavyzdys, kaip manipuliuoti įterptimis, kad būtų pademonstruota lygtis **kind-man+woman = queen** (turėjau šiek tiek pakoreguoti koeficientą, kad tai veiktų):\n"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'queen'"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# get the vector corresponding to kind-man+woman\n",
"qvec = vocab.vectors[vocab.stoi['king']]-vocab.vectors[vocab.stoi['man']]+1.3*vocab.vectors[vocab.stoi['woman']]\n",
"# find the index of the closest embedding vector \n",
"d = torch.sum((vocab.vectors-qvec)**2,dim=1)\n",
"min_idx = torch.argmin(d)\n",
"# find the corresponding word\n",
"vocab.itos[min_idx]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Norint apmokyti klasifikatorių naudojant šiuos įterpimus, pirmiausia turime užkoduoti savo duomenų rinkinį naudodami GloVe žodyną:\n"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [],
"source": [
"def offsetify(b):\n",
" # first, compute data tensor from all sequences\n",
" x = [torch.tensor(encode(t[1],voc=vocab)) for t in b] # pass the instance of vocab to encode function!\n",
" # now, compute the offsets by accumulating the tensor of sequence lengths\n",
" o = [0] + [len(t) for t in x]\n",
" o = torch.tensor(o[:-1]).cumsum(dim=0)\n",
" return ( \n",
" torch.LongTensor([t[0]-1 for t in b]), # labels\n",
" torch.cat(x), # text \n",
" o\n",
" )"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Kaip matėme aukščiau, visi vektorių įterpimai saugomi `vocab.vectors` matricoje. Tai labai palengvina šių svorių įkėlimą į įterpimo sluoksnio svorius naudojant paprastą kopijavimą:\n"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [],
"source": [
"net = EmbedClassifier(len(vocab),len(vocab.vectors[0]),len(classes))\n",
"net.embedding.weight.data = vocab.vectors\n",
"net = net.to(device)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3200: acc=0.6271875\n",
"6400: acc=0.68078125\n",
"9600: acc=0.7030208333333333\n",
"12800: acc=0.71984375\n",
"16000: acc=0.7346875\n",
"19200: acc=0.7455729166666667\n",
"22400: acc=0.7529464285714286\n"
]
},
{
"data": {
"text/plain": [
"(35.53972978646833, 0.7575175943698017)"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=16, collate_fn=offsetify, shuffle=True)\n",
"train_epoch_emb(net,train_loader, lr=4, epoch_size=25000)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Viena iš priežasčių, kodėl nematome reikšmingo tikslumo padidėjimo, yra ta, kad kai kurių žodžių iš mūsų duomenų rinkinio nėra iš anksto apmokyto GloVe žodyno, todėl jie iš esmės ignoruojami. Norėdami įveikti šią problemą, galime apmokyti savo įterpimus pagal mūsų duomenų rinkinį.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Kontekstinės įterptys\n",
"\n",
"Viena pagrindinių tradicinių iš anksto apmokytų įterpčių, tokių kaip Word2Vec, apribojimų yra žodžių reikšmių išskyrimo problema. Nors iš anksto apmokytos įterptys gali užfiksuoti dalį žodžių reikšmės kontekste, visos galimos žodžio reikšmės yra užkoduojamos toje pačioje įterptyje. Tai gali sukelti problemų tolimesniuose modeliuose, nes daugelis žodžių, pavyzdžiui, žodis „play“, turi skirtingas reikšmes priklausomai nuo konteksto, kuriame jie naudojami.\n",
"\n",
"Pavyzdžiui, žodis „play“ šiuose dviejuose sakiniuose turi gana skirtingas reikšmes:\n",
"- Aš nuėjau į **spektaklį** teatre.\n",
"- Jonas nori **žaisti** su savo draugais.\n",
"\n",
"Aukščiau pateiktos iš anksto apmokytos įterptys abu šiuos žodžio „play“ reikšmes pateikia toje pačioje įterptyje. Norint įveikti šį apribojimą, reikia kurti įterptis, pagrįstas **kalbos modeliu**, kuris yra apmokytas naudojant didelį tekstų korpusą ir *žino*, kaip žodžiai gali būti naudojami skirtinguose kontekstuose. Kontekstinių įterpčių aptarimas nėra šio vadovo dalis, tačiau mes prie jų sugrįšime, kai kalbėsime apie kalbos modelius kitame skyriuje.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Atsakomybės apribojimas**: \nŠis dokumentas buvo išverstas naudojant AI vertimo paslaugą [Co-op Translator](https://github.com/Azure/co-op-translator). Nors siekiame tikslumo, prašome atkreipti dėmesį, kad automatiniai vertimai gali turėti klaidų ar netikslumų. Originalus dokumentas jo gimtąja kalba turėtų būti laikomas autoritetingu šaltiniu. Kritinei informacijai rekomenduojama naudoti profesionalų žmogaus vertimą. Mes neprisiimame atsakomybės už nesusipratimus ar neteisingą interpretaciją, atsiradusią dėl šio vertimo naudojimo.\n"
]
}
],
"metadata": {
"interpreter": {
"hash": "0cb620c6d4b9f7a635928804c26cf22403d89d98d79684e4529119355ee6d5a5"
},
"kernelspec": {
"display_name": "py37_pytorch",
"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.8.12"
},
"coopTranslator": {
"original_hash": "f50b026abce5cf36783a560ea72cb9b1",
"translation_date": "2025-08-31T14:07:10+00:00",
"source_file": "lessons/5-NLP/14-Embeddings/EmbeddingsPyTorch.ipynb",
"language_code": "lt"
}
},
"nbformat": 4,
"nbformat_minor": 2
}