3.6 门控循环网络
1.让模型自己来管记忆
上一章我们聊过普通的循环网络,它每次都用新输入和旧状态一股脑算出新状态,想删什么、想留什么,它都说了不算,只能被动地把全部信息搅在一起。这么一来,只要序列一长,老信息要么被新输入冲淡,要么在反复相乘里被慢慢压扁,模型到头来基本就是个记不住事的状态,长期依赖这条路也就走不通了。这个毛病我们上一章也提过,叫梯度消失。
门控循环网络就在这一点上做了文章。它给网络加了一批可以学习的控制信号,这些信号叫作门。门的作用是让模型自己拿主意,决定哪些旧信息该留着、哪些新信息该写进去。有了门,模型就从被动记录升级成主动管理,记忆这件事也就变得有取有舍了。
这么说大概有点空,我们一层一层来看,先从结构最精简的GRU讲起。
2.先看GRU:两个门就够用
我们先以GRU(门控循环单元)为例,感受一下门是怎么工作的。GRU结构相对精简,是挺好上手的入门款。
每一个时间步 ( 就是时间步的下标,表示第 个时刻),GRU会先算出两个门。第一个叫更新门,记作 ,它的值在0和1之间,管旧状态和新候选状态各按多大比例混进新状态里,计算式是 。第二个叫重置门,记作 ,值也在0和1之间,管算候选状态的时候旧状态要参与多少,计算式是 。
这一步喂进来的输入是 ( 就是当前时间步看到的输入),上一时间步留下来的旧状态是 ( 可以理解成模型上一秒的隐藏记忆)。两个门公式里的方括号 表示把旧状态和当前输入拼成一个更长的向量, 和 是两个可以学习的权重矩阵(模型在训练里自己调出来的参数), 是Sigmoid函数,它能把任意实数压到0和1之间,所以门的值都落在0到1这个区间。
模型还会先算出一个候选状态 (候选状态就是这一步备选的新内容,相当于打草稿),计算式是 。这里 是双曲正切函数,把实数压到 和 之间,所以候选状态可能带正负号, 是候选状态的权重矩阵,符号 叫Hadamard积,也叫逐元素相乘,就是把两个同样形状的向量按位置一位一位地乘(比如 和 的Hadamard积是 ),用它来控制旧状态每一维参与多少。
算出候选状态之后,新的隐藏状态就由更新门来拍板了,公式是 。这个式子读起来很直白,更新门 接近1,旧状态 就保留得多(倾向于保持记忆),接近0,候选状态 就写进去得多(倾向于接受新内容),两边按门的值做加权平均。重置门 接近1,算候选状态时就多参考旧状态,接近0就基本不看旧状态。
说起来,我们不妨打个比方。GRU有点像在一个本子上记笔记。更新门决定这一页是接着上一页的思路往下写,还是另起一页重写,重置门决定写新内容之前,要不要把上一页翻出来参考参考。两个门一配合,记忆的新旧更替就完成了。
再换一个更贴近日常的例子。小明打开手机,群里有几百条未读消息。更新门 在问,前面那一长串对话的上下文还要不要接着用,接近1就接着用,接近0就基本丢掉重来。重置门 在问,整理当前这条消息的时候,前面的内容要参考多少,接近1就多参考,接近0就基本不看。两个门一拿主意,新消息怎么记、记多少,心里就有数了。
3.LSTM:多了条专跑记忆的快速路
LSTM(长短期记忆网络)比GRU更进一步。它除了隐藏状态 ( 是隐藏状态,模型对外的输出就靠它),还额外维护一条专门的细胞状态,记作 ( 就是细胞状态,可以理解成模型长期藏起来的小金库,外人不直接看)。围绕这条细胞状态,LSTM安排了三个门来照看它。
第一个是遗忘门 ( 管旧细胞状态保留多少),计算式是 。第二个是输入门 ( 管新信息写进去多少),计算式是 。第三个是输出门 ( 管当前状态对外暴露多少),计算式是 。三个门各管一摊,分工明确,值通通在0和1之间,公式里的 、、 是各自的权重矩阵,方括号 同样是把旧状态和当前输入拼起来。
LSTM还要算一个候选细胞状态 (可以理解成这一步备选的新长期内容),公式是 ,这里的 是候选细胞状态的权重矩阵, 让它的值落在 和 之间。接着细胞状态自己按下式更新:
这个式子读起来就是,遗忘门 决定上一秒的旧细胞状态 各维保留多少,输入门 决定候选细胞状态 各维写进去多少,两块加起来就是新的细胞状态 , 同样是逐元素相乘。最后隐藏状态由输出门把关,公式是 ,也就是先用 把细胞状态压到 和 之间,再由输出门 决定每一维对外暴露多少。
这条细胞状态 的妙处在于,它是一条相对平稳的信息通道,加加减减的操作比较多,反复相乘的比较少。还记得梯度消失是怎么来的吧,就是一路相乘给乘没的。LSTM这条细胞状态绕开了频繁的相乘,所以梯度可以沿着它传很远而不被压扁。就这一点设计,让LSTM在长序列任务上稳稳站住了脚,捕捉长距离依赖时比普通循环网络靠谱得多,在当年可以说是序列建模里的绝对主角。
顺带一提,LSTM还有一个挺有意思的变种叫窥孔连接(peephole connection)。标准LSTM的三个门只看 和 ,门对细胞状态本身当前是多少完全没数。窥孔连接的改动很直接,把上一时刻的细胞状态 也塞进门的输入里,让门能看一眼小金库里到底攒了多少货。拿遗忘门来说,公式就从 变成 ,输入门和输出门同理也把 加进去。这么一改,门调节起来就更有针对性,好比管账的得先看一眼账户余额,再决定这笔账怎么记。窥孔连接在某些任务上能再挤出一点效果,不过参数也跟着多一点,算是锦上添花的小改款。
门控网络的核心思想,其实就一句话,把记忆的更新这件事,拆成几个各司其职、可以分别控制的小步骤,让模型自己学着怎么管最划算。
4.门控也是有代价的
门控结构确实治好了长期依赖这个老大难,可天下没有免费的午餐。门一多,参数量和计算量都跟着往上涨,训练起来更费时费显存。我记得有一本讲工程项目经验的书里说过,再精巧的设计,离开了足够的算力和存储也跑不起来。门控网络也是这个道理,参数上去了,就得有GPU和显存来支撑。
所以GRU和LSTM经常被拿来比较。GRU参数少、跑得快,简单任务上够用,LSTM参数多、更稳,长序列和复杂任务上更顶得住。选哪个,得看具体任务和手头资源,没有绝对的优劣。
还有一点特别容易让人误解,得专门澄清一下。门虽然听起来像是开关,但它真正的值,其实是0到1之间的连续小数,远没有人类开关那样非开即关那么干脆。门里头的数值,完全是为预测任务服务、由数据学出来的连续控制信号。模型觉得怎么管记忆对完成任务最有利,就会把门调成什么样,跟我们直觉里的开关闭合差得比较远。
5.GRU和LSTM到底怎么挑
初学者最容易纠结的,大概就是这两个到底选哪个。我们把这件事掰开来说。
先看参数量。GRU有更新门、重置门和候选状态,权重矩阵一共三组,对应 、、。LSTM有遗忘门、输入门、输出门和候选细胞状态,权重矩阵一共四组,对应 、、、。每组的尺寸两家差不多,都是把拼接后的 映射回隐藏维度。所以粗算下来,LSTM的参数量大约是GRU的三分之四倍,也就是多出大约三分之一。可别小看这三分之一,隐藏维度一拉大、层数一叠上去,多出来的参数和显存就相当可观了。
再看什么时候偏好哪个。数据量不大、序列不长、显卡又有点紧的场合,GRU是更划算的选择,它训练快、不容易在小数据上过拟合,调起来也省心,属于那种小而美的款。反过来,数据量足、序列长、任务又复杂(比如要建模几十上百步的远距离依赖),LSTM就更顶得住,它那条专门的细胞状态在长程记忆上更稳,能扛住更长的序列。工程实践里有个常用的经验,先拿GRU快速跑个baseline看看效果,不够再换LSTM往上顶,这样性价比最高。小张做毕设那会儿就是这么干的,先GRU快速验证想法行得通,最后要交差了才换LSTM把指标再往上提一截。
其实这两个谁也没把谁彻底比下去。学术界和工业界都做过海量对比,结论大体上是大部分任务上两者效果接近,个别长序列任务LSTM略占优。所以纠结选哪个意义不大,把数据喂够、把超参数调明白,才是真正决定成败的地方。
6.往前看一眼,再叠高几层
光一个单向的LSTM或GRU,有时候还不够用。这里说两个常见的加强思路。
第一个是双向循环网络(Bidirectional RNN)。它让序列从前往后跑一遍,再从后往前跑一遍,两路各自维护一份隐藏状态,最后把同一时刻的两个隐藏状态拼起来对外输出。单向网络只能根据当前时刻之前的信息做判断,双向网络还能参考一下未来的信息,理解得更全面。比如做词性标注,一个词是名词还是动词,光看前面几个词可能定不下来,往后看一眼往往就清楚了。这有点像看推理小说,读到后半段才知道前面某个不起眼的细节是什么意思,回头再看一遍,理解就完整了。双向的代价是得先把整个序列拿到手,那些必须边来边处理的实时在线任务(实时语音流、股票下一秒预测这种)就用不了,只能老老实实用单向。
第二个是把多层循环网络叠起来用,叫堆叠循环网络(Stacked RNN)。第一层的输出当成第二层的输入,第二层的输出再喂给第三层,层数自己定。底层抽局部短程的特征,高层把这些特征再往上抽象一层,表达能力强了不少。不过叠得太高也容易出问题,参数变多、训练变慢、还容易过拟合,有点像运动员训练,加量固然能出成绩,加过了头反而容易受伤。一般叠个两到三层就够用了,再往上性价比就不行了。用PyTorch搭堆叠循环网络很省事,创建LSTM或GRU的时候把num_layers参数设成大于1的数就行。
7.门控循环网络能做什么
学了这一堆门和状态,到底能拿去干些什么,我们点几个典型应用场景,让大家心里有个数。
第一个是语音识别。语音信号本质上是随时间变化的波形,切成帧之后就是一条很长的序列,相邻帧之间还高度相关,正好是循环网络的主场。在Transformer进来之前,LSTM配连接时序分类(CTC)一度是语音识别的标配,比如DeepSpeech那一路,把声学特征帧喂给多层双向LSTM,输出每个时刻对应声学单元的概率,效果相当出彩。就算到了今天,端到端语音识别系统里依然能看到LSTM和GRU的身影。
第二个是时间序列预测。无论是股价走势、电网负荷、还是校园里食堂的人流量,凡是带时间顺序的连续数据,门控循环网络都能搭一把手。它擅长抓住历史里的周期和趋势,再用门控决定哪些老信息对预测下一时刻有用,这在流量预测、设备故障预警、天气预测这类任务上,都是个靠谱的baseline。
第三个是早期的机器翻译。在注意力机制和Transformer火起来之前,神经机器翻译基本靠编码器和解码器两个LSTM撑场面,也就是大名鼎鼎的seq2seq框架。编码器把源语言句子读成一个向量,解码器再一个词一个词地往外吐目标语言。那个年代这套组合拳可以说是封神之作,把传统统计翻译方法甩开了不止一个身位,循环网络也因此风光了好些年。
除了这三个大头,门控循环网络在文本分类、情感分析、命名实体识别这些自然语言处理任务上也都广泛使用过。可以说在Transformer出来之前的那几年,LSTM和GRU就是序列建模的绝对主力,养活了一大批论文和工程。哪怕后来注意力机制成了新宠,门控循环网络作为入门序列模型的核心地位依然没动摇,把它学透了,后面再去看Transformer和注意力那些东西,会觉得顺理成章得多。
练习
Q1. LSTM 多出来的那条细胞状态 ,凭什么就能治好长期依赖?
因为 这条通道更新时主要是 这种加加减减的线性叠加,反复相乘的环节少。梯度沿它反传时只要遗忘门 接近 1,梯度就能基本不衰减地一路传回浅层,绕开了普通循环网络里那种连乘把梯度压扁的毛病。所以它能捕捉长距离依赖。
Q2. GRU 的隐藏状态更新式是 。若某时刻更新门 全等于 1,或全等于 0,新状态分别是什么?又已知 LSTM 权重矩阵有四组、GRU 有三组,两者参数量大致什么比例?
全等于 1 时 ,完全保留旧记忆、忽略新候选内容; 全等于 0 时 ,完全采用新候选状态。参数量上,每组矩阵尺寸两家差不多,LSTM 四组、GRU 三组,所以 LSTM 的参数量约为 GRU 的三分之四倍,也就是多出大约三分之一。
Q3. 有人把门控循环网络里的"门"理解成一个二值开关,说门开了信息才过、门关了就彻底断,这种说法对吗?
不对。门的值是 Sigmoid 输出,落在 0 到 1 之间的连续小数,不是非开即关的二值开关。它是一组为预测任务服务、由数据学出来的连续控制信号,门值 0.3、0.7 都很常见,决定的是每一维信息按多大比例通过,而不是通断。
Q4.(面试题) 手头有个序列建模任务,序列中等长度、显卡显存也紧,你倾向先上 GRU 还是 LSTM?为什么?再说说双向和堆叠循环网络分别适用什么场合。
一般先上 GRU。它参数少约三分之一、训练快、在小数据上不容易过拟合,先用它快速跑出 baseline 验证思路,不够再换 LSTM 顶上去,这是工程上性价比最高的做法。双向循环网络适合能把整段序列一次性拿到手的场合(如词性标注、机器翻译编码器),它能同时参考前后文;但它要求先看完全程,对实时语音流、下一秒预测这种在线任务就用不了,只能用单向。堆叠循环网络(多层叠加)适合需要更强表达能力的任务,一般叠两到三层,能从局部短程特征往高层抽象特征层层提,但叠太高会过拟合、训练变慢。