在所謂的 seq2seq 問題中,如機(jī)器翻譯(如 第 10.5 節(jié)所述),其中輸入和輸出均由可變長度的未對齊序列組成,我們通常依賴編碼器-解碼器架構(gòu)(第10.6 節(jié))。在本節(jié)中,我們將演示編碼器-解碼器架構(gòu)在機(jī)器翻譯任務(wù)中的應(yīng)用,其中編碼器和解碼器均作為 RNN 實(shí)現(xiàn)( Cho等人,2014 年,Sutskever等人,2014 年)。
在這里,編碼器 RNN 將可變長度序列作為輸入并將其轉(zhuǎn)換為固定形狀的隱藏狀態(tài)。稍后,在 第 11 節(jié)中,我們將介紹注意力機(jī)制,它允許我們訪問編碼輸入,而無需將整個(gè)輸入壓縮為單個(gè)固定長度的表示形式。
然后,為了生成輸出序列,一次一個(gè)標(biāo)記,由一個(gè)單獨(dú)的 RNN 組成的解碼器模型將在給定輸入序列和輸出中的前一個(gè)標(biāo)記的情況下預(yù)測每個(gè)連續(xù)的目標(biāo)標(biāo)記。在訓(xùn)練期間,解碼器通常會(huì)以官方“ground-truth”標(biāo)簽中的前面標(biāo)記為條件。然而,在測試時(shí),我們希望根據(jù)已經(jīng)預(yù)測的標(biāo)記來調(diào)節(jié)解碼器的每個(gè)輸出。請注意,如果我們忽略編碼器,則 seq2seq 架構(gòu)中的解碼器的行為就像普通語言模型一樣。圖 10.7.1說明了如何在機(jī)器翻譯中使用兩個(gè) RNN 進(jìn)行序列到序列學(xué)習(xí)。
在圖 10.7.1中,特殊的“”標(biāo)記標(biāo)志著序列的結(jié)束。一旦生成此令牌,我們的模型就可以停止進(jìn)行預(yù)測。在 RNN 解碼器的初始時(shí)間步,有兩個(gè)特殊的設(shè)計(jì)決策需要注意:首先,我們以特殊的序列開始“”標(biāo)記開始每個(gè)輸入。其次,我們可以在每個(gè)解碼時(shí)間步將編碼器的最終隱藏狀態(tài)輸入解碼器(Cho等人,2014 年)。在其他一些設(shè)計(jì)中,例如Sutskever等人。( 2014 ),RNN 編碼器的最終隱藏狀態(tài)僅在第一個(gè)解碼步驟用于啟動(dòng)解碼器的隱藏狀態(tài)。
import collections
import math
import torch
from torch import nn
from torch.nn import functional as F
from d2l import torch as d2l
import collections
import math
import tensorflow as tf
from d2l import tensorflow as d2l
10.7.1。教師強(qiáng)迫
雖然在輸入序列上運(yùn)行編碼器相對簡單,但如何處理解碼器的輸入和輸出則需要更加小心。最常見的方法有時(shí)稱為 教師強(qiáng)制。在這里,原始目標(biāo)序列(標(biāo)記標(biāo)簽)作為輸入被送入解碼器。更具體地說,特殊的序列開始標(biāo)記和原始目標(biāo)序列(不包括最終標(biāo)記)被連接起來作為解碼器的輸入,而解碼器輸出(用于訓(xùn)練的標(biāo)簽)是原始目標(biāo)序列,移動(dòng)了一個(gè)標(biāo)記: “”,“Ils”,“regardent”,“?!?→“Ils”、“regardent”、“.”、“”(圖 10.7.1)。
我們在10.5.3 節(jié)中的實(shí)施為教師強(qiáng)制準(zhǔn)備了訓(xùn)練數(shù)據(jù),其中用于自監(jiān)督學(xué)習(xí)的轉(zhuǎn)移標(biāo)記類似于9.3 節(jié)中的語言模型訓(xùn)練。另一種方法是將來自前一個(gè)時(shí)間步的預(yù)測標(biāo)記作為當(dāng)前輸入提供給解碼器。
下面,我們 將更詳細(xì)地解釋圖 10.7.1中描繪的設(shè)計(jì)。我們將在第 10.5 節(jié)中介紹的英語-法語數(shù)據(jù)集上訓(xùn)練該模型進(jìn)行機(jī)器翻譯 。
10.7.2。編碼器
回想一下,編碼器將可變長度的輸入序列轉(zhuǎn)換為固定形狀的上下文變量 c(見圖 10.7.1)。
考慮一個(gè)單序列示例(批量大小 1)。假設(shè)輸入序列是x1,…,xT, 這樣xt是個(gè) tth令牌。在時(shí)間步t, RNN 變換輸入特征向量xt為了xt 和隱藏狀態(tài)ht?1從上一次進(jìn)入當(dāng)前隱藏狀態(tài)ht. 我們可以使用一個(gè)函數(shù)f表達(dá)RNN循環(huán)層的變換:
通常,編碼器通過自定義函數(shù)將所有時(shí)間步的隱藏狀態(tài)轉(zhuǎn)換為上下文變量q:
例如,在圖 10.7.1中,上下文變量只是隱藏狀態(tài)hT對應(yīng)于編碼器 RNN 在處理輸入序列的最終標(biāo)記后的表示。
在這個(gè)例子中,我們使用單向 RNN 來設(shè)計(jì)編碼器,其中隱藏狀態(tài)僅取決于隱藏狀態(tài)時(shí)間步和之前的輸入子序列。我們還可以使用雙向 RNN 構(gòu)建編碼器。在這種情況下,隱藏狀態(tài)取決于時(shí)間步長前后的子序列(包括當(dāng)前時(shí)間步長的輸入),它編碼了整個(gè)序列的信息。
現(xiàn)在讓我們來實(shí)現(xiàn) RNN 編碼器。請注意,我們使用嵌入層來獲取輸入序列中每個(gè)標(biāo)記的特征向量。嵌入層的權(quán)重是一個(gè)矩陣,其中行數(shù)對應(yīng)于輸入詞匯表的大小 ( vocab_size
),列數(shù)對應(yīng)于特征向量的維度 ( embed_size
)。對于任何輸入令牌索引i,嵌入層獲取ith權(quán)矩陣的行(從 0 開始)返回其特征向量。在這里,我們使用多層 GRU 實(shí)現(xiàn)編碼器。
def init_seq2seq(module): #@save
"""Initialize weights for Seq2Seq."""
if type(module) == nn.Linear:
nn.init.xavier_uniform_(module.weight)
if type(module) == nn.GRU:
for param in module._flat_weights_names:
if "weight" in param:
nn.init.xavier_uniform_(module._parameters[param])
class Seq2SeqEncoder(d2l.Encoder): #@save
"""The RNN encoder for sequence to sequence learning."""
def __init__(self, vocab_size, embed_size, num_hiddens, num_layers,
dropout=0):
super().__init__()
self.embedding = nn.Embedding(vocab_size, embed_size)
self.rnn = d2l.GRU(embed_size, num_hiddens, num_layers, dropout)
self.apply(init_seq2seq)
def forward(self, X, *args):
# X shape: (batch_size, num_steps)
embs = self.embedding(X.t().type(torch.in
評論
查看更多