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

720 lines
42 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## एम्बेडिङहरू\n",
"\n",
"हाम्रो अघिल्लो उदाहरणमा, हामीले `vocab_size` लम्बाइ भएका उच्च-आयामीय बाग-ऑफ-वर्ड्स भेक्टरहरूमा काम गरेका थियौं, र हामीले कम-आयामीय स्थानगत प्रतिनिधित्व भेक्टरहरूलाई स्पष्ट रूपमा sparse one-hot प्रतिनिधित्वमा रूपान्तरण गरिरहेका थियौं। यो one-hot प्रतिनिधित्व मेमोरी-कुशल छैन, साथै, प्रत्येक शब्दलाई एक-अर्काबाट स्वतन्त्र रूपमा व्यवहार गरिन्छ, अर्थात् one-hot एन्कोड गरिएको भेक्टरहरूले शब्दहरू बीचको कुनै पनि अर्थपूर्ण समानता व्यक्त गर्दैनन्।\n",
"\n",
"यस इकाईमा, हामी **News AG** डेटासेटको अन्वेषण जारी राख्नेछौं। सुरु गर्नका लागि, डेटा लोड गरौं र अघिल्लो नोटबुकबाट केही परिभाषाहरू प्राप्त गरौं।\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": [
"## एम्बेडिङ भनेको के हो?\n",
"\n",
"**एम्बेडिङ** को विचार भनेको शब्दहरूलाई कम-आयामीय घना भेक्टरहरूद्वारा प्रतिनिधित्व गर्नु हो, जसले कुनै प्रकारले शब्दको अर्थलाई झल्काउँछ। हामी पछि कसरी अर्थपूर्ण शब्द एम्बेडिङहरू निर्माण गर्ने भन्ने छलफल गर्नेछौं, तर अहिलेका लागि एम्बेडिङलाई शब्द भेक्टरको आयाम घटाउने तरिकाको रूपमा सोचौं। \n",
"\n",
"त्यसैले, एम्बेडिङ लेयरले शब्दलाई इनपुटको रूपमा लिन्छ, र निर्दिष्ट गरिएको `embedding_size` को आउटपुट भेक्टर उत्पादन गर्छ। यो अर्थमा, यो `Linear` लेयरसँग धेरै मिल्दोजुल्दो छ, तर यो एक-हट इन्कोड गरिएको भेक्टर लिने सट्टा, शब्दको नम्बरलाई इनपुटको रूपमा लिन सक्षम हुनेछ।\n",
"\n",
"हाम्रो नेटवर्कमा पहिलो लेयरको रूपमा एम्बेडिङ लेयर प्रयोग गरेर, हामी ब्याग-अफ-वर्ड्स मोडेलबाट **एम्बेडिङ ब्याग** मोडेलमा स्विच गर्न सक्छौं, जहाँ हामी पहिलो पटक हाम्रो पाठका प्रत्येक शब्दलाई सम्बन्धित एम्बेडिङमा रूपान्तरण गर्छौं, र त्यसपछि ती सबै एम्बेडिङहरूमा `sum`, `average` वा `max` जस्ता कुनै एग्रिगेट फङ्सन गणना गर्छौं। \n",
"\n",
"![पाँच अनुक्रम शब्दहरूको लागि एम्बेडिङ क्लासिफायर देखाउने छवि।](../../../../../translated_images/ne/embedding-classifier-example.b77f021a7ee67eee.webp)\n",
"\n",
"हाम्रो क्लासिफायर न्यूरल नेटवर्क एम्बेडिङ लेयरबाट सुरु हुनेछ, त्यसपछि एग्रिगेसन लेयर, र यसको माथि लीनियर क्लासिफायर हुनेछ:\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": [
"### भिन्न-भिन्न आकारका भेरिएबल अनुक्रमसँग काम गर्ने\n",
"\n",
"यो आर्किटेक्चरको परिणामस्वरूप, हाम्रो नेटवर्कका लागि मिनिब्याचहरू विशेष तरिकाले तयार गर्नुपर्ने हुन्छ। अघिल्लो युनिटमा, जब bag-of-words प्रयोग गरिँदै थियो, सबै BoW टेन्सरहरूको आकार मिनिब्याचमा `vocab_size` बराबर हुन्थ्यो, चाहे हाम्रो पाठ अनुक्रमको वास्तविक लम्बाइ जेसुकै किन नहोस्। जब हामी word embeddings तर्फ सर्छौं, प्रत्येक पाठ नमूनामा शब्दहरूको संख्या फरक-फरक हुन सक्छ, र ती नमूनाहरूलाई मिनिब्याचमा संयोजन गर्दा हामीले केही padding लागू गर्नुपर्ने हुन्छ।\n",
"\n",
"यो काम datasource मा `collate_fn` function प्रदान गरेर गर्न सकिन्छ:\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": [
"### एम्बेडिङ वर्गीकरणकर्ता प्रशिक्षण गर्दै\n",
"\n",
"अब हामीले उपयुक्त डाटालोडर परिभाषित गरिसकेपछि, हामीले अघिल्लो इकाईमा परिभाषित गरेको प्रशिक्षण कार्य प्रयोग गरेर मोडेल प्रशिक्षण गर्न सक्छौं:\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": [
"> **नोट**: हामी यहाँ समय बचतको लागि मात्र २५ हजार रेकर्डहरू (एक पूर्ण युगभन्दा कम) को लागि प्रशिक्षण गर्दैछौं, तर तपाईं प्रशिक्षण जारी राख्न सक्नुहुन्छ, धेरै युगहरूको लागि प्रशिक्षण गर्नको लागि एक कार्य लेख्न सक्नुहुन्छ, र उच्च शुद्धता प्राप्त गर्नको लागि सिक्ने दर प्यारामिटरसँग प्रयोग गर्न सक्नुहुन्छ। तपाईं लगभग ९०% शुद्धतामा जान सक्षम हुनुहुनेछ।\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### EmbeddingBag लेयर र भिन्न-लम्बाइ अनुक्रमको प्रतिनिधित्व\n",
"\n",
"अघिल्लो आर्किटेक्चरमा, हामीले सबै अनुक्रमहरूलाई एउटै लम्बाइमा ल्याउनका लागि padding गर्नुपर्ने हुन्थ्यो ताकि तिनीहरूलाई मिनिब्याचमा फिट गर्न सकियोस्। यो भिन्न-लम्बाइ अनुक्रमहरूको प्रतिनिधित्व गर्ने सबैभन्दा प्रभावकारी तरिका होइन - अर्को तरिका भनेको **offset** भेक्टर प्रयोग गर्नु हो, जसले एउटै ठूलो भेक्टरमा भण्डारण गरिएका सबै अनुक्रमहरूको offsets राख्छ।\n",
"\n",
"![Offset अनुक्रम प्रतिनिधित्व देखाउने चित्र](../../../../../translated_images/ne/offset-sequence-representation.eb73fcefb29b46ee.webp)\n",
"\n",
"> **Note**: माथिको चित्रमा, हामीले क्यारेक्टरहरूको अनुक्रम देखाएका छौं, तर हाम्रो उदाहरणमा हामी शब्दहरूको अनुक्रमसँग काम गरिरहेका छौं। यद्यपि, offset भेक्टर प्रयोग गरेर अनुक्रमहरूको प्रतिनिधित्व गर्ने सामान्य सिद्धान्त उस्तै रहन्छ।\n",
"\n",
"Offset प्रतिनिधित्वसँग काम गर्नका लागि, हामी [`EmbeddingBag`](https://pytorch.org/docs/stable/generated/torch.nn.EmbeddingBag.html) लेयर प्रयोग गर्छौं। यो `Embedding` जस्तै छ, तर यसले सामग्री भेक्टर र offset भेक्टरलाई इनपुटको रूपमा लिन्छ, र यसमा औसत गर्ने लेयर पनि समावेश छ, जुन `mean`, `sum` वा `max` हुन सक्छ।\n",
"\n",
"यहाँ `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": [
"डेटासेट प्रशिक्षणको लागि तयार गर्न, हामीले अफसेट भेक्टर तयार गर्ने रूपान्तरण कार्य प्रदान गर्न आवश्यक छ:\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": [
"ध्यान दिनुहोस्, कि सबै अघिल्लो उदाहरणहरू भन्दा फरक, हाम्रो नेटवर्कले अब दुई प्यारामिटरहरू स्वीकार गर्दछ: डेटा भेक्टर र अफसेट भेक्टर, जसको आकार फरक छ। त्यस्तै गरी, हाम्रो डेटा लोडरले पनि हामीलाई २ को सट्टा ३ मानहरू प्रदान गर्दछ: पाठ र अफसेट भेक्टरहरू दुवैलाई सुविधाहरूको रूपमा प्रदान गरिन्छ। त्यसैले, हामीले हाम्रो प्रशिक्षण कार्यलाई त्यसको हेरचाह गर्न अलिकति समायोजन गर्न आवश्यक छ:\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": [
"## सेम्यान्टिक एम्बेडिङ: वर्ड२भेक\n",
"\n",
"हाम्रो अघिल्लो उदाहरणमा, मोडेलको एम्बेडिङ तहले शब्दहरूलाई भेक्टर प्रतिनिधित्वमा म्याप गर्न सिक्यो, तर यो प्रतिनिधित्वमा धेरै सेम्यान्टिक अर्थ थिएन। यस्तो भेक्टर प्रतिनिधित्व सिक्न राम्रो हुने थियो, जहाँ समान शब्दहरू वा पर्यायवाची शब्दहरू केही भेक्टर दूरी (जस्तै, युक्लिडियन दूरी) को हिसाबले एक-अर्कासँग नजिक हुनेछन्।\n",
"\n",
"यसका लागि, हामीले हाम्रो एम्बेडिङ मोडेललाई विशेष तरिकाले ठूलो पाठ संग्रहमा प्रि-ट्रेन गर्न आवश्यक छ। सेम्यान्टिक एम्बेडिङहरू ट्रेन गर्ने पहिलो तरिकाहरू मध्ये एकलाई [Word2Vec](https://en.wikipedia.org/wiki/Word2vec) भनिन्छ। यो शब्दहरूको वितरित प्रतिनिधित्व उत्पादन गर्न प्रयोग गरिने दुई मुख्य आर्किटेक्चरहरूमा आधारित छ:\n",
"\n",
" - **कन्टिन्युअस ब्याग-अफ-वर्ड्स** (CBoW) — यस आर्किटेक्चरमा, हामी मोडेललाई वरपरको सन्दर्भबाट शब्दको भविष्यवाणी गर्न ट्रेन गर्छौं। दिइएको ngram $(W_{-2},W_{-1},W_0,W_1,W_2)$ मा, मोडेलको लक्ष्य $(W_{-2},W_{-1},W_1,W_2)$ बाट $W_0$ को भविष्यवाणी गर्नु हो।\n",
" - **कन्टिन्युअस स्किप-ग्राम** CBoW को विपरीत हो। मोडेलले सन्दर्भ शब्दहरूको वरपरको विन्डो प्रयोग गरेर वर्तमान शब्दको भविष्यवाणी गर्छ।\n",
"\n",
"CBoW छिटो हुन्छ, जबकि स्किप-ग्राम ढिलो हुन्छ, तर दुर्लभ शब्दहरूको प्रतिनिधित्व गर्न राम्रो काम गर्छ।\n",
"\n",
"![CBoW र स्किप-ग्राम एल्गोरिदमहरू शब्दहरूलाई भेक्टरमा रूपान्तरण गर्ने तरिका देखाउने चित्र।](../../../../../translated_images/ne/example-algorithms-for-converting-words-to-vectors.fbe9207a726922f6.webp)\n",
"\n",
"Google News डेटासेटमा प्रि-ट्रेन गरिएको word2vec एम्बेडिङसँग प्रयोग गर्न, हामी **gensim** लाइब्रेरी प्रयोग गर्न सक्छौं। तल हामी 'neural' शब्दसँग सबैभन्दा मिल्दो शब्दहरू फेला पार्छौं।\n",
"\n",
"> **Note:** जब तपाईं पहिलो पटक शब्द भेक्टरहरू सिर्जना गर्नुहुन्छ, तिनीहरू डाउनलोड गर्न केही समय लाग्न सक्छ!\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": [
"हामी शब्दबाट भेक्टर एम्बेडिङहरू पनि गणना गर्न सक्छौं, जसलाई वर्गीकरण मोडेल प्रशिक्षणमा प्रयोग गर्न सकिन्छ (स्पष्टताको लागि हामी भेक्टरका पहिलो २० घटकहरू मात्र देखाउँछौं):\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": []
},
{
"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": [
"CBoW र Skip-Grams दुबै \"predictive\" embeddings हुन्, किनकि यी केवल स्थानीय सन्दर्भहरूलाई मात्र ध्यानमा राख्छन्। Word2Vec ले विश्वव्यापी सन्दर्भको फाइदा उठाउँदैन।\n",
"\n",
"**FastText** ले Word2Vec मा आधारित भएर प्रत्येक शब्द र शब्दभित्र पाइने अक्षर n-grams को लागि भेक्टर प्रतिनिधित्वहरू सिक्छ। प्रतिनिधित्वहरूको मानहरू प्रत्येक प्रशिक्षण चरणमा एक भेक्टरमा औसत गरिन्छ। यसले पूर्व-प्रशिक्षणमा धेरै अतिरिक्त गणना थपे पनि, यसले शब्द embeddings लाई उप-शब्द जानकारी समेट्न सक्षम बनाउँछ।\n",
"\n",
"अर्को विधि, **GloVe**, सह-अवस्थित म्याट्रिक्सको विचारलाई उपयोग गर्दछ, सह-अवस्थित म्याट्रिक्सलाई अधिक अभिव्यक्तिपूर्ण र गैर-रेखीय शब्द भेक्टरहरूमा विघटन गर्न न्युरल विधिहरू प्रयोग गर्दछ।\n",
"\n",
"तपाईं embeddings लाई FastText र GloVe मा परिवर्तन गरेर उदाहरणसँग खेल्न सक्नुहुन्छ, किनकि gensim ले विभिन्न शब्द embedding मोडेलहरूलाई समर्थन गर्दछ।\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## PyTorch मा प्रि-ट्रेन गरिएको एम्बेडिङहरू प्रयोग गर्दै\n",
"\n",
"हामी माथिको उदाहरणलाई संशोधन गरेर हाम्रो एम्बेडिङ लेयरको म्याट्रिक्सलाई Word2Vec जस्ता अर्थपूर्ण एम्बेडिङहरूद्वारा पहिले नै भरिएको बनाउन सक्छौं। हामीले ध्यान दिनुपर्छ कि प्रि-ट्रेन गरिएको एम्बेडिङको शब्दावली र हाम्रो पाठ कर्पसको शब्दावली सम्भवतः मेल खाने छैन, त्यसैले हामी हराएका शब्दहरूको लागि तौलहरूलाई र्यान्डम मानहरूद्वारा आरम्भ गर्नेछौं:\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": []
},
{
"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": [
"हाम्रो अवस्थामा, हामीले शुद्धतामा ठूलो वृद्धि देख्दैनौं, जुन सम्भवतः धेरै फरक शब्दावलीका कारण हो। \n",
"विभिन्न शब्दावलीको समस्यालाई समाधान गर्न, हामी निम्न समाधानहरू प्रयोग गर्न सक्छौं: \n",
"* हाम्रो शब्दावलीमा आधारित word2vec मोडेल पुनः-प्रशिक्षण गर्नुहोस् \n",
"* प्रि-ट्रेन गरिएको word2vec मोडेलको शब्दावली प्रयोग गरेर हाम्रो डेटासेट लोड गर्नुहोस्। डेटासेट लोड गर्दा प्रयोग गरिने शब्दावली निर्दिष्ट गर्न सकिन्छ। \n",
"\n",
"दोस्रो विधि सजिलो देखिन्छ, विशेष गरी किनभने PyTorch `torchtext` फ्रेमवर्कमा embeddings को लागि बिल्ट-इन समर्थन छ। उदाहरणका लागि, हामी GloVe-आधारित शब्दावली निम्न तरिकाले निर्माण गर्न सक्छौं: \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": [
"लोड गरिएको शब्दकोशमा निम्न आधारभूत कार्यहरू छन्:\n",
"* `vocab.stoi` शब्दकोशले हामीलाई शब्दलाई यसको शब्दकोश सूचकांकमा परिवर्तन गर्न अनुमति दिन्छ\n",
"* `vocab.itos` यसको उल्टो गर्छ - नम्बरलाई शब्दमा परिवर्तन गर्छ\n",
"* `vocab.vectors` एम्बेडिङ भेक्टरहरूको एरे हो, त्यसैले कुनै शब्द `s` को एम्बेडिङ प्राप्त गर्न हामीले `vocab.vectors[vocab.stoi[s]]` प्रयोग गर्नुपर्छ\n",
"\n",
"यहाँ एम्बेडिङहरूलाई परिमार्जन गरेर समीकरण **kind-man+woman = queen** प्रदर्शन गर्ने उदाहरण छ (यसलाई काम गर्नका लागि मैले थोरै गुणांक समायोजन गर्नुपर्‍यो):\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": [
"हामीले ती एम्बेडिङहरू प्रयोग गरेर वर्गीकरणकर्ता प्रशिक्षण गर्न, पहिलो चरणमा हाम्रो डेटासेटलाई 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": [
"जस्तो कि हामीले माथि देख्यौं, सबै भेक्टर एम्बेडिङहरू `vocab.vectors` म्याट्रिक्समा संग्रहित छन्। यसले ती वजनहरूलाई एम्बेडिङ तहको वजनहरूमा साधारण प्रतिलिपि गरेर लोड गर्न अत्यन्त सजिलो बनाउँछ:\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": [
"हाम्रो डाटासेटका केही शब्दहरू प्रि-ट्रेन गरिएको GloVe शब्दकोशमा नभएको कारणले गर्दा हामीले शुद्धतामा उल्लेखनीय वृद्धि देख्न नसकेको मुख्य कारणहरू मध्ये एक हो, र त्यसैले ती शब्दहरूलाई मूलतः बेवास्ता गरिएको छ। यस तथ्यलाई पार गर्नका लागि, हामी हाम्रो डाटासेटमा आफ्नै एम्बेडिङहरू प्रशिक्षण गर्न सक्छौं।\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## सन्दर्भगत एम्बेडिङहरू\n",
"\n",
"Word2Vec जस्ता परम्परागत प्रीट्रेन गरिएको एम्बेडिङ प्रतिनिधित्वहरूको एउटा मुख्य सीमाबद्धता भनेको शब्द अर्थ अस्पष्टताको समस्या हो। यद्यपि प्रीट्रेन गरिएको एम्बेडिङहरूले शब्दहरूको सन्दर्भमा केही अर्थ समेट्न सक्छन्, कुनै पनि शब्दको सबै सम्भावित अर्थ एउटै एम्बेडिङमा समेटिन्छ। यसले डाउनस्ट्रीम मोडेलहरूमा समस्या उत्पन्न गर्न सक्छ, किनभने धेरै शब्दहरू, जस्तै 'play' शब्द, प्रयोग गरिएको सन्दर्भ अनुसार फरक अर्थ राख्छन्।\n",
"\n",
"उदाहरणका लागि, 'play' शब्दले यी दुई वाक्यहरूमा निकै फरक अर्थ राख्छ:\n",
"- म थिएटरमा एउटा **play** हेर्न गएँ।\n",
"- जोन आफ्ना साथीहरूसँग **play** गर्न चाहन्छ।\n",
"\n",
"माथिका प्रीट्रेन गरिएको एम्बेडिङहरूले 'play' शब्दका यी दुवै अर्थहरू एउटै एम्बेडिङमा प्रतिनिधित्व गर्छन्। यस सीमाबद्धतालाई पार गर्नका लागि, हामीले **भाषा मोडेल**मा आधारित एम्बेडिङहरू निर्माण गर्न आवश्यक छ, जुन ठूलो पाठको संग्रहमा प्रशिक्षित गरिएको हुन्छ र *जान्दछ* कि शब्दहरू विभिन्न सन्दर्भहरूमा कसरी राख्न सकिन्छ। सन्दर्भगत एम्बेडिङहरूको चर्चा यस ट्युटोरियलको दायराभन्दा बाहिर पर्छ, तर हामी यसबारे भाषा मोडेलहरूको कुरा गर्दा अर्को युनिटमा फर्कनेछौं।\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**अस्वीकरण**: \nयो दस्तावेज़ AI अनुवाद सेवा [Co-op Translator](https://github.com/Azure/co-op-translator) प्रयोग गरेर अनुवाद गरिएको छ। हामी शुद्धताको लागि प्रयास गर्छौं, तर कृपया ध्यान दिनुहोस् कि स्वचालित अनुवादमा त्रुटिहरू वा अशुद्धताहरू हुन सक्छ। यसको मूल भाषा मा रहेको मूल दस्तावेज़लाई आधिकारिक स्रोत मानिनुपर्छ। महत्वपूर्ण जानकारीको लागि, व्यावसायिक मानव अनुवाद सिफारिस गरिन्छ। यस अनुवादको प्रयोगबाट उत्पन्न हुने कुनै पनि गलतफहमी वा गलत व्याख्याको लागि हामी जिम्मेवार हुने छैनौं।\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-28T09:47:38+00:00",
"source_file": "lessons/5-NLP/14-Embeddings/EmbeddingsPyTorch.ipynb",
"language_code": "ne"
}
},
"nbformat": 4,
"nbformat_minor": 2
}