白話RNN系列(二)

緊接白話談RNN系列(一)

上文討論了基礎的全連接神經網絡,本文,我們來說說RNN。

首先,RNN相比於普通的神經網絡,有什麼改進?

兩點比較突出:權值共享和隱層神經元節點的有序連接。

直接上圖,淺顯易懂:

è¿éåå¾çæè¿°

上圖,摘自深度學習(花書),左側圖和右側圖表達了相同的含義,我們以右側圖爲例,並配合實例,對RNN進行透徹的分析,我儘可能以很通俗移動的方式把RNN講明白。

從本圖中,我們很清晰能夠看到權值共享的痕跡,我們可以把X理解爲一句話,一個字符序列,後面的字符依賴於前面的字符(當然,這種結構肯定是不完善的,所以會有一些雙向RNN等,這裏咱不討論)。

"I love you" 當成一句話,來作爲我們RNN的輸入,即x,那按照時間序列,第一次輸入的就是I, 第二次輸入的就是love, 第三次輸入的則是you。

這裏多說一句,大家會發現圖中展示的結構好像與全連接網絡有很大的不同,但實際上,我們仔細思考下全連接神經網絡,實質就是輸入向量與權重矩陣相乘,再經過隱藏層的激活函數,得到下一隱藏層的輸入,而實際上,這裏的U,如上圖,其實就是我們前面所定義的權重矩陣。

而在自然語言處理內部,我們每個詞輸入的時候,其實輸入的是一個one-hot向量,其大小爲詞表的大小,比如詞表大小爲100,而I 在其中排在第60的位置,我們輸入的x1,其實就是一個一維向量,其大部分元素都是0,僅在index=60的位置上有一個1,當然,這種表達方式不盡完善,大家可以瞭解下詞向量等,可能比one-hot效果好很多。

從上圖中,我們看到每個隱藏層神經元都有兩個輸入,即h和x,這就是RNN隱藏層神經元的神祕之處,除了第一個神經元,餘下的神經元都會把前一個神經元的隱藏狀態拿來使用,配合本次輸入x,拼接成一個大的輸入向量使用。

而對於第一個神經元,其使用到的隱藏狀態通常是自定義的全零的向量。

OK,我們繼續。

重申下上面的定義:輸入向量列向量:維度爲100。

這裏,假設我們的RNN隱藏神經元的數目是128,大家一定要注意,上面右側的圖是分解來來的,他們實際上用到的權值矩陣是完全一樣的,其實更深層來說,他們所經過的神經元就是完全一樣的,只是我按照時間序列,不斷地把輸入和上一次隱藏層狀態的輸入拼接在一起,再次填充給隱藏層的神經元。

輸入向量是100維度,隱藏層神經元數目是128維,則之間的權重矩陣U應該是128 * 100 的矩陣,則 U * X 得到一個128 * 1的向量,這一步,跟全連接神經網絡完全一致。

我們說了,t時刻的隱藏層的輸出,會作爲t+1時刻的輸入,舉個例子,最開始我輸入了100維度的向量,得到了128維度的輸出,那在第二秒的時候,我就會把這128維的向量,作爲隱藏狀態,再次輸入到隱藏層中去。

因此,U和W是不同的,因爲其處理的往往是不同維度的向量,U負責處理x,而W負責處理隱藏狀態。

我們確定了U(100 * 128)的維度,現在來說下W的維度。

看看W接收到的輸入是128 * 1的隱藏狀態,所以W 肯定是N * 128 維度的矩陣。

我們知道: U * x + W *  h(t-1) ,通常,我們會把x和h(t-1) 進行拼接,拼接出228 * 1的向量,同時把U和W進行堆疊,最終輸出的纔是隱層狀態(其維度爲128 * 1)。

則U和W的堆疊應該爲128 * 228的矩陣,而實際上,這個拼接出來的矩陣應該是分塊矩陣,其左上角是U,右下角是W。

形如:\begin{bmatrix} U & W \end{bmatrix} 而同時x和h(t-1)的拼接則形如:\begin{pmatrix} x\\ h(t-1) \end{pmatrix}

後面的x和h(t-1) 均爲列向量,拼接出來的維度爲228 * 1。

則前面的矩陣大小,應該爲128 * 228 ,最終產出的是128 * 1的隱藏層向量,其中U的大小爲128 * 100, 則W的大小毫無疑問爲128 * 128。

很好理解,因爲W處理的是隱層狀態到隱層狀態,其維度很容易確定(這些分析看似無意義,但對於實現代碼是很有幫助的)。

稍微總結下:W的維度爲128 * 128 ,U的維度爲128 * 100 ,隱藏層神經元同時處理上一隱藏層的狀態和本次的輸入向量(228 * 1),最終得到隱藏層的輸出向量:128 * 1。

而這個隱藏層的輸出,又會不斷地向後面的神經元進行傳遞。

OK,到這裏我們分析清楚了RNN產生的隱藏層輸出的來龍,我們再看下隱藏層輸出的去脈。

從解釋圖中可以看出,隱藏層的輸出去了兩個方向,一個產生了本次的輸出,一個流向下一次狀態,流向下一次狀態的我們不討論了,看下流向本次輸出的隱層狀態:

這裏涉及到了一個新的矩陣:V,毫無疑問,又是一個全連接:V * h(t-1) 再加上激活函數,得到最終的輸出o(t)。

看到了吧,RNN與普通的全連接神經網絡區別並不大,只是其加上了權值共享(這裏的權值共享的利用與卷積神經網絡不完全一樣,重要的是循環輸入)。

上圖中的大部分參數都說清楚了,但還有一個參數沒詳細介紹,即 t ,其表示當前輸入的時刻,也代表了當前RNN循環的次數。

講清楚了這個過程,RNN的前向傳播很容易理解,我們這裏舉個使用RNN的例子,看下在實際實現中,RNN是如何發揮作用的;

直接找個代碼例子進行分析,方便快捷:以下例子是從吳金洪老師的《深度學習之Tensorflow》一書拿到的例子:

需求:搭建一個簡單的RNN神經網絡,使用一串隨機的模擬數據作爲原始信號,讓RNN來擬合其對應的回聲序列,比如我們輸入的樣本數據爲隨機的由0,1組成的數字,將其當成發射出去的一串信號,當碰到阻攔會反彈回來後,會收到原始信號的迴音

這個例子,需要幾個步驟來進行,本文只介紹樣本數據的產出,後續系列詳情介紹程序的完整實現:

total_series_length = 50000
echo_step = 3
batch_size = 5
def generateData():
    x = np.array(np.random.choice(2, total_series_length, p=[0.5, 0.5]))  # 在0 和1 中選擇total_series_length個數
    y = np.roll(x, echo_step)  # 向右循環移位【1111000】---【0001111】
    y[0:echo_step] = 0

    # 原本50000個元素,拆分成5個批次,每個批次是1000個
    x = x.reshape((batch_size, -1))  # 5,10000
    print(x.shape)
    y = y.reshape((batch_size, -1))  # 同樣是5,10000
    print(y.shape)
    return (x, y)

這裏,我們定義了一個產生數據的函數:

echo_step定義了我們產生迴音的時間,比如我們輸入50000個數字,在第三步才產生迴音,則迴音的前三個數字都會默認定義爲0。

上面的np.random.choice函數,其參數中,2等同於range的作用,定義序列中數字爲0,1;total_series_length定義序列總長度,p代表概率,即序列中每個元素爲0和爲1的概率均爲0.5。

產生長度均爲50000的輸入序列x和回聲序列y後,我們對齊進行reshape操作,變換爲5*10000的矩陣,這裏的reshape和numpy.reshape用途是一致的。

系列下文將對程序進行分析。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章