什么是门控循环单元?

一、概念

门控循环单元(Gated Recurrent Unit,GRU)是一种改进的循环神经网络(RNN),由Cho等人在2014年提出。GRU是LSTM的简化版本,通过减少门的数量和简化结构,保留了LSTM的长时间依赖捕捉能力,同时提高了计算效率。GRU通过引入两个门(重置门和更新门)来控制信息的流动 。与LSTM不同,GRU没有单独的细胞状态,而是将隐藏状态直接作为信息传递的载体,因此结构更简单,计算效率更高。

二、核心算法

为时间步 t 的输入向量,为前一个时间步的隐藏状态向量,为当前时间步的隐藏状态向量,为当前时间步的重置门向量,为当前时间步的更新门向量,为当前时间步的候选隐藏状态向量,分别为各门的权重矩阵,为偏置向量,为sigmoid激活函数,tanh为tanh激活函数,*为元素级乘法。

1、重置门

重置门控制前一个时间步的隐藏状态对当前时间步的影响。通过sigmoid激活函数,重置门的输出在0到1之间,表示前一个隐藏状态元素被保留的比例。

2、更新门

更新门控制前一个时间步的隐藏状态和当前时间步的候选隐藏状态的混合比例。通过sigmoid激活函数,更新门的输出在0到1之间,表示前一个隐藏状态元素被保留的比例。

3、候选隐藏状态

候选隐藏状态结合当前输入和前一个时间步的隐藏状态生成。重置门的输出与前一个隐藏状态相乘,表示保留的旧信息。然后与当前输入一起通过tanh激活函数生成候选隐藏状态。

4、隐藏状态更新

隐藏状态结合更新门的结果进行更新。更新门的输出与前一个隐藏状态相乘,表示保留的旧信息。更新门的补数与候选隐藏状态相乘,表示写入的新信息。两者相加得到当前时间步的隐藏状态。

三、python实现

python 复制代码
import torch
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt
 
# 设置随机种子
torch.manual_seed(0)
np.random.seed(0)
 
# 生成正弦波数据
timesteps = 1000
sin_wave = np.array([np.sin(2 * np.pi * i / timesteps) for i in range(timesteps)])
 
# 创建数据集
def create_dataset(data, time_step=1):
    dataX, dataY = [], []
    for i in range(len(data) - time_step - 1):
        a = data[i:(i + time_step)]
        dataX.append(a)
        dataY.append(data[i + time_step])
    return np.array(dataX), np.array(dataY)
 
time_step = 10
X, y = create_dataset(sin_wave, time_step)
 
# 数据预处理
X = X.reshape(X.shape[0], time_step, 1)
y = y.reshape(-1, 1)
 
# 转换为Tensor
X = torch.tensor(X, dtype=torch.float32)
y = torch.tensor(y, dtype=torch.float32)
 
# 划分训练集和测试集
train_size = int(len(X) * 0.7)
test_size = len(X) - train_size
trainX, testX = X[:train_size], X[train_size:]
trainY, testY = y[:train_size], y[train_size:]
 
# 定义RNN模型
class GRUModel(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(GRUModel, self).__init__()
        self.hidden_size = hidden_size
        self.gru = nn.GRU(input_size, hidden_size, batch_first=True)
        self.fc = nn.Linear(hidden_size, output_size)
 
    def forward(self, x):
        h0 = torch.zeros(1, x.size(0), self.hidden_size)
        out, _ = self.gru(x, h0)
        out = self.fc(out[:, -1, :])
        return out
 
input_size = 1
hidden_size = 50
output_size = 1
model = GRUModel(input_size, hidden_size, output_size)
 
# 定义损失函数和优化器
criterion = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
 
# 训练模型
num_epochs = 50
for epoch in range(num_epochs):
    model.train()
    optimizer.zero_grad()
    outputs = model(trainX)
    loss = criterion(outputs, trainY)
    loss.backward()
    optimizer.step()
 
    if (epoch + 1) % 10 == 0:
        print(f'Epoch [{epoch + 1}/{num_epochs}], Loss: {loss.item():.4f}')
 
# 预测
model.eval()
train_predict = model(trainX)
test_predict = model(testX)
train_predict = train_predict.detach().numpy()
test_predict = test_predict.detach().numpy()
 
# 绘制结果
plt.figure(figsize=(10, 6))
plt.plot(sin_wave, label='Original Data')
plt.plot(np.arange(time_step, time_step + len(train_predict)), train_predict, label='Training Predict')
plt.plot(np.arange(time_step + len(train_predict), time_step + len(train_predict) + len(test_predict)), test_predict, label='Test Predict')
plt.legend()
plt.show()

四、总结

GRU的结构比LSTM更简单,只有两个门(重置门和更新门),没有单独的细胞状态。这使得GRU的计算复杂度较低,训练和推理速度更快。通过引入重置门和更新门,GRU也有效地解决了标准RNN在处理长序列时的梯度消失和梯度爆炸问题。然而,在需要更精细的门控制和信息流动的任务中,LSTM的性能可能优于GRU。因此在我们实际的建模过程中,可以根据数据特点选择合适的RNN系列模型,并没有哪个模型能在所有任务中都具有优势。

相关推荐
陈敬雷-充电了么-CEO兼CTO1 分钟前
强化学习三巨头PK:PPO、GRPO、DPO谁是大模型训练的「王炸」?
人工智能·python·机器学习·chatgpt·aigc·ppo·grpo
Wendy14414 分钟前
【图像噪点消除】——图像预处理(OpenCV)
人工智能·opencv·计算机视觉
大江东去浪淘尽千古风流人物4 分钟前
【prompt】Lyra 提示词深度研究
人工智能·prompt
Adorable老犀牛15 分钟前
AI×运维:从“救火队员”到“预见者”的涅槃:智启下一代IT运维的无限可能
运维·人工智能·aiops
coder_pig18 分钟前
👦抠腚男孩的AI学习之旅 | 3、AI-概念名词 & LLM-模型微调
人工智能·aigc·ai编程
是店小二呀19 分钟前
【动态规划 | 多状态问题】动态规划求解多状态问题
算法·动态规划
竹子_231 小时前
《零基础入门AI:传统机器学习核心算法解析(KNN、模型调优与朴素贝叶斯)》
人工智能·算法·机器学习
boyedu1 小时前
哈希指针与数据结构:构建可信数字世界的基石
数据结构·算法·区块链·哈希算法·加密货币
SiYuanFeng1 小时前
【问题未解决-寻求帮助】VS Code 中使用 Conda 环境,运行 Python 后 PowerShell 终端输出内容立即消失
开发语言·python·conda
✿ ༺ ོIT技术༻1 小时前
剑指offer第2版:双指针+排序+分治+滑动窗口
算法·排序算法·剑指offer·双指针·滑动窗口·分治