🏷️sec_seq2seq_attention
Trong phần này, chúng ta thêm cơ chế tập trung vào mô hình chuỗi sang chuỗi (seq2seq) giới thiệu trong :numref:sec_seq2seq
để gộp các trạng thái theo trọng số tương ứng một cách tường minh.
:numref:fig_s2s_attention
mô tả kiến trúc mô hình thực hiện mã hóa và giải mã tại bước thời gian
Để minh họa kiến trúc tổng thể của mô hình seq2seq áp dụng cơ chế tập trung, cấu trúc tầng của bộ mã hóa và bộ giải mã được mô tả trong :numref:fig_s2s_attention_details
.
from d2l import mxnet as d2l
from mxnet import np, npx
from mxnet.gluon import rnn, nn
npx.set_np()
Do bộ mã hóa của mô hình seq2seq áp dụng cơ chế tập trung giống với bộ mã hóa của Seq2SeqEncoder
trong :numref:sec_seq2seq
nên ở phần này, chúng ta sẽ chỉ tập trung vào bộ giải mã.
Ta thêm tầng tập trung MLP (MLPAttention
) có cùng kích thước ẩn với tầng LSTM trong bộ giải mã.
Sau đó ta khởi tạo trạng thái của bộ giải mã bằng cách truyền vào ba đầu ra thu được từ bộ mã hóa:
- Đầu ra của bộ mã hóa tại tất cả các bước thời gian: được sử dụng như bộ nhớ của tầng tập trung có cùng các khóa và giá trị;
- Trạng thái ẩn của bộ mã hóa tại bước thời gian cuối cùng: được sử dụng làm trạng thái ẩn ban đầu của bộ giải mã;
- Độ dài hợp lệ của bộ mã hóa: để tầng tập trung có thể bỏ qua những token đệm có trong đầu ra của bộ mã hóa.
Ở mỗi bước thời gian trong quá trình giải mã, ta sử dụng trạng thái ẩn của tầng RNN cuối cùng làm câu truy vấn cho tầng tập trung.
Đầu ra của mô hình tập trung sau đó được ghép nối với vector embedding đầu vào để đưa vào tầng RNN.
Mặc dù trạng thái ẩn của tầng RNN cũng chứa thông tin từ bộ giải mã ở các bước thời gian trước đó nhưng đầu ra của tầng tập trung sẽ lựa chọn các đầu ra của bộ mã hóa một cách tường minh dựa vào enc_valid_len
nhằm loại bỏ những thông tin không liên quan.
Hãy cùng lập trình bộ giải mã Seq2SeqAttentionDecoder
và xem xét sự khác biệt của nó so với bộ giải mã trong mô hình seq2seq ở :numref:sec_seq2seq_decoder
.
class Seq2SeqAttentionDecoder(d2l.Decoder):
def __init__(self, vocab_size, embed_size, num_hiddens, num_layers,
dropout=0, **kwargs):
super(Seq2SeqAttentionDecoder, self).__init__(**kwargs)
self.attention_cell = d2l.MLPAttention(num_hiddens, dropout)
self.embedding = nn.Embedding(vocab_size, embed_size)
self.rnn = rnn.LSTM(num_hiddens, num_layers, dropout=dropout)
self.dense = nn.Dense(vocab_size, flatten=False)
def init_state(self, enc_outputs, enc_valid_len, *args):
outputs, hidden_state = enc_outputs
# Transpose outputs to (batch_size, seq_len, num_hiddens)
return (outputs.swapaxes(0, 1), hidden_state, enc_valid_len)
def forward(self, X, state):
enc_outputs, hidden_state, enc_valid_len = state
X = self.embedding(X).swapaxes(0, 1)
outputs = []
for x in X:
# query shape: (batch_size, 1, num_hiddens)
query = np.expand_dims(hidden_state[0][-1], axis=1)
# context has same shape as query
context = self.attention_cell(
query, enc_outputs, enc_outputs, enc_valid_len)
# Concatenate on the feature dimension
x = np.concatenate((context, np.expand_dims(x, axis=1)), axis=-1)
# Reshape x to (1, batch_size, embed_size + num_hiddens)
out, hidden_state = self.rnn(x.swapaxes(0, 1), hidden_state)
outputs.append(out)
outputs = self.dense(np.concatenate(outputs, axis=0))
return outputs.swapaxes(0, 1), [enc_outputs, hidden_state,
enc_valid_len]
Giờ ta có thể chạy thử mô hình seq2seq áp dụng cơ chế tập trung.
Để nhất quán khi so sánh với mô hình không áp dụng cơ chế tập trung trong :numref:sec_seq2seq
, những siêu tham số vocab_size
, embed_size
, num_hiddens
, và num_layers
sẽ được giữ nguyên.
Kết quả, ta thu được đầu ra của bộ giải mã có cùng kích thước nhưng khác về cấu trúc trạng thái.
encoder = d2l.Seq2SeqEncoder(vocab_size=10, embed_size=8,
num_hiddens=16, num_layers=2)
encoder.initialize()
decoder = Seq2SeqAttentionDecoder(vocab_size=10, embed_size=8,
num_hiddens=16, num_layers=2)
decoder.initialize()
X = np.zeros((4, 7))
state = decoder.init_state(encoder(X), None)
out, state = decoder(X, state)
out.shape, len(state), state[0].shape, len(state[1]), state[1][0].shape
Chúng ta hãy xây dựng một mô hình đơn giản sử dụng cùng một bộ siêu tham số và hàm mất mát để huấn luyện như :numref:sec_seq2seq_training
.
Từ kết quả, ta thấy tầng tập trung được thêm vào mô hình không tạo ra cải thiện đáng kể nào do các chuỗi trong tập huấn luyện khá ngắn.
Bởi chi phí tính toán tốn thêm từ các tầng tập trung trong bộ mã hóa và bộ giải mã, mô hình này họat động chậm hơn nhiều so với mô hình seq2seq không áp dụng tập trung.
embed_size, num_hiddens, num_layers, dropout = 32, 32, 2, 0.0
batch_size, num_steps = 64, 10
lr, num_epochs, ctx = 0.005, 200, d2l.try_gpu()
src_vocab, tgt_vocab, train_iter = d2l.load_data_nmt(batch_size, num_steps)
encoder = d2l.Seq2SeqEncoder(
len(src_vocab), embed_size, num_hiddens, num_layers, dropout)
decoder = Seq2SeqAttentionDecoder(
len(tgt_vocab), embed_size, num_hiddens, num_layers, dropout)
model = d2l.EncoderDecoder(encoder, decoder)
d2l.train_s2s_ch9(model, train_iter, lr, num_epochs, ctx)
Cuối cùng, ta hãy thử dự đoán một vài mẫu dưới đây.
for sentence in ['Go .', 'Wow !', "I'm OK .", 'I won !']:
print(sentence + ' => ' + d2l.predict_s2s_ch9(
model, sentence, src_vocab, tgt_vocab, num_steps, ctx))
- Mô hình seq2seq áp dụng cơ chế tập trung thêm một tầng tập trung vào mô hình seq2seq ban đầu.
- Bộ giải mã của mô hình seq2seq áp dụng cơ chế tập trung được truyền vào ba đầu ra từ bộ mã hóa: đầu ra của bộ mã hóa tại tất cả các bước thời gian, trạng thái ẩn của bộ mã hóa tại bước thời gian cuối cùng, độ dài hợp lệ của bộ mã hóa.
- So sánh
Seq2SeqAttentionDecoder
vàSeq2seqDecoder
bằng cách sử dụng cùng bộ tham số và kiểm tra giá trị hàm mất mát. - Bạn hãy thử suy nghĩ liệu có trường hợp nào mà
Seq2SeqAttentionDecoder
vượt trội hơnSeq2seqDecoder
?
Bản dịch trong trang này được thực hiện bởi:
- Đoàn Võ Duy Thanh
- Đỗ Trường Giang
- Nguyễn Văn Quang
- Nguyễn Văn Cường
- Lê Khắc Hồng Phúc
- Phạm Hồng Vinh