Skip to content

循环神经网络

循环神经网络(Recurrent Neural Networks,简称 RNNs)是一类非常适合处理序列数据的神经网络,例如时间序列、文本或语音。与假设输入独立的[前馈神经网络](feedforward neural networks)不同,RNN 具有形成有向循环的连接,这使得它们能够维持一种内部状态或对过去信息的“记忆”。这种记忆使其能够捕获序列数据中的时间依赖性和模式。

RNN 的核心思想是,它对序列中的每个元素执行相同的计算,并且每一步的输出都依赖于之前的计算。它们之所以被称为“循环”(recurrent),是因为它们对序列中的每个元素执行相同的任务,并且输出依赖于先前的计算结果。

训练 RNN 通常涉及以下概念性步骤:

步骤 1:提供数据集中的一个序列(或一批序列,batch of sequences)作为输入。

步骤 2:网络逐个处理序列元素,更新其内部状态(internal state)并在每一步(或在序列末尾)产生输出。初始状态和权重(weights)通常随机初始化。

步骤 3:根据网络的输出计算预测结果。

步骤 4:使用[损失函数](loss function)将预测结果与实际目标值进行比较,生成误差信号。

步骤 5:误差通过时间反向传播(Backpropagation Through Time,简称 BPTT 的算法),并通过网络层反向传播,以调整模型的权重。

步骤 6:重复步骤 1-5 多个[周期](epochs)和批次(batches),直到模型在[验证集](validation set)上的性能令人满意。

步骤 7:训练好的模型可用于对新的、未见过的序列进行预测。

一个简单的 RNN 可以可视化为一个循环。在每个时间步(time step)‘t’,RNN 接收输入 x(t) 和来自前一个时间步的隐藏状态(hidden state)h(t-1),以计算新的隐藏状态 h(t) 和输出 y(t)。在所有时间步中都使用相同的权重集合(U, W, V)。

使用 TensorFlow Keras 实现循环神经网络

Section titled “使用 TensorFlow Keras 实现循环神经网络”

我们将演示如何使用 tf.keras 实现一个简单的 RNN 来对手写数字 MNIST 进行分类。虽然 CNNs 通常更适合图像数据,但此示例展示了 RNN 如何通过将其行(或列)视为序列来处理图像。

步骤 1:导入必要的模块并加载数据。

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
import numpy as np
# Load MNIST dataset
(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
# Preprocess data: Normalize pixel values
x_train = x_train.astype("float32") / 255.0
x_test = x_test.astype("float32") / 255.0
# Convert labels to one-hot encoded vectors
num_classes = 10
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
print(f"x_train shape: {x_train.shape}") # (60000, 28, 28)
print(f"y_train shape: {y_train.shape}") # (60000, 10)

对于 RNN,我们可以将每张图像(28x28 像素)视为由 28 个时间步组成的序列,其中每个时间步有 28 个特征(features)(即一行的像素值)。

步骤 2:定义 RNN 模型参数并构建模型。

# RNN 参数
# 输入图像为 28x28。我们将其视为 28 个时间步,每个时间步有 28 个特征。
n_steps = 28 # 时间步数量(图像高度)
n_input = 28 # 每个时间步的特征数量(图像宽度)
n_hidden = 128 # LSTM 层中的单元数量
model = keras.Sequential([
# LSTM 层:处理行序列
# input_shape 为 (时间步, 每个时间步的特征)
layers.LSTM(n_hidden, input_shape=(n_steps, n_input)),
# 如有需要,可在此添加 Dropout 进行正则化
# layers.Dropout(0.5),
# 用于分类的全连接(Dense)输出层
layers.Dense(num_classes, activation='softmax')
])
model.summary()

我们使用 LSTM(Long Short-Term Memory,长短期记忆网络)层,它是一种特殊的 RNN 单元,特别擅长捕获长期依赖性并缓解可能影响简单 RNN 的[梯度消失问题](vanishing gradient problem)。LSTM 层的 input_shape 为 (n_steps, n_input)。

步骤 3:编译模型。

learning_rate = 0.001
optimizer = tf.keras.optimizers.Adam(learning_rate=learning_rate)
model.compile(optimizer=optimizer,
loss='categorical_crossentropy',
metrics=['accuracy'])

我们使用 Adam 优化器、[分类交叉熵损失](categorical cross-entropy loss,适用于多类别分类)来编译模型,并跟踪准确率(accuracy)。

步骤 4:训练模型。

batch_size = 128
training_epochs = 5 # 为快速演示而减少周期数
print("\n正在训练模型...")
history = model.fit(x_train, y_train,
batch_size=batch_size,
epochs=training_epochs,
validation_data=(x_test, y_test),
verbose=1)
print("\n优化完成!")
# 在测试集上评估模型
print("\n正在测试数据上评估模型...")
loss, acc = model.evaluate(x_test, y_test, verbose=0)
print(f"测试损失: {loss:.6f}")
print(f"测试准确率: {acc:.5f}")
# 示例预测
if len(x_test) > 0:
sample_idx = 0
sample_image = x_test[sample_idx]
prediction = model.predict(np.expand_dims(sample_image, axis=0))
predicted_class = np.argmax(prediction[0])
actual_class = np.argmax(y_test[sample_idx])
print(f"样本 {sample_idx} - 预测类别: {predicted_class}, 实际类别: {actual_class}")

训练过程将会显示,随后是最终的测试准确率。虽然 LSTMs 可以用这种方式处理图像,但由于 CNNs 更善于高效捕获局部空间模式,它们通常在图像任务中更有效。

预期输出(准确率可能有所不同,特别是周期数较少时):

x_train shape: (60000, 28, 28)
y_train shape: (60000, 10)
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm (LSTM) (None, 128) 80384
dense (Dense) (None, 10) 1290
=================================================================
Total params: 81,674
Trainable params: 81,674
Non-trainable params: 0
_________________________________________________________________
Training the model...
Epoch 1/5
469/469 [==============================] - 7s 11ms/step - loss: 0.7749 - accuracy: 0.7544 - val_loss: 0.2873 - val_accuracy: 0.9136
Epoch 2/5
469/469 [==============================] - 5s 10ms/step - loss: 0.2228 - accuracy: 0.9335 - val_loss: 0.1629 - val_accuracy: 0.9509
Epoch 3/5
469/469 [==============================] - 5s 10ms/step - loss: 0.1466 - accuracy: 0.9564 - val_loss: 0.1250 - val_accuracy: 0.9610
Epoch 4/5
469/469 [==============================] - 5s 10ms/step - loss: 0.1125 - accuracy: 0.9660 - val_loss: 0.1048 - val_accuracy: 0.9667
Epoch 5/5
469/469 [==============================] - 5s 10ms/step - loss: 0.0914 - accuracy: 0.9726 - val_loss: 0.0938 - val_accuracy: 0.9709
Optimization Finished!
Evaluating model on test data...
Test Loss: 0.093812
Test Accuracy: 0.97090
Sample 0 - Predicted class: 7, Actual class: 7