1. 程式人生 > >第二十一節,使用TensorFlow實現LSTM和GRU網絡

第二十一節,使用TensorFlow實現LSTM和GRU網絡

進行 初始 引入 turn lean tuple inf deep can

本節主要介紹在TensorFlow中實現LSTM以及GRU網絡。

關於LSTM的詳細內容推薦閱讀以下博客:

  • LSTM模型與前向反向傳播算法
  • 深度學習筆記(五):LSTM
  • tensorflow筆記:多層LSTM代碼分析

一 LSTM網絡

Long Short Term 網絡—— 一般就叫做 LSTM ——是一種 RNN 特殊的類型,可以學習長期依賴信息。LSTM 由 Hochreiter & Schmidhuber (1997) 提出,並在近期被 Alex Graves 進行了改良和推廣。在很多問題,LSTM 都取得相當巨大的成功,並得到了廣泛的使用。

LSTM 通過刻意的設計來避免長期依賴問題。記住長期的信息在實踐中是 LSTM 的默認行為,而非需要付出很大代價才能獲得的能力!

LSTM的結構如下:

技術分享圖片

這種結構的核心思想是引入了一個叫做細胞狀態的連接,這個細胞狀態用來存放想要記憶的東西。同時在裏面加入了三個門:

  • 忘記門;顧名思義,是控制是否遺忘的,在LSTM中即以一定的概率控制是否遺忘上一層的隱藏細胞狀態。
  • 輸入門:輸入門(input gate)負責處理當前序列位置的輸入.
  • 輸出門:決定什麽時候需要把狀態和輸出放在一起輸出。

二 LSTM 的變體

上面我們介紹了正常的 LSTM。但是不是所有的 LSTM 都長成一個樣子的。實際上,幾乎所有包含 LSTM 的論文都采用了微小的變體。差異非常小,但是也值得拿出來講一下。

1.窺視孔連接(peephole )

其中一個流形的 LSTM 變體,就是由 Gers & Schmidhuber (2000) 提出的,增加了 “peephole connection”。是說,我們讓 門層 也會接受細胞狀態的輸入。

技術分享圖片

上面的圖例中,我們增加了 peephole 到每個門上,但是許多論文會加入部分的 peephole 而非所有都加。

2.oupled 忘記門和輸入門

另一個變體是通過使用 coupled 忘記和輸入門。不同於之前是分開確定什麽忘記和需要添加什麽新的信息,這裏是一同做出決定。我們僅僅會當我們將要輸入在當前位置時忘記。我們僅僅輸入新的值到那些我們已經忘記舊的信息的那些狀態 。

技術分享圖片

3.GRU

另一個改動較大的變體是 Gated Recurrent Unit (GRU),這是由 Cho, et al. (2014) 提出。它將忘記門和輸入門合成了一個單一的 更新門。同樣還混合了細胞狀態和隱藏狀態,和其他一些改動。最終的模型比標準的 LSTM 模型要簡單,也是非常流行的變體。由於GRU比LSTM少了一個狀態輸出,效果幾乎一樣,因此在編碼使用時使用GRU可以讓代碼更為簡單一些。

技術分享圖片

這裏只是部分流行的 LSTM 變體。當然還有很多其他的,如 Yao, et al. (2015) 提出的 Depth Gated RNN。還有用一些完全不同的觀點來解決長期依賴的問題,如 Koutnik, et al. (2014) 提出的 Clockwork RNN。

要問哪個變體是最好的?其中的差異性真的重要嗎? Greff, et al. (2015) 給出了流行變體的比較,結論是他們基本上是一樣的。 Jozefowicz, et al. (2015) 則在超過 1 萬中 RNN 架構上進行了測試,發現一些架構在某些任務上也取得了比 LSTM 更好的結果。

三 Bi-RNN網絡介紹

Bi-RNN又叫雙向RNN,是采用了兩個方向的RNN網絡。

RNN網絡擅長的是對於連續數據的處理,既然是連續的數據規律,我們不僅可以學習他的正向規律,還可以學習他的反向規律。這樣正向和反向結合的網絡,回比單向循環網絡有更高的擬合度。

雙向RNN的處理過程和單向RNN非常相似,就是在正向傳播的基礎上再進行一次反向傳播,而且這兩個都連接這一個輸出層。

四 TensorFlow中cell庫

TensorFlow中定義了5個關於cell的類,cell我們可以理解為DNN中的一個隱藏層,只不過是一個比較特殊的層。如下

1.BasicRNNCell類

最基本的RNN類實現:

  def __init__(self, num_units, activation=None, reuse=None)
  • num_units:LSTM網絡單元的個數,也即隱藏層的節點數。
  • activation: Nonlinearity to use. Default: `tanh`.
  • reuse:(optional) Python boolean describing whether to reuse variables in an existing scope. If not `True`, and the existing scope already has the given variables, an error is raised.

2.BasicLSTMCell類

LSTM網絡:

def __init__(self, num_units, forget_bias=1.0, state_is_tuple=True, activation=None, reuse=None):
  • num_units:LSTM網絡單元的個數,也即隱藏層的節點數。
  • forget_bias:添加到忘記門的偏置。
  • state_is_tuple:由於細胞狀態ct和輸出ht是分開的,當為True時放在一個tuple中,(c=array([[]]),h=array([[]])),當為False時兩個值就按列連接起來,成為[batch,2n],建議使用True。
  • activation: Activation function of the inner states. Default: `tanh`.
  • reuse: (optional) Python boolean describing whether to reuse variables in an existing scope. If not `True`, and the existing scope already has the given variables, an error is raised. 在一個scope中是否重用。

3.LSTMCell類

LSTM實現的一個高級版本。

def __init__(self, num_units,
use_peepholes=False, cell_clip=None,
initializer=None, num_proj=None, proj_clip=None,
num_unit_shards=None, num_proj_shards=None,
forget_bias=1.0, state_is_tuple=True,
activation=None, reuse=None):
  • num_units:LSTM網絡單元的個數,也即隱藏層的節點數。
  • use_peepholes:默認False,True表示啟用Peephole連接。
  • cell_clip:是否在輸出前對cell狀態按照給定值進行截斷處理。
  • initializer: (optional) The initializer to use for the weight and projection matrices.
  • num_proj: (optional) int, The output dimensionality for the projection matrices. If None, no projection is performed.通過projection層進行模型壓縮的輸出維度。
  • proj_clip: (optional) A float value. If `num_proj > 0` and `proj_clip` is provided, then the projected values are clipped elementwise to within `[-proj_clip, proj_clip]`.將num_proj按照給定的proj_clip截斷。
  • num_unit_shards: Deprecated, will be removed by Jan. 2017. Use a variable_scope partitioner instead.
  • num_proj_shards: Deprecated, will be removed by Jan. 2017. Use a variable_scope partitioner instead.
  • forget_bias: Biases of the forget gate are initialized by default to 1 in order to reduce the scale of forgetting at the beginning of the training.
  • state_is_tuple: If True, accepted and returned states are 2-tuples of the `c_state` and `m_state`. If False, they are concatenated along the column axis. This latter behavior will soon be deprecated.
  • activation: Activation function of the inner states. Default: `tanh`.
  • reuse: (optional) Python boolean describing whether to reuse variables in an existing scope. If not `True`, and the existing scope already has the given variables, an error is raised.

4.GRU類

  def __init__(self,
               num_units,
               activation=None,
               reuse=None,
               kernel_initializer=None,
               bias_initializer=None):
  • num_units:GRU網絡單元的個數,也即隱藏層的節點數。
  • activation: Nonlinearity to use. Default: `tanh`.
  • reuse:(optional) Python boolean describing whether to reuse variables in an existing scope. If not `True`, and the existing scope already has the given variables, an error is raise

5.MultiRNNCell

多層RNN的實現:

def __init__(self, cells, state_is_tuple=True)
  • cells: list of RNNCells that will be composed in this order. 一個cell列表。將列表中的cell一個個堆疊起來,如果使用cells=[cell1,cell2],就是一共有2層,數據經過cell1後還要經過cells。
  • state_is_tuple: If True, accepted and returned states are n-tuples, where `n = len(cells)`. If False, the states are all concatenated along the column axis. This latter behavior will soon be deprecated.如果是True則返回的是n-tuple,即cell的輸出值與cell的輸出狀態組成了一個元組。其中輸出值和輸出狀態的結構均為[batch,num_units]。

五 通過cell類構建RNN

定義好cell類之後,還需要將它們連接起來構成RNN網絡,TensorFlow中有幾種現成的構建網絡模式,是封裝好的函數,直接調用即可:

1.靜態RNN構建

def tf.contrib.rnn.static_rnn(cell, inputs, initial_state=None, dtype=None, sequence_length=None, scope=None):
  • cell:生成好的cell類對象。
  • inputs:A length T list of inputs, each a `Tensor` of shape `[batch_size, input_size]`, or a nested tuple of such elements.輸入數據,由張量組成的list。list的順序就是時間順序。元素就是每個序列的值,形狀為[batch_size,input_size]。
  • initial_state: (optional) An initial state for the RNN. If `cell.state_size` is an integer, this must be a `Tensor` of appropriate type and shape `[batch_size, cell.state_size]`. If `cell.state_size` is a tuple, this should be a tuple of tensors having shapes `[batch_size, s] for s in cell.state_size`.初始化cell狀態。
  • dtype: (optional) The data type for the initial state and expected output. Required if initial_state is not provided or RNN state has a heterogeneous。期望輸出和初始化state的類型。
  • sequence_length: Specifies the length of each sequence in inputs. An int32 or int64 vector (tensor) size `[batch_size]`, values in `[0, T)`.每一個輸入的序列長度。
  • scope: VariableScope for the created subgraph; defaults to "rnn".命名空間

返回值有兩個,一個是輸出結果,一個是cell狀態。我們只關註結果,結果也是一個list,輸入是多少個時序,list裏面就會有多少個元素。每個元素大小為[batch_size,num_units]。

註意:在輸入時,一定要將我們習慣使用的張量改為由張量組成的list。另外,在得到輸出時也要去最後一個時序的輸出參與後面的運算。

2.動態RNN構建

def tf.nn.dynamic_rnn(cell, inputs, sequence_length=None, 
    initial_state=None, dtype=None, parallel_iterations=None,
swap_memory=False, time_major=False, scope=None):
  • cell:生成好的cell類對象。
  • inputs:If `time_major == False` (default), this must be a `Tensor` of shape:`[batch_size, max_time, ...]`, or a nested tuple of such elements. If `time_major == True`, this must be a `Tensor` of shape: `[max_time, batch_size, ...]`, or a nested tuple of such elements. 輸入數據,是一個張量,默認是三維張量,[batch_size,max_time,...],batch_size表示一個批次數量,max_time:表示時間序列總數,後面是一個時序輸入數據的長度。
  • sequence_length: Specifies the length of each sequence in inputs. An int32 or int64 vector (tensor) size `[batch_size]`, values in `[0, T)`.每一個輸入的序列長度。
  • initial_state: (optional) An initial state for the RNN.If `cell.state_size` is an integer, this must be a `Tensor` of appropriate type and shape `[batch_size, cell.state_size]`. If `cell.state_size` is a tuple, this should be a tuple of tensors having shapes `[batch_size, s] for s in cell.state_size`.初始化cell狀態。
  • dtype:期望輸出和初始化state的類型。
  • parallel_iterations: (Default: 32). The number of iterations to run inparallel. Those operations which do not have any temporal dependency and can be run in parallel, will be. This parameter trades off time for space. Values >> 1 use more memory but take less time, while smaller values use less memory but computations take longer.
  • swap_memory: Transparently swap the tensors produced in forward inference but needed for back prop from GPU to CPU. This allows training RNNs which would typically not fit on a single GPU, with very minimal (or no) performance penalty.
  • time_major: The shape format of the `inputs` and `outputs` Tensors. If true, these `Tensors` must be shaped `[max_time, batch_size, depth]`. If false, these `Tensors` must be shaped `[batch_size, max_time, depth]`. Using `time_major = True` is a bit more efficient because it avoids transposes at the beginning and end of the RNN calculation. However, most TensorFlow data is batch-major, so by default this function accepts input and emits output in batch-major form.
  • scope: VariableScope for the created subgraph; defaults to "rnn".命名空間。

返回值:一個是結果,一個是cell狀態。

A pair (outputs, state) where:

outputs: The RNN output `Tensor`.

If time_major == False (default), this will be a `Tensor` shaped: `[batch_size, max_time, cell.output_size]`.

If time_major == True, this will be a `Tensor` shaped: `[max_time, batch_size, cell.output_size]`.

Note, if `cell.output_size` is a (possibly nested) tuple of integers or `TensorShape` objects, then `outputs` will be a tuple having the same structure as `cell.output_size`, containing Tensors having shapes
corresponding to the shape data in `cell.output_size`.

state: The final state. If `cell.state_size` is an int, this will be shaped `[batch_size, cell.state_size]`. If it is a `TensorShape`, this will be shaped `[batch_size] + cell.state_size`. If it is a (possibly nested) tuple of ints or `TensorShape`, this will be a tuple having the corresponding shapes.

由於time_major默認值是False,所以結果是以[batch_size,max_time,...]形式的張量。

註意:在輸出時如果是以[batch_size,max_time,...]形式,即批次優先的矩陣,因為我們需要取最後一個時序的輸出,所以需要轉置成時間優先的形式。

outputs = tf.transpose(outputs,[1,0,2])

3.雙向RNN構建

雙向RNN作為一個可以學習正反規律的循環神經網絡,在TensorFlow中有4個函數可以使用。

第二十一節,使用TensorFlow實現LSTM和GRU網絡