.. _sec_transformer:
Máy biến áp
===========
Chúng tôi đã so sánh CNN, RNN và sự tự chú ý trong
:numref:`subsec_cnn-rnn-self-attention`. Đáng chú ý, sự tự chú ý thích
cả tính toán song song và độ dài đường tối đa ngắn nhất. Do đó về mặt tự
nhiên, nó là hấp dẫn để thiết kế kiến trúc sâu sắc bằng cách sử dụng sự
tự chú ý. Không giống như các mô hình tự chú ý trước đó vẫn dựa vào RNN
để biểu diễn đầu vào
:cite:`Cheng.Dong.Lapata.2016,Lin.Feng.Santos.ea.2017,Paulus.Xiong.Socher.2017`,
mô hình máy biến áp chỉ dựa trên các cơ chế chú ý mà không có bất kỳ lớp
phức tạp hoặc tái phát nào :cite:`Vaswani.Shazeer.Parmar.ea.2017`. Mặc
dù ban đầu được đề xuất cho trình tự để học trình tự về dữ liệu văn bản,
các máy biến áp đã phổ biến trong một loạt các ứng dụng học sâu hiện
đại, chẳng hạn như trong các lĩnh vực ngôn ngữ, tầm nhìn, lời nói và học
tập củng cố.
Mô hình
-------
Là một ví dụ của kiến trúc bộ mã hóa-giải mã, kiến trúc tổng thể của máy
biến áp được trình bày trong :numref:`fig_transformer`. Như chúng ta
có thể thấy, máy biến áp bao gồm một bộ mã hóa và bộ giải mã. Khác với
sự chú ý của Bahdanau cho trình tự để học trình tự trong
:numref:`fig_s2s_attention_details`, các nhúng chuỗi đầu vào (nguồn)
và đầu ra (mục tiêu) được thêm vào với mã hóa vị trí trước khi được đưa
vào bộ mã hóa và bộ giải mã ngăn xếp các mô-đun dựa trên sự tự chú ý.
.. _fig_transformer:
.. figure:: ../img/transformer.svg
:width: 500px
The transformer architecture.
Bây giờ chúng tôi cung cấp một cái nhìn tổng quan về kiến trúc biến áp
trong :numref:`fig_transformer`. Ở mức độ cao, bộ mã hóa biến áp là
một chồng của nhiều lớp giống hệt nhau, trong đó mỗi lớp có hai lớp con
(hoặc được ký hiệu là :math:`\mathrm{sublayer}`). Đầu tiên là một tập
hợp tự chú ý nhiều đầu và thứ hai là một mạng lưới chuyển tiếp nguồn cấp
dữ liệu theo định vị. Cụ thể, trong bộ mã hóa tự chú ý, truy vấn, phím
và giá trị đều từ đầu ra của lớp mã hóa trước đó. Lấy cảm hứng từ thiết
kế ResNet trong :numref:`sec_resnet`, một kết nối còn lại được sử dụng
xung quanh cả hai lớp con. Trong máy biến áp, đối với bất kỳ đầu vào
:math:`\mathbf{x} \in \mathbb{R}^d` nào ở bất kỳ vị trí nào của chuỗi,
chúng tôi yêu cầu :math:`\mathrm{sublayer}(\mathbf{x}) \in \mathbb{R}^d`
để kết nối còn lại
:math:`\mathbf{x} + \mathrm{sublayer}(\mathbf{x}) \in \mathbb{R}^d` là
khả thi. Bổ sung này từ kết nối còn lại ngay lập tức được theo sau bởi
chuẩn hóa lớp :cite:`Ba.Kiros.Hinton.2016`. Kết quả là, bộ mã hóa biến
áp xuất ra một biểu diễn vector :math:`d` chiều cho mỗi vị trí của chuỗi
đầu vào.
Bộ giải mã biến áp cũng là một chồng của nhiều lớp giống hệt nhau với
các kết nối còn lại và chuẩn hóa lớp. Bên cạnh hai sublayers được mô tả
trong bộ mã hóa, bộ giải mã chèn một sublayer thứ ba, được gọi là sự chú
ý của bộ mã hóa-giải mã, giữa hai bộ giải mã này. Trong sự chú ý của bộ
mã hóa giải mã, các truy vấn là từ đầu ra của lớp giải mã trước đó và
các phím và giá trị là từ đầu ra bộ mã hóa biến áp. Trong bộ giải mã tự
chú ý, truy vấn, khóa và giá trị đều từ đầu ra của lớp giải mã trước đó.
Tuy nhiên, mỗi vị trí trong bộ giải mã chỉ được phép tham dự tất cả các
vị trí trong bộ giải mã cho đến vị trí đó. Chú ý *masked* này bảo tồn
thuộc tính tự động hồi quy, đảm bảo rằng dự đoán chỉ phụ thuộc vào các
token đầu ra đã được tạo ra.
Chúng tôi đã mô tả và thực hiện sự chú ý nhiều đầu dựa trên các sản phẩm
dot-thu nhỏ trong :numref:`sec_multihead-attention` và mã hóa vị trí
trong :numref:`subsec_positional-encoding`. Sau đây, chúng tôi sẽ thực
hiện phần còn lại của mô hình máy biến áp.
.. raw:: html
.. raw:: html
.. code:: python
import math
import pandas as pd
from mxnet import autograd, np, npx
from mxnet.gluon import nn
from d2l import mxnet as d2l
npx.set_np()
.. raw:: html
.. raw:: html
.. code:: python
import math
import pandas as pd
import torch
from torch import nn
from d2l import torch as d2l
.. raw:: html
.. raw:: html
.. code:: python
import numpy as np
import pandas as pd
import tensorflow as tf
from d2l import tensorflow as d2l
.. raw:: html
.. raw:: html
Các mạng thức ăn chuyển tiếp vị trí
-----------------------------------
Mạng chuyển tiếp nguồn cấp dữ liệu định vị biến đổi biểu diễn ở tất cả
các vị trí trình tự bằng cách sử dụng cùng một MLP. Đây là lý do tại sao
chúng tôi gọi nó là *positionwise*. Trong phần thực hiện dưới đây, đầu
vào ``X`` với hình dạng (kích thước lô, số bước thời gian hoặc độ dài
chuỗi trong thẻ, số đơn vị ẩn hoặc kích thước tính năng) sẽ được chuyển
đổi bởi một MLP hai lớp thành một tensor đầu ra của hình dạng (kích
thước lô, số bước thời gian, ``ffn_num_outputs``).
.. raw:: html
.. raw:: html
.. code:: python
#@save
class PositionWiseFFN(nn.Block):
"""Positionwise feed-forward network."""
def __init__(self, ffn_num_hiddens, ffn_num_outputs, **kwargs):
super(PositionWiseFFN, self).__init__(**kwargs)
self.dense1 = nn.Dense(ffn_num_hiddens, flatten=False,
activation='relu')
self.dense2 = nn.Dense(ffn_num_outputs, flatten=False)
def forward(self, X):
return self.dense2(self.dense1(X))
.. raw:: html
.. raw:: html
.. code:: python
#@save
class PositionWiseFFN(nn.Module):
"""Positionwise feed-forward network."""
def __init__(self, ffn_num_input, ffn_num_hiddens, ffn_num_outputs,
**kwargs):
super(PositionWiseFFN, self).__init__(**kwargs)
self.dense1 = nn.Linear(ffn_num_input, ffn_num_hiddens)
self.relu = nn.ReLU()
self.dense2 = nn.Linear(ffn_num_hiddens, ffn_num_outputs)
def forward(self, X):
return self.dense2(self.relu(self.dense1(X)))
.. raw:: html
.. raw:: html
.. code:: python
#@save
class PositionWiseFFN(tf.keras.layers.Layer):
"""Positionwise feed-forward network."""
def __init__(self, ffn_num_hiddens, ffn_num_outputs, **kwargs):
super().__init__(*kwargs)
self.dense1 = tf.keras.layers.Dense(ffn_num_hiddens)
self.relu = tf.keras.layers.ReLU()
self.dense2 = tf.keras.layers.Dense(ffn_num_outputs)
def call(self, X):
return self.dense2(self.relu(self.dense1(X)))
.. raw:: html
.. raw:: html
Ví dụ sau đây cho thấy chiều trong cùng của một tensor thay đổi thành số
lượng đầu ra trong mạng chuyển tiếp theo vị trí. Vì cùng một MLP biến
đổi ở tất cả các vị trí, khi các đầu vào ở tất cả các vị trí này giống
nhau, đầu ra của chúng cũng giống hệt nhau.
.. raw:: html
.. raw:: html
.. code:: python
ffn = PositionWiseFFN(4, 8)
ffn.initialize()
ffn(np.ones((2, 3, 4)))[0]
.. parsed-literal::
:class: output
array([[ 0.00239431, 0.00927085, -0.00021069, -0.00923989, -0.0082903 ,
-0.00162741, 0.00659031, 0.00023905],
[ 0.00239431, 0.00927085, -0.00021069, -0.00923989, -0.0082903 ,
-0.00162741, 0.00659031, 0.00023905],
[ 0.00239431, 0.00927085, -0.00021069, -0.00923989, -0.0082903 ,
-0.00162741, 0.00659031, 0.00023905]])
.. raw:: html
.. raw:: html
.. code:: python
ffn = PositionWiseFFN(4, 4, 8)
ffn.eval()
ffn(torch.ones((2, 3, 4)))[0]
.. parsed-literal::
:class: output
tensor([[ 0.3415, -0.6896, -0.0887, -0.0173, -0.1739, 0.2835, 0.6384, 0.3857],
[ 0.3415, -0.6896, -0.0887, -0.0173, -0.1739, 0.2835, 0.6384, 0.3857],
[ 0.3415, -0.6896, -0.0887, -0.0173, -0.1739, 0.2835, 0.6384, 0.3857]],
grad_fn=)
.. raw:: html
.. raw:: html
.. code:: python
ffn = PositionWiseFFN(4, 8)
ffn(tf.ones((2, 3, 4)))[0]
.. parsed-literal::
:class: output
.. raw:: html
.. raw:: html
Kết nối còn lại và chuẩn hóa lớp
--------------------------------
Bây giờ chúng ta hãy tập trung vào thành phần “add & norm” trong
:numref:`fig_transformer`. Như chúng tôi đã mô tả ở đầu phần này, đây
là một kết nối còn lại ngay lập tức theo sau là chuẩn hóa lớp. Cả hai
đều là chìa khóa cho các kiến trúc sâu hiệu quả.
Trong :numref:`sec_batch_norm`, chúng tôi đã giải thích cách bình
thường hóa hàng loạt lại và rescales trên các ví dụ trong một minibatch.
Chuẩn hóa lớp giống như bình thường hóa hàng loạt ngoại trừ việc trước
đây bình thường hóa trên kích thước tính năng. Mặc dù các ứng dụng phổ
biến của nó trong tầm nhìn máy tính, việc bình thường hóa hàng loạt
thường kém hiệu quả hơn so với bình thường hóa lớp trong các tác vụ xử
lý ngôn ngữ tự nhiên, có đầu vào thường là chuỗi độ dài thay đổi.
Đoạn mã sau đây so sánh bình thường hóa trên các kích thước khác nhau
theo chuẩn hóa lớp và chuẩn hóa hàng lạnh.
.. raw:: html
.. raw:: html
.. code:: python
ln = nn.LayerNorm()
ln.initialize()
bn = nn.BatchNorm()
bn.initialize()
X = np.array([[1, 2], [2, 3]])
# Compute mean and variance from `X` in the training mode
with autograd.record():
print('layer norm:', ln(X), '\nbatch norm:', bn(X))
.. parsed-literal::
:class: output
layer norm: [[-0.99998 0.99998]
[-0.99998 0.99998]]
batch norm: [[-0.99998 -0.99998]
[ 0.99998 0.99998]]
.. raw:: html
.. raw:: html
.. code:: python
ln = nn.LayerNorm(2)
bn = nn.BatchNorm1d(2)
X = torch.tensor([[1, 2], [2, 3]], dtype=torch.float32)
# Compute mean and variance from `X` in the training mode
print('layer norm:', ln(X), '\nbatch norm:', bn(X))
.. parsed-literal::
:class: output
layer norm: tensor([[-1.0000, 1.0000],
[-1.0000, 1.0000]], grad_fn=)
batch norm: tensor([[-1.0000, -1.0000],
[ 1.0000, 1.0000]], grad_fn=)
.. raw:: html
.. raw:: html
.. code:: python
ln = tf.keras.layers.LayerNormalization()
bn = tf.keras.layers.BatchNormalization()
X = tf.constant([[1, 2], [2, 3]], dtype=tf.float32)
print('layer norm:', ln(X), '\nbatch norm:', bn(X, training=True))
.. parsed-literal::
:class: output
layer norm: tf.Tensor(
[[-0.998006 0.9980061]
[-0.9980061 0.998006 ]], shape=(2, 2), dtype=float32)
batch norm: tf.Tensor(
[[-0.998006 -0.9980061 ]
[ 0.9980061 0.99800587]], shape=(2, 2), dtype=float32)
.. raw:: html
.. raw:: html
Bây giờ chúng ta có thể thực hiện lớp ``AddNorm`` sử dụng một kết nối
còn lại theo sau là chuẩn hóa lớp . Dropout cũng được áp dụng để thường
xuyên hóa.
.. raw:: html
.. raw:: html
.. code:: python
#@save
class AddNorm(nn.Block):
"""Residual connection followed by layer normalization."""
def __init__(self, dropout, **kwargs):
super(AddNorm, self).__init__(**kwargs)
self.dropout = nn.Dropout(dropout)
self.ln = nn.LayerNorm()
def forward(self, X, Y):
return self.ln(self.dropout(Y) + X)
.. raw:: html
.. raw:: html
.. code:: python
#@save
class AddNorm(nn.Module):
"""Residual connection followed by layer normalization."""
def __init__(self, normalized_shape, dropout, **kwargs):
super(AddNorm, self).__init__(**kwargs)
self.dropout = nn.Dropout(dropout)
self.ln = nn.LayerNorm(normalized_shape)
def forward(self, X, Y):
return self.ln(self.dropout(Y) + X)
.. raw:: html
.. raw:: html
.. code:: python
#@save
class AddNorm(tf.keras.layers.Layer):
"""Residual connection followed by layer normalization."""
def __init__(self, normalized_shape, dropout, **kwargs):
super().__init__(**kwargs)
self.dropout = tf.keras.layers.Dropout(dropout)
self.ln = tf.keras.layers.LayerNormalization(normalized_shape)
def call(self, X, Y, **kwargs):
return self.ln(self.dropout(Y, **kwargs) + X)
.. raw:: html
.. raw:: html
Kết nối còn lại yêu cầu hai đầu vào có cùng hình dạng sao cho tensor đầu
ra cũng có hình dạng giống nhau sau khi hoạt động bổ sung.
.. raw:: html
.. raw:: html
.. code:: python
add_norm = AddNorm(0.5)
add_norm.initialize()
add_norm(np.ones((2, 3, 4)), np.ones((2, 3, 4))).shape
.. parsed-literal::
:class: output
(2, 3, 4)
.. raw:: html
.. raw:: html
.. code:: python
add_norm = AddNorm([3, 4], 0.5) # Normalized_shape is input.size()[1:]
add_norm.eval()
add_norm(torch.ones((2, 3, 4)), torch.ones((2, 3, 4))).shape
.. parsed-literal::
:class: output
torch.Size([2, 3, 4])
.. raw:: html
.. raw:: html
.. code:: python
add_norm = AddNorm([1, 2], 0.5) # Normalized_shape is: [i for i in range(len(input.shape))][1:]
add_norm(tf.ones((2, 3, 4)), tf.ones((2, 3, 4)), training=False).shape
.. parsed-literal::
:class: output
TensorShape([2, 3, 4])
.. raw:: html
.. raw:: html
Bộ mã hóa
---------
Với tất cả các thành phần thiết yếu để lắp ráp bộ mã hóa biến áp, chúng
ta hãy bắt đầu bằng cách thực hiện một lớp duy nhất trong bộ mã hóa. Lớp
``EncoderBlock`` sau chứa hai lớp con: nhiều đầu tự chú ý và các mạng
chuyển tiếp theo định vị, trong đó một kết nối còn lại tiếp theo là
chuẩn hóa lớp được sử dụng xung quanh cả hai lớp con.
.. raw:: html
.. raw:: html
.. code:: python
#@save
class EncoderBlock(nn.Block):
"""Transformer encoder block."""
def __init__(self, num_hiddens, ffn_num_hiddens, num_heads, dropout,
use_bias=False, **kwargs):
super(EncoderBlock, self).__init__(**kwargs)
self.attention = d2l.MultiHeadAttention(
num_hiddens, num_heads, dropout, use_bias)
self.addnorm1 = AddNorm(dropout)
self.ffn = PositionWiseFFN(ffn_num_hiddens, num_hiddens)
self.addnorm2 = AddNorm(dropout)
def forward(self, X, valid_lens):
Y = self.addnorm1(X, self.attention(X, X, X, valid_lens))
return self.addnorm2(Y, self.ffn(Y))
.. raw:: html
.. raw:: html
.. code:: python
#@save
class EncoderBlock(nn.Module):
"""Transformer encoder block."""
def __init__(self, key_size, query_size, value_size, num_hiddens,
norm_shape, ffn_num_input, ffn_num_hiddens, num_heads,
dropout, use_bias=False, **kwargs):
super(EncoderBlock, self).__init__(**kwargs)
self.attention = d2l.MultiHeadAttention(
key_size, query_size, value_size, num_hiddens, num_heads, dropout,
use_bias)
self.addnorm1 = AddNorm(norm_shape, dropout)
self.ffn = PositionWiseFFN(
ffn_num_input, ffn_num_hiddens, num_hiddens)
self.addnorm2 = AddNorm(norm_shape, dropout)
def forward(self, X, valid_lens):
Y = self.addnorm1(X, self.attention(X, X, X, valid_lens))
return self.addnorm2(Y, self.ffn(Y))
.. raw:: html
.. raw:: html
.. code:: python
#@save
class EncoderBlock(tf.keras.layers.Layer):
"""Transformer encoder block."""
def __init__(self, key_size, query_size, value_size, num_hiddens,
norm_shape, ffn_num_hiddens, num_heads, dropout, bias=False, **kwargs):
super().__init__(**kwargs)
self.attention = d2l.MultiHeadAttention(key_size, query_size, value_size, num_hiddens,
num_heads, dropout, bias)
self.addnorm1 = AddNorm(norm_shape, dropout)
self.ffn = PositionWiseFFN(ffn_num_hiddens, num_hiddens)
self.addnorm2 = AddNorm(norm_shape, dropout)
def call(self, X, valid_lens, **kwargs):
Y = self.addnorm1(X, self.attention(X, X, X, valid_lens, **kwargs), **kwargs)
return self.addnorm2(Y, self.ffn(Y), **kwargs)
.. raw:: html
.. raw:: html
Như chúng ta có thể thấy, bất kỳ lớp nào trong bộ mã hóa biến áp không
thay đổi hình dạng của đầu vào của nó.
.. raw:: html
.. raw:: html
.. code:: python
X = np.ones((2, 100, 24))
valid_lens = np.array([3, 2])
encoder_blk = EncoderBlock(24, 48, 8, 0.5)
encoder_blk.initialize()
encoder_blk(X, valid_lens).shape
.. parsed-literal::
:class: output
(2, 100, 24)
.. raw:: html
.. raw:: html
.. code:: python
X = torch.ones((2, 100, 24))
valid_lens = torch.tensor([3, 2])
encoder_blk = EncoderBlock(24, 24, 24, 24, [100, 24], 24, 48, 8, 0.5)
encoder_blk.eval()
encoder_blk(X, valid_lens).shape
.. parsed-literal::
:class: output
torch.Size([2, 100, 24])
.. raw:: html
.. raw:: html
.. code:: python
X = tf.ones((2, 100, 24))
valid_lens = tf.constant([3, 2])
norm_shape = [i for i in range(len(X.shape))][1:]
encoder_blk = EncoderBlock(24, 24, 24, 24, norm_shape, 48, 8, 0.5)
encoder_blk(X, valid_lens, training=False).shape
.. parsed-literal::
:class: output
TensorShape([2, 100, 24])
.. raw:: html
.. raw:: html
Trong thực hiện transformer encoder sau đây, chúng tôi xếp chồng
``num_layers`` phiên bản của ``EncoderBlock`` lớp trên. Vì chúng ta sử
dụng mã hóa vị trí cố định có giá trị luôn nằm trong khoảng từ -1 đến 1,
chúng ta nhân các giá trị của các nhúng đầu vào có thể học được bằng căn
bậc hai của kích thước nhúng để giải phóng trước khi tổng hợp nhúng đầu
vào và mã hóa vị trí.
.. raw:: html
.. raw:: html
.. code:: python
#@save
class TransformerEncoder(d2l.Encoder):
"""Transformer encoder."""
def __init__(self, vocab_size, num_hiddens, ffn_num_hiddens,
num_heads, num_layers, dropout, use_bias=False, **kwargs):
super(TransformerEncoder, self).__init__(**kwargs)
self.num_hiddens = num_hiddens
self.embedding = nn.Embedding(vocab_size, num_hiddens)
self.pos_encoding = d2l.PositionalEncoding(num_hiddens, dropout)
self.blks = nn.Sequential()
for _ in range(num_layers):
self.blks.add(
EncoderBlock(num_hiddens, ffn_num_hiddens, num_heads, dropout,
use_bias))
def forward(self, X, valid_lens, *args):
# Since positional encoding values are between -1 and 1, the embedding
# values are multiplied by the square root of the embedding dimension
# to rescale before they are summed up
X = self.pos_encoding(self.embedding(X) * math.sqrt(self.num_hiddens))
self.attention_weights = [None] * len(self.blks)
for i, blk in enumerate(self.blks):
X = blk(X, valid_lens)
self.attention_weights[
i] = blk.attention.attention.attention_weights
return X
.. raw:: html
.. raw:: html
.. code:: python
#@save
class TransformerEncoder(d2l.Encoder):
"""Transformer encoder."""
def __init__(self, vocab_size, key_size, query_size, value_size,
num_hiddens, norm_shape, ffn_num_input, ffn_num_hiddens,
num_heads, num_layers, dropout, use_bias=False, **kwargs):
super(TransformerEncoder, self).__init__(**kwargs)
self.num_hiddens = num_hiddens
self.embedding = nn.Embedding(vocab_size, num_hiddens)
self.pos_encoding = d2l.PositionalEncoding(num_hiddens, dropout)
self.blks = nn.Sequential()
for i in range(num_layers):
self.blks.add_module("block"+str(i),
EncoderBlock(key_size, query_size, value_size, num_hiddens,
norm_shape, ffn_num_input, ffn_num_hiddens,
num_heads, dropout, use_bias))
def forward(self, X, valid_lens, *args):
# Since positional encoding values are between -1 and 1, the embedding
# values are multiplied by the square root of the embedding dimension
# to rescale before they are summed up
X = self.pos_encoding(self.embedding(X) * math.sqrt(self.num_hiddens))
self.attention_weights = [None] * len(self.blks)
for i, blk in enumerate(self.blks):
X = blk(X, valid_lens)
self.attention_weights[
i] = blk.attention.attention.attention_weights
return X
.. raw:: html
.. raw:: html
.. code:: python
#@save
class TransformerEncoder(d2l.Encoder):
"""Transformer encoder."""
def __init__(self, vocab_size, key_size, query_size, value_size,
num_hiddens, norm_shape, ffn_num_hiddens, num_heads,
num_layers, dropout, bias=False, **kwargs):
super().__init__(**kwargs)
self.num_hiddens = num_hiddens
self.embedding = tf.keras.layers.Embedding(vocab_size, num_hiddens)
self.pos_encoding = d2l.PositionalEncoding(num_hiddens, dropout)
self.blks = [EncoderBlock(
key_size, query_size, value_size, num_hiddens, norm_shape,
ffn_num_hiddens, num_heads, dropout, bias) for _ in range(
num_layers)]
def call(self, X, valid_lens, **kwargs):
# Since positional encoding values are between -1 and 1, the embedding
# values are multiplied by the square root of the embedding dimension
# to rescale before they are summed up
X = self.pos_encoding(self.embedding(X) * tf.math.sqrt(
tf.cast(self.num_hiddens, dtype=tf.float32)), **kwargs)
self.attention_weights = [None] * len(self.blks)
for i, blk in enumerate(self.blks):
X = blk(X, valid_lens, **kwargs)
self.attention_weights[
i] = blk.attention.attention.attention_weights
return X
.. raw:: html
.. raw:: html
Dưới đây chúng tôi chỉ định các siêu tham số để tạo một bộ mã hóa biến
áp hai lớp. Hình dạng của đầu ra bộ mã hóa biến áp là (kích thước lô, số
bước thời gian, ``num_hiddens``).
.. raw:: html
.. raw:: html
.. code:: python
encoder = TransformerEncoder(200, 24, 48, 8, 2, 0.5)
encoder.initialize()
encoder(np.ones((2, 100)), valid_lens).shape
.. parsed-literal::
:class: output
(2, 100, 24)
.. raw:: html
.. raw:: html
.. code:: python
encoder = TransformerEncoder(
200, 24, 24, 24, 24, [100, 24], 24, 48, 8, 2, 0.5)
encoder.eval()
encoder(torch.ones((2, 100), dtype=torch.long), valid_lens).shape
.. parsed-literal::
:class: output
torch.Size([2, 100, 24])
.. raw:: html
.. raw:: html
.. code:: python
encoder = TransformerEncoder(200, 24, 24, 24, 24, [1, 2], 48, 8, 2, 0.5)
encoder(tf.ones((2, 100)), valid_lens, training=False).shape
.. parsed-literal::
:class: output
TensorShape([2, 100, 24])
.. raw:: html
.. raw:: html
Bộ giải mã
----------
Như thể hiện trong :numref:`fig_transformer`, bộ giải mã biến áp bao
gồm nhiều lớp giống hệt nhau. Mỗi lớp được triển khai trong lớp
``DecoderBlock`` sau, chứa ba lớp con: bộ giải mã tự chú ý, chú ý bộ mã
hóa-giải mã, và các mạng chuyển tiếp nguồn cấp dữ liệu định vị. Các lớp
con này sử dụng một kết nối còn lại xung quanh chúng theo sau là chuẩn
hóa lớp.
Như chúng ta đã mô tả trước đó trong phần này, trong bộ giải mã đa đầu
được đeo mặt nạ tự chú ý (con đầu tiên), truy vấn, khóa và giá trị đều
đến từ đầu ra của lớp giải mã trước đó. Khi đào tạo các mô hình trình tự
theo trình tự, mã thông báo ở tất cả các vị trí (bước thời gian) của
chuỗi đầu ra được biết đến. Tuy nhiên, trong quá trình dự đoán chuỗi đầu
ra được tạo ra token bằng mã thông báo; do đó, tại bất kỳ bước thời gian
giải mã nào chỉ có các token được tạo ra có thể được sử dụng trong bộ
giải mã tự chú ý. Để duy trì hồi quy tự động trong bộ giải mã, sự tự chú
ý đeo mặt nạ của nó chỉ định ``dec_valid_lens`` để bất kỳ truy vấn nào
chỉ tham quan đến tất cả các vị trí trong bộ giải mã cho đến vị trí truy
vấn.
.. raw:: html
.. raw:: html
.. code:: python
class DecoderBlock(nn.Block):
# The `i`-th block in the decoder
def __init__(self, num_hiddens, ffn_num_hiddens, num_heads,
dropout, i, **kwargs):
super(DecoderBlock, self).__init__(**kwargs)
self.i = i
self.attention1 = d2l.MultiHeadAttention(num_hiddens, num_heads,
dropout)
self.addnorm1 = AddNorm(dropout)
self.attention2 = d2l.MultiHeadAttention(num_hiddens, num_heads,
dropout)
self.addnorm2 = AddNorm(dropout)
self.ffn = PositionWiseFFN(ffn_num_hiddens, num_hiddens)
self.addnorm3 = AddNorm(dropout)
def forward(self, X, state):
enc_outputs, enc_valid_lens = state[0], state[1]
# During training, all the tokens of any output sequence are processed
# at the same time, so `state[2][self.i]` is `None` as initialized.
# When decoding any output sequence token by token during prediction,
# `state[2][self.i]` contains representations of the decoded output at
# the `i`-th block up to the current time step
if state[2][self.i] is None:
key_values = X
else:
key_values = np.concatenate((state[2][self.i], X), axis=1)
state[2][self.i] = key_values
if autograd.is_training():
batch_size, num_steps, _ = X.shape
# Shape of `dec_valid_lens`: (`batch_size`, `num_steps`), where
# every row is [1, 2, ..., `num_steps`]
dec_valid_lens = np.tile(np.arange(1, num_steps + 1, ctx=X.ctx),
(batch_size, 1))
else:
dec_valid_lens = None
# Self-attention
X2 = self.attention1(X, key_values, key_values, dec_valid_lens)
Y = self.addnorm1(X, X2)
# Encoder-decoder attention. Shape of `enc_outputs`:
# (`batch_size`, `num_steps`, `num_hiddens`)
Y2 = self.attention2(Y, enc_outputs, enc_outputs, enc_valid_lens)
Z = self.addnorm2(Y, Y2)
return self.addnorm3(Z, self.ffn(Z)), state
.. raw:: html
.. raw:: html
.. code:: python
class DecoderBlock(nn.Module):
# The `i`-th block in the decoder
def __init__(self, key_size, query_size, value_size, num_hiddens,
norm_shape, ffn_num_input, ffn_num_hiddens, num_heads,
dropout, i, **kwargs):
super(DecoderBlock, self).__init__(**kwargs)
self.i = i
self.attention1 = d2l.MultiHeadAttention(
key_size, query_size, value_size, num_hiddens, num_heads, dropout)
self.addnorm1 = AddNorm(norm_shape, dropout)
self.attention2 = d2l.MultiHeadAttention(
key_size, query_size, value_size, num_hiddens, num_heads, dropout)
self.addnorm2 = AddNorm(norm_shape, dropout)
self.ffn = PositionWiseFFN(ffn_num_input, ffn_num_hiddens,
num_hiddens)
self.addnorm3 = AddNorm(norm_shape, dropout)
def forward(self, X, state):
enc_outputs, enc_valid_lens = state[0], state[1]
# During training, all the tokens of any output sequence are processed
# at the same time, so `state[2][self.i]` is `None` as initialized.
# When decoding any output sequence token by token during prediction,
# `state[2][self.i]` contains representations of the decoded output at
# the `i`-th block up to the current time step
if state[2][self.i] is None:
key_values = X
else:
key_values = torch.cat((state[2][self.i], X), axis=1)
state[2][self.i] = key_values
if self.training:
batch_size, num_steps, _ = X.shape
# Shape of `dec_valid_lens`: (`batch_size`, `num_steps`), where
# every row is [1, 2, ..., `num_steps`]
dec_valid_lens = torch.arange(
1, num_steps + 1, device=X.device).repeat(batch_size, 1)
else:
dec_valid_lens = None
# Self-attention
X2 = self.attention1(X, key_values, key_values, dec_valid_lens)
Y = self.addnorm1(X, X2)
# Encoder-decoder attention. Shape of `enc_outputs`:
# (`batch_size`, `num_steps`, `num_hiddens`)
Y2 = self.attention2(Y, enc_outputs, enc_outputs, enc_valid_lens)
Z = self.addnorm2(Y, Y2)
return self.addnorm3(Z, self.ffn(Z)), state
.. raw:: html
.. raw:: html
.. code:: python
class DecoderBlock(tf.keras.layers.Layer):
# The `i`-th block in the decoder
def __init__(self, key_size, query_size, value_size, num_hiddens,
norm_shape, ffn_num_hiddens, num_heads, dropout, i, **kwargs):
super().__init__(**kwargs)
self.i = i
self.attention1 = d2l.MultiHeadAttention(key_size, query_size, value_size, num_hiddens, num_heads, dropout)
self.addnorm1 = AddNorm(norm_shape, dropout)
self.attention2 = d2l.MultiHeadAttention(key_size, query_size, value_size, num_hiddens, num_heads, dropout)
self.addnorm2 = AddNorm(norm_shape, dropout)
self.ffn = PositionWiseFFN(ffn_num_hiddens, num_hiddens)
self.addnorm3 = AddNorm(norm_shape, dropout)
def call(self, X, state, **kwargs):
enc_outputs, enc_valid_lens = state[0], state[1]
# During training, all the tokens of any output sequence are processed
# at the same time, so `state[2][self.i]` is `None` as initialized.
# When decoding any output sequence token by token during prediction,
# `state[2][self.i]` contains representations of the decoded output at
# the `i`-th block up to the current time step
if state[2][self.i] is None:
key_values = X
else:
key_values = tf.concat((state[2][self.i], X), axis=1)
state[2][self.i] = key_values
if kwargs["training"]:
batch_size, num_steps, _ = X.shape
# Shape of `dec_valid_lens`: (`batch_size`, `num_steps`), where
# every row is [1, 2, ..., `num_steps`]
dec_valid_lens = tf.repeat(tf.reshape(tf.range(1, num_steps + 1),
shape=(-1, num_steps)), repeats=batch_size, axis=0)
else:
dec_valid_lens = None
# Self-attention
X2 = self.attention1(X, key_values, key_values, dec_valid_lens, **kwargs)
Y = self.addnorm1(X, X2, **kwargs)
# Encoder-decoder attention. Shape of `enc_outputs`: (`batch_size`, `num_steps`, `num_hiddens`)
Y2 = self.attention2(Y, enc_outputs, enc_outputs, enc_valid_lens, **kwargs)
Z = self.addnorm2(Y, Y2, **kwargs)
return self.addnorm3(Z, self.ffn(Z), **kwargs), state
.. raw:: html
.. raw:: html
Để tạo điều kiện cho các hoạt động sản phẩm điểm thu nhỏ trong bộ giải
mã mã hóa chú ý và các hoạt động bổ sung trong các kết nối còn lại, kích
thước tính năng (``num_hiddens``) của bộ giải mã giống như của bộ mã
hóa.
.. raw:: html
.. raw:: html
.. code:: python
decoder_blk = DecoderBlock(24, 48, 8, 0.5, 0)
decoder_blk.initialize()
X = np.ones((2, 100, 24))
state = [encoder_blk(X, valid_lens), valid_lens, [None]]
decoder_blk(X, state)[0].shape
.. parsed-literal::
:class: output
(2, 100, 24)
.. raw:: html
.. raw:: html
.. code:: python
decoder_blk = DecoderBlock(24, 24, 24, 24, [100, 24], 24, 48, 8, 0.5, 0)
decoder_blk.eval()
X = torch.ones((2, 100, 24))
state = [encoder_blk(X, valid_lens), valid_lens, [None]]
decoder_blk(X, state)[0].shape
.. parsed-literal::
:class: output
torch.Size([2, 100, 24])
.. raw:: html
.. raw:: html
.. code:: python
decoder_blk = DecoderBlock(24, 24, 24, 24, [1, 2], 48, 8, 0.5, 0)
X = tf.ones((2, 100, 24))
state = [encoder_blk(X, valid_lens), valid_lens, [None]]
decoder_blk(X, state, training=False)[0].shape
.. parsed-literal::
:class: output
TensorShape([2, 100, 24])
.. raw:: html
.. raw:: html
Bây giờ chúng ta xây dựng toàn bộ bộ giải mã transformer bao gồm
``num_layers`` trường hợp của ``DecoderBlock``. Cuối cùng, một lớp kết
nối hoàn toàn tính toán dự đoán cho tất cả các token đầu ra có thể có
``vocab_size``. Cả hai trọng lượng tự chú ý của bộ giải mã và trọng
lượng chú ý bộ giải mã hóa được lưu trữ để trực quan hóa sau này.
.. raw:: html
.. raw:: html
.. code:: python
class TransformerDecoder(d2l.AttentionDecoder):
def __init__(self, vocab_size, num_hiddens, ffn_num_hiddens,
num_heads, num_layers, dropout, **kwargs):
super(TransformerDecoder, self).__init__(**kwargs)
self.num_hiddens = num_hiddens
self.num_layers = num_layers
self.embedding = nn.Embedding(vocab_size, num_hiddens)
self.pos_encoding = d2l.PositionalEncoding(num_hiddens, dropout)
self.blks = nn.Sequential()
for i in range(num_layers):
self.blks.add(
DecoderBlock(num_hiddens, ffn_num_hiddens, num_heads,
dropout, i))
self.dense = nn.Dense(vocab_size, flatten=False)
def init_state(self, enc_outputs, enc_valid_lens, *args):
return [enc_outputs, enc_valid_lens, [None] * self.num_layers]
def forward(self, X, state):
X = self.pos_encoding(self.embedding(X) * math.sqrt(self.num_hiddens))
self._attention_weights = [[None] * len(self.blks) for _ in range (2)]
for i, blk in enumerate(self.blks):
X, state = blk(X, state)
# Decoder self-attention weights
self._attention_weights[0][
i] = blk.attention1.attention.attention_weights
# Encoder-decoder attention weights
self._attention_weights[1][
i] = blk.attention2.attention.attention_weights
return self.dense(X), state
@property
def attention_weights(self):
return self._attention_weights
.. raw:: html
.. raw:: html
.. code:: python
class TransformerDecoder(d2l.AttentionDecoder):
def __init__(self, vocab_size, key_size, query_size, value_size,
num_hiddens, norm_shape, ffn_num_input, ffn_num_hiddens,
num_heads, num_layers, dropout, **kwargs):
super(TransformerDecoder, self).__init__(**kwargs)
self.num_hiddens = num_hiddens
self.num_layers = num_layers
self.embedding = nn.Embedding(vocab_size, num_hiddens)
self.pos_encoding = d2l.PositionalEncoding(num_hiddens, dropout)
self.blks = nn.Sequential()
for i in range(num_layers):
self.blks.add_module("block"+str(i),
DecoderBlock(key_size, query_size, value_size, num_hiddens,
norm_shape, ffn_num_input, ffn_num_hiddens,
num_heads, dropout, i))
self.dense = nn.Linear(num_hiddens, vocab_size)
def init_state(self, enc_outputs, enc_valid_lens, *args):
return [enc_outputs, enc_valid_lens, [None] * self.num_layers]
def forward(self, X, state):
X = self.pos_encoding(self.embedding(X) * math.sqrt(self.num_hiddens))
self._attention_weights = [[None] * len(self.blks) for _ in range (2)]
for i, blk in enumerate(self.blks):
X, state = blk(X, state)
# Decoder self-attention weights
self._attention_weights[0][
i] = blk.attention1.attention.attention_weights
# Encoder-decoder attention weights
self._attention_weights[1][
i] = blk.attention2.attention.attention_weights
return self.dense(X), state
@property
def attention_weights(self):
return self._attention_weights
.. raw:: html
.. raw:: html
.. code:: python
class TransformerDecoder(d2l.AttentionDecoder):
def __init__(self, vocab_size, key_size, query_size, value_size,
num_hiddens, norm_shape, ffn_num_hidens, num_heads, num_layers, dropout, **kwargs):
super().__init__(**kwargs)
self.num_hiddens = num_hiddens
self.num_layers = num_layers
self.embedding = tf.keras.layers.Embedding(vocab_size, num_hiddens)
self.pos_encoding = d2l.PositionalEncoding(num_hiddens, dropout)
self.blks = [DecoderBlock(key_size, query_size, value_size, num_hiddens, norm_shape,
ffn_num_hiddens, num_heads, dropout, i) for i in range(num_layers)]
self.dense = tf.keras.layers.Dense(vocab_size)
def init_state(self, enc_outputs, enc_valid_lens, *args):
return [enc_outputs, enc_valid_lens, [None] * self.num_layers]
def call(self, X, state, **kwargs):
X = self.pos_encoding(self.embedding(X) * tf.math.sqrt(tf.cast(self.num_hiddens, dtype=tf.float32)), **kwargs)
self._attention_weights = [[None] * len(self.blks) for _ in range(2)] # 2 Attention layers in decoder
for i, blk in enumerate(self.blks):
X, state = blk(X, state, **kwargs)
# Decoder self-attention weights
self._attention_weights[0][i] = blk.attention1.attention.attention_weights
# Encoder-decoder attention weights
self._attention_weights[1][i] = blk.attention2.attention.attention_weights
return self.dense(X), state
@property
def attention_weights(self):
return self._attention_weights
.. raw:: html
.. raw:: html
Đào tạo
-------
Hãy để chúng tôi khởi tạo một mô hình bộ mã hóa-giải mã bằng cách làm
theo kiến trúc biến áp. Ở đây chúng tôi chỉ định rằng cả bộ mã hóa biến
áp và bộ giải mã biến áp có 2 lớp sử dụng sự chú ý 4 đầu. Tương tự như
:numref:`sec_seq2seq_training`, chúng tôi đào tạo mô hình biến áp cho
trình tự để học trình tự trên tập dữ liệu dịch máy tiếng Anh-Pháp.
.. raw:: html
.. raw:: html
.. code:: python
num_hiddens, num_layers, dropout, batch_size, num_steps = 32, 2, 0.1, 64, 10
lr, num_epochs, device = 0.005, 200, d2l.try_gpu()
ffn_num_hiddens, num_heads = 64, 4
train_iter, src_vocab, tgt_vocab = d2l.load_data_nmt(batch_size, num_steps)
encoder = TransformerEncoder(
len(src_vocab), num_hiddens, ffn_num_hiddens, num_heads, num_layers,
dropout)
decoder = TransformerDecoder(
len(tgt_vocab), num_hiddens, ffn_num_hiddens, num_heads, num_layers,
dropout)
net = d2l.EncoderDecoder(encoder, decoder)
d2l.train_seq2seq(net, train_iter, lr, num_epochs, tgt_vocab, device)
.. parsed-literal::
:class: output
loss 0.030, 2322.4 tokens/sec on gpu(0)
.. figure:: output_transformer_5722f1_159_1.svg
.. raw:: html
.. raw:: html
.. code:: python
num_hiddens, num_layers, dropout, batch_size, num_steps = 32, 2, 0.1, 64, 10
lr, num_epochs, device = 0.005, 200, d2l.try_gpu()
ffn_num_input, ffn_num_hiddens, num_heads = 32, 64, 4
key_size, query_size, value_size = 32, 32, 32
norm_shape = [32]
train_iter, src_vocab, tgt_vocab = d2l.load_data_nmt(batch_size, num_steps)
encoder = TransformerEncoder(
len(src_vocab), key_size, query_size, value_size, num_hiddens,
norm_shape, ffn_num_input, ffn_num_hiddens, num_heads,
num_layers, dropout)
decoder = TransformerDecoder(
len(tgt_vocab), key_size, query_size, value_size, num_hiddens,
norm_shape, ffn_num_input, ffn_num_hiddens, num_heads,
num_layers, dropout)
net = d2l.EncoderDecoder(encoder, decoder)
d2l.train_seq2seq(net, train_iter, lr, num_epochs, tgt_vocab, device)
.. parsed-literal::
:class: output
loss 0.032, 5927.0 tokens/sec on cuda:0
.. figure:: output_transformer_5722f1_162_1.svg
.. raw:: html
.. raw:: html
.. code:: python
num_hiddens, num_layers, dropout, batch_size, num_steps = 32, 2, 0.1, 64, 10
lr, num_epochs, device = 0.005, 200, d2l.try_gpu()
ffn_num_hiddens, num_heads = 64, 4
key_size, query_size, value_size = 32, 32, 32
norm_shape = [2]
train_iter, src_vocab, tgt_vocab = d2l.load_data_nmt(batch_size, num_steps)
encoder = TransformerEncoder(
len(src_vocab), key_size, query_size, value_size, num_hiddens, norm_shape,
ffn_num_hiddens, num_heads, num_layers, dropout)
decoder = TransformerDecoder(
len(tgt_vocab), key_size, query_size, value_size, num_hiddens, norm_shape,
ffn_num_hiddens, num_heads, num_layers, dropout)
net = d2l.EncoderDecoder(encoder, decoder)
d2l.train_seq2seq(net, train_iter, lr, num_epochs, tgt_vocab, device)
.. parsed-literal::
:class: output
loss 0.026, 1018.2 tokens/sec on
.. figure:: output_transformer_5722f1_165_1.svg
.. raw:: html
.. raw:: html
Sau khi đào tạo, chúng tôi sử dụng mô hình biến áp để dịch một vài câu
tiếng Anh sang tiếng Pháp và tính điểm BLEU của họ.
.. raw:: html
.. raw:: html
.. code:: python
engs = ['go .', "i lost .", 'he\'s calm .', 'i\'m home .']
fras = ['va !', 'j\'ai perdu .', 'il est calme .', 'je suis chez moi .']
for eng, fra in zip(engs, fras):
translation, dec_attention_weight_seq = d2l.predict_seq2seq(
net, eng, src_vocab, tgt_vocab, num_steps, device, True)
print(f'{eng} => {translation}, ',
f'bleu {d2l.bleu(translation, fra, k=2):.3f}')
.. parsed-literal::
:class: output
go . => va !, bleu 1.000
i lost . => j'ai perdu ., bleu 1.000
he's calm . => il est calme ., bleu 1.000
i'm home . => je suis chez moi ., bleu 1.000
.. raw:: html
.. raw:: html
.. code:: python
engs = ['go .', "i lost .", 'he\'s calm .', 'i\'m home .']
fras = ['va !', 'j\'ai perdu .', 'il est calme .', 'je suis chez moi .']
for eng, fra in zip(engs, fras):
translation, dec_attention_weight_seq = d2l.predict_seq2seq(
net, eng, src_vocab, tgt_vocab, num_steps, device, True)
print(f'{eng} => {translation}, ',
f'bleu {d2l.bleu(translation, fra, k=2):.3f}')
.. parsed-literal::
:class: output
go . => va !, bleu 1.000
i lost . => j'ai perdu ., bleu 1.000
he's calm . => il est ., bleu 0.658
i'm home . => je suis chez moi ., bleu 1.000
.. raw:: html
.. raw:: html
.. code:: python
engs = ['go .', "i lost .", 'he\'s calm .', 'i\'m home .']
fras = ['va !', 'j\'ai perdu .', 'il est calme .', 'je suis chez moi .']
for eng, fra in zip(engs, fras):
translation, dec_attention_weight_seq = d2l.predict_seq2seq(
net, eng, src_vocab, tgt_vocab, num_steps, True)
print(f'{eng} => {translation}, ',
f'bleu {d2l.bleu(translation, fra, k=2):.3f}')
.. parsed-literal::
:class: output
go . => va !, bleu 1.000
i lost . => j'ai perdu ., bleu 1.000
he's calm . => il est mouillé ., bleu 0.658
i'm home . => je suis chez moi ., bleu 1.000
.. raw:: html
.. raw:: html
Hãy để chúng tôi hình dung trọng lượng chú ý của máy biến áp khi dịch
câu tiếng Anh cuối cùng sang tiếng Pháp. Hình dạng của các quả cân tự
chú ý của bộ mã hóa là (số lớp mã hóa, số lượng đầu chú ý, ``num_steps``
hoặc số truy vấn, ``num_steps`` hoặc số cặp giá trị khóa).
.. raw:: html
.. raw:: html
.. code:: python
enc_attention_weights = np.concatenate(net.encoder.attention_weights, 0).reshape((num_layers,
num_heads, -1, num_steps))
enc_attention_weights.shape
.. parsed-literal::
:class: output
(2, 4, 10, 10)
.. raw:: html
.. raw:: html
.. code:: python
enc_attention_weights = torch.cat(net.encoder.attention_weights, 0).reshape((num_layers, num_heads,
-1, num_steps))
enc_attention_weights.shape
.. parsed-literal::
:class: output
torch.Size([2, 4, 10, 10])
.. raw:: html
.. raw:: html
.. code:: python
enc_attention_weights = tf.reshape(
tf.concat(net.encoder.attention_weights, 0),
(num_layers, num_heads, -1, num_steps))
enc_attention_weights.shape
.. parsed-literal::
:class: output
TensorShape([2, 4, 10, 10])
.. raw:: html
.. raw:: html
Trong bộ mã hóa tự chú ý, cả truy vấn và phím đều đến từ cùng một chuỗi
đầu vào. Vì thẻ đệm không mang ý nghĩa, với độ dài hợp lệ được chỉ định
của chuỗi đầu vào, không có truy vấn nào tham dự các vị trí của thẻ đệm.
Sau đây, hai lớp trọng lượng chú ý nhiều đầu được trình bày từng hàng.
Mỗi đầu độc lập tham dự dựa trên một không gian con đại diện riêng biệt
của các truy vấn, khóa và giá trị.
.. raw:: html
.. raw:: html
.. code:: python
d2l.show_heatmaps(
enc_attention_weights, xlabel='Key positions', ylabel='Query positions',
titles=['Head %d' % i for i in range(1, 5)], figsize=(7, 3.5))
.. figure:: output_transformer_5722f1_195_0.svg
.. raw:: html
.. raw:: html
.. code:: python
d2l.show_heatmaps(
enc_attention_weights.cpu(), xlabel='Key positions',
ylabel='Query positions', titles=['Head %d' % i for i in range(1, 5)],
figsize=(7, 3.5))
.. figure:: output_transformer_5722f1_198_0.svg
.. raw:: html
.. raw:: html
.. code:: python
d2l.show_heatmaps(
enc_attention_weights, xlabel='Key positions', ylabel='Query positions',
titles=['Head %d' % i for i in range(1, 5)], figsize=(7, 3.5))
.. figure:: output_transformer_5722f1_201_0.svg
.. raw:: html
.. raw:: html
Để hình dung cả trọng lượng tự chú ý của bộ giải mã và trọng lượng chú ý
bộ giải mã hóa, chúng ta cần nhiều thao tác dữ liệu Ví dụ, chúng tôi lấp
đầy trọng lượng chú ý đeo mặt nạ bằng 0. Lưu ý rằng trọng lượng tự chú ý
của bộ giải mã và trọng lượng chú ý bộ giải mã hóa cả hai đều có cùng
một truy vấn: mã thông báo bắt đầu theo sau là mã thông báo đầu ra.
.. raw:: html
.. raw:: html
.. code:: python
dec_attention_weights_2d = [np.array(head[0]).tolist()
for step in dec_attention_weight_seq
for attn in step for blk in attn for head in blk]
dec_attention_weights_filled = np.array(
pd.DataFrame(dec_attention_weights_2d).fillna(0.0).values)
dec_attention_weights = dec_attention_weights_filled.reshape((-1, 2, num_layers, num_heads, num_steps))
dec_self_attention_weights, dec_inter_attention_weights = \
dec_attention_weights.transpose(1, 2, 3, 0, 4)
dec_self_attention_weights.shape, dec_inter_attention_weights.shape
.. parsed-literal::
:class: output
((2, 4, 6, 10), (2, 4, 6, 10))
.. raw:: html
.. raw:: html
.. code:: python
dec_attention_weights_2d = [head[0].tolist()
for step in dec_attention_weight_seq
for attn in step for blk in attn for head in blk]
dec_attention_weights_filled = torch.tensor(
pd.DataFrame(dec_attention_weights_2d).fillna(0.0).values)
dec_attention_weights = dec_attention_weights_filled.reshape((-1, 2, num_layers, num_heads, num_steps))
dec_self_attention_weights, dec_inter_attention_weights = \
dec_attention_weights.permute(1, 2, 3, 0, 4)
dec_self_attention_weights.shape, dec_inter_attention_weights.shape
.. parsed-literal::
:class: output
(torch.Size([2, 4, 6, 10]), torch.Size([2, 4, 6, 10]))
.. raw:: html
.. raw:: html
.. code:: python
dec_attention_weights_2d = [head[0] for step in dec_attention_weight_seq
for attn in step
for blk in attn for head in blk]
dec_attention_weights_filled = tf.convert_to_tensor(
np.asarray(pd.DataFrame(dec_attention_weights_2d).fillna(
0.0).values).astype(np.float32))
dec_attention_weights = tf.reshape(dec_attention_weights_filled, shape=(
-1, 2, num_layers, num_heads, num_steps))
dec_self_attention_weights, dec_inter_attention_weights = tf.transpose(
dec_attention_weights, perm=(1, 2, 3, 0, 4))
print(dec_self_attention_weights.shape, dec_inter_attention_weights.shape)
.. parsed-literal::
:class: output
(2, 4, 6, 10) (2, 4, 6, 10)
.. raw:: html
.. raw:: html
Do thuộc tính tự động hồi quy của bộ giải mã tự chú ý, không có truy vấn
nào tham dự các cặp khóa-giá trị sau vị trí truy vấn.
.. raw:: html
.. raw:: html
.. code:: python
# Plus one to include the beginning-of-sequence token
d2l.show_heatmaps(
dec_self_attention_weights[:, :, :, :len(translation.split()) + 1],
xlabel='Key positions', ylabel='Query positions',
titles=['Head %d' % i for i in range(1, 5)], figsize=(7, 3.5))
.. figure:: output_transformer_5722f1_219_0.svg
.. raw:: html
.. raw:: html
.. code:: python
# Plus one to include the beginning-of-sequence token
d2l.show_heatmaps(
dec_self_attention_weights[:, :, :, :len(translation.split()) + 1],
xlabel='Key positions', ylabel='Query positions',
titles=['Head %d' % i for i in range(1, 5)], figsize=(7, 3.5))
.. figure:: output_transformer_5722f1_222_0.svg
.. raw:: html
.. raw:: html
.. code:: python
# Plus one to include the beginning-of-sequence token
d2l.show_heatmaps(
dec_self_attention_weights[:, :, :, :len(translation.split()) + 1],
xlabel='Key positions', ylabel='Query positions',
titles=['Head %d' % i for i in range(1, 5)], figsize=(7, 3.5))
.. figure:: output_transformer_5722f1_225_0.svg
.. raw:: html
.. raw:: html
Tương tự như trường hợp trong bộ mã hóa tự chú ý, thông qua độ dài hợp
lệ được chỉ định của chuỗi đầu vào, không có truy vấn từ chuỗi đầu ra
tham quan đến các thẻ đệm đó từ chuỗi đầu vào.
.. raw:: html
.. raw:: html
.. code:: python
d2l.show_heatmaps(
dec_inter_attention_weights, xlabel='Key positions',
ylabel='Query positions', titles=['Head %d' % i for i in range(1, 5)],
figsize=(7, 3.5))
.. figure:: output_transformer_5722f1_231_0.svg
.. raw:: html
.. raw:: html
.. code:: python
d2l.show_heatmaps(
dec_inter_attention_weights, xlabel='Key positions',
ylabel='Query positions', titles=['Head %d' % i for i in range(1, 5)],
figsize=(7, 3.5))
.. figure:: output_transformer_5722f1_234_0.svg
.. raw:: html
.. raw:: html
.. code:: python
d2l.show_heatmaps(
dec_inter_attention_weights, xlabel='Key positions',
ylabel='Query positions', titles=['Head %d' % i for i in range(1, 5)],
figsize=(7, 3.5))
.. figure:: output_transformer_5722f1_237_0.svg
.. raw:: html
.. raw:: html
Mặc dù kiến trúc biến áp ban đầu được đề xuất để học theo trình tự, vì
chúng ta sẽ khám phá sau trong cuốn sách, bộ mã hóa biến áp hoặc bộ giải
mã biến áp thường được sử dụng riêng cho các nhiệm vụ học sâu khác nhau.
Tóm tắt
-------
- Máy biến áp là một thể hiện của kiến trúc bộ mã hóa-giải mã, mặc dù
bộ mã hóa hoặc bộ giải mã có thể được sử dụng riêng lẻ trong thực tế.
- Trong máy biến áp, tự chú ý nhiều đầu được sử dụng để biểu diễn trình
tự đầu vào và chuỗi đầu ra, mặc dù bộ giải mã phải bảo toàn thuộc
tính hồi quy tự động thông qua một phiên bản đeo mặt nạ.
- Cả hai kết nối còn lại và bình thường hóa lớp trong máy biến áp đều
quan trọng để đào tạo một mô hình rất sâu.
- Mạng chuyển tiếp nguồn cấp dữ liệu định vị trong mô hình máy biến áp
biến đổi biểu diễn ở tất cả các vị trí trình tự bằng cách sử dụng
cùng một MLP.
Bài tập
-------
1. Đào tạo một máy biến áp sâu hơn trong các thí nghiệm. Làm thế nào để
nó ảnh hưởng đến tốc độ đào tạo và hiệu suất dịch thuật?
2. Có phải là một ý tưởng tốt để thay thế sự chú ý của sản phẩm điểm thu
nhỏ bằng sự chú ý phụ gia trong máy biến áp? Tại sao?
3. Đối với mô hình hóa ngôn ngữ, chúng ta có nên sử dụng bộ mã hóa biến
áp, bộ giải mã hoặc cả hai? Làm thế nào để thiết kế phương pháp này?
4. Điều gì có thể là thách thức đối với máy biến áp nếu chuỗi đầu vào
rất dài? Tại sao?
5. Làm thế nào để cải thiện tính toán và hiệu quả bộ nhớ của máy biến
áp? Hint: you may refer to the survey paper by Tay et al.
:cite:`Tay.Dehghani.Bahri.ea.2020`.
6. Làm thế nào chúng ta có thể thiết kế các mô hình dựa trên máy biến áp
cho các tác vụ phân loại hình ảnh mà không cần sử dụng CNN? Hint: you
may refer to the vision transformer
:cite:`Dosovitskiy.Beyer.Kolesnikov.ea.2021`.
.. raw:: html
.. raw:: html
`Discussions `__
.. raw:: html
.. raw:: html
`Discussions `__
.. raw:: html
.. raw:: html