RNN Layer

這是我參與8月更文挑戰的第11天,活動詳情查看: 8月更文挑戰html

一個RNN Layer以下圖所示 python

假設x的shape是[10, 3, 100],翻譯一下就是,10個單詞,每次訓練3句話,每一個單詞用一個100維的tensor來表達markdown

那麼對於輸入 x t x_t 來講, x t x_t 的shape就是[3 100]app

接着再看上面的運算過程,其中hidden len就是memory的維度,假設是20。所以:函數

h t + 1 = x t @ w x h + h t @ w h h = [ 3 , 100 ] @ [ 20 , 100 ] T + [ 3 , 20 ] @ [ 20 , 20 ] T = [ 3 , 20 ] \begin{aligned} h_{t+1} &= x_t @ w_{xh} + h_t @ w_{hh}\\ &= [3, 100] @ [20, 100]^T + [3, 20] @ [20, 20]^T \\ &= [3, 20] \end{aligned}

nn.RNN

用代碼定義一個RNN Layer,而後查看其參數信息oop

import torch
import torch.nn as nn

rnn = nn.RNN(100, 20)
print(rnn._parameters.keys())
print(rnn.weight_ih_l0.shape) # w_{xh} [20, 100]
print(rnn.weight_hh_l0.shape) # w_{hh} [20, 20]
print(rnn.bias_ih_l0.shape) # b_{xh} [20]
print(rnn.bias_hh_l0.shape) # b_{hh} [20]
複製代碼

解釋上面的代碼前先看一下PyTorch中RNN類的參數(參考於PyTorch官網RNN APIpost

  • 必選參數input_size,指定輸入序列中單個樣本的尺寸大小,例如可能用一個1000長度的向量表示一個單詞,則input_size=1000
  • 必選參數hidden_size,指的是隱藏層中輸出特徵的大小
  • 必選參數num_layers,指的是縱向的隱藏層個數,通常設置爲1~10,default=1

如今上面的代碼就很好理解了,nn.RNN(100, 20)中100指的是用一個長度爲100的向量表示一個單詞,20指的是hidden_sizeui

RNN的forward函數與CNN定義的方式有點不太同樣,具體見下圖spa

參數中的 x x 不是 x t x_t ,就是直接把 x = [ seq_len , batch , feature_len ] x=[\text{seq\_len}, \text{batch}, \text{feature\_len}] 帶進去翻譯

h 0 h_0 若是不寫默認就是0,若是寫的話, h 0 h_0 的維度是 [ layers , batch , hidden_len ] [\text{layers},\text{batch}, \text{hidden\_len}]

看下代碼

import torch
import torch.nn as nn

rnn = nn.RNN(input_size=100, hidden_size=20, num_layers=1)
x = torch.randn(10, 3, 100)
out, h_t = rnn(x, torch.zeros(1, 3, 20))
print(out.shape) # [10, 3, 20]
print(h_t.shape) # [1, 3, 20]
複製代碼

每一個地方參數的shape都是有關聯的,必需要把上面我寫的內容看懂了才能理解

h t h_t o u t out 很容易搞混,咱們先看一個2層的RNN模型

在解釋 h t h_t o u t out 以前要先理解一個概念——時間戳,時間戳是針左右而不是上下,什麼意思呢,就是上圖是一個兩層的RNN,假設這兩層的RNN右邊分別又各接一層,那這樣的左右結構就是時間戳,基於此,給出 h t h_t o u t out 的定義:

  • h t h_t :最後一個時間戳上面全部的memory狀態
  • o u t out :全部時間戳上的最後一個memory狀態

而第幾個memory是針對層來講的,比方說第一層的memory就是第一個memory,最後一層的memory就是最後一個memory

看下代碼

import torch
import torch.nn as nn

rnn = nn.RNN(input_size=100, hidden_size=20, num_layers=4)
x = torch.randn(10, 3, 100)
out, h_t = rnn(x)
print(out.shape) # [10, 3, 20]
print(h_t.shape) # [4, 3, 20]
複製代碼

若是理解了上面 o u t out h t h_t 的shape,這裏的輸出也就不難想到了

上面nn.RNN()的定義方式是直接把整個 x x 輸入,自動完成循環。下面再介紹一種定義RNN的方式,須要手動完成循環

nn.RNNCell

先看一下PyTorch的官方API

參數和nn.RNN大致類似,可是注意input_size的shape是(batch, input_size),並且hidden_size的shape也是(batch, hidden_size),這就致使forward也不同

看下代碼

import torch
import torch.nn as nn

cell1 = nn.RNNCell(100, 20)
x = torch.randn(10, 3, 100)
h1 = torch.zeros(3, 20)
for xt in x:
    h1 = cell1(xt, h1)
print(h1.shape) # [3, 20]
複製代碼

上面就就是一層的RNN,用RNNCell的方式,手動循環進行訓練

下面在看一個兩層的RNN,用RNNCell的方式怎麼作

import torch
import torch.nn as nn

cell1 = nn.RNNCell(100, 30) # 100 -> 30
cell2 = nn.RNNCell(30, 20)
x = torch.randn(10, 3, 100)
h1 = torch.zeros(3, 30)
h2 = torch.zeros(3, 20)
for xt in x:
    h1 = cell1(xt, h1)
    h2 = cell2(h1, h2)
print(h2.shape) # [3, 20]
複製代碼

第一層的做用是將一個100維的輸入變爲30維的memory輸出,而後將輸出帶入到第二層,第二層的輸出是一個20維的memory。最重要的代碼是for中的兩句話,第一層的輸入是xt和memory h1,第二層的輸入是第一層的memory h1,以及第二層的memory h2

相關文章
相關標籤/搜索