【大模型】 01.大模型核心原理

参考:

00 预训练语言模型的前世今生(全文 24854 个词) - B站-水论文的程序猿 - 博客园

AI-Guide-and-Demos-zh_CN/PaperNotes/Transformer 论文精读.md at master · Hoper-J/AI-Guide-and-Demos-zh_CN · GitHub

-------------------------------

1 预训练

卷积神经网络(CNN),CNN 一般用于图片分类任务,并且CNN 由多个层级结构组成,不同层学到的图像特征也不同,越浅的层学到的特征越通用,越深的层学到的特征和具体任务的关联性越强(人脸-人脸轮廓、汽车-汽车轮廓),如下图所示:

在小数据集上,如果我们亲手设计一个深度神经网络基本是不可能的,因为深度学习一个弱项就是在训练阶段对于数据量的需求特别大。

虽然我们的数据量很少,但是我们是否可以利用网上现有的大量已做好分类标注的图片。比如 ImageNet 中有 1400 万张图片,并且这些图片都已经做好了分类标注。

上述利用网络上现有图片的思想就是预训练的思想,具体做法就是:

  1. 通过 ImageNet 数据集我们训练出一个模型 A
  2. 由于上面提到 CNN 的浅层学到的特征通用性特别强,我们可以对模型 A 做出一部分改进得到模型 B(两种方法):
    1. 冻结:浅层参数使用模型 A 的参数,高层参数随机初始化浅层参数一直不变,然后利用领导给出的 30 张图片训练参数
    2. 微调:浅层参数使用模型 A 的参数,高层参数随机初始化,然后利用领导给出的 30 张图片训练参数,但是在这里浅层参数会随着任务的训练不断发生变化

2 语言模型

语言模型通俗点讲就是计算一个句子的概率。

下面通过两个实例具体了解上述所描述的意思:

  1. 假设给定两句话 “判断这个词的磁性” 和 “判断这个词的词性”,语言模型会认为后者更自然。转化成数学语言也就是:P(判断,这个,词,的,词性)>P(判断,这个,词,的,磁性)
  2. 假设给定一句话做填空 “判断这个词的____”,则问题就变成了给定前面的词,找出后面的一个词是什么,转化成数学语言就是:P(词性|判断,这个,词,的)>P(磁性|判断,这个,词,的)

通过上述两个实例,可以给出语言模型更加具体的描述:给定一句由 n 个词组成的句子 W=w1,w2,⋯,wn,计算这个句子的概率 P(w1,w2,⋯,wn),或者计算根据上文计算下一个词的概率 P(wn|w1,w2,⋯,wn−1)。

下面将介绍语言模型的两个分支,统计语言模型和神经网络语言模型。

2.1 统计语言模型

统计语言模型的基本思想就是计算条件概率

给定一句由 n 个词组成的句子 W=w1,w2,⋯,wn,计算这个句子的概率 P(w1,w2,⋯,wn) 的公式如下(条件概率乘法公式的推广,链式法则):

对于上一节提到的另外一个问题,当给定前面词的序列 “判断,这个,词,的” 时,想要知道下一个词是什么,可以直接计算如下概率:

其中,wnext∈V 表示词序列的下一个词,V 是一个具有 |V| 个词的词典(词集合)。

可以把字典 V 中的每个单词,逐一作为 wnext,带入计算,最后取最大概率的词作为 wnext 的候选词。

如果 |V| 特别大,公式的计算将会非常困难,但是我们可以引入马尔科夫链的思想:假设 wnext 只和它之前的 k 个词有相关性

其中二元语言模型的公式为:

假设有一个文本集合:

“词性是动词”
“判断单词的词性”
“磁性很强的磁铁”
“北京的词性是名词”

对于上述文本,如果要计算 P(词性|的) 的概率:

上述文本集合是我们自定制的,然而对于绝大多数具有现实意义的文本,会出现数据稀疏的情况,例如训练时未出现,测试时出现了的未登录单词

由于数据稀疏问题,则会出现概率值为 0 的情况,为了避免 0 值的出现,会使用一种平滑的策略——分子和分母都加入一个非 0 正数,例如可以把公式(4)改为:

2.2 神经网络语言模型

神经网络语言模型则引入神经网络架构来估计单词的分布,并且通过词向量的距离衡量单词之间的相似度

上图为神经网络语言模型结构图,它的学习任务是输入某个句中单词 wt=bert 前的 t−1 个单词,要求网络正确预测单词 “bert”,即最大化:

上图所示的神经网络语言模型分为三层,接下来我们详细讲解这三层的作用:

  1. 神经网络语言模型的第一层,为输入层。首先将前 t−1 个单词用 Onehot 编码(例如:0001000)作为原始单词输入,之后乘以一个随机初始化的矩阵 Q 后获得 t-1 个词向量 C(wi),对这 t−1 个词向量处理后得到输入 x,记作 x=(C(w1),C(w2),⋯,C(wt−1))。
    (输入 * Q = (C(w1),C(w2),⋯,C(wt−1)))
  2. 神经网络语言模型的第二层,为隐层,H 代表权重矩阵,因此隐层的输出为 Hx+d,其中 d 为偏置项。并且在此之后使用 tanh 作为激活函数。
  3. 神经网络语言模型的第三层,为输出层,一共有 |V| 个输出节点(字典大小),直观上讲,每个输出节点 yi 是词典中每一个单词概率值。最终得到的计算公式为:y=softmax(Wx+Utanh(d+Hx)+b),其中 W 是直接从输入层到输出层的权重矩阵,U 是隐层到输出层的参数矩阵。(+Wx为残差链接)

3 词向量

3.1 独热(Onehot)编码

把单词用向量表示,是把深度神经网络语言模型引入自然语言处理领域的一个核心技术。

在自然语言处理任务中,训练集大多为一个字或者一个词,把他们转化为计算机适合处理的数值类数据非常重要。

早期,人们想到的方法是使用独热(Onehot)编码,如下图所示:

但是,对于独热表示的向量,如果采用余弦相似度计算向量间的相似度,可以明显的发现任意两者向量的相似度结果都为 0,即任意二者都不相关,也就是说独热表示无法解决词之间的相似性问题。

3.2 词向量(Word Embedding)

由于独热表示无法解决词之间相似性问题,这种表示很快就被词向量表示给替代了,这个时候聪明的你可能想到了在神经网络语言模型中出现的一个词向量 C(wi),对的,这个 C(wi) 其实就是单词对应的 Word Embedding 值,也就是我们这节的核心——词向量。

在神经网络语言模型中,我们并没有详细解释词向量是如何计算的,现在让我们重看神经网络语言模型的架构图:

上图所示有一个 V×m 的矩阵 Q,这个矩阵 Q 包含 V 行,V 代表词典大小。
(输入 * Q = (C(w1),C(w2),⋯,C(wt−1)))

只不过 Q 的内容也是网络参数,需要学习获得,训练刚开始用随机值初始化矩阵 Q,当这个网络训练好之后,矩阵 Q 的内容被赋值。

但是这个词向量有没有解决词之间的相似度问题呢?为了回答这个问题,我们可以看看词向量的计算过程:

通过上述词向量的计算,可以发现第 4 个词的词向量表示为 [10 12 19]。

如果再次采用余弦相似度计算两个词之间的相似度,结果不再是 0 ,可以一定程度上描述两个词之间的相似度。

下图给了网上找的几个例子,可以看出有些例子效果还是很不错的,一个单词表达成 Word Embedding 后,很容易找出语义相近的其它词汇。

4 Word2Vec模型

Word2Vec是怎么工作的呢?看下图:

Word2Vec 有两种训练方法:

  1. 第一种叫 CBOW,核心思想是从一个句子里面把一个词抠掉,用这个词的上文和下文去预测被抠掉的这个词;
  2. 第二种叫做 Skip-gram,和 CBOW 正好反过来,输入某个单词,要求网络预测它的上下文单词。

而你回头看看,NNLM 是怎么训练的?是输入一个单词的上文,去预测这个单词。

NNLM 的主要任务是要学习一个解决语言模型任务的网络结构,语言模型就是要看到上文预测下文,而 Word Embedding只是 NNLM 无心插柳的一个副产品;但是 Word2Vec 目标不一样,它单纯就是要 Word Embedding 的,这是主产品。

5 自然语言处理的预训练模型

Word Embedding 这种做法能算是预训练吗?这其实就是标准的预训练过程。要理解这一点要看看学会 Word Embedding 后下游任务是怎么使用它的。

假设如上图所示,我们有个NLP的下游任务,比如 QA,就是问答问题,所谓问答问题,指的是给定一个问题 X,给定另外一个句子 Y,要判断句子 Y 是否是问题 X 的正确答案。

问答问题假设设计的网络结构如上图所示。

Word Embedding的使用方法其实和前面讲的 NNLM 是一样的,句子中每个单词以 Onehot 形式作为输入,然后乘上学好的矩阵 Q,就直接取出单词对应的 Word Embedding 了。

预训练:Word Embedding矩阵Q其实就是网络 Onehot 层到 embedding 层映射的网络参数矩阵。

使用 Word Embedding 等价于什么?等价于把 Onehot 层到 embedding 层的网络用预训练好的参数矩阵 Q 初始化了。

下游NLP任务在使用 Word Embedding 的时候也类似图像有两种做法,一种是 Frozen,就是 Word Embedding 那层网络参数固定不动;另外一种是 Fine-Tuning,就是 Word Embedding 这层参数使用新的训练集合训练也需要跟着训练过程更新掉。

上面这种做法就是18年之前NLP领域里面采用预训练的典型做法,并且 Word Embedding 其实对于很多下游 NLP 任务是有帮助的,只是帮助没有大到闪瞎忘记戴墨镜的围观群众的双眼而已。

6 RNN 和 LSTM

为什么要在这里穿插一个 RNN(Recurrent Neural Network) 和 LSTM(Long Short-Term Memory) 呢?

因为接下来要介绍的 ELMo(Embeddings from Language Models) 模型在训练过程中使用了双向长短期记忆网络(Bi-LSTM)。

6.1 RNN

传统的神经网络无法获取时序信息,然而时序信息在自然语言处理任务中非常重要

例如对于这一句话 “我吃了一个苹果”,“苹果” 的词性和意思,在这里取决于前面词的信息,如果没有 “我吃了一个” 这些词,“苹果” 也可以翻译为乔布斯搞出来的那个被咬了一口的苹果。

RNN 的出现,让处理时序信息变为可能。

RNN 的基本单元结构如下图所示:

上图左边部分称作 RNN 的一个 timestep,在这个 timestep 中可以看到,在 t 时刻,输入变量 xt,通过 RNN 的一个基础模块 A,输出变量 ht,而 t 时刻的信息,将会传递到下一个时刻 t+1。

RNN 解决了时序依赖问题,但这里的时序一般指的是短距离的,首先我们先介绍下短距离依赖和长距离依赖的区别:

  • 短距离依赖:对于这个填空题 “我想看一场篮球____”,我们很容易就判断出 “篮球” 后面跟的是 “比赛”,这种短距离依赖问题非常适合 RNN。
  • 长距离依赖:对于这个填空题 “我出生在中国的瓷都景德镇,小学和中学离家都很近,……,我的母语是____”,对于短距离依赖,“我的母语是” 后面可以紧跟着 “汉语”、“英语”、“法语”,但是如果我们想精确答案,则必须回到上文中很长距离之前的表述 “我出生在中国的瓷都景德镇”,进而判断答案为 “汉语”,而 RNN 是很难学习到这些信息的。

上图为RNN模型结构,前向传播过程包括:

RNN 所有的 timestep 共享一套参数 U,V,W,在 RNN 反向传播过程中,需要计算 U,V,W 等参数的梯度,以 W 的梯度表达式为例(假设 RNN 模型的损失函数为 L):

6.2 LSTM

为了解决 RNN 缺乏的序列长距离依赖问题,LSTM 被提了出来,首先我们来看看 LSTM 相对于 RNN 做了哪些改进:

如上图所示,为 LSTM 的 RNN 门控结构(LSTM 的 timestep),LSTM 前向传播过程包括:

6.2.1 LSTM 解决 RNN 的梯度消失问题

STM 遗忘门值 ft 可以选择在 [0,1] 之间,让 LSTM 来改善梯度消失的情况。也可以选择接近 1,让遗忘门饱和,此时远距离信息梯度不消失;也可以选择接近 0,此时模型是故意阻断梯度流,遗忘之前信息。

另外需要强调的是LSTM搞的这么复杂,除了在结构上天然地克服了梯度消失的问题,更重要的是具有更多的参数来控制模型;通过四倍于RNN的参数量,可以更加精细地预测时间序列变量。

7 ELMo 模型

7.1 ELMo 的预训练

在讲解 Word Embedding 时,细心的读者一定已经发现,这些词表示方法本质上是静态的,每一个词都有一个唯一确定的词向量,不能根据句子的不同而改变,无法处理自然语言处理任务中的多义词问题。

如上图所示,例如多义词 Bank,有两个常用含义,但是 Word Embedding 在对 bank 这个单词进行编码的时候,是区分不开这两个含义的。

因为尽管这两句含有 bank 的句子中 bank 上下文环境中出现的单词不同,但是在用语言模型训练的时候,不论什么上下文的句子经过 Word2Vec,都是预测相同的单词 bank,而同一个单词占用的是同一行的参数空间,这会导致两种不同的上下文信息都会编码到相同的 Word Embedding 空间里,进而导致Word Embedding 无法区分多义词的不同语义。

针对 Word Embedding 中出现的多义词问题,ELMo 提供了一个简洁优雅的解决方案:根据当前上下文对 Word Embedding 动态调整。

ELMo 采用了典型的两阶段过程:

  1. 第一个阶段是利用语言模型进行预训练;
  2. 第二个阶段是在做下游任务时,从预训练网络中提取对应单词的网络各层的 Word Embedding 作为新特征补充到下游任务中。

上图展示的是其第一阶段预训练过程,它的网络结构采用了双层双向 LSTM,目前语言模型训练的任务目标是根据单词 w_i 的上下文去正确预测单词 w_i,w_i 之前的单词序列 Context-before 称为上文,之后的单词序列 Context-after 称为下文。

图中左端的前向双层 LSTM 代表正方向编码器;右端的逆向双层 LSTM 代表反方向编码器;每个编码器的深度都是两层 LSTM 叠加。

这个网络结构其实在 NLP 中是很常用的。如果预训练好这个网络后,输入一个新句子 s_new ,句子中每个单词都能得到对应的三个 Embedding:

  • 最底层是单词的 Word Embedding;
  • 往上走是第一层双向 LSTM 中对应单词位置的 Embedding,这层编码单词的句法信息更多一些;
  • 再往上走是第二层 LSTM 中对应单词位置的 Embedding,这层编码单词的语义信息更多一些。

7.2 ELMo 的 Feature-based Pre-Training

上面介绍的是 ELMo 的第一阶段:预训练阶段。那么预训练好网络结构后,如何给下游任务使用呢?

上图展示了下游任务的使用过程,比如我们的下游任务仍然是 QA 问题,此时对于问句 X:

  1. 我们可以先将句子 X 作为预训练好的 ELMo 网络的输入,这样句子 X 中每个单词在 ELMo 网络中都能获得对应的三个 Embedding;
  2. 之后给予这三个 Embedding 中的每一个 Embedding 一个权重 a,这个权重可以学习得来,根据各自权重累加求和,将三个 Embedding 整合成一个;
  3. 然后将整合后的这个 Embedding 作为 X 句在自己任务的那个网络结构中对应单词的输入,以此作为补充的新特征给下游任务使用。
  4. 对于上图所示下游任务 QA 中的回答句子 Y 来说也是如此处理。

因为 ELMo 给下游提供的是每个单词的特征形式,所以这一类预训练的方法被称为 “Feature-based Pre-Training”。

8 Attention

8.1 人类的视觉注意力

视觉注意力机制是人类视觉所特有的大脑信号处理机制。人类视觉通过快速扫描全局图像,获得需要重点关注的目标区域,也就是一般所说的注意力焦点,而后对这一区域投入更多注意力资源,以获取更多所需要关注目标的细节信息,而抑制其他无用信息。

这是人类利用有限的注意力资源从大量信息中快速筛选出高价值信息的手段,是人类在长期进化中形成的一种生存机制,人类视觉注意力机制极大地提高了视觉信息处理的效率与准确性。

上图形象化展示了人类在看到一副图像时是如何高效分配有限的注意力资源的,其中红色区域表明视觉系统更关注的目标,很明显对于上图所示的场景,人们会把注意力更多投入到人的脸部,文本的标题以及文章首句等位置。

深度学习中的注意力机制从本质上讲和人类的选择性视觉注意力机制类似,核心目标也是从众多信息中选择出对当前任务目标更关键的信息。

8.2 Attention 的本质思想

从人类的视觉注意力可以看出,注意力模型 Attention 的本质思想为:从大量信息中有选择地筛选出少量重要信息并聚焦到这些重要信息上,忽略不重要的信息。

之前我们讲解 LSTM 的时候说到,虽然 LSTM 解决了序列长距离依赖问题,但是单词超过 200 的时候就会失效。而 Attention 机制可以更加好的解决序列长距离依赖问题,并且具有并行计算能力

首先我们得明确一个点,注意力模型从大量信息 Values 中筛选出少量重要信息,这些重要信息一定是相对于另外一个信息 Query 而言是重要的。我们必须得要有一个 Query 和一个 Values,然后通过 Query 这个信息从 Values 中筛选出重要信息,简单点说,就是计算 Query 和 Values 中每个信息的相关程度。

再具体点,通过上图,Attention 通常可以进行如下描述,其中 query、每个 key、每个 value 都是向量,输出是 V 中所有 values 的加权,其中权重是由 Query 和每个 key 计算出来的,计算方法分为三步:

8.3 Self Attention 模型

Self Attention 模型的架构如下图所示,接下来我们将按照这个模型架构的顺序来逐一解释。

首先可以看到 Self Attention 有三个输入 Q、K、V:对于 Self Attention,Q、K、V 来自句子 X 的 词向量 x 的线性转化,即对于词向量 x,给定三个可学习的矩阵参数 W_Q,W_K,W_V,x 分别右乘上述矩阵得到 Q、K、V

接下来为了表示的方便,我们先通过向量的计算叙述 Self Attention 计算的流程,然后再描述 Self Attention 的矩阵计算过程.

第一步,Q、K、V 的获取

上图操作:两个单词 Thinking 和 Machines。通过线性变换,即 x1 和 x2 两个向量分别与Wq,Wk,Wv 三个矩阵点乘得到 q1,q2,k1,k2,v1,v2 共 6 个向量。矩阵 Q 则是向量 q1,q2 的拼接,K、V 同理。

第二步,MatMul

上图操作:向量 q1,k1 做点乘得到得分 112, q1,k2 做点乘得到得分96。注意:这里是通过 q1 这个信息找到 x1,x2 中的重要信息。

第三步和第四步,Scale + Softmax

上图操作:对该得分进行规范,除以 √dk=8

第五步,MatMul

用得分比例 [0.88,0.12] 乘以 [v1,v2] 值得到一个加权后的值,将这些值加起来得到 z1。

上述所说就是 Self Attention 模型所做的事,仔细感受一下,用 q1、K=[k1,k2] 去计算一个 Thinking 相对于 Thinking 和 Machine 的权重,再用权重乘以 Thinking 和 Machine 的 V=[v1,v2] 得到加权后的 Thinking 和 Machine 的 V=[v1,v2],最后求和得到针对各单词的输出 z1。

同理可以计算出 Machine 相对于 Thinking 和 Machine 的加权输出 z2,拼接 z1 和 z2 即可得到 Attention 值 Z=[z1,z2],这就是 Self Attention 的矩阵计算,如下所示。

之前的例子是单个向量的运算例子。这张图展示的是矩阵运算的例子,输入是一个 [2x4] 的矩阵(句子中每个单词的词向量的拼接),乘以 [4x3] 的矩阵,求得 [2*3] 的 Q、K、V,其中每行代表每个单词。

Q 对 K 转制做点乘,除以 √dk,做一个 softmax 得到合为 1 的比例,对 V 做点乘得到输出 Z。那么这个 Z 就是一个考虑过 Thinking 周围单词 Machine 的输出。

注意看这个公式,其实就会组成一个 word2word 的 attention map!(加了 softmax 之后就是一个合为 1 的权重了)。比如说你的输入是一句话 "i have a dream" 总共 4 个单词,这里就会形成一张 4x4 的注意力机制的图:

这样一来,每一个单词对应每一个单词都会有一个权重,这也是 Self Attention 名字的来源,即 Attention 的计算来源于 Source(源句) 和 Source 本身,通俗点讲就是 Q、K、V 都来源于输入 X 本身。

8.4 Self Attention 和 RNN、LSTM 的区别

引入 Self Attention 有什么好处呢?或者说通过 Self Attention 到底学到了哪些规律或者抽取出了哪些特征呢?我们可以通过下述两幅图来讲解:

从上述两张图可以看出,Self Attention 可以捕获同一个句子中单词之间的一些句法特征(例如第一张图展示的有一定距离的短语结构)或者语义特征(例如第二张图展示的 its 的指代对象为 Law)。

有了上述的讲解,我们现在可以来看看 Self Attention 和 RNN、LSTM 的区别:

  • RNN、LSTM:如果是 RNN 或者 LSTM,需要依次序列计算,对于远距离的相互依赖的特征,要经过若干时间步步骤的信息累积才能将两者联系起来,而距离越远,有效捕获的可能性越小
  • Self Attention:
    • 通过上述两幅图,很明显的可以看出,引入 Self Attention 后会更容易捕获句子中长距离的相互依赖的特征,因为 Self Attention 在计算过程中会直接将句子中任意两个单词的联系通过一个计算步骤直接联系起来,所以远距离依赖特征之间的距离被极大缩短,有利于有效地利用这些特征
    • 除此之外,Self Attention 对于一句话中的每个单词都可以单独的进行 Attention 值的计算,也就是说 Self Attention 对计算的并行性也有直接帮助作用,而对于必须得依次序列计算的 RNN 而言,是无法做到并行计算的。

从上面的计算步骤和图片可以看出,无论句子序列多长,都可以充分捕获近距离上往下问中的任何依赖关系,进而可以很好的提取句法特征还可以提取语义特征;而且对于一个句子而言,每个单词的计算是可以并行处理的

理论上 Self-Attention (Transformer 50 个左右的单词效果最好)解决了 RNN 模型的长序列依赖问题,但是由于文本长度增加时,训练时间也将会呈指数增长,因此在处理长文本任务时可能不一定比 LSTM(200 个左右的单词效果最好) 等传统的 RNN 模型的效果好。

8.5 Masked Self Attention 模型

这里的 Masked 就是要在做语言模型(或者像翻译)的时候,不给模型看到未来的信息,它的结构如下图所示:

上图中和 Self Attention 重复的部分此处就不讲了,主要讲讲 Mask 这一块。

假设在此之前我们已经通过 scale 之前的步骤得到了一个 attention map,而 mask 就是沿着对角线把灰色的区域用0覆盖掉,不给模型看到未来的信息,如下图所示:

并且在做完 softmax 之后,横轴结果合为 1。如下图所示:

8.6 Multi-head Self Attention 模型

由于 Transformer 使用的都是 Self Attention 的进阶版 Multi-head Self Attention,我们简单讲讲 Multi-head Self Attention 的架构,并且在该小节结尾处讲讲它的优点。

为了使得输出与输入结构相同,拼接矩阵 Zi 后乘以一个线性 W0 得到最终的Z:

可以通过下图看看 multi-head attention 的整个流程:

上述操作有什么好处呢?多头相当于把原始信息 Source 放入了多个子空间中,也就是捕捉了多个信息,对于使用 multi-head(多头) attention 的简单回答就是,多头保证了 attention 可以注意到不同子空间的信息,捕捉到更加丰富的特征信息。其实本质上是论文原作者发现这样效果确实好。

9 Position Embedding

在 Attention 和 RNN、LSTM 的对比中,我们说到 Attention 解决了长距离依赖问题,并且可以支持并行化,但是它就真的百利而无一害了吗?

其实不然,我们往前回顾,Self Attention 的 Q、K、V 三个矩阵是由同一个输入 X1=(x1,x2,⋯,xn) 线性转换而来,也就是说对于这样的一个被打乱序列顺序的 X2=(x2,x1,⋯,xn) 而言,由于 Attention 值的计算最终会被加权求和,也就是说两者最终计算的 Attention 值都是一样的,进而也就表明了 Attention 丢掉了 X1 的序列顺序信息。

如上图所示,为了解决 Attention 丢失的序列顺序信息,Transformer 的提出者提出了 Position Embedding,也就是对于输入 X 进行 Attention 计算之前,在 X 的词向量中加上位置信息。

但是如何得到 X 的位置向量呢?

其中位置编码公式如下图所示:

其中 pos 表示位置、i 表示维度、d_model表示位置向量的向量维度 、2i、2i+1 表示的是奇偶数(奇偶维度),上图所示就是偶数位置使用 sin 函数,奇数位置使用 cos 函数。

有了位置编码,我们再来看看位置编码是如何嵌入单词编码的(其中 512 表示编码维度),通过把单词的词向量和位置向量进行叠加,这种方式就称作位置嵌入,如下图所示:

Position Embedding 本身是一个绝对位置的信息,但在语言模型中,相对位置也很重要。那么为什么位置嵌入机制有用呢?

我们不要去关心三角函数公式,可以看看下图公式(3)中的第一行,我们做如下的解释。

总而言之就是,某个单词的位置信息是其他单词位置信息的线性组合,这种线性组合就意味着位置向量中蕴含了相对位置信息。

10 Transformer

Transformer 已成为语言模型领域的奠基之作,大幅推动了自然语言处理(NLP,Natural Language Processing)的发展。它最初是为机器翻译任务提出的,因此背景介绍离不开 NLP,在 Transformer 出现之前,NLP 领域的 SOTA(State-of-the-Art)模型以循环神经网络(RNN)架构为主导,包含 LSTM 和 GRU 等变体,但 RNN 存在两个主要缺点:

  • 按时间步递进处理数据:输入必须按照序列顺序依次处理,导致并行计算能力受限,训练速度慢。
  • 长距离依赖问题:在处理长序列时,信息容易随时间步 t 的增加而被遗忘,尽管 LSTM 等变体在一定程度上减轻了此问题。

RNN 的递归原理

给定输入序列 X=(x1,x2,...,xt), X 可以理解为一个句子,RNN 的隐藏状态递归更新如下:

其中 f 是激活函数。

从公式可以看出,每个时间步的状态 ht 依赖于前一时间步 ht−1, 这使得 RNN 无法并行处理序列中的数据。

实际在这一阶段的工作中,注意力机制就已经在编码器-解码器架构中被广泛应用(与 RNN 一起使用),但 Transformer 彻底颠覆了默认采取的逻辑:直接放弃 RNN 的递归结构,只使用注意力机制来编码和解码序列信息。这相当于,大家原本都端着碗(RNN)拿着筷子(Attention)吃饭,而 Transformer 直接把“碗”扔掉,表示只用筷子也能直接吃,而且吃得又快又好,还不用装饭。展示了一种全新的思路:Attention Is All You Need

Transformer 的主要贡献如下:

  • 取消递归结构,实现并行计算

    通过采用自注意力机制(Self-Attention),Transformer 可以同时处理多个输入序列,极大提高了计算的并行度和训练速度。

  • 引入位置编码(Positional Encoding)并结合 Attention 机制巧妙地捕捉位置信息

    在不依赖 RNN 结构的情况下,通过位置编码为序列中的每个元素嵌入位置信息,从而使模型能够感知输入的顺序。

10.1 Transformer 的结构

万事俱备,只欠东风,下面我们来讲讲我们的重点之一,Transformer,你可以先记住这一句话:Transformer 简单点看其实就是 self-attention 模型的叠加,首先我们来看看 Transformer 的整体框架。

Transformer 的整体框架如下图所示:

上图所示的整体框架乍一眼一看非常复杂,由于 Transformer 起初是作为翻译模型,因此我们以翻译举例,简化一下上述的整体框架:

从上图可以看出 Transformer 相当于一个黑箱,左边输入 “Je suis etudiant”,右边会得到一个翻译结果 “I am a student”。

再往细里讲,Transformer 也是一个 Seq2Seq 模型(Encoder-Decoder 框架的模型),左边一个 Encoders 把输入读进去,右边一个 Decoders 得到输出,如下所示:

在这里,我们穿插描述下 Encoder-Decoder 框架的模型是如何进行文本翻译的:

  1. 将序列 (x1,x2,⋯,xn) 作为 Encoders 的输入,得到输出序列 (z1,z2,⋯,zn)
  2. 把 Encoders 的输出序列 (z1,z2,⋯,zn) 作为 Decoders 的输入,生成一个输出序列 (y1,y2,⋯,ym)。

第一眼看到上述的 Encodes-Decoders 框架图,随之产生问题就是 Transformer 中 左边 Encoders 的输出是怎么和右边 Decoders 结合的。N 表示编码器或解码器的层数,在原始 Transformer 论文中,均设为 N=6, 即编码器和解码器各由六层堆叠(Stack)而成。再画张图直观的看就是这样:

也就是说,Encoders 的输出,会和每一层的 Decoder 进行结合

现在我们取其中一层进行详细的展示:

通过上述分析,发现我们想要详细了解 Transformer,只要了解 Transformer 中的 Encoder 和 Decoder 单元即可,接下来我们将详细阐述这两个单元。

10.2 Encoder

输入:嵌入层(Embedding Layer)

输入序列(Inputs)的 Tokens 转换为固定维度的向量表示(Input embedding),使模型能够处理文本数据。

我们知道 Eecoders 是 N=6 层,通过上图我们可以看到每层 Encoder 包括两个 sub-layers:

  • 第一个 sub-layer 是 multi-head self-attention,用来计算输入的 self-attention;
    • 在标准的Transformer架构中,**编码器(encoder)的自注意力(self-attention)不是掩码的(masked)**,但有一个重要细节需要注意:

      没有因果掩码(No Causal Mask):
      - **编码器**的self-attention是**双向的(bidirectional)**,每个token可以关注序列中的所有位置(包括前后文)。
      - 这与解码器(decoder)不同:解码器的self-attention使用**因果掩码(causal mask)**,即每个token只能关注它之前的位置(防止信息泄露)。

      但可能有填充掩码(Padding Mask):
      尽管没有因果掩码,但编码器通常需要处理**变长序列**(通过填充符`<pad>`使长度一致)。因此,编码器会使用**填充掩码(padding mask)**来:
      - 防止注意力机制关注到填充位置(将这些位置的注意力权重置为0)。
      - 这是通过一个二进制掩码实现的,标记哪些位置是真实token(1),哪些是填充符(0)。

      为什么编码器不需要因果掩码?
      - 编码器的目标是**理解整个输入序列**(如机器翻译的源语言句子、文本分类的整个文本)。
      - 双向注意力允许每个token同时利用前后文信息,这对理解语义至关重要。
      - 例如,在BERT等预训练模型中,这种双向性是其核心优势。

      总结:
      - **编码器self-attention:没有因果掩码(双向),只有填充掩码(可选)**
      - **解码器self-attention:有因果掩码(单向) + 填充掩码**
      这种设计使得Transformer既能双向理解输入(编码器),又能自回归生成输出(解码器)。

  • 第二个 sub-layer 是简单的前馈神经网络层 Feed Forward;

注意:在每个 sub-layer 我们都模拟了残差网络(在下面的数据流示意图中会细讲),每个sub-layer的输出都是 LayerNorm(x+Sub_layer(x)),其中 sub_layer 表示的是该层的上一层的输出。

现在我们给出 Encoder 的数据流示意图,一步一步去剖析:

  1. 深绿色的 x1 表示 Embedding 层的输出,加上代表 Positional Embedding 的向量之后,得到最后输入 Encoder 中的特征向量,也就是浅绿色向量 x1;
  2. 浅绿色向量 x1 表示单词 “Thinking” 的特征向量,其中 x1 经过 Self-Attention 层,变成浅粉色向量 z1;
  3. x1 作为残差结构的直连向量,直接和 z1 相加,之后进行 Layer Norm 操作,得到粉色向量 z1;
    1. 残差结构的作用:避免出现梯度消失的情况
    2. Layer Norm 的作用:为了保证数据特征分布的稳定性,并且可以加速模型的收敛
  4. z1 经过前馈神经网络(Feed Forward)层,经过残差结构与自身相加,之后经过 LN 层,得到一个输出向量 r1;
    1. FFN 是一个简单的全连接神经网络,通常由**两层线性变换**和一个**非线性激活函数**组成,其结构如下:
      ```
      FFN(x) = Activation(x · W₁ + b₁) · W₂ + b₂
      ```
      其中:
      - **第一层**(扩展层):将输入维度 `d_model` 扩展到更大的维度 `d_ff`(例如 2048 或 4096)。
      - **激活函数**:如 ReLU、GELU 或 Swish,引入非线性。
      - **第二层**(收缩层):将维度从 `d_ff` 压缩回 `d_model`,以匹配后续层的输入维度。

      常见变体:
      - **原始 Transformer**(《Attention Is All You Need》):  
        `FFN(x) = ReLU(xW₁ + b₁)W₂ + b₂`
      - **BERT 与 GPT**:使用 GELU 激活函数。
      - **T5 模型**:采用 GLU(Gated Linear Unit)变体,如:  
        `FFN(x) = (GELU(xW₁) ⊗ xV) W₂`,其中 `⊗` 是逐元素相乘。

      设计意义:
      - 为每个 token 提供**独立的非线性转换**,增强模型的表达能力。
      - 与自注意力层配合:注意力层整合上下文信息,FFN 层对整合后的信息进行深度加工。

      总结:
      FFN 层是 Transformer 中负责**非线性特征转换**的核心模块,结构简单但至关重要。它通过“扩展 → 激活 → 压缩”的流程,增强了模型对每个位置信息的处理能力,与自注意力机制形成了功能上的互补。

  5. 由于 Transformer 的 Encoders 具有 6 个 Encoder,r1 也将会作为下一层 Encoder 的输入,代替 x1 的角色,如此循环,直至最后一层 Encoder。

需要注意的是,上述的 x、z、r 都具有相同的维数,论文中为 512 维。

10.3 Decoder

Decoders 也是 N=6 层,通过上图我们可以看到每层 Decoder 包括 3 个 sub-layers:

  • 第一个 sub-layer 是 Masked multi-head self-attention,也是计算输入的 self-attention;
  • 第二个 sub-layer 是 Encoder-Decoder Attention 计算,对 Encoder 的输入和 Decoder 的Masked multi-head self-attention 的输出进行 attention 计算;
  • 第三个 sub-layer 是前馈神经网络层,与 Encoder 相同。

10.4 Transformer 输出结果

以上,就讲完了 Transformer 编码和解码两大模块,那么我们回归最初的问题,将 “机器学习” 翻译成 “machine learing”,解码器的输出是一个浮点型的向量,怎么转化成 “machine learing” 这两个词呢?让我们来看看 Encoders 和 Decoders 交互的过程寻找答案:

从上图可以看出,Transformer 最后的工作是让解码器的输出通过线性层 Linear 后接上一个 softmax:

其中X为输入序列(x1,x2,...,xt), X 可以理解为一个句子。

  • 其中线性层(Linear Layer)是一个简单的全连接神经网络,它将解码器产生的向量 A 投影到一个更高维度的向量 B 上,假设我们模型的词汇表是10000个词,那么向量 B 就有10000个维度,每个维度对应一个唯一的词的得分。
    • self.proj = nn.Linear(d_model, vocab)
    • 你可以把解码器的输出向量 A 想象成模型当前步骤的 “抽象思想” 或 “上下文理解”

    • 它是什么:一个固定长度的向量(比如512维或1024维),这个向量浓缩了到目前为止整个输入序列(来自编码器)和已经生成的所有输出词(来自解码器自身)的全部信息。

    • 它不是什么:它不是直接指向“苹果”、“run”、“the”这些具体词的信号。它更像是一种高度抽象的、机器能理解的“意义状态”。

    • 线性层 + Softmax 的工作,就是担任 “词典翻译官” 的角色。它的任务是把这种抽象的“思想”翻译成具体的、人类语言中的“单词”。

  • 之后的softmax层将这些分数转换为在目标词汇表上的概率分布
  • 生成目标词:
    • 从 Softmax 输出的概率分布中选择当前时间步的词,常用的生成策略包括:

    • 贪心搜索(Greedy Search):每次选择概率最高的词。

    • 束搜索(Beam Search):保留多个可能的候选路径,以优化最终结果(Transformer 使用的就是 beam search,参数设置:波束大小 beam size=4, 长度惩罚 length penalty α=0.6)。

    • Top-K 和 Top-P 采样:从概率分布中随机采样,以增加生成的多样性。

假设词汇表维度是 6,那么输出最大概率词汇的过程如下:

11 Transformer 流程

假设上图是训练模型的某一个阶段,我们来结合 Transformer 的完整框架描述下这个动态的流程图:

  1. 输入 “je suis etudiant” 到 Encoders,然后得到一个 Ke、Ve 矩阵;
  2. 输入 “I am a student” 到 Decoders ,首先通过 Masked Multi-head Attention 层得到 “I am a student” 的 attention 值 Qd,然后用 attention 值 Qd 和 Encoders 的输出 Ke、Ve 矩阵进行 attention 计算,得到第 1 个输出 “I”;
  3. 输入 “I am a student” 到 Decoders ,首先通过 Masked Multi-head Attention 层得到 “I am a student” 的 attention 值 Qd(masked与上一步不一样),然后用 attention 值 Qd 和 Encoders 的输出 Ke、Ve 矩阵进行 attention 计算,得到第 2 个输出 “am”;
  4. ……

12 GPT 模型

在讲解 ELMo 的时候,我们说到 ELMo 这一类预训练的方法被称为 “Feature-based Pre-Training”。

除了以 ELMo 为代表的这种基于特征融合的预训练方法外,NLP 里还有一种典型做法,一般将这种方法称为 “基于Fine-tuning的模式”,而 GPT 就是这一模式的典型开创者。

简单来说,两者的核心区别在于:预训练好的模型,在下游任务中是被当作一个“静态特征提取器”来使用,还是作为一个“可整体调整的起点”来使用。它揭示了NLP预训练范式从“词向量”时代到“大模型”时代的一次关键转变。

1. Feature-based Pre-Training (以 ELMo 为例)
这种方法把预训练模型看作一个强大的 **“特征生产车间”**。

*   **上游预训练**:在大语料上训练一个双层双向 LSTM,目标是学习词语在上下文中的表示。
*   **下游应用**:
    1.  **冻结车间**:这个训练好的 ELMo 网络参数被固定,不再改变。
    2.  **生产特征**:对于下游任务(如情感分类)的每一个句子,将它输入这个冻结的 ELMo 网络,得到句子中每个词的上下文向量表示。
    3.  **新建模型**:下游任务的研究者需要**自己从头搭建一个任务专用的模型**(例如,一个 BiLSTM 接一个分类层)。
    4.  **使用特征**:将 ELMo 产生的词向量,**拼接或求和**到任务模型原有的词嵌入输入中,作为增强的特征。然后**只训练这个新建的任务模型**。

**关键**:预训练的“知识”被凝固在了产出的静态特征向量里,下游模型只能“使用”这些特征,不能“改造”生产特征的车间。

2. Fine-tuning-based Pre-Training (以 GPT 为例)
这种方法把预训练模型看作一个 **“万事通的基础模型”**,只需稍加教导(微调)就能胜任具体工作。

*   **上游预训练**:在大语料上训练一个 Transformer 解码器(单向),目标是通用的语言建模(预测下一个词)。
*   **下游应用**:
    1.  **接管模型**:直接将预训练好的整个 GPT 模型拿过来,作为下游任务的**初始化模型**。
    2.  **简单改造**:根据任务(如文本分类),在 GPT 的序列输出后面**添加一个最简单的线性分类层**。
    3.  **整体微调**:在下游任务的小规模数据上,**对整个“GPT主干 + 新分类层”进行端到端的训练**。这意味着预训练模型中几乎所有的参数(从底部的词嵌入到高层的 Transformer 块)都会根据新任务的数据进行小幅调整。

**关键**:预训练的“知识”存储在整个模型的动态参数中,下游任务通过微调让这些参数“自适应”到新任务上,模型结构本身是统一的。

下面先让我们看看 GPT 的网络结构。

GPT 是 “Generative Pre-Training” 的简称,从名字看其含义是指的生成式的预训练。

GPT也采用两阶段过程:

  1. 第一个阶段:利用语言模型进行预训练;
  2. 第二个阶段:通过 Fine-tuning 的模式解决下游任务。

上面讲的是 GPT 如何进行第一阶段的预训练,那么假设预训练好了网络模型,后面下游任务怎么用?它有自己的个性,和 ELMO 的方式大有不同。

ps

ps1 残差网络是如何避免梯度消失的

链式法则:在反向传播时,L对参数w_l求导(即梯度),由链式法则可变为 L对x_(l+2)求导 * x_(l+2)对x_(l+1)求导 * x_(l+1)对x_(l)求导 * x_l对w_l求导, x_(l+1)对x_(l)求导等项通常是小于1的数(例如经过Sigmoid激活函数后,其导数最大为0.25),这些小数的连乘会使底层的梯度变得及其小,使w_l得不到有效更新。

残差快使最终的输出加上输入x:在前向传播中,y = F(x) + x,假设x = 5,F(x) = 0.5,y = 0.5 + 5 = 5.5,网络只需要学习一个小的残差(0.5)。

L对输入x的梯度

= L对y求导 * y对x求导

= L对y求导 * (F(x)对x的导数 + 1)

= L对y求导 * F(x)对x的导数 + L对y求导 * 1

这里的L对y求导 * 1是残差链接带来的梯度,保证来自上一层的梯度可以直接传递到x这来。

posted @ 2025-04-14 19:30  轻闲一号机  阅读(0)  评论(0)    收藏  举报  来源