Sequence to Sequence (seq2seq)
Classe di approcci e metodi utilizzati nel campo del deep learning, soprattutto nei task di traduzione automatica ma anche, per esempio, per la creazione di didascalie di immagini. Essi fondano le basi concettuali per le architetture transformer.
Sono basati su un modello encoder-decoder in cui:
- l'encoder processa la sequenza di input creando una rappresentazione semantica dell'input, che catturi le informazioni essenziali
- il decoder utilizza gli stati nascosti dell'encoder, in cui viene rappresentazione l'informazione per creare una sequenza di output
Esempio di utilizzo nel task di traduzione automatica
La pubblicazione Learning Phrase Representations using RNN Encoder-Decoder for Statistical Machine Translation del 2014 dimostra il concetto perfettamente. Si può seguire il codice nell'implementazione in Pytorch a questo URL.
Seguendo l'esempio, si vuole addestrare la rete a tradurre solo le seguenti sequenze di parole "contrarie", notare che sono stati anche appositamente introdotti degli errori.
['man', 'women'], ['black', 'white'], ['king', 'queen'], ['girl', 'boy'], ['up', 'down'], ['high', 'low']
Concettualmente, il modello Seq2Seq viene rappresentato come due RNN. Durante il forward pass:
- la prima RNN, l'encoder, prende l'input, man (in realtà manPP con padding a lunghezza 5), e lo processa, finendo con l'avere uno stato nascosto
- questo stato nascosto dell'encoder viene passato come stato nascosto della RNN decoder, e come input viene data la sequenza di output (Swoman in quanto viene introdotto sempre un token di start della sequenza). Durante la fase di training, questa sequenza corrisponde effettivamente all'output atteso. Dopo il processamento della RNN, l'hidden layer conterrà, concettualmente, la rappresentazione dell'intero processo di traduzione della sequenza "manPPSwoman"
- Un layer lineare connette poi lo stato nascosto del decoder all'output, in questo caso con rappresentazione one-hot
Durante la fase di addestramento, la loss viene calcolata confrontando l'output con il target,
class Seq2Seq(nn.Module): def __init__(self): super(Seq2Seq, self).__init__() # Definizione della cella dell'encoder RNN con dropout per ridurre l'overfitting self.enc_cell = nn.RNN(input_size=n_class, hidden_size=n_hidden, dropout=0.5) # Definizione della cella del decoder RNN, simile all'encoder self.dec_cell = nn.RNN(input_size=n_class, hidden_size=n_hidden, dropout=0.5) # Un layer lineare che mappa l'output del decoder alla dimensione della classe di output self.fc = nn.Linear(n_hidden, n_class) def forward(self, enc_input, enc_hidden, dec_input): # ... # Esecuzione dell'encoder con l'input e lo stato nascosto iniziale, ottenendo lo stato nascosto finale _, enc_states = self.enc_cell(enc_input, enc_hidden) # Esecuzione del decoder con l'input e lo stato nascosto dell'encoder come stato iniziale outputs, _ = self.dec_cell(dec_input, enc_states) # Applicazione del layer lineare per ottenere le previsioni finali model = self.fc(outputs) return model
La cosa più importante è che