Task 4
Seq2Seq以及機(jī)器翻譯
Seq2Seq也就是“序列到序列”模型愈腾,是一類用于解決輸入為序列,輸出為序列的生成式任務(wù)的模型啄刹。
其中目前最通用的架構(gòu)即為Encoder-Decoder诉植,即 編碼器-解碼器 架構(gòu)。
Seq2Seq的一個(gè)經(jīng)典任務(wù)即為機(jī)器翻譯,它要求輸入一個(gè)語言的句子(單詞序列)父能,輸出目標(biāo)語言的同義句子(翻譯)稳强。
Encoder-Decoder架構(gòu)的一個(gè)優(yōu)點(diǎn)是,輸出序列的長度可以和輸入序列不一樣(即不需要字字對應(yīng))间涵,這很好的解決了翻譯的兩個(gè)語言的句子通常長度不一致的問題仁热。
它的思想在于通過一個(gè)Encoder編碼器提取出序列的低維(Dense)抽象編碼信息(向量),再通過Decoder把其中的信息解碼并轉(zhuǎn)碼為目標(biāo)領(lǐng)域的序列勾哩。
機(jī)器翻譯最新進(jìn)展與對應(yīng)模型:
https://github.com/sebastianruder/NLP-progress/blob/master/english/machine_translation.md
神經(jīng)翻譯模型列表:
https://github.com/jonsafari/nmt-list
使用RNN實(shí)現(xiàn)一個(gè)用于機(jī)器翻譯的Encoder-Decoder結(jié)構(gòu)模型
數(shù)據(jù)預(yù)處理
首先需要一個(gè)不同語言的語句對數(shù)據(jù)集:
中英17萬條語句對:https://www.kesci.com/home/dataset/5dbbc1a1080dc300371ec4e6/files
機(jī)器翻譯英法數(shù)據(jù)17萬條:https://www.kesci.com/home/dataset/5ddde161ca27f8002c4a5a26
520萬個(gè)中英文平行語料:https://www.kesci.com/home/dataset/5de5fcafca27f8002c4ca993
字符在計(jì)算機(jī)里是以編碼的形式存在抗蠢,我們通常所用的空格是 \x20 ,是在標(biāo)準(zhǔn)ASCII可見字符 0x20~0x7e 范圍內(nèi)思劳。 而 \xa0 屬于 latin1 (ISO/IEC_8859-1)中的擴(kuò)展字符集字符迅矛,代表不間斷空白符nbsp(non-breaking space),超出gbk編碼范圍潜叛,是需要去除的特殊字符秽褒。再數(shù)據(jù)預(yù)處理的過程中,我們首先需要對數(shù)據(jù)進(jìn)行清洗威兜。
將需要分隔的標(biāo)點(diǎn)與單詞用空格隔開销斟,去除掉不需要的符號。
分詞
外語通辰范妫看情況可以直接用空格蚂踊,或者使用分詞庫。
中文需要使用分詞庫(jieba等)笔宿。
注意:如果使用詞嵌入犁钟,最好使用與訓(xùn)練詞向量時(shí)相同的分詞方法棱诱。
建立詞典
注意特殊字符
統(tǒng)一輸入序列的長度,多余的部分用<pad>-0填充涝动,在后續(xù)訓(xùn)練時(shí)不會傳梯度迈勋。-需要使用MaskSoftmaxCELoss的原因。
結(jié)構(gòu)
訓(xùn)練時(shí)會將真實(shí)序列(Ground truth)作為Decoder的輸入醋粟。
(訓(xùn)練時(shí)的輸出會預(yù)測錯(cuò)靡菇,避免影響訓(xùn)練效果)
預(yù)測是會把t-1時(shí)間步的輸出作為t時(shí)間步的輸入。
Encoder
class Seq2SeqEncoder(d2l.Encoder):
def __init__(self, vocab_size, embed_size, num_hiddens, num_layers,
dropout=0, **kwargs):
super(Seq2SeqEncoder, self).__init__(**kwargs)
self.num_hiddens=num_hiddens
self.num_layers=num_layers
self.embedding = nn.Embedding(vocab_size, embed_size)
self.rnn = nn.LSTM(embed_size,num_hiddens, num_layers, dropout=dropout)
#Encoder的隱藏層使用0初始化
def begin_state(self, batch_size, device):
return [torch.zeros(size=(self.num_layers, batch_size, self.num_hiddens), device=device),
torch.zeros(size=(self.num_layers, batch_size, self.num_hiddens), device=device)]
def forward(self, X, *args):
#嵌入層昔穴,將index序列轉(zhuǎn)為對應(yīng)的embed_size維嵌入向量(獨(dú)熱編碼or詞向量)
X = self.embedding(X) # X shape: (batch_size, seq_len, embed_size)
X = X.transpose(0, 1) # RNN的第0軸需要是 序列長度
# state = self.begin_state(X.shape[1], device=X.device)
out, state = self.rnn(X)
# The shape of out is (seq_len, batch_size, num_hiddens).
# 對于lstm镰官,state包含隱藏層和記憶細(xì)胞(memory cell)
# of the last time step, the shape is (num_layers, batch_size, num_hiddens)
#也就是把全部時(shí)間步的狀態(tài)都返回在state里?
return out, state
Decoder
class Seq2SeqDecoder(d2l.Decoder):
def __init__(self, vocab_size, embed_size, num_hiddens, num_layers,
dropout=0, **kwargs):
super(Seq2SeqDecoder, self).__init__(**kwargs)
self.embedding = nn.Embedding(vocab_size, embed_size)
self.rnn = nn.LSTM(embed_size,num_hiddens, num_layers, dropout=dropout)
self.dense = nn.Linear(num_hiddens,vocab_size)
def init_state(self, enc_outputs, *args):
return enc_outputs[1] #encoder的lstm的最后一時(shí)間步的隱藏狀態(tài)
def forward(self, X, state):
X = self.embedding(X).transpose(0, 1)
out, state = self.rnn(X, state)
# 為簡化損失函數(shù)吗货,將batch_size轉(zhuǎn)到第0維
out = self.dense(out).transpose(0, 1)
return out, state
將pad的位置進(jìn)行掩蓋 - 變長句子適應(yīng)
def SequenceMask(X, X_len,value=0):
#將X中泳唠,x_len數(shù)值對應(yīng)索引及其之后位置用value進(jìn)行掩蓋
maxlen = X.size(1)
mask = torch.arange(maxlen)[None, :].to(X_len.device) < X_len[:, None]
X[~mask]=value
return X
class MaskedSoftmaxCELoss(nn.CrossEntropyLoss):
# pred shape: (batch_size, seq_len, vocab_size)
# label shape: (batch_size, seq_len)
# valid_length shape: (batch_size, )
def forward(self, pred, label, valid_length):
# the sample weights shape should be (batch_size, seq_len)
#保留的位置的權(quán)重設(shè)為1,不保留設(shè)為0
weights = torch.ones_like(label)
weights = SequenceMask(weights, valid_length).float()
self.reduction='none'
output=super(MaskedSoftmaxCELoss, self).forward(pred.transpose(1,2), label)
#不保留的位置乘以0宙搬,就去掉了這處的損失
#然后再對batch取平均
return (output*weights).mean(dim=1)
訓(xùn)練
def train_ch7(model, data_iter, lr, num_epochs, device):
model.to(device)
optimizer = optim.Adam(model.parameters(), lr=lr)
loss = MaskedSoftmaxCELoss()
tic = time.time()
for epoch in range(1, num_epochs+1):
l_sum, num_tokens_sum = 0.0, 0.0
for batch in data_iter:
optimizer.zero_grad()
# 一個(gè)batch:(輸入序列笨腥,輸入長度,目標(biāo)序列勇垛,目標(biāo)長度)
X, X_vlen, Y, Y_vlen = [x.to(device) for x in batch]
#目標(biāo)序列的t-1時(shí)間步的Ground truth作為t的輸入脖母,bos不需要輸入
#eos不需要訓(xùn)練?
Y_input, Y_label, Y_vlen = Y[:,:-1], Y[:,1:], Y_vlen-1
Y_hat, _ = model(X, Y_input, X_vlen, Y_vlen)
l = loss(Y_hat, Y_label, Y_vlen).sum()
l.backward()
with torch.no_grad():
d2l.grad_clipping_nn(model, 5, device)
num_tokens = Y_vlen.sum().item()
optimizer.step()
l_sum += l.sum().item()
num_tokens_sum += num_tokens
if epoch % 50 == 0:
print("epoch {0:4d},loss {1:.3f}, time {2:.1f} sec".format(
epoch, (l_sum/num_tokens_sum), time.time()-tic))
tic = time.time()
Beam Search - 集束搜索
若直接采用貪心方法闲孤,在每個(gè)時(shí)間步選擇概率最大的結(jié)果輸出谆级,不一定能得到全局最優(yōu)解。
在Beam Search中只有一個(gè)參數(shù)B讼积,叫做beam width(集束寬)肥照,用來表示在每一次挑選top B的結(jié)果。
最終輸出B個(gè)概率最高的序列
seq2seq 中的 beam search 算法過程是怎樣的勤众? - 習(xí)翔宇的回答 - 知乎 https://www.zhihu.com/question/54356960/answer/375588742
seq2seq中的beam search算法過程 - 憶臻的文章 - 知乎 https://zhuanlan.zhihu.com/p/28048246
注意力機(jī)制
視覺注意力機(jī)制是人類視覺所特有的大腦信號處理機(jī)制舆绎。人類視覺通過快速掃描全局圖像,獲得需要重點(diǎn)關(guān)注的目標(biāo)區(qū)域们颜,也就是一般所說的注意力焦點(diǎn)吕朵,而后對這一區(qū)域投入更多注意力資源,以獲取更多所需要關(guān)注目標(biāo)的細(xì)節(jié)信息窥突,而抑制其他無用信息努溃。
深度學(xué)習(xí)中的注意力機(jī)制從本質(zhì)上講和人類的選擇性視覺注意力機(jī)制類似,核心目標(biāo)也是從眾多信息中選擇出對當(dāng)前任務(wù)目標(biāo)更關(guān)鍵的信息阻问。
深度學(xué)習(xí)中的注意力模型(2017版) - 張俊林的文章 - 知乎 https://zhuanlan.zhihu.com/p/37601161
attention機(jī)制:又稱為注意力機(jī)制茅坛,顧名思義,是一種能讓模型對重要信息重點(diǎn)關(guān)注并充分學(xué)習(xí)吸收的技術(shù)则拷,它不算是一個(gè)完整的模型贡蓖,應(yīng)當(dāng)是一種技術(shù),能夠作用于任何序列模型中煌茬。
Attention機(jī)制簡單總結(jié) - 邱震宇的文章 - 知乎 https://zhuanlan.zhihu.com/p/46313756
無論是RNN還是CNN都在抽取以及保留全局上下文信息上斥铺,對過長的序列遇到了瓶頸。
Attention可以跨過很長的區(qū)域坛善,建立起某個(gè)上文與當(dāng)前輸入的聯(lián)系
例:一個(gè)代詞與間隔了很多單詞的命名實(shí)體的聯(lián)系(同一個(gè)指代)
實(shí)際做法是通過對每個(gè)(當(dāng)前輸入晾蜘,上一時(shí)間步輸出)(query-查詢),通過之前所有輸入(key)計(jì)算出對應(yīng)隱藏狀態(tài)(value)對于當(dāng)前上下文的聯(lián)系強(qiáng)度(貢獻(xiàn)眠屎,注意力聯(lián)系)剔交,即注意力分?jǐn)?shù)(權(quán)重),并加權(quán)求和求出當(dāng)前上下文改衩。
淺談Attention注意力機(jī)制及其實(shí)現(xiàn) - 空字符的文章 - 知乎 https://zhuanlan.zhihu.com/p/67909876
描述
相似度計(jì)算(注意力打分)
nlp中的Attention注意力機(jī)制+Transformer詳解 - JayLou的文章 - 知乎 https://zhuanlan.zhihu.com/p/53682800
然后通過一個(gè)softmax得到注意力分布(概率分布)
最后加權(quán)平均
點(diǎn)積注意力
MLP注意力
將key 和 value 在特征的維度上合并(concatenate)岖常,然后送至 a single hidden layer perceptron 這層中 hidden layer 為 ? and 輸出的size為 1 .隱層激活函數(shù)為tanh,無偏置.
引入注意力機(jī)制的Seq2seq模型
注意力機(jī)制只需要改動Decoder的實(shí)現(xiàn)
the encoder outputs of all timesteps:encoder輸出的各個(gè)狀態(tài)葫督,被用于attetion layer的memory部分竭鞍,有相同的key和values
the hidden state of the encoder’s final timestep:編碼器最后一個(gè)時(shí)間步的隱藏狀態(tài),被用于初始化decoder 的hidden state
the encoder valid length: 編碼器的有效長度橄镜,借此偎快,注意層不會考慮編碼器輸出中的填充標(biāo)記(Paddings)
在解碼的每個(gè)時(shí)間步,我們使用解碼器的最后一個(gè)RNN層的輸出作為注意層的query洽胶。然后晒夹,將注意力模型的輸出與輸入嵌入向量連接起來,輸入到RNN層姊氓。雖然RNN層隱藏狀態(tài)也包含來自解碼器的歷史信息丐怯,但是attention model的輸出顯式地選擇了enc_valid_len以內(nèi)的編碼器輸出,這樣attention機(jī)制就會盡可能排除其他不相關(guān)的信息他膳。
class Seq2SeqAttentionDecoder(d2l.Decoder):
def __init__(self, vocab_size, embed_size, num_hiddens, num_layers,
dropout=0, **kwargs):
super(Seq2SeqAttentionDecoder, self).__init__(**kwargs)
self.attention_cell = MLPAttention(num_hiddens,num_hiddens, dropout)
self.embedding = nn.Embedding(vocab_size, embed_size)
self.rnn = nn.LSTM(embed_size+ num_hiddens,num_hiddens, num_layers, dropout=dropout)
self.dense = nn.Linear(num_hiddens,vocab_size)
def init_state(self, enc_outputs, enc_valid_len, *args):
outputs, hidden_state = enc_outputs
# Transpose outputs to (batch_size, seq_len, hidden_size)
return (outputs.permute(1,0,-1), hidden_state, enc_valid_len)
def forward(self, X, state):
enc_outputs, hidden_state, enc_valid_len = state
#("X.size",X.size())
X = self.embedding(X).transpose(0,1)
# print("Xembeding.size2",X.size())
outputs = []
for l, x in enumerate(X):
# query shape: (batch_size, 1, hidden_size)
# select hidden state of the last rnn layer as query
query = hidden_state[0][-1].unsqueeze(1) # np.expand_dims(hidden_state[0][-1], axis=1)
# context has same shape as query
# print("query enc_outputs, enc_outputs:\n",query.size(), enc_outputs.size(), enc_outputs.size())
context = self.attention_cell(query, enc_outputs, enc_outputs, enc_valid_len)
# Concatenate on the feature dimension
# print("context.size:",context.size())
x = torch.cat((context, x.unsqueeze(1)), dim=-1)
# Reshape x to (1, batch_size, embed_size+hidden_size)
# print("rnn",x.size(), len(hidden_state))
out, hidden_state = self.rnn(x.transpose(0,1), hidden_state)
outputs.append(out)
outputs = self.dense(torch.cat(outputs, dim=0))
return outputs.transpose(0, 1), [enc_outputs, hidden_state,
enc_valid_len]
Transformer
transformer本質(zhì)上是一個(gè)完全只使用(多頭)注意力機(jī)制 (而不是RNN或CNN)構(gòu)建 Encoder-Decoder 結(jié)構(gòu)的模型响逢。
詳解Transformer (Attention Is All You Need) - 劉巖的文章 - 知乎 https://zhuanlan.zhihu.com/p/48508221
https://mp.weixin.qq.com/s/RLxWevVWHXgX-UcoxDS70w
Encoder的輸出,會和每一層的Decoder進(jìn)行結(jié)合棕孙。
具體結(jié)構(gòu):
- Transformer blocks:將seq2seq模型重的循環(huán)網(wǎng)絡(luò)替換為了Transformer Blocks舔亭,該模塊包含一個(gè)多頭注意力層(Multi-head Attention Layers)以及兩個(gè)position-wise feed-forward networks(FFN)。對于解碼器來說蟀俊,另一個(gè)多頭注意力層被用于接受編碼器的隱藏狀態(tài)钦铺。
- Add and norm:多頭注意力層和前饋網(wǎng)絡(luò)的輸出被送到兩個(gè)“add and norm”層進(jìn)行處理,該層包含殘差結(jié)構(gòu)以及層歸一化肢预。
- Position encoding:由于自注意力層并沒有區(qū)分元素的順序矛洞,所以一個(gè)位置編碼層被用于向序列元素里添加位置信息。
多頭注意力
自注意力機(jī)制:每個(gè)時(shí)間步的Q,K沼本,V相同噩峦。
多頭注意力:如上文,將輸入單詞轉(zhuǎn)化成嵌入向量抽兆;
根據(jù)嵌入向量得到 识补,, 三個(gè)向量辫红;
為每個(gè)向量計(jì)算一個(gè)score: [圖片上傳失敗...(image-b96ae8-1582115228360)]
為了梯度的穩(wěn)定凭涂,Transformer使用了score歸一化,即除以 [圖片上傳失敗...(image-edfe87-1582115228360)]
對score施以softmax激活函數(shù)贴妻;softmax點(diǎn)乘Value值 切油,得到加權(quán)的每個(gè)輸入向量的評分 ;
相加之后得到最終的輸出結(jié)果 : 名惩。
作者:劉巖
鏈接:https://zhuanlan.zhihu.com/p/48508221
來源:知乎
著作權(quán)歸作者所有澎胡。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處绢片。
多頭注意力層包含h個(gè)并行的自注意力層
位置編碼
論文給出的編碼公式如下:
[圖片上傳失敗...(image-959a89-1582115228360)]
[圖片上傳失敗...(image-987224-1582115228360)]
在上式中滤馍,表示單詞的位置, 表示單詞的維度底循。
作者這么設(shè)計(jì)的原因是考慮到在NLP任務(wù)重巢株,除了單詞的絕對位置,單詞的相對位置也非常重要熙涤。根據(jù)公式 [圖片上傳失敗...(image-8ad92d-1582115228360)] 以及[圖片上傳失敗...(image-b742b7-1582115228360)] 阁苞,這表明位置 的位置向量可以表示為位置 的特征向量的線性變化,這為模型捕捉單詞之間的相對位置關(guān)系提供了非常大的便利祠挫。