目录
[一、GRU 解决什么问题?](#一、GRU 解决什么问题?)
[二、GRU 的结构与原理](#二、GRU 的结构与原理)
[三、 API 介绍](#三、 API 介绍)
摘要
基础 RNN 虽然引入隐藏状态实现序列记忆,但存在致命缺陷:长序列训练时梯度消失,句子很长时,模型会遗忘开头的信息。为了解决这个问题,研究者提出门控循环单元(Gated Recurrent Unit,GRU)。
GRU 保留了 RNN 逐时间步读取序列、参数共享的核心思路,新增门控机制,可控地决定:哪些记忆保留、哪些信息丢弃。GRU 是 LSTM 的轻量化版本,参数量更少、训练速度更快,工业场景里非常常用。
**一、**GRU 解决什么问题?
回顾基础 RNN 痛点:当句子很长时,早期时间步的信息,经过多层时间递推,梯度不断衰减,最后几乎丢失。
|-----------------------------------------------|
| "今天天气很好,阳光明媚,微风不燥,适合出门去公园散步,公园里花开了,五颜六色非常好看。" |
基础 RNN 读到后半句"好看"的时候,很容易遗忘最开头的「今天天气很好」这个关键上下文。
GRU 通过更新门、重置门两个门,对记忆做可控筛选,缓解长距离依赖与梯度消失问题。
**二、**GRU 的结构与原理
GRU 不再是简单直接覆盖旧记忆,而是用两个门控来控制隐藏状态的更新:
- 重置门( Reset Gate ) :控制要不要忘掉过去的记忆;
- 更新门( Update Gate ) :控制多大程度保留旧记忆,多大程度吸收新输入。
**1.**逐时间步计算公式

输入:当前时间步词嵌入 xt
,上一步隐藏状态 ht-1
。
重置门
更新门
候选隐藏状态

:sigmoid 激活,输出 0~1 之间的值。越靠近 1 代表"打开门",靠近 0 代表"关闭门";
:哈达玛积,对应元素相乘。
门含义通俗解释
1. 重置门 
越小,代表更多丢弃上一步旧记忆;
接近 1,保留过去记忆。适合场景:句子出现全新语义,需要清空旧记忆。
2. 更新门 
决定新旧记忆的融合比例:
:几乎保留旧记忆,不接收新信息;
:几乎丢弃旧记忆,完全使用新候选状态 h ~t
。
|---------------------------------------------------------------------------------------------------------------------------------|
| 依然满足:所有时间步共享同一套权重 。参数是记忆规则,隐藏状态 ht
存储当前句子的上下文。 |
**2.**初始化 
和 RNN 保持一致:每个新句子都要单独初始化
(零向量),句子之间记忆隔离,互不干扰。
句子读完后,最后时间步隐藏状态
依然压缩整句上下文信息,后续送入 MLP 做分类任务。
**三、**API 介绍
完整构造签名:
python
torch.nn.GRU(input_size, hidden_size, num_layers=1, bias=True,
batch_first=False, dropout=0.0, bidirectional=False)
|-------------------------|-------------------------------------------------|-------------------------|
| 参数 | 含义 | 文档例子取值 |
| input_size(embed_dim) | 每个时间步输入向量维度,词嵌入维度 | 8:单个词为 8 维向量 |
| hidden_size(hidden_dim) | 隐藏状态维度,记忆容量 | 16 |
| batch_first=True | 批量放第一维,输入 shape 变为 (batch, seq_len, input_size) | True,和 Embedding 输出直接对接 |
|-----------------------------------------------------------------------------------------------------------------|
| 输出说明: · output:(batch, seq_len, hidden_dim),保存每一个时间步的隐状态 · h_n:(num_layers, batch, hidden_dim),最后一层最后时间步隐状态 |
每个时间步隐藏状态维度固定 = hidden_dim=16;输入词嵌入 xt
依旧是 8 维。
四、代码Demo
python
import torch
import torch.nn as nn
class GRUClassifier(nn.Module):
def __init__(self, vocab_size, embed_dim, hidden_dim, num_classes):
super().__init__()
self.embedding = nn.Embedding(vocab_size, embed_dim)
# GRU 层替换原来的 RNN
self.gru = nn.GRU(embed_dim, hidden_dim, batch_first=True)
self.fc = nn.Linear(hidden_dim, num_classes)
def forward(self, x):
# x: (batch, seq_len) 词索引
emb = self.embedding(x) # (batch, seq_len, embed_dim)
output, h_n = self.gru(emb) # output: (batch, seq_len, hidden_dim)
h_last = h_n[-1] # 取最后一层隐状态 (batch, hidden_dim)
logits = self.fc(h_last) # (batch, num_classes)
return logits
# 测试
vocab_size, embed_dim, hidden_dim, num_classes = 20, 8, 16, 2
model = GRUClassifier(vocab_size, embed_dim, hidden_dim, num_classes)
x = torch.tensor([[1, 2, 3, 4],
[5, 6, 7, 8]])
logits = model(x)
print(logits.shape) # torch.Size([2, 2])
五、总结
优点
- 引入门控机制,有效缓解基础 RNN 的梯度消失问题,可以捕捉更长距离的上下文,长句子的理解效果明显优于基础 RNN;
- 训练稳定性更强,长序列训练时,不容易出现梯度剧烈衰减的情况;
- 保留基础 RNN 的核心优势:所有时间步参数共享,天然支持长度不固定的序列输入;
- 仅维护一组隐藏状态,模型逻辑直观,代码实现难度低。
缺点
- 和基础 RNN 相同,必须逐时间步串行计算,序列无法并行处理;
- 虽然改善了梯度消失,但面对极长的句子,远距离信息依然会有一定程度衰减;
- 相比基础 RNN,增加了门控相关权重,参数量更大,训练占用的内存资源更多;
- 模型计算流程比基础 RNN 复杂,单步运算耗时高于原始 RNN。