[temp]预测情况汇报1


Prediction Model

Introduction

本次预测实验采用的模型为LSTM(Long Short-Term Memory)长短期记忆网络,它是一种基于RNN(Recurrent Neural Network)递归(循环)神经网络的改进神经网络,继承了RNN的Sequence to Sequence 特点,同时解决了RNN面对长时间段时间序列由于梯度发散现象导致的信息遗忘问题,对于长时序问题的预测效果十分可观。

Model Structure

see in my blog

Details in Model Implementation

本实验采用基于Python的开源深度学习库keras构建模型,其后台为著名机器学习库TensorFlow,其源代码见github: keras-team/keras

Dataset

目前暂时还未找到十分合适的(包含有风能各项指标,数据量足够大)用于预测精确到1小时数据的数据集,因此我选取了含有“风速”这一项的一组北京市空气污染数据集,其中特征主要包括 日期(精确到1h)、pm2.5指数、露点、温度、大气压、风向、风速、降雨量、降雪量。本实验将利用其它所有数据来预测未来某时间段内的风速数据

(数据选取自UCI(加州大学欧文分校)的Machine Learning Repository,数据集链接:beijing_air_quality数据集

Implementation

数据预处理:

  1. 数据集已经将数据按时间顺序排列好,考虑到“风向”这一栏数据为类别数据(Categorical data),并且只有4种类别,因此对这一栏进行One-Hot编码,此后,再对整个数据集进行MinMaxScaler归一化操作(可以使梯度下降过程中loss函数降低得更快,更优),公式如下:
  1. 将归一化后的时间序列数据转化为监督学习数据(简单来说就是利用过去一个时间点的数据预测未来一个时间点的目标数据),并将数据格式改为keras对应LSTM的输入格式
  2. 划分训练集、验证集、测试集,本次实验的比例设置为(3:1:1)

LSTM模型搭建:

  • 经过反复试验,最终确定模型结构如下:

    解释:LSTM网络总共三层:

    1. 输入层:接受输入数据(时间步,数据维度)
    2. 隐含层:50个神经元,经计算得出参数为12400个,激活函数为reLU(整流线性单元)
    3. 输出层:1个神经元,激活函数为线性激活函数(不作任何改变)

    image.png

  • 模型训练配置如下

    • 采用Adam优化算法(优于SGD和RMSprop)
    • 损失函数采用均方误差(mean_squared_error)
    • epoch=50, batch_size=72
    • 采用(patience=10)的回调(因为发现了epoch过多时会出现验证集上的loss不降反增的现象,考虑可能是迭代次数过多,出现过拟合了)
  • 实验结果

    • 在测试集上的损失(loss)评估结果(效果非常好)image.png

    • 训练集和测试集的loss对比如下image.png

    • 由于数据集范围较广,数据是以 数据/1h的形式给出,所以无法预测1小时线,下面给出了此模型对测试集的 四小时线、日线、周线 的预测图(数据范围随机抽取)

      image.png

      image.png

      image.png

    • 可以看出,由于数据间隔较大,4小时线预测较为不准确,但趋势正确,而日线预测数据和原始数据几乎重合,周线数据除了个别数值有偏离现象,其他也吻合得非常好

All Python Notebook Code

see in my github repo: wind_spd_preds_practice

看下面代码也行(含注释)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
import numpy as np
import tensorflow as tf
import keras
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import matplotlib as mpl
from sklearn.preprocessing import OneHotEncoder
from sklearn.preprocessing import MinMaxScaler
mpl.rcParams['figure.figsize']=8,6
from datetime import datetime

# 序列转化为监督学习的函数
def series_to_supervised(data, n_in=1, n_out=1, dropna=True):
'''
data: origin data
n_in:
'''
n_vars = 1 if type(data) is list else data.shape[1]
df = pd.DataFrame(data)
cols, names = list(),list()

for i in range(n_in,0,-1):
cols.append(df.shift(i))
names+=[('var%d(t-%d)'%(j+1, i)) for j in range(n_vars)]
for i in range(0, n_out):
cols.append(df.shift(-i))
if i==0:
names += [('var%d(t)'%(j+1)) for j in range(n_vars)]
else:
names += [('var%d(t+%d)'%(j+1, i)) for j in range(n_vars)]
agg = pd.concat(cols, axis=1)
agg.columns = names
if dropna: # 是否去除缺失值的行
agg.dropna(inplace=True)
return agg

# 我用来plot结果对比的函数
def plot_history(model_history):
plt.plot(model_history.history['loss'],label='train_loss')
plt.plot(model_history.history['val_loss'],label='vlaid_loss')
plt.legend(fontsize=15)
plt.title('loss-epoch graph',fontsize=15)
plt.xlabel('epoch',fontsize=15)
plt.ylabel('loss',fontsize=15)
plt.grid(linestyle='--',alpha=0.5)
plt.legend()
plt.show()

# 读取数据
data = pd.read_csv('PRSA_data_2010.1.1-2014.12.31.csv')
data.drop(['year','month','day','hour','No'],axis=1,inplace=True)
data['pm2.5'].fillna(0, inplace=True)
data = data[24:].reset_index()
data.drop(['index'],axis=1,inplace=True)

# 把列弄得好看一点
data.columns=['pollution','dew','temp','press','wnd_dir','wnd_spd','snow','rain']

# 把风向这一组one-hot编码
enc = OneHotEncoder(sparse=False)
obj_cols = ['wnd_dir']
trans = pd.DataFrame(enc.fit_transform(data[obj_cols]))
trans.columns=['dir_1','dir_2','dir_3','dir_4']
data.drop(obj_cols, axis=1, inplace=True)
oh_data = pd.concat([data, trans], axis=1)

# 把预测数据和第一列数据换一下
old_order = list(oh_data)
t=old_order[4]
old_order[4]=old_order[0]
old_order[0]=t
oh_data = oh_data[old_order]

# 归一化操作
scaler = MinMaxScaler(feature_range=(0,1))
scaled_data = scaler.fit_transform(oh_data[[name for name in oh_data.columns]])
reframed_data = series_to_supervised(scaled_data, 1, 1)

# 把不需要预测的列取去除
useless_cols = reframed_data.columns[12:]
reframed_data.drop(useless_cols,axis=1,inplace=True)
reframed_data.head()
reframed_data.info()

# 设定训练集、验证集比例
train_ratio = 0.6
valid_ratio = 0.2
train_days = int(train_ratio*len(reframed_data))
valid_days = int(valid_ratio*len(reframed_data))
test_days = len(reframed_data)-train_days-valid_days

# 划分训练集、验证集、测试集
train = reframed_data.values[:train_days,:]
valid = reframed_data.values[train_days:train_days+valid_days,:]
test = reframed_data.values[train_days+valid_days:,:]

train_x, train_y = train[:,:-1], train[:,-1]
valid_x, valid_y = valid[:,:-1], valid[:,-1]
test_x, test_y = test[:,:-1], test[:,-1]

# 把输入调成keras的LSTM要求的输入格式
train_x = train_x.reshape((train_x.shape[0], 1, train_x.shape[1]))
valid_x = valid_x.reshape((valid_x.shape[0], 1, valid_x.shape[1]))
test_x = test_x.reshape((test_x.shape[0], 1, test_x.shape[1]))
# 看一下维度对不对
print(train_x.shape,train_y.shape,valid_x.shape,valid_y.shape,test_x.shape,test_y.shape)

# 建立LSTM模型
model = keras.Sequential([
keras.layers.LSTM(50, activation='relu', input_shape=(train_x.shape[1],train_x.shape[2])),
keras.layers.Dense(1, activation='linear')
])
# 配置模型
model.compile(optimizer='adam',
loss='mean_squared_error')
# 看一下模型概括
model.summary()

# 设置回调
early_stop = keras.callbacks.EarlyStopping(monitor='val_loss',patience=10)

# 定义一个训练进度可视化函数,便于查看进度
class PrintDot(keras.callbacks.Callback):
def on_epoch_end(self, epoch, logs):
if epoch%25==0:
print(" ")
print(".",end='')

# 开始训练
model_history = model.fit(train_x, train_y,
epochs=50,
batch_size=72,
validation_data=(valid_x, valid_y),
verbose=0,
callbacks=[early_stop,PrintDot()],
shuffle=False)

# 绘出loss对比图
plot_history(model_history)
# 评估结果
evaluate_res = model.evaluate(test_x,test_y)
print(evaluate_res)

# 预测
preds = model.predict(test_x)
# 画出结果对比图
plt.plot(preds[8600:],label='test_predict')
plt.plot(test_y[8600:], label='test_actual')
plt.legend()
plt.show()


# 反归一化操作
for i in range(10):
preds=np.column_stack((preds, np.zeros(len(test_y))))

inverse_preds = scaler.inverse_transform(preds)
origin_test_y = oh_data['wnd_spd'][train_days+valid_days:]
origin_test_y = [x for x in origin_test_y]

# 把结果画出来并对比的函数
def plot_trends(hour_begin, hour_end):
plt.plot(inverse_preds[hour_begin:hour_end,0],label='actual_preds')
plt.plot(origin_test_y[hour_begin:hour_end], label='actual_test_y')
plt.legend(fontsize=15)
plt.xlabel('hour',fontsize=15)
plt.ylabel('wind_speed',fontsize=15)
if hour_end-hour_begin<=24:
plt.title('{0}_hours_line'.format(hour_end-hour_begin),fontsize=15)
else:
plt.title('{0:.0f}_days_line'.format((hour_end-hour_begin)/24),fontsize=15)
plt.grid(linestyle='--',alpha=0.5)
plt.show()

# 我随便选取的时间(8000小时为起点)
plot_trends(8000,8000+4)
plot_trends(8000,8000+24)
plot_trends(8000,8000+24*7)


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!