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

726 lines
28 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Pembenaman\n",
"\n",
"Dalam contoh sebelumnya, kita telah bekerja dengan vektor bag-of-words berdimensi tinggi dengan panjang `vocab_size`, dan kita secara eksplisit menukar daripada vektor perwakilan kedudukan berdimensi rendah kepada perwakilan one-hot yang jarang. Perwakilan one-hot ini tidak cekap dari segi memori, selain itu, setiap perkataan dianggap secara bebas antara satu sama lain, iaitu vektor one-hot yang dikodkan tidak menyatakan sebarang persamaan semantik antara perkataan.\n",
"\n",
"Dalam unit ini, kita akan terus meneroka set data **News AG**. Untuk memulakan, mari kita muatkan data dan dapatkan beberapa definisi daripada buku nota sebelumnya.\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": [
"## Apakah itu embedding?\n",
"\n",
"Idea **embedding** adalah untuk mewakili perkataan dengan vektor padat berdimensi rendah, yang mencerminkan makna semantik sesuatu perkataan. Kita akan bincangkan kemudian bagaimana untuk membina embedding perkataan yang bermakna, tetapi buat masa ini, anggaplah embedding sebagai cara untuk mengurangkan dimensi vektor perkataan.\n",
"\n",
"Jadi, lapisan embedding akan mengambil satu perkataan sebagai input, dan menghasilkan vektor output dengan `embedding_size` yang ditentukan. Dalam erti kata lain, ia sangat mirip dengan lapisan `Linear`, tetapi bukannya mengambil vektor yang dikodkan satu-panas (one-hot encoded), ia akan dapat mengambil nombor perkataan sebagai input.\n",
"\n",
"Dengan menggunakan lapisan embedding sebagai lapisan pertama dalam rangkaian kita, kita boleh beralih daripada model bag-of-words kepada model **embedding bag**, di mana kita mula-mula menukar setiap perkataan dalam teks kita kepada embedding yang sepadan, dan kemudian mengira beberapa fungsi agregat ke atas semua embedding tersebut, seperti `sum`, `average` atau `max`.\n",
"\n",
"![Imej menunjukkan pengklasifikasi embedding untuk lima perkataan dalam urutan.](../../../../../translated_images/ms/embedding-classifier-example.b77f021a7ee67eee.webp)\n",
"\n",
"Rangkaian neural pengklasifikasi kita akan bermula dengan lapisan embedding, kemudian lapisan agregasi, dan pengklasifikasi linear di atasnya:\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": [
"### Mengendalikan Saiz Urutan Pembolehubah\n",
"\n",
"Akibat daripada seni bina ini, minibatch untuk rangkaian kita perlu dicipta dengan cara tertentu. Dalam unit sebelumnya, semasa menggunakan bag-of-words, semua tensor BoW dalam satu minibatch mempunyai saiz yang sama `vocab_size`, tanpa mengira panjang sebenar urutan teks kita. Apabila kita beralih kepada embedding perkataan, kita akan berakhir dengan bilangan perkataan yang berbeza dalam setiap sampel teks, dan apabila menggabungkan sampel-sampel tersebut ke dalam minibatch, kita perlu menggunakan padding.\n",
"\n",
"Ini boleh dilakukan dengan menggunakan teknik yang sama iaitu menyediakan fungsi `collate_fn` kepada datasource:\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": [
"### Melatih pengklasifikasi embedding\n",
"\n",
"Sekarang setelah kita menetapkan dataloader yang sesuai, kita boleh melatih model menggunakan fungsi latihan yang telah kita tetapkan dalam unit sebelumnya:\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": [
"> **Nota**: Kami hanya melatih untuk 25k rekod di sini (kurang daripada satu epoch penuh) demi menjimatkan masa, tetapi anda boleh meneruskan latihan, menulis fungsi untuk melatih beberapa epoch, dan bereksperimen dengan parameter kadar pembelajaran untuk mencapai ketepatan yang lebih tinggi. Anda sepatutnya dapat mencapai ketepatan sekitar 90%.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Lapisan EmbeddingBag dan Representasi Jujukan Panjang Berubah\n",
"\n",
"Dalam seni bina sebelumnya, kami perlu menambah semua jujukan kepada panjang yang sama untuk dimuatkan ke dalam minibatch. Ini bukan cara yang paling efisien untuk mewakili jujukan panjang berubah - pendekatan lain adalah menggunakan vektor **offset**, yang akan menyimpan offset semua jujukan yang disimpan dalam satu vektor besar.\n",
"\n",
"![Imej menunjukkan representasi jujukan offset](../../../../../translated_images/ms/offset-sequence-representation.eb73fcefb29b46ee.webp)\n",
"\n",
"> **Nota**: Dalam gambar di atas, kami menunjukkan jujukan watak, tetapi dalam contoh kami, kami bekerja dengan jujukan perkataan. Walau bagaimanapun, prinsip umum mewakili jujukan dengan vektor offset tetap sama.\n",
"\n",
"Untuk bekerja dengan representasi offset, kami menggunakan lapisan [`EmbeddingBag`](https://pytorch.org/docs/stable/generated/torch.nn.EmbeddingBag.html). Ia serupa dengan `Embedding`, tetapi ia mengambil vektor kandungan dan vektor offset sebagai input, dan ia juga termasuk lapisan purata, yang boleh menjadi `mean`, `sum` atau `max`.\n",
"\n",
"Berikut adalah rangkaian yang diubah suai yang menggunakan `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": [
"Untuk menyediakan set data untuk latihan, kita perlu menyediakan fungsi penukaran yang akan menyediakan vektor anjakan:\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": [
"Perhatikan bahawa tidak seperti dalam semua contoh sebelumnya, rangkaian kita kini menerima dua parameter: vektor data dan vektor offset, yang mempunyai saiz berbeza. Begitu juga, pemuat data kita juga memberikan kita 3 nilai bukannya 2: kedua-dua vektor teks dan offset disediakan sebagai ciri. Oleh itu, kita perlu sedikit menyesuaikan fungsi latihan kita untuk mengurus perkara tersebut:\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": [
"## Perwakilan Semantik: Word2Vec\n",
"\n",
"Dalam contoh sebelumnya, lapisan embedding model belajar memetakan perkataan kepada perwakilan vektor, namun, perwakilan ini tidak mempunyai banyak makna semantik. Adalah lebih baik jika kita dapat belajar perwakilan vektor sedemikian rupa, di mana perkataan yang serupa atau sinonim akan sepadan dengan vektor yang dekat antara satu sama lain berdasarkan jarak vektor tertentu (contohnya, jarak Euclidean).\n",
"\n",
"Untuk mencapai ini, kita perlu melatih model embedding kita terlebih dahulu pada koleksi teks yang besar dengan cara tertentu. Salah satu kaedah pertama untuk melatih perwakilan semantik ini dipanggil [Word2Vec](https://en.wikipedia.org/wiki/Word2vec). Ia berdasarkan dua seni bina utama yang digunakan untuk menghasilkan perwakilan teragih bagi perkataan:\n",
"\n",
" - **Continuous bag-of-words** (CBoW) — dalam seni bina ini, kita melatih model untuk meramalkan satu perkataan berdasarkan konteks sekeliling. Diberikan ngram $(W_{-2},W_{-1},W_0,W_1,W_2)$, matlamat model adalah untuk meramalkan $W_0$ daripada $(W_{-2},W_{-1},W_1,W_2)$.\n",
" - **Continuous skip-gram** adalah bertentangan dengan CBoW. Model ini menggunakan tetingkap perkataan konteks sekeliling untuk meramalkan perkataan semasa.\n",
"\n",
"CBoW lebih pantas, manakala skip-gram lebih perlahan, tetapi lebih baik dalam mewakili perkataan yang jarang digunakan.\n",
"\n",
"![Imej menunjukkan kedua-dua algoritma CBoW dan Skip-Gram untuk menukar perkataan kepada vektor.](../../../../../translated_images/ms/example-algorithms-for-converting-words-to-vectors.fbe9207a726922f6.webp)\n",
"\n",
"Untuk mencuba embedding word2vec yang telah dilatih terlebih dahulu pada dataset Google News, kita boleh menggunakan perpustakaan **gensim**. Di bawah ini, kita mencari perkataan yang paling serupa dengan 'neural'\n",
"\n",
"> **Nota:** Apabila anda mula-mula mencipta vektor perkataan, proses memuat turun mungkin mengambil sedikit masa!\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": [
"Kita juga boleh mengira penjelmaan vektor daripada perkataan, untuk digunakan dalam melatih model klasifikasi (kami hanya menunjukkan 20 komponen pertama vektor untuk kejelasan):\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": [
"Perkara hebat tentang pengekodan semantik ialah anda boleh memanipulasi pengekodan vektor untuk mengubah semantik. Sebagai contoh, kita boleh meminta untuk mencari perkataan, yang pengekodan vektornya sedekat mungkin dengan perkataan *raja* dan *wanita*, dan sejauh mungkin dari perkataan *lelaki*:\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": [
"Kedua-dua CBoW dan Skip-Grams adalah \"embedding ramalan\", kerana mereka hanya mengambil kira konteks tempatan. Word2Vec tidak memanfaatkan konteks global.\n",
"\n",
"**FastText** dibina berdasarkan Word2Vec dengan mempelajari representasi vektor untuk setiap perkataan dan n-gram aksara yang terdapat dalam setiap perkataan. Nilai representasi ini kemudian dirata-rata menjadi satu vektor pada setiap langkah latihan. Walaupun ini menambah banyak pengiraan tambahan semasa pra-latihan, ia membolehkan embedding perkataan menyandikan maklumat sub-perkataan.\n",
"\n",
"Kaedah lain, **GloVe**, menggunakan idea matriks ko-berlaku, dan menggunakan kaedah neural untuk menguraikan matriks ko-berlaku menjadi vektor perkataan yang lebih ekspresif dan tidak linear.\n",
"\n",
"Anda boleh mencuba contoh ini dengan menukar embedding kepada FastText dan GloVe, kerana gensim menyokong beberapa model embedding perkataan yang berbeza.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Menggunakan Pra-Latihan Embedding dalam PyTorch\n",
"\n",
"Kita boleh mengubah contoh di atas untuk mengisi matriks dalam lapisan embedding kita dengan embedding semantik, seperti Word2Vec. Kita perlu mengambil kira bahawa perbendaharaan kata embedding pra-latihan dan korpus teks kita mungkin tidak sepadan, jadi kita akan memulakan berat untuk perkataan yang hilang dengan nilai rawak:\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": [
"Sekarang mari kita latih model kita. Perhatikan bahawa masa yang diperlukan untuk melatih model adalah jauh lebih lama berbanding contoh sebelumnya, disebabkan saiz lapisan embedding yang lebih besar, dan oleh itu bilangan parameter yang jauh lebih tinggi. Selain itu, kerana perkara ini, kita mungkin perlu melatih model kita dengan lebih banyak contoh jika kita ingin mengelakkan overfitting.\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": [
"Dalam kes kita, kita tidak melihat peningkatan ketepatan yang besar, yang mungkin disebabkan oleh perbezaan kosa kata yang ketara. \n",
"Untuk mengatasi masalah perbezaan kosa kata, kita boleh menggunakan salah satu penyelesaian berikut: \n",
"* Melatih semula model word2vec pada kosa kata kita \n",
"* Memuatkan dataset kita dengan kosa kata daripada model word2vec yang telah dilatih. Kosa kata yang digunakan untuk memuatkan dataset boleh ditentukan semasa proses pemuatan. \n",
"\n",
"Pendekatan kedua kelihatan lebih mudah, terutamanya kerana rangka kerja PyTorch `torchtext` mengandungi sokongan terbina dalam untuk embedding. Sebagai contoh, kita boleh mencipta kosa kata berasaskan GloVe dengan cara berikut: \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": [
"Perbendaharaan kata yang dimuatkan mempunyai operasi asas berikut:\n",
"* Kamus `vocab.stoi` membolehkan kita menukar perkataan kepada indeks kamusnya\n",
"* `vocab.itos` melakukan sebaliknya - menukar nombor kepada perkataan\n",
"* `vocab.vectors` adalah array vektor embedding, jadi untuk mendapatkan embedding bagi perkataan `s` kita perlu menggunakan `vocab.vectors[vocab.stoi[s]]`\n",
"\n",
"Berikut adalah contoh manipulasi embedding untuk menunjukkan persamaan **kind-man+woman = queen** (saya terpaksa menyesuaikan koefisien sedikit untuk menjadikannya berfungsi):\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": [
"Untuk melatih pengklasifikasi menggunakan pengekodan tersebut, kita perlu mengekod dataset kita menggunakan perbendaharaan kata 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": [
"Seperti yang telah kita lihat di atas, semua vektor embedding disimpan dalam matriks `vocab.vectors`. Ia menjadikannya sangat mudah untuk memuatkan berat tersebut ke dalam berat lapisan embedding menggunakan penyalinan mudah:\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": [
"Sekarang mari kita latih model kita dan lihat jika kita mendapat hasil yang lebih baik:\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": [
"Salah satu sebab kita tidak melihat peningkatan ketepatan yang ketara adalah kerana beberapa perkataan daripada set data kita tiada dalam kosa kata GloVe yang telah dilatih, dan oleh itu ia pada dasarnya diabaikan. Untuk mengatasi perkara ini, kita boleh melatih penjelmaan kita sendiri pada set data kita.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Pembenaman Kontekstual\n",
"\n",
"Satu kekurangan utama dalam representasi pembenaman pralatih tradisional seperti Word2Vec adalah masalah penyahkekeliruan makna perkataan. Walaupun pembenaman pralatih dapat menangkap sebahagian makna perkataan dalam konteks, setiap kemungkinan makna sesuatu perkataan dikodkan ke dalam pembenaman yang sama. Ini boleh menyebabkan masalah dalam model hiliran, kerana banyak perkataan seperti 'play' mempunyai makna yang berbeza bergantung pada konteks penggunaannya.\n",
"\n",
"Sebagai contoh, perkataan 'play' dalam dua ayat berikut mempunyai makna yang agak berbeza:\n",
"- Saya pergi ke sebuah **play** di teater.\n",
"- John mahu **play** dengan kawan-kawannya.\n",
"\n",
"Pembenaman pralatih di atas mewakili kedua-dua makna perkataan 'play' dalam pembenaman yang sama. Untuk mengatasi kekurangan ini, kita perlu membina pembenaman berdasarkan **model bahasa**, yang dilatih pada korpus teks yang besar, dan *tahu* bagaimana perkataan boleh disusun dalam pelbagai konteks. Perbincangan mengenai pembenaman kontekstual adalah di luar skop tutorial ini, tetapi kita akan kembali kepada topik ini apabila membincangkan model bahasa dalam unit seterusnya.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Penafian**: \nDokumen ini telah diterjemahkan menggunakan perkhidmatan terjemahan AI [Co-op Translator](https://github.com/Azure/co-op-translator). Walaupun kami berusaha untuk memastikan ketepatan, sila ambil perhatian bahawa terjemahan automatik mungkin mengandungi kesilapan atau ketidaktepatan. Dokumen asal dalam bahasa asalnya harus dianggap sebagai sumber yang berwibawa. Untuk maklumat yang kritikal, terjemahan manusia profesional adalah disyorkan. Kami tidak bertanggungjawab atas sebarang salah faham atau salah tafsir yang timbul daripada penggunaan terjemahan ini.\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-29T16:31:59+00:00",
"source_file": "lessons/5-NLP/14-Embeddings/EmbeddingsPyTorch.ipynb",
"language_code": "ms"
}
},
"nbformat": 4,
"nbformat_minor": 2
}