460 lines
24 KiB
Plaintext
460 lines
24 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Jaringan saraf berulang\n",
|
|
"\n",
|
|
"Pada modul sebelumnya, kita telah membahas representasi semantik yang kaya dari teks. Arsitektur yang kita gunakan menangkap makna agregat dari kata-kata dalam sebuah kalimat, tetapi tidak mempertimbangkan **urutan** kata-kata, karena operasi agregasi yang mengikuti embedding menghilangkan informasi ini dari teks asli. Karena model-model ini tidak dapat merepresentasikan urutan kata, mereka tidak dapat menyelesaikan tugas yang lebih kompleks atau ambigu seperti pembuatan teks atau menjawab pertanyaan.\n",
|
|
"\n",
|
|
"Untuk menangkap makna dari urutan teks, kita akan menggunakan arsitektur jaringan saraf yang disebut **jaringan saraf berulang**, atau RNN. Saat menggunakan RNN, kita melewatkan kalimat kita melalui jaringan satu token pada satu waktu, dan jaringan menghasilkan beberapa **state**, yang kemudian kita lewati kembali ke jaringan bersama token berikutnya.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"Diberikan urutan input token $X_0,\\dots,X_n$, RNN menciptakan urutan blok jaringan saraf, dan melatih urutan ini secara end-to-end menggunakan backpropagation. Setiap blok jaringan mengambil pasangan $(X_i,S_i)$ sebagai input, dan menghasilkan $S_{i+1}$ sebagai hasil. State akhir $S_n$ atau output $Y_n$ masuk ke dalam pengklasifikasi linier untuk menghasilkan hasil. Semua blok jaringan berbagi bobot yang sama, dan dilatih secara end-to-end menggunakan satu langkah backpropagation.\n",
|
|
"\n",
|
|
"> Gambar di atas menunjukkan jaringan saraf berulang dalam bentuk yang diurai (di sebelah kiri), dan dalam representasi berulang yang lebih ringkas (di sebelah kanan). Penting untuk menyadari bahwa semua Sel RNN memiliki **bobot yang dapat dibagikan**.\n",
|
|
"\n",
|
|
"Karena vektor state $S_0,\\dots,S_n$ dilewatkan melalui jaringan, RNN mampu mempelajari ketergantungan berurutan antara kata-kata. Sebagai contoh, ketika kata *tidak* muncul di suatu tempat dalam urutan, ia dapat belajar untuk meniadakan elemen tertentu dalam vektor state.\n",
|
|
"\n",
|
|
"Di dalamnya, setiap sel RNN berisi dua matriks bobot: $W_H$ dan $W_I$, serta bias $b$. Pada setiap langkah RNN, diberikan input $X_i$ dan state input $S_i$, state output dihitung sebagai $S_{i+1} = f(W_H\\times S_i + W_I\\times X_i+b)$, di mana $f$ adalah fungsi aktivasi (sering kali $\\tanh$).\n",
|
|
"\n",
|
|
"> Untuk masalah seperti pembuatan teks (yang akan kita bahas di unit berikutnya) atau terjemahan mesin, kita juga ingin mendapatkan nilai output pada setiap langkah RNN. Dalam kasus ini, ada juga matriks lain $W_O$, dan output dihitung sebagai $Y_i=f(W_O\\times S_i+b_O)$.\n",
|
|
"\n",
|
|
"Mari kita lihat bagaimana jaringan saraf berulang dapat membantu kita mengklasifikasikan dataset berita kita.\n",
|
|
"\n",
|
|
"> Untuk lingkungan sandbox, kita perlu menjalankan sel berikut untuk memastikan pustaka yang diperlukan terinstal, dan data telah diambil sebelumnya. Jika Anda menjalankan secara lokal, Anda dapat melewati sel berikut.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 1,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"import sys\n",
|
|
"!{sys.executable} -m pip install --quiet tensorflow_datasets==4.4.0\n",
|
|
"!cd ~ && wget -q -O - https://mslearntensorflowlp.blob.core.windows.net/data/tfds-ag-news.tgz | tar xz"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 2,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"import tensorflow as tf\n",
|
|
"from tensorflow import keras\n",
|
|
"import tensorflow_datasets as tfds\n",
|
|
"import numpy as np\n",
|
|
"\n",
|
|
"# We are going to be training pretty large models. In order not to face errors, we need\n",
|
|
"# to set tensorflow option to grow GPU memory allocation when required\n",
|
|
"physical_devices = tf.config.list_physical_devices('GPU') \n",
|
|
"if len(physical_devices)>0:\n",
|
|
" tf.config.experimental.set_memory_growth(physical_devices[0], True)\n",
|
|
"\n",
|
|
"ds_train, ds_test = tfds.load('ag_news_subset').values()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"nteract": {
|
|
"transient": {
|
|
"deleting": false
|
|
}
|
|
}
|
|
},
|
|
"source": [
|
|
"Ketika melatih model besar, alokasi memori GPU dapat menjadi masalah. Kita juga mungkin perlu mencoba berbagai ukuran minibatch, agar data dapat masuk ke dalam memori GPU kita, namun pelatihan tetap cukup cepat. Jika Anda menjalankan kode ini di mesin GPU Anda sendiri, Anda dapat bereksperimen dengan menyesuaikan ukuran minibatch untuk mempercepat pelatihan.\n",
|
|
"\n",
|
|
"> **Note**: Beberapa versi driver NVidia diketahui tidak melepaskan memori setelah melatih model. Kami menjalankan beberapa contoh dalam notebook ini, dan hal ini dapat menyebabkan memori habis dalam beberapa konfigurasi, terutama jika Anda melakukan eksperimen sendiri dalam notebook yang sama. Jika Anda mengalami kesalahan aneh saat mulai melatih model, Anda mungkin perlu merestart kernel notebook.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 3,
|
|
"metadata": {
|
|
"collapsed": true,
|
|
"jupyter": {
|
|
"outputs_hidden": false,
|
|
"source_hidden": false
|
|
},
|
|
"nteract": {
|
|
"transient": {
|
|
"deleting": false
|
|
}
|
|
}
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"batch_size = 16\n",
|
|
"embed_size = 64"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Klasifikasi RNN Sederhana\n",
|
|
"\n",
|
|
"Dalam kasus RNN sederhana, setiap unit rekuren adalah jaringan linear sederhana yang menerima vektor input dan vektor keadaan, lalu menghasilkan vektor keadaan baru. Dalam Keras, ini dapat direpresentasikan dengan lapisan `SimpleRNN`.\n",
|
|
"\n",
|
|
"Meskipun kita dapat langsung memberikan token yang dikodekan one-hot ke lapisan RNN, ini bukan ide yang baik karena dimensi yang terlalu tinggi. Oleh karena itu, kita akan menggunakan lapisan embedding untuk menurunkan dimensi vektor kata, diikuti oleh lapisan RNN, dan akhirnya sebuah klasifikator `Dense`.\n",
|
|
"\n",
|
|
"> **Catatan**: Dalam kasus di mana dimensinya tidak terlalu tinggi, misalnya saat menggunakan tokenisasi tingkat karakter, mungkin masuk akal untuk langsung memberikan token yang dikodekan one-hot ke sel RNN.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 4,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Model: \"sequential\"\n",
|
|
"_________________________________________________________________\n",
|
|
"Layer (type) Output Shape Param # \n",
|
|
"=================================================================\n",
|
|
"text_vectorization (TextVect (None, None) 0 \n",
|
|
"_________________________________________________________________\n",
|
|
"embedding (Embedding) (None, None, 64) 1280000 \n",
|
|
"_________________________________________________________________\n",
|
|
"simple_rnn (SimpleRNN) (None, 16) 1296 \n",
|
|
"_________________________________________________________________\n",
|
|
"dense (Dense) (None, 4) 68 \n",
|
|
"=================================================================\n",
|
|
"Total params: 1,281,364\n",
|
|
"Trainable params: 1,281,364\n",
|
|
"Non-trainable params: 0\n",
|
|
"_________________________________________________________________\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"vocab_size = 20000\n",
|
|
"\n",
|
|
"vectorizer = keras.layers.experimental.preprocessing.TextVectorization(\n",
|
|
" max_tokens=vocab_size,\n",
|
|
" input_shape=(1,))\n",
|
|
"\n",
|
|
"model = keras.models.Sequential([\n",
|
|
" vectorizer,\n",
|
|
" keras.layers.Embedding(vocab_size, embed_size),\n",
|
|
" keras.layers.SimpleRNN(16),\n",
|
|
" keras.layers.Dense(4,activation='softmax')\n",
|
|
"])\n",
|
|
"\n",
|
|
"model.summary()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"> **Catatan:** Di sini kita menggunakan embedding layer yang belum dilatih untuk kesederhanaan, tetapi untuk hasil yang lebih baik kita bisa menggunakan embedding layer yang sudah dilatih sebelumnya dengan Word2Vec, seperti yang dijelaskan di unit sebelumnya. Akan menjadi latihan yang baik bagi Anda untuk mengadaptasi kode ini agar bekerja dengan embedding yang sudah dilatih sebelumnya.\n",
|
|
"\n",
|
|
"Sekarang mari kita latih RNN kita. Secara umum, RNN cukup sulit untuk dilatih, karena ketika sel-sel RNN diurai sepanjang panjang urutan, jumlah lapisan yang terlibat dalam backpropagation menjadi sangat besar. Oleh karena itu, kita perlu memilih learning rate yang lebih kecil, dan melatih jaringan pada dataset yang lebih besar untuk menghasilkan hasil yang baik. Ini bisa memakan waktu cukup lama, jadi disarankan menggunakan GPU.\n",
|
|
"\n",
|
|
"Untuk mempercepat proses, kita hanya akan melatih model RNN pada judul berita, tanpa menyertakan deskripsi. Anda bisa mencoba melatih dengan deskripsi dan melihat apakah Anda bisa membuat modelnya terlatih.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 5,
|
|
"metadata": {
|
|
"scrolled": true
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Training vectorizer\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"def extract_title(x):\n",
|
|
" return x['title']\n",
|
|
"\n",
|
|
"def tupelize_title(x):\n",
|
|
" return (extract_title(x),x['label'])\n",
|
|
"\n",
|
|
"print('Training vectorizer')\n",
|
|
"vectorizer.adapt(ds_train.take(2000).map(extract_title))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 6,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"7500/7500 [==============================] - 82s 11ms/step - loss: 0.6629 - acc: 0.7623 - val_loss: 0.5559 - val_acc: 0.7995\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\n"
|
|
]
|
|
},
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<tensorflow.python.keras.callbacks.History at 0x7f3e0030d350>"
|
|
]
|
|
},
|
|
"execution_count": 6,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer='adam')\n",
|
|
"model.fit(ds_train.map(tupelize_title).batch(batch_size),validation_data=ds_test.map(tupelize_title).batch(batch_size))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"nteract": {
|
|
"transient": {
|
|
"deleting": false
|
|
}
|
|
}
|
|
},
|
|
"source": [
|
|
"> **Catatan** bahwa akurasi kemungkinan akan lebih rendah di sini, karena kami hanya melatih pada judul berita.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Meninjau Kembali Urutan Variabel\n",
|
|
"\n",
|
|
"Ingat bahwa lapisan `TextVectorization` secara otomatis akan menambahkan padding pada urutan dengan panjang variabel dalam satu minibatch menggunakan token padding. Ternyata, token-token tersebut juga ikut serta dalam pelatihan, dan ini dapat mempersulit konvergensi model.\n",
|
|
"\n",
|
|
"Ada beberapa pendekatan yang dapat kita ambil untuk meminimalkan jumlah padding. Salah satunya adalah dengan menyusun ulang dataset berdasarkan panjang urutan dan mengelompokkan semua urutan berdasarkan ukuran. Hal ini dapat dilakukan menggunakan fungsi `tf.data.experimental.bucket_by_sequence_length` (lihat [dokumentasi](https://www.tensorflow.org/api_docs/python/tf/data/experimental/bucket_by_sequence_length)).\n",
|
|
"\n",
|
|
"Pendekatan lain adalah menggunakan **masking**. Dalam Keras, beberapa lapisan mendukung input tambahan yang menunjukkan token mana yang harus diperhitungkan selama pelatihan. Untuk memasukkan masking ke dalam model kita, kita dapat menambahkan lapisan `Masking` terpisah ([dokumentasi](https://keras.io/api/layers/core_layers/masking/)), atau kita dapat menentukan parameter `mask_zero=True` pada lapisan `Embedding` kita.\n",
|
|
"\n",
|
|
"> **Note**: Pelatihan ini akan memakan waktu sekitar 5 menit untuk menyelesaikan satu epoch pada seluruh dataset. Anda dapat menghentikan pelatihan kapan saja jika merasa tidak sabar. Anda juga dapat membatasi jumlah data yang digunakan untuk pelatihan dengan menambahkan klausa `.take(...)` setelah dataset `ds_train` dan `ds_test`.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 7,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"7500/7500 [==============================] - 371s 49ms/step - loss: 0.5401 - acc: 0.8079 - val_loss: 0.3780 - val_acc: 0.8822\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\n"
|
|
]
|
|
},
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<tensorflow.python.keras.callbacks.History at 0x7f3dec118850>"
|
|
]
|
|
},
|
|
"execution_count": 7,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"def extract_text(x):\n",
|
|
" return x['title']+' '+x['description']\n",
|
|
"\n",
|
|
"def tupelize(x):\n",
|
|
" return (extract_text(x),x['label'])\n",
|
|
"\n",
|
|
"model = keras.models.Sequential([\n",
|
|
" vectorizer,\n",
|
|
" keras.layers.Embedding(vocab_size,embed_size,mask_zero=True),\n",
|
|
" keras.layers.SimpleRNN(16),\n",
|
|
" keras.layers.Dense(4,activation='softmax')\n",
|
|
"])\n",
|
|
"\n",
|
|
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer='adam')\n",
|
|
"model.fit(ds_train.map(tupelize).batch(batch_size),validation_data=ds_test.map(tupelize).batch(batch_size))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Sekarang kita menggunakan masking, kita dapat melatih model pada seluruh dataset judul dan deskripsi.\n",
|
|
"\n",
|
|
"> **Note**: Apakah Anda menyadari bahwa kita telah menggunakan vectorizer yang dilatih pada judul berita, bukan pada seluruh isi artikel? Hal ini berpotensi menyebabkan beberapa token diabaikan, jadi lebih baik untuk melatih ulang vectorizer. Namun, efeknya mungkin sangat kecil, jadi kita akan tetap menggunakan vectorizer yang sudah dilatih sebelumnya demi kesederhanaan.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## LSTM: Long short-term memory\n",
|
|
"\n",
|
|
"Salah satu masalah utama pada RNN adalah **vanishing gradients**. RNN bisa cukup panjang, dan mungkin kesulitan untuk menyebarkan gradien kembali ke lapisan pertama jaringan selama proses backpropagation. Ketika ini terjadi, jaringan tidak dapat mempelajari hubungan antara token yang berjauhan. Salah satu cara untuk menghindari masalah ini adalah dengan memperkenalkan **manajemen keadaan secara eksplisit** menggunakan **gates**. Dua arsitektur paling umum yang memperkenalkan gates adalah **long short-term memory** (LSTM) dan **gated relay unit** (GRU). Di sini kita akan membahas LSTM.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"Jaringan LSTM diorganisasi dengan cara yang mirip dengan RNN, tetapi ada dua keadaan yang diteruskan dari lapisan ke lapisan: keadaan sebenarnya $c$, dan vektor tersembunyi $h$. Pada setiap unit, vektor tersembunyi $h_{t-1}$ digabungkan dengan input $x_t$, dan bersama-sama mereka mengontrol apa yang terjadi pada keadaan $c_t$ dan output $h_{t}$ melalui **gates**. Setiap gate memiliki aktivasi sigmoid (output dalam rentang $[0,1]$), yang dapat dianggap sebagai masker bitwise ketika dikalikan dengan vektor keadaan. LSTM memiliki gates berikut (dari kiri ke kanan pada gambar di atas):\n",
|
|
"* **forget gate** yang menentukan komponen mana dari vektor $c_{t-1}$ yang perlu kita lupakan, dan mana yang diteruskan.\n",
|
|
"* **input gate** yang menentukan seberapa banyak informasi dari vektor input dan vektor tersembunyi sebelumnya yang harus dimasukkan ke dalam vektor keadaan.\n",
|
|
"* **output gate** yang mengambil vektor keadaan baru dan memutuskan komponen mana yang akan digunakan untuk menghasilkan vektor tersembunyi baru $h_t$.\n",
|
|
"\n",
|
|
"Komponen-komponen dari keadaan $c$ dapat dianggap sebagai tanda yang dapat diaktifkan atau dinonaktifkan. Sebagai contoh, ketika kita menemukan nama *Alice* dalam urutan, kita menebak bahwa itu merujuk pada seorang wanita, dan mengaktifkan tanda dalam keadaan yang menunjukkan bahwa kita memiliki kata benda perempuan dalam kalimat. Ketika kita kemudian menemukan kata *and Tom*, kita akan mengaktifkan tanda yang menunjukkan bahwa kita memiliki kata benda jamak. Dengan demikian, dengan memanipulasi keadaan, kita dapat melacak sifat gramatikal dari kalimat.\n",
|
|
"\n",
|
|
"> **Note**: Berikut adalah sumber yang bagus untuk memahami struktur internal LSTM: [Understanding LSTM Networks](https://colah.github.io/posts/2015-08-Understanding-LSTMs/) oleh Christopher Olah.\n",
|
|
"\n",
|
|
"Meskipun struktur internal dari sel LSTM mungkin terlihat rumit, Keras menyembunyikan implementasi ini di dalam lapisan `LSTM`, sehingga satu-satunya hal yang perlu kita lakukan dalam contoh di atas adalah mengganti lapisan rekuren:\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 8,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"15000/15000 [==============================] - 188s 13ms/step - loss: 0.5692 - acc: 0.7916 - val_loss: 0.3441 - val_acc: 0.8870\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\n"
|
|
]
|
|
},
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<tensorflow.python.keras.callbacks.History at 0x7f3d6af5c350>"
|
|
]
|
|
},
|
|
"execution_count": 8,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"model = keras.models.Sequential([\n",
|
|
" vectorizer,\n",
|
|
" keras.layers.Embedding(vocab_size, embed_size),\n",
|
|
" keras.layers.LSTM(8),\n",
|
|
" keras.layers.Dense(4,activation='softmax')\n",
|
|
"])\n",
|
|
"\n",
|
|
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer='adam')\n",
|
|
"model.fit(ds_train.map(tupelize).batch(8),validation_data=ds_test.map(tupelize).batch(8))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": []
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## RNN Bidirectional dan Multilayer\n",
|
|
"\n",
|
|
"Dalam contoh-contoh sebelumnya, jaringan rekuren beroperasi dari awal hingga akhir sebuah urutan. Hal ini terasa alami bagi kita karena mengikuti arah yang sama seperti saat kita membaca atau mendengarkan percakapan. Namun, untuk skenario yang membutuhkan akses acak terhadap urutan input, lebih masuk akal untuk menjalankan perhitungan rekuren di kedua arah. RNN yang memungkinkan perhitungan di kedua arah disebut **RNN bidirectional**, dan dapat dibuat dengan membungkus lapisan rekuren menggunakan lapisan khusus `Bidirectional`.\n",
|
|
"\n",
|
|
"> **Note**: Lapisan `Bidirectional` membuat dua salinan dari lapisan di dalamnya, dan mengatur properti `go_backwards` dari salah satu salinan tersebut menjadi `True`, sehingga berjalan ke arah yang berlawanan sepanjang urutan.\n",
|
|
"\n",
|
|
"Jaringan rekuren, baik unidirectional maupun bidirectional, menangkap pola dalam sebuah urutan, dan menyimpannya ke dalam vektor status atau mengembalikannya sebagai output. Seperti halnya jaringan konvolusi, kita dapat membangun lapisan rekuren lain setelah lapisan pertama untuk menangkap pola tingkat yang lebih tinggi, yang dibangun dari pola tingkat rendah yang diekstraksi oleh lapisan pertama. Hal ini membawa kita pada konsep **RNN multilayer**, yang terdiri dari dua atau lebih jaringan rekuren, di mana output dari lapisan sebelumnya diteruskan ke lapisan berikutnya sebagai input.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"*Gambar dari [postingan luar biasa ini](https://towardsdatascience.com/from-a-lstm-cell-to-a-multilayer-lstm-network-with-pytorch-2899eb5696f3) oleh Fernando López.*\n",
|
|
"\n",
|
|
"Keras membuat konstruksi jaringan ini menjadi tugas yang mudah, karena Anda hanya perlu menambahkan lebih banyak lapisan rekuren ke model. Untuk semua lapisan kecuali yang terakhir, kita perlu menentukan parameter `return_sequences=True`, karena kita membutuhkan lapisan tersebut untuk mengembalikan semua status intermediate, bukan hanya status akhir dari perhitungan rekuren.\n",
|
|
"\n",
|
|
"Mari kita bangun LSTM bidirectional dua lapis untuk masalah klasifikasi kita.\n",
|
|
"\n",
|
|
"> **Note** kode ini sekali lagi membutuhkan waktu yang cukup lama untuk selesai, tetapi memberikan akurasi tertinggi yang pernah kita lihat sejauh ini. Jadi mungkin layak menunggu dan melihat hasilnya.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 9,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"5044/7500 [===================>..........] - ETA: 2:33 - loss: 0.3709 - acc: 0.8706\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r5045/7500 [===================>..........] - ETA: 2:33 - loss: 0.3709 - acc: 0.8706"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"model = keras.models.Sequential([\n",
|
|
" vectorizer,\n",
|
|
" keras.layers.Embedding(vocab_size, 128, mask_zero=True),\n",
|
|
" keras.layers.Bidirectional(keras.layers.LSTM(64,return_sequences=True)),\n",
|
|
" keras.layers.Bidirectional(keras.layers.LSTM(64)), \n",
|
|
" keras.layers.Dense(4,activation='softmax')\n",
|
|
"])\n",
|
|
"\n",
|
|
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer='adam')\n",
|
|
"model.fit(ds_train.map(tupelize).batch(batch_size),\n",
|
|
" validation_data=ds_test.map(tupelize).batch(batch_size))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## RNN untuk tugas lainnya\n",
|
|
"\n",
|
|
"Sejauh ini, kita telah berfokus pada penggunaan RNN untuk mengklasifikasi urutan teks. Namun, RNN dapat menangani banyak tugas lainnya, seperti pembuatan teks dan penerjemahan mesin — kita akan membahas tugas-tugas tersebut di unit berikutnya.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"\n---\n\n**Penafian**: \nDokumen ini telah diterjemahkan menggunakan layanan penerjemahan AI [Co-op Translator](https://github.com/Azure/co-op-translator). Meskipun kami berusaha untuk memberikan hasil yang akurat, harap diingat bahwa terjemahan otomatis mungkin mengandung kesalahan atau ketidakakuratan. Dokumen asli dalam bahasa aslinya harus dianggap sebagai sumber yang otoritatif. Untuk informasi yang bersifat kritis, disarankan menggunakan jasa penerjemahan profesional oleh manusia. Kami tidak bertanggung jawab atas kesalahpahaman atau penafsiran yang keliru yang timbul dari penggunaan terjemahan ini.\n"
|
|
]
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernel_info": {
|
|
"name": "conda-env-py37_tensorflow-py"
|
|
},
|
|
"kernelspec": {
|
|
"display_name": "py37_tensorflow",
|
|
"language": "python",
|
|
"name": "conda-env-py37_tensorflow-py"
|
|
},
|
|
"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.7.9"
|
|
},
|
|
"nteract": {
|
|
"version": "nteract-front-end@1.0.0"
|
|
},
|
|
"coopTranslator": {
|
|
"original_hash": "81351e61f619b432ff51010a4f993194",
|
|
"translation_date": "2025-08-29T16:12:18+00:00",
|
|
"source_file": "lessons/5-NLP/16-RNN/RNNTF.ipynb",
|
|
"language_code": "id"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 4
|
|
} |