1. 程式人生 > >keras系列︱seq2seq系列相關實現與案例(feedback、peek、attention類型)

keras系列︱seq2seq系列相關實現與案例(feedback、peek、attention類型)

課堂 簡單實現 htm 構建 氣象 use 然而 示例 tps

之前在看《Semi-supervised Sequence Learning》這篇文章的時候對seq2seq半監督的方式做文本分類的方式產生了一定興趣,於是開始簡單研究了seq2seq。先來簡單說一下這篇paper的內容:
創立的新形式Sequence AutoEncoder LSTM(SA-LSTM),Pre-trained RNNs are more stable, generalize better, and achieve state-of-the-art results on various text classification tasks. The authors show that unlabeled data can compensate for a lack of labeled data(來源dennybritz/deeplearning-papernotes).
技術分享

創新之處:

  • (1)第一種模型Sequence autoencoder(SA-LSTM):The objective is to reconstruct the input sequence itself,其中output序列就是Input序列,輸出的結果作為下一個LSTM的初始值
  • (2)第二種模型稱為Language Model LSTM(LM-LSTM),encoder部分去掉就是LM模型。
  • (3)jointly training模式.一般用LSTM中的最後一個hidden state作為輸出,但本文也嘗試用了每個hidden state權重遞增的線性組合作為輸出。
    這兩種思路都是將無監督和有監督分開訓練,本文也提供了一種聯合訓練的思路作為對比,稱為joint learning。

優勢:

  • (1)unsupervised,不用標簽
  • (2)large quantities of unlabeled data to improve its quality,不用標簽之外,用大量無標註數據反而還可以增強模型的泛化能力
  • (3)相較於LSTM的隨機初始化,可以fine-tuning過來別的整個部分的weight作為初始化權重

想要閱讀的童鞋可看原版論文 + 一份大牛的解讀,當然作者好像沒開源他的做法以及實驗詳細數據,筆者折騰了幾天沒做出效果也就放棄了….
.


一、seq2seq幾類常見架構

不累述seq2seq的原理,來看看《漫談四種神經網絡序列解碼模型【附示例代碼】》中總結的四類:

1、模式一:普通作弊 basic encoder-decoder

編碼時RNN每個時刻除了自己上一時刻的隱層狀態編碼外,還有當前時刻的輸入字符,而解碼時則沒有這種輸入。那麽,一種比較直接的方式是把編碼端得到的編碼向量做為解碼模型的每時刻輸入特征。如下圖所示:
技術分享
簡單直觀而且解碼模型和編碼模型並沒有任何區別
.

2、模式二: 學霸模式 encoder-decoder with feedback

編碼端則是對課本的理解所整理的課堂筆記。解碼端的隱層神經網絡則是我們的大腦,而每一時刻的輸出則是考試時要寫在卷子上的答案。在上面最簡單的解碼模型中,可以考慮成是考試時一邊寫答案一邊翻看課堂筆記。如果這是一般作弊學生的做法,學霸則不需要翻書,他們有一個強大的大腦神經網絡,可以記住自己的課堂筆記。解碼時只需要回顧一下自己前面寫過什麽,然後依次認真的把答案寫在答卷上,就是下面這種模型了:
技術分享
.

3、模式三:學弱作弊 encoder-decoder with peek

很多學弱,他們不只需要作弊,而且翻看筆記的時候還需要回顧自己上一時刻寫在答卷上的答案
技術分享
.

4、模式四:學渣作弊 encoder-decoder with attention

然而學渣渣也是存在的,他們不只需要作弊,不只需要回顧自己上一時刻卸載答卷上的答案,還需要老師在課本上畫出重點才能整理出自己的課題筆記(這就是一種註意力機制Attention,記筆記的時候一定要根據考題畫出重點啊!)
技術分享

5、結果對比

設定一些參數如下:
(‘Vocab size:’, 51, ‘unique words’)
(‘Input max length:’, 5, ‘words’)
(‘Target max length:’, 5, ‘words’)
(‘Dimension of hidden vectors:’, 20)
(‘Number of training stories:’, 5)
(‘Number of test stories:’, 5)
技術分享
.


二、seq2seq的實現

1、四類seq2seq實現-encoder_decoder

上述文章 《漫談四種神經網絡序列解碼模型【附示例代碼】》中總結的四類的實現在作者的github之中,由於作者用keras0.3做的,筆者在實踐過程中遇到很多坑,而且py2與py3之間都會有各自的問題,所以這邊只貼其輸入輸出的數據:

輸入數據:

input_list

[[‘1‘, ‘2‘, ‘3‘, ‘4‘, ‘5‘], [‘6‘, ‘7‘, ‘8‘, ‘9‘, ‘10‘], [‘11‘, ‘12‘, ‘13‘, ‘14‘, ‘15‘], [‘16‘, ‘17‘, ‘18‘, ‘19‘, ‘20‘], [‘21‘, ‘22‘, ‘23‘, ‘24‘, ‘25‘]]
  • 1
  • 1

tar_list

[[‘one‘, ‘two‘, ‘three‘, ‘four‘, ‘five‘], [‘six‘, ‘seven‘, ‘eight‘, ‘nine‘, ‘ten‘], [‘eleven‘, ‘twelve‘, ‘thirteen‘, ‘fourteen‘, ‘fifteen‘], [‘sixteen‘, ‘seventeen‘, ‘eighteen‘, ‘nineteen‘, ‘twenty‘], [‘twenty_one‘, ‘twenty_two‘, ‘twenty_three‘, ‘twenty_four‘, ‘twenty_five‘]]
  • 1
  • 1

訓練輸入數據【en_de_model.fit(inputs_train, tars_train, batch_size=3, nb_epoch=1, show_accuracy=True)】:

inputs_train:5*5

array([[ 1, 12, 19, 20, 21],
       [22, 23, 24, 25,  2],
       [ 3,  4,  5,  6,  7],
       [ 8,  9, 10, 11, 13],
       [14, 15, 16, 17, 18]], dtype=int32)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

tars_train:5*5*51(51為單詞個數 + 1)

array([[[False, False, False, ..., False, False, False],
        [False, False, False, ..., False, False,  True],
        [False, False, False, ..., False, False, False],
        [False, False, False, ..., False, False, False],
        [False, False, False, ..., False, False, False]],

       [[False, False, False, ..., False, False, False],
        [False, False, False, ..., False, False, False],
        [False, False, False, ..., False, False, False],
        [False, False, False, ..., False, False, False],
        [False, False, False, ..., False, False, False]],

       [[False, False, False, ..., False, False, False],
        [False, False, False, ..., False, False, False],
        [False, False, False, ..., False, False, False],
        [False, False, False, ..., False, False, False],
        [False, False, False, ..., False, False, False]],

       [[False, False, False, ..., False, False, False],
        [False, False, False, ..., False, False, False],
        [False, False, False, ..., False, False, False],
        [False, False, False, ..., False, False, False],
        [False, False, False, ..., False, False, False]],

       [[False, False, False, ..., False, False, False],
        [False, False, False, ..., False,  True, False],
        [False, False, False, ...,  True, False, False],
        [False, False, False, ..., False, False, False],
        [False, False, False, ..., False, False, False]]], dtype=bool)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

來看一個完整的keras0.3版本的code:

decoder_mode = 1  # 0 最簡單模式,1 [1]向後模式,2 [2] Peek模式,3 [3]Attention模式
# encoder部分
if decoder_mode == 3:
    encoder_top_layer = LSTM(hidden_dim, return_sequences=True)
else:
    encoder_top_layer = LSTM(hidden_dim)
# decoder部分
if decoder_mode == 0:
    decoder_top_layer = LSTM(hidden_dim, return_sequences=True)
    decoder_top_layer.get_weights()
elif decoder_mode == 1:
    decoder_top_layer = LSTMDecoder(hidden_dim=hidden_dim, output_dim=hidden_dim
                                    , output_length=tar_maxlen, state_input=False, return_sequences=True)
elif decoder_mode == 2:
    decoder_top_layer = LSTMDecoder2(hidden_dim=hidden_dim, output_dim=hidden_dim
                                     , output_length=tar_maxlen, state_input=False, return_sequences=True)
elif decoder_mode == 3:
    decoder_top_layer = AttentionDecoder(hidden_dim=hidden_dim, output_dim=hidden_dim
                                         , output_length=tar_maxlen, state_input=False, return_sequences=True)

#  模型構建
en_de_model = Sequential()
en_de_model.add(Embedding(input_dim=vocab_size,
                          output_dim=hidden_dim,
                          input_length=input_maxlen))
en_de_model.add(encoder_top_layer)
if decoder_mode == 0:
    en_de_model.add(RepeatVector(tar_maxlen))
en_de_model.add(decoder_top_layer)

en_de_model.add(TimeDistributedDense(output_dim))
en_de_model.add(Activation(‘softmax‘))
en_de_model.compile(loss=‘categorical_crossentropy‘, optimizer=‘rmsprop‘)
en_de_model.fit(inputs_train, tars_train, batch_size=3, nb_epoch=1, show_accuracy=True)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

其中遇到的報錯:

from keras import activations, initializers     # py3要這麽寫
from keras import activations, initializations # py2要這麽寫
  • 1
  • 2
  • 1
  • 2
TypeError: build() takes exactly 1 argument (2 given) # py2無此報錯
  • 1
  • 1

.

2、keras實現farizrahman4u/seq2seq

本github裏面用Keras做的seq2seq封裝比較好,使用的方法有點類似上述的模式二

其中有5款seq2seq款式可以選擇:

(1)A simple Seq2Seq model

import seq2seq
from seq2seq.models import SimpleSeq2Seq

model = SimpleSeq2Seq(input_dim=5, hidden_dim=10, output_length=8, output_dim=8)
model.compile(loss=‘mse‘, optimizer=‘rmsprop‘)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

(2)Deep Seq2Seq models

depth=3(效果為3 + 3 = 6)或者也可設置深度為(4, 5)


import seq2seq
from seq2seq.models import SimpleSeq2Seq

model = SimpleSeq2Seq(input_dim=5, hidden_dim=10, output_length=8, output_dim=8, depth=3)
model.compile(loss=‘mse‘, optimizer=‘rmsprop‘)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

(3)Advanced Seq2Seq models

import seq2seq
from seq2seq.models import Seq2Seq

model = Seq2Seq(batch_input_shape=(16, 7, 5), hidden_dim=10, output_length=8, output_dim=20, depth=4)
model.compile(loss=‘mse‘, optimizer=‘rmsprop‘)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

(4)Peeky Seq2seq model

the decoder gets a ‘peek’ at the context vector at every timestep.
打開peek=True,類似於上述的模式三
技術分享

import seq2seq
from seq2seq.models import Seq2Seq

model = Seq2Seq(batch_input_shape=(16, 7, 5), hidden_dim=10, output_length=8, output_dim=20, depth=4, peek=True)
model.compile(loss=‘mse‘, optimizer=‘rmsprop‘)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

(5)AttentionSeq2Seq

類似於模式四,帶註意力機制
技術分享

import seq2seq
from seq2seq.models import AttentionSeq2Seq

model = AttentionSeq2Seq(input_dim=5, input_length=7, hidden_dim=10, output_length=8, output_dim=20, depth=4)
model.compile(loss=‘mse‘, optimizer=‘rmsprop‘)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

來看一個案例:

def test_Seq2Seq():
    x = np.random.random((samples, input_length, input_dim))
    y = np.random.random((samples, output_length, output_dim))

    models = []
    models += [Seq2Seq(output_dim=output_dim, hidden_dim=hidden_dim, output_length=output_length, input_shape=(input_length, input_dim))]
    models += [Seq2Seq(output_dim=output_dim, hidden_dim=hidden_dim, output_length=output_length, input_shape=(input_length, input_dim), peek=True)]
    models += [Seq2Seq(output_dim=output_dim, hidden_dim=hidden_dim, output_length=output_length, input_shape=(input_length, input_dim), depth=2)]
    models += [Seq2Seq(output_dim=output_dim, hidden_dim=hidden_dim, output_length=output_length, input_shape=(input_length, input_dim), peek=True, depth=2)]

    for model in models:
        model.compile(loss=‘mse‘, optimizer=‘sgd‘)
        model.fit(x, y, epochs=1)

    model = Seq2Seq(output_dim=output_dim, hidden_dim=hidden_dim, output_length=output_length, input_shape=(input_length, input_dim), peek=True, depth=2, teacher_force=True)
    model.compile(loss=‘mse‘, optimizer=‘sgd‘)
    model.fit([x, y], y, epochs=1)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

其中遇到以下報錯:

執行:
SimpleSeq2Seq(Input(shape=(5,), dtype=‘int32‘), hidden_dim=10, output_length=8, output_dim=8)

報錯:
/home/amax/.local/lib/python2.7/site-packages/keras/engine/topology.py:1513: UserWarning: Model inputs must come from a Keras Input layer, 
            they cannot be the output of a previous non-Input layer. Here, 
            a tensor specified as input to "model_86" was not an Input tensor, it was generated by layer dropout_17.
Note that input tensors are instantiated via `tensor = Input(shape)`.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

其中dropout設置不正確,加入dropout=0.3就可以執行

ValueError: Shape must be rank 2 but is rank 3 for ‘lambda_272/MatMul‘ (op: ‘MatMul‘) with input shapes: [?,2], [?,2,2].
  • 1
  • 1

筆者之前一直用py2,改用了py3後就無報錯了

.

3、keras自實現seq2seq:Pig Latin——Linusp/soph

本節參考博文《使用 Keras 實現簡單的 Sequence to Sequence 模型》 相關code可見:github
train_x 和 train_y 必須是 3-D 的數據

直接上案例:

from keras.models import Sequential
from keras.layers.recurrent import LSTM
from keras.layers.wrappers import TimeDistributed
from keras.layers.core import Dense, RepeatVector


def build_model(input_size, seq_len, hidden_size):
    """建立一個 sequence to sequence 模型"""
    model = Sequential()
    model.add(GRU(input_dim=input_size, output_dim=hidden_size, return_sequences=False))
    model.add(Dense(hidden_size, activation="relu"))
    model.add(RepeatVector(seq_len))
    model.add(GRU(hidden_size, return_sequences=True))
    model.add(TimeDistributed(Dense(output_dim=input_size, activation="linear")))
    model.compile(loss="mse", optimizer=‘adam‘)

    return model
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • Encoder(即第一個 LSTM) 只在序列結束時輸出一個語義向量,所以其 “return_sequences” 參數設置為 “False”
  • Decoder(即第二個 LSTM) 需要在每一個 time step 都輸出,所以其 “return_sequences” 參數設置為 “True”
  • 使用 “RepeatVector” 將 Encoder 的輸出(最後一個 time step)復制 N 份作為 Decoder 的 N 次輸入
  • TimeDistributed 是為了保證 Dense 和 Decoder 之間的一致,可以不用太關心

之所以說是 “簡單的 seq2seq 模型”,就在於第 3 點其實並不符合兩篇論文的模型要求,不過要將 Decoder 的每一個時刻的輸出作為下一個時刻的輸入,會麻煩很多,所以這裏對其進行簡化,但用來處理 Pig Latin 這樣的簡單問題,這種簡化問題是不大的。

另外,雖然 seq2seq 模型在理論上是能學習 “變長輸入序列-變長輸出序列” 的映射關系,但在實際訓練中,Keras 的模型要求數據以 Numpy 的多維數組形式傳入,這就要求訓練數據中每一條數據的大小都必須是一樣的。針對這個問題,現在的常規做法是設定一個最大長度,對於長度不足的輸入以及輸出序列,用特殊的符號進行填充,使所有輸入序列的長度保持一致(所有輸出序列長度也一致)。
.


三、tensorflow實現seq2seq的相關案例

1、seq2seq簡單實現

相關github: https://github.com/ichuang/tflearn_seq2seq
來簡單看看實現:
輸入:0 1 2 3 4 5 6 7 8 9
輸出:prediction=[8 8 5 3 6 5 4 2 3 1] (expected=[9 8 7 6 5 4 3 2 1 0])

[TFLearnSeq2Seq] model weights loaded from t2s__basic__sorted_1.tfl
==> For input [9, 8, 7, 6, 5, 4, 3, 2, 1, 2, 5, 1, 8, 7, 7, 3, 9, 1, 4, 6], prediction=[1 1 1 2 2 3 3 4 4 5 5 6 6 7 7 7 7 8 8 8] (expected=[1 1 1 2 2 3 3 4 4 5 5 6 6 7 7 7 8 8 9 9])
  • 1
  • 2
  • 1
  • 2

2、自動標題生成案例

tensorflow實現的中文自動標題生成案例可見:https://github.com/rockingdingo/deepnlp/tree/master/deepnlp/textsum
textsum基於tensorflow (1.0.0) 實現的Seq2Seq-attention模型, 來解決中文新聞標題自動生成的任務。

Example:
技術分享

Output examples

news:      中央 氣象臺 TAG_DATE TAG_NUMBER 時 繼續 發布 暴雨 藍色 預警 TAG_NAME_EN 預計 TAG_DATE TAG_NUMBER 時至 TAG_DATE TAG_NUMBER 時 TAG_NAME_EN 內蒙古 東北部 、 山西 中 北部 、 河北 中部 和 東北部 、 京津 地區 、 遼寧 西南部 、 吉林 中部 、 黑龍江 中部 偏南 等 地 的 部分 地區 有 大雨 或 暴雨 。
headline:  中央 氣象臺 發布 暴雨 藍色 預警 華北 等 地 持續 暴雨

news:      美國 科羅拉多州 山林 大火 持續 肆虐 TAG_NAME_EN 當地 時間 TAG_DATE 橫掃 州 內 第二 大 城市 科羅拉多斯 普林斯 一 處 居民區 TAG_NAME_EN 迫使 超過 TAG_NUMBER TAG_NAME_EN TAG_NUMBER 萬 人 緊急 撤離 。 美國 正 值 山火 多發 季 TAG_NAME_EN 現有 TAG_NUMBER 場 山火 處於 活躍 狀態 。
headline:  美國 多地 山火 致 TAG_NUMBER 人 死亡

keras系列︱seq2seq系列相關實現與案例(feedback、peek、attention類型)