AI-For-Beginners/translations/vi/lessons/5-NLP/16-RNN/RNNTF.ipynb

460 lines
26 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Mạng nơ-ron hồi quy\n",
"\n",
"Trong mô-đun trước, chúng ta đã tìm hiểu về các biểu diễn ngữ nghĩa phong phú của văn bản. Kiến trúc mà chúng ta đã sử dụng có khả năng nắm bắt ý nghĩa tổng hợp của các từ trong một câu, nhưng nó không xem xét đến **thứ tự** của các từ, vì thao tác tổng hợp sau khi nhúng đã loại bỏ thông tin này khỏi văn bản gốc. Vì các mô hình này không thể biểu diễn thứ tự từ, chúng không thể giải quyết các nhiệm vụ phức tạp hoặc mơ hồ hơn như tạo văn bản hoặc trả lời câu hỏi.\n",
"\n",
"Để nắm bắt ý nghĩa của một chuỗi văn bản, chúng ta sẽ sử dụng một kiến trúc mạng nơ-ron gọi là **mạng nơ-ron hồi quy**, hay RNN. Khi sử dụng RNN, chúng ta truyền câu qua mạng từng token một, và mạng tạo ra một **trạng thái**, sau đó chúng ta truyền trạng thái này vào mạng cùng với token tiếp theo.\n",
"\n",
"![Hình minh họa một ví dụ về quá trình tạo mạng nơ-ron hồi quy.](../../../../../translated_images/vi/rnn.27f5c29c53d727b5.webp)\n",
"\n",
"Với chuỗi đầu vào các token $X_0,\\dots,X_n$, RNN tạo ra một chuỗi các khối mạng nơ-ron và huấn luyện chuỗi này từ đầu đến cuối bằng cách sử dụng lan truyền ngược. Mỗi khối mạng nhận một cặp $(X_i,S_i)$ làm đầu vào và tạo ra $S_{i+1}$ làm kết quả. Trạng thái cuối cùng $S_n$ hoặc đầu ra $Y_n$ được đưa vào một bộ phân loại tuyến tính để tạo ra kết quả. Tất cả các khối mạng đều chia sẻ cùng một trọng số và được huấn luyện từ đầu đến cuối bằng một lần lan truyền ngược.\n",
"\n",
"> Hình trên minh họa mạng nơ-ron hồi quy ở dạng mở rộng (bên trái) và dạng biểu diễn hồi quy gọn hơn (bên phải). Điều quan trọng cần nhận ra là tất cả các tế bào RNN đều có **trọng số chia sẻ**.\n",
"\n",
"Vì các vector trạng thái $S_0,\\dots,S_n$ được truyền qua mạng, RNN có khả năng học các phụ thuộc tuần tự giữa các từ. Ví dụ, khi từ *không* xuất hiện ở đâu đó trong chuỗi, nó có thể học cách phủ định một số yếu tố trong vector trạng thái.\n",
"\n",
"Bên trong, mỗi tế bào RNN chứa hai ma trận trọng số: $W_H$ và $W_I$, cùng với độ lệch $b$. Tại mỗi bước RNN, với đầu vào $X_i$ và trạng thái đầu vào $S_i$, trạng thái đầu ra được tính bằng $S_{i+1} = f(W_H\\times S_i + W_I\\times X_i+b)$, trong đó $f$ là một hàm kích hoạt (thường là $\\tanh$).\n",
"\n",
"> Đối với các vấn đề như tạo văn bản (mà chúng ta sẽ tìm hiểu trong đơn vị tiếp theo) hoặc dịch máy, chúng ta cũng muốn nhận được một giá trị đầu ra tại mỗi bước RNN. Trong trường hợp này, có thêm một ma trận khác $W_O$, và đầu ra được tính bằng $Y_i=f(W_O\\times S_i+b_O)$.\n",
"\n",
"Hãy cùng xem cách mạng nơ-ron hồi quy có thể giúp chúng ta phân loại tập dữ liệu tin tức.\n",
"\n",
"> Đối với môi trường sandbox, chúng ta cần chạy ô sau để đảm bảo thư viện cần thiết được cài đặt và dữ liệu được tải trước. Nếu bạn đang chạy trên máy cục bộ, bạn có thể bỏ qua ô sau.\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": [
"Khi huấn luyện các mô hình lớn, việc phân bổ bộ nhớ GPU có thể trở thành một vấn đề. Chúng ta cũng có thể cần thử nghiệm với các kích thước minibatch khác nhau để dữ liệu vừa với bộ nhớ GPU, đồng thời đảm bảo quá trình huấn luyện đủ nhanh. Nếu bạn đang chạy mã này trên máy GPU của riêng mình, bạn có thể thử điều chỉnh kích thước minibatch để tăng tốc độ huấn luyện.\n",
"\n",
"> **Note**: Một số phiên bản trình điều khiển NVidia được biết là không giải phóng bộ nhớ sau khi huấn luyện mô hình. Chúng tôi đang chạy một số ví dụ trong notebook này, và điều này có thể dẫn đến việc bộ nhớ bị cạn kiệt trong một số cấu hình, đặc biệt nếu bạn đang thực hiện các thử nghiệm của riêng mình trong cùng notebook. Nếu bạn gặp phải một số lỗi kỳ lạ khi bắt đầu huấn luyện mô hình, bạn có thể muốn khởi động lại kernel của 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": [
"## Bộ phân loại RNN đơn giản\n",
"\n",
"Trong trường hợp của một RNN đơn giản, mỗi đơn vị hồi quy là một mạng tuyến tính đơn giản, nhận vào một vector đầu vào và một vector trạng thái, sau đó tạo ra một vector trạng thái mới. Trong Keras, điều này có thể được biểu diễn bằng lớp `SimpleRNN`.\n",
"\n",
"Mặc dù chúng ta có thể truyền các token được mã hóa one-hot trực tiếp vào lớp RNN, nhưng đây không phải là một ý tưởng hay do tính chất chiều cao của chúng. Vì vậy, chúng ta sẽ sử dụng một lớp embedding để giảm chiều của các vector từ, tiếp theo là một lớp RNN, và cuối cùng là một bộ phân loại `Dense`.\n",
"\n",
"> **Lưu ý**: Trong các trường hợp mà chiều không quá cao, ví dụ khi sử dụng mã hóa ở cấp độ ký tự, việc truyền các token được mã hóa one-hot trực tiếp vào cell RNN có thể hợp lý.\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": [
"> **Lưu ý:** Chúng ta sử dụng một lớp nhúng chưa được huấn luyện ở đây để đơn giản hóa, nhưng để có kết quả tốt hơn, chúng ta có thể sử dụng một lớp nhúng đã được huấn luyện trước bằng Word2Vec, như đã mô tả trong bài học trước. Đây sẽ là một bài tập tốt để bạn thử điều chỉnh mã này để hoạt động với các nhúng đã được huấn luyện trước.\n",
"\n",
"Bây giờ, hãy huấn luyện RNN của chúng ta. Nói chung, RNN khá khó để huấn luyện, bởi vì khi các tế bào RNN được mở rộng theo chiều dài chuỗi, số lượng lớp tham gia vào quá trình lan truyền ngược trở nên rất lớn. Do đó, chúng ta cần chọn một tốc độ học nhỏ hơn và huấn luyện mạng trên một tập dữ liệu lớn hơn để đạt được kết quả tốt. Điều này có thể mất khá nhiều thời gian, vì vậy việc sử dụng GPU là điều được khuyến khích.\n",
"\n",
"Để tăng tốc, chúng ta sẽ chỉ huấn luyện mô hình RNN trên tiêu đề tin tức, bỏ qua phần mô tả. Bạn có thể thử huấn luyện với phần mô tả và xem liệu bạn có thể làm cho mô hình huấn luyện được hay không.\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": [
"> **Lưu ý** rằng độ chính xác có thể thấp hơn ở đây, vì chúng tôi chỉ đang huấn luyện trên các tiêu đề tin tức.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Xem lại các chuỗi biến đổi\n",
"\n",
"Hãy nhớ rằng lớp `TextVectorization` sẽ tự động thêm các token đệm vào các chuỗi có độ dài biến đổi trong một minibatch. Tuy nhiên, những token này cũng tham gia vào quá trình huấn luyện và có thể làm phức tạp việc hội tụ của mô hình.\n",
"\n",
"Có một số cách chúng ta có thể áp dụng để giảm thiểu số lượng token đệm. Một trong số đó là sắp xếp lại tập dữ liệu theo độ dài chuỗi và nhóm tất cả các chuỗi theo kích thước. Điều này có thể thực hiện bằng cách sử dụng hàm `tf.data.experimental.bucket_by_sequence_length` (xem [tài liệu](https://www.tensorflow.org/api_docs/python/tf/data/experimental/bucket_by_sequence_length)).\n",
"\n",
"Một cách khác là sử dụng **masking**. Trong Keras, một số lớp hỗ trợ đầu vào bổ sung để chỉ ra những token nào nên được tính đến khi huấn luyện. Để tích hợp masking vào mô hình của chúng ta, chúng ta có thể thêm một lớp riêng biệt `Masking` ([tài liệu](https://keras.io/api/layers/core_layers/masking/)), hoặc chúng ta có thể chỉ định tham số `mask_zero=True` trong lớp `Embedding`.\n",
"\n",
"> **Note**: Quá trình huấn luyện này sẽ mất khoảng 5 phút để hoàn thành một epoch trên toàn bộ tập dữ liệu. Bạn có thể dừng huấn luyện bất cứ lúc nào nếu cảm thấy mất kiên nhẫn. Một cách khác là giới hạn lượng dữ liệu được sử dụng để huấn luyện bằng cách thêm câu lệnh `.take(...)` sau các tập dữ liệu `ds_train` và `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": [
"Bây giờ, khi chúng ta sử dụng kỹ thuật masking, chúng ta có thể huấn luyện mô hình trên toàn bộ tập dữ liệu gồm tiêu đề và mô tả.\n",
"\n",
"> **Lưu ý**: Bạn có nhận ra rằng chúng ta đã sử dụng vectorizer được huấn luyện trên tiêu đề tin tức, chứ không phải toàn bộ nội dung bài viết? Điều này có thể dẫn đến việc một số token bị bỏ qua, vì vậy tốt hơn là nên huấn luyện lại vectorizer. Tuy nhiên, điều này có thể chỉ ảnh hưởng rất nhỏ, nên chúng ta sẽ tiếp tục sử dụng vectorizer đã được huấn luyện trước để giữ mọi thứ đơn giản.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## LSTM: Bộ nhớ dài ngắn hạn\n",
"\n",
"Một trong những vấn đề chính của RNN là **gradient biến mất**. RNN có thể khá dài và gặp khó khăn trong việc truyền gradient ngược lại toàn bộ đến lớp đầu tiên của mạng trong quá trình lan truyền ngược. Khi điều này xảy ra, mạng không thể học được mối quan hệ giữa các token xa nhau. Một cách để tránh vấn đề này là giới thiệu **quản lý trạng thái rõ ràng** bằng cách sử dụng **cổng**. Hai kiến trúc phổ biến nhất giới thiệu cổng là **bộ nhớ dài ngắn hạn** (LSTM) và **đơn vị chuyển tiếp có cổng** (GRU). Chúng ta sẽ tìm hiểu về LSTM ở đây.\n",
"\n",
"![Hình ảnh minh họa một tế bào bộ nhớ dài ngắn hạn](../../../../../lessons/5-NLP/16-RNN/images/long-short-term-memory-cell.svg)\n",
"\n",
"Mạng LSTM được tổ chức theo cách tương tự như RNN, nhưng có hai trạng thái được truyền từ lớp này sang lớp khác: trạng thái thực tế $c$, và vector ẩn $h$. Tại mỗi đơn vị, vector ẩn $h_{t-1}$ được kết hợp với đầu vào $x_t$, và cùng nhau chúng kiểm soát những gì xảy ra với trạng thái $c_t$ và đầu ra $h_{t}$ thông qua **cổng**. Mỗi cổng có kích hoạt sigmoid (đầu ra trong phạm vi $[0,1]$), có thể được coi như một mặt nạ bitwise khi nhân với vector trạng thái. LSTM có các cổng sau (từ trái sang phải trên hình ảnh ở trên):\n",
"* **cổng quên** xác định những thành phần nào của vector $c_{t-1}$ cần quên và những thành phần nào cần truyền qua.\n",
"* **cổng đầu vào** xác định lượng thông tin từ vector đầu vào và vector ẩn trước đó nên được kết hợp vào vector trạng thái.\n",
"* **cổng đầu ra** lấy vector trạng thái mới và quyết định những thành phần nào của nó sẽ được sử dụng để tạo ra vector ẩn mới $h_t$.\n",
"\n",
"Các thành phần của trạng thái $c$ có thể được coi như các cờ có thể bật hoặc tắt. Ví dụ, khi chúng ta gặp tên *Alice* trong chuỗi, chúng ta đoán rằng nó đề cập đến một phụ nữ và bật cờ trong trạng thái để nói rằng chúng ta có một danh từ nữ trong câu. Khi chúng ta gặp thêm các từ *and Tom*, chúng ta sẽ bật cờ nói rằng chúng ta có một danh từ số nhiều. Do đó, bằng cách thao tác trạng thái, chúng ta có thể theo dõi các thuộc tính ngữ pháp của câu.\n",
"\n",
"> **Note**: Đây là một tài liệu tuyệt vời để hiểu rõ hơn về cấu trúc bên trong của LSTM: [Understanding LSTM Networks](https://colah.github.io/posts/2015-08-Understanding-LSTMs/) của Christopher Olah.\n",
"\n",
"Mặc dù cấu trúc bên trong của một tế bào LSTM có thể trông phức tạp, Keras ẩn việc triển khai này bên trong lớp `LSTM`, vì vậy điều duy nhất chúng ta cần làm trong ví dụ trên là thay thế lớp hồi quy:\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 hai chiều và nhiều lớp\n",
"\n",
"Trong các ví dụ trước đây, mạng hồi quy hoạt động từ đầu đến cuối của một chuỗi. Điều này có vẻ tự nhiên với chúng ta vì nó tuân theo hướng mà chúng ta đọc hoặc nghe lời nói. Tuy nhiên, đối với các tình huống yêu cầu truy cập ngẫu nhiên vào chuỗi đầu vào, việc thực hiện tính toán hồi quy theo cả hai hướng sẽ hợp lý hơn. Các RNN cho phép tính toán theo cả hai hướng được gọi là **RNN hai chiều**, và chúng có thể được tạo bằng cách bao bọc lớp hồi quy với một lớp đặc biệt `Bidirectional`.\n",
"\n",
"> **Note**: Lớp `Bidirectional` tạo hai bản sao của lớp bên trong nó, và đặt thuộc tính `go_backwards` của một trong những bản sao đó thành `True`, khiến nó đi theo hướng ngược lại dọc theo chuỗi.\n",
"\n",
"Mạng hồi quy, dù là một chiều hay hai chiều, đều nắm bắt các mẫu trong một chuỗi và lưu trữ chúng vào các vector trạng thái hoặc trả về chúng dưới dạng đầu ra. Tương tự như mạng tích chập, chúng ta có thể xây dựng một lớp hồi quy khác sau lớp đầu tiên để nắm bắt các mẫu cấp cao hơn, được xây dựng từ các mẫu cấp thấp hơn mà lớp đầu tiên đã trích xuất. Điều này dẫn đến khái niệm về **RNN nhiều lớp**, bao gồm hai hoặc nhiều mạng hồi quy, trong đó đầu ra của lớp trước được truyền vào lớp tiếp theo dưới dạng đầu vào.\n",
"\n",
"![Hình ảnh minh họa một RNN LSTM nhiều lớp](../../../../../translated_images/vi/multi-layer-lstm.dd975e29bb2a59fe.webp)\n",
"\n",
"*Hình ảnh từ [bài viết tuyệt vời này](https://towardsdatascience.com/from-a-lstm-cell-to-a-multilayer-lstm-network-with-pytorch-2899eb5696f3) của Fernando López.*\n",
"\n",
"Keras giúp việc xây dựng các mạng này trở nên dễ dàng, vì bạn chỉ cần thêm nhiều lớp hồi quy vào mô hình. Đối với tất cả các lớp ngoại trừ lớp cuối cùng, chúng ta cần chỉ định tham số `return_sequences=True`, vì chúng ta cần lớp trả về tất cả các trạng thái trung gian, chứ không chỉ trạng thái cuối cùng của tính toán hồi quy.\n",
"\n",
"Hãy xây dựng một LSTM hai lớp hai chiều cho bài toán phân loại của chúng ta.\n",
"\n",
"> **Note** đoạn mã này một lần nữa mất khá nhiều thời gian để hoàn thành, nhưng nó mang lại độ chính xác cao nhất mà chúng ta từng thấy. Vì vậy, có lẽ đáng để chờ đợi và xem kết quả.\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": [
"## RNNs cho các nhiệm vụ khác\n",
"\n",
"Cho đến nay, chúng ta đã tập trung vào việc sử dụng RNNs để phân loại chuỗi văn bản. Nhưng chúng có thể xử lý nhiều nhiệm vụ khác, chẳng hạn như tạo văn bản và dịch máy — chúng ta sẽ xem xét những nhiệm vụ đó trong đơn vị tiếp theo.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Tuyên bố miễn trừ trách nhiệm**: \nTài liệu này đã được dịch bằng dịch vụ dịch thuật AI [Co-op Translator](https://github.com/Azure/co-op-translator). Mặc dù chúng tôi cố gắng đảm bảo độ chính xác, xin lưu ý rằng các bản dịch tự động có thể chứa lỗi hoặc không chính xác. Tài liệu gốc bằng ngôn ngữ bản địa nên được coi là nguồn thông tin chính thức. Đối với các thông tin quan trọng, khuyến nghị sử dụng dịch vụ dịch thuật chuyên nghiệp bởi con người. Chúng tôi không chịu trách nhiệm cho bất kỳ sự hiểu lầm hoặc diễn giải sai nào phát sinh từ việc sử dụng bản dịch này.\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:11:30+00:00",
"source_file": "lessons/5-NLP/16-RNN/RNNTF.ipynb",
"language_code": "vi"
}
},
"nbformat": 4,
"nbformat_minor": 4
}