{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Rangkaian Neural Berulang\n", "\n", "Dalam modul sebelumnya, kita telah menggunakan representasi semantik teks yang kaya, dan pengklasifikasi linear yang ringkas di atas embedding. Apa yang dilakukan oleh seni bina ini adalah menangkap makna agregat perkataan dalam satu ayat, tetapi ia tidak mengambil kira **susunan** perkataan, kerana operasi pengagregatan di atas embedding telah menghapuskan maklumat ini daripada teks asal. Oleh kerana model-model ini tidak dapat memodelkan susunan perkataan, mereka tidak dapat menyelesaikan tugas yang lebih kompleks atau samar seperti penjanaan teks atau menjawab soalan.\n", "\n", "Untuk menangkap makna urutan teks, kita perlu menggunakan seni bina rangkaian neural lain, yang dipanggil **rangkaian neural berulang**, atau RNN. Dalam RNN, kita memproses ayat kita melalui rangkaian satu simbol pada satu masa, dan rangkaian menghasilkan beberapa **keadaan**, yang kemudian kita masukkan semula ke dalam rangkaian bersama simbol seterusnya.\n", "\n", "Diberikan urutan token input $X_0,\\dots,X_n$, RNN mencipta satu urutan blok rangkaian neural, dan melatih urutan ini secara hujung ke hujung menggunakan back propagation. Setiap blok rangkaian mengambil pasangan $(X_i,S_i)$ sebagai input, dan menghasilkan $S_{i+1}$ sebagai hasil. Keadaan akhir $S_n$ atau output $X_n$ dimasukkan ke dalam pengklasifikasi linear untuk menghasilkan keputusan. Semua blok rangkaian berkongsi berat yang sama, dan dilatih secara hujung ke hujung menggunakan satu laluan back propagation.\n", "\n", "Oleh kerana vektor keadaan $S_0,\\dots,S_n$ dihantar melalui rangkaian, ia mampu mempelajari kebergantungan berurutan antara perkataan. Sebagai contoh, apabila perkataan *tidak* muncul di suatu tempat dalam urutan, ia boleh belajar untuk menegatifkan elemen tertentu dalam vektor keadaan, menghasilkan penafian.\n", "\n", "> Oleh kerana berat semua blok RNN dalam gambar adalah sama, gambar yang sama boleh diwakili sebagai satu blok (di sebelah kanan) dengan gelung maklum balas berulang, yang menghantar keadaan output rangkaian kembali ke input.\n", "\n", "Mari kita lihat bagaimana rangkaian neural berulang dapat membantu kita mengklasifikasikan dataset berita kita.\n" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Loading dataset...\n", "Building vocab...\n" ] } ], "source": [ "import torch\n", "import torchtext\n", "from torchnlp import *\n", "train_dataset, test_dataset, classes, vocab = load_dataset()\n", "vocab_size = len(vocab)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Pengelas RNN Ringkas\n", "\n", "Dalam kes RNN ringkas, setiap unit berulang adalah rangkaian linear mudah, yang mengambil vektor input yang digabungkan dengan vektor keadaan, dan menghasilkan vektor keadaan baharu. PyTorch mewakili unit ini dengan kelas `RNNCell`, dan rangkaian sel-sel tersebut - sebagai lapisan `RNN`.\n", "\n", "Untuk mentakrifkan pengelas RNN, kita akan terlebih dahulu menggunakan lapisan embedding untuk mengurangkan dimensi kosa kata input, dan kemudian meletakkan lapisan RNN di atasnya:\n" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "class RNNClassifier(torch.nn.Module):\n", " def __init__(self, vocab_size, embed_dim, hidden_dim, num_class):\n", " super().__init__()\n", " self.hidden_dim = hidden_dim\n", " self.embedding = torch.nn.Embedding(vocab_size, embed_dim)\n", " self.rnn = torch.nn.RNN(embed_dim,hidden_dim,batch_first=True)\n", " self.fc = torch.nn.Linear(hidden_dim, num_class)\n", "\n", " def forward(self, x):\n", " batch_size = x.size(0)\n", " x = self.embedding(x)\n", " x,h = self.rnn(x)\n", " return self.fc(x.mean(dim=1))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> **Nota:** Kami menggunakan lapisan embedding yang tidak dilatih di sini untuk kesederhanaan, tetapi untuk hasil yang lebih baik, kami boleh menggunakan lapisan embedding yang telah dilatih terlebih dahulu dengan embedding Word2Vec atau GloVe, seperti yang diterangkan dalam unit sebelumnya. Untuk pemahaman yang lebih baik, anda mungkin ingin menyesuaikan kod ini untuk berfungsi dengan embedding yang telah dilatih terlebih dahulu.\n", "\n", "Dalam kes kami, kami akan menggunakan pemuat data yang dipadatkan, jadi setiap batch akan mempunyai beberapa urutan yang dipadatkan dengan panjang yang sama. Lapisan RNN akan mengambil urutan tensor embedding, dan menghasilkan dua output:\n", "* $x$ adalah urutan output sel RNN pada setiap langkah\n", "* $h$ adalah keadaan tersembunyi akhir untuk elemen terakhir dalam urutan\n", "\n", "Kami kemudian menerapkan pengklasifikasi linear yang sepenuhnya bersambung untuk mendapatkan bilangan kelas.\n", "\n", "> **Nota:** RNN agak sukar untuk dilatih, kerana apabila sel RNN diurai sepanjang panjang urutan, bilangan lapisan yang terlibat dalam propagasi balik menjadi sangat besar. Oleh itu, kita perlu memilih kadar pembelajaran yang kecil, dan melatih rangkaian pada dataset yang lebih besar untuk menghasilkan keputusan yang baik. Ia boleh mengambil masa yang agak lama, jadi penggunaan GPU adalah disyorkan.\n" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3200: acc=0.3090625\n", "6400: acc=0.38921875\n", "9600: acc=0.4590625\n", "12800: acc=0.511953125\n", "16000: acc=0.5506875\n", "19200: acc=0.57921875\n", "22400: acc=0.6070089285714285\n", "25600: acc=0.6304296875\n", "28800: acc=0.6484027777777778\n", "32000: acc=0.66509375\n", "35200: acc=0.6790056818181818\n", "38400: acc=0.6929166666666666\n", "41600: acc=0.7035817307692308\n", "44800: acc=0.7137276785714286\n", "48000: acc=0.72225\n", "51200: acc=0.73001953125\n", "54400: acc=0.7372794117647059\n", "57600: acc=0.7436631944444444\n", "60800: acc=0.7503947368421052\n", "64000: acc=0.75634375\n", "67200: acc=0.7615773809523809\n", "70400: acc=0.7662642045454545\n", "73600: acc=0.7708423913043478\n", "76800: acc=0.7751822916666666\n", "80000: acc=0.7790625\n", "83200: acc=0.7825\n", "86400: acc=0.7858564814814815\n", "89600: acc=0.7890513392857142\n", "92800: acc=0.7920474137931034\n", "96000: acc=0.7952708333333334\n", "99200: acc=0.7982258064516129\n", "102400: acc=0.80099609375\n", "105600: acc=0.8037594696969697\n", "108800: acc=0.8060569852941176\n" ] } ], "source": [ "train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=16, collate_fn=padify, shuffle=True)\n", "net = RNNClassifier(vocab_size,64,32,len(classes)).to(device)\n", "train_epoch(net,train_loader, lr=0.001)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Long Short Term Memory (LSTM)\n", "\n", "Salah satu masalah utama RNN klasik ialah masalah **vanishing gradients**. Disebabkan RNN dilatih secara end-to-end dalam satu laluan back-propagation, ia menghadapi kesukaran untuk menyebarkan ralat ke lapisan pertama rangkaian, dan oleh itu rangkaian tidak dapat mempelajari hubungan antara token yang jauh. Salah satu cara untuk mengelakkan masalah ini ialah dengan memperkenalkan **pengurusan keadaan secara eksplisit** menggunakan apa yang dipanggil **gates**. Terdapat dua seni bina yang paling terkenal dalam kategori ini: **Long Short Term Memory** (LSTM) dan **Gated Relay Unit** (GRU).\n", "\n", "![Imej menunjukkan contoh sel long short term memory](../../../../../lessons/5-NLP/16-RNN/images/long-short-term-memory-cell.svg)\n", "\n", "Rangkaian LSTM disusun dengan cara yang serupa dengan RNN, tetapi terdapat dua keadaan yang dihantar dari lapisan ke lapisan: keadaan sebenar $c$, dan vektor tersembunyi $h$. Pada setiap unit, vektor tersembunyi $h_i$ digabungkan dengan input $x_i$, dan mereka mengawal apa yang berlaku kepada keadaan $c$ melalui **gates**. Setiap gate ialah rangkaian neural dengan pengaktifan sigmoid (output dalam julat $[0,1]$), yang boleh dianggap sebagai topeng bitwise apabila didarabkan dengan vektor keadaan. Terdapat gate berikut (dari kiri ke kanan pada gambar di atas):\n", "* **forget gate** mengambil vektor tersembunyi dan menentukan komponen vektor $c$ yang perlu dilupakan, dan yang perlu diteruskan.\n", "* **input gate** mengambil beberapa maklumat daripada input dan vektor tersembunyi, dan memasukkannya ke dalam keadaan.\n", "* **output gate** mengubah keadaan melalui beberapa lapisan linear dengan pengaktifan $\\tanh$, kemudian memilih beberapa komponennya menggunakan vektor tersembunyi $h_i$ untuk menghasilkan keadaan baharu $c_{i+1}$.\n", "\n", "Komponen keadaan $c$ boleh dianggap sebagai beberapa bendera yang boleh dihidupkan dan dimatikan. Sebagai contoh, apabila kita menemui nama *Alice* dalam urutan, kita mungkin menganggap bahawa ia merujuk kepada watak perempuan, dan menaikkan bendera dalam keadaan bahawa kita mempunyai kata nama perempuan dalam ayat. Apabila kita seterusnya menemui frasa *and Tom*, kita akan menaikkan bendera bahawa kita mempunyai kata nama majmuk. Oleh itu, dengan memanipulasi keadaan, kita boleh menjejaki sifat tatabahasa bahagian ayat.\n", "\n", "> **Note**: Sumber yang hebat untuk memahami struktur dalaman LSTM ialah artikel hebat ini [Understanding LSTM Networks](https://colah.github.io/posts/2015-08-Understanding-LSTMs/) oleh Christopher Olah.\n", "\n", "Walaupun struktur dalaman sel LSTM mungkin kelihatan kompleks, PyTorch menyembunyikan pelaksanaan ini di dalam kelas `LSTMCell`, dan menyediakan objek `LSTM` untuk mewakili keseluruhan lapisan LSTM. Oleh itu, pelaksanaan pengklasifikasi LSTM akan sangat serupa dengan RNN ringkas yang telah kita lihat di atas:\n" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "class LSTMClassifier(torch.nn.Module):\n", " def __init__(self, vocab_size, embed_dim, hidden_dim, num_class):\n", " super().__init__()\n", " self.hidden_dim = hidden_dim\n", " self.embedding = torch.nn.Embedding(vocab_size, embed_dim)\n", " self.embedding.weight.data = torch.randn_like(self.embedding.weight.data)-0.5\n", " self.rnn = torch.nn.LSTM(embed_dim,hidden_dim,batch_first=True)\n", " self.fc = torch.nn.Linear(hidden_dim, num_class)\n", "\n", " def forward(self, x):\n", " batch_size = x.size(0)\n", " x = self.embedding(x)\n", " x,(h,c) = self.rnn(x)\n", " return self.fc(h[-1])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3200: acc=0.259375\n", "6400: acc=0.25859375\n", "9600: acc=0.26177083333333334\n", "12800: acc=0.2784375\n", "16000: acc=0.313\n", "19200: acc=0.3528645833333333\n", "22400: acc=0.3965625\n", "25600: acc=0.4385546875\n", "28800: acc=0.4752777777777778\n", "32000: acc=0.505375\n", "35200: acc=0.5326704545454546\n", "38400: acc=0.5557552083333334\n", "41600: acc=0.5760817307692307\n", "44800: acc=0.5954910714285714\n", "48000: acc=0.6118333333333333\n", "51200: acc=0.62681640625\n", "54400: acc=0.6404779411764706\n", "57600: acc=0.6520138888888889\n", "60800: acc=0.662828947368421\n", "64000: acc=0.673546875\n", "67200: acc=0.6831547619047619\n", "70400: acc=0.6917897727272727\n", "73600: acc=0.6997146739130434\n", "76800: acc=0.707109375\n", "80000: acc=0.714075\n", "83200: acc=0.7209134615384616\n", "86400: acc=0.727037037037037\n", "89600: acc=0.7326674107142858\n", "92800: acc=0.7379633620689655\n", "96000: acc=0.7433645833333333\n", "99200: acc=0.7479032258064516\n", "102400: acc=0.752119140625\n", "105600: acc=0.7562405303030303\n", "108800: acc=0.76015625\n", "112000: acc=0.7641339285714286\n", "115200: acc=0.7677777777777778\n", "118400: acc=0.7711233108108108\n" ] }, { "data": { "text/plain": [ "(0.03487814127604167, 0.7728)" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net = LSTMClassifier(vocab_size,64,32,len(classes)).to(device)\n", "train_epoch(net,train_loader, lr=0.001)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Jujukan yang Dipadatkan\n", "\n", "Dalam contoh kita, kita perlu menambah semua jujukan dalam minibatch dengan vektor kosong. Walaupun ini menyebabkan pembaziran memori, dengan RNN, lebih kritikal bahawa sel-sel RNN tambahan dicipta untuk item input yang dipadatkan, yang turut serta dalam latihan tetapi tidak membawa sebarang maklumat input penting. Adalah lebih baik untuk melatih RNN hanya mengikut saiz jujukan sebenar.\n", "\n", "Untuk melakukannya, satu format khas untuk penyimpanan jujukan yang dipadatkan diperkenalkan dalam PyTorch. Katakan kita mempunyai minibatch input yang dipadatkan seperti berikut:\n", "```\n", "[[1,2,3,4,5],\n", " [6,7,8,0,0],\n", " [9,0,0,0,0]]\n", "```\n", "Di sini 0 mewakili nilai yang dipadatkan, dan vektor panjang sebenar bagi jujukan input adalah `[5,3,1]`.\n", "\n", "Untuk melatih RNN dengan jujukan yang dipadatkan secara efektif, kita mahu memulakan latihan kumpulan pertama sel RNN dengan minibatch besar (`[1,6,9]`), tetapi kemudian menghentikan pemprosesan jujukan ketiga, dan meneruskan latihan dengan minibatch yang lebih pendek (`[2,7]`, `[3,8]`), dan seterusnya. Oleh itu, jujukan yang dipadatkan diwakili sebagai satu vektor - dalam kes kita `[1,6,9,2,7,3,8,4,5]`, dan vektor panjang (`[5,3,1]`), yang daripadanya kita boleh membina semula minibatch yang dipadatkan asal.\n", "\n", "Untuk menghasilkan jujukan yang dipadatkan, kita boleh menggunakan fungsi `torch.nn.utils.rnn.pack_padded_sequence`. Semua lapisan berulang, termasuk RNN, LSTM dan GRU, menyokong jujukan yang dipadatkan sebagai input, dan menghasilkan output yang dipadatkan, yang boleh dinyahkod menggunakan `torch.nn.utils.rnn.pad_packed_sequence`.\n", "\n", "Untuk dapat menghasilkan jujukan yang dipadatkan, kita perlu menghantar vektor panjang kepada rangkaian, dan oleh itu kita memerlukan fungsi yang berbeza untuk menyediakan minibatch:\n" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "def pad_length(b):\n", " # build vectorized sequence\n", " v = [encode(x[1]) for x in b]\n", " # compute max length of a sequence in this minibatch and length sequence itself\n", " len_seq = list(map(len,v))\n", " l = max(len_seq)\n", " return ( # tuple of three tensors - labels, padded features, length sequence\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", " torch.tensor(len_seq)\n", " )\n", "\n", "train_loader_len = torch.utils.data.DataLoader(train_dataset, batch_size=16, collate_fn=pad_length, shuffle=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Rangkaian sebenar akan sangat serupa dengan `LSTMClassifier` di atas, tetapi laluan `forward` akan menerima kedua-dua minibatch yang telah dipadatkan dan vektor panjang jujukan. Selepas mengira embedding, kita mengira jujukan yang dipadatkan, menghantarnya ke lapisan LSTM, dan kemudian membuka semula hasilnya.\n", "\n", "> **Nota**: Sebenarnya, kita tidak menggunakan hasil yang telah dibuka `x`, kerana kita menggunakan output daripada lapisan tersembunyi dalam pengiraan berikutnya. Oleh itu, kita boleh membuang proses membuka sepenuhnya daripada kod ini. Sebab kita meletakkannya di sini adalah supaya anda dapat mengubah suai kod ini dengan mudah, sekiranya anda perlu menggunakan output rangkaian dalam pengiraan selanjutnya.\n" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "class LSTMPackClassifier(torch.nn.Module):\n", " def __init__(self, vocab_size, embed_dim, hidden_dim, num_class):\n", " super().__init__()\n", " self.hidden_dim = hidden_dim\n", " self.embedding = torch.nn.Embedding(vocab_size, embed_dim)\n", " self.embedding.weight.data = torch.randn_like(self.embedding.weight.data)-0.5\n", " self.rnn = torch.nn.LSTM(embed_dim,hidden_dim,batch_first=True)\n", " self.fc = torch.nn.Linear(hidden_dim, num_class)\n", "\n", " def forward(self, x, lengths):\n", " batch_size = x.size(0)\n", " x = self.embedding(x)\n", " pad_x = torch.nn.utils.rnn.pack_padded_sequence(x,lengths,batch_first=True,enforce_sorted=False)\n", " pad_x,(h,c) = self.rnn(pad_x)\n", " x, _ = torch.nn.utils.rnn.pad_packed_sequence(pad_x,batch_first=True)\n", " return self.fc(h[-1])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [] }, { "cell_type": "code", "execution_count": 8, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3200: acc=0.285625\n", "6400: acc=0.33359375\n", "9600: acc=0.3876041666666667\n", "12800: acc=0.44078125\n", "16000: acc=0.4825\n", "19200: acc=0.5235416666666667\n", "22400: acc=0.5559821428571429\n", "25600: acc=0.58609375\n", "28800: acc=0.6116666666666667\n", "32000: acc=0.63340625\n", "35200: acc=0.6525284090909091\n", "38400: acc=0.668515625\n", "41600: acc=0.6822596153846154\n", "44800: acc=0.6948214285714286\n", "48000: acc=0.7052708333333333\n", "51200: acc=0.71521484375\n", "54400: acc=0.7239889705882353\n", "57600: acc=0.7315277777777778\n", "60800: acc=0.7388486842105263\n", "64000: acc=0.74571875\n", "67200: acc=0.7518303571428572\n", "70400: acc=0.7576988636363636\n", "73600: acc=0.7628940217391305\n", "76800: acc=0.7681510416666667\n", "80000: acc=0.7728125\n", "83200: acc=0.7772235576923077\n", "86400: acc=0.7815393518518519\n", "89600: acc=0.7857700892857142\n", "92800: acc=0.7895043103448276\n", "96000: acc=0.7930520833333333\n", "99200: acc=0.7959072580645161\n", "102400: acc=0.798994140625\n", "105600: acc=0.802064393939394\n", "108800: acc=0.8051378676470589\n", "112000: acc=0.8077857142857143\n", "115200: acc=0.8104600694444445\n", "118400: acc=0.8128293918918919\n" ] }, { "data": { "text/plain": [ "(0.029785829671223958, 0.8138166666666666)" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net = LSTMPackClassifier(vocab_size,64,32,len(classes)).to(device)\n", "train_epoch_emb(net,train_loader_len, lr=0.001,use_pack_sequence=True)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> **Nota:** Anda mungkin telah perasan parameter `use_pack_sequence` yang kita berikan kepada fungsi latihan. Pada masa ini, fungsi `pack_padded_sequence` memerlukan tensor urutan panjang berada pada peranti CPU, dan oleh itu fungsi latihan perlu mengelakkan daripada memindahkan data urutan panjang ke GPU semasa latihan. Anda boleh melihat pelaksanaan fungsi `train_emb` dalam fail [`torchnlp.py`](../../../../../lessons/5-NLP/16-RNN/torchnlp.py).\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## RNN Dwihala dan Berlapis\n", "\n", "Dalam contoh kita, semua rangkaian berulang beroperasi dalam satu arah, dari permulaan urutan hingga ke penghujung. Ia kelihatan semula jadi, kerana ia menyerupai cara kita membaca dan mendengar ucapan. Walau bagaimanapun, memandangkan dalam banyak kes praktikal kita mempunyai akses rawak kepada urutan input, mungkin masuk akal untuk menjalankan pengiraan berulang dalam kedua-dua arah. Rangkaian seperti ini dipanggil **RNN dwihala**, dan ia boleh dicipta dengan memberikan parameter `bidirectional=True` kepada pembina RNN/LSTM/GRU.\n", "\n", "Apabila berurusan dengan rangkaian dwihala, kita memerlukan dua vektor keadaan tersembunyi, satu untuk setiap arah. PyTorch menyandikan vektor-vektor tersebut sebagai satu vektor dengan saiz dua kali ganda, yang agak mudah, kerana biasanya anda akan menghantar keadaan tersembunyi yang terhasil kepada lapisan linear yang bersambung sepenuhnya, dan anda hanya perlu mengambil kira peningkatan saiz ini semasa mencipta lapisan tersebut.\n", "\n", "Rangkaian berulang, sama ada satu hala atau dwihala, menangkap corak tertentu dalam urutan, dan boleh menyimpannya ke dalam vektor keadaan atau menghantarnya ke output. Seperti rangkaian konvolusi, kita boleh membina lapisan berulang lain di atas lapisan pertama untuk menangkap corak tahap lebih tinggi, yang dibina daripada corak tahap rendah yang diekstrak oleh lapisan pertama. Ini membawa kita kepada konsep **RNN berlapis**, yang terdiri daripada dua atau lebih rangkaian berulang, di mana output daripada lapisan sebelumnya dihantar ke lapisan seterusnya sebagai input.\n", "\n", "![Imej menunjukkan RNN LSTM berlapis panjang-pendek](../../../../../translated_images/ms/multi-layer-lstm.dd975e29bb2a59fe.webp)\n", "\n", "*Gambar daripada [post yang hebat ini](https://towardsdatascience.com/from-a-lstm-cell-to-a-multilayer-lstm-network-with-pytorch-2899eb5696f3) oleh Fernando López*\n", "\n", "PyTorch memudahkan pembinaan rangkaian seperti ini, kerana anda hanya perlu memberikan parameter `num_layers` kepada pembina RNN/LSTM/GRU untuk membina beberapa lapisan berulang secara automatik. Ini juga bermakna saiz vektor tersembunyi/keadaan akan meningkat secara berkadar, dan anda perlu mengambil kira perkara ini semasa mengendalikan output daripada lapisan berulang.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## RNN untuk tugas lain\n", "\n", "Dalam unit ini, kita telah melihat bahawa RNN boleh digunakan untuk klasifikasi jujukan, tetapi sebenarnya, ia boleh menangani banyak lagi tugas, seperti penjanaan teks, terjemahan mesin, dan lain-lain. Kita akan membincangkan tugas-tugas tersebut 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 penting, 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": "16af2a8bbb083ea23e5e41c7f5787656b2ce26968575d8763f2c4b17f9cd711f" }, "kernelspec": { "display_name": "Python 3.8.12 ('py38')", "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": "522ee52ae3d5ae933e283286254e9a55", "translation_date": "2025-08-29T16:18:33+00:00", "source_file": "lessons/5-NLP/16-RNN/RNNPyTorch.ipynb", "language_code": "ms" } }, "nbformat": 4, "nbformat_minor": 2 }