前面几篇文章介绍了CNN网络实战:
1、简单CNN网络实现手写数据集识别(附完整代码)
2、CNN简单实战:CIFAR10数据集物品分类(附完整代码)
3、【CNN实战】利用CNN对自定义数据集进行分类
通过这几篇文章,相信大家能够掌握基本CNN网络结构的搭建以及预测步骤,在本篇中,将对另外一种全新的深度学习模型进行详细介绍:RNN (Recurrent Neural Network) - 循环神经网络:用于处理序列数据的神经网络结构。
本文用到的数据集下载地址:
ETTH数据集下载,本文用到的是ETTH1.csv
首先,先简单介绍一下RNN网络
一、什么是RNN(循环神经网络)?
1、RNN的定义
循环神经网络(Recurrent Neural Network,RNN)是一种用于处理序列数据的神经网络。相比一般的神经网络来说,他能够处理序列变化的数据。比如某个单词的意思会因为上文提到的内容不同而有不同的含义,RNN就能够很好地解决这类问题。
2、RNN网络类型
RNN按照输入个数以及输出个数,可以划分为如下不同类型:
(1)一对一:其实和全连接神经网络并没有什么区别,这一类别算不上 RNN。
(2)一对多:输入不是序列,输出是序列。可用于按主题生成文章或音乐等。
(3)多对一:输入是序列,输出不是序列(为单个值)。常用于文本分类、回归预测。
(4)多对多:输入和输出都是不定长的序列。这也就是Encoder-Decoder结构,常用于机器翻译。
(5)多对多(输入==输出):输入和输出都是等长的序列数据。这是 RNN 中最经典的结构类型,常用于NLP的命名实体识别、序列预测。
3、RNN原理
关于RNN模型,我们还是从数据、模型、学习目标、优化算法这几个要素展开解析,使用过程需要重点关注的是其输入和输出的差异(以经典的m==n的RNN结构为例)。
3.1 数据层面
在传统的机器学习模型中,输入数据是相互独立的,但在RNN中,其输入数据元素有一定的依赖性,这些数据按照时间输入模型中。上一步的输入对下一步的预测是有影响的(如文字预测的任务,以“猫吃鱼”这段序列文字,上一步的输入“猫”–x(0)会影响下一步的预测“吃”–x(1)的概率,也会继续影响下下步的预测“鱼”–x(2)的概率),我们通过RNN结构就可以将历史的(上下文)的信息反馈到下一步
3.2 模型层面
RNN模型结构如下:
上图的数据说明如下:
Xt:时刻 t 的输入,shape = (batch_size,feature_dim)。feature_dim表示词向量的长度。
St:时刻 t 的状态值,shape = (batch_size,hidden_dim)。这个状态值有两个作用:经过一个全连接层得到输出Ot;输入到下一个时刻,影响下一个时刻的状态值,用来记录过往时刻的信息。第一个时刻的 St 会初始化为全0 的向量。
Ot:时刻t的输出,shape = (batch_size,classes)。
U:linear层输入Xt的权重参数,shape = (feature_dim, hidden_dim)。
W:linear 层状态值 St-1的权重参数,shape = (hidden_dim, hidden dim)。
V:linear 层状态值 St的权重参数,shape = (hidden_dim,classes)。
由上图可以看出:
RNN除了接受每一步的输入x(t),同时还会连接输入上一步的反馈信息——隐藏状态S(t-1),也就是当前时刻的隐藏状态 S(t) 由当前时刻的输入 x(t)和上一时刻的隐藏状态S(t-1)共同决定。
另外,RNN神经元在每个时间步上是共享权重参数矩阵的(不同于CNN是空间上的参数共享),时间维度上的参数共享可以充分利用数据之间的时域关联性,如果我们在每个时间点都有一个单独的参数,不但不能泛化到训练时没有见过序列长度,也不能在时间上共享不同序列长度和不同位置的统计强度。
模型公式如下:
其中,f 为激活函数,一般为tanh, relu
3.3各个时间步的计算流程:
此处参考了【深度学习】一文详解RNN及股票预测实战(Python)!
其中:
U是 输入层到隐藏层的权重矩阵;
V是 隐藏层到输出层的权重矩阵;
b是 偏置项;
3.3.1 输入 x(t) 到输出 h(t) 计算流程如下:
注意,这里隐藏层用h(t)表示
首先,将上一时刻的隐藏状态 h(t - 1)[对应数据为:[0.537, 0.462]] 与当前时刻的输入 x(t)[对应数据为[2.0]] 拼接成一维向量作为全连接的隐藏层的输入[[0.537, 0.462, 2.0]] ,对应隐藏层的的输入维度为 3。
隐藏层的权重矩阵为[[0.1, 0.2], [0.3, 0.4], [0.5, 0.6]],偏置项b1为[0.1, -0.1]。
经过隐藏层的矩阵运算为:(h(t-1) 拼接 x(t) )* (权重W 拼接 权重U )+ 偏置项(b1) ,再由tanh转换后输出为状态 h(t)。
接着 h(t) 与 x(t+1) 继续输入到下一步(t+1)的隐藏层。
隐藏层的矩阵运算的对应代码如下:
# 隐藏层的矩阵运算的对应代码
np.tanh(np.dot(np.array([[0.537, 0.462, 2.0]]),np.array([[0.1, 0.2], [0.3, 0.4], [0.5, 0.6]])) + np.array([0.1, -0.1]))
# 输出h(t)为:array([[0.85972772, 0.88365397]])
3.3.2 h(t) 到输出 O(t) 的过程:
t 时刻隐藏层输出状态 h(t) 为[0.86, 0.884],
输出层权重矩阵为[[1.0], [2.0]],偏置项b为[0.1],
h(t) 经由输出层的矩阵运算为:h(t) * V +偏置项(b)后,输出o(t)
输出层的矩阵运算的对应代码
# 输出层的矩阵运算的对应代码
np.dot(np.array([[0.85972772, 0.88365397]]),np.array([[1.0], [2.0]])) + np.array([0.1])
# o(t) 输出: array([[2.72703566]])
上述过程从初始输入(t=0)遍历到序列结束(t=m),就是一个完整的前向传播过程,我们可以看出权重矩阵、和偏置项在不同时刻都是同一组,这也说明RNN在不同时刻中是共享参数的。
4、RNN实战
4.1 数据处理
4.1.1 数据预览
在这里,我们用到的是真实数据集ETT,该数据存储了不同时刻变压器的负载情况以及该时刻对应的温度,具体如下:
其中,date为时间,HUFL - LULL(共六列)分别对应了变压器的不同负载情况,OT为当前时刻变压器的温度值。
先我们希望根据前面10个时刻的历史变压器属性,预测未来一个时刻的变压器温度OT。
4.1.2 数据预处理
读取数据
# 读取数据
data = pd.read_csv('ETTh1.csv')
# 提取特征和标签
f1 = data['HUFL'].values
f2 = data[