9.6. Arquitetura Encoder-Decoder
Open the notebook in Colab
Open the notebook in Colab
Open the notebook in Colab
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.

../_images/encoder-decoder.svg

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

  1. Suponha que usamos redes neurais para implementar a arquitetura codificador-decodificador. O codificador e o decodificador precisam ser do mesmo tipo de rede neural?

  2. Além da tradução automática, você consegue pensar em outro aplicativo em que a arquitetura codificador-decodificador possa ser aplicada?