.. _sec_lstm:
Bộ nhớ ngắn hạn dài (LSTM)
==========================
Thách thức giải quyết việc bảo tồn thông tin dài hạn và bỏ qua đầu vào
ngắn hạn trong các mô hình biến tiềm ẩn đã tồn tại trong một thời gian
dài. Một trong những cách tiếp cận sớm nhất để giải quyết vấn đề này là
bộ nhớ ngắn hạn dài (LSTM) :cite:`Hochreiter.Schmidhuber.1997`. Nó
chia sẻ nhiều tài sản của GRU. Điều thú vị là LSTMs có thiết kế phức tạp
hơn một chút so với Grus nhưng trước Grus gần hai thập kỷ.
Gated Memory Cell
-----------------
Được cho là thiết kế của LSTM được lấy cảm hứng từ cổng logic của máy
tính. LSTM giới thiệu một tế bào *memory cell* (hoặc *cell* nói ngắn) có
hình dạng giống như trạng thái ẩn (một số văn học coi ô nhớ là một loại
đặc biệt của trạng thái ẩn), được thiết kế để ghi lại thông tin bổ sung.
Để kiểm soát tế bào bộ nhớ, chúng ta cần một số cổng. Một cổng là cần
thiết để đọc các mục từ ô. Chúng tôi sẽ đề cập đến điều này là *cổng đầu
ra*. Một cổng thứ hai là cần thiết để quyết định khi nào nên đọc dữ liệu
vào ô. Chúng tôi gọi đây là cổng đầu vào \*\*. Cuối cùng, chúng ta cần
một cơ chế để thiết lập lại nội dung của ô, được chi phối bởi một cổng
\* quên \*. Động lực cho một thiết kế như vậy giống như của Grus, cụ thể
là có thể quyết định khi nào cần nhớ và khi nào bỏ qua các đầu vào ở
trạng thái ẩn thông qua một cơ chế chuyên dụng. Hãy để chúng tôi xem làm
thế nào điều này hoạt động trong thực tế.
Cổng đầu vào, Cổng quên và Cổng đầu ra
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Cũng giống như trong Grus, việc cấp dữ liệu vào cổng LSTM là đầu vào ở
bước thời gian hiện tại và trạng thái ẩn của bước thời gian trước đó,
như minh họa trong :numref:`lstm_0`. Chúng được xử lý bởi ba lớp được
kết nối hoàn toàn với chức năng kích hoạt sigmoid để tính toán các giá
trị của đầu vào, quê. và cổng đầu ra. Kết quả là, các giá trị của ba
cổng nằm trong khoảng :math:`(0, 1)`.
.. _lstm_0:
.. figure:: ../img/lstm-0.svg
Computing the input gate, the forget gate, and the output gate in an
LSTM model.
Về mặt toán học, giả sử rằng có :math:`h` đơn vị ẩn, kích thước lô là
:math:`n` và số lượng đầu vào là :math:`d`. Do đó, đầu vào là
:math:`\mathbf{X}_t \in \mathbb{R}^{n \times d}` và trạng thái ẩn của
bước thời gian trước là
:math:`\mathbf{H}_{t-1} \in \mathbb{R}^{n \times h}`. Tương ứng, các
cổng tại bước thời điểm :math:`t` được định nghĩa như sau: cổng đầu vào
là :math:`\mathbf{I}_t \in \mathbb{R}^{n \times h}`, cổng quên là
:math:`\mathbf{F}_t \in \mathbb{R}^{n \times h}` và cổng đầu ra là
:math:`\mathbf{O}_t \in \mathbb{R}^{n \times h}`. Chúng được tính như
sau:
.. math::
\begin{aligned}
\mathbf{I}_t &= \sigma(\mathbf{X}_t \mathbf{W}_{xi} + \mathbf{H}_{t-1} \mathbf{W}_{hi} + \mathbf{b}_i),\\
\mathbf{F}_t &= \sigma(\mathbf{X}_t \mathbf{W}_{xf} + \mathbf{H}_{t-1} \mathbf{W}_{hf} + \mathbf{b}_f),\\
\mathbf{O}_t &= \sigma(\mathbf{X}_t \mathbf{W}_{xo} + \mathbf{H}_{t-1} \mathbf{W}_{ho} + \mathbf{b}_o),
\end{aligned}
trong đó
:math:`\mathbf{W}_{xi}, \mathbf{W}_{xf}, \mathbf{W}_{xo} \in \mathbb{R}^{d \times h}`
và
:math:`\mathbf{W}_{hi}, \mathbf{W}_{hf}, \mathbf{W}_{ho} \in \mathbb{R}^{h \times h}`
là các thông số trọng lượng và
:math:`\mathbf{b}_i, \mathbf{b}_f, \mathbf{b}_o \in \mathbb{R}^{1 \times h}`
là các thông số thiên vị.
Tế bào bộ nhớ ứng cử viên
~~~~~~~~~~~~~~~~~~~~~~~~~
Tiếp theo chúng tôi thiết kế các tế bào bộ nhớ. Vì chúng tôi chưa chỉ
định hành động của các cổng khác nhau, trước tiên chúng tôi giới thiệu
tế bào bộ nhớ \*ứng cử viên
:math:`\tilde{\mathbf{C}}_t \in \mathbb{R}^{n \times h}`. Tính toán của
nó tương tự như của ba cổng được mô tả ở trên, nhưng sử dụng hàm
:math:`\tanh` với phạm vi giá trị cho :math:`(-1, 1)` làm hàm kích hoạt.
Điều này dẫn đến phương trình sau tại bước thời điểm :math:`t`:
.. math:: \tilde{\mathbf{C}}_t = \text{tanh}(\mathbf{X}_t \mathbf{W}_{xc} + \mathbf{H}_{t-1} \mathbf{W}_{hc} + \mathbf{b}_c),
trong đó :math:`\mathbf{W}_{xc} \in \mathbb{R}^{d \times h}` và
:math:`\mathbf{W}_{hc} \in \mathbb{R}^{h \times h}` là các thông số
trọng lượng và :math:`\mathbf{b}_c \in \mathbb{R}^{1 \times h}` là một
tham số thiên vị.
Một minh họa nhanh chóng của tế bào bộ nhớ ứng cử viên được thể hiện
trong :numref:`lstm_1`.
.. _lstm_1:
.. figure:: ../img/lstm-1.svg
Computing the candidate memory cell in an LSTM model.
Bộ nhớ Cell
~~~~~~~~~~~
Trong Grus, chúng ta có một cơ chế để chi phối đầu vào và quên (hoặc bỏ
qua). Tương tự, trong LSTMs, chúng tôi có hai cổng chuyên dụng cho các
mục đích như vậy: cổng đầu vào :math:`\mathbf{I}_t` chi phối số lượng
chúng tôi tính đến dữ liệu mới thông qua :math:`\tilde{\mathbf{C}}_t` và
cổng quên :math:`\mathbf{F}_t` giải quyết bao nhiêu nội dung tế bào bộ
nhớ cũ :math:`\mathbf{C}_{t-1} \in \mathbb{R}^{n \times h}` chúng tôi
giữ lại. Sử dụng cùng một thủ thuật nhân theo chiều ngang như trước đây,
chúng tôi đến phương trình cập nhật sau:
.. math:: \mathbf{C}_t = \mathbf{F}_t \odot \mathbf{C}_{t-1} + \mathbf{I}_t \odot \tilde{\mathbf{C}}_t.
Nếu cổng quên luôn xấp xỉ 1 và cổng đầu vào luôn xấp xỉ 0, các ô bộ nhớ
quá khứ :math:`\mathbf{C}_{t-1}` sẽ được lưu theo thời gian và được
chuyển sang bước thời gian hiện tại. Thiết kế này được giới thiệu để
giảm bớt vấn đề gradient biến mất và để nắm bắt tốt hơn các phụ thuộc
tầm xa trong chuỗi.
Do đó, chúng tôi đến sơ đồ dòng chảy trong :numref:`lstm_2`.
.. _lstm_2:
.. figure:: ../img/lstm-2.svg
Computing the memory cell in an LSTM model.
Nhà nước ẩn
~~~~~~~~~~~
Cuối cùng, chúng ta cần xác định cách tính trạng thái ẩn
:math:`\mathbf{H}_t \in \mathbb{R}^{n \times h}`. Đây là nơi cổng đầu ra
phát huy tác dụng. Trong LSTM, nó chỉ đơn giản là một phiên bản cổng của
:math:`\tanh` của tế bào bộ nhớ. Điều này đảm bảo rằng các giá trị của
:math:`\mathbf{H}_t` luôn nằm trong khoảng :math:`(-1, 1)`.
.. math:: \mathbf{H}_t = \mathbf{O}_t \odot \tanh(\mathbf{C}_t).
Bất cứ khi nào cổng đầu ra xấp xỉ 1, chúng tôi có hiệu quả truyền tất cả
thông tin bộ nhớ đến bộ dự đoán, trong khi đối với cổng đầu ra gần 0,
chúng tôi chỉ giữ lại tất cả thông tin trong ô nhớ và không thực hiện xử
lý thêm.
:numref:`lstm_3` có một minh họa đồ họa của luồng dữ liệu.
.. _lstm_3:
.. figure:: ../img/lstm-3.svg
Computing the hidden state in an LSTM model.
Thực hiện từ đầu
----------------
Bây giờ chúng ta hãy thực hiện một LSTM từ đầu. Giống như các thí nghiệm
trong :numref:`sec_rnn_scratch`, lần đầu tiên chúng ta tải tập dữ liệu
máy thời gian.
.. raw:: html
.. raw:: html
.. code:: python
from mxnet import np, npx
from mxnet.gluon import rnn
from d2l import mxnet as d2l
npx.set_np()
batch_size, num_steps = 32, 35
train_iter, vocab = d2l.load_data_time_machine(batch_size, num_steps)
.. raw:: html
.. raw:: html
.. code:: python
import torch
from torch import nn
from d2l import torch as d2l
batch_size, num_steps = 32, 35
train_iter, vocab = d2l.load_data_time_machine(batch_size, num_steps)
.. raw:: html
.. raw:: html
.. code:: python
import tensorflow as tf
from d2l import tensorflow as d2l
batch_size, num_steps = 32, 35
train_iter, vocab = d2l.load_data_time_machine(batch_size, num_steps)
.. parsed-literal::
:class: output
Downloading ../data/timemachine.txt from http://d2l-data.s3-accelerate.amazonaws.com/timemachine.txt...
.. raw:: html
.. raw:: html
Initializing Model Parameters
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tiếp theo chúng ta cần xác định và khởi tạo các tham số mô hình. Như
trước đây, siêu tham số ``num_hiddens`` xác định số đơn vị ẩn. Chúng tôi
khởi tạo các trọng lượng sau một phân phối Gaussian với 0,01 độ lệch
chuẩn, và chúng tôi đặt các thành kiến là 0.
.. raw:: html
.. raw:: html
.. code:: python
def get_lstm_params(vocab_size, num_hiddens, device):
num_inputs = num_outputs = vocab_size
def normal(shape):
return np.random.normal(scale=0.01, size=shape, ctx=device)
def three():
return (normal((num_inputs, num_hiddens)),
normal((num_hiddens, num_hiddens)),
np.zeros(num_hiddens, ctx=device))
W_xi, W_hi, b_i = three() # Input gate parameters
W_xf, W_hf, b_f = three() # Forget gate parameters
W_xo, W_ho, b_o = three() # Output gate parameters
W_xc, W_hc, b_c = three() # Candidate memory cell parameters
# Output layer parameters
W_hq = normal((num_hiddens, num_outputs))
b_q = np.zeros(num_outputs, ctx=device)
# Attach gradients
params = [W_xi, W_hi, b_i, W_xf, W_hf, b_f, W_xo, W_ho, b_o, W_xc, W_hc,
b_c, W_hq, b_q]
for param in params:
param.attach_grad()
return params
.. raw:: html
.. raw:: html
.. code:: python
def get_lstm_params(vocab_size, num_hiddens, device):
num_inputs = num_outputs = vocab_size
def normal(shape):
return torch.randn(size=shape, device=device)*0.01
def three():
return (normal((num_inputs, num_hiddens)),
normal((num_hiddens, num_hiddens)),
torch.zeros(num_hiddens, device=device))
W_xi, W_hi, b_i = three() # Input gate parameters
W_xf, W_hf, b_f = three() # Forget gate parameters
W_xo, W_ho, b_o = three() # Output gate parameters
W_xc, W_hc, b_c = three() # Candidate memory cell parameters
# Output layer parameters
W_hq = normal((num_hiddens, num_outputs))
b_q = torch.zeros(num_outputs, device=device)
# Attach gradients
params = [W_xi, W_hi, b_i, W_xf, W_hf, b_f, W_xo, W_ho, b_o, W_xc, W_hc,
b_c, W_hq, b_q]
for param in params:
param.requires_grad_(True)
return params
.. raw:: html
.. raw:: html
.. code:: python
def get_lstm_params(vocab_size, num_hiddens):
num_inputs = num_outputs = vocab_size
def normal(shape):
return tf.Variable(tf.random.normal(shape=shape, stddev=0.01,
mean=0, dtype=tf.float32))
def three():
return (normal((num_inputs, num_hiddens)),
normal((num_hiddens, num_hiddens)),
tf.Variable(tf.zeros(num_hiddens), dtype=tf.float32))
W_xi, W_hi, b_i = three() # Input gate parameters
W_xf, W_hf, b_f = three() # Forget gate parameters
W_xo, W_ho, b_o = three() # Output gate parameters
W_xc, W_hc, b_c = three() # Candidate memory cell parameters
# Output layer parameters
W_hq = normal((num_hiddens, num_outputs))
b_q = tf.Variable(tf.zeros(num_outputs), dtype=tf.float32)
# Attach gradients
params = [W_xi, W_hi, b_i, W_xf, W_hf, b_f, W_xo, W_ho, b_o, W_xc, W_hc,
b_c, W_hq, b_q]
return params
.. raw:: html
.. raw:: html
Xác định mô hình
~~~~~~~~~~~~~~~~
Trong the initialization function, trạng thái ẩn của LSTM cần trả về một
ô bộ nhớ *additional* với giá trị 0 và hình dạng (kích thước lô, số đơn
vị ẩn). Do đó chúng tôi nhận được sự khởi tạo trạng thái sau đây.
.. raw:: html
.. raw:: html
.. code:: python
def init_lstm_state(batch_size, num_hiddens, device):
return (np.zeros((batch_size, num_hiddens), ctx=device),
np.zeros((batch_size, num_hiddens), ctx=device))
.. raw:: html
.. raw:: html
.. code:: python
def init_lstm_state(batch_size, num_hiddens, device):
return (torch.zeros((batch_size, num_hiddens), device=device),
torch.zeros((batch_size, num_hiddens), device=device))
.. raw:: html
.. raw:: html
.. code:: python
def init_lstm_state(batch_size, num_hiddens):
return (tf.zeros(shape=(batch_size, num_hiddens)),
tf.zeros(shape=(batch_size, num_hiddens)))
.. raw:: html
.. raw:: html
mô hình thực tế được định nghĩa giống như những gì chúng ta đã thảo luận
trước đây: cung cấp ba cổng và một tế bào bộ nhớ phụ trợ. Lưu ý rằng chỉ
có trạng thái ẩn được chuyển đến lớp đầu ra. Các tế bào bộ nhớ
:math:`\mathbf{C}_t` không trực tiếp tham gia vào việc tính toán đầu ra.
.. raw:: html
.. raw:: html
.. code:: python
def lstm(inputs, state, params):
[W_xi, W_hi, b_i, W_xf, W_hf, b_f, W_xo, W_ho, b_o, W_xc, W_hc, b_c,
W_hq, b_q] = params
(H, C) = state
outputs = []
for X in inputs:
I = npx.sigmoid(np.dot(X, W_xi) + np.dot(H, W_hi) + b_i)
F = npx.sigmoid(np.dot(X, W_xf) + np.dot(H, W_hf) + b_f)
O = npx.sigmoid(np.dot(X, W_xo) + np.dot(H, W_ho) + b_o)
C_tilda = np.tanh(np.dot(X, W_xc) + np.dot(H, W_hc) + b_c)
C = F * C + I * C_tilda
H = O * np.tanh(C)
Y = np.dot(H, W_hq) + b_q
outputs.append(Y)
return np.concatenate(outputs, axis=0), (H, C)
.. raw:: html
.. raw:: html
.. code:: python
def lstm(inputs, state, params):
[W_xi, W_hi, b_i, W_xf, W_hf, b_f, W_xo, W_ho, b_o, W_xc, W_hc, b_c,
W_hq, b_q] = params
(H, C) = state
outputs = []
for X in inputs:
I = torch.sigmoid((X @ W_xi) + (H @ W_hi) + b_i)
F = torch.sigmoid((X @ W_xf) + (H @ W_hf) + b_f)
O = torch.sigmoid((X @ W_xo) + (H @ W_ho) + b_o)
C_tilda = torch.tanh((X @ W_xc) + (H @ W_hc) + b_c)
C = F * C + I * C_tilda
H = O * torch.tanh(C)
Y = (H @ W_hq) + b_q
outputs.append(Y)
return torch.cat(outputs, dim=0), (H, C)
.. raw:: html
.. raw:: html
.. code:: python
def lstm(inputs, state, params):
W_xi, W_hi, b_i, W_xf, W_hf, b_f, W_xo, W_ho, b_o, W_xc, W_hc, b_c, W_hq, b_q = params
(H, C) = state
outputs = []
for X in inputs:
X=tf.reshape(X,[-1,W_xi.shape[0]])
I = tf.sigmoid(tf.matmul(X, W_xi) + tf.matmul(H, W_hi) + b_i)
F = tf.sigmoid(tf.matmul(X, W_xf) + tf.matmul(H, W_hf) + b_f)
O = tf.sigmoid(tf.matmul(X, W_xo) + tf.matmul(H, W_ho) + b_o)
C_tilda = tf.tanh(tf.matmul(X, W_xc) + tf.matmul(H, W_hc) + b_c)
C = F * C + I * C_tilda
H = O * tf.tanh(C)
Y = tf.matmul(H, W_hq) + b_q
outputs.append(Y)
return tf.concat(outputs, axis=0), (H,C)
.. raw:: html
.. raw:: html
Đào tạo và Dự đoán
~~~~~~~~~~~~~~~~~~
Chúng ta hãy đào tạo một LSTM giống như những gì chúng tôi đã làm trong
:numref:`sec_gru`, bằng cách khởi tạo lớp ``RNNModelScratch`` như được
giới thiệu trong :numref:`sec_rnn_scratch`.
.. raw:: html
.. raw:: html
.. code:: python
vocab_size, num_hiddens, device = len(vocab), 256, d2l.try_gpu()
num_epochs, lr = 500, 1
model = d2l.RNNModelScratch(len(vocab), num_hiddens, device, get_lstm_params,
init_lstm_state, lstm)
d2l.train_ch8(model, train_iter, vocab, lr, num_epochs, device)
.. parsed-literal::
:class: output
perplexity 1.2, 9030.1 tokens/sec on gpu(0)
time travellerit s against reason said filbywh w beewhith of man
travellery off the bredarsoncacimitabdeat fil so walk thisk
.. figure:: output_lstm_86eb9f_51_1.svg
.. raw:: html
.. raw:: html
.. code:: python
vocab_size, num_hiddens, device = len(vocab), 256, d2l.try_gpu()
num_epochs, lr = 500, 1
model = d2l.RNNModelScratch(len(vocab), num_hiddens, device, get_lstm_params,
init_lstm_state, lstm)
d2l.train_ch8(model, train_iter, vocab, lr, num_epochs, device)
.. parsed-literal::
:class: output
perplexity 1.1, 21930.5 tokens/sec on cuda:0
time travelleryou can show black is white by argument said filby
traveller a forescereopeer three dimensions and the latter
.. figure:: output_lstm_86eb9f_54_1.svg
.. raw:: html
.. raw:: html
.. code:: python
vocab_size, num_hiddens, device_name = len(vocab), 256, d2l.try_gpu()._device_name
num_epochs, lr = 500, 1
strategy = tf.distribute.OneDeviceStrategy(device_name)
with strategy.scope():
model = d2l.RNNModelScratch(len(vocab), num_hiddens, init_lstm_state, lstm, get_lstm_params)
d2l.train_ch8(model, train_iter, vocab, lr, num_epochs, strategy)
.. parsed-literal::
:class: output
perplexity 1.1, 3704.1 tokens/sec on /GPU:0
time traveller for so it will be convenient to speak of himwas e
travellerification of this know a feetsmin sfiflly thus thi
.. figure:: output_lstm_86eb9f_57_1.svg
.. raw:: html
.. raw:: html
Thiết tập
---------
Sử dụng API cấp cao, chúng ta có thể khởi tạo trực tiếp một mô hình
``LSTM``. Điều này đóng gói tất cả các chi tiết cấu hình mà chúng tôi đã
thực hiện rõ ràng ở trên. Mã này nhanh hơn đáng kể vì nó sử dụng các
toán tử được biên dịch hơn là Python cho nhiều chi tiết mà chúng tôi đã
nêu chi tiết trước đây.
.. raw:: html
.. raw:: html
.. code:: python
lstm_layer = rnn.LSTM(num_hiddens)
model = d2l.RNNModel(lstm_layer, len(vocab))
d2l.train_ch8(model, train_iter, vocab, lr, num_epochs, device)
.. parsed-literal::
:class: output
perplexity 1.1, 158023.4 tokens/sec on gpu(0)
time traveller but now you begin to seethe object of my investig
traveller he way be able to stop oraccelerate his drift alo
.. figure:: output_lstm_86eb9f_63_1.svg
.. raw:: html
.. raw:: html
.. code:: python
num_inputs = vocab_size
lstm_layer = nn.LSTM(num_inputs, num_hiddens)
model = d2l.RNNModel(lstm_layer, len(vocab))
model = model.to(device)
d2l.train_ch8(model, train_iter, vocab, lr, num_epochs, device)
.. parsed-literal::
:class: output
perplexity 1.0, 311960.7 tokens/sec on cuda:0
time traveller with a slight accession ofcheerfulness really thi
travelleryou can show black is white by argument said filby
.. figure:: output_lstm_86eb9f_66_1.svg
.. raw:: html
.. raw:: html
.. code:: python
lstm_cell = tf.keras.layers.LSTMCell(num_hiddens,
kernel_initializer='glorot_uniform')
lstm_layer = tf.keras.layers.RNN(lstm_cell, time_major=True,
return_sequences=True, return_state=True)
device_name = d2l.try_gpu()._device_name
strategy = tf.distribute.OneDeviceStrategy(device_name)
with strategy.scope():
model = d2l.RNNModel(lstm_layer, vocab_size=len(vocab))
d2l.train_ch8(model, train_iter, vocab, lr, num_epochs, strategy)
.. parsed-literal::
:class: output
perplexity 1.0, 8551.9 tokens/sec on /GPU:0
time traveller for so it will be convenient to speak of himwas e
traveller with a slight accession ofcheerfulness really thi
.. figure:: output_lstm_86eb9f_69_1.svg
.. raw:: html
.. raw:: html
LSTMs là mô hình autoregressive biến tiềm ẩn nguyên mẫu với điều khiển
trạng thái không tầm thường. Nhiều biến thể của chúng đã được đề xuất
trong những năm qua, ví dụ, nhiều lớp, kết nối còn lại, các loại chính
quy hóa khác nhau. Tuy nhiên, việc đào tạo LSTMs và các mô hình trình tự
khác (chẳng hạn như Grus) khá tốn kém do sự phụ thuộc tầm xa của trình
tự. Sau đó chúng ta sẽ gặp phải các mô hình thay thế như máy biến áp có
thể được sử dụng trong một số trường hợp.
Tóm tắt
-------
- LSTMs có ba loại cổng: cổng đầu vào, cổng quên và cổng đầu ra kiểm
soát luồng thông tin.
- Đầu ra lớp ẩn của LSTM bao gồm trạng thái ẩn và ô nhớ. Chỉ có trạng
thái ẩn được truyền vào lớp đầu ra. Tế bào bộ nhớ hoàn toàn bên
trong.
- LSTMs có thể làm giảm bớt độ dốc biến mất và bùng nổ.
Bài tập
-------
1. Điều chỉnh các siêu tham số và phân tích ảnh hưởng của chúng đối với
thời gian chạy, bối rối và trình tự đầu ra.
2. Làm thế nào bạn sẽ cần phải thay đổi mô hình để tạo ra các từ thích
hợp như trái ngược với chuỗi các ký tự?
3. So sánh chi phí tính toán cho Grus, LSTMs và RNN thông thường cho một
chiều ẩn nhất định. Đặc biệt chú ý đến chi phí đào tạo và suy luận.
4. Vì ô bộ nhớ ứng cử viên đảm bảo rằng phạm vi giá trị nằm trong khoảng
:math:`-1` và :math:`1` bằng cách sử dụng hàm :math:`\tanh`, tại sao
trạng thái ẩn cần sử dụng lại chức năng :math:`\tanh` để đảm bảo rằng
phạm vi giá trị đầu ra nằm trong khoảng từ :math:`-1` và :math:`1`?
5. Triển khai một mô hình LSTM cho dự đoán chuỗi thời gian hơn là dự
đoán chuỗi ký tự.
.. raw:: html
.. raw:: html
`Discussions `__
.. raw:: html
.. raw:: html
`Discussions `__
.. raw:: html
.. raw:: html