Task 4

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è)語言的句子通常長度不一致的問題仁热。

Image Name

它的思想在于通過一個(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)

Image Name

訓(xùn)練時(shí)會將真實(shí)序列(Ground truth)作為Decoder的輸入醋粟。

(訓(xùn)練時(shí)的輸出會預(yù)測錯(cuò)靡菇,避免影響訓(xùn)練效果)

Image Name

預(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

image.png
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é)果。

image

最終輸出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)前上下文改衩。

Image Name
preview

淺談Attention注意力機(jī)制及其實(shí)現(xiàn) - 空字符的文章 - 知乎 https://zhuanlan.zhihu.com/p/67909876

描述

相似度計(jì)算(注意力打分)

img

nlp中的Attention注意力機(jī)制+Transformer詳解 - JayLou的文章 - 知乎 https://zhuanlan.zhihu.com/p/53682800

然后通過一個(gè)softmax得到注意力分布(概率分布)

最后加權(quán)平均

點(diǎn)積注意力

Q\in R^{m\times d}即m個(gè)queries,K\in R^{n\times d}即n個(gè)keys\\\alpha(Q,K)=QK^T/\sqrtht7jfrl

MLP注意力

W_k\in R^{h\times d_k},W_q\in R^{h\times d_q},v\in R^h\\\alpha(q,k)=v^Ttanh(W_kk+W_qq)

將key 和 value 在特征的維度上合并(concatenate)岖常,然后送至 a single hidden layer perceptron 這層中 hidden layer 為 ? and 輸出的size為 1 .隱層激活函數(shù)為tanh,無偏置.

引入注意力機(jī)制的Seq2seq模型

Image Name
Image Name

注意力機(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

image.png

Encoder的輸出,會和每一層的Decoder進(jìn)行結(jié)合棕孙。

具體結(jié)構(gòu):

  1. 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)钦铺。
  2. Add and norm:多頭注意力層和前饋網(wǎng)絡(luò)的輸出被送到兩個(gè)“add and norm”層進(jìn)行處理,該層包含殘差結(jié)構(gòu)以及層歸一化肢预。
  3. Position encoding:由于自注意力層并沒有區(qū)分元素的順序矛洞,所以一個(gè)位置編碼層被用于向序列元素里添加位置信息。

多頭注意力

Fig. 10.3.2 自注意力結(jié)構(gòu)

自注意力機(jī)制:每個(gè)時(shí)間步的Q,K沼本,V相同噩峦。

image

如上文,將輸入單詞轉(zhuǎn)化成嵌入向量抽兆;

根據(jù)嵌入向量得到 q识补,kv 三個(gè)向量辫红;

為每個(gè)向量計(jì)算一個(gè)score: [圖片上傳失敗...(image-b96ae8-1582115228360)]
為了梯度的穩(wěn)定凭涂,Transformer使用了score歸一化,即除以 [圖片上傳失敗...(image-edfe87-1582115228360)]
對score施以softmax激活函數(shù)贴妻;

softmax點(diǎn)乘Value值 v切油,得到加權(quán)的每個(gè)輸入向量的評分 v

相加之后得到最終的輸出結(jié)果 zz=\sum v 名惩。

img

作者:劉巖
鏈接:https://zhuanlan.zhihu.com/p/48508221
來源:知乎
著作權(quán)歸作者所有澎胡。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處绢片。

多頭注意力:
Image Name

多頭注意力層包含h個(gè)并行的自注意力層

位置編碼

論文給出的編碼公式如下:

[圖片上傳失敗...(image-959a89-1582115228360)]

[圖片上傳失敗...(image-987224-1582115228360)]

在上式中滤馍,pos表示單詞的位置, i表示單詞的維度底循。

作者這么設(shè)計(jì)的原因是考慮到在NLP任務(wù)重巢株,除了單詞的絕對位置,單詞的相對位置也非常重要熙涤。根據(jù)公式 [圖片上傳失敗...(image-8ad92d-1582115228360)] 以及[圖片上傳失敗...(image-b742b7-1582115228360)] 阁苞,這表明位置 kp 的位置向量可以表示為位置 k 的特征向量的線性變化,這為模型捕捉單詞之間的相對位置關(guān)系提供了非常大的便利祠挫。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末那槽,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子等舔,更是在濱河造成了極大的恐慌骚灸,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件慌植,死亡現(xiàn)場離奇詭異甚牲,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蝶柿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進(jìn)店門丈钙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人交汤,你說我怎么就攤上這事雏赦。” “怎么了?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵星岗,是天一觀的道長填大。 經(jīng)常有香客問我,道長伍茄,這世上最難降的妖魔是什么栋盹? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮敷矫,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘汉额。我一直安慰自己曹仗,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布蠕搜。 她就那樣靜靜地躺著怎茫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪妓灌。 梳的紋絲不亂的頭發(fā)上轨蛤,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天,我揣著相機(jī)與錄音虫埂,去河邊找鬼祥山。 笑死,一個(gè)胖子當(dāng)著我的面吹牛掉伏,可吹牛的內(nèi)容都是我干的缝呕。 我是一名探鬼主播,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼斧散,長吁一口氣:“原來是場噩夢啊……” “哼供常!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起鸡捐,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤栈暇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后箍镜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體源祈,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年鹿寨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了新博。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,605評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡脚草,死狀恐怖赫悄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤埂淮,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布姑隅,位于F島的核電站,受9級特大地震影響倔撞,放射性物質(zhì)發(fā)生泄漏讲仰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一痪蝇、第九天 我趴在偏房一處隱蔽的房頂上張望鄙陡。 院中可真熱鬧,春花似錦躏啰、人聲如沸趁矾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽毫捣。三九已至,卻和暖如春帝际,著一層夾襖步出監(jiān)牢的瞬間蔓同,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工蹲诀, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留斑粱,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓侧甫,卻偏偏與公主長得像珊佣,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子披粟,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評論 2 348

推薦閱讀更多精彩內(nèi)容