鄭重聲明,文章大部分翻譯自:
Time Series Prediction with LSTM Recurrent Neural Networks in Python with Keras
本文目錄:
* 1.導入相應庫文件及數據情況
* 2.標准化數據,划分數據
* 3.生成樣本
* 4.構建LSTM網絡
* 5.查看模型效果
* 6.預測未來的數據
* 7.擴展
數據: 1949到1960共12年,每年12個月的數據,一共 144 個數據,單位是 1000, 原文數據下載在這里
目標: 預測國際航班未來 1 個月的乘客數
1.導入相應庫文件及數據情況
- #導入相應的庫
- import numpy
- import matplotlib.pyplot as plt
- from pandas import read_csv
- import math
- from keras.models import Sequential
- from keras.layers import Dense
- from keras.layers import LSTM
- from keras.utils import plot_model
- from sklearn.preprocessing import MinMaxScaler
- from sklearn.metrics import mean_squared_error
- from IPython.display import SVG
- from keras.utils.vis_utils import model_to_dot
- #將數據存儲為兩個矩陣,一個矩陣的ind位置存儲t時刻的值,另一個矩陣存儲t+1時刻的值
- def create_dataset(dataset, look_back=1):
- dataX, dataY = [], []
- for i in range(len(dataset)-look_back-1):
- a = dataset[i:(i+look_back), 0]
- dataX.append(a)
- dataY.append(dataset[i + look_back, 0])
- return numpy.array(dataX), numpy.array(dataY)
- # fix random seed for reproducibility
- numpy.random.seed(7)
- #讀取數據
- dataframe = read_csv('international-airline-passengers.csv', usecols=[1], engine='python', skipfooter=3)
- dataset = dataframe.values
- dataset = dataset.astype('float32')
- #查看數據集
- print('樣本中的前面兩個數據: \n',dataset[0:2])
- print('整個樣本的規模: ',len(dataset))
- plt.plot(dataset)
- plt.show()
輸出:
樣本中的前面兩個數據:
[[112.] [118.]]
整個樣本的規模: 144

2.標准化數據,划分數據
- #LSTM對輸入數據的規模很敏感,特別是在使用sigmoid(默認)或tanh激活函數時。
- #將數據重新調整到0到1的范圍(也稱為標准化)可能是一種很好的做法。
- scaler = MinMaxScaler(feature_range=(0, 1))
- dataset = scaler.fit_transform(dataset)
- # 划分訓練集與測試集,這里使用67%的原始數據作為訓練數據,剩下33%作為測試數據
- train_size = int(len(dataset) * 0.67)
- test_size = len(dataset) - train_size
- train, test = dataset[0:train_size,:], dataset[train_size:len(dataset),:]
- print('划分數據集后的得到的訓練數據和測試數據(訓練數據未有標簽): ',train.shape,test.shape)
輸出:
划分數據集后的得到的訓練數據和測試數據(訓練數據未有標簽): (96, 1) (48, 1)
3.生成樣本
- # 生成[t,t+look_back]時間間隔和t+look_back時刻的兩個矩陣
- look_back = 1
- trainX, trainY = create_dataset(train, look_back)
- testX, testY = create_dataset(test, look_back)
- print(trainX[:2], trainY[:2])
- # 數據被Reshape成 [samples, time steps, features],這是放入LSTM的shape
- trainX = numpy.reshape(trainX, (trainX.shape[0], 1, trainX.shape[1]))
- testX = numpy.reshape(testX, (testX.shape[0], 1, testX.shape[1]))
- print('構造得到模型的輸入數據(訓練數據已有標簽trainY): ',trainX.shape,testX.shape)
輸出:
[[0.01544401] #第一個月份數據
[0.02702703]] #第二個月份數據
[0.02702703 0.05405405] #每個樣本在模型上的應該得到的輸出
構造得到模型的輸入數據(訓練數據已有標簽trainY): (95, 1, 1) (47, 1, 1)
這里解釋下數據為什么這樣划分?
前面我們已經說明了,我們是基於歷史數據預測下一時刻的數據,但是每次依賴多少歷史數據,我們沒有說.這個例子的參數look_back=1
設置說明歷史數據是1,也就是基於前一個月份數據預測下一個月份數據.下面我以第一年的數據說明數據划分情況.

當我們基於1個歷史數據預測下一個值時,樣本划分就像圖示的藍,紅框,藍色框表示輸入模型的數據,紅色表示希望
模型輸出的數據(當然只是希望,會有偏差,后面我們用均方根誤差來衡量模型真實輸出和這個值的差距).藍,紅框在所有的數據上滑動,得到類似上面的數據划分情況.
當然,你也可以改動這個look_back
這個值,基於歷史多少數據來預測下一個數據可以自己設定.
注意:本來訓練數據和測試數據分別有96,48個,但是經過這樣划分后都減少1個,分別為95,47.這是因為最后一個數據沒有標簽.但是測試數據沒有必要這樣分,因為他不需要標簽,這里分的意思是利用分到的標簽用於計算模型在測試數據上的均方根誤差.
4.構建LSTM網絡
- #構建LSTM網絡
- model = Sequential()
- model.add(LSTM(4, input_shape=(1, look_back)))
- model.add(Dense(1))
- #編譯訓練LSTM網絡
- model.compile(loss='mean_squared_error', optimizer='adam')
- model.fit(trainX, trainY, epochs=50, batch_size=1, verbose=1)
- #打印模型
- model.summary()
- #保存模型
- SVG(model_to_dot(model,show_shapes=True).create(prog='dot', format='svg'))
輸出:
- Epoch 1/50
- 95/95 [==============================] - 2s 18ms/step - loss: 0.0406
- Epoch 2/50
- 95/95 [==============================] - 1s 6ms/step - loss: 0.0199
- Epoch 3/50
- 95/95 [==============================] - 1s 6ms/step - loss: 0.0147
- ........后面直到50次省略
- ______________________________________________________________________________________
- Layer (type) Output Shape Param #
- ======================================================================================
- lstm_7 (LSTM) (None, 4) 96
- ______________________________________________________________________________________
- dense_7 (Dense) (None, 1) 5
- ======================================================================================
- Total params: 101
- Trainable params: 101
- Non-trainable params: 0
- ______________________________________________________________________________________

5.查看模型效果
- # 使用已訓練的模型進行預測
- trainPredict = model.predict(trainX)
- testPredict = model.predict(testX)
- # 預測的值是[0,1]這樣的標准化數據,需要將該值轉換回原始值
- trainPredict = scaler.inverse_transform(trainPredict)
- trainY = scaler.inverse_transform([trainY])
- testPredict = scaler.inverse_transform(testPredict)
- testY = scaler.inverse_transform([testY])
- # 計算預測的均方根誤差
- trainScore = math.sqrt(mean_squared_error(trainY[0], trainPredict[:,0]))
- print('Train Score: %.2f RMSE' % (trainScore))
- testScore = math.sqrt(mean_squared_error(testY[0], testPredict[:,0]))
- print('Test Score: %.2f RMSE' % (testScore))
- # 畫圖:對訓練數據的預測
- trainPredictPlot = numpy.empty_like(dataset)
- trainPredictPlot[:, :] = numpy.nan
- trainPredictPlot[look_back:len(trainPredict)+look_back, :] = trainPredict
- # 畫圖:對測試數據的預測
- testPredictPlot = numpy.empty_like(dataset)
- testPredictPlot[:, :] = numpy.nan
- #testPredictPlot[len(trainPredict)+(look_back*2)+1:len(dataset)-1, :] = testPredict
- testPredictPlot[len(trainPredict)+look_back:len(dataset)-1, :] = testPredict
- # 顯示圖片
- plt.plot(scaler.inverse_transform(dataset),color='blue',label='Raw data')
- plt.plot(trainPredictPlot,color='red',label='Train process')
- plt.plot(testPredictPlot,color='green',label='Test process')
- #在折線圖上顯示標簽
- leg = plt.legend(loc='best', ncol=1, fancybox=True)
- leg.get_frame().set_alpha(0.5)
- plt.show()
輸出:
Train Score: 23.39 RMSE #訓練數據的均方根誤差
Test Score: 46.92 RMSE #測試數據的均方根誤差

藍色線是原始數據
,紅色是訓練數據的預測情況
,綠色是測試數據的預測情況
,紅色和綠色線越靠近藍色線,表示模型對數據擬合能力越好.
6.預測未來的數據
最后一個數據集的下一個月情況沒有被預測,現把它拿到后進行預測.
- #測試數據的最后一個數據沒有預測,這里補上
- finalX = numpy.reshape(test[-1], (1, 1, testX.shape[1]))
- #預測得到標准化數據
- featruePredict = model.predict(finalX)
- #將標准化數據轉換為人數
- featruePredict = scaler.inverse_transform(featruePredict)
- #原始數據是1949-1960年的數據,下一個月是1961年1月份
- print('模型預測1961年1月份的國際航班人數是: ',featruePredict)
輸出:
模型預測1961年1月份的國際航班人數是: [[430.27188]]
7.擴展
模型有些參數可以自己手動調一下,看看模型在不同參數下的效果(雖然我估計數據量太少,可能調參帶來的變化不是很大,但是可以體驗調參的過程),下面我就可以調的參數說明:
(1)損失函數現在使用的是
mean_squared_error
,可以調成別的
(2)優化器是adam
,也可以調,甚至對優化器內的參數進行調整(比如學習率)
(3)訓練次數是50,可以調低點(因為我看后面模型的損失不下降了)
(4)基於歷史多少數據的參數look_back
可調,你可以設置為3,5.....
全部代碼可以在這里找到.