.. _sec_rnn: Mạng nơ-ron tái phát ==================== Trong :numref:`sec_language_model` chúng tôi đã giới thiệu các mô hình :math:`n`-gram, trong đó xác suất có điều kiện của từ :math:`x_t` tại bước thời gian :math:`t` chỉ phụ thuộc vào :math:`n-1` từ trước. Nếu chúng ta muốn kết hợp hiệu ứng có thể có của các từ sớm hơn bước thời gian :math:`t-(n-1)` trên :math:`x_t`, chúng ta cần tăng :math:`n`. Tuy nhiên, số lượng tham số mô hình cũng sẽ tăng theo cấp số nhân với nó, vì chúng ta cần lưu trữ :math:`|\mathcal{V}|^n` số cho một bộ từ vựng :math:`\mathcal{V}`. Do đó, thay vì mô hình hóa :math:`P(x_t \mid x_{t-1}, \ldots, x_{t-n+1})`, tốt hơn là sử dụng một mô hình biến tiềm ẩn: .. math:: P(x_t \mid x_{t-1}, \ldots, x_1) \approx P(x_t \mid h_{t-1}), trong đó :math:`h_{t-1}` là trạng thái \* ẩn\* (còn được gọi là biến ẩn) lưu trữ thông tin trình tự lên đến bước thời gian :math:`t-1`. Nói chung, trạng thái ẩn bất cứ lúc nào bước :math:`t` có thể được tính toán dựa trên cả đầu vào hiện tại :math:`x_{t}` và trạng thái ẩn trước đó :math:`h_{t-1}`: .. math:: h_t = f(x_{t}, h_{t-1}). :label: eq_ht_xt Đối với một chức năng đủ mạnh :math:`f` trong :eq:`eq_ht_xt`, mô hình biến tiềm ẩn không phải là một xấp xỉ. Rốt cuộc, :math:`h_t` có thể chỉ đơn giản là lưu trữ tất cả dữ liệu mà nó đã quan sát cho đến nay. Tuy nhiên, nó có khả năng có thể làm cho cả tính toán và lưu trữ đắt tiền. Nhớ lại rằng chúng ta đã thảo luận về các lớp ẩn với các đơn vị ẩn trong :numref:`chap_perceptrons`. Đáng chú ý là các lớp ẩn và trạng thái ẩn đề cập đến hai khái niệm rất khác nhau. Các lớp ẩn, như giải thích, các lớp được ẩn khỏi chế độ xem trên đường dẫn từ đầu vào đến đầu ra. Các trạng thái ẩn đang nói về mặt kỹ thuật \* đầu vào\* cho bất cứ điều gì chúng ta làm ở một bước nhất định và chúng chỉ có thể được tính toán bằng cách xem dữ liệu ở các bước thời gian trước. *Mạng thần kinh định kỳ* (RNN) là các mạng thần kinh với các trạng thái ẩn. Trước khi giới thiệu mô hình RNN, lần đầu tiên chúng tôi xem lại mô hình MLP được giới thiệu trong :numref:`sec_mlp`. Mạng thần kinh không có trạng thái ẩn ------------------------------------- Chúng ta hãy nhìn vào một MLP với một lớp ẩn duy nhất. Hãy để chức năng kích hoạt của lớp ẩn là :math:`\phi`. Với một minibatch các ví dụ :math:`\mathbf{X} \in \mathbb{R}^{n \times d}` với kích thước lô :math:`n` và :math:`d` đầu vào, đầu ra :math:`\mathbf{H} \in \mathbb{R}^{n \times h}` của lớp ẩn được tính như .. math:: \mathbf{H} = \phi(\mathbf{X} \mathbf{W}_{xh} + \mathbf{b}_h). :label: rnn_h_without_state Trong :eq:`rnn_h_without_state`, chúng ta có tham số trọng lượng :math:`\mathbf{W}_{xh} \in \mathbb{R}^{d \times h}`, tham số thiên vị :math:`\mathbf{b}_h \in \mathbb{R}^{1 \times h}` và số lượng đơn vị ẩn :math:`h`, cho lớp ẩn. Như vậy, phát sóng (xem :numref:`subsec_broadcasting`) được áp dụng trong quá trình tổng kết. Tiếp theo, biến ẩn :math:`\mathbf{H}` được sử dụng làm đầu vào của lớp đầu ra. Lớp đầu ra được đưa ra bởi .. math:: \mathbf{O} = \mathbf{H} \mathbf{W}_{hq} + \mathbf{b}_q, trong đó :math:`\mathbf{O} \in \mathbb{R}^{n \times q}` là biến đầu ra, :math:`\mathbf{W}_{hq} \in \mathbb{R}^{h \times q}` là tham số trọng lượng và :math:`\mathbf{b}_q \in \mathbb{R}^{1 \times q}` là tham số thiên vị của lớp đầu ra. Nếu đó là một bài toán phân loại, chúng ta có thể sử dụng :math:`\text{softmax}(\mathbf{O})` để tính toán phân phối xác suất của các loại đầu ra. Điều này hoàn toàn tương tự như bài toán hồi quy mà chúng tôi đã giải quyết trước đây trong :numref:`sec_sequence`, do đó chúng tôi bỏ qua các chi tiết. Đủ để nói rằng chúng ta có thể chọn các cặp nhãn tính năng một cách ngẫu nhiên và tìm hiểu các thông số của mạng của chúng tôi thông qua sự phân biệt tự động và gốc gradient ngẫu nhiên. .. _subsec_rnn_w_hidden_states: Mạng thần kinh định kỳ với các trạng thái ẩn -------------------------------------------- Các vấn đề hoàn toàn khác nhau khi chúng ta có các trạng thái ẩn. Chúng ta hãy nhìn vào cấu trúc một số chi tiết hơn. Giả sử rằng chúng ta có một minibatch của đầu vào :math:`\mathbf{X}_t \in \mathbb{R}^{n \times d}` tại bước thời gian :math:`t`. Nói cách khác, đối với một minibatch gồm :math:`n` ví dụ chuỗi, mỗi hàng :math:`\mathbf{X}_t` tương ứng với một ví dụ tại bước thời điểm :math:`t` từ chuỗi. Tiếp theo, biểu thị bằng :math:`\mathbf{H}_t \in \mathbb{R}^{n \times h}` biến ẩn của bước thời gian :math:`t`. Không giống như MLP, ở đây chúng ta lưu biến ẩn :math:`\mathbf{H}_{t-1}` từ bước thời gian trước và giới thiệu một tham số trọng lượng mới :math:`\mathbf{W}_{hh} \in \mathbb{R}^{h \times h}` để mô tả cách sử dụng biến ẩn của bước thời gian trước đó trong bước thời gian hiện tại. Cụ thể, việc tính toán biến ẩn của bước thời gian hiện tại được xác định bởi đầu vào của bước thời gian hiện tại cùng với biến ẩn của bước thời gian trước đó: .. math:: \mathbf{H}_t = \phi(\mathbf{X}_t \mathbf{W}_{xh} + \mathbf{H}_{t-1} \mathbf{W}_{hh} + \mathbf{b}_h). :label: rnn_h_with_state So với :eq:`rnn_h_without_state`, :eq:`rnn_h_with_state` bổ sung thêm một thuật ngữ :math:`\mathbf{H}_{t-1} \mathbf{W}_{hh}` và do đó khởi tạo :eq:`eq_ht_xt`. Từ mối quan hệ giữa các biến ẩn :math:`\mathbf{H}_t` và :math:`\mathbf{H}_{t-1}` của các bước thời gian liền kề, chúng ta biết rằng các biến này đã thu thập và giữ lại thông tin lịch sử của trình tự lên đến bước thời gian hiện tại của chúng, giống như trạng thái hoặc bộ nhớ của bước thời gian hiện tại của mạng thần kinh. Do đó, một biến ẩn như vậy được gọi là trạng thái \* hidden\ *. Vì trạng thái ẩn sử dụng cùng một định nghĩa của bước thời gian trước đó trong bước thời gian hiện tại, việc tính toán :eq:`rnn_h_with_state` là* đệ quy\ *. Do đó, các mạng thần kinh với các trạng thái ẩn dựa trên tính toán tái phát được đặt tên *\ mạng thần kinh định kỳ\ *. Các lớp thực hiện tính toán :eq:`rnn_h_with_state` trong RNN s được gọi là * lớp\* lặp đi lặp lại\*. Có nhiều cách khác nhau để xây dựng RNNs. RNNs với trạng thái ẩn được xác định bởi :eq:`rnn_h_with_state` là rất phổ biến. Đối với bước thời gian :math:`t`, đầu ra của lớp đầu ra tương tự như tính toán trong MLP: .. math:: \mathbf{O}_t = \mathbf{H}_t \mathbf{W}_{hq} + \mathbf{b}_q. Các thông số của RNN bao gồm các trọng lượng :math:`\mathbf{W}_{xh} \in \mathbb{R}^{d \times h}, \mathbf{W}_{hh} \in \mathbb{R}^{h \times h}`, và thiên vị :math:`\mathbf{b}_h \in \mathbb{R}^{1 \times h}` của lớp ẩn, cùng với trọng lượng :math:`\mathbf{W}_{hq} \in \mathbb{R}^{h \times q}` và thiên vị :math:`\mathbf{b}_q \in \mathbb{R}^{1 \times q}` của lớp đầu ra. Điều đáng nói là ngay cả ở các bước thời gian khác nhau, RNN s luôn sử dụng các tham số mô hình này. Do đó, chi phí tham số hóa của một RNN không tăng lên khi số bước thời gian tăng lên. :numref:`fig_rnn` minh họa logic tính toán của một RNN tại ba bước thời gian liền kề. Bất cứ lúc nào bước :math:`t`, tính toán trạng thái ẩn có thể được coi là: (i) nối đầu vào :math:`\mathbf{X}_t` ở bước thời gian hiện tại :math:`t` và trạng thái ẩn :math:`\mathbf{H}_{t-1}` ở bước thời gian trước :math:`t-1`; (ii) cho kết quả nối vào một lớp kết nối đầy đủ với sự kích hoạt chức năng :math:`\phi`. Đầu ra của một lớp được kết nối hoàn toàn như vậy là trạng thái ẩn :math:`\mathbf{H}_t` của bước thời gian hiện tại :math:`t`. Trong trường hợp này, các thông số mô hình là nối :math:`\mathbf{W}_{xh}` và :math:`\mathbf{W}_{hh}`, và một sự thiên vị của :math:`\mathbf{b}_h`, tất cả từ :eq:`rnn_h_with_state`. Trạng thái ẩn của bước thời gian hiện tại :math:`t`, :math:`\mathbf{H}_t`, sẽ tham gia vào việc tính toán trạng thái ẩn :math:`\mathbf{H}_{t+1}` của bước thời gian tiếp theo :math:`t+1`. Hơn nữa, :math:`\mathbf{H}_t` cũng sẽ được đưa vào lớp đầu ra được kết nối hoàn toàn để tính toán đầu ra :math:`\mathbf{O}_t` của bước thời gian hiện tại :math:`t`. .. _fig_rnn: .. figure:: ../img/rnn.svg An RNN with a hidden state. Chúng tôi vừa đề cập rằng việc tính :math:`\mathbf{X}_t \mathbf{W}_{xh} + \mathbf{H}_{t-1} \mathbf{W}_{hh}` cho trạng thái ẩn tương đương với phép nhân ma trận của nối :math:`\mathbf{X}_t` và :math:`\mathbf{H}_{t-1}` và nối :math:`\mathbf{H}_{t-1}` và nối :math:`\mathbf{W}_{xh}` và :math:`\mathbf{W}_{hh}`. Mặc dù điều này có thể được chứng minh trong toán học, sau đây chúng ta chỉ sử dụng một đoạn mã đơn giản để hiển thị điều này. Để bắt đầu, chúng tôi xác định ma trận ``X``, ``W_xh``, ``H`` và ``W_hh``, có hình dạng tương ứng (3, 1), (1, 4), (3, 4) và (4, 4). Nhân ``X`` với ``W_xh``, và ``H`` ``W_hh``, tương ứng, và sau đó thêm hai nhân này, chúng tôi có được một ma trận hình dạng (3, 4). .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. code:: python from mxnet import np, npx from d2l import mxnet as d2l npx.set_np() X, W_xh = np.random.normal(0, 1, (3, 1)), np.random.normal(0, 1, (1, 4)) H, W_hh = np.random.normal(0, 1, (3, 4)), np.random.normal(0, 1, (4, 4)) np.dot(X, W_xh) + np.dot(H, W_hh) .. parsed-literal:: :class: output array([[-0.21952915, 4.256434 , 4.5812645 , -5.344988 ], [ 3.447858 , -3.0177274 , -1.6777471 , 7.535347 ], [ 2.2390068 , 1.4199957 , 4.744728 , -8.421293 ]]) .. raw:: html
.. raw:: html
.. code:: python import torch from d2l import torch as d2l X, W_xh = torch.normal(0, 1, (3, 1)), torch.normal(0, 1, (1, 4)) H, W_hh = torch.normal(0, 1, (3, 4)), torch.normal(0, 1, (4, 4)) torch.matmul(X, W_xh) + torch.matmul(H, W_hh) .. parsed-literal:: :class: output tensor([[ 0.0795, 4.0775, 0.3635, -3.0606], [-1.0515, -3.1537, 1.4391, 4.5820], [-2.4584, -0.9088, 3.2624, 2.7681]]) .. raw:: html
.. raw:: html
.. code:: python import tensorflow as tf from d2l import tensorflow as d2l X, W_xh = tf.random.normal((3, 1), 0, 1), tf.random.normal((1, 4), 0, 1) H, W_hh = tf.random.normal((3, 4), 0, 1), tf.random.normal((4, 4), 0, 1) tf.matmul(X, W_xh) + tf.matmul(H, W_hh) .. parsed-literal:: :class: output .. raw:: html
.. raw:: html
Bây giờ chúng ta nối các ma trận ``X`` và ``H`` dọc theo các cột (trục 1), và ma trận ``W_xh`` và ``W_hh`` dọc theo hàng (trục 0). Hai kết nối này dẫn đến ma trận của hình dạng (3, 5) và hình dạng (5, 4), tương ứng. Nhân hai ma trận nối này, chúng ta có được ma trận đầu ra giống nhau của hình dạng (3, 4) như trên. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. code:: python np.dot(np.concatenate((X, H), 1), np.concatenate((W_xh, W_hh), 0)) .. parsed-literal:: :class: output array([[-0.21952918, 4.256434 , 4.5812645 , -5.344988 ], [ 3.4478583 , -3.0177271 , -1.677747 , 7.535347 ], [ 2.2390068 , 1.4199957 , 4.744728 , -8.421294 ]]) .. raw:: html
.. raw:: html
.. code:: python torch.matmul(torch.cat((X, H), 1), torch.cat((W_xh, W_hh), 0)) .. parsed-literal:: :class: output tensor([[ 0.0795, 4.0775, 0.3635, -3.0606], [-1.0515, -3.1537, 1.4391, 4.5820], [-2.4584, -0.9088, 3.2624, 2.7681]]) .. raw:: html
.. raw:: html
.. code:: python tf.matmul(tf.concat((X, H), 1), tf.concat((W_xh, W_hh), 0)) .. parsed-literal:: :class: output .. raw:: html
.. raw:: html
Mô hình ngôn ngữ cấp ký tự dựa trên RNN --------------------------------------- Nhớ lại rằng đối với mô hình hóa ngôn ngữ trong :numref:`sec_language_model`, chúng tôi đặt mục tiêu dự đoán token tiếp theo dựa trên các mã thông báo hiện tại và quá khứ, do đó chúng tôi chuyển chuỗi gốc bằng một mã thông báo làm nhãn. Bengio et al. lần đầu tiên đề xuất sử dụng mạng thần kinh để mô hình hóa ngôn ngữ :cite:`Bengio.Ducharme.Vincent.ea.2003`. Sau đây, chúng tôi minh họa cách RNN có thể được sử dụng để xây dựng một mô hình ngôn ngữ. Hãy để kích thước minibatch là một, và trình tự của văn bản là “máy”. Để đơn giản hóa việc đào tạo trong các phần tiếp theo, chúng tôi mã hóa văn bản thành các ký tự thay vì từ và xem xét mô hình ngôn ngữ cấp ký tự\*. :numref:`fig_rnn_train` thể hiện cách dự đoán ký tự tiếp theo dựa trên các ký tự hiện tại và trước đó thông qua RNN cho mô hình ngôn ngữ cấp ký tự. .. _fig_rnn_train: .. figure:: ../img/rnn-train.svg A character-level language model based on the RNN. The input and label sequences are "machin" and "achine", respectively. Trong quá trình đào tạo, chúng tôi chạy một thao tác softmax trên đầu ra từ lớp đầu ra cho mỗi bước thời gian, sau đó sử dụng tổn thất chéo entropy để tính toán lỗi giữa đầu ra mô hình và nhãn. Do tính toán tái phát của trạng thái ẩn trong lớp ẩn, đầu ra của bước thời gian 3 trong :numref:`fig_rnn_train`, :math:`\mathbf{O}_3`, được xác định bởi chuỗi văn bản “m”, “a”, và “c”. Vì ký tự tiếp theo của dãy trong dữ liệu đào tạo là “h”, việc mất thời gian bước 3 sẽ phụ thuộc vào phân bố xác suất của ký tự tiếp theo được tạo ra dựa trên dãy tính năng “m”, “a”, “c” và nhãn “h” của bước thời gian này. Trong thực tế, mỗi mã thông báo được đại diện bởi một vector :math:`d` chiều, và chúng tôi sử dụng kích thước lô :math:`n>1`. Do đó, đầu vào :math:`\mathbf X_t` tại bước thời gian :math:`t` sẽ là ma trận :math:`n\times d`, giống hệt với những gì chúng ta đã thảo luận trong :numref:`subsec_rnn_w_hidden_states`. .. _subsec_perplexity: Bối rối ------- Cuối cùng, chúng ta hãy thảo luận về cách đo lường chất lượng mô hình ngôn ngữ, sẽ được sử dụng để đánh giá các mô hình dựa trên RN của chúng tôi trong các phần tiếp theo. Một cách là kiểm tra văn bản đáng ngạc nhiên như thế nào. Một mô hình ngôn ngữ tốt có thể dự đoán với các mã thông báo có độ chính xác cao mà những gì chúng ta sẽ thấy tiếp theo. Hãy xem xét các liên tục sau của cụm từ “Trời mưa”, như được đề xuất bởi các mô hình ngôn ngữ khác nhau: 1. “Trời đang mưa bên ngoài” 2. “Trời đang mưa cây chuối” 3. “It is raining mưa; kcj pwepoiut” Về chất lượng, ví dụ 1 rõ ràng là tốt nhất. Các từ là hợp lý và hợp lý mạch lạc. Mặc dù nó có thể không hoàn toàn phản ánh chính xác từ nào theo ngữ nghĩa (“ở San Francisco” và “vào mùa đông” sẽ là phần mở rộng hoàn toàn hợp lý), mô hình có thể nắm bắt loại từ nào theo sau. Ví dụ 2 tồi tệ hơn đáng kể bằng cách tạo ra một phần mở rộng vô nghĩa. Tuy nhiên, ít nhất mô hình đã học cách đánh vần các từ và một mức độ tương quan giữa các từ. Cuối cùng, ví dụ 3 chỉ ra một mô hình được đào tạo kém không phù hợp với dữ liệu đúng cách. Chúng ta có thể đo lường chất lượng của mô hình bằng cách tính toán khả năng của trình tự. Thật không may đây là một con số khó hiểu và khó so sánh. Rốt cuộc, các trình tự ngắn hơn có nhiều khả năng xảy ra hơn nhiều so với các trình tự dài hơn, do đó đánh giá mô hình trên magnum opus của Tolstoy *Chiến tranh và Hòa bình* chắc chắn sẽ tạo ra một khả năng nhỏ hơn nhiều so với, nói, trên tiểu thuyết của Saint-Exupery\* The Little Prince\*. Những gì còn thiếu là tương đương với mức trung bình. Lý thuyết thông tin có ích ở đây. Chúng tôi đã xác định entropy, ngạc nhiên và cross-entropy khi chúng tôi giới thiệu hồi quy softmax (:numref:`subsec_info_theory_basics`) và nhiều lý thuyết thông tin được thảo luận trong `online appendix on information theory `__. Nếu chúng ta muốn nén văn bản, chúng ta có thể hỏi về việc dự đoán token tiếp theo cho bộ mã thông báo hiện tại. Một mô hình ngôn ngữ tốt hơn sẽ cho phép chúng ta dự đoán token tiếp theo chính xác hơn. Do đó, nó sẽ cho phép chúng ta chi tiêu ít bit hơn trong việc nén chuỗi. Vì vậy, chúng ta có thể đo lường nó bằng cách mất chéo entropy trung bình trên tất cả các mã thông báo :math:`n` của một chuỗi: .. math:: \frac{1}{n} \sum_{t=1}^n -\log P(x_t \mid x_{t-1}, \ldots, x_1), :label: eq_avg_ce_for_lm trong đó :math:`P` được đưa ra bởi một mô hình ngôn ngữ và :math:`x_t` là mã thông báo thực tế quan sát tại bước thời gian :math:`t` từ chuỗi. Điều này làm cho hiệu suất trên các tài liệu có độ dài khác nhau tương đương. Vì lý do lịch sử, các nhà khoa học trong xử lý ngôn ngữ tự nhiên thích sử dụng một số lượng gọi là *perplexity*. Tóm lại, nó là hàm mũ của :eq:`eq_avg_ce_for_lm`: .. math:: \exp\left(-\frac{1}{n} \sum_{t=1}^n \log P(x_t \mid x_{t-1}, \ldots, x_1)\right). Sự bối rối có thể được hiểu rõ nhất là trung bình hài hòa của số lượng lựa chọn thực sự mà chúng ta có khi quyết định token nào sẽ chọn tiếp theo. Chúng ta hãy xem xét một số trường hợp: - Trong trường hợp tốt nhất, mô hình luôn ước tính hoàn hảo xác suất của mã thông báo nhãn là 1. Trong trường hợp này, sự bối rối của mô hình là 1. - Trong trường hợp xấu nhất, mô hình luôn dự đoán xác suất của token nhãn là 0. Trong tình huống này, sự bối rối là vô cùng tích cực. - Tại đường cơ sở, mô hình dự đoán phân phối thống nhất trên tất cả các mã thông báo có sẵn của từ vựng. Trong trường hợp này, sự bối rối bằng số lượng mã thông báo duy nhất của từ vựng. Trên thực tế, nếu chúng ta lưu trữ trình tự mà không cần bất kỳ nén nào, đây sẽ là điều tốt nhất chúng ta có thể làm để mã hóa nó. Do đó, điều này cung cấp một ràng buộc trên không tầm thường mà bất kỳ mô hình hữu ích nào cũng phải đánh bại. Trong các phần sau, chúng tôi sẽ triển khai RNNcho các mô hình ngôn ngữ cấp ký tự và sử dụng sự bối rối để đánh giá các mô hình như vậy. Tóm tắt ------- - Một mạng thần kinh sử dụng tính toán định kỳ cho các trạng thái ẩn được gọi là mạng thần kinh tái phát (RNN). - Trạng thái ẩn của một RNN có thể nắm bắt thông tin lịch sử của chuỗi lên đến bước thời gian hiện tại. - Số lượng tham số mô hình RNN không tăng lên khi số bước thời gian tăng lên. - Chúng ta có thể tạo mô hình ngôn ngữ cấp ký tự bằng cách sử dụng RNN. - Chúng ta có thể sử dụng sự bối rối để đánh giá chất lượng của các mô hình ngôn ngữ. Bài tập ------- 1. Nếu chúng ta sử dụng một RNN để dự đoán ký tự tiếp theo trong một chuỗi văn bản, kích thước cần thiết cho bất kỳ đầu ra nào là gì? 2. Tại sao RNN s có thể thể hiện xác suất có điều kiện của một mã thông báo tại một bước thời gian dựa trên tất cả các token trước đó trong chuỗi văn bản? 3. Điều gì xảy ra với gradient nếu bạn backpropagate thông qua một chuỗi dài? 4. Một số vấn đề liên quan đến mô hình ngôn ngữ được mô tả trong phần này là gì? .. raw:: html
.. raw:: html
`Discussions `__ .. raw:: html
.. raw:: html
`Discussions `__ .. raw:: html
.. raw:: html
`Discussions `__ .. raw:: html
.. raw:: html