递归神经网络实现预测
有一天,一个女孩子向电脑小黑说“我好喜欢你好久了,你能不能。。。”,女孩子羞涩的低下下了头。小黑一愣“她啥意思啊?”,女孩子一看无望,转身离开。小黑绞尽脑汁,终于研究了一种“递归神经网络(RNN)”有了这个神器,就可以根据有逻辑关系的一段话进行判断,预测啦~简直就是直男的救星。
专业点来讲,递归神经网络就是处理序列数据的神经网络,也就是处理前后有关系的数据,比如语言、天气预测。比起其他神经网络,它引入了“记忆”功能,不仅能根据当前输入找到输出,也会结合前面的信息。
RNN (Recurrent Neural Network)
RNN基本结构
RNN的结构图很简单,添加中间隐层s,传递上一个输入的信息,输出o(t)
不仅由当前输入x(t)
决定,也和上一个隐层h(t-1)
有关。
在时刻t,隐层h(t)
表达如下,其中f为激活函数,b为偏置
h
t
=
f
(
W
h
t
−
1
+
U
x
t
+
b
)
h_t=f(Wh_{t-1}+Ux_t+b)
ht=f(Wht−1+Uxt+b)
在时刻t,输出o(t)
表达式如下,c为偏置
o
t
=
V
h
t
+
c
o_t=Vh_t+c
ot=Vht+c
输入和输出
我们会发现,此时输出有些多,到底取哪一个输出呢?这就由我们的任务决定了。
1.多输入单输出
如果我们需要一个输出,比如根据一句话判断是夸你的还是骂你的.那么很明显就取最后一个结果,原因很简单,最后一个输出是最全面。
2.单输入多输出
如果从一张图像描述出来一段话,可以把输入x作为一个信息输入,也可以把输入x作为每个输入
3.多输入多输出
如果是一一对应,那么直接把输出都拿到就可以,比如后面还要连接下一层的RNN,。大多数情况是并不是一一对应。比如,输入是:“我喜欢你很久了,能不能”,输出是"做我男朋友"这样就是seq2seq,也可以称为Encoder-Decoder模型.
Encoder是一个RNN, 它的输出为上下文向量c,c可以取Encoder最后一个隐状态,也可以是Encoder多个隐状态组合. Decoder是另一个RNN, 将c用作RNN隐层初始状态.
还有一种Decoder是将c用作每一步的输入
RNN反向传播
BPTT(back-propagation through time)算法是常用的训练RNN的方法,其实本质还是BP算法,也就是梯度下降,损失对各个参数求梯度(偏导)就是关键问题.三个参数W,U和V.
对参数V
, 和时间没有关系,但是损失L会随着t的增加而增加的,也就是累加
∂
L
∂
V
=
∑
t
=
1
n
(
∂
L
t
∂
o
t
∂
o
t
∂
V
)
\frac{\partial L}{\partial V} =\sum_{t=1}^n\left(\frac{\partial L_t}{\partial o_t}\frac{\partial o_t}{\partial V} \right)
∂V∂L=t=1∑n(∂ot∂Lt∂V∂ot)
对于参数W
和U
求偏导,都需要涉及历史数据
∂
L
t
∂
W
=
∂
L
t
∂
o
t
∂
o
t
∂
h
t
∑
k
=
1
t
[
(
∏
i
=
k
+
1
t
∂
h
i
∂
h
i
−
1
)
∂
h
k
∂
W
]
\frac{\partial L_t}{\partial W}= \frac{\partial L_t}{\partial o_t}\frac{\partial o_t}{\partial h_t}\sum_{k=1}^t\left[\left(\prod_{i=k+1}^t\frac{\partial h_i}{\partial h_{i-1}}\right)\frac{\partial h_k}{\partial W}\right]
∂W∂Lt=∂ot∂Lt∂ht∂otk=1∑t[(i=k+1∏t∂hi−1∂hi)∂W∂hk]
∂ L t ∂ U = ∂ L t ∂ o t ∂ o t ∂ h t ∑ k = 1 t [ ( ∏ i = k + 1 t ∂ h i ∂ h s i − 1 ) ∂ h k ∂ U ] \frac{\partial L_t}{\partial U}= \frac{\partial L_t}{\partial o_t}\frac{\partial o_t}{\partial h_t}\sum_{k=1}^t\left[\left(\prod_{i=k+1}^t\frac{\partial h_i}{\partial hs_{i-1}}\right)\frac{\partial h_k}{\partial U}\right] ∂U∂Lt=∂ot∂Lt∂ht∂otk=1∑t[(i=k+1∏t∂hsi−1∂hi)∂U∂hk]
得到隐层h
时, 加入激活函数f
(f 可以是Relu, Sigmoid, tanh)的,还需要包括激活函数求导
∏
i
=
k
+
1
t
∂
h
i
∂
h
i
−
1
=
∏
i
=
k
+
1
t
f
′
W
\prod_{i=k+1}^t\frac{\partial h_i}{\partial h_{i-1}}=\prod_{i=k+1}^tf'W
i=k+1∏t∂hi−1∂hi=i=k+1∏tf′W
累乘会导致激活函数导数和权重矩阵的累乘,进而会导致“梯度消失“和“梯度爆炸“现象的发生
LSTM (Long Short Term Memory)
LSTM是RNN的一种变体,RNN由于梯度消失的原因只能有短期记忆,LSTM网络通过精妙的门控制将短期记忆与长期记忆结合起来,一定程度上解决了梯度消失的问题, 长短记忆相可以结合.
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OcjvYWGR-1587817246618)(E:\Tensorflow\0.我的笔记\5.从RNN到LSTM实现气温数据预测\image\7.RNN与LSTM比较.png)]
LSTM相比RNN,除了隐层 h 随着时间流动,还多出一个随时间流动的细胞状态c,这就是长记忆的关键. LSTM由遗忘门,输入门和输出门三部分构成,每个部分都是使用sigmoid函数作为选择工具,tanh函数作为变换工具来实现.
遗忘门
决定c(t-1)
有多少被遗忘。sigmoid函数输出是(0,1)之间一个数(0代表不通过,1代表全通过),与c(t-1)
相乘
输入门
输入门会根据遗忘门的信息, 并增加此时输入x(t)
和h(t-1)
的信息, 共同更新 c(t)
. 也就是说, 把前面的适当忘记一部分, 再加上新学的一部分.
输出门
输出h(t)
由细胞状态c(t)
, x(t)
, h(t-1)
决定. 把细胞状态c(t)
通过 tanh 进行处理(得到一个在 -1 到 1 之间的值),x(t)
和h(t-1)
通过sigmoid 门,两者相乘, 得到输出. 简单来说, 就是很久之前学到的, 刚刚学到的, 正在学的综合在一起.
LSTM网络实现气温预测
任务目标:利用多组时间有关的数据来预测之后的某个时间/时间段的温度
数据集准备
1.读取数据
pandas读取,数据每隔10分钟记录一次
df = pd.read_csv('jena_climate_2009_2016.csv')
features_considered = ['p (mbar)', 'T (degC)', 'rho (g/m**3)']
features = df[features_considered]
dataset = features.values
2.数据预处理
前30w为训练数据,取训练数据均值和方差,将数据预处理。(只能用训练数据求均值方差)
TRAIN_SPLIT = 300000
data_mean = dataset[:TRAIN_SPLIT].mean(axis=0)
data_std = dataset[:TRAIN_SPLIT].std(axis=0)
dataset = (dataset-data_mean)/data_std
3.数据&标签制作
数据:取5天内每隔1小时的3个特征,表格是以10分钟记录一次的,所以取5* 24 * 6 = 720时间点,以6为间隔取值,最终一条数据维度为(120,3)
标签:12小时之后的温度值(12*6=72个时间点后)
- dataset:训练数据,包括三个特征[‘p (mbar)’, ‘T (degC)’, ‘rho (g/m**3)’]
- target:标签数据,只有温度特征 [‘T (degC)’]
- start_index:开始索引,训练集为0,验证集为TRAIN_SPLIT
- end_index:结束索引,训练集为TRAIN_SPLIT,验证集为None
- history_size:窗口长度,选择训练窗口为5天的数据,5 * 24 *6=720条记录
- target_size: 预测标签在多少时间点后取值。预测为12小时后的温度值,即12*6=72, 72个时间点
- step: 每隔多长时间取数据,step= 6,720/6=120 ,索引为range(0, 720, 6)
- single_step: 如果为True,标签为一个时间点的值,如果为False,标签为一个时间段的值
def multivariate_data(dataset, target, start_index, end_index, history_size,
target_size, step, single_step=False):
data = []
labels = []
start_index = start_index + history_size
if end_index is None:
end_index = len(dataset) - target_size
for i in range(start_index, end_index):
indices = range(i-history_size, i, step) #训练集索引为range(0,720,6),range(1,721,6)
data.append(dataset[indices])
if single_step:
labels.append(target[i+target_size]) #训练集(720+72),(721+72)
else:
labels.append(target[i:i+target_size])
return np.array(data), np.array(labels)
past_history = 720
future_target = 72
STEP = 6
x_train_single, y_train_single = multivariate_data(dataset, dataset[:, 1], 0,
TRAIN_SPLIT, past_history,
future_target, STEP,
single_step=True)
x_val_single, y_val_single = multivariate_data(dataset, dataset[:, 1],
TRAIN_SPLIT, None, past_history,
future_target, STEP,
single_step=True)
结果:
x_train_single
维度为(299280, 120, 3),299280组数据,每组数据包含120个时间点,3个不同特征
y_train_single
维度为(299280,),299280组标签
4.构建训练数据
把数据和标签放在一起,打乱并放入batch
train_data_single = tf.data.Dataset.from_tensor_slices((x_train_single, y_train_single))
train_data_single = train_data_single.cache().shuffle(BUFFER_SIZE).batch(BATCH_SIZE).repeat()
val_data_single = tf.data.Dataset.from_tensor_slices((x_val_single, y_val_single))
val_data_single = val_data_single.batch(BATCH_SIZE).repeat()
模型构建
Demo一下,越简单越好,只连接一个LSTM网络和全连接层。输入为(120,3)放入LSTM网络,输出一个32维向量,然后接全连接层,最终得到一个结果
single_step_model = tf.keras.models.Sequential()
single_step_model.add(tf.keras.layers.LSTM(32,
input_shape=x_train_single.shape[-2:]))
single_step_model.add(tf.keras.layers.Dense(1))
single_step_model.compile(optimizer=tf.keras.optimizers.RMSprop(), loss='mae')
训练结果
为提高速度,每个epoch训练200组数据
VALUATION_INTERVAL=200
single_step_history = single_step_model.fit(train_data_single, epochs=EPOCHS,
steps_per_epoch=EVALUATION_INTERVAL,
validation_data=val_data_single,
validation_steps=50)
结果看一下
引申:预测某段时间温度
标签并不是一个值,而变成一组值, single_step设置为False,标签为一个时间段的值,此时 y_train_multi
维度为(299280, 72),299280组数据,每组数据有72标签。
网络设计为多加一层LSTM,并且全连接层输出为72,因为我们有72个值
multi_step_model = tf.keras.models.Sequential()
# 因为后续还要接LSTM,所以return_sequences=True
multi_step_model.add(tf.keras.layers.LSTM(32,
return_sequences=True,
input_shape=x_train_multi.shape[-2:]))
multi_step_model.add(tf.keras.layers.LSTM(16, activation='relu'))
multi_step_model.add(tf.keras.layers.Dense(72))
multi_step_model.compile(optimizer=tf.keras.optimizers.RMSprop(clipvalue=1.0), loss='mae')
看看结果,趋势还可以但是不是很精确