RNN也叫循环神经网络,普通的神经网络层的输入都是上一层的输出,而循环神经网络会在RNN层循环指定次数,这样的特点使得RNN在处理序列数据上表现得很好,因为它可以更好地记住前后文的关系
我们有一段数字序列,我们训练一个神经网络,使得该模型能通过任意连在一起的两个数,判断出第三个数
我们先定义数字序列
data_sequence = [1, 3, 5, 2, 4, 9, 7, 6, 8]
import numpy as np
from keras.models import Sequential
from keras.layers import SimpleRNN, Dense
# 准备训练数据,使用前两个数字作为输入,预测第三个数字,以此类推
X = []
y = []
for i in range(len(data_sequence)-2):
X.append([data_sequence[i], data_sequence[i+1]])
y.append(data_sequence[i+2])
X = np.array(X)
y = np.array(y)
# 转换数据形状以适应RNN
X = X.reshape((X.shape[0], X.shape[1], 1))
我们打印X,得到下图结果,结果竖向排列,无法展示完全,X的形状为(7, 2, 1)(两两排列有七组数据,每组数据两个特征,每个特征单独输入)
打印y
为每两个数的第三个数
接下来我们定义一个简单的前馈神经网络
model = Sequential()
model.add(Dense(500, input_dim=2))
model.add(Dense(1))
该模型有三层,输入层(没有在这里定义,我们等下输入的数据就充当这一层),一个500个神经元的线性层(输入维度为二),一个输出维度为1的输出层(输入维度为上一层神经元的个数,即500)
定义一个循环神经网络
# 创建RNN模型
model = Sequential()
model.add(SimpleRNN(500, input_shape=(2, 1)))
model.add(Dense(1))
该模型有三层,输入层(没有在这里定义,我们等下输入的数据就充当这一层),一个500个神经元的RNN层(input_shape=(2,1)的意思是时间步为2,每个时间步有一个数据,可以理解时间步为网络记忆的长度),一个输出维度为1的输出层(输入维度为上一层神经元的个数,即500)
# 编译模型
model.compile(optimizer='adam', loss='mse')
# 训练模型
model.fit(X, y, epochs=200, batch_size=1, verbose=2)
接下来看看在相同神经元数量和相同训练批次上谁的效果更好吧
# 使用模型进行预测
input_data = np.array([[data_sequence[2], data_sequence[3]]])
predicted_value = model.predict(input_data)[0, 0]
# 打印预测结果
print(f"输入序列: {data_sequence[2:4]},预测下一个数字: {predicted_value}")
我们训练后使用5, 2进行预测,查看原始数据,我们知道下一个数字应该是4,让我们看看两个模型运行的结果吧
前馈神经网络
循环神经网络
可以看到循环神经网络的效果更优
当在网络的反向传播过程中梯度逐渐减小到几乎为零时,就会出现梯度消失问题。这使得网络难以学习到远距离时间步的依赖关系,因为在反向传播时,较早时间步的信息无法有效传递给较晚时间步。
相反,梯度爆炸是指在反向传播中,梯度变得非常大,这可能导致权重更新变得非常大,模型不稳定。这可能导致数值溢出和无法收敛。
这两个问题在神经网络中都会出现,只是由于RNN的结构,梯度消失与梯度爆炸问题会更加显著
这两种循环神经网络能有效地应对梯度消失和梯度爆炸的问题,这里先做了解,之后会具体介绍