目錄
引言
LSTM的預(yù)測效果圖
LSTM機(jī)制
了解LSTM的結(jié)構(gòu)
忘記門
輸入門
輸出門
LSTM的變體
只有忘記門的LSTM單元
獨(dú)立循環(huán)(IndRNN)單元
雙向RNN結(jié)構(gòu)(LSTM)
運(yùn)行代碼
代碼講解
引言
LSTM(Long Short-Term Memory)是一種常用的循環(huán)神經(jīng)網(wǎng)絡(luò)(RNN)模型,用于處理序列數(shù)據(jù),具有記憶長短期的能力。在時(shí)間序列預(yù)測中,LSTM既可以多元預(yù)測機(jī)制又可以作為單元預(yù)測機(jī)制使用。
作為多元預(yù)測機(jī)制,LSTM可以處理多個相關(guān)變量的歷史數(shù)據(jù),從而可以預(yù)測這些變量的未來值。具體地,我們可以將多個變量的歷史數(shù)據(jù)作為LSTM的輸入,將多個變量的未來值作為LSTM的輸出。在訓(xùn)練過程中,我們可以使用誤差反向傳播算法來更新LSTM的參數(shù),從而優(yōu)化模型的預(yù)測性能。
作為單元預(yù)測機(jī)制,LSTM可以預(yù)測單一變量的未來值,例如股票價(jià)格、銷售量等。在單元時(shí)間序列預(yù)測中,我們需要對歷史數(shù)據(jù)進(jìn)行分析,確定趨勢、季節(jié)性和周期性等因素,并使用這些因素來預(yù)測未來的值。LSTM可以通過學(xué)習(xí)歷史數(shù)據(jù)中的模式和規(guī)律,來預(yù)測未來的值。
LSTM作為多元預(yù)測機(jī)制和單元預(yù)測機(jī)制的優(yōu)點(diǎn)是可以處理序列數(shù)據(jù)中的長期依賴關(guān)系,從而可以捕捉到數(shù)據(jù)中的復(fù)雜模式和規(guī)律。它可以自適應(yīng)地學(xué)習(xí)和調(diào)整模型參數(shù),從而提高模型的預(yù)測性能和泛化能力。
總的來說,LSTM作為多元預(yù)測機(jī)制和單元預(yù)測機(jī)制的應(yīng)用廣泛,可以用于預(yù)測股票價(jià)格、氣象數(shù)據(jù)、交通流量等多個領(lǐng)域的數(shù)據(jù)。
(文末有復(fù)制粘貼即可運(yùn)行的代碼)
LSTM的預(yù)測效果圖
這里先給大家展示一下LSTM的預(yù)測效果圖(這里的預(yù)測指的是預(yù)測未知的數(shù)據(jù)并不是在測試集或者驗(yàn)證集上的預(yù)測),其中MAE誤差為0.15,ME誤差為-0.03。
其誤差損失圖為,其為MAE的誤差圖
LSTM機(jī)制
LSTM(長短期記憶,Long Short-Term Memory)是一種用于處理序列數(shù)據(jù)的深度學(xué)習(xí)模型,屬于循環(huán)神經(jīng)網(wǎng)絡(luò)(RNN)的一種變體,其使用一種類似于搭橋術(shù)結(jié)構(gòu)的RNN單元。相對于普通的RNN,LSTM引入了門控機(jī)制,能夠更有效地處理長期依賴和短期記憶問題,是RNN網(wǎng)絡(luò)中最常使用的Cell之一。
了解LSTM的結(jié)構(gòu)
LSTM通過刻意的設(shè)計(jì)來實(shí)現(xiàn)學(xué)習(xí)序列關(guān)系的同時(shí),又能夠避免長期依賴的問題。它的結(jié)構(gòu)示意圖如下所示。
在LSTM的結(jié)構(gòu)示意圖中,每一條黑線傳輸著一整個向量,從一個節(jié)點(diǎn)的輸出到其他節(jié)點(diǎn)的輸入。其中“+”號代表著運(yùn)算操作(如矢量的和),而矩形代表著學(xué)習(xí)到的神經(jīng)網(wǎng)絡(luò)層。匯合在一起的線表示向量的連接,分叉的線表示內(nèi)容被復(fù)制,然后分發(fā)到不同的位置。
如果上面的LSTM結(jié)構(gòu)圖你看著很難理解,但是其實(shí)LSTM的本質(zhì)就是一個帶有tanh激活函數(shù)的簡單RNN,如下圖所示。
LSTM這種結(jié)構(gòu)的原理是引入一個稱為細(xì)胞狀態(tài)的連接。這個狀態(tài)細(xì)胞用來存放想要的記憶的東西(對應(yīng)簡單LSTM結(jié)構(gòu)中的h,只不過這里面不再只保存上一次狀態(tài)了,而是通過網(wǎng)絡(luò)學(xué)習(xí)存放那些有用的狀態(tài)),同時(shí)在加入三個門,分別是。
? ? ? ? 忘記門:決定什么時(shí)候?qū)⒁郧暗臓顟B(tài)忘記。
? ? ? ? 輸入門:決定什么時(shí)候?qū)⑿碌臓顟B(tài)加進(jìn)來。
? ? ? ? 輸出門:決定什么時(shí)候需要把狀態(tài)和輸入放在一起輸出。
從字面上可以看出,由于三個門的操作,LSTM在狀態(tài)的更新和狀態(tài)是否要作為輸入,全部交給了神經(jīng)網(wǎng)絡(luò)的訓(xùn)練機(jī)制來選擇。
下面分別來介紹一下三個門的結(jié)構(gòu)和作用。
忘記門
下圖所示為忘記門的操作,忘記門決定模型會從細(xì)胞狀態(tài)中丟棄什么信息。
忘記門會讀取前一序列模型的輸出和當(dāng)前模型的輸入來控制細(xì)胞狀態(tài)中的每個數(shù)是否保留。
例如:在一個語言模型的例子中,假設(shè)細(xì)胞狀態(tài)會包含當(dāng)前主語的性別,于是根據(jù)這個狀態(tài)便可以選擇正確的代詞。當(dāng)我們看到新的主語時(shí),應(yīng)該把新的主語在記憶中更新。忘記們的功能就是先去記憶中找到一千那個舊的主語(并沒有真正執(zhí)行忘記的操作,只是找到而已。
在上圖的LSTM的忘記門中,代表忘記門的輸出,?α代表激活函數(shù),代表忘記門的權(quán)重,代表當(dāng)前模型的輸入,代表前一個序列模型的輸出,代表忘記門的偏置。
輸入門
輸入門可以分為兩部分功能,一部分是找到那些需要更新的細(xì)胞狀態(tài)。另一部分是把需要更新的信息更新到細(xì)胞狀態(tài)里
在上面輸入門的結(jié)構(gòu)中,代表要更新的細(xì)胞狀態(tài),α代表激活函數(shù),代表當(dāng)前模型的輸入,代表前一個序列模型的輸出,代表計(jì)算的權(quán)重,代表計(jì)算的偏置,代表使用tanh所創(chuàng)建的新細(xì)胞狀態(tài),代表計(jì)算的權(quán)重,代表計(jì)算的偏置。
忘記門找到了需要忘掉的信息后,在將它與舊狀態(tài)相乘,丟棄確定需要丟棄的信息。(如果需要丟棄對應(yīng)位置權(quán)重設(shè)置為0),然后,將結(jié)果加上?*?使細(xì)胞狀態(tài)獲得新的信息。這樣就完成了細(xì)胞狀態(tài)的更新,如下圖輸入門的更新圖所示。
再上圖LSTM輸入門的更新圖中,代表忘記門的輸出結(jié)果,?代表忘記門的輸出結(jié)果,代表前一個序列模型的細(xì)胞狀態(tài),代表要更新的細(xì)胞狀態(tài),代表使用tanh所創(chuàng)建的新細(xì)胞狀態(tài)。
輸出門
如下圖LSTM的輸出門結(jié)構(gòu)圖所示,在輸出門中,通過一個激活函數(shù)層(實(shí)際使用的是Sigmoid激活函數(shù))來確定哪個部分的信息將輸出,接著把細(xì)胞狀態(tài)通過tanh進(jìn)行處理(得到一個在-1~1的值),并將它和Sigmoid門的輸出相乘,得出最終想要輸出的那個部分,例如,在語言模型中,假設(shè)已經(jīng)輸入了一個代詞,便會計(jì)算出需要輸出一個與該代詞相關(guān)的信息(詞向量)
在LSTM的輸出門結(jié)構(gòu)圖中,代表要輸出的信息,α代表激活函數(shù),代表計(jì)算?的權(quán)重,代表計(jì)算的偏置,代表更新后的細(xì)胞狀態(tài),代表當(dāng)前序列模型的輸出結(jié)果。
LSTM的變體
只有忘記門的LSTM單元
只有忘記門的JANET)單元也是LSTM單元的一個變種,發(fā)布于2018年。該單元結(jié)構(gòu)源于一個大膽的猜測——當(dāng)LSTM只有忘記門時(shí)會如何?
實(shí)驗(yàn)表明,只有忘記門的網(wǎng)絡(luò)性能居然優(yōu)于標(biāo)準(zhǔn)LSTM單元。同樣,該優(yōu)化方式也可以被用于其他RNN結(jié)構(gòu)。
如果大家想了解更多LSTM的變體,可以查看一下其他相關(guān)資料例如該論文。
獨(dú)立循環(huán)(IndRNN)單元
獨(dú)立循環(huán)單元是一種新型的循環(huán)神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)單元結(jié)構(gòu),發(fā)布于2018年其效果和速度均優(yōu)于LSTM的單元。
IndRNN單元不但可以有效解決傳統(tǒng)RNN模型存在的梯度爆炸和梯度小時(shí)問題,而且能夠更好地學(xué)習(xí)樣本中的長期依賴關(guān)系。
在搭建模型時(shí):
? ? ? ? 可以用堆疊、殘差、全連接的方式使用IndRNN單元,搭建更深的網(wǎng)絡(luò)結(jié)構(gòu);
? ? ? ? 將IndRNN單元配合ReLU等非飽和激活函數(shù)一起使用,會使模型表現(xiàn)出更好的魯棒性
雙向RNN結(jié)構(gòu)(LSTM)
雙向RNN又稱Bi-RNN,是采用兩個方向的RNN模型
RNN模型擅長的是對連續(xù)數(shù)據(jù)的處理,既然是連續(xù)的數(shù)據(jù),那么模型不但可以學(xué)習(xí)它的正向特征,而且可以學(xué)習(xí)它的反向特征,這種將正向和反向結(jié)合的結(jié)構(gòu),會比單向的循環(huán)網(wǎng)絡(luò)有更高的擬合度。
雙向RNN的處理過程就是在正向傳播的基礎(chǔ)上再進(jìn)行一次反向傳播。正向傳播和反向傳播都連接著一個輸出層。這個結(jié)構(gòu)提供給輸出層輸入序列中每一個點(diǎn)的完整的過去和未來的上下文信息。如圖所示是一個沿著事件展開的雙向循環(huán)神經(jīng)網(wǎng)絡(luò)(我們用LSTM舉例實(shí)際可以是其他任何一個RNN結(jié)構(gòu))
雙向RNN會比單項(xiàng)RNN多一個隱藏層,6個獨(dú)特的權(quán)值在每一個時(shí)步被重復(fù)利用,6個權(quán)值分別對應(yīng):輸入到向前和向后隱含層,隱含層到隱含層自身,向前和向后隱含層到輸出層。
有關(guān)雙向RNN(LSTM)我會在后續(xù)文章中講到,這里只是大致介紹一下。?
(在大多數(shù)應(yīng)用中,基于時(shí)間序列的分析和有關(guān)NLP中自動回答類的一些問題中,一般是以雙向LSTM配合單向LSTM或RNN橫向擴(kuò)展來實(shí)現(xiàn),效果非常好)
運(yùn)行代碼
下面的代碼大家可以賦值到一個py文件中輸入自己的文件預(yù)測文件即可運(yùn)行,這只是一個簡單LSTM模型搭建,其可以搭配許多其他模塊進(jìn)行組合例如:注意力機(jī)制(TPA)、其他的網(wǎng)絡(luò)結(jié)構(gòu)(GRU)、雙向的RNN結(jié)構(gòu)等。
import time
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
from matplotlib import pyplot as plt
from sklearn.preprocessing import MinMaxScaler
np.random.seed(0)
def calculate_mae(y_true, y_pred):
# 平均絕對誤差
mae = np.mean(np.abs(y_true - y_pred))
return mae
true_data = pd.read_csv('ETTh1-Test.csv') # 填你自己的數(shù)據(jù)地址
target = 'OT'
# 這里加一些數(shù)據(jù)的預(yù)處理, 最后需要的格式是pd.series
true_data = np.array(true_data['OT'])
# 定義窗口大小
test_data_size = 32
# 訓(xùn)練集和測試集的尺寸劃分
test_size = 0.15
train_size = 0.85
# 標(biāo)準(zhǔn)化處理
scaler_train = MinMaxScaler(feature_range=(0, 1))
scaler_test = MinMaxScaler(feature_range=(0, 1))
train_data = true_data[:int(train_size * len(true_data))]
test_data = true_data[-int(test_size * len(true_data)):]
print("訓(xùn)練集尺寸:", len(train_data))
print("測試集尺寸:", len(test_data))
train_data_normalized = scaler_train.fit_transform(train_data.reshape(-1, 1))
test_data_normalized = scaler_test.fit_transform(test_data.reshape(-1, 1))
# 轉(zhuǎn)化為深度學(xué)習(xí)模型需要的類型Tensor
train_data_normalized = torch.FloatTensor(train_data_normalized).view(-1)
test_data_normalized = torch.FloatTensor(test_data_normalized).view(-1)
def create_inout_sequences(input_data, tw, pre_len):
inout_seq = []
L = len(input_data)
for i in range(L - tw):
train_seq = input_data[i:i + tw]
if (i + tw + 4) > len(input_data):
break
train_label = input_data[i + tw:i + tw + pre_len]
inout_seq.append((train_seq, train_label))
return inout_seq
pre_len = 4
train_window = 16
# 定義訓(xùn)練器的的輸入
train_inout_seq = create_inout_sequences(train_data_normalized, train_window, pre_len)
class LSTM(nn.Module):
def __init__(self, input_dim=1, hidden_dim=350, output_dim=1):
super(LSTM, self).__init__()
self.hidden_dim = hidden_dim
self.lstm = nn.LSTM(input_dim, hidden_dim, batch_first=True)
self.fc = nn.Linear(hidden_dim, output_dim)
def forward(self, x):
x = x.unsqueeze(1)
h0_lstm = torch.zeros(1, self.hidden_dim).to(x.device)
c0_lstm = torch.zeros(1, self.hidden_dim).to(x.device)
out, _ = self.lstm(x, (h0_lstm, c0_lstm))
out = out[:, -1]
out = self.fc(out)
return out
lstm_model = LSTM(input_dim=1, output_dim=pre_len, hidden_dim=train_window)
loss_function = nn.MSELoss()
optimizer = torch.optim.Adam(lstm_model.parameters(), lr=0.001)
epochs = 10
Train = True # 訓(xùn)練還是預(yù)測
if Train:
losss = []
lstm_model.train() # 訓(xùn)練模式
start_time = time.time() # 計(jì)算起始時(shí)間
for i in range(epochs):
for seq, labels in train_inout_seq:
lstm_model.train()
optimizer.zero_grad()
y_pred = lstm_model(seq)
single_loss = loss_function(y_pred, labels)
single_loss.backward()
optimizer.step()
print(f'epoch: {i:3} loss: {single_loss.item():10.8f}')
losss.append(single_loss.detach().numpy())
torch.save(lstm_model.state_dict(), 'save_model.pth')
print(f"模型已保存,用時(shí):{(time.time() - start_time) / 60:.4f} min")
plt.plot(losss)
# 設(shè)置圖表標(biāo)題和坐標(biāo)軸標(biāo)簽
plt.title('Training Error')
plt.xlabel('Epoch')
plt.ylabel('Error')
# 保存圖表到本地
plt.savefig('training_error.png')
else:
# 加載模型進(jìn)行預(yù)測
lstm_model.load_state_dict(torch.load('save_model.pth'))
lstm_model.eval() # 評估模式
results = []
reals = []
losss = []
test_inout_seq = create_inout_sequences(test_data_normalized, train_window, pre_len)
for seq, labels in train_inout_seq:
pred = lstm_model(seq)[0].item()
results.append(pred)
mae = calculate_mae(pred, labels.detach().numpy()) # MAE誤差計(jì)算絕對值(預(yù)測值 - 真實(shí)值)
reals.append(labels.detach().numpy())
losss.append(mae)
print("模型預(yù)測結(jié)果:", results)
print("預(yù)測誤差MAE:", losss)
plt.style.use('ggplot')
# 創(chuàng)建折線圖
plt.plot(results, label='real', color='blue') # 實(shí)際值
plt.plot(reals, label='forecast', color='red', linestyle='--') # 預(yù)測值
# 增強(qiáng)視覺效果
plt.grid(True)
plt.title('real vs forecast')
plt.xlabel('time')
plt.ylabel('value')
plt.legend()
plt.savefig('test——results.png')
數(shù)據(jù)格式?
運(yùn)行上述代碼需要只需要一列數(shù)據(jù)即可,并且最后數(shù)據(jù)需要是一個pd.series類型,大家可以在讀取進(jìn)來文件后面加一些數(shù)據(jù)的處理操作。
(需要注意的是大家的數(shù)據(jù)不要打亂時(shí)間順序否則預(yù)測出來的結(jié)果可能不準(zhǔn))?
代碼講解
在上面的運(yùn)行代碼中,主要填寫的是在下面填上你的數(shù)據(jù)目錄,
true_data = pd.read_csv('') # 填你自己的數(shù)據(jù)地址
這里是定義了test_data_size是指根據(jù)過去多少條數(shù)據(jù)來預(yù)測未來的預(yù)測值
test_size和train_size代表訓(xùn)練集和測試集的尺寸劃分。?
# 定義窗口大小
test_data_size = 350
# 訓(xùn)練集和測試集的尺寸劃分
test_size = 0.15
train_size = 0.85
這里定義了數(shù)據(jù)加載器的操作?
def create_inout_sequences(input_data, tw):
inout_seq = []
L = len(input_data)
for i in range(L - tw):
train_seq = input_data[i:i + tw]
if (i + tw + 4) > len(input_data):
break
train_label = input_data[i + tw:i + tw + 1]
inout_seq.append((train_seq, train_label))
return inout_seq
train_window = 350
# 定義訓(xùn)練器的的輸入
train_inout_seq = create_inout_sequences(train_data_normalized, train_window)
模型的本身其中包含一個LSTM層和一個全連接層進(jìn)行結(jié)果的輸出?
class LSTM(nn.Module):
def __init__(self, input_dim=1, hidden_dim=350, output_dim=1):
super(LSTM, self).__init__()
self.hidden_dim = hidden_dim
self.lstm = nn.LSTM(input_dim, hidden_dim, batch_first=True)
self.fc = nn.Linear(hidden_dim, output_dim)
def forward(self, x):
x = x.unsqueeze(1)
h0_lstm = torch.zeros(1, self.hidden_dim).to(x.device)
c0_lstm = torch.zeros(1, self.hidden_dim).to(x.device)
out, _ = self.lstm(x, (h0_lstm, c0_lstm))
out = out[:, -1]
out = self.fc(out)
return out
這里定義了模型損失函數(shù)和優(yōu)化器,其中的Train代表著是否進(jìn)行訓(xùn)練。?
lstm_model = LSTM()
loss_function = nn.MSELoss()
optimizer = torch.optim.Adam(lstm_model.parameters(), lr=0.001)
epochs = 20
Train = False # 訓(xùn)練還是預(yù)測
當(dāng)Train為True時(shí)則開始訓(xùn)練并在最好將模型保存到本地,訓(xùn)練好模型之后我門將Train設(shè)置為False則開始評估模式。?
if Train:
losss = []
lstm_model.train() # 訓(xùn)練模式
for i in range(epochs):
start_time = time.time() # 計(jì)算起始時(shí)間
for seq, labels in train_inout_seq:
lstm_model.train()
optimizer.zero_grad()
y_pred = lstm_model(seq)
single_loss = loss_function(y_pred, labels)
losss.append(single_loss.detach().numpy())
single_loss.backward()
optimizer.step()
if i % 1 == 1:
print(f'epoch: {i:3} loss: {single_loss.item():10.8f}')
torch.save(lstm_model.state_dict(), 'save_model.pth')
print(f"模型已保存,用時(shí):{(time.time() - start_time) / 60:.4f} min")
plt.plot(losss)
# 設(shè)置圖表標(biāo)題和坐標(biāo)軸標(biāo)簽
plt.title('Training Error')
plt.xlabel('Epoch')
plt.ylabel('Error')
# 保存圖表到本地
plt.savefig('training_error.png')
else:
# 加載模型進(jìn)行預(yù)測
lstm_model.load_state_dict(torch.load('save_model.pth'))
lstm_model.eval() # 評估模式
results = []
losss = []
test_inout_seq = create_inout_sequences(test_data_normalized, train_window)
for seq, labels in test_inout_seq:
# 這里的pred = lstm_model(seq)[0].item()是結(jié)果的輸出這里只保存了輸出的第一個值如果想拿出更多的預(yù)測值可以修改數(shù)字0用切片的方式
pred = lstm_model(seq)[0].item()
results.append(pred)
mae = calculate_mae(pred, labels.detach().numpy()) # MAE誤差計(jì)算絕對值(預(yù)測值 - 真實(shí)值)
losss.append(mae)
print("模型預(yù)測結(jié)果:", results)
print("預(yù)測誤差MAE:", losss)
后期我也會講一些最新的預(yù)測模型包括Informer,TPA-LSTM,ARIMA,XGBOOST,Holt-winter,移動平均法等等一系列關(guān)于時(shí)間序列預(yù)測的模型,包括深度學(xué)習(xí)和機(jī)器學(xué)習(xí)方向的模型我都會講,你可以根據(jù)需求選取適合你自己的模型進(jìn)行預(yù)測,如果有需要可以+個關(guān)注,包括本次模型我自己的代碼大家有需要我也會放出百度網(wǎng)盤下載鏈接!!
其它時(shí)間序列預(yù)測模型的講解!
--------------------------------------------------------MTS-Mixers---------------------------------------------------------
【全網(wǎng)首發(fā)】(MTS-Mixers)(Python)(Pytorch)最新由華為發(fā)布的時(shí)間序列預(yù)測模型實(shí)戰(zhàn)案例(一)(包括代碼講解)實(shí)現(xiàn)企業(yè)級預(yù)測精度包括官方代碼BUG修復(fù)Transform模型
?--------------------------------------------------------Holt-Winters--------------------------------------------------------
時(shí)間序列預(yù)測模型實(shí)戰(zhàn)案例(二)(Holt-Winter)(Python)結(jié)合K-折交叉驗(yàn)證進(jìn)行時(shí)間序列預(yù)測實(shí)現(xiàn)企業(yè)級預(yù)測精度(包括運(yùn)行代碼以及代碼講解)文章來源:http://www.zghlxwxcb.cn/news/detail-715995.html
如果大家有不懂的也可以評論區(qū)留言一些報(bào)錯什么的大家可以討論討論看到我也會給大家解答如何解決!
?文章來源地址http://www.zghlxwxcb.cn/news/detail-715995.html
到了這里,關(guān)于時(shí)間序列預(yù)測模型實(shí)戰(zhàn)案例(三)(LSTM)(Python)(深度學(xué)習(xí))時(shí)間序列預(yù)測(包括運(yùn)行代碼以及代碼講解)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!