背景
之前在學習深度學習的時候,由於只是做一些圖像處理相關的東西,因此關注的重心都在CNN上;但是目前,RNN也逐漸在圖像處理上發揮了非常重要的作用,所以學習並瞭解一些RNN的基礎和原理也是非常重要。
本次,將利用sin函數值預測cos函數值
,分別用RNN
、GRU
和LSTM
進行了測試。我也得到了一些結論,但是目前我對循環神經網絡理解還比較淺薄,所以可能也不一定對。
最後,本文的大部分代碼參考了以下鏈接的代碼;這個repo裏有很多非常有意思的代碼,非常感謝他的貢獻和分享。
https://github.com/MorvanZhou/PyTorch-Tutorial
代碼
代碼鏈接:https://github.com/Yannnnnnnnnnnn/learnPyTorch/blob/master/RNN_sin.ipynb
#讀取各個模塊
import torch
from torch import nn
from torch.autograd import Variable
import numpy as np
import matplotlib.pyplot as plt
# 隨機數種子
torch.manual_seed(1)
# 參數
TIME_STEP = 10 # 每個batch包含時間序列的長度,即包含數據的數量
INPUT_SIZE = 1 # 每個數據的特徵長度
LR = 0.02 # 學習率
HIDD_NUM = 32 # 隱含特徵的數量
# 定義模型 RNN
class RNN(nn.Module):
def __init__(self):
super(RNN, self).__init__()
self.rnn = nn.RNN(
input_size=INPUT_SIZE,
hidden_size=HIDD_NUM, # rnn hidden unit
num_layers=3, # RNN層數
batch_first=True, # 將batch數量放在最前. (batch, time_step, input_size)
)
self.fc3 = nn.Linear(HIDD_NUM, 1)
def forward(self, x, h_state):
# x (batch, time_step, input_size)
# h_state (n_layers, batch, hidden_size)
# r_out (batch, time_step, hidden_size)
r_out, h_state = self.rnn(x, h_state)
outs = [] # save all predictions
for time_step in range(r_out.size(1)): # calculate output for each time step
out = self.fc3(r_out[:, time_step, :])
outs.append(out)
return torch.stack(outs, dim=1), h_state
# 定義模型
rnn = RNN()
# 定義優化器和損失函數
optimizer = torch.optim.Adam(rnn.parameters(), lr=LR)
loss_func = nn.MSELoss()
# 定義隱藏狀態
h_state = None
# 畫圖
plt.figure(1, figsize=(12, 5))
plt.ion() # continuously plot
# 訓練
for step in range(2400):
# 生成訓練數據
start, end = step * np.pi, (step+1)*np.pi # time range
steps = np.linspace(start, end, TIME_STEP, dtype=np.float32)
x_np = np.sin(steps) # float32 for converting torch FloatTensor
y_np = np.cos(steps)
# 數據轉換成Tensor
x = Variable(torch.from_numpy(x_np[np.newaxis, :, np.newaxis])) # shape (batch, time_step, input_size)
y = Variable(torch.from_numpy(y_np[np.newaxis, :, np.newaxis]))
# forward
prediction, h_state = rnn(x, h_state) # rnn output
# 由於生成數據並沒有保存,所以並不用保存隱藏狀態
h_state = Variable(h_state.data)
# backward
loss = loss_func(prediction, y) # cross entropy loss
optimizer.zero_grad() # clear gradients for this training step
loss.backward() # backpropagation, compute gradients
optimizer.step() # apply gradients
# 畫圖
if step%100==0 or step%101==0:
plt.plot(steps, y_np.flatten(), 'r-')
plt.plot(steps, prediction.data.numpy().flatten(), 'b-')
plt.draw(); plt.pause(0.05)
代碼擴展
上述代碼是利用的RNN,可以很容易的擴展成GRU和LSTM,其代碼分別入下:
- GRU
...
# 定義模型 RNN
class RNN(nn.Module):
def __init__(self):
super(RNN, self).__init__()
self.rnn = nn.GRU(
input_size=INPUT_SIZE,
hidden_size=HIDD_NUM, # rnn hidden unit
num_layers=3, # RNN層數
batch_first=True, # 將batch數量放在最前. (batch, time_step, input_size)
)
....
- LSTM
...
# 定義模型 RNN
class RNN(nn.Module):
def __init__(self):
super(RNN, self).__init__()
self.rnn = nn.LSTM(
input_size=INPUT_SIZE,
hidden_size=HIDD_NUM, # rnn hidden unit
num_layers=3, # RNN層數
batch_first=True, # 將batch數量放在最前. (batch, time_step, input_size)
)
....
# 由於生成數據並沒有保存,所以並不用保存隱藏狀態
h_state = (Variable(h_state[0].data),Variable(h_state[1].data))
...
這裏利用《Dive into DL Pytorch》的插圖,分別可視化一下RNN、GRU和LSTM,就可以很容易明白foward的返回值都是不一樣的,具體去看那本書吧。
RNN
GRU
LSTM
分析和結論
由於上述代碼可以直接跑通,所以我在此處只直接列出我遇到的一些現象,並做一下簡單的分析
- 將
TIME_STEP=50
,RNN網絡模型就不會收斂,而GRU和LSTM可以收斂;猜測原因可能是RNN僅能學習到比較短的信息,從而當TIME_STEP較大時,就不能學習到sin函數的精髓,而GRU和LSTM則引入了遺忘率和更新率保證了網絡能夠學習到更多的信息。 - 在GRU和LSTM模型下,可以通過提高
HIDD_NUM
和num_layber
來保證模型的精度,這個相當於引入了更多的參數吧。 - 最後還有一個我也沒有想明白的事情,我本身想直接利用steps預測cos(steps),但是模型無論如何不收斂;現在我還不知道原因,等以後瞭解循環神經網絡了,再回來考慮這個問題。
總之,大家可以自行跑跑代碼,或者看看MorvanZhou的代碼和《Dive into DL Pytorch》,肯定會有所收穫。