深度学习之循环神经网络及进化(RNN-LSTM-GRU)

发布于:2024-12-19 ⋅ 阅读:(16) ⋅ 点赞:(0)

循环神经网络 (RNN)
核心思想:像人一样拥有记忆能力。用以往的记忆和当前的输入,生成输出。
应用场景:1.文本生成 2.语音识别 3.机器翻译 4.生成图像描述 5.视频标记
缺点:
RNN 有短期记忆问题,无法处理很长的输入序列
RNN 是一种死板的逻辑,越晚的输入影响越大,越早的输入影响越小,且无法改变这个逻辑。容易出现梯度消失或梯度爆炸。

长短期记忆网络 (LSTM)
LSTM 是一种特定类型的 RNN,旨在解决长序列中的梯度消失问题。LSTM 通过一种特殊的单元结构来记住信息的长短期依赖。

LSTM的原理
LSTM 通过以下几个门来控制信息的流动:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
LSTM的梯度反向传播过程可能会涉及到复杂的计算和动态规划技巧,因为每个时间步的梯度都依赖于前面时间步的计算结果。在实际应用中,可以使用成熟的深度学习框架(如TensorFlow或PyTorch)来实现LSTM的训练和推断过程。

import torch
import torch.nn as nn
import torch.optim as optim

# 定义LSTM模型
class LSTMModel(nn.Module):
    def __init__(self, input_size, hidden_size, output_size, num_layers):
        super(LSTMModel, self).__init__()
        self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True)
        self.fc = nn.Linear(hidden_size, output_size)  # 全连接层,以输出序列的每一个时间步的预测

    def forward(self, x):
        # x形状为 (batch_size, seq_length, input_size)
        lstm_out, _ = self.lstm(x)  # lstm_out为每个时间步的输出
        predictions = self.fc(lstm_out[:, -1, :])  # 只获取最后一个时间步的输出
        return predictions

    # 超参数
input_size = 10       # 输入特征的维度
hidden_size = 20      # 隐藏层的节点数量
output_size = 1       # 预测输出的维度
num_layers = 2        # LSTM层的数量
num_epochs = 100      # 训练轮次
learning_rate = 0.01  # 学习率

# 创建模型实例
model = LSTMModel(input_size, hidden_size, output_size, num_layers)
criterion = nn.MSELoss()  # 损失函数
optimizer = optim.Adam(model.parameters(), lr=learning_rate)  # 优化器

# 假设输入数据和目标输出
# 生成模拟数据,[batch_size, seq_length, input_size]
x_train = torch.randn(100, 5, input_size)  # 100个样本,每个样本5个时间步
y_train = torch.randn(100, output_size)      # 100个对应的目标值

# 训练过程
for epoch in range(num_epochs):
    model.train()  # 设置模型为训练模式
    optimizer.zero_grad()  # 清空之前的梯度

    # 前向传播
    outputs = model(x_train)  # 获取模型的预测

    # 计算损失
    loss = criterion(outputs, y_train)
    print(f'Epoch [{epoch + 1}/{num_epochs}], Loss: {loss.item():.4f}')

    # 反向传播与优化
    loss.backward()  # 计算梯度
    optimizer.step()  # 更新参数
import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt

# 生成模拟数据(股票价格时间序列)
def create_dataset(data, time_step=1):
    X, Y = [], []
    for i in range(len(data) - time_step - 1):
        X.append(data[i:(i + time_step)])
        Y.append(data[i + time_step])
    return np.array(X), np.array(Y)

# 超参数
input_size = 1        # 输入特征的维度(股票价格)
hidden_size = 20      # 隐藏层的节点数量
output_size = 1       # 输出的维度(预测的价格)
num_layers = 2        # LSTM层的数量
num_epochs = 100      # 训练轮次
learning_rate = 0.01  # 学习率
time_step = 10        # 使用过去10个时间步作预测

# 生成模拟时间序列数据
np.random.seed(0)  # 为了可重现性
data = np.sin(np.linspace(0, 100, 500)) + np.random.normal(0, 0.1, 500)  # 正弦波 + 添加噪声
data = data.astype(np.float32)

# 划分数据集
train_size = int(len(data) * 0.8)
train_data, test_data = data[:train_size], data[train_size:]

# 创建训练和测试数据集
X_train, y_train = create_dataset(train_data, time_step)
X_test, y_test = create_dataset(test_data, time_step)

# 转换为PyTorch张量并增加维度
X_train = torch.from_numpy(X_train).unsqueeze(-1)  # (samples, time step, input size)
y_train = torch.from_numpy(y_train).unsqueeze(-1)  # (samples, output size)
X_test = torch.from_numpy(X_test).unsqueeze(-1)
y_test = torch.from_numpy(y_test).unsqueeze(-1)

# 定义LSTM模型
class LSTMModel(nn.Module):
    def __init__(self, input_size, hidden_size, output_size, num_layers):
        super(LSTMModel, self).__init__()
        self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True)
        self.fc = nn.Linear(hidden_size, output_size)  # 全连接层

    def forward(self, x):
        lstm_out, (h_n, c_n) = self.lstm(x)  # lstm_out为每个时间步的输出
        # lstm_out的形状为 (batch_size, seq_length, hidden_size)
        predictions = self.fc(lstm_out[:, -1, :])  # 只获取最后一个时间步的输出
        return predictions

# 创建模型实例
model = LSTMModel(input_size, hidden_size, output_size, num_layers)
criterion = nn.MSELoss()  # 均方误差损失函数
optimizer = optim.Adam(model.parameters(), lr=learning_rate)  # Adam优化器

# 训练过程
for epoch in range(num_epochs):
    model.train()  # 设置模型为训练模式
    optimizer.zero_grad()  # 清空之前的梯度

                                        # 前向传播
    outputs = model(X_train)            # 获取模型的预测
    loss = criterion(outputs, y_train)  # 计算损失

    # 反向传播与优化
    loss.backward()  # 计算梯度
    optimizer.step()  # 更新参数

    if (epoch + 1) % 10 == 0:
        print(f'Epoch [{epoch + 1}/{num_epochs}], Loss: {loss.item():.4f}')

    # 测试过程
model.eval()  # 设置模型为评估模式
with torch.no_grad():  # 不计算梯度
    predicted = model(X_test)

# 绘制预测结果
plt.figure(figsize=(12, 6))
plt.plot(np.arange(len(data)), data, label='True Data', color='blue')
plt.plot(np.arange(len(train_data) + time_step, len(data) - 1), predicted.numpy(), label='Predicted Data', color='red')
plt.title('Stock Price Prediction')
plt.xlabel('Time Step')
plt.ylabel('Stock Price')
plt.legend()
plt.show()

GRU(门控循环单元)
GRU(门控循环单元)也是一种循环神经网络(RNN)的变体。与LSTM相比,GRU的结构相对简单。

GRU的结构由两部分组成:更新门和重置门。更新门用于控制上一时刻隐藏状态对当前时刻隐藏状态的贡献程度,而重置门用于控制当前输入对当前时刻隐藏状态的更新程度。
在这里插入图片描述
LSTM与GRU比较
1结构复杂性:
LSTM:有三个门(输入门、遗忘门、输出门),每个门都有自己的权重和偏置。
GRU:只有两个门(重置门和更新门),整体结构更加简洁,参数更少,计算效率更高。

2记忆能力:
LSTM:通过单独的细胞状态来保存信息,能够获得更强的长期记忆能力。
GRU:通过更新门控制信息的传递,相对而言稍微简化了记忆机制,但在很多实际应用中也能有效捕捉长期依赖。

3性能:
LSTM:对长序列学习效果较好,但相对复杂的结构会导致训练时间更长。
GRU:由于结构简单,通常在小样本和短序列任务上能够表现出更快的收敛速度,公式的计算量也较小。

4适用场景:
在某些应用中(如语音识别、自然语言处理),GRU 通常能与 LSTM 竞争甚至超越其性能。
LSTM 更适合需要复杂记忆机制的任务,尤其是在长序列情况下。

GRU 可以通过构建一个与 LSTM 类似的模型来实现。下面是使用 PyTorch 实现 GRU 的示例代码:

import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt

# 生成模拟数据(股价时间序列)
def create_dataset(data, time_step=1):
    X, Y = [], []
    for i in range(len(data) - time_step - 1):
        X.append(data[i:(i + time_step)])
        Y.append(data[i + time_step])
    return np.array(X), np.array(Y)

# 超参数设置
input_size = 1        # 输入特征的维度(股价)
hidden_size = 20      # 隐藏层的单元数量
output_size = 1       # 输出的维度(预测的股价)
num_layers = 2        # GRU层的数量
num_epochs = 100      # 训练轮次
learning_rate = 0.01  # 学习率
time_step = 10        # 使用过去10个时间步作为输入预测

# 生成模拟时间序列数据
np.random.seed(0)  # 为了可重现性
data = np.sin(np.linspace(0, 100, 500)) + np.random.normal(0, 0.1, 500)  # 生成正弦波 + 添加噪声
data = data.astype(np.float32)

# 划分数据集
train_size = int(len(data) * 0.8)
train_data, test_data = data[:train_size], data[train_size:]

# 创建训练和测试数据集
X_train, y_train = create_dataset(train_data, time_step)
X_test, y_test = create_dataset(test_data, time_step)

# 转换为 PyTorch 张量并增加维度
X_train = torch.from_numpy(X_train).unsqueeze(-1)  # (样本数, 时间步, 输入特征数)
y_train = torch.from_numpy(y_train).unsqueeze(-1)  # (样本数, 输出特征数)
X_test = torch.from_numpy(X_test).unsqueeze(-1)
y_test = torch.from_numpy(y_test).unsqueeze(-1)

# 定义 GRU 模型
class GRUModel(nn.Module):
    def __init__(self, input_size, hidden_size, output_size, num_layers):
        super(GRUModel, self).__init__()
        self.gru = nn.GRU(input_size, hidden_size, num_layers, batch_first=True)
        self.fc = nn.Linear(hidden_size, output_size)  # 全连接层

    def forward(self, x):
        gru_out, _ = self.gru(x)  # gru_out为每个时间步的输出
        predictions = self.fc(gru_out[:, -1, :])  # 取最后一个时间步的输出
        return predictions

    # 创建模型实例
model = GRUModel(input_size, hidden_size, output_size, num_layers)
criterion = nn.MSELoss()  # 均方误差损失函数
optimizer = optim.Adam(model.parameters(), lr=learning_rate)  # Adam 优化器

# 训练过程
for epoch in range(num_epochs):
    model.train()  # 设置模型为训练模式
    optimizer.zero_grad()  # 清空之前的梯度

    # 前向传播
    outputs = model(X_train)  # 获取模型的预测
    loss = criterion(outputs, y_train)  # 计算损失

    # 反向传播与优化
    loss.backward()  # 计算梯度
    optimizer.step()  # 更新参数

    if (epoch + 1) % 10 == 0:
        print(f'Epoch [{epoch + 1}/{num_epochs}], Loss: {loss.item():.4f}')

    # 测试过程
model.eval()  # 设置模型为评估模式
with torch.no_grad():  # 不计算梯度
    predicted = model(X_test)

# 绘制预测结果
plt.figure(figsize=(12, 6))
plt.plot(np.arange(len(data)), data, label='True Data', color='blue')
plt.plot(np.arange(len(train_data) + time_step, len(data) - 1), predicted.numpy(), label='Predicted Data', color='red')
plt.title('Stock Price Prediction Using GRU')
plt.xlabel('Time Step')
plt.ylabel('Stock Price')
plt.legend()
plt.show()