長短期記憶網路(LSTM,Long Short-Term Memory)
使用kears 搭建一個LSTM預測模型,使用2022年美國大學生數學建模大賽中C題中處理後的BTC比特幣的資料進行資料訓練和預測。
這篇部落格包含兩個預測,一種是使用前N天的資料預測後一天的資料,一種使用前N天的資料預測後N天的資料
第一種:使用前個三十天資料進行預測後一天的資料。
總資料集:1826個資料
資料下載地址:需要的可以自行下載,很快
- 連結:https://pan.baidu.com/s/1TmQxLfzHiyOL3vEVcuWlgQ
- 提取碼:wy0f
模型結構
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm (LSTM) (None, 30, 64) 16896
_________________________________________________________________
lstm_1 (LSTM) (None, 30, 128) 98816
_________________________________________________________________
lstm_2 (LSTM) (None, 32) 20608
_________________________________________________________________
dropout (Dropout) (None, 32) 0
_________________________________________________________________
dense (Dense) (None, 1) 33
=================================================================
Total params: 136,353
Trainable params: 136,353
Non-trainable params: 0
_________________________________________________________________
訓練100次:
損失函式影像:
預測和真實值比較,可以看到效果並不是很好,這個需要自己調參進行變化
- 我的GPU加速時1650還挺快,7.5算力,訓練時間可以接受
程式碼:
# 呼叫庫
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from tensorflow import keras
from tensorflow.keras import layers
from sklearn.preprocessing import MinMaxScaler
#### 資料處理部分 ####
# 讀入資料
data = pd.read_excel('BTCtest.xlsx')
# 時間戳長度
time_step = 30 # 輸入序列長度
print(len(data))
# 劃分訓練集與驗證集
data = data[['Value']]
train = data[0:1277]
valid = data[1278:1550]
test = data[1551:]
# 歸一化
scaler = MinMaxScaler(feature_range=(0, 1))
# datas 切片資料 time_step要輸入的維度 pred 預測維度
def scalerClass(datas,scaler,time_step,pred):
x, y = [], []
scaled_data = scaler.fit_transform(datas)
for i in range(time_step, len(datas) - pred):
x.append(scaled_data[i - time_step:i])
y.append(scaled_data[i: i + pred])
# 把x_train轉變為array陣列
x, y = np.array(x), np.array(y).reshape(-1, 1) # reshape(-1,5)的意思時不知道分成多少行,但是是五列
return x,y
# 訓練集 驗證集 測試集 切片
x_train,y_train = scalerClass(train,scaler,time_step=time_step,pred=1)
x_valid, y_valid = scalerClass(valid,scaler,time_step=time_step,pred=1)
x_test, y_test = scalerClass(test,scaler,time_step=time_step,pred=1)
#### 建立神經網路模型 ####
model = keras.Sequential()
model.add(layers.LSTM(64, return_sequences=True, input_shape=(x_train.shape[1:])))
model.add(layers.LSTM(128, return_sequences=True))
model.add(layers.LSTM(32))
model.add(layers.Dropout(0.3))
model.add(layers.Dense(1))
# model.compile(optimizer = 優化器,loss = 損失函式, metrics = ["準確率”])
# “adam" 或者 tf.keras.optimizers.Adam(lr = 學習率,decay = 學習率衰減率)
# ”mse" 或者 tf.keras.losses.MeanSquaredError()
model.compile(optimizer=keras.optimizers.Adam(), loss='mse',metrics=['accuracy'])
# monitor:要監測的數量。
# factor:學習速率降低的因素。new_lr = lr * factor
# patience:沒有提升的epoch數,之後學習率將降低。
# verbose:int。0:安靜,1:更新訊息。
# mode:{auto,min,max}之一。在min模式下,當監測量停止下降時,lr將減少;在max模式下,當監測數量停止增加時,它將減少;在auto模式下,從監測數量的名稱自動推斷方向。
# min_delta:對於測量新的最優化的閥值,僅關注重大變化。
# cooldown:在學習速率被降低之後,重新恢復正常操作之前等待的epoch數量。
# min_lr:學習率的下限
learning_rate= keras.callbacks.ReduceLROnPlateau(monitor='val_loss', patience=3, factor=0.7, min_lr=0.00000001)
#顯示模型結構
model.summary()
# 訓練模型
history = model.fit(x_train, y_train,
batch_size = 128,
epochs=100,
validation_data=(x_valid, y_valid),
callbacks=[learning_rate])
# loss變化趨勢視覺化
plt.title('LSTM loss figure')
plt.plot(history.history['loss'],label='training loss')
plt.plot(history.history['val_loss'], label='val loss')
plt.legend(loc='upper right')
plt.show()
#### 預測結果分析&視覺化 ####
# 輸入測試資料,輸出預測結果
y_pred = model.predict(x_test)
# 輸入資料和標籤,輸出損失和精確度
model.evaluate(x_test)
scaler.fit_transform(pd.DataFrame(valid['Value'].values))
# 反歸一化
y_pred = scaler.inverse_transform(y_pred.reshape(-1,1)[:,0].reshape(1,-1)) #只取第一列
y_test = scaler.inverse_transform(y_test.reshape(-1,1)[:,0].reshape(1,-1))
# 預測效果視覺化
plt.figure(figsize=(16, 8))
plt.title('Predicted and real')
dict = {
'Predictions': y_pred[0],
'Value': y_test[0]
}
data_pd = pd.DataFrame(dict)
plt.plot(data_pd[['Value']],linewidth=3,alpha=0.8)
plt.plot(data_pd[['Predictions']],linewidth=1.2)
#plt.savefig('lstm.png', dpi=600)
plt.show()
預測後幾天的資料和預測後一天原理是一樣的
- 因為預測的是5天的資料所以不能使用影像顯示出來,只能取出預測五天的頭一天的資料進行繪圖。資料結構可以列印出來的,我沒有反歸一化,需要的時候再弄把
- 前五十天預測五天的程式碼:
# 呼叫庫
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from tensorflow import keras
from tensorflow.keras import layers
from sklearn.preprocessing import MinMaxScaler
# 讀入資料
data = pd.read_excel('BTCtest.xlsx')
time_step = 50 # 輸入序列長度
# 劃分訓練集與驗證集
data = data[['Value']]
train = data[0:1277] #70%
valid = data[1278:1550] #15%
test = data[1551:] #15%
# 歸一化
scaler = MinMaxScaler(feature_range=(0, 1))
# 定義一個切片函式
# datas 切片資料 time_step要輸入的維度 pred 預測維度
def scalerClass(datas,scaler,time_step,pred):
x, y = [], []
scaled_data = scaler.fit_transform(datas)
for i in range(time_step, len(datas) - pred):
x.append(scaled_data[i - time_step:i])
y.append(scaled_data[i: i + pred])
# 把x_train轉變為array陣列
x, y = np.array(x), np.array(y).reshape(-1, 5) # reshape(-1,5)的意思時不知道分成多少行,但是是五列
return x,y
# 訓練集 驗證集 測試集 切片
x_train,y_train = scalerClass(train,scaler,time_step=time_step,pred=5)
x_valid, y_valid = scalerClass(valid,scaler,time_step=time_step,pred=5)
x_test, y_test = scalerClass(test,scaler,time_step=time_step,pred=5)
# 建立網路模型
model = keras.Sequential()
model.add(layers.LSTM(64, return_sequences=True, input_shape=(x_train.shape[1:])))
model.add(layers.LSTM(64, return_sequences=True))
model.add(layers.LSTM(32))
model.add(layers.Dropout(0.1))
model.add(layers.Dense(5))
model.compile(optimizer=keras.optimizers.Adam(), loss='mse',metrics=['accuracy'])
learning_rate_reduction = keras.callbacks.ReduceLROnPlateau(monitor='val_loss', patience=3, factor=0.7, min_lr=0.000000005)
model.summary()
history = model.fit(x_train, y_train,
batch_size = 128,
epochs=30,
validation_data=(x_valid, y_valid),
callbacks=[learning_rate_reduction])
# loss變化趨勢視覺化
plt.title('LSTM loss figure')
plt.plot(history.history['loss'],label='training loss')
plt.plot(history.history['val_loss'], label='val loss')
plt.legend(loc='upper right')
plt.show()
#### 預測結果分析&視覺化 ####
y_pred = model.predict(x_test)
model.evaluate(x_test)
scaler.fit_transform(pd.DataFrame(valid['Value'].values))
print(y_pred)
print(y_test)
# 預測效果視覺化
# 反歸一化
y_pred = scaler.inverse_transform(y_pred.reshape(-1,5)[:,0].reshape(1,-1)) #只取第一列
y_test = scaler.inverse_transform(y_test.reshape(-1,5)[:,0].reshape(1,-1))
plt.figure(figsize=(16, 8))
plt.title('Predicted and real')
dict_data = {
'Predictions': y_pred.reshape(1,-1)[0],
'Value': y_test[0]
}
data_pd = pd.DataFrame(dict_data)
plt.plot(data_pd[['Value']],linewidth=3,alpha=0.8)
plt.plot(data_pd[['Predictions']],linewidth=1.2)
plt.savefig('lstm.png', dpi=600)
plt.show()