[深度学习] Pytorch中RNN/LSTM 模型小结
目錄
一 Liner
二 RNN
三 LSTM
四 LSTM 代碼例子
概念介紹可以參考:[深度學習]理解RNN, GRU, LSTM 網絡
Pytorch中所有模型分為構造參數和輸入和輸出構造參數兩種類型。
- ?模型構造參數主要限定了網絡的結構,如對循環網絡,則包括輸入維度、隱層\輸出維度、層數;對卷積網絡,無論卷積層還是池化層,都不關心輸入維度,其構造方法只涉及卷積核大小\步長等。這里的參數決定了模型持久化后的大小.
- ?輸入和輸出的構造參數一般和模型訓練相關,都需指定batch大小,seq大小(循環網絡)\chanel大小(卷積網絡),以及輸入\輸出維度,如果是RNN還需涉及h0和c0的初始化等。這里的參數決定了模型訓練效果。
?
一 Liner
- Liner(x_dim,y_dim)
– 輸入x,程序輸入(batch,x)
– 輸出y, 程序輸出(batch,y)
?
x = V(torch.randn(5,2)) # batch為5,即一次輸入10個x print(x) line(x) # 輸出為batch*4?
二 RNN
對于不同的網絡層,輸入的維度雖然不同,但是通常輸入的第一個維度都是batch_size,比如torch.nn.Linear的輸入(batch_size,in_features),torch.nn.Conv2d的輸入(batch_size,??,)。而RNN的輸入卻是(seq_len, batch_size, input_size),batch_size位于第二維度!雖然你可以將batch_size和序列長度seq_len對換位置,此時只需要把batch_first設置為True。但是默認情況下RNN輸入為啥不是batch first?原因同上,因為cuDNN中RNN的API就是batch_size在第二維度!進一步,為啥cuDNN要這么做呢?
因為batch first意味著模型的輸入(一個Tensor)在內存中存儲時,先存儲第一個sequence,再存儲第二個... 而如果是seq_len first,模型的輸入在內存中,先存儲所有序列的第一個單元,然后是第二個單元... 兩種區別如下圖所示:
seq_len first意味著不同序列中同一個時刻對應的輸入單元在內存中是毗鄰的,這樣才能做到真正的batch計算。
CNN中和RNN中batchSize的默認位置是不同的。
- CNN中:batchsize的位置是position 0.
- RNN中:batchsize的位置是position 1.
?
首先介紹一下什么是rnn,rnn特別擅長處理序列類型的數據,因為他是一個循環的結構
一個序列的數據依次進入網絡A,網絡A循環的往后傳遞。
這里輸入X一般是一個sequence, 如[我 愛 上海 小籠包]
對于最簡單的 RNN,我們可以使用下面兩種方式去調用,分別是 torch.nn.RNNCell() 和 torch.nn.RNN(),這兩種方式的區別在于 RNNCell() 只能接受序列中單步的輸入,且必須傳入隱藏狀態,而 RNN() 可以接受一個序列的輸入,默認會傳入全 0 的隱藏狀態,也可以自己申明隱藏狀態傳入。
?
- input_dim是輸入的維度,比如是128
- batch_size是一次往RNN輸入句子的數目,比如是5。
- seq_len是一個句子的最大長度,比如15
所以千萬注意,RNN輸入的是序列,一次把批次的所有句子都輸入了,得到的ouptut和hidden都是這個批次的所有的輸出和隱藏狀態,維度也是三維。
**可以理解為現在一共有batch_size個獨立的RNN組件,RNN的輸入維度是input_dim,總共輸入seq_len個時間步,則每個時間步輸入到這個整個RNN模塊的維度是[batch_size,input_dim]
?
# 構造RNN網絡,x的維度5,隱層的維度10,網絡的層數2 rnn_seq = nn.RNN(5, 10,2) # 構造一個輸入序列,長為 6,batch 是 3, 特征是 5 x = V(torch.randn(6, 3, 5)) #out,ht = rnn_seq(x, h0) # h0可以指定或者不指定 out,ht = rnn_seq(x) # q1:這里out、ht的size是多少呢? out:6*3*10, ht:2*3*10問題1:這里out、ht的size是多少呢?
回答:out:6 * 3 * 10, ht: 2 * 3 * 10,out的輸出維度[seq_len,batch_size,output_dim],ht的維度[num_layers * num_directions, batch, hidden_size],如果是單向單層的RNN那么一個句子只有一個hidden。
問題2:out[-1]和ht[-1]是否相等?
回答:相等,隱藏單元就是輸出的最后一個單元,可以想象,每個的輸出其實就是那個時間步的隱藏單元
?
這就是RNN的基本結構類型。而最早的RNN模型,序列依次進入網絡中,之前進入序列的數據會保存信息而對后面的數據產生影響,所以RNN有著記憶的特性,而同時越前面的數據進入序列的時間越早,所以對后面的數據的影響也就越弱,簡而言之就是一個數據會更大程度受到其臨近數據的影響。但是我們很有可能需要更長時間之前的信息,而這個能力傳統的RNN特別弱,于是有了LSTM這個變體。
三 LSTM
?
這就是LSTM的模型結構,也是一個向后傳遞的鏈式模型,而現在廣泛使用的RNN其實就是LSTM,序列中每個數據傳入LSTM可以得到兩個輸出,而這兩個輸出和序列中下一個數據一起又作為傳入LSTM的輸入,然后不斷地循環向后,直到序列結束。
注意共4個非線性變化。其中三個sigmoid變化分別對應的三個門:遺忘門f、輸入門(當前狀態)i、輸出門o。這三個門的取值為[0,1],可以看做選擇系數可以很好的控制信息的傳導。
?
LSTM參數
- input_size 表示的是輸入的數據維數
- hidden_size 表示的是輸出維數
- num_layers 表示堆疊幾層的LSTM,默認是1
- bias True 或者 False,決定是否使用bias, False則b_h=0. 默認為True
- batch_first True 或者 False,因為nn.lstm()接受的數據輸入是(序列長度,batch,輸入維數),這和我們cnn輸入的方式不太一致,所以使用batch_first,我們可以將輸入變成(batch,序列長度,輸入維數)
- dropout 表示除了最后一層之外都引入一個dropout,
- bidirectional 表示雙向LSTM,也就是序列從左往右算一次,從右往左又算一次,這樣就可以兩倍的輸出
?
LSTM數據格式:
-
num_layers: 我們構建的循環網絡有幾層lstm
-
num_directions: 當bidirectional=True時,num_directions=2;當bidirectional=False時,num_directions=1
LSTM? Input 數據格式
? LSTM輸入的X數據格式尺寸為(seq_len, batch, input_size),此外h0和c0尺寸如下
-
h0(num_layers * num_directions, ?batch_size, ?hidden_size)
-
c0(num_layers * num_directions, ?batch_size, ?hidden_size)
LSTM? Output 數據格式
? LSTM輸出數據格式尺寸為(seq_len, batch, hidden_size * num_directions);輸出的hn和cn尺寸如下
-
hn(num_layers * num_directions, ?batch_size, ?hidden_size)
-
cn(num_layers * num_directions, ?batch_size, ?hidden_size)
LSTM的輸出多了一個memory單元
# 輸入維度 50,隱層100維,兩層 lstm_seq = nn.LSTM(50, 100, num_layers=2) # 輸入序列seq= 10,batch =3,輸入維度=50 lstm_input = torch.randn(10, 3, 50) out, (h, c) = lstm_seq(lstm_input) # 使用默認的全 0 隱藏狀態問題1:out和(h,c)的size各是多少?
回答:out:(10 * 3 * 100),(h,c):都是(2 * 3 * 100)
問題2:out[-1,:,:]和h[-1,:,:]相等嗎?
回答: 相等
四 LSTM 代碼例子
?
Example 1
# 輸入維度 50,隱層100維,兩層 lstm_seq = nn.LSTM(50, 100, num_layers=2) # 查看網絡的權重,ih和hh,共2層,所以有四個要學習的參數 print(lstm_seq.weight_hh_l0.size()) print(lstm_seq.weight_ih_l0.size()) print(lstm_seq.weight_hh_l1.size()) print(lstm_seq.weight_ih_l1.size()) # q1: 輸出的size是多少?#torch.Size([400, 100]) #torch.Size([400, 50]) #torch.Size([400, 100]) #torch.Size([400, 100])?
# 輸入序列seq= 10,batch =3,輸入維度=50 lstm_input = V(torch.randn(10, 3, 50)) out, (h, c) = lstm_seq(lstm_input) # 使用默認的全 0 隱藏狀態 # q1:out和(h,c)的size各是多少?out:(10*3*100),(h,c):都是(2*3*100) print(out.shape, h.shape, c.shape)#torch.Size([10, 3, 100]) torch.Size([2, 3, 100]) torch.Size([2, 3, 100])Example 2
通過這樣定義一個一層的LSTM輸入是10,輸出是30
from torch.autograd import Variable lstm = nn.LSTM(10, 30, batch_first=True) print(lstm.weight_hh_l0.size()) print(lstm.weight_ih_l0.size()) print(lstm.bias_hh_l0.size()) print(lstm.bias_ih_l0.size())#torch.Size([120, 30]) #torch.Size([120, 10]) #torch.Size([120]) #torch.Size([120])可以分別得到權重的維數,注意之前我們定義的4個weights被整合到了一起,比如這個lstm,輸入是10維,輸出是30維,相對應的weight就是30x10,這樣的權重有4個,然后pytorch將這4個組合在了一起,方便表示,也就是lstm.weight_ih_l0,所以它的維數就是120x10
我們定義一個輸入
x = Variable(torch.randn((50, 100, 10))) h0 = Variable(torch.randn(1, 50, 30)) c0 = Variable(torch.randn(1, 50 ,30))- x的三個數字分別表示batch_size為50,序列長度為100,每個數據維數為10
- h0的第二個參數表示batch_size為50,輸出維數為30,第一個參數取決于網絡層數和是否是雙向的,如果雙向需要乘2,如果是多層,就需要乘以網絡層數
- c0的三個參數和h0是一致的
這樣就可以得到網絡的輸出了,和上面講的一致,另外如果不傳入h0和c0,默認的會傳入相同維數的0矩陣
這就是我們如何在pytorch上使用RNN的基本操作了,了解完最基本的參數我們才能夠使用其來做應用。
Example 3
使用單向LSTM進行MNIST分類
import torch from torch import nn from torch.autograd import Variable import torchvision.datasets as datasets import torchvision.transforms as transforms import numpy as np import matplotlib.pyplot as plt# 超參數 EPOCH = 1 BATCH_SIZE = 64 TIME_STEP = 28 # rnn time step / image height INPUT_SIZE = 28 # rnn input size / image width LR = 0.01 DOWNLOWD_MNIST = False # 如果沒有下載好MNIST數據,設置為True# 下載數據 # 訓練數據 train_data = datasets.MNIST(root='./mnist', train=True, transform=transforms.ToTensor(), download=DOWNLOWD_MNIST) train_loader = torch.utils.data.DataLoader(dataset=train_data, batch_size=BATCH_SIZE, shuffle=True)# 測試數據 test_data = datasets.MNIST(root='./mnist', train=False, transform=transforms.ToTensor()) test_x = Variable(test_data.test_data).type(torch.FloatTensor)[:2000] / 255. test_y = np.squeeze(test_data.test_labels.numpy())[:2000]class RNN(nn.Module):def __init__(self):super(RNN, self).__init__()self.rnn = nn.LSTM(input_size=INPUT_SIZE,hidden_size=64,num_layers=2, # hidden_layer的數目batch_first=True, # 輸入數據的維度一般是(batch, time_step, input),該屬性表征batch是否放在第一個維度)self.out = nn.Linear(64, 10)def forward(self, x):# rnn 運行的結果出了每層的輸出之外,還有該層要傳入下一層進行輔助分析的hidden state,# lstm 的hidden state相比于 RNN,其分成了主線h_n,分線h_cr_out, (h_n, h_c) = self.rnn(x, None) # x shape ( batch, step, input_size), None 之前的hidden state(沒有則填None)out = self.out(r_out[:, -1, :]) # 選取最后一個時刻的output,進行最終的類別判斷return outrnn = RNN() # print(rnn)# 優化器 optimizer = torch.optim.Adam(rnn.parameters(), lr=LR) # 誤差函數 loss_func = nn.CrossEntropyLoss() for epoch in range(EPOCH):for step, (x, y) in enumerate(train_loader):b_x = Variable(x.view(-1, 28, 28)) # reshape x to (batch, time_step, input_size)b_y = Variable(y)output = rnn(b_x)loss = loss_func(output, b_y)optimizer.zero_grad()loss.backward()optimizer.step()if step % 50 == 0:test_output = rnn(test_x)pred_y = np.squeeze(torch.max(test_output, 1)[1].data.numpy())accuracy = float((pred_y == test_y).astype(int).sum()) / float(test_y.size)print('Epoch: ', epoch, ' | train loss: %.4f' % loss.data.numpy(), ' | test accuracy: %.2f' % accuracy )?
# 輸出前10個測試數據的測試值 test_output = rnn(test_x[: 10].view(-1, 28, 28)) pred_y = np.squeeze(torch.max(test_output, 1)[1].data.numpy()) print(pred_y, 'prediction number') print(test_y[:10], 'real number')?
由上面代碼可以看到輸出為:output,(h_n,c_n)=self.rnn(x)
?
?
?
更多的RNN的應用可以看這個資源
?
總結
以上是生活随笔為你收集整理的[深度学习] Pytorch中RNN/LSTM 模型小结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 斑马条码打印机gk888t如何使用_斑马
- 下一篇: 梳理百年深度学习发展史-七月在线机器学习