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

726 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": [
"## Vdelave\n",
"\n",
"V našem prejšnjem primeru smo delali z visoko-dimenzionalnimi vektorji vreče besed dolžine `vocab_size`, pri čemer smo izrecno pretvarjali nizko-dimenzionalne vektorske predstavitve položajev v redke enovredne predstavitve. Ta enovredna predstavitev ni učinkovita glede pomnilnika, poleg tega pa se vsaka beseda obravnava neodvisno od drugih, tj. enovredni kodirani vektorji ne izražajo nobene semantične podobnosti med besedami.\n",
"\n",
"V tej enoti bomo nadaljevali z raziskovanjem podatkovne zbirke **News AG**. Za začetek naložimo podatke in pridobimo nekaj definicij iz prejšnjega zvezka.\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": [
"## Kaj je vgrajevanje?\n",
"\n",
"Ideja **vgrajevanja** je predstaviti besede z nižjedimenzionalnimi gostimi vektorji, ki nekako odražajo semantični pomen besede. Kasneje bomo razpravljali o tem, kako zgraditi smiselne vgrajene besede, vendar za zdaj razmišljajmo o vgrajevanju kot o načinu za zmanjšanje dimenzionalnosti vektorja besede.\n",
"\n",
"Torej, vgrajevalna plast bi kot vhod vzela besedo in proizvedla izhodni vektor določene velikosti `embedding_size`. Na nek način je zelo podobna plasti `Linear`, vendar namesto da bi vzela enovročno kodiran vektor, bo lahko kot vhod vzela številko besede.\n",
"\n",
"Z uporabo vgrajevalne plasti kot prve plasti v naši mreži lahko preklopimo iz modela vreče besed na model **vreče vgrajevanja**, kjer najprej vsako besedo v našem besedilu pretvorimo v ustrezno vgrajevanje, nato pa izračunamo neko agregatno funkcijo nad vsemi temi vgrajevanji, kot so `sum`, `average` ali `max`.\n",
"\n",
"![Slika, ki prikazuje klasifikator vgrajevanja za pet zaporednih besed.](../../../../../translated_images/sl/embedding-classifier-example.b77f021a7ee67eee.webp)\n",
"\n",
"Naša nevronska mreža klasifikatorja se bo začela z vgrajevalno plastjo, nato agregatno plastjo in linearnim klasifikatorjem na vrhu:\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": [
"### Obdelava spremenljive velikosti zaporedja\n",
"\n",
"Zaradi te arhitekture je treba minibatche za našo mrežo ustvariti na določen način. V prejšnji enoti, ko smo uporabljali vrečo besed (bag-of-words), so imeli vsi BoW tenzorji v minibatchu enako velikost `vocab_size`, ne glede na dejansko dolžino našega besedilnega zaporedja. Ko preidemo na vektorske predstavitve besed (word embeddings), bomo imeli različno število besed v vsakem vzorcu besedila, in pri združevanju teh vzorcev v minibatche bomo morali uporabiti nekaj oblazinjenja (padding).\n",
"\n",
"To lahko storimo z uporabo iste tehnike, kjer funkcijo `collate_fn` zagotovimo viru podatkov:\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": [
"### Usposabljanje klasifikatorja vdelav\n",
"\n",
"Zdaj, ko smo definirali ustrezen nalagalnik podatkov, lahko model usposobimo z uporabo funkcije za usposabljanje, ki smo jo definirali v prejšnji enoti:\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": [
"> **Opomba**: Tukaj treniramo samo za 25k zapisov (manj kot eno celotno epoho) zaradi prihranka časa, vendar lahko nadaljujete s treniranjem, napišete funkcijo za treniranje skozi več epoch in eksperimentirate s parametrom hitrosti učenja, da dosežete višjo natančnost. Morali bi doseči natančnost približno 90%.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Plast EmbeddingBag in predstavitev zaporedij spremenljive dolžine\n",
"\n",
"V prejšnji arhitekturi smo morali vsa zaporedja zapolniti do enake dolžine, da so ustrezala mini seriji. To ni najbolj učinkovit način za predstavitev zaporedij spremenljive dolžine drugačen pristop bi bil uporaba **offset** vektorja, ki bi vseboval zamike vseh zaporedij, shranjenih v enem velikem vektorju.\n",
"\n",
"![Slika, ki prikazuje predstavitev zaporedij z zamiki](../../../../../translated_images/sl/offset-sequence-representation.eb73fcefb29b46ee.webp)\n",
"\n",
"> **Opomba**: Na zgornji sliki prikazujemo zaporedje znakov, vendar v našem primeru delamo z zaporedji besed. Kljub temu splošno načelo predstavitve zaporedij z offset vektorjem ostaja enako.\n",
"\n",
"Za delo s predstavitvijo z zamiki uporabljamo plast [`EmbeddingBag`](https://pytorch.org/docs/stable/generated/torch.nn.EmbeddingBag.html). Ta je podobna `Embedding`, vendar kot vhod prejme vsebinski vektor in offset vektor, poleg tega pa vključuje tudi plast za povprečenje, ki je lahko `mean`, `sum` ali `max`.\n",
"\n",
"Tukaj je spremenjeno omrežje, ki uporablja `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": [
"Za pripravo nabora podatkov za učenje moramo zagotoviti pretvorbeno funkcijo, ki bo pripravila vektorski premik:\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": [
"Upoštevajte, da za razliko od vseh prejšnjih primerov naša mreža zdaj sprejema dva parametra: podatkovni vektor in odmik vektor, ki sta različnih velikosti. Podobno nam naš nalagalnik podatkov zagotavlja 3 vrednosti namesto 2: tako besedilni kot odmikovni vektorji so zagotovljeni kot značilnosti. Zato moramo nekoliko prilagoditi našo funkcijo usposabljanja, da to upošteva:\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": [
"## Semantične vektorske predstavitve: Word2Vec\n",
"\n",
"V našem prejšnjem primeru se je plast za vektorsko predstavitev v modelu naučila preslikati besede v vektorsko obliko, vendar ta predstavitev ni imela veliko semantičnega pomena. Bilo bi koristno, če bi se naučili takšne vektorske predstavitve, kjer bi bile podobne besede ali sinonimi predstavljeni z vektorji, ki so si blizu glede na neko vektorsko razdaljo (npr. evklidska razdalja).\n",
"\n",
"Da to dosežemo, moramo naš model za vektorsko predstavitev predhodno naučiti na veliki zbirki besedil na specifičen način. Ena prvih metod za učenje semantičnih vektorskih predstavitev se imenuje [Word2Vec](https://en.wikipedia.org/wiki/Word2vec). Temelji na dveh glavnih arhitekturah, ki se uporabljata za ustvarjanje porazdeljene predstavitve besed:\n",
"\n",
" - **Neprekinjena vreča besed** (CBoW) — pri tej arhitekturi model učimo, da napove besedo iz okoliškega konteksta. Glede na n-gram $(W_{-2},W_{-1},W_0,W_1,W_2)$ je cilj modela napovedati $W_0$ na podlagi $(W_{-2},W_{-1},W_1,W_2)$.\n",
" - **Neprekinjeni preskok-gram** (skip-gram) je nasproten CBoW. Model uporablja okoliško okno kontekstnih besed za napoved trenutne besede.\n",
"\n",
"CBoW je hitrejši, medtem ko je skip-gram počasnejši, vendar bolje predstavlja redke besede.\n",
"\n",
"![Slika, ki prikazuje algoritma CBoW in Skip-Gram za pretvorbo besed v vektorje.](../../../../../translated_images/sl/example-algorithms-for-converting-words-to-vectors.fbe9207a726922f6.webp)\n",
"\n",
"Za eksperimentiranje z vektorsko predstavitvijo Word2Vec, predhodno naučeno na zbirki podatkov Google News, lahko uporabimo knjižnico **gensim**. Spodaj poiščemo besede, ki so najbolj podobne 'neural'.\n",
"\n",
"> **Opomba:** Ko prvič ustvarjate vektorske predstavitve besed, lahko prenos podatkov traja nekaj časa!\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": [
"Prav tako lahko izračunamo vektorske vdelave iz besede, ki jih uporabimo pri usposabljanju modela za klasifikacijo (za jasnost prikažemo le prvih 20 komponent vektorja):\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": [
"Odlična stvar semantičnih vdelav je, da lahko manipulirate z vektorskim kodiranjem, da spremenite semantiko. Na primer, lahko zahtevamo, da najdemo besedo, katere vektorska predstavitev bi bila čim bližje besedama *kralj* in *ženska*, ter čim bolj oddaljena od besede *moški*:\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": [
"Tako CBoW kot Skip-Grams sta \"prediktivni\" vektorski predstavitvi, saj upoštevata le lokalne kontekste. Word2Vec ne izkorišča globalnega konteksta.\n",
"\n",
"**FastText** nadgrajuje Word2Vec tako, da se nauči vektorske predstavitve za vsako besedo in za n-grame znakov, ki jih najdemo znotraj vsake besede. Vrednosti teh predstavitev se nato povprečijo v en vektor pri vsakem koraku učenja. Čeprav to doda veliko dodatnega računanja med predhodnim učenjem, omogoča, da vektorske predstavitve besed vključujejo informacije o podbesedah.\n",
"\n",
"Druga metoda, **GloVe**, izkorišča idejo matrike so-pojavitev in uporablja nevronske metode za razgradnjo matrike so-pojavitev v bolj izrazne in nelinearne vektorske predstavitve besed.\n",
"\n",
"Lahko se poigrate s primerom tako, da spremenite vektorske predstavitve v FastText in GloVe, saj gensim podpira več različnih modelov za vektorske predstavitve besed.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Uporaba vnaprej naučenih vektorskih predstavitev v PyTorch\n",
"\n",
"Primer zgoraj lahko prilagodimo tako, da matriko v naši slojni predstavitvi (embedding layer) predhodno napolnimo s semantičnimi vektorskimi predstavitvami, kot je Word2Vec. Upoštevati moramo, da se besedišča vnaprej naučenih predstavitev in našega besedilnega korpusa verjetno ne bodo ujemala, zato bomo uteži za manjkajoče besede inicializirali z naključnimi vrednostmi:\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": [
"Zdaj trenirajmo naš model. Upoštevajte, da je čas, potreben za treniranje modela, bistveno daljši kot v prejšnjem primeru, zaradi večje velikosti vdelane plasti in posledično veliko večjega števila parametrov. Prav tako bomo morda morali model trenirati na več primerih, če želimo preprečiti prenaučenje.\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": [
"V našem primeru ne opazimo velikega povečanja natančnosti, kar je verjetno posledica precej različnih besedišč. \n",
"Da bi premagali težavo različnih besedišč, lahko uporabimo eno od naslednjih rešitev: \n",
"* Ponovno usposobimo model word2vec na našem besedišču \n",
"* Naložimo naš nabor podatkov z besediščem iz že usposobljenega modela word2vec. Besedišče, ki se uporablja za nalaganje nabora podatkov, je mogoče določiti med nalaganjem. \n",
"\n",
"Drugi pristop se zdi lažji, še posebej zato, ker PyTorch `torchtext` ogrodje vsebuje vgrajeno podporo za vektorske predstavitve besed. Na primer, lahko ustvarimo besedišče, ki temelji na GloVe, na naslednji način:\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": [
"Naloženi slovar ima naslednje osnovne operacije:\n",
"* Slovar `vocab.stoi` nam omogoča pretvorbo besede v njen indeks v slovarju\n",
"* `vocab.itos` opravlja nasprotno - pretvori številko v besedo\n",
"* `vocab.vectors` je matrika vektorskih predstavitev, tako da za pridobitev vektorske predstavitve besede `s` uporabimo `vocab.vectors[vocab.stoi[s]]`\n",
"\n",
"Tukaj je primer manipulacije vektorskih predstavitev, ki prikazuje enačbo **kind-man+woman = queen** (moral sem nekoliko prilagoditi koeficient, da deluje):\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": [
"Za učenje klasifikatorja z uporabo teh vdelav moramo najprej kodirati naš nabor podatkov z besediščem GloVe:\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": [
"Kot smo videli zgoraj, so vsi vektorski vdelki shranjeni v matriki `vocab.vectors`. To omogoča zelo enostavno nalaganje teh uteži v uteži vdelane plasti z enostavnim kopiranjem:\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": [
"Zdaj trenirajmo naš model in preverimo, ali dobimo boljše rezultate:\n"
]
},
{
"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": [
"Eden od razlogov, zakaj ne opažamo znatnega povečanja natančnosti, je dejstvo, da nekaterih besed iz našega nabora podatkov ni v predhodno naučenem GloVe besedišču, zato so v bistvu prezrte. Da bi to premagali, lahko naučimo lastne vdelave na našem naboru podatkov.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Kontekstualne vektorske predstavitve\n",
"\n",
"Ena ključnih omejitev tradicionalnih vnaprej naučenih vektorskih predstavitev, kot je Word2Vec, je problem razločevanja pomenov besed. Čeprav lahko vnaprej naučene vektorske predstavitve zajamejo del pomena besed v kontekstu, je vsak možen pomen besede kodiran v isti vektor. To lahko povzroči težave v nadaljnjih modelih, saj imajo številne besede, kot je na primer beseda 'play', različne pomene glede na kontekst, v katerem so uporabljene.\n",
"\n",
"Na primer, beseda 'play' ima v teh dveh stavkih precej različen pomen:\n",
"- Šel sem na **igro** v gledališče.\n",
"- John želi **igrati** s svojimi prijatelji.\n",
"\n",
"Vnaprej naučene vektorske predstavitve zgoraj predstavljajo oba pomena besede 'play' v istem vektorju. Da bi premagali to omejitev, moramo graditi vektorske predstavitve na podlagi **jezikovnega modela**, ki je naučen na velikem korpusu besedil in *ve*, kako se besede lahko povezujejo v različnih kontekstih. Razprava o kontekstualnih vektorskih predstavitvah presega okvir tega vodiča, vendar se bomo k njim vrnili, ko bomo govorili o jezikovnih modelih v naslednji enoti.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Omejitev odgovornosti**: \nTa dokument je bil preveden z uporabo storitve za strojno prevajanje [Co-op Translator](https://github.com/Azure/co-op-translator). Čeprav si prizadevamo za natančnost, vas prosimo, da upoštevate, da lahko avtomatizirani prevodi vsebujejo napake ali netočnosti. Izvirni dokument v njegovem izvirnem jeziku je treba obravnavati kot avtoritativni vir. Za ključne informacije priporočamo strokovno človeško prevajanje. Ne prevzemamo odgovornosti za morebitna nesporazumevanja ali napačne razlage, ki izhajajo iz uporabe tega prevoda.\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-30T08:16:17+00:00",
"source_file": "lessons/5-NLP/14-Embeddings/EmbeddingsPyTorch.ipynb",
"language_code": "sl"
}
},
"nbformat": 4,
"nbformat_minor": 2
}