對循環神經網路參數的理解|LSTM RNN Input_size Batch Sequence

在很多部落格和知乎中我看到了許多對於pytorch框架中RNN介面的一些解析,但都較為淺顯甚至出現一些不準確的理解,在這裡我想闡述下我對於pytorch中RNN介面的參數的理解。

我們經常看到的RNN網路是如圖下所示:

imgRNN的

1. timestep訓練過程

這個左邊圖中間循環的箭頭難以理解,所以將其按照時間軸展開成多個單元。

但是!!!!

網路只有一個,網路只有一個,網路只有一個, 並不是想右邊那樣畫的。右邊的圖只不過是不同時刻的輸入。因為每個時刻RNN會產生兩個輸出,一個output和一個state(state是輸入向下一個時序的結果),上一個時刻state和當前作為輸入給當前網路,就如右圖所示。上圖很容易造成了誤解。

比如我們需要預測一個sin函數,那麼我們會用x的坐標去預測y,batchsize=1(batch_size的問題較為複雜,後續會聊),timestep(sequence的長度)為5,特徵為1(只有x坐標),所以整個訓練過程是這樣的,我們預備出5個坐標,一個一個依次放入到網路中,初始化的h0是0,然後會得到h1,去得到h2,用h2和x3去得到h4,以此類推。。。我們其實只要看上圖的左邊,不要被右圖給搞混,只有一個網路結構而已。只是不停的放入不停的迭代。

2. batch理解

網上對batch的理解魚龍混雜,什麼樣的解釋都有,這裡我要闡述我的觀點,用一個部落格上的例子,

給定一個長序列,序列中的每一個值,也都由一個很長的向量(或矩陣)表示。把序列從前往後理解為時間維度,那麼timestep就是指的這個維度中的值,如果timestep=n,就是用序列內的n個向量(或矩陣)預測一個值,下圖的timestep為2。

而對於每一個向量來說,它本身有一個空間維度(如長度),那麼Batchsize就是這個空間維度上的概念。

比如一共有5個字母ABCDE,它們分別如此表示:

A:1 1 1 1 1

B:2 2 2 2 2

C:3 3 3 3 3

D:4 4 4 4 4

E:5 5 5 5 5

X Y
AB C
BC D
CD E

下面我們只看第一對數據:AB-C

t=0,A進入訓練,生成h(0)

t=1,B進入訓練,生成h(1)

如果我們分batch的話,設batch=2,那就AB-C, BC-D一起放入訓練,同時平均loss之後經過一次backward更新超參數,由於超參數的方法更新很多,可能是類似於加權的平均。

這樣或許很抽象,於是我我以文本數據為例畫了一張圖

image-20221004235108421

3. hidden_size理解

hidden_size類似於全連接網路的結點個數,hidden_size的維度等於hn的維度,這就是每個時間輸出的維度結果。我們的hidden_size是自己定的,根據煉丹得到最佳結果。

為什麼我們的input_size可以和hidden_size不同呢,因為超參數已經幫我們完成了升維或降維,如下圖(超參數計算流程)。

image-20221004233402703

此時我引用正弦預測例子,後續會展示程式碼,其中input_size=1,hidden_size=50

我們可以得到以下結果:

image-20221004232847216

程式碼附下:

import numpy as np
import pandas as pd

import torch
import torch.nn as nn
import matplotlib.pyplot as plt
# %matplotlib inline

# 跟matlab差不多 返回一個1維張量,包含在區間start和end上均勻間隔的step個點。
# torch.linspace(start, end, steps, out=None) → Tensor
x = torch.linspace(0,799,800)

y = torch.sin(x*2*3.1416/40)

plt.figure(figsize=(12,4))
plt.xlim(-10,801)
plt.grid(True)
plt.xlabel("x")
plt.ylabel("sin")
plt.title("Sin plot")
plt.plot(y.numpy(),color='#8000ff')
plt.show()

test_size = 40
train_set = y[:-test_size]#前760個數
test_set = y[-test_size:]#後40個數


plt.figure(figsize=(12,4))
plt.xlim(-10,801)
plt.grid(True)
plt.xlabel("x")
plt.ylabel("sin")
plt.title("Sin plot")
plt.plot(train_set.numpy(),color='#8000ff')
plt.plot(range(760,800),test_set.numpy(),color="#ff8000")
plt.show()

# 在使用LSTM模型時,我們將訓練序列分為一系列重疊的窗口。用於比較的標籤是序列中的下一個值。【滑動窗口】
# 例如,如果我們有一系列12條記錄,窗口大小為3,我們將[x1, x2, x3]送入模型,並將預測值與x4比較。
# 然後我們回溯,更新參數,將[x2, x3, x4]輸入模型,並將預測結果與x5進行比較。
# 為了簡化這個過程,我定義了一個函數input_data(seq,ws),創建了一個(seq,labels)圖元的列表。
# 如果ws是窗口大小,那麼(seq,labels)圖元的總數將是len(series)-ws。

def input_data(seq, ws):
    out = []
    L = len(seq)

    for i in range(L - ws):
        window = seq[i:i + ws]
        label = seq[i + ws:i + ws + 1]
        out.append((window, label))

    return out

# The length of x = 800
# The length of train_set = 800 - 40 = 760
# The length of train_data = 760 - 40 - 720

window_size = 40
train_data = input_data(train_set, window_size)
len(train_data)

train_data[0]#40個滑動窗口,作為一個輸入


class LSTM(nn.Module):
    def __init__(self, input_size=1, hidden_size=50, out_size=1):
        super().__init__()
        self.hidden_size = hidden_size
        self.lstm = nn.LSTM(input_size, hidden_size)
        self.linear = nn.Linear(hidden_size, out_size)
        self.hidden = (torch.zeros(1, 1, hidden_size), torch.zeros(1, 1, hidden_size))

    def forward(self, seq):
        lstm_out, self.hidden = self.lstm(seq.view(len(seq), 1, -1), self.hidden)
        pred = self.linear(lstm_out.view(len(seq), -1))
        return pred[-1]

torch.manual_seed(42)
model = LSTM()
criterion = nn.MSELoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

epochs = 10
future = 40

for i in range(epochs):

    for seq, y_train in train_data:
        optimizer.zero_grad()
        model.hidden = (torch.zeros(1, 1, model.hidden_size),
                        torch.zeros(1, 1, model.hidden_size))

        y_pred = model(seq)
        loss = criterion(y_pred, y_train)
        loss.backward()
        optimizer.step()

    print(f"Epoch {i} Loss: {loss.item()}")

    preds = train_set[-window_size:].tolist()
    for f in range(future):
        seq = torch.FloatTensor(preds[-window_size:])
        with torch.no_grad():
            model.hidden = (torch.zeros(1, 1, model.hidden_size),
                            torch.zeros(1, 1, model.hidden_size))
            preds.append(model(seq).item())

    loss = criterion(torch.tensor(preds[-window_size:]), y[760:])
    print(f"Performance on test range: {loss}")

    plt.figure(figsize=(12, 4))
    plt.xlim(700, 801)
    plt.grid(True)
    plt.plot(y.numpy(), color='#8000ff')
    plt.plot(range(760, 800), preds[window_size:], color='#ff8000')
    plt.show()

參考文章://zhuanlan.zhihu.com/p/460282865
原創作者:孤飛-部落格園
個人部落格://blog.onefly.top