一、引言
? ? ? ? LSTM出現(xiàn)以來,在捕獲時(shí)間序列依賴關(guān)系方面表現(xiàn)出了強(qiáng)大的潛力,直到Transformer的大殺四方。但是,就像我在上一篇博客《RNN與LSTM原理淺析》末尾提到的一樣,雖然Transformer在目標(biāo)檢測、目標(biāo)識別、時(shí)間序列預(yù)測等各領(lǐng)域都有著優(yōu)于傳統(tǒng)模型的表現(xiàn),甚至是壓倒性的優(yōu)勢。但Transformer所依賴的Multi-Head Attention機(jī)制給模型帶來了巨大的參數(shù)量與計(jì)算開銷,這使得模型難以滿足實(shí)時(shí)性要求高的任務(wù)需求。我也提到,LSTM想與Transformer抗衡,似乎應(yīng)該從注意力機(jī)制方面下手。事實(shí)上,已經(jīng)有研究這么做了,那就是LSTNet。
二、LSTNet
? ? ? ? 2018年,論文《Modeling Long- and Short-Term Temporal Patterns with Deep Neural Networks》正式提出了LSTNet。LSTNet的出現(xiàn)可以認(rèn)為是研究人員通過注意力機(jī)制提升LSTM模型時(shí)序預(yù)測能力的一次嘗試,文中共提出了LST-Skip與LST-Atten兩種模型。其中,LST-Skip需要手動設(shè)置序列的周期,比較適用于交通流預(yù)測等周期明確可知的時(shí)間序列,而LST-Atten模型則可以自動捕捉模型的周期。實(shí)驗(yàn)表明,上述兩種模型在周期序列預(yù)測中表現(xiàn)出了良好的性能。
? ? ? ? 然而,上述模型的性能受制于序列的周期與可用歷史狀態(tài)的長度。首先,模型的注意力機(jī)制為“時(shí)間注意力機(jī)制”,其本質(zhì)是利用了序列內(nèi)部的時(shí)間性周期,因此對于沒有明顯周期性的序列(如:車輛軌跡序列)則不能很好地發(fā)揮優(yōu)勢。其次,LST-Atten依賴于歷史狀態(tài)挖掘序列的周期,若可用的歷史狀態(tài)較短,無法反映一個(gè)完整的周期,則模型可自主挖掘周期性的優(yōu)勢仍無法體現(xiàn)。
三、方法? ? ? ??
? ? ? ? 本文以實(shí)現(xiàn)LST-Atten為例(在進(jìn)入FC層前的張量處理與原文稍有不同),描述LSTM中的時(shí)間注意力機(jī)制。由于在CSDN的編輯器中不方便使用各種專業(yè)符號,因此下文中使用的符號一切從簡,不以專業(yè)性為目的。
? ? ? ? 我們假設(shè)使用過去的A幀數(shù)據(jù)預(yù)測未來的B幀,且LSTM編碼器中A個(gè)LSTM單元的隱狀態(tài)為H,LSTM解碼器中第一個(gè)LSTM單元的隱狀態(tài)為h1。那么集成了時(shí)間注意力機(jī)制的LSTM編碼-解碼器工作原理可用如下表示:
? ? ? ? ?其中,F(xiàn)為打分函數(shù),用于計(jì)算H與h1之間的余弦相關(guān)度。然后,通過softmax函數(shù),這些余弦相關(guān)度被轉(zhuǎn)換為各歷史隱狀態(tài)的相對權(quán)重。H中各時(shí)刻隱藏狀態(tài)的加權(quán)和與解碼器第一幀輸出的隱藏狀態(tài)h1相結(jié)合,最終通過全連接層得到第一幀的預(yù)測輸出。 其工作原理如圖1所示。
四、結(jié)果?
? ? ? ? 該模型在公共交通流數(shù)據(jù)集 PeMS04 上進(jìn)行了測試。 我們選擇了測試點(diǎn)1測得的交通流數(shù)據(jù),其中70%的數(shù)據(jù)用于模型訓(xùn)練,30%的數(shù)據(jù)用于模型測試。使用 Adam 作為優(yōu)化器,MSE 作為模型測量指標(biāo),學(xué)習(xí)率為 0.0001,訓(xùn)練 800 輪。 訓(xùn)練損失如圖2所示,測試結(jié)果如表1所示。
?
?最后,我在測試集中選取了 4000 個(gè)樣本點(diǎn)進(jìn)行可視化,結(jié)果如圖 3 所示。文章來源:http://www.zghlxwxcb.cn/news/detail-502794.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-502794.html
?五、源代碼
"模型訓(xùn)練"
import numpy as np
import torch
import torch.nn as nn
import os
import matplotlib.pyplot as plt
os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"
file=np.load('PeMS04.npz')
data=torch.from_numpy(file['data'])
device = torch.device('cuda:0')
'定義子函數(shù)與類模型'
def creat_dataset(data,look_back,pre_len):
data_x = []
data_y = []
for i in range(len(data)-look_back-pre_len+1): # 對選好的該輛車的數(shù)據(jù)劃分輸入(過去8點(diǎn))與輸出(未來12點(diǎn))
data_x.append(data[i:i + look_back, :])
data_y.append(data[i+look_back:i+look_back+pre_len, :])
return np.asarray(data_x), np.asarray(data_y) # 轉(zhuǎn)為ndarray數(shù)據(jù)
class LSTM_Atten(nn.Module):
"""搭建Decoder結(jié)構(gòu)"""
def __init__(self,look_back,pre_len):
super(LSTM_Atten, self).__init__()
self.lstm = nn.LSTM(input_size=1, #1個(gè)輸入特征
hidden_size=128, #隱狀態(tài)h擴(kuò)展為為128維
num_layers=1, #1層LSTM
batch_first=True, # 輸入結(jié)構(gòu)為(batch_size, seq_len, feature_size). Default: False
)
self.lstmcell=nn.LSTMCell(input_size=128, hidden_size=128)
self.drop=nn.Dropout(0.2) #掉落率
self.fc1=nn.Linear(256,128)
self.fc2=nn.Linear(128,1)
self.look_back=look_back
self.pre_len=pre_len
self.Softmax=nn.Softmax(dim=1)
def forward(self, x):
H,(h,c) = self.lstm(x.float(),None) #編碼
h=h.squeeze(0)
c=c.squeeze(0)
H_pre=torch.empty((h.shape[0],self.pre_len,128*2)).to(device)
for i in range(self.pre_len): #解碼
h_t, c_t = self.lstmcell(h, (h, c)) # 預(yù)測
H=torch.cat((H,h_t.unsqueeze(1)),1)
h_atten=self.Atten(H) #獲取結(jié)合了注意力的隱狀態(tài)
H_pre[:,i,:]=h_atten #記錄解碼器每一步的隱狀態(tài)
h, c = h_t, c_t # 將當(dāng)前的隱狀態(tài)與細(xì)胞狀態(tài)記錄用于下一個(gè)時(shí)間步
return self.fc2(self.fc1(H_pre)).squeeze(2)
def Atten(self,H):
h=H[:,-1,:].unsqueeze(1) #[batch_size,1,128]
H=H[:,-1-self.look_back:-1,:] #[batch_size,look_back,128]
atten=torch.matmul(h,H.transpose(1,2)).transpose(1,2) #注意力矩陣
atten=self.Softmax(atten)
atten_H=atten*H #帶有注意力的歷史隱狀態(tài)
atten_H=torch.sum(atten_H,dim=1).unsqueeze(1) #按時(shí)間維度降維
return torch.cat((atten_H,h),2).squeeze(1)
'數(shù)據(jù)預(yù)處理'
#data格式:[數(shù)據(jù)條數(shù);記錄點(diǎn)id;3個(gè)特征(0號特征是交通流)]
look_back=20
pre_len=30
data=data[0:11890,0,0].unsqueeze(1).numpy() #第0個(gè)記錄點(diǎn)記錄的前11890條交通流數(shù)據(jù)作為訓(xùn)練集[11890,1]
data=(data-data.min())/(data.max()-data.min()) #數(shù)據(jù)歸一化
X,Y=creat_dataset(data,look_back,pre_len) #[11871,5,1],[11871,1] 使用前5點(diǎn)預(yù)測后一點(diǎn)
X=torch.from_numpy(X).to(device)
Y=torch.from_numpy(Y).to(device)
'訓(xùn)練模型'
model=LSTM_Atten(look_back,pre_len).to(device)
loss_fun=nn.MSELoss()
optimizer=torch.optim.Adam(model.parameters(),0.0001)
epoch=800
batch_size=3947
cost=[]
for i in range(epoch):
for t in range(int(11871/batch_size)):
input=X[t*batch_size:(t+1)*batch_size,:,:] #按batch_size獲取輸入與標(biāo)簽
label=Y[t*batch_size:(t+1)*batch_size,:].squeeze(2)
output=model(input.float()) #預(yù)測值
loss=loss_fun(label.float(),output.float()) #計(jì)算MSE損失
optimizer.zero_grad() #梯度清零
loss.backward() #反向傳播
optimizer.step() #參數(shù)更新
print(((i + 1) / epoch) * 100)
cost.append(loss.item()) #記錄損失
torch.save(model.state_dict(),'lstm-atten.pth')
plt.figure()
plt.plot(cost)
plt.show()
"模型測試"
import numpy as np
import torch
import torch.nn as nn
import os
import matplotlib.pyplot as plt
os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"
file=np.load('PeMS04.npz')
data=torch.from_numpy(file['data'])
'定義子函數(shù)與類模型'
def creat_dataset(data,look_back,pre_len):
data_x = []
data_y = []
for i in range(len(data)-look_back-pre_len+1): # 對選好的該輛車的數(shù)據(jù)劃分輸入(過去8點(diǎn))與輸出(未來12點(diǎn))
data_x.append(data[i:i + look_back, :])
data_y.append(data[i+look_back:i+look_back+pre_len, :])
return np.asarray(data_x), np.asarray(data_y) # 轉(zhuǎn)為ndarray數(shù)據(jù)
class LSTM_Atten(nn.Module):
"""搭建Decoder結(jié)構(gòu)"""
def __init__(self,look_back,pre_len):
super(LSTM_Atten, self).__init__()
self.lstm = nn.LSTM(input_size=1, #1個(gè)輸入特征
hidden_size=128, #隱狀態(tài)h擴(kuò)展為為128維
num_layers=1, #1層LSTM
batch_first=True, # 輸入結(jié)構(gòu)為(batch_size, seq_len, feature_size). Default: False
)
self.lstmcell=nn.LSTMCell(input_size=128, hidden_size=128)
self.drop=nn.Dropout(0.2) #掉落率
self.fc1=nn.Linear(256,128)
self.fc2=nn.Linear(128,1)
self.look_back=look_back
self.pre_len=pre_len
self.Softmax=nn.Softmax(dim=1)
def forward(self, x):
H,(h,c) = self.lstm(x.float(),None) #編碼
h=h.squeeze(0)
c=c.squeeze(0)
H_pre=torch.empty((h.shape[0],self.pre_len,128*2))
for i in range(self.pre_len): #解碼
h_t, c_t = self.lstmcell(h, (h, c)) # 預(yù)測
H=torch.cat((H,h_t.unsqueeze(1)),1)
h_atten=self.Atten(H) #獲取結(jié)合了注意力的隱狀態(tài)
H_pre[:,i,:]=h_atten #記錄解碼器每一步的隱狀態(tài)
h, c = h_t, c_t # 將當(dāng)前的隱狀態(tài)與細(xì)胞狀態(tài)記錄用于下一個(gè)時(shí)間步
return self.fc2(self.fc1(H_pre)).squeeze(2)
def Atten(self,H):
h=H[:,-1,:].unsqueeze(1) #[batch_size,1,128]
H=H[:,-1-self.look_back:-1,:] #[batch_size,look_back,128]
atten=torch.matmul(h,H.transpose(1,2)).transpose(1,2) #注意力矩陣
atten=self.Softmax(atten)
atten_H=atten*H #帶有注意力的歷史隱狀態(tài)
atten_H=torch.sum(atten_H,dim=1).unsqueeze(1) #按時(shí)間維度降維
return torch.cat((atten_H,h),2).squeeze(1)
'數(shù)據(jù)預(yù)處理'
#data格式:[數(shù)據(jù)條數(shù);記錄點(diǎn)id;3個(gè)特征(0號特征是交通流)]
look_back=20
pre_len=30
data=data[11890:,0,0].unsqueeze(1).numpy() #第0個(gè)記錄點(diǎn)記錄的11890后的5102條交通流數(shù)據(jù)作為訓(xùn)練集[5102,1]
data=(data-data.min())/(data.max()-data.min()) #數(shù)據(jù)歸一化
X,Y=creat_dataset(data,look_back,pre_len) #[5083,10,1],[5083,10,1] 使用前5點(diǎn)預(yù)測后一點(diǎn)
X=torch.from_numpy(X)
Y=torch.from_numpy(Y).squeeze(2)
'測試模型'
model=LSTM_Atten(look_back,pre_len)
loss_fun=nn.MSELoss()
model.load_state_dict(torch.load('lstm-atten.pth'))
output=model(X.float()).squeeze(1)
loss=loss_fun(output.float(),Y.float())
print(loss)
plt.figure()
plt.plot(output[0:4000,0].detach(),'r')
plt.plot(Y[0:4000,0],'b')
plt.show()
到了這里,關(guān)于LSTNet--結(jié)合時(shí)間注意力機(jī)制的LSTM模型(附源碼)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!