9.6. Arquitetura Encoder-Decoder¶ Open the notebook in SageMaker Studio Lab
Como discutimos em Section 9.5, maquina de tradução é um domínio de problema principal para modelos de transdução de sequência, cuja entrada e saída são ambas as sequências de comprimento variável. Para lidar com este tipo de entradas e saídas, podemos projetar uma arquitetura com dois componentes principais. O primeiro componente é um codificador: ele pega uma sequência de comprimento variável como entrada e a transforma em um estado com uma forma fixa. O segundo componente é um decodificador: ele mapeia o estado codificado de uma forma fixa a uma sequência de comprimento variável. Isso é chamado de arquitetura codificador-decodificador, que é representado em Fig. 9.6.1.
Fig. 9.6.1 A arquitetura encoder-decoder.¶
Vamos fazer uma tradução automática de inglês para francês como um exemplo. Dada uma sequência de entrada em inglês: “They”, “are”, “watching”, “.”, esta arquitetura de codificador-decodificador primeiro codifica a entrada de comprimento variável em um estado, então decodifica o estado para gerar o token de sequência traduzido por token como saída: “Ils”, “respectent”, “.”. Uma vez que a arquitetura codificador-decodificador forma a base de diferentes modelos de transdução de sequência nas seções subsequentes, esta seção irá converter esta arquitetura em uma interface que será implementada posteriormente.
9.6.1. Encoder¶
Na interface do codificador, nós apenas especificamos isso o codificador
recebe sequências de comprimento variável como X
de entrada. A
implementação será fornecida por qualquer modelo que herde esta classe
Encoder
base.
from mxnet.gluon import nn
#@save
class Encoder(nn.Block):
"""The base encoder interface for the encoder-decoder architecture."""
def __init__(self, **kwargs):
super(Encoder, self).__init__(**kwargs)
def forward(self, X, *args):
raise NotImplementedError
from torch import nn
#@save
class Encoder(nn.Module):
"""The base encoder interface for the encoder-decoder architecture."""
def __init__(self, **kwargs):
super(Encoder, self).__init__(**kwargs)
def forward(self, X, *args):
raise NotImplementedError
9.6.2. Decoder¶
Na seguinte interface do decodificador, adicionamos uma função adicional
init_state
para converter a saída do codificador (enc_outputs
)
no estado codificado. Observe que esta etapa pode precisar de entradas
extras, como o comprimento válido da entrada, o que foi explicado in
Section 9.5.4. Para gerar um token de sequência de
comprimento variável por token, toda vez que o decodificador pode mapear
uma entrada (por exemplo, o token gerado na etapa de tempo anterior) e o
estado codificado em um token de saída na etapa de tempo atual.
#@save
class Decoder(nn.Block):
"""The base decoder interface for the encoder-decoder architecture."""
def __init__(self, **kwargs):
super(Decoder, self).__init__(**kwargs)
def init_state(self, enc_outputs, *args):
raise NotImplementedError
def forward(self, X, state):
raise NotImplementedError
#@save
class Decoder(nn.Module):
"""The base decoder interface for the encoder-decoder architecture."""
def __init__(self, **kwargs):
super(Decoder, self).__init__(**kwargs)
def init_state(self, enc_outputs, *args):
raise NotImplementedError
def forward(self, X, state):
raise NotImplementedError
9.6.3. Somando o Encoder e o Decoder¶
No fim, a arquitetura codificador-decodificador contém um codificador e um decodificador, com argumentos opcionalmente extras. Na propagação direta, a saída do codificador é usado para produzir o estado codificado, e este estado será posteriormente usado pelo decodificador como uma de suas entradas.
#@save
class EncoderDecoder(nn.Block):
"""The base class for the encoder-decoder architecture."""
def __init__(self, encoder, decoder, **kwargs):
super(EncoderDecoder, self).__init__(**kwargs)
self.encoder = encoder
self.decoder = decoder
def forward(self, enc_X, dec_X, *args):
enc_outputs = self.encoder(enc_X, *args)
dec_state = self.decoder.init_state(enc_outputs, *args)
return self.decoder(dec_X, dec_state)
#@save
class EncoderDecoder(nn.Module):
"""The base class for the encoder-decoder architecture."""
def __init__(self, encoder, decoder, **kwargs):
super(EncoderDecoder, self).__init__(**kwargs)
self.encoder = encoder
self.decoder = decoder
def forward(self, enc_X, dec_X, *args):
enc_outputs = self.encoder(enc_X, *args)
dec_state = self.decoder.init_state(enc_outputs, *args)
return self.decoder(dec_X, dec_state)
O termo “estado” na arquitetura do codificador-decodificador provavelmente inspirou você a implementar este arquitetura usando redes neurais com estados. Na próxima seção, veremos como aplicar RNNs para projetar modelos de transdução de sequência baseados em esta arquitetura de codificador-decodificador.
9.6.4. Sumário¶
A arquitetura do codificador-decodificador pode lidar com entradas e saídas que são sequências de comprimento variável, portanto, é adequada para problemas de transdução de sequência, como tradução automática.
O codificador pega uma sequência de comprimento variável como entrada e a transforma em um estado com forma fixa.
O decodificador mapeia o estado codificado de uma forma fixa para uma sequência de comprimento variável.
9.6.5. Exercícios¶
Suponha que usamos redes neurais para implementar a arquitetura codificador-decodificador. O codificador e o decodificador precisam ser do mesmo tipo de rede neural?
Além da tradução automática, você consegue pensar em outro aplicativo em que a arquitetura codificador-decodificador possa ser aplicada?