標籤:

tensor flow dynamic_rnn 與rnn有啥區別?

最近看了一下rnn.py中的源碼。由於水平有限,沒有完全看懂,看到rnn函數和dynamic_rnn函數的時候,總感覺這兩函數沒啥區別,只是一個輸入是 list of tensor,另一個是tensor。而且dynamic並沒有想像的那麼dynamic,只是將填充的部分輸出為0。


在每一個train step,傳入model的是一個batch的數據(這一個batch的數據forward得到predictions,計算loss,backpropagation更新參數),這一個batch內的數據一定是padding成相同長度的。

那麼,如果可以只在一個batch內部進行padding,例如一個batch中數據長度均在6-10這個範圍內,就可以讓這個batch中所有數據pad到固定長度10,而整個dataset上的數據最大長度很可能是100,這樣就不需要讓這些數據也pad到100那麼長,白白浪費空間。

所以dynamic_rnn實現的功能就是可以讓不同迭代傳入的batch可以是長度不同數據,但同一次迭代一個batch內部的所有數據長度仍然是固定的。例如,第一時刻傳入的數據shape=[batch_size, 10],第二時刻傳入的數據shape=[batch_size, 12],第三時刻傳入的數據shape=[batch_size, 8]等等。

但是rnn不能這樣,它要求每一時刻傳入的batch數據的[batch_size, max_seq],在每次迭代過程中都保持不變。

這樣不就必須要求全部數據都要pad到統一的max_seq長度了嗎?是的,但也有個折中辦法。

——將數據集的sequence length做個初步統計,看會落在哪幾個區間段內。然後根據區間段將數據進行歸類,也就是所謂的放在不同buckets中。

最後用rnn為每一個buckets都創建一個sub graph。訓練的時候,根據當前batch data所歸屬的bucket id,找到它對應的sub graph,進行參數更新(雖然是不同的sub graph,但參數是共享的。至少tensorflow中是這麼實現的~(≧▽≦)/~)

具體可參看:https://github.com/tensorflow/tensorflow/blob/master/tensorflow/contrib/legacy_seq2seq/python/ops/seq2seq.py#L1143

另外,兩者的輸入形式確實不同,但你提到的「將填充的部分輸出為0」,給rnn傳入sequence_length這個參數後,也是可以的。


dynamic-rnn可以允許不同batch的sequence length不同,但rnn不能。這點 @王買買提 說的挺清楚了。

然後我認為 @elzyyzl 說到達sequence length之後終止計算不太對。

文檔里說 「sequence length is more for correctness than for performance」 原文

我理解的「correctness」 是到達指定length 時間步後,就返回此時沒有padding elements參與運算的hidden state,後續padding的計算就不會影響返回結果( copy-through state and zero-out outputs)---,但是後續padding的計算仍然確確實實發生了——由整個batch的 max time step決定,所以"performance"(計算速度)並沒有改善。

因此為了得到沒有padding污染的hidden state(LSTM,GRU會傾向於記住更多後面的信息,padding會對前向RNN的final state產生負面影響),需要傳入sequence被pad前的長度;而為了加快計算速度,還是需要對整個數據集根據長度做bucketing。


我從具體的實現上講一下。

調用static_rnn實際上是生成了rnn按時間序列展開之後的圖。打開tensorboard你會看到sequence_length個rnn_cell stack在一起,只不過這些cell是share weight的。因此,sequence_length就和圖的拓撲結構綁定在了一起,因此也就限制了每個batch的sequence_length必須是一致。

調用dynamic_rnn不會將rnn展開,而是利用tf.while_loop這個api,通過Enter, Switch, Merge, LoopCondition, NextIteration等這些control flow的節點,生成一個可以執行循環的圖(這個圖應該還是靜態圖,因為圖的拓撲結構在執行時是不會變化的)。在tensorboard上,你只會看到一個rnn_cell, 外面被一群control flow節點包圍著。對於dynamic_rnn來說,sequence_length僅僅代表著循環的次數,而和圖本身的拓撲沒有關係,所以每個batch可以有不同sequence_length。


tf.nn.rnn creates an unrolled graph for a fixed RNN length. That means, if you call tf.nn.rnn with inputs having 200 time steps you are creating a static graph with 200 RNN steps. First, graph creation is slow. Second, you』re unable to pass in longer sequences (&> 200) than you』ve originally specified.

tf.nn.dynamic_rnn solves this. It uses a tf.While loop to dynamically construct the graph when it is executed. That means graph creation is faster and you can feed batches of variable size.

轉載鏈接:Whats the difference between tensorflow dynamic_rnn and rnn?


@未來 代碼:

import tensorflow as tf

import numpy as np

import time

x = tf.placeholder(tf.int32, [None, 1000])

embedding = tf.get_variable("embedding", shape=[100, 256])

x_embedding = tf.nn.embedding_lookup(embedding,x)

source_sentence_length = tf.placeholder(tf.int32, [None])

encoder_cell = tf.nn.rnn_cell.BasicLSTMCell(num_units=256)

encoder_outputs, encoder_state = tf.nn.dynamic_rnn(encoder_cell, x_embedding, sequence_length=source_sentence_length, dtype=tf.float32)

with tf.Session() as sess:

tf.global_variables_initializer().run()

X_batch = np.random.randint(0, 100, size=[512, 1000])

time0 = time.time()

for i in range(100):

encoder_outputs.eval(feed_dict={x: X_batch, source_sentence_length: [10]*512})

time1 = time.time()

print("sequence_length_10, time: %.9f" % (time1-time0))

time2 = time.time()

for i in range(100):

encoder_outputs.eval(feed_dict={x: X_batch, source_sentence_length: [1000]*512})

time3 = time.time()

print("sequence_length_1000, time: %.9f" % (time3-time2))

result:

sequence_length_10, time: 41.683850050

sequence_length_1000, time: 41.620696306

sequence_length 參數不會影響計算效率


" 但同一時刻一個batch內部的所有數據長度仍然是固定的。例如,第一時刻傳入的數據shape=[batch_size, 10],第二時刻傳入的數據shape=[batch_size, 12],第三時刻傳入的數據shape=[batch_size, 8]等等 "這句話看糊塗了。

參考RNNs in Tensorflow, a Practical Guide and Undocumented Features 會更清楚些。我理解不論dynamic_rnn還是static_rnn(1.0版本沒有tf.nn.rnn了),每個batch的序列長度都是一樣的(不足的話自己要去padding),不同的是dynamic會根據 sequence_length 中止計算。另外一個不同是dynamic_rnn動態生成graph


推薦閱讀:

怎樣在tensorflow中使用batch normalization?
tensorflow如何訓練自己的圖像數據?
關於Tensorflow的一些想法?
tensorflow中的tensorboard可視化中的準確率損失率曲線,為什麼有類似毛刺一樣?
Tensorflow 中怎麼定義自己的層呢?

TAG:TensorFlow |