word2vec
Embedding嵌入
NLP领域的相关任务中要将自然语言交给机器学习中的算法来处理,这通常需要将语言数学化。使用向量的表示形式是一个不错的选择,我们一般称将任何类型的数据转换为向量的过程为Embedding嵌入操作。
在前文介绍Tranformers等模型时,我们已经提及过Embedding嵌入的本质,即提供一个巨大的语料词元矩阵。该矩阵的每一列都对应某个词元的Embedding嵌入向量。
Embedding操作不同于独热编码(One-Hot Encoding),其采用稠密向量的形式压缩了空间复杂度。Embedding操作有多种实现形式,接下来我们主要关注NLP领域的word2vec模型。
word2vec
word2vec模型由Google团队在2013年提出,它将每个词映射到一个固定长度的向量,这些向量能更好地表达不同词之间的相似性和类比关系。word2Vec是轻量级的神经网络,其模型仅仅包括输入层、隐藏层和输出层,模型框架根据输入输出的不同word2vec包含两个模型,即跳元模型(Skip-Gram)和连续词袋(CBOW)。
word2vec模型中,隐藏层是存储Embedding嵌入矩阵的地方,其采用线性激活函数。当我们将一个单词的独热向量传入网络时,其中唯一的 1 会触发该单词对应的Embedding向量传递到下一层。因此word2vec模型本质上是在训练隐藏层参数。
CBOW
CBOW模型考虑了给定周围上下文词生成中心词条件概率,也就是根据上下文预测某个词元。由于连续词袋模型中存在多个上下文词,因此在计算条件概率时对这些上下文词向量进行平均。对于字典中索引为 i 的任意词,使用 vi,ui∈Rd 表示用作上下文词和中心词的两个向量并随机初始化。假设上下文词 wo1,⋯,wo2m 在词表中的索引为 o1,⋯,o2m,根据这 2m 个上下文词语生成中心词 wc 的条件概率表示为
P(wc∣wo1,⋯,wo2m)=i∈V∑exp(uiTvˉ)exp(ucTvˉ)
其中 V={0,1⋯,∣V∣−1} 为词表索引集,vˉ=2m1j=1∑2mvj 为上下文平均向量。上式具有和Softmax类似的形式,以保证概率归一化。分子中的 ucTvˉ 表示当前候选中心词的向量和上下文平均向量做点积,以衡量语义特征相似度。
一般的,对于长度为 T 的文本序列,设时间步为 t 的词表示为 w(t)。假设上下文窗口为 m,那么CBOW模型的似然函数是给定其上下文词的情况下生成所有中心词的概率乘积
t=1∏TP(w(t)∣w(t−m),⋯,w(t−1),w(t+1),⋯,w(t+m))
根据这个似然函数可以训练模型,其最大似然估计等价于最小化问题
min−t=1∑TlogP(w(t)∣w(t−m),⋯,w(t−1),w(t+1),⋯,w(t+m))
为表示简便,令 Wo={wo1,⋯,wo2m},则有
logP(wc∣Wo)=ucTvˉ−log(i∈V∑exp(uiTvˉ))
可以得到上下文词向量 vi(i=o1,⋯,o2m) 的梯度为
∂vi∂logP(wc∣Wo)=2m1uc−j∈V∑P(wj∣Wo)uj
对于某个中心词 wc,其中心词向量的梯度为
∂uc∂logP(wc∣Wo)=uc−vˉ
接着就可以使用梯度法进行优化了。
Skip-Gram
与CBOW模型相反,跳元模型假设一个词可以用来在文本序列生成其周围的词。对于字典中索引为 i 的任意词,使用 ui,vi∈Rd 表示用作上下文词和中心词的两个向量并随机初始化(注意:此符号记号与CBOW模型恰好相反)。根据某个给定中心词 wc 生成上下文词 wo 的条件概率可以表示为
P(wo∣wc)=i∈V∑exp(uiTvc)exp(uoTvc)
假设上下文词是在给定任何中心词的情况下独立生成的。对于上下文窗口 m,跳元模型的似然函数是在给定任何中心词的情况下生成所有上下文词的概率乘积
t=1∏T−m⩽j⩽m,j=0∏P(w(t+j)∣w(t))
将其写作最小化优化形式为
−t=1∑T−m⩽j⩽m,j=0∑logP(w(t+j)∣w(t))
不难得到中心词向量 vc 的梯度为
∂vi∂logP(wo∣wc)=uo−j∈V∑P(wj∣wc)uj
word2vec使用大规模的文本语料库训练,不需要人工标注,因此是一种自监督学习算法。word2vec的核心思想是利用词义的上下文,因此对于不同规模的问题有不同的架构。
- CBOW:适用于数据集较小的情况,通过 2m 个上下文词和当前中心词的匹配程度训练,擅长捕捉局部上下文信息
- Skip-Gram:由中心词预测上下文,能够更好地捕捉稀疏的语义关系,但计算量更大
在训练过程中,模型尝试把语义相似的词语聚集,从而达到合适的Embedding效果。
负采样训练
在某个规模较大的词典上训练word2vec模型是不可取的,因为这涉及到Softmax操作中的分母求和,带来了巨大的计算成本。为了降低复杂度,模型引入了一种负采样方法。
假设有一对真实的文本序列 (w,c),从字典里采样 K 个噪声词 w~1,⋯,w~K,定义最大化目标
maxL=logσ(ucTvw)+i=1∑Klogσ(−uw~iTvw)
其中 σ(⋅) 为sigmoid函数,上式可以被视为两项
- 前半部分为正样本:我们希望其点积尽可能大
- 后半部分为负样本:我们希望其点积尽可能小
计算这个函数的梯度是很容易的。总结而言,负采样方法用二元Logistic回归替代了Softmax计算。通过引入负样本项,将问题转化为了最大化正样本概率、最小化负样本概率的简单优化问题。
分层Softmax
另一种近似训练方法采用Huffman树的数据结构表示词表,可以通过数据结构知识证明最终的计算复杂度将被压缩为 O(log2∣V∣) 量级。更多详情在本篇日志里不再赘述。

GloVe和ELMo
word2vec中使用了交叉熵损失函数,这可能不是衡量两种概率分布差异的好选择,特别是对于大型语料库。在全局语料统计的跳元模型基础上,GloVe模型通过平方误差损失进行了优化,其数学推导不在此列出。
ELMo模型是一种基于双向LSTM的动态上下文词表示方法,它不同于word2vec生成固定的静态词向量,而是将word2vec作为底层的字符卷积输入,通过深层双向LSTM编码上下文信息,并能够根据任务需求对所有层的输出进行加权求和,从而为同一个词生成随语境变化的、包含丰富语法和语义信息的动态词向量。
BERT
迁移学习
BERT(Bidirectional Encoder Representation from Transformers)是2018年10月由Google AI研究院提出的一种预训练模型,该模型在机器阅读理解顶级水平测试SQuAD1.1中取得了 SOTA。得益于其出色的语言理解能力,谷歌也积极将BERT应用于搜索引擎业务,并且声称应用后成功将搜索效果提高了约10%。
SOTA全拼为 state-of-the-art,意味着在某个领域取得了全新的高度。
从模型本身来讲,其来自于Transformer架构中的编码器部分;从功能上来说,BERT是一个针对文本的特征抽取器,其使得NLP领域的迁移学习(Transfer Learning)成为可能。
在计算机视觉领域,CNN的一大成功之处在于迁移学习的能力。训练在大型CV数据集(如ImageNet)上的CNN模型可以学习到大量通用的视觉特征和模式,这使得模型在其他视觉任务上表现良好,而不用从头开始训练。
具体来说,可以保留原本CNN的参数,对于不同下游任务只需重新在CNN后面训练全连接层,或者训练新的全连接层并更新CNN最后几层的参数,这样不仅可以借助模型的迁移能力实现较好的下游表现,也可以有效降低模型的训练成本。
这种迁移学习的范式称为预训练+微调(Pre-Training+Fine-Tuning)范式。虽然word2vec等模型得到了很好的单词级别的语义表示,但是单词级的表示难以处理句子和段落中复杂的上下文信息。
此外,NLP领域没有大型的有标注数据集,因此需要采用自监督学习的形式进行预训练。自监督对于模型来说是有监督的训练,但是这些标签又不需要由人工标注来得到,只需要通过一定规则就可以自动从无监督文本中源源不断获取。
BERT架构
在自然语言中有很多词具有多重含义,因此必须结合上下文去理解其实际含义。BERT的模型架构使用了若干个Transformer的Encoder块堆叠而成,这使得其更专注于输入序列的语义理解。
BERT架构有两种规模
- 基础架构:12 个编码器,12 个多头注意力,特征维度为 768
- 大型架构:24 个编码器,16 个多头注意力,特征维度为 1024
我们现在重点关注输入序列是如何被处理的,其经过了三重嵌入操作
- 词元嵌入:常规的词元嵌入操作
- 段落嵌入(Segment Embedding):对序列进行段落划分
- 位置嵌入:可训练的位置编码
特别地,BERT引入了三种特殊词元 <CLS>、<SEP> 和 <PAD>
<CLS>:添加到每个序列的首部
<SEP>:添加到序列的分段处
<PAD>:填充无意义词元以满足最大长度
下面是添加特殊词元的一个实例

预训练
预训练是指在海量通用数据上对模型进行基础训练,使其掌握广泛适用的知识,特别是在NLP领域。这一过程类似于人类通过阅读大量书籍、文献和网络内容,系统性掌握语法规则、事实信息及语言内在规律。
掩码语言模型(Masked Language Model, MLM)是BERT预训练的一个任务。其主要思想是在训练过程中,可以随机遮住输入文本中的一些词元,并将其替换为特殊的标记 [MASK]。然后,模型需要根据上下文中的其他词元来预测被遮住的词元是什么。
下一句预测(Next Sequence Prediction, NSP)任务旨在让模型学习理解句子之间的关系。在训练过程中,可以随机选择一些相邻的句子对和两个不相邻的句子对,以帮助模型更好的认识句子之间的关联性。
两个任务的具体训练过程如下
- MLM通过设置
[MASK] 比例(一般是 15%),随机遮掩一些词元。模型通过层层编码器对每一个 [MASK] 输出一个概率分布,并计算交叉熵损失反向传播并修改参数。
- NSP通过构造相邻句子对
IsNext 和不相邻句子对 NotNext,处理完序列后关注 <CLS> 对应的输出。模型通过对 <CLS> 的输出构造二分类器,得到 IsNext 和 NotNext 的概率,并计算损失反向传播。
微调
BERT模型经过反复的预训练步骤后,需要经过模型微调以应用到不同的下游任务中。微调通常只需要少量指定任务的训练集,以保证模型在预训练的基础之上专精于某一指定任务。这个过程不需要过多的训练,但需要保证模型的参数与指定任务适配。
BERT作者在原文中给出了下游任务微调中的几种用例,在下表中给出。
| 任务 |
输入 |
应用和数据集 |
| 句子对分类 |
<CLS>,A,<SEP>,B,<SEP> |
自然语言推理(MNLI)、问答匹配(QQP) |
| 单句分类 |
<CLS>,A,<SEP> |
情感分析(SST-2)、句子相关性判断 |
| 问答任务 |
<CLS>,A,<SEP>,B,<SEP> |
抽取式问答(SQuAD) |
| 单句标注 |
<CLS>,A,<SEP> |
命名实体识别(NER) |

改动
最后,我们在这里简单列出BERT在Transformer架构上的改动
- 只使用了Encoder块,且堆叠层数更多
- 输入和输出共用Token Embedding矩阵
- 在 FFN 前馈神经网络中采用 GeLU 激活函数
- 引入了特殊词元标记
- 引入了预训练+微调范式
GPT
参考文献:【知乎】GPT 系列论文精读:从 GPT-1 到 GPT-4
生成式预训练(GPT-1)
GPT模型全称生成式预训练Transformer模型(Generative Pre-Trained Transformer),目前已经被广泛应用于NLP领域。OpenAI在2018年发布了GPT-1,这是第一个基于Transformer的无监督预训练语言模型。数年后的事情无需多言——ChatGPT的腾空出世在全球各个领域引起热议。目前,OpenAI旗下的ChatGPT系列模型已然成为大语言模型(LLM)的发展标杆之一。
在GPT-1出现之前,传统的NLP模型往往使用大量的数据对有监督的模型进行任务相关的模型训练,但是这种有监督学习的任务存在两个缺点:
- 需要大量的标注数据,高质量的标注数据往往很难获得
- 根据一个任务训练的模型很难泛化到其它任务中
为了解决这一痛点,OpenAI的研究人员在 Improving Language Understanding
by Generative Pre-Training 一文中提出了一种基于预训练的自回归序列生成模型。自回归模型的特点在于:在序列生成过程中,每个新生成的词元依赖于之前生成的词元。这意味着生成过程是串行的,每一步的输入由前面已生成的词元组成的上下文序列构成。
具体而言,对于一个无监督语料库 中的序列 U={u1,⋯,un},其似然函数表示为
L(U)=i∑logP(ui∣ui−k,⋯,ui−1;Θ)
其中 k 为上下文窗口,Θ 为神经网络参数。GPT-1模型可以视为仅使用Transformer架构的Decoder块的模型,其主要在大规模语料库上进行生成式预训练并进行微调。下图展示了GPT-1的完整架构

GPT-1采用了标准的从左到右的语言模型目标,也就是说模型只能始终按照上文进行预测。在模型架构层面,这由Decoder的Masked自注意力保证。
GPT-1的超参数设置如下
- Transformer参数
- Decoder块层数 N:12
- 隐藏层维度 dmodel:768
- 注意力头数 head:12
- FFN隐藏层维度 dff:3072=768×4
- Dropout率:10%
- 训练参数
- 训练轮数:100
- 批量大小:64
- 最大序列长度:512
- 优化器策略:常规Adam
- 学习率策略:线性增加再余弦衰减
尽管GPT-1的提出早于BERT,其对于不同任务的输入处理方式也不同,这通常被视为微调的一种形式。论文中主要提出了四种任务形式,借此机会补充以下NLP的文本处理任务。GPT-1的输入使用了三种特殊词元
- 开始词元
<s>:表示序列开始
- 结束词元
<e>:表示序列结束
- 分割词元
$:用于分割子序列(句子)
四种NLP任务如下
- 文本分类:根据文本内容预测其类别
- 举例:某种评论的情感分析(积极评论或消极评论)
- 输入格式:
<s>,Text,<e>
- 文本蕴含:判断前提与假设的关系
- 本质:三分类问题(蕴含、矛盾、无关)
- 输入格式:
<s>,Text1,$,Text2,<e>
- 语义相似性:判断两个句子在语义上是否相似
- 注意事项:由于句子的相对顺序不影响,因此输入需要变换顺序
- 输入格式:
<s>,Text1,$,Text2,<e>
<s>,Text2,$,Text1,<e>
- 选择题:从多个后选答案中选择一个最可能的答案
- 对于 k 个候选答案 a1,⋯,ak,其上下文文档为 z,问题为 q,则每一个候选答案对应的输入可以表示为
<s>,z,q,$,ai,<e>
Zero-Shot(GPT-2)
GPT-2 的整体设计思想相较于 GPT-1 没有变化,但通过模型规模的扩展和数据集的优化,在零样本学习(Zero-Shot Learning)上迈出了一大步。GPT-1在微调中引入了三个特殊符号,而GPT-2不再使用这些符号,而是采用自然语言输入格式。
具体的,在 GPT-2 的原始论文 Language Models are Unsupervised Multitask Learners 中,模型并未针对任何下游任务进行有监督的微调,而是直接在大规模文本上进行预训练,然后在各种 NLP 任务上测试性能。因此可以把 Zero-Shot 简单而片面地理解为只进行预训练。
Zero-Shot的正式定义为:在训练集中没有某个类别的样本,但在测试集中出现了这个类别。模型在训练过程中,即使没有接触过这个类别的样本,但仍然可以通过对这个类别的描述,对没见过的类别进行分类。
除此之外,GPT-2在超参数设置和架构设计上有所升级
- 更大的数据集:GPT-2 使用了 WebText 数据集进行训练
- 更大的模型:GPT-2 使用了更大的参数规模,调整了部分超参数
- 残差权重初始化:采用了 1/N 的权重缩放因子
- 层归一化方式:调整至每个子模块的输入端,变为 PreNorm
PreNorm 也就是在残差连接之前进行层归一化,这是与 GPT-1 等模型的不同之处。

Few-Shot(GPT-3)
GPT-3 延续了 GPT-2 模型的基本架构,其采用了相当大胆的参数规模。从 1.2B 涨到 1750B,总训练量约为三千亿个词元。这在很大程度上得益于计算机算力的提升和优质数据集的补充。
GPT-3 创新在于示例样本和少样本学习(Few-Shot Learning)的引入。推理时,通过在提示(Prompt)中加入少量样本来告诉模型要完成的具体任务,不对模型进行任何参数更新。这种做法相较于需要额外微调的做法,极大减少了特定任务的数据量需求。
对于某个指定任务的测试样本,Few-Shot 的一般流程如下
- 从对应任务的训练集中随机选取 K 个示例样本,其包含上下文和答案
- 将这 K 条示例样本与当前测试样本的上下文拼在一起,作为输入
- 模型根据输入中蕴含的提示来生成答案
- (可选)选取不同的超参数 K 优化模型表现
GPT系列发展到此,已经形成了上下文学习(In-Context Learning)的范式,其和微调的区别如下。
| 特性 |
上下文学习(In-Context Learning) |
微调(Fine-Tuning) |
| 工作原理 |
在输入提示中提供示例,利用上下文推理完成任务 |
更新模型内部的权重参数,以适应特定任务 |
| 参数状态 |
模型参数不变 |
模型参数发生改变 |
| 数据需求 |
仅需少量示例,甚至无需示例 |
需要大量的标注训练数据 |
| 资源消耗 |
推理阶段计算量大,无需训练资源 |
训练阶段需要大量算力消耗 |
| 存储需求 |
仅存储基础模型 |
需要为每个特定任务存储一个独立的模型 |
| 适用场景 |
快速原型设计、数据稀疏场景、通用大模型应用 |
领域特定任务、对性能要求极高且资源充足的场景 |
GPT-3之后的模型已经彻底放飞自我,参数量不断提升。OpenAI介于技术保密原则,没有在后续分享任何 GPT 模型的技术细节,但已知的是后续模型几乎延续了早期 GPT 模型的训练范式(从左到右的语言模型+上下文学习)。
Transformer in CV
ViT
ViT(Vision in Transformer)是2020年Google团队提出的模型,虽然不是第一篇将Transformer应用在视觉任务的论文,但是因为其模型简单且效果好,可扩展性强,成为了Transformer在CV领域应用的里程碑著作,也引爆了后续相关研究。
ViT原论文 An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale 中最核心的结论是:当拥有足够多的数据进行预训练的时候,ViT的表现就会超过CNN。
ViT通过对图片输入进行处理,并利用Transformer的编码器块实现CV分类任务,其框架如下。

对于一个输入的2D照片 x∈RH×W×C,ViT将其拆分为若干个一维向量 xp∈RP2⋅C。这些向量称为 patch,其中 H,W,C 为原输入的长宽和通道数,P 为每个 patch 的边长。可以计算总的 patch 个数为
N=P2CHWC=P2HW
由于常规的Transformer采用固定的隐藏层维度 dmodel,因此还需要对这些 patch 进行处理。通过引入一个可训练的线性层把这些 patch 映射到指定的维度,称之为Patch Embedding。
类似于Transformer架构,模型还需要对输入进行位置编码。作者在原文中采用了一维位置编码,通过加法操作植入到输入中。ViT使用了两种操作方法以实现最终的图像分类任务,实验证明二者效果相似
- 类比BERT模型,在序列开头添加特殊字符
<CLS>,将其输出 zL0 作为分类依据
- 对所有序列的输出求平均,即应用平均池化层。
为了探究模型的可扩展性,预训练阶段使用了ImageNet-1K、ImageNet-21K,JFT-18K三个数据集。同时删除了预训练数据集中和下游任务测试集中重复的数据。ViT一共设置了三种不同的参数规格,在预训练中采用Adam优化器,在微调中采用SGD和动量法。
在原文的实验中,作者们表明了一个重要结论
- 当数据集过小时,ViT性能明显低于ResNet等CNN模型。这很大程度上取决于CNN的归纳偏置(Inductive Bias)——局部性和平移不变性
- 当数据集很大时,ViT通常取得SOTA

Swin Transformer 是一种基于 Transformer 的视觉模型,由 Microsoft 研究团队提出,旨在解决传统 Transformer 模型在CV任务中的高计算复杂度问题。在传统的 Transformer 中,自注意力机制的计算复杂度为 O(n2),这对于高分辨率图像产生了很大的计算量。
Swin Transformer(下简称 Swin-T)借鉴了图像处理中的局部性关系,其采用窗口和分层的形式代替长序列。Swin-T 采用了类似CNN的层次化构建方法,特征图会随着层数加深,逐渐下采样至 4 倍,8 倍,16 倍等。但 ViT 的结构特征图经过一次 16 倍下采样之后就不变了。其架构图如下
[{"url":"/img/cs231n/Swin-T.png","alt":"Swin-T","title":""}]
下面按照原论文的代码介绍各个部件
- Patch Partition:模型接收输入 x∈RH×W×3,通过 48×3×4×4 且步幅为 4 的卷积层转化为 4H×4W 个 patch,每个 patch 的大小为 4×4×3=48,与卷积层的输出通道数保持一致
- 线性映射:通过线性层将 patch 的维度映射为指定的 C=96
- 位置编码:采用绝对位置编码进行位置信息的植入
- Patch Merging:每个Stage中会经过一个Patch Merging层进行下采样(Stage1除外)
- 下采样在空间维度上每隔一个 patch(注意,这里 patch 的空间大小随Stage的进行而变化)取样,得到若干 2×2 个 patch 组成的区域
- 将这些大 patch 分别在通道数上 Concat 拼接
- 在通道拼接后,通过 LayerNorm 归一化
- 最后通过线性映射层使得通道数翻倍,空间维度减半
- W-MSA和SW-MSA:这是两种基于Transformer多头注意力的改良版自注意力机制,他们成对且交替出现,因此Swin-T由偶数个Swin Transformer块组成
- W-MSA:给定一个窗口大小,使得自注意力机制只能在某个窗口的 patch 内进行,以减小计算量
- SW-MSA:通过划分可滑动的窗口,使得W-MSA中不同窗口之间的 patch 可以通过自注意力机制交换信息,具体滑动方式见论文
MoE
MoE混合专家模型(Mixture of Experts, MoE)是一种神经网络架构。其核心思想是通过一个路由器(Router),针对每个输入动态选择并激活一小部分称为专家(Experts)的子网络进行计算,而非使用全部网络。
常规的Transformer架构中,前馈神经网络FFN由于全连接层特性,其计算量占比超过总模型的一半。这类模型通常称为稠密型网络,而MoE属于典型的稀疏型网络,其特征在于运用了MoE层。
[{"url":"/img/cs231n/MoE.png","alt":"MoE","title":""}]
MoE层由若干个小型的已训练神经网络组成
- 专家:若干个专家网络,分别负责处理不同特征的数据
- 路由器:控制输入由哪个专家网络进行处理
- 特点:由路由器门控管理专家网络的激活状态,计算量大幅减少
2023年起,MoE被广泛应用于各类超大规模语言模型及多模态系统中。
RoPE编码
绝对位置编码
介于自注意力机制的并行计算特性,我们需要为序列植入位置信息以帮助模型理解序列的顺序关系。在Transformer原论文中,作者提出了一种基于正弦、余弦函数的绝对位置编码方式。对于位置索引 pos 和维度索引 i,定义位置编码计算公式为
PE(pos,2i)=sin(100002i/dmodelpos)
PE(pos,2i+1)=cos(100002i/dmodelpos)
虽然这种固定的函数形式简化了计算量,但是导致模型对长距离依赖的建模能力相对较弱。
可学习位置编码
前文提到:BERT和GPT初期模型都使用了可学习位置编码。这种方法就是把将位置编码的嵌入矩阵 P 视为模型的可学习参数,就像词嵌入一样。即便如此,这也导致了计算量的增加,并且使得模型无法处理那些超过最大长度的序列。
相对位置编码
正余弦位置编码是一种绝对位置编码,但是可以通过三角函数的和差化积公式证明,这种编码方式也可以通过矩阵乘法获取不同位置之间的相对位置。相对编码位置不关注词在句子中的绝对位置,而是关注两个词之间的距离或相对位置关系,这使得其可以处理任意长度的序列。
RoPE通过绝对位置编码的方式实现相对位置编码,综合了绝对位置编码和相对位置编码的优点。假设我们想通过下述操作给查询 q 和键 k 添加绝对位置信息 m,n,可以表示为
q~=f(q,m),k~=f(k,n)
由于自注意力机制的核心操作为内积,因此我们希望新的查询 q~ 和键 k~ 通过内积带有相对位置信息,令下式
⟨q~,k~⟩=g(q,k,m−n)
为了使得上述恒等式成立,我们合理的给出初始条件
f(q,0)=q,f(k,0)=k
复数域求解
根据复数知识可知,二维复向量 q,k 的内积满足
⟨q,k⟩=Re[qk∗]
其中 Re[⋅] 表示求实部。根据上小节的恒等式可以列出
⟨q~,k~⟩=Re[f(q,m)f∗(k,n)]=g(q,k,m−n)
简单起见,假设存在复数 z=g(q,k,m−n) 使得 f(q,m)f∗(k,n)=z,改写为指数形式
f(q,m)=Rf(q,m)eiΘf(q,m),f(k,n)=Rf(k,n)eiΘf(k,n)
z=g(q,k,m−n)=Rg(q,k,m−n)eiΘg(q,k,m−n)
解得方程组
{Rf(q,m)Rf(k,n)=Rg(q,k,m−n),Θf(q,m)−Θf(k,n)=Θg(q,k,m−n),(1)(2)
对于方程 (1),令 m=n 解得
Rf(q,m)Rf(k,m)=Rg(q,k,0)=Rf(q,0)Rf(k,0)=∥f(q,0)∣∣∥f(k,0)∥=∥q∥∥k∥
因此我们大胆的认为 Rf(x,m)=∥x∥ 不依赖于 m,同样在 (2) 中令 m=n 有
Θf(q,m)−Θf(k,m)=Θg(q,k,0)=Θf(q,0)−Θf(k,0)=argq−argk=defΘ(q)−Θ(k)
上式取决于 q 和 k 的幅角,对上式两端移项得到
Θf(q,m)−Θ(q)=Θf(k,m)−Θ(k)
因此 Θf(q,m)−Θ(q) 应当是一个仅关于 m 的函数,记
φ(m)=Θf(q,m)−Θ(q)
令 n=m−1 代入方程 (2) 并结合上式有
φ(m)−φ(m−1)=Θf(q,m)−Θ(q)−[Θf(q,m−1)−Θ(q)]=Θf(q,m)−Θ(q)−[Θf(k,m−1)−Θ(k)]=[Θf(q,m)−Θf(k,m−1)]+Θ(k)−Θ(q)=Θg(q,k,1)+Θ(k)−Θ(q)
故 {φ(m)} 是一个等差数列。设右端为 θ,结合 φ(0)=0 得到
φ(m)=mθ
将这些代入 f 的指数形式得到
f(q,m)=Rf(q,m)eiΘf(q,m)=∥q∥ei[mθ+Θ(q)]=eimθq
RoPE旋转矩阵
根据复数乘法的几何意义,上式在二维情况下可以改写为
f(q,m)=eimθq=[cosmθsinmθ−sinmθcosmθ][q0q1]
由于内积满足线性叠加性,因此任意偶数维的情况可以表示为二维情况的拼接,即有
f(q,m)=cosmθ0sinmθ000⋮00−sinmθ0cosmθ000⋮0000cosmθ1sinmθ1⋮0000−sinmθ1cosmθ1⋮00⋯⋯⋯⋯⋱⋯⋯0000⋮cosmθ2d−1sinmθ2d−10000⋮−sinmθ2d−1cosmθ2d−1q0q1q2q3⋮qd−2qd−1
上式中涉及的旋转矩阵记为 Rm,这是一个正交矩阵,不会改变原向量的模长。因此我们只需要对目标向量乘以这个旋转矩阵就可以注入位置信息了。根据三角恒等式不难得到
⟨q~,k~⟩=(Rmq)T(Rnk)=qTRn−mk
因此,RoPE位置编码在自注意力机制中仅关注二者的相对位置差 n−m。上式中的 θi=10000−2i/d 沿用了 Transformer 架构的三角编码形式,使得词元之间的依赖性随着相对距离的变远而衰减。
为了避免 Rm 的稀疏性而导致的算力浪费,实际代码通常按照下式实现RoPE位置编码
f(q,m)=q0q1q2q3⋮qd−2qd−1∘cosmθ0cosmθ0cosmθ1cosmθ1⋮cosmθ2d−1cosmθ2d−1+−q1q0−q3q2⋮−qd−1qd−2∘sinmθ0sinmθ0sinmθ1sinmθ1⋮sinmθ2d−1sinmθ2d−1
RoPE位置编码直观理解为:索引为 m 的向量被旋转了 mθi 角度。
AdamW
本节的最后,我们主要讲讲目前最主流的参数优化方法:AdamW。
在深度学习(七)中,我们简单介绍了Adam算法。Adam算法结合了SGD、动量法、AdaGrad、RMSProp算法等特点,在曾经的梯度算法中被视为权威。定义滑动平均
vt←β1vt−1+(1−β1)gt
st←β2st−1+(1−β2)gt∘gt
其中 gt 是在 t 时刻的小批量梯度。其中,通常设置参数 β1=0.9,β2=0.999 并初始化 v0=s0=0。为保证梯度估计的无偏性,定义下述标准化向量
v^t=1−β1tvt,s^t=1−β2tst
在此基础上,类比RMSProp算法更新参数
xt←xt−1−s^t+εηv^t
后来,人们发现了Adam优化器的一个不足之处:如果采用了 L2 权重衰减,则相应的权重衰减项会被直接加在损失函数里,从而导致动量的一阶与二阶滑动平均考虑了该权重衰减项。这显然影响了Adam的优化效果。
将权重衰减与梯度的计算进行解耦能够显著提升Adam的效果,于是便有了AdamW优化器。目前,AdamW现在已经成为Transformer架构训练中的默认优化器了。具体来说,采用 L2 正则化的损失函数定义为
L=Lerror+2λ∥x∥2
其中 x 是训练参数,其求导后会得到 λx 项。为了消去这一项,在最后的参数更新中规定
xt←xt−1−η(s^t+εv^t+λx)
没错,这就是AdamW的完整形式。