赞
踩
上一篇博客讲了基于LSTM不同类型的时间预测,这篇文档使用pytorch 动手实现如何基于LSTM模型单变量时间预测。同样使用sns flight(数据网盘下载链接见文末) 作为数据源,这里将数据下载下来存放在本机中。
首先读取存储在本机中的flights.csv数据:
-
import torch
-
import torch.nn
as nn
-
import numpy
as np
-
import pandas
as pd
-
import matplotlib.pyplot
as plt
-
%matplotlib inline
-
-
flight_data = pd.read_csv('flights.csv')
-
flight_data.shape #(
144,
3)
绘制数据看看大致的走向
-
fig_
size
= plt.rcParams[
"figure.figsize"]
-
fig_
size[
0]
=
15
-
fig_
size[
1]
=
5
-
plt.rcParams[
"figure.figsize"]
= fig_
size
-
plt.title(
'Month vs Passenger')
-
plt.ylabel(
'Total Passengers')
-
plt.xlabel(
'Months')
-
plt.grid(
True)
-
plt.autoscale(axis
=
'x',tight
=
True)
-
plt.plot(flight_
data[
'passengers'])
-
# 提取所有乘客量的数据
-
all_data = flight_data[
'passengers'].values.astype(
float)
-
print(all_data)
-
-
'''输出为
-
[112. 118. 132. 129. 121. 135. 148. 148. 136. 119. 104. 118. 115. 126.
-
141. 135. 125. 149. 170. 170. 158. 133. 114. 140. 145. 150. 178. 163.
-
172. 178. 199. 199. 184. 162. 146. 166. 171. 180. 193. 181. 183. 218.
-
230. 242. 209. 191. 172. 194. 196. 196. 236. 235. 229. 243. 264. 272.
-
237. 211. 180. 201. 204. 188. 235. 227. 234. 264. 302. 293. 259. 229.
-
203. 229. 242. 233. 267. 269. 270. 315. 364. 347. 312. 274. 237. 278.
-
284. 277. 317. 313. 318. 374. 413. 405. 355. 306. 271. 306. 315. 301.
-
356. 348. 355. 422. 465. 467. 404. 347. 305. 336. 340. 318. 362. 348.
-
363. 435. 491. 505. 404. 359. 310. 337. 360. 342. 406. 396. 420. 472.
-
548. 559. 463. 407. 362. 405. 417. 391. 419. 461. 472. 535. 622. 606.
-
508. 461. 390. 432.]
-
'''
可知,共有144个数据,我们将前132个数据作为训练集,后12个数据作为测试集
-
#将数据区分为训练数据和测试数据
-
test_
data_
size
=
12
-
train_
data
=
all_
data[:-
test_
data_
size]
-
test_
data
=
all_
data[-
test_
data_
size:]
根据数据绘制结果,可以发现数据呈现较快的增长趋势,范围跨度也比较大,为便于训练,我们将训练数据进行归一化处理,归一化的方法是min/max方法,该方法归一化后的数据值域为[-1, 1]。这里注意,我们仅仅对训练数据进行归一化而未考虑测试数据,这样做的目的为了防止信息从训练数据泄露到的测试数据。
-
# 由于训练数据存在相差较大的,因此使用min
/max尺度变换对训练数据进行归一化
-
# 注意只对训练数据进行归一化,为了防止有些信息从训练数据泄露到的测试数据
-
from sklearn.preprocessing import MinMaxScaler
-
-
scaler
= MinMaxScaler(feature_range
=(-
1,
1))
-
train_
data_normalized
= scaler.fit_transform(train_
data .reshape(-
1,
1))
现在我们看看归一化以后的数据,全部位于[-1,1]区间内:
-
#现在可以看到所有训练值都在[-1,1]的范围内
-
print(train_data_normalized[:
5])
-
print(train_data_normalized[-
5:])
-
-
'''输出
-
[[-0.96483516]
-
[-0.93846154]
-
[-0.87692308]
-
[-0.89010989]
-
[-0.92527473]]
-
[[1. ]
-
[0.57802198]
-
[0.33186813]
-
[0.13406593]
-
[0.32307692]]
-
'''
然后将数据转化为tensor 以便于训练
-
# 将数据转换为torch tensor
-
train_
data_normalized
= torch.FloatTensor(train_
data_normalized).view(-
1)
这里我们的目的是使用LSTM进行时间序列预测,那么我们首先需要明确输入LSTM的时间序列的步长,由于我们输出的是多个年份的数据、且数据以月为单位,所以可以很自然想到使用12作为我们输入时间序列长度。通过当前12个数据预测第13个数据的值。以sequence length 为参数,我们构建一个函数来提取时间序列的输入输出:
-
# 由于我们使用的是以月为单位的乘客数据,所以很自然想到的是
-
# 使用
12 作为一个时间序列,我们使用
12个月的数据来预测第
13个月的乘客量
-
# 定义如下函数来构建输入输出
-
# @tw 为
sequence
length
-
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]
-
train_label
=
input_
data[i
+tw:i
+tw
+
1]
-
inout_seq.append((train_seq ,train_label))
-
return inout_seq
定义sequence length 为12
-
train_window
=
12
-
train_inout_seq
= create_inout_sequences(train_
data_normalized, train_window)
接下来我们定义LSTM模型,注意LSTM模型的输出大小为(sequence_legth,bacth_size,hidden_size),其中输出指的是隐藏层在各个时间步上计算并输出的隐藏状态,它们通常作为后续输出层的输入。需要强调的是,该“输出”本身并不涉及输出层计算,形状为(时间步数, 批量大小, 隐藏单元个数)。所以这里在输出后面再加一个线性层来获得最终输出。
-
class LSTM(nn.Module):
-
def __init__(
self,
input_
size
=
1, hidden_
size
=
100,
output_
size
=
1):
-
super().__init__()
-
self.hidden_
size
= hidden_
size
-
# 定义lstm 层
-
self.lstm
= nn.LSTM(
input_
size, hidden_
size)
-
# 定义线性层,即在LSTM的的输出后面再加一个线性层
-
self.linear
= nn.Linear(hidden_
size,
output_
size)
-
-
#
input_seq参数表示输入
sequence
-
def forward(
self,
input_seq):
-
-
# lstm默认的输入X是(
sequence_legth,bacth_
size,
input_
size)
-
lstm_out,
self.hidden_cell
=
self.lstm(
input_seq.view(len(
input_seq) ,
1, -
1),
self.hidden_cell)
-
-
# lstm_out的默认大小是(
sequence_legth,bacth_
size,hidden_
size)
-
# 转化之后lstm_out的大小是(
sequence_legth, bacth_
size
*hidden_
size)
-
predictions
=
self.linear(lstm_out.view(len(
input_seq), -
1))
-
-
# 由于bacth_
size
=
1, 可知predictions 的维度为(
sequence_legth,
output_
size)
-
# [-
1] 表示的是取最后一个时间步长对应的输出
-
return predictions[-
1]
-
-
''
'
-
# 另一种常见的lstm_out的变换方式是将其转换为(sequence_legth* batch_size, num_hiddens)
-
即
-
predictions = self.linear(lstm_out.view(-1, lstm_out.shape[-1]))
-
return predictions[-1]
-
但是,当batch_size!= 1 时,这种转换在这里就不合适了,因为这里的predictions[-1] 对应的并不是最后一个步长的输出。这种转换更常用于预测每个步长都有对应输出的情况,如http://tangshusen.me/Dive-into-DL-PyTorch/#/chapter06_RNN/6.4_rnn-scratch 中的 “6.4.7 定义模型训练函数” 对应的就是这种情况。
-
-
'
''
接下来,定义模型实例、损失函数、优化函数
-
# 模型实例化并定义损失函数和优化函数
-
model = LSTM()
-
loss_function = nn.MSELoss()
-
optimizer = torch.optim.Adam(model.parameters(), lr=
0.001)
-
print(model)
-
-
'''输出
-
LSTM(
-
(lstm): LSTM(1, 100)
-
(linear): Linear(in_features=100, out_features=1, bias=True)
-
)
-
'''
训练模型,设置迭代的次数为150, 这里的batch为1,所以我们取train_inout_seq的每一行数据即为一个batch
-
epochs =
150
-
for i
in
range(epochs):
-
for seq, labels
in train_inout_seq:
-
optimizer.zero_grad()
-
# 下面这一步是对隐含状态以及细胞转态进行初始化
-
# 这一步是必须的,否则会报错 "RuntimeError: Trying to backward through the graph a
-
# second time, but the buffers have already been freed"
-
model.hidden_cell = (torch.zeros(
1,
1, model.hidden_size),
-
torch.zeros(
1,
1, model.hidden_size))
-
y_pred = model(seq)
-
single_loss = loss_function(y_pred, labels)
-
single_loss.backward()
-
optimizer.step()
-
-
if i%
25 ==
1:
-
print(
f'epoch: {i:3} loss: {single_loss.item():10.8f}')
-
-
print(
f'epoch: {i:3} loss: {single_loss.item():10.10f}')
-
-
'''输出
-
epoch: 1 loss: 0.00139548
-
epoch: 26 loss: 0.01831282
-
epoch: 51 loss: 0.00002390
-
epoch: 76 loss: 0.00029238
-
epoch: 101 loss: 0.00020424
-
epoch: 126 loss: 0.00000069
-
epoch: 149 loss: 0.0001339361
-
'''
提取train data 中最后12 个数据,作为预测的输入
-
# 以train data的最后12个数据进行预测
-
fut_pred =
12
-
test_inputs = train_data_normalized[-train_window:].tolist()
-
print(test_inputs)
-
-
'''输出
-
[0.12527473270893097, 0.04615384712815285, 0.3274725377559662, 0.2835164964199066, 0.3890109956264496, 0.6175824403762817, 0.9516483545303345, 1.0, 0.5780220031738281, 0.33186814188957214, 0.13406594097614288, 0.32307693362236023]
-
'''
进行模型的预测,首先我们将模型设置为模型评价模式防止出现模型预测不一致的情况(比如说采用了dropout 层)。可以看到,下面预测基本思路是将新的预测值加入到数据集当中用于进一步的预测,我们最开始使用是第121-132个值去预测第133值,然后我们在用第122-133去预测第134个值,依次类推直到第144个值被预测。
-
model.eval()
-
#基于最后
12个数据来预测第
133个数据,并基于新的预测数据进一步预测
-
#
134-
144 的数据
-
for i
in range(fut_pred):
-
seq
= torch.FloatTensor(
test_inputs[-train_window:])
-
# 模型评价时候关闭梯度下降
-
with torch.
no_grad():
-
model.hidden
= (torch.
zeros(
1,
1, model.hidden_
size),
-
torch.
zeros(
1,
1, model.hidden_
size))
-
test_inputs.append(model(seq).item())
-
-
test_inputs[fut_pred:]
-
-
''
'输出
-
LSTM(
-
(lstm): LSTM(1, 100)
-
(linear): Linear(in_features=100, out_features=1, bias=True)
-
)
-
-
[0.5331172943115234,
-
0.5856705904006958,
-
0.8344773650169373,
-
1.0538733005523682,
-
1.2378138303756714,
-
1.3108998537063599,
-
1.1621872186660767,
-
1.4777841567993164,
-
1.5506138801574707,
-
1.7615976333618164,
-
1.7539771795272827,
-
1.5737073421478271]
-
-
'
''
由于输出输入时,我们采用了归一化的操作,所以这里通过反向变换,将预测之后的数据进行转换。
-
# 通过反向尺度变换,将预测数据抓换成非归一化的数据
-
actual_predictions = scaler.inverse_transform(np.array(test_inputs[train_window:] ).reshape(-
1,
1))
-
print(actual_predictions)
-
-
'''输出
-
[[452.78418446]
-
[464.74005932]
-
[521.34360054]
-
[571.25617588]
-
[613.10264641]
-
[629.72971672]
-
[595.89759225]
-
[667.69589567]
-
[684.26465774]
-
[732.26346159]
-
[730.52980834]
-
[689.51842034]]
-
'''
接下来,我们通过可视化看看预测的值和实际的值之间的关系
-
# 绘制图像查看预测的[
133-
144]的数据和实际的
133-
144 之间的数据差别
-
x
= np.arange(
132,
144,
1)
-
print(x)
-
plt.title(
'Month vs Passenger')
-
plt.ylabel(
'Total Passengers')
-
plt.grid(
True)
-
plt.autoscale(axis
=
'x', tight
=
True)
-
plt.plot(flight_
data[
'passengers'])
-
plt.plot(x,actual_predictions)
-
plt.show()
可以发现预测的值在总体的趋势上与实际类似,但是在预测数值还存在一定的差距,这里只是为了说明如何使用LSTM进行单变量进行预测,所以不过分追求精度。实际应用中,需要通过调参不断的优化模型来提高预测的精度。
flight数据下载连接
链接:https://pan.baidu.com/s/1p7INoocovFMkUWnv7MGy_g
提取码:dlcw
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。