神经网络的一个基础假设是:通过堆叠一些基础的计算单元,我们能够构建强大的智能体。
所以不仅仅是Transformer,其它所有主流的神经网络架构(如CNN、RNN、MLP),他们的核心计算公式都很简单。
比如,RNN和MLP,它们最核心的计算都可以归结为一个线性变换: ��+�=� 。CNN的核心计算可以归结为点乘求和: �1×�1+�2×�2=� 。
单看这些公式,不能得出任何关于它们能力的相关信息。
所以我们不能以核心公式的复杂性来推断模型的能力。
以Transformer为基础组件的大语言模型之所以表现出了强大的能力,一个重要的原因是它解决了传统基于CNN或RNN在对文本建模中遇到的长依赖问题。
像现在大家都知道的大语言模型、基于人类反馈的强化学习,没有Transformer一样能做。用CNN、用RNN都能做,但没有质变。这些都不是Transformer能成功,而CNN、RNN不能成功的本质问题。
本质是长依赖问题在Transformer中“不存在了”。
1. 文本建模中的长依赖问题
在Transformer出来之前,深度学习在自然语言处理中面临的最大难题就是长依赖问题。举一个简单例子,假设有如下这句话:
小明在星期天要去露营,他准备叫上小红。
在这个例子中,“小明”和“他”指代的对象是同一个,理解这个概念是理解整个句子的前提。可以把这个问题当做“长依赖”的一个典型问题。所谓的“长”,是指“小明”和“他”这两个词之间隔了许多其它词。虽然这个例子中看起来这个距离并不长,但这是相对于人类能力来说的。下面会具体到神经网络的计算层面来解释这个问题。
可以先从人类的角度来考虑一下要把“小明”和“他”关联起来的前提是什么。
这个前提很简单:我们需要同时看到这两个词。
这么简单的事情对Transformer之前的模型来说(比如CNN、RNN),却并不容易。
1.1 CNN捕捉长依赖的方式
先以CNN为例。CNN可以看作是以一个滑动窗口的形式作用于这一串文本,在每个窗口对窗口内的数据进行计算,如图1所示。
图1 卷积计算示意图1
图1中,假设卷积窗口从左向右移动,因此它在计算开始时处于文本最左侧,如图中橙色文字区域所示(小明在)。卷积操作通过对这三个文字进行处理,得到输出特征A1。实际上,图1中的文字、特征都是以向量形式在参与计算。这里为了简化,不展开细节。在理解这个过程时,可以只以信息流的形式来理解:“小明在”三个字的信息流入了特征A1。
计算完A1后,卷积向右滑动一格,计算特征A2,如图2所示。按照之前的理解方式,“明在星”的信息流入了特征A2。
图2 卷积计算示意图2
重复这个过程,对整个文本进行处理,便得到一个卷积核计算出的所有特征,如图3所示。
图3 卷积计算的结果,得到特征A1至A15
图3中,我们用橙色区域强调了“小明”和“他”之间的距离。在原文中,这两个词之间相距8个字。在新计算出的特征中,注意“小明”两个字只共同参与了特征A1的计算,而“他”参与了特征A9、A10和A11的计算,其中A9距离A1最近。
A1中含有“小明”的信息,而A9中含有“他”的信息。
在这一次的卷积计算过程,卷积操作本身并没有同时作用于“小明”和“他”这两个词,所以它无法建立它们之间的任何关系(因为模型的卷积操作都没有同时看到它们)。
但是容易发现,特征A1和特征A9之间的距离相较于“小明”和“他”的距离变近了。所以我们可以继续在特征A上再堆叠一次卷积,然后计算,然后再堆叠卷积,再计算,直到卷积操作能直接获取到“小明”和“他”的信息,如图4所示。
图4 持续堆叠卷积,让“小明”和“他”的信息能够被卷积捕捉到
我们通过堆叠更多的卷积,终于让“小明”和“他”的信息流入了同一个卷积中,即作用于D1、D2、D3的卷积中,它的输出是E2。换言之,E2中第一次同时包含了“小明”和“他”的信息。这个信息流的过程以图4中蓝线表示。
我们不用考虑E2表示这个信息的具体形式,只需要明白,按照卷积计算的逻辑,只有到了D1、D2、D3时,模型才能同时看到与“小明”和“他”有关的信息。
这就是深度神经网络中深度的含义。在这个例子中,我们通过更多的卷积操作,把卷积网络堆叠的更深,以此来让它有机会捕捉“长距离依赖”。
换言之,卷积网络主要依靠深度来捕捉长距离依赖。
但这个过程太间接了,因为信息在网络中实际传播了太多层。究竟哪些信息被保留,哪些被丢弃了,弄不清楚。
从实践经验来看,卷积网络捕捉长依赖的能力非常弱。这也是为什么在大多数需要长依赖关系建模的场景中,CNN用的并不多的原因。
1.2 RNN捕捉长依赖的方式
从信息流的角度看,RNN要比CNN容易理解。假设有一段文本:I will go to play,一共包含5个单词。为了将这句话输入给神经网络,需要将其中的每一个单词转变为一个向量,这个过程称为词嵌入(Word Embedding)。之前的CNN其实也需要这一步,但为了简化,图中省略了。不熟悉的朋友可以把词嵌入的过程简单理解为查表。例如有一张表E,E(I)就表示用单词I去查表得到的单词I所对应的向量。
用下图来表示RNN对上面这句示例的计算过程:
图5 RNN计算过程示意图
从左面开始看上图。首先处理文本的第一个输入单词“I”。首先通过查表得到单词“I”的向量表示E(I)。然后将E(I)和一个初始的 ℎ0输入给模型(蓝色框)。 ℎ一般被称为“隐状态(hidden state)”, ℎ0因为是第一个隐状态,所以它的值通常要么全为0,要么赋为随机值。
模型在第一步得到输入ℎ0 和E(I),并通过函数� 来计算输出ℎ1=�(ℎ0,�(�))。
� 的具体形式因模型而异,这里不展开介绍。我们只需要明白它的输入和输出分别是什么即可。
ℎ1 又和下一个词“will”的向量E(will)一起输入进模型,同样通过函数� 得到输出 ℎ2=�(ℎ1,�(����)),然后重复这个过程,直到处理完最后一个词“play”,并得到最终的输出 ℎ5。
容易看出,在这个过程中,因为后一个词的计算需要用到前一个词的输出结果,所以理论上任何两个词的依赖RNN都能捕捉到。
以信息流的方法来看,图中绿色箭头表明信息的流动方向。容易看到,不论是相隔多远的词,它们的信息一定会相聚在某一步计算中。
上述是理论上的情况。实际中,因为RNN训练过程中容易出现梯度消失或梯度爆炸,所以它实际上很难把长依赖捕捉的比较好。有一些研究表明,不论是何种改进的RNN(如LSTM、GRU),它们一般捕捉长依赖的极限也就20个词的左右能力。
2. Transformer中的长依赖
CNN之所以有长依赖问题,是因为单个卷积的长度一般较小,它一次看不了那么长的文字段,属于网路结构固有的问题。
RNN的长依赖问题,主要来自于当前对模型优化方法的问题。它未必是RNN结构本身的问题,但目前的优化方法解决不了。
Transformer既没有结构上的问题,也没有优化上的问题。
还是用"I will go to play"的例子来看Transformer计算时的信息流。
图6 Transformer计算时的信息流
不用看Transformer的公式,单看它的计算逻辑就能发现,它在计算任意一个词的新表征(特征)时,同时用到了其它所有词的信息。
例如,图6中,为了计算单词I的新特征L(I),在自注意计算过程中,实际上L(I)的结果由所有词共同计算得到。
只需要从这一点来看,Transformer就没有长依赖的问题。
题主提到的数学期望其实是一个“加权和”。它是在计算得到所有attention score后,以加权和的形式来计算某一个词的新表征。
例如,对于单词I,它的新表征L(I)就是其它所有单词Value的加权和。所以仅仅只看这一点,I的新表征中就能用到其它所有单词的信息。因此,没有距离的概念,也就没有长依赖的问题。
以现在的视角来看,这当然很简单。但在当年,Transformer轰动了整个学术界,直接导致NLP领域全面由CNN、RNN为主导的情况转向了Transformer。
最后,这只是Transformer能够work很好的一个原因。但不论如何,它一定是重要原因,甚至可能是最重要的原因之一。