循环神经网络(RNN)
循环神经网络(Recurrent Neural Network,RNN)是一类利用了输入数据的序列化特征的神经网络。序列化输入可以是文本、语音、时间序列或任意其元素的出现依赖于它之前的元素的序列。RNN网络非常灵活,已被用来解决诸如语音识别、语言建模、机器翻译、情感分析和图片说明等各类问题。
简单循环神经网络
观看RNN的一种方式是把它展开,如图1 所示。展开是指我们把整个序列的网络绘制出来。这里展示的是一个三层的RNN,适用于处理3个元素的序列化数据。注意权重矩阵U、V、W是所有时间步共享的。这是因为我们在每个时间步的不同输入上施加了相同的操作。在所有时间步上共享相同的权重向量,极大地减少了RNN网络需要学习的参数个数。
用方程来描述简单循环神经网络中的计算。图1中t时间的内部状态,由给出。的值,是先对权重矩阵W和时间步t-1上的隐藏状态值的乘积与权重矩阵U和输入的的乘积求和后,再传入tanh函数取得的值。tanh的二阶导数衰减到0很慢,这一特性可以有效防止梯度消失问题。
梯度消失和梯度爆炸
一个隐藏状态关于它前一个状态的梯度小于1的情况,因为跨多个时间步反向传播,梯度的乘积变得越来越小,这就导致了梯度消失问题的出现。如果梯度比1大很多,乘积就会变得越来越大,梯度爆炸就这样形成了。
如果发生了梯度消失,那么相距较远的时间步上的学习对梯度没有任何用处,因此RNN无法做到进行大范围的学习。梯度消失在很多神经网络上都会发生,因为RNN通过时间步分解后会有很多层,所以梯度消失对于RNN的可见性更高。
梯度爆炸很容易可以检测到,最直接的表现是total loss的剧烈变化。梯度爆炸可以使用Clipping方法。也就是定义一个阙值来控制值的上界。
解决梯度消失的方法:
1 选择tanh作为激活函数,这和它的二阶导数衰减到0非常缓慢有关。这保持了激活函数的线性域的斜度,并帮助防止梯度消失问 题。
2 W权重向量适当初始化,使用ReLU替代tanh层,以及使用非监督方法预训练网络。
3 最流行的方案是使用LSTM或GRU架构。
Long Short-term Memory (LSTM)
简单循环神经网络并不具有学习到很远的信息的能力。长短时记忆网络(Long Short-term Memory,LSTM)的设计就是为了解决这个问题。LSTM是循环神经网络被成功应用的关键。使用LSTM的循环神经网络相比于简单神经网络具有更好的表现。LSTM是一种拥有三个“门”结构的特殊网络结构。
图4 中的LSTM结构有输入门、遗忘门和输出门。每个门都使用sigmoid作为激活函数,于是门的输出值在0到1之间。
门控循环单元-GRU
GRU是LSTM的一个变体,它保留了LSTM对梯度消失问题的抵抗力,但它的内部结构更加简单,更新隐藏状态时需要的计算也更少,因此训练得更快。
循环神经网络的dropout
通过dropout,卷积神经网络可以更加健壮(robust)。类似地,在循环神经网络中使用dropout也有同样的功能。而且,类似卷积神经网络只在最后的全连接中使用dropout,循环神经网络一般只在不同层循环体结构之间使用。也就是从时刻t-1传递到时刻t时,循环神经网络不会进行dropout;而在同一时刻t中,不同层循环体之间会使用dropout。
循环神经网络挖掘数据中的时序信息以及语义信息的深度表达能力被充分利用,并在语音识别、语言模型、机器翻译以及时序分析等方面实现了突破。LSTM和GRU都可以很方便地替换掉简单RNN单元,使用两者之一的网络在性能方面会有明显的改进。
tensorflow- 预测正弦函数
"""
Created on Wed Dec 5 09:27:45 2018
tensorflow rnn 预测正弦函数
@author: lingtwave wang
"""
import numpy as np
import tensorflow as tf
import matplotlib as mpl
from matplotlib import pyplot as plt
from tensorflow.contrib.learn.python.learn.estimators.estimator import SKCompat
# TensorFlow的高层封装TFLearn
learn = tf.contrib.learn
# 神经网络参数
HIDDEN_SIZE = 30 # LSTM隐藏节点个数
NUM_LAYERS = 2 # LSTM层数
TIMESTEPS = 10 # 循环神经网络截断长度
BATCH_SIZE = 32 # batch大小
# 数据参数
TRAINING_STEPS = 3000 # 训练轮数
TRAINING_EXAMPLES = 10000 # 训练数据个数
TESTING_EXAMPLES = 1000 # 测试数据个数
SAMPLE_GAP = 0.01 # 采样间隔
def generate_data(seq):
# 序列的第i项和后面的TIMESTEPS-1项合在一起作为输入,第i+TIMESTEPS项作为输出
X = []
y = []
for i in range(len(seq) - TIMESTEPS - 1): #0-9989 #0-989
X.append([seq[i:i + TIMESTEPS]])
y.append([seq[i + TIMESTEPS]])
return np.array(X, dtype=np.float32), np.array(y, dtype=np.float32)
# LSTM基本结构单元
def LstmCell():
lstm_cell = tf.contrib.rnn.BasicLSTMCell(HIDDEN_SIZE)
return lstm_cell
def lstm_model(X, y):
# 使用多层LSTM,不能用lstm_cell*NUM_LAYERS的方法,会导致LSTM的tensor名字都一样
cell = tf.contrib.rnn.MultiRNNCell([LstmCell() for i in range(NUM_LAYERS)])
# 将多层LSTM结构连接成RNN网络并计算前向传播结果
#输入 cell,inputs,dtype
#输出形如 [batch_size,max_time,cell.output.size]
output, _ = tf.nn.dynamic_rnn(cell, X, dtype=tf.float32)
#形如[? *30] -1表示自适应
output = tf.reshape(output, [-1, HIDDEN_SIZE])
# 通过无激活函数的全联接层计算线性回归,并将数据压缩成一维数组的结构
# 输入,输出,激活函数
predictions = tf.contrib.layers.fully_connected(output, 1, None)
# 将predictions和labels调整为统一的shape
y = tf.reshape(y, [-1])
predictions = tf.reshape(predictions, [-1])
# 计算损失值
loss = tf.losses.mean_squared_error(predictions, y)
# 创建模型优化器并得到优化步骤
train_op = tf.contrib.layers.optimize_loss(
loss,
tf.train.get_global_step(),
optimizer='Adagrad',
learning_rate=0.1
)
return predictions, loss, train_op
#用正弦函数生成训练和测试数据集合
#numpu.linspace函数可以创建一个等差序列的数组,它常用的参数有三个参数,第一个参数
#表示起始值,第二个参数表示终止值,第三个参数表示数列的长度。例如,linespace(1,10,10)
#产生的数组是array([1,2,3,4,5,6,7,8,9,10])
test_start = TRAINING_EXAMPLES * SAMPLE_GAP
test_end = (TRAINING_EXAMPLES + TESTING_EXAMPLES) * SAMPLE_GAP
train_X, train_y = generate_data(np.sin(np.linspace(0, test_start, TRAINING_EXAMPLES, dtype=np.float32)))
test_X, test_y = generate_data(np.sin(np.linspace(test_start, test_end, TESTING_EXAMPLES, dtype=np.float32)))
# 建立深层循环网络模型
regressor = SKCompat(learn.Estimator(model_fn=lstm_model, model_dir='model/'))
# 调用fit函数训练模型
regressor.fit(train_X, train_y, batch_size=BATCH_SIZE, steps=TRAINING_STEPS)
# 使用训练好的模型对测试集进行预测
predicted = [[pred] for pred in regressor.predict(test_X)]
# 计算rmse作为评价指标
rmse = np.sqrt(((predicted - test_y)**2).mean(axis=0))
print('Mean Square Error is: %f' % (rmse[0]))
# 对预测曲线绘图,并存储到sin.jpg
fig = plt.figure()
plot_test, = plt.plot(test_y, label='real_sin',c='blue')
plot_predicted, = plt.plot(predicted,label='predicted',c='red')
plt.legend([plot_predicted, plot_test], ['predicted', 'real_sin'])
plt.show()
fig.savefig('sin.png')
训练300个轮次之后模型的平方误差损失为0.002278,从图中可以看出预测得到的结果和真实的sin函数几乎是重合的,也就是说通过循环神经网络可以非常好的预测sin函数的取值。
参考资料和文献:
Keras 深度学习实战
李宏毅 Machine Learning (2017,Fall)
TensorFlow 实战Google深度学习框架
https://blog.csdn.net/zhaojc1995/article/details/80572098