.. raw:: html
.. raw:: html
.. raw:: html
.. _sec_rnn_gluon:
Lập trình súc tích Mạng nơ-ron Hồi tiếp
=======================================
.. raw:: html
Dù :numref:`sec_rnn_scratch` đã mô tả cách lập trình mạng nơ-ron hồi
tiếp từ đầu một cách chi tiết, tuy nhiên cách làm này không được nhanh
và thuận tiện. Phần này sẽ hướng dẫn cách lập trình cùng một mô hình
ngôn ngữ nhưng hiệu quả hơn bằng các hàm của Gluon. Như trước, ta cũng
bắt đầu với việc đọc kho ngữ liệu “Cỗ máy Thời gian”.
.. code:: python
from d2l import mxnet as d2l
from mxnet import np, npx
from mxnet.gluon import nn, rnn
npx.set_np()
batch_size, num_steps = 32, 35
train_iter, vocab = d2l.load_data_time_machine(batch_size, num_steps)
.. raw:: html
Định nghĩa Mô hình
------------------
.. raw:: html
Mô-đun ``rnn`` của Gluon đã lập trình sẵn mạng nơ-ron hồi tiếp (cùng với
các mô hình chuỗi khác). Ta xây dựng tầng hồi tiếp ``rnn_layer`` với một
tầng ẩn có 256 nút rồi khởi tạo các trọng số.
.. code:: python
num_hiddens = 256
rnn_layer = rnn.RNN(num_hiddens)
rnn_layer.initialize()
.. raw:: html
Việc khởi tạo trạng thái cũng khá đơn giản, chỉ cần gọi phương thức
``rnn_layer.begin_state(batch_size)``. Phương thức này trả về một trạng
thái ban đầu cho mỗi phần tử trong minibatch, có kích thước là (số tầng
ẩn, kích thước batch, số nút ẩn). Số tầng ẩn mặc định là 1. Thực ra ta
chưa thảo luận việc mạng có nhiều tầng sẽ như thế nào — điều này sẽ được
đề cập ở :numref:`sec_deep_rnn`. Tạm thời, có thể nói rằng trong mạng
nhiều tầng, đầu ra của một RNN sẽ là đầu vào của RNN tiếp theo.
.. code:: python
batch_size = 1
state = rnn_layer.begin_state(batch_size=batch_size)
len(state), state[0].shape
.. parsed-literal::
:class: output
(1, (1, 1, 256))
.. raw:: html
Với một biến trạng thái và một đầu vào, ta có thể tính đầu ra với trạng
thái vừa được cập nhật.
.. code:: python
num_steps = 1
X = np.random.uniform(size=(num_steps, batch_size, len(vocab)))
Y, state_new = rnn_layer(X, state)
Y.shape, len(state_new), state_new[0].shape
.. parsed-literal::
:class: output
((1, 1, 256), 1, (1, 1, 256))
.. raw:: html
.. raw:: html
.. raw:: html
Tương tự :numref:`sec_rnn_scratch`, ta định nghĩa khối ``RNNModel``
bằng cách kế thừa lớp ``Block`` để xây dựng mạng nơ-ron hồi tiếp hoàn
chỉnh. Chú ý rằng ``rnn_layer`` chỉ chứa các tầng hồi tiếp ẩn và ta cần
tạo riêng biệt một tầng đầu ra, trong khi ở phần trước tầng đầu ra được
tích hợp sẵn trong khối ``rnn``.
.. code:: python
# Saved in the d2l package for later use
class RNNModel(nn.Block):
def __init__(self, rnn_layer, vocab_size, **kwargs):
super(RNNModel, self).__init__(**kwargs)
self.rnn = rnn_layer
self.vocab_size = vocab_size
self.dense = nn.Dense(vocab_size)
def forward(self, inputs, state):
X = npx.one_hot(inputs.T, self.vocab_size)
Y, state = self.rnn(X, state)
# The fully connected layer will first change the shape of Y to
# (num_steps * batch_size, num_hiddens). Its output shape is
# (num_steps * batch_size, vocab_size).
output = self.dense(Y.reshape(-1, Y.shape[-1]))
return output, state
def begin_state(self, *args, **kwargs):
return self.rnn.begin_state(*args, **kwargs)
.. raw:: html
Huấn luyện và Dự đoán
---------------------
.. raw:: html
Trước khi huấn luyện, hãy thử dự đoán bằng mô hình có trọng số ngẫu
nhiên.
.. code:: python
ctx = d2l.try_gpu()
model = RNNModel(rnn_layer, len(vocab))
model.initialize(force_reinit=True, ctx=ctx)
d2l.predict_ch8('time traveller', 10, model, vocab, ctx)
.. parsed-literal::
:class: output
'time travellervmjznnngii'
.. raw:: html
Khá rõ ràng, mô hình này không tốt. Tiếp theo, ta gọi hàm ``train_ch8``
với các siêu tham số định nghĩa trong :numref:`sec_rnn_scratch` để
huấn luyện mô hình bằng Gluon.
.. code:: python
num_epochs, lr = 500, 1
d2l.train_ch8(model, train_iter, vocab, lr, num_epochs, ctx)
.. parsed-literal::
:class: output
perplexity 1.3, 148171.2 tokens/sec on gpu(0)
time traveller came back for anachronisms one might get ohe thr
traveller with a slight accession ofcheerfulness really thi
.. figure:: output_rnn-gluon_vn_d54b89_13_1.svg
.. raw:: html
So với phần trước, mô hình này đạt được perplexity tương đương, nhưng
thời gian huấn luyện tốt hơn do các đoạn mã được tối ưu hơn.
.. raw:: html
Tóm tắt
-------
.. raw:: html
- Mô-đun ``rnn`` của Gluon đã lập trình sẵn tầng mạng nơ-ron hồi tiếp.
- Mỗi thực thể của ``nn.RNN`` trả về đầu ra và trạng thái ẩn sau lượt
truyền xuôi. Lượt truyền xuôi này không bao gồm tính toán tại tầng
đầu ra.
- Như trước, đồ thị tính toán cần được tách khỏi các bước trước đó để
đảm bảo hiệu năng.
.. raw:: html
Bài tập
-------
.. raw:: html
1. So sánh với cách lập trình từ đầu ở phần trước.
- Tại sao lập trình bằng Gluon chạy nhanh hơn?
- Nếu bạn nhận thấy khác biệt đáng kể nào khác ngoài tốc độ, hãy thử
tìm hiểu tại sao.
2. Bạn có thể làm quá khớp mô hình này không? Hãy thử
- Tăng số nút ẩn.
- Tăng số vòng lặp.
- Thay đổi tham số gọt (*clipping*) thì sao?
3. Hãy lập trình mô hình tự hồi quy ở phần giới thiệu của chương này
bằng RNN.
4. Nếu tăng số tầng ẩn của mô hình RNN thì sao? Bạn có thể làm mô hình
hoạt động không?
5. Có thể nén văn bản bằng cách sử dụng mô hình này không?
- Nếu có thì cần bao nhiêu bit?
- Tại sao không ai sử dụng mô hình này để nén văn bản? Gợi ý: bản
thân bộ nén thì sao?
.. raw:: html
.. raw:: html
Thảo luận
---------
- `Tiếng Anh `__
- `Tiếng Việt `__
Những người thực hiện
---------------------
Bản dịch trong trang này được thực hiện bởi:
- Đoàn Võ Duy Thanh
- Nguyễn Văn Cường
- Phạm Hồng Vinh
- Lê Khắc Hồng Phúc