.. 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