seq2seq

seq2seq:python

seq2seq就是將輸入序列通過encoder-decoder變成目標序列。git

    

如圖所示,輸入序列是 [A, B, C, <EOS>],輸出序列是  [W, X, Y, Z, <EOS>]github

encoder-decoder:api

    主要過程就是用RNN對輸入序列進行編碼,而後再用RNN對上下文向量進行解碼。數組

實現方式:bash

一、tf.nn.dynamic_rnn函數

    參考:https://github.com/ematvey/tensorflow-seq2seq-tutorials/blob/master/1-seq2seq.ipynboop

    流程:this

    輸入序列: [A,B,C,EOS],其中A,B,C, EOS都要進行embedding,encoder部分的代碼以下所示:編碼

encoder_cell = tf.contrib.rnn.LSTMCell(encoder_hidden_units)

encoder_outputs, encoder_final_state = tf.nn.dynamic_rnn(
    encoder_cell, encoder_inputs_embedded,
    dtype=tf.float32, time_major=True,
)

 encoder_outputs是一個時間步的輸出,這個在decoder中用不到。encoder_final_stata是最後一層的輸出結果,encoder_final_state是一個二元組,(總體的記憶c,隱藏層狀態h),而後用encoder_final_state來初始化decoder的狀態,而decoder的輸入序列爲 [EOS, A, B, C],由於dynamic_rnn不能根據上一步的輸出來做爲當前的輸入,因此對於輸入來講是固定,而非動態變化的。

decoder_cell = tf.contrib.rnn.LSTMCell(decoder_hidden_units)

decoder_outputs, decoder_final_state = tf.nn.dynamic_rnn(
    decoder_cell, decoder_inputs_embedded,

    initial_state=encoder_final_state,

    dtype=tf.float32, time_major=True, scope="plain_decoder",
)

二、tf.nn.raw_rnn

這種方法不像dynamic_rnn那樣固定,它比較靈活,能夠經過迭代函數改變每個時間步的 輸入狀態、輸入。

offical document 

tf.nn.raw_rnn(
    cell, //基礎神經元
    loop_fn, //迭代函數,每次的狀態與輸入均可以在這裏定義
    parallel_iterations=None,
    swap_memory=False,
    scope=None
)
輸出:
(emit_ta, final_state, final_loop_state),其中emit_ta是TensorArray類型,其實就是每個時間步輸出的tensor的數組,final_state最後的狀態,final_loop_state這個好像是None,不知道啥做用

 實現步驟

總體:

decoder_outputs_ta, decoder_final_state, _ = tf.nn.raw_rnn(decoder_cell, loop_fn) //decoder_cell是基礎神經單元,loop_fn是迭代函數

迭代函數:

//迭代函數包含time, previous_output, previous_state, previous_loop_state(這個至關於LSTM中那個全局的記憶)
def loop_fn(time, previous_output, previous_state, previous_loop_state):
if previous_state is None: # time == 0, 初始化 assert previous_output is None and previous_state is None return loop_fn_initial() else: return loop_fn_transition(time, previous_output, previous_state, previous_loop_state) //在上一個時間步結束後,即將進入當前時間步時會執行該函數,目的就是肯定要將哪些內容傳給下一步做爲狀態輸入和輸入向量

初始化函數:

def loop_fn_initial():
    initial_elements_finished = (0 >= decoder_lengths)  # all False at the initial step
    initial_input = eos_step_embedded #第一步的輸入是EOS
    initial_cell_state = encoder_final_state #狀態輸入就是encoder的最終輸出狀態,包括(c,h)
    initial_cell_output = None
    initial_loop_state = None  # we don't need to pass any additional information
    return (initial_elements_finished,
            initial_input,
            initial_cell_state,
            initial_cell_output,
            initial_loop_state)

迭代函數:

def loop_fn_transition(time, previous_output, previous_state, previous_loop_state):
    #如何獲取上一步的輸出
yhat = softmax(previous_output * W + b)
而後機率最大的那個yhat即爲上一步的輸出結果,並對這個結果進行embedding,做爲下一步的輸入

def get_next_input(): output_logits
= tf.add(tf.matmul(previous_output, W), b) prediction = tf.argmax(output_logits, axis=1) next_input = tf.nn.embedding_lookup(embeddings, prediction) return next_input
#判斷是否中止,常數 >= tensor向量,tensor中每一個位置都要和常數進行比較,結果是一個布爾型的tensor向量 elements_finished
= (time >= decoder_lengths) # this operation produces boolean tensor of [batch_size] # defining if corresponding sequence has ended #由於這是一個batch塊,因此該batch完成的標誌是 全部的item都finish,因此須要reduce_all finished = tf.reduce_all(elements_finished) # -> boolean scalar
#當前步的輸入 = 上一步的輸出(get_next_input)
#tf.cond(條件,True時調用的函數, False時調用的函數) input
= tf.cond(finished, lambda: pad_step_embedded, get_next_input) state = previous_state #狀態不用改變直接傳過去 output = previous_output #previous_output也不用變,好像這個output是一個TensorArray吧? loop_state = None return (elements_finished, input, state, output, loop_state)

 調用過程:

decoder_outputs_ta, decoder_final_state, _ = tf.nn.raw_rnn(decoder_cell, loop_fn)

 這樣就實現了將上一步decoder出來的結果做爲下一步的輸入,真正實現上圖中的過程。

待補充Attention機制

 

 

參考:

https://github.com/ematvey/tensorflow-seq2seq-tutorials

https://hanxiao.github.io/2017/08/16/Why-I-use-raw-rnn-Instead-of-dynamic-rnn-in-Tensorflow-So-Should-You-0/

相關文章
相關標籤/搜索