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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

RNN入门

發布時間:2025/3/15 编程问答 13 豆豆
生活随笔 收集整理的這篇文章主要介紹了 RNN入门 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

雷鋒網 AI科技評論按:本文作者何之源,原文載于知乎專欄AI Insight,雷鋒網(公眾號:雷鋒網) AI科技評論獲其授權發布。

上周寫的文章《完全圖解RNN、RNN變體、Seq2Seq、Attention機制》介紹了一下RNN的幾種結構,今天就來聊一聊如何在TensorFlow中實現這些結構,這篇文章的主要內容為:

  • 一個完整的、循序漸進的學習TensorFlow中RNN實現的方法。這個學習路徑的曲線較為平緩,應該可以減少不少學習精力,幫助大家少走彎路。

  • 一些可能會踩的坑

  • TensorFlow源碼分析

  • 一個Char RNN實現示例,可以用來寫詩,生成歌詞,甚至可以用來寫網絡小說!(項目地址:https://github.com/hzy46/Char-RNN-TensorFlow)

一、學習單步的RNN:RNNCell

如果要學習TensorFlow中的RNN,第一站應該就是去了解“RNNCell”,它是TensorFlow中實現RNN的基本單元,每個RNNCell都有一個call方法,使用方式是:(output, next_state) = call(input, state)。

借助圖片來說可能更容易理解。假設我們有一個初始狀態h0,還有輸入x1,調用call(x1, h0)后就可以得到(output1, h1):

?

再調用一次call(x2, h1)就可以得到(output2, h2):

?

也就是說,每調用一次RNNCell的call方法,就相當于在時間上“推進了一步”,這就是RNNCell的基本功能。

在代碼實現上,RNNCell只是一個抽象類,我們用的時候都是用的它的兩個子類BasicRNNCell和BasicLSTMCell。顧名思義,前者是RNN的基礎類,后者是LSTM的基礎類。這里推薦大家閱讀其源碼實現(地址:http://t.cn/RNJrfMl),一開始并不需要全部看一遍,只需要看下RNNCell、BasicRNNCell、BasicLSTMCell這三個類的注釋部分,應該就可以理解它們的功能了。

除了call方法外,對于RNNCell,還有兩個類屬性比較重要:

  • state_size

  • output_size

前者是隱層的大小,后者是輸出的大小。比如我們通常是將一個batch送入模型計算,設輸入數據的形狀為(batch_size, input_size),那么計算時得到的隱層狀態就是(batch_size, state_size),輸出就是(batch_size, output_size)。

可以用下面的代碼驗證一下(注意,以下代碼都基于TensorFlow最新的1.2版本):

import tensorflow as tf

import numpy as np

?

cell = tf.nn.rnn_cell.BasicRNNCell(num_units=128) # state_size = 128

print(cell.state_size) # 128

?

inputs = tf.placeholder(np.float32, shape=(32, 100)) # 32 是 batch_size

h0 = cell.zero_state(32, np.float32) # 通過zero_state得到一個全0的初始狀態,形狀為(batch_size, state_size)

output, h1 = cell.call(inputs, h0) #調用call函數

?

print(h1.shape) # (32, 128)

對于BasicLSTMCell,情況有些許不同,因為LSTM可以看做有兩個隱狀態h和c,對應的隱層就是一個Tuple,每個都是(batch_size, state_size)的形狀:

import tensorflow as tf

import numpy as np

lstm_cell = tf.nn.rnn_cell.BasicLSTMCell(num_units=128)

inputs = tf.placeholder(np.float32, shape=(32, 100)) # 32 是 batch_size

h0 = lstm_cell.zero_state(32, np.float32) # 通過zero_state得到一個全0的初始狀態

output, h1 = lstm_cell.call(inputs, h0)

?

print(h1.h) ?# shape=(32, 128)

print(h1.c) ?# shape=(32, 128)

二、學習如何一次執行多步:tf.nn.dynamic_rnn

基礎的RNNCell有一個很明顯的問題:對于單個的RNNCell,我們使用它的call函數進行運算時,只是在序列時間上前進了一步。比如使用x1、h0得到h1,通過x2、h1得到h2等。這樣的h話,如果我們的序列長度為10,就要調用10次call函數,比較麻煩。對此,TensorFlow提供了一個tf.nn.dynamic_rnn函數,使用該函數就相當于調用了n次call函數。即通過{h0,x1, x2, …., xn}直接得{h1,h2…,hn}。

具體來說,設我們輸入數據的格式為(batch_size, time_steps, input_size),其中time_steps表示序列本身的長度,如在Char RNN中,長度為10的句子對應的time_steps就等于10。最后的input_size就表示輸入數據單個序列單個時間維度上固有的長度。另外我們已經定義好了一個RNNCell,調用該RNNCell的call函數time_steps次,對應的代碼就是:

# inputs: shape = (batch_size, time_steps, input_size)

# cell: RNNCell

# initial_state: shape = (batch_size, cell.state_size)。初始狀態。一般可以取零矩陣

outputs, state = tf.nn.dynamic_rnn(cell, inputs, initial_state=initial_state)

此時,得到的outputs就是time_steps步里所有的輸出。它的形狀為(batch_size, time_steps, cell.output_size)。state是最后一步的隱狀態,它的形狀為(batch_size, cell.state_size)。

此處建議大家閱讀tf.nn.dynamic_rnn的文檔(地址:https://www.tensorflow.org/api_docs/python/tf/nn/dynamic_rnn)做進一步了解。

三、學習如何堆疊RNNCell:MultiRNNCell

很多時候,單層RNN的能力有限,我們需要多層的RNN。將x輸入第一層RNN的后得到隱層狀態h,這個隱層狀態就相當于第二層RNN的輸入,第二層RNN的隱層狀態又相當于第三層RNN的輸入,以此類推。在TensorFlow中,可以使用tf.nn.rnn_cell.MultiRNNCell函數對RNNCell進行堆疊,相應的示例程序如下:

import tensorflow as tf

import numpy as np

?

# 每調用一次這個函數就返回一個BasicRNNCell

def get_a_cell():
? ?return tf.nn.rnn_cell.BasicRNNCell(num_units=128)

# 用tf.nn.rnn_cell MultiRNNCell創建3層RNN

cell = tf.nn.rnn_cell.MultiRNNCell([get_a_cell() for _ in range(3)]) # 3層RNN

# 得到的cell實際也是RNNCell的子類

# 它的state_size是(128, 128, 128)

# (128, 128, 128)并不是128x128x128的意思

# 而是表示共有3個隱層狀態,每個隱層狀態的大小為128

print(cell.state_size) # (128, 128, 128)

# 使用對應的call函數

inputs = tf.placeholder(np.float32, shape=(32, 100)) # 32 是 batch_size

h0 = cell.zero_state(32, np.float32) # 通過zero_state得到一個全0的初始狀態

output, h1 = cell.call(inputs, h0)

print(h1) # tuple中含有3個32x128的向量

通過MultiRNNCell得到的cell并不是什么新鮮事物,它實際也是RNNCell的子類,因此也有call方法、state_size和output_size屬性。同樣可以通過tf.nn.dynamic_rnn來一次運行多步。

此處建議閱讀MutiRNNCell源碼(地址:http://t.cn/RNJrfMl)中的注釋進一步了解其功能。

四、可能遇到的坑1:Output說明

在經典RNN結構中有這樣的圖:

在上面的代碼中,我們好像有意忽略了調用call或dynamic_rnn函數后得到的output的介紹。將上圖與TensorFlow的BasicRNNCell對照來看。h就對應了BasicRNNCell的state_size。那么,y是不是就對應了BasicRNNCell的output_size呢?答案是否定的。

找到源碼中BasicRNNCell的call函數實現:

def call(self, inputs, state):
? ?"""Most basic RNN: output = new_state = act(W * input + U * state + B)."""
? ?output = self._activation(_linear([inputs, state], self._num_units, True))
? ?return output, output

這句“return output, output”說明在BasicRNNCell中,output其實和隱狀態的值是一樣的。因此,我們還需要額外對輸出定義新的變換,才能得到圖中真正的輸出y。由于output和隱狀態是一回事,所以在BasicRNNCell中,state_size永遠等于output_size。TensorFlow是出于盡量精簡的目的來定義BasicRNNCell的,所以省略了輸出參數,我們這里一定要弄清楚它和圖中原始RNN定義的聯系與區別。

再來看一下BasicLSTMCell的call函數定義(函數的最后幾行):

new_c = (
? ?c * sigmoid(f + self._forget_bias) + sigmoid(i) * self._activation(j))

new_h = self._activation(new_c) * sigmoid(o)

?

if self._state_is_tuple:
?new_state = LSTMStateTuple(new_c, new_h)

else:
?new_state = array_ops.concat([new_c, new_h], 1)

return new_h, new_state

我們只需要關注self._state_is_tuple == True的情況,因為self._state_is_tuple == False的情況將在未來被棄用。返回的隱狀態是new_c和new_h的組合,而output就是單獨的new_h。如果我們處理的是分類問題,那么我們還需要對new_h添加單獨的Softmax層才能得到最后的分類概率輸出。

還是建議大家親自看一下源碼實現(地址:http://t.cn/RNJsJoH)來搞明白其中的細節。

五、可能遇到的坑2:因版本原因引起的錯誤

在前面我們講到堆疊RNN時,使用的代碼是:

# 每調用一次這個函數就返回一個BasicRNNCell

def get_a_cell():
? ?return tf.nn.rnn_cell.BasicRNNCell(num_units=128)

# 用tf.nn.rnn_cell MultiRNNCell創建3層RNN

cell = tf.nn.rnn_cell.MultiRNNCell([get_a_cell() for _ in range(3)]) # 3層RNN

這個代碼在TensorFlow 1.2中是可以正確使用的。但在之前的版本中(以及網上很多相關教程),實現方式是這樣的:

one_cell = ?tf.nn.rnn_cell.BasicRNNCell(num_units=128)

cell = tf.nn.rnn_cell.MultiRNNCell([one_cell] * 3) # 3層RNN

如果在TensorFlow 1.2中還按照原來的方式定義,就會引起錯誤!

參考自https://www.leiphone.com/news/201709/QJAIUzp0LAgkF45J.html

總結

以上是生活随笔為你收集整理的RNN入门的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。