日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

从Encoder到Decoder实现Seq2Seq模型

發(fā)布時(shí)間:2024/7/5 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 从Encoder到Decoder实现Seq2Seq模型 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
首發(fā)于機(jī)器不學(xué)習(xí)關(guān)注專欄寫文章

從Encoder到Decoder實(shí)現(xiàn)Seq2Seq模型

天雨粟模型師傅 / 果粉?關(guān)注他300 人贊同了該文章

更新:感謝@Gang He指出的代碼錯(cuò)誤。get_batches函數(shù)中第15行與第19行,代碼已經(jīng)重新修改,GitHub已更新。

前言

好久沒有更新專欄,今天我們來看一個(gè)簡單的Seq2Seq實(shí)現(xiàn),我們將使用TensorFlow來實(shí)現(xiàn)一個(gè)基礎(chǔ)版本的Seq2Seq,主要幫助理解Seq2Seq中的基礎(chǔ)架構(gòu)。

最基礎(chǔ)的Seq2Seq模型包含了三個(gè)部分,即Encoder、Decoder以及連接兩者的中間狀態(tài)向量,Encoder通過學(xué)習(xí)輸入,將其編碼成一個(gè)固定大小的狀態(tài)向量S,繼而將S傳給Decoder,Decoder再通過對(duì)狀態(tài)向量S的學(xué)習(xí)來進(jìn)行輸出。


圖中每一個(gè)box代表了一個(gè)RNN單元,通常是LSTM或者GRU。其實(shí)基礎(chǔ)的Seq2Seq是有很多弊端的,首先Encoder將輸入編碼為固定大小狀態(tài)向量的過程實(shí)際上是一個(gè)信息“信息有損壓縮”的過程,如果信息量越大,那么這個(gè)轉(zhuǎn)化向量的過程對(duì)信息的損失就越大,同時(shí),隨著sequence length的增加,意味著時(shí)間維度上的序列很長,RNN模型也會(huì)出現(xiàn)梯度彌散。最后,基礎(chǔ)的模型連接Encoder和Decoder模塊的組件僅僅是一個(gè)固定大小的狀態(tài)向量,這使得Decoder無法直接去關(guān)注到輸入信息的更多細(xì)節(jié)。由于基礎(chǔ)Seq2Seq的種種缺陷,隨后引入了Attention的概念以及Bi-directional encoder layer等,由于本篇文章主要是構(gòu)建一個(gè)基礎(chǔ)的Seq2Seq模型,對(duì)其他改進(jìn)tricks先不做介紹。

總結(jié)起來說,基礎(chǔ)的Seq2Seq主要包括Encoder,Decoder,以及連接兩者的固定大小的State Vector。


實(shí)戰(zhàn)代碼

下面我們就將利用TensorFlow來構(gòu)建一個(gè)基礎(chǔ)的Seq2Seq模型,通過向我們的模型輸入一個(gè)單詞(字母序列),例如hello,模型將按照字母順序排序輸出,即輸出ehllo。

版本信息:Python 3 / TensorFlow 1.1

1. 數(shù)據(jù)集

數(shù)據(jù)集包括source與target:

- source_data: 每一行是一個(gè)單詞

- target_data: 每一行是經(jīng)過字母排序后的“單詞”,它的每一行與source_data中每一行一一對(duì)應(yīng)

例如,source_data的第一行是hello,第二行是what,那么target_data中對(duì)應(yīng)的第一行是ehllo,第二行是ahtw。

2. 數(shù)據(jù)預(yù)覽

我們先把source和target數(shù)據(jù)加載進(jìn)來,可以看一下前10行,target的每一行是對(duì)source源數(shù)據(jù)中的單詞進(jìn)行了排序。下面我們就將基于這些數(shù)據(jù)來訓(xùn)練一個(gè)Seq2Seq模型,來幫助大家理解基礎(chǔ)架構(gòu)。

3. 數(shù)據(jù)預(yù)處理

在神經(jīng)網(wǎng)絡(luò)中,對(duì)于文本的數(shù)據(jù)預(yù)處理無非是將文本轉(zhuǎn)化為模型可理解的數(shù)字,這里都比較熟悉,不作過多解釋。但在這里我們需要加入以下四種字符,<PAD>主要用來進(jìn)行字符補(bǔ)全,<EOS>和<GO>都是用在Decoder端的序列中,告訴解碼器句子的起始與結(jié)束,<UNK>則用來替代一些未出現(xiàn)過的詞或者低頻詞。

  • < PAD>: 補(bǔ)全字符。
  • < EOS>: 解碼器端的句子結(jié)束標(biāo)識(shí)符。
  • < UNK>: 低頻詞或者一些未遇到過的詞等。
  • < GO>: 解碼器端的句子起始標(biāo)識(shí)符。


通過上面步驟,我們可以得到轉(zhuǎn)換為數(shù)字后的源數(shù)據(jù)與目標(biāo)數(shù)據(jù)。

4. 模型構(gòu)建

Encoder

模型構(gòu)建主要包括Encoder層與Decoder層。在Encoder層,我們首先需要對(duì)定義輸入的tensor,同時(shí)要對(duì)字母進(jìn)行Embedding,再輸入到RNN層。

在這里,我們使用TensorFlow中的tf.contrib.layers.embed_sequence來對(duì)輸入進(jìn)行embedding。

我們來看一個(gè)栗子,假如我們有一個(gè)batch=2,sequence_length=5的樣本,features = [[1,2,3,4,5],[6,7,8,9,10]],使用

tf.contrib.layers.embed_sequence(features,vocab_size=n_words, embed_dim=10)

那么我們會(huì)得到一個(gè)2 x 5 x 10的輸出,其中features中的每個(gè)數(shù)字都被embed成了一個(gè)10維向量。

官方關(guān)于tf.contrib.layers.embed_sequence()的解釋如下:
Maps a sequence of symbols to a sequence of embeddings.
Typical use case would be reusing embeddings between an encoder and decoder.

Decoder

在Decoder端,我們主要要完成以下幾件事情:

  • 對(duì)target數(shù)據(jù)進(jìn)行處理
  • 構(gòu)造Decoder
    • Embedding
    • 構(gòu)造Decoder層
    • 構(gòu)造輸出層,輸出層會(huì)告訴我們每個(gè)時(shí)間序列的RNN輸出結(jié)果
    • Training Decoder
    • Predicting Decoder

下面我們會(huì)對(duì)這每個(gè)部分進(jìn)行一一介紹。


1. target數(shù)據(jù)處理

我們的target數(shù)據(jù)有兩個(gè)作用:

  • 在訓(xùn)練過程中,我們需要將我們的target序列作為輸入傳給Decoder端RNN的每個(gè)階段,而不是使用前一階段預(yù)測(cè)輸出,這樣會(huì)使得模型更加準(zhǔn)確。(這就是為什么我們會(huì)構(gòu)建Training和Predicting兩個(gè)Decoder的原因,下面還會(huì)有對(duì)這部分的解釋)。
  • 需要用target數(shù)據(jù)來計(jì)算模型的loss。


我們首先需要對(duì)target端的數(shù)據(jù)進(jìn)行一步預(yù)處理。在我們將target中的序列作為輸入給Decoder端的RNN時(shí),序列中的最后一個(gè)字母(或單詞)其實(shí)是沒有用的。我們來用下圖解釋:


我們此時(shí)只看右邊的Decoder端,可以看到我們的target序列是[<go>, W, X, Y, Z, <eos>],其中<go>,W,X,Y,Z是每個(gè)時(shí)間序列上輸入給RNN的內(nèi)容,我們發(fā)現(xiàn),<eos>并沒有作為輸入傳遞給RNN。因此我們需要將target中的最后一個(gè)字符去掉,同時(shí)還需要在前面添加<go>標(biāo)識(shí),告訴模型這代表一個(gè)句子的開始。


如上圖,所示,紅色和橙色為我們最終的保留區(qū)域,灰色是序列中的最后一個(gè)字符,我們把它刪掉即可。

我們使用tf.strided_slice()來進(jìn)行這一步處理。

其中tf.fill(dims, value)參數(shù)會(huì)生成一個(gè)dims形狀并用value填充的tensor。舉個(gè)栗子:tf.fill([2,2], 7) => [[7,7], [7,7]]。tf.concat()會(huì)按照某個(gè)維度將兩個(gè)tensor拼接起來。

2. 構(gòu)造Decoder

  • 對(duì)target數(shù)據(jù)進(jìn)行embedding。
  • 構(gòu)造Decoder端的RNN單元。
  • 構(gòu)造輸出層,從而得到每個(gè)時(shí)間序列上的預(yù)測(cè)結(jié)果。
  • 構(gòu)造training decoder。
  • 構(gòu)造predicting decoder。

注意,我們這里將decoder分為了training和predicting,這兩個(gè)encoder實(shí)際上是共享參數(shù)的,也就是通過training decoder學(xué)得的參數(shù),predicting會(huì)拿來進(jìn)行預(yù)測(cè)。那么為什么我們要分兩個(gè)呢,這里主要考慮模型的robust。

在training階段,為了能夠讓模型更加準(zhǔn)確,我們并不會(huì)把t-1的預(yù)測(cè)輸出作為t階段的輸入,而是直接使用target data中序列的元素輸入到Encoder中。而在predict階段,我們沒有target data,有的只是t-1階段的輸出和隱層狀態(tài)。

上面的圖中代表的是training過程。在training過程中,我們并不會(huì)把每個(gè)階段的預(yù)測(cè)輸出作為下一階段的輸入,下一階段的輸入我們會(huì)直接使用target data,這樣能夠保證模型更加準(zhǔn)確。

這個(gè)圖代表我們的predict階段,在這個(gè)階段,我們沒有target data,這個(gè)時(shí)候前一階段的預(yù)測(cè)結(jié)果就會(huì)作為下一階段的輸入。

當(dāng)然,predicting雖然與training是分開的,但他們是會(huì)共享參數(shù)的,training訓(xùn)練好的參數(shù)會(huì)供predicting使用。

decoder層的代碼如下:

構(gòu)建好了Encoder層與Decoder以后,我們需要將它們連接起來build我們的Seq2Seq模型。

定義超參數(shù)

# 超參數(shù) # Number of Epochs epochs = 60 # Batch Size batch_size = 128 # RNN Size rnn_size = 50 # Number of Layers num_layers = 2 # Embedding Size encoding_embedding_size = 15 decoding_embedding_size = 15 # Learning Rate learning_rate = 0.001

定義loss function、optimizer以及gradient clipping

目前為止我們已經(jīng)完成了整個(gè)模型的構(gòu)建,但還沒有構(gòu)造batch函數(shù),batch函數(shù)用來每次獲取一個(gè)batch的訓(xùn)練樣本對(duì)模型進(jìn)行訓(xùn)練。

在這里,我們還需要定義另一個(gè)函數(shù)對(duì)batch中的序列進(jìn)行補(bǔ)全操作。這是啥意思呢?我們來看個(gè)例子,假如我們定義了batch=2,里面的序列分別是

[['h', 'e', 'l', 'l', 'o'],['w', 'h', 'a', 't']]

那么這兩個(gè)序列的長度一個(gè)是5,一個(gè)是4,變長的序列對(duì)于RNN來說是沒辦法訓(xùn)練的,所以我們這個(gè)時(shí)候要對(duì)短序列進(jìn)行補(bǔ)全,補(bǔ)全以后,兩個(gè)序列會(huì)變成下面的樣子:

[['h', 'e', 'l', 'l', 'o'],['w', 'h', 'a', 't', '<PAD>']]

這樣就保證了我們每個(gè)batch中的序列長度是固定的。

感謝@Gang He提出的錯(cuò)誤。此處代碼已修正。修改部分為get_batches中的兩個(gè)for循環(huán),for target in targets_batch和for source in sources_batch(之前的代碼是for target in pad_targets_batch和for source in pad_sources_batch),因?yàn)槲覀冇胹equence_mask計(jì)算了每個(gè)句子的權(quán)重,該權(quán)重作為參數(shù)傳入loss函數(shù),主要用來忽略句子中pad部分的loss。如果是對(duì)pad以后的句子進(jìn)行l(wèi)oop,那么輸出權(quán)重都是1,不符合我們的要求。在這里做出修正。GitHub上代碼也已修改。

至此,我們完成了整個(gè)模型的構(gòu)建與數(shù)據(jù)的處理。接下來我們對(duì)模型進(jìn)行訓(xùn)練,我定義了batch_size=128,epochs=60。訓(xùn)練loss如下:

模型預(yù)測(cè)

我們通過實(shí)際的例子來進(jìn)行驗(yàn)證。

輸入“hello”:


輸入“machine”:

輸入“common”:


總結(jié)

至此,我們實(shí)現(xiàn)了一個(gè)基本的序列到序列模型,Encoder通過對(duì)輸入序列的學(xué)習(xí),將學(xué)習(xí)到的信息轉(zhuǎn)化為一個(gè)狀態(tài)向量傳遞給Decoder,Decoder再基于這個(gè)輸入得到輸出。除此之外,我們還知道要對(duì)batch中的單詞進(jìn)行補(bǔ)全保證一個(gè)batch內(nèi)的樣本具有相同的序列長度。

我們可以看到最終模型的訓(xùn)練loss相對(duì)已經(jīng)比較低了,并且從例子看,其對(duì)短序列的輸出還是比較準(zhǔn)確的,但一旦我們的輸入序列過長,比如15甚至20個(gè)字母的單詞,其Decoder端的輸出就非常的差。

完整代碼已上傳至GitHub。

轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán)。編輯于 2018-03-19「真誠贊賞,手留余香」贊賞

4 人已贊賞

深度學(xué)習(xí)(Deep Learning)文本處理神經(jīng)網(wǎng)絡(luò)?贊同 300??149 條評(píng)論?分享?收藏?

總結(jié)

以上是生活随笔為你收集整理的从Encoder到Decoder实现Seq2Seq模型的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。