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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

从Encoder到Decoder实现Seq2Seq模型

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

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

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

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

前言

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

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


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

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


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

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

版本信息:Python 3 / TensorFlow 1.1

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

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

- source_data: 每一行是一個單詞

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

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

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

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

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

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

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


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

4. 模型構(gòu)建

Encoder

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

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

我們來看一個栗子,假如我們有一個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)

那么我們會得到一個2 x 5 x 10的輸出,其中features中的每個數(shù)字都被embed成了一個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端,我們主要要完成以下幾件事情:

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

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


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

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

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


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


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


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

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

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

2. 構(gòu)造Decoder

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

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

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

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

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

當(dāng)然,predicting雖然與training是分開的,但他們是會共享參數(shù)的,training訓(xùn)練好的參數(shù)會供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òu)建,但還沒有構(gòu)造batch函數(shù),batch函數(shù)用來每次獲取一個batch的訓(xùn)練樣本對模型進(jìn)行訓(xùn)練。

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

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

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

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

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

感謝@Gang He提出的錯誤。此處代碼已修正。修改部分為get_batches中的兩個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ì)算了每個句子的權(quán)重,該權(quán)重作為參數(shù)傳入loss函數(shù),主要用來忽略句子中pad部分的loss。如果是對pad以后的句子進(jìn)行l(wèi)oop,那么輸出權(quán)重都是1,不符合我們的要求。在這里做出修正。GitHub上代碼也已修改。

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

模型預(yù)測

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

輸入“hello”:


輸入“machine”:

輸入“common”:


總結(jié)

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

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

完整代碼已上傳至GitHub。

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

4 人已贊賞

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

總結(jié)

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

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