python神经网络编程入门(二十八)——RNN vs LSTM vs GRU 三模型横向对比实验
2026/8/8 20:31:05 网站建设 项目流程

引言:同一张桌子,比个高下

上一章把 GRU 单独训练了 25 轮,验证准确率爬到 73.85%。但单看一家,看不出好坏——GRU 到底比 RNN 强多少?LSTM 是不是更厉害?不放在一起跑,谁也说不清。

这一章做一件简单又公平的事:把 RNN、LSTM、GRU 三个模型,用完全一样的食材(同一份数据)、一样的火候(同一批超参数)、一样的起点(同一个随机种子)各炒一遍,然后看谁先熟、谁最香、谁糊了。

🎯本章目标

  1. 用"控制变量法"搭起三模型公平对比的框架;
  2. 同一张图上看清三者的收敛速度与最终精度;
  3. 用真实数据验证第 10 章的理论结论:RNN 梯度消失、GRU 性价比最高。

一、控制变量:公平对决的前提

要比得公平,就得让三个模型只差"循环单元"这一处,其余全部锁死。这就是理科里常说的控制变量法——其他条件不动,只改想比的那一个变量。

超参数取值为什么锁死
Embedding 维度64三个模型查同一本"字典"
Hidden 维度64记忆容量一样大
Epoch12大家都跑同样的时长
学习率1e-3(Adam)步子一样大
Batch Size64每批吃同样多的样本
梯度裁剪5.0防止个别模型当场爆炸
随机种子42起点完全一致,可复现

这样,最后谁高谁低,就只可能是"循环单元"本身造成的。


二、同一副骨架,换核心零件

三个模型只是中间那层不同,前后的EmbeddingLinear(1)完全一样。代码上把循环单元做成一个能随时替换的"零件"即可:

defmake_model(cell):unit={'RNN':nn.RNN,'GRU':nn.GRU,'LSTM':nn.LSTM}[cell]classNet(nn.Module):def__init__(self):super().__init__()self.emb=nn.Embedding(VOCAB,64,padding_idx=0)self.rnn=unit(64,64,batch_first=True)# 只换这一层self.fc=nn.Linear(64,1)defforward(self,x):e=self.emb(x)out=self.rnn(e)h_n=out[1]ifisinstance(out,tuple)elseout[-1]h_n=h_n[0]ifisinstance(h_n,tuple)elseh_nreturnself.fc(h_n[-1]).squeeze(-1)# 最后一步记忆打分returnNet()

有个小坑值得记住:nn.LSTM返回的是(输出, (h_n, c_n))的元组,而nn.RNN/nn.GRU返回(输出, h_n)。所以取最后一步记忆时,得先判断out是不是元组、h_n又是不是元组,统一取到"最后一层、最后一个时间步"的隐藏状态——上面那两行isinstance就是在处理这个差异。

训练三遍,唯一变的只有make_model('RNN')/make_model('GRU')/make_model('LSTM')

forcellin['RNN','GRU','LSTM']:torch.manual_seed(42);np.random.seed(42)# 起点一致model=make_model(cell)opt=torch.optim.Adam(model.parameters(),lr=1e-3)forepinrange(12):forstinrange(0,8000,64):x,y=pack(train_idx[st:st+64])# 填充+掩码,同第14章lo=lossf(model(x),y)opt.zero_grad();lo.backward()nn.utils.clip_grad_norm_(model.parameters(),5.0)opt.step()vacc=evaluate(model,val_idx)# 每轮在验证集打分

训练期间,每轮在 2000 条验证集上测一次准确率,12 轮下来就有三条完整的曲线。


三、三足鼎立,一张图见分晓

把三条验证准确率曲线画在同一张图上,谁在爬升、谁在打摆子,一目了然:

三模型验证准确率对比(IMDB,12 轮)0.450.500.550.600.650.700.7501234567891011EpochRNN(贴底震荡)LSTM(后段跳升)GRU(5 轮后陡升)73.65%63.25%RNN 峰值 51.35%
  • RNN(绿线,贴底震荡):12 轮全程在 50% 上下抖动,峰值只有51.35%。这就是第 5 章讲的梯度消失——信息传不了几层就湮灭,训练损失虽然缓慢下降,验证准确率却始终在"瞎猜"水平,学不进去。
  • GRU(蓝线,陡升最高):前 4 轮还在 50% 附近热身,第 5 轮后突然崛起,一路冲到峰值73.65%,最终测试集73.06%。既快又准,名不虚传。
  • LSTM(红线,后段跳升):前 5 轮也压着 50%,第 6 轮跳升到 60.95%,随后又回落震荡,第 9 轮再爬到63.25%,测试集62.38%。能学,但"热得慢",节奏不稳定。

把关键数字收拢成一张结果表:

模型验证峰值测试集准确率参数量表现
RNN51.35%50.65%328,513梯度消失,学不动
GRU73.65%73.06%345,153收敛快、精度高
LSTM63.25%62.38%353,473慢热、震荡

GRU 用最少的门控(2 个门)、比 LSTM 小的参数量,却跑出了最高的精度——这正是第 10 章"IMDB 实战选 GRU 当主力"的实证。


四、为什么 RNN 会"打摆子"?

RNN 全程贴底,根源还是那条老路的梯度消失。把第 4 章的结论搬回来:隐藏状态更新是

ht=tanh⁡(Whh ht−1+Wxh xt)h_t = \tanh\big(W_{hh}\,h_{t-1} + W_{xh}\,x_t\big)ht=tanh(Whhht1+Wxhxt)

反向传播时,梯度要一路乘过∂ht/∂ht−1\partial h_t/\partial h_{t-1}ht/ht1

∂L∂h1=∏t=2T[Whh⊤ diag(1−ht2)] ∂L∂hT\frac{\partial L}{\partial h_1} = \prod_{t=2}^{T} \Big[ W_{hh}^\top \,\mathrm{diag}\big(1-h_t^2\big) \Big] \,\frac{\partial L}{\partial h_T}h1L=t=2T[Whhdiag(1ht2)]hTL

影评动辄一两百个词,这一串连乘里只要谱半径小于 1,梯度就指数式缩到几乎为零,模型收不到来自远处的信号,自然学不动。GRU 和 LSTM 靠门控另开了"高速通道",梯度能顺着细胞状态近无损地传回去,才解得开这个结。


五、常见坑与自查

  • 公平性被破坏:某个模型忘了设种子,或改了学习率,曲线就不具可比性。每次换模型前都要重新manual_seed(42)
  • LSTM 取错状态:LSTM 返回的是元组(h_n, c_n),直接当 RNN 那样取out[-1]会拿到c_n张量、维度对不上。先用isinstance判一下类型。
  • 只看训练损失:RNN 的训练损失也在降(从 0.70 到 0.55),但验证准确率纹丝不动——评价必须看验证集准确率,不能只看训练损失
  • 种子固定了但数据没洗牌:忘了第 14 章的教训,切分前不shuffle,验证集变单一种类,三条曲线统统失真。

小结

这一章用控制变量法,把三个模型放到同一张桌子上比了个高下:

  • 公平对决:同一份数据、同一批超参数、同一个种子,只换循环单元;
  • RNN 学不动:全程卡在 50% 震荡,峰值 51.35%,梯度消失的实证;
  • GRU 又快又准:第 5 轮陡升,验证峰值 73.65%、测试 73.06%,性价比之王;
  • LSTM 慢热:后段才爬到 63.25%,能学但不稳。

结论和第 10 章的理论预测完全对上了:IMDB 这类中等规模任务,GRU 是效率与精度兼得的主力。但这还只是 12 轮、64 维隐藏的"初赛"。下一章还会在最优模型上引入 Dropout、调大隐藏维度,挑战更高的精度上限。

下一篇(二十九):模型优化、测试评估与RNN知识体系收官

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询