Transformer是如何工作的(以GPT2为例)

Transformer是如何工作的(以GPT2为例)

脱离模型直接聊Transformer会感觉很空,本文以GPT-2为例(base版,768维维度、12层Transformer、12头自注意力),介绍一下其训练和推理的核心逻辑,以及推理时的具体执行流程。

一、GPT-2训练时数据处理步骤

训练的核心目标是让模型通过海量文本,自主学习语义、语法和逻辑,最终优化所有可训练参数,全程围绕“前向传播计算损失→反向传播更新参数”循环迭代,具体步骤如下:

步骤1:文本输入与Token化处理
1. 输入原始文本(一段话),例如“猫坐在柔软的垫子上”;
2. 采用BPE(字节对编码)算法进行子词切分,将文本拆分为模型可识别的最小语言单元(Token),避免单个汉字/单词的局限,平衡词汇量与表达能力,例如切分为[“猫”, “坐在”, “柔软”, “的”, “垫子”, “上”];
3. 通过模型内置的词汇表(Vocab),将每个Token映射为唯一的整数ID(如[1001, 2005, 3010, 4002, 5008, 6003]),这是模型能够识别的“数字语言”。

步骤2:嵌入层与位置编码处理
1. 初始化嵌入矩阵(形状为[词汇表大小×768]),训练初期该矩阵为随机小数值,与后续所有模型参数同步参与训练;
2. 根据Token ID,从嵌入矩阵中“查表”,取出每个Token对应的768维向量(即Token嵌入),此时的向量初期无语义,后续通过训练逐步承载语义信息;
3. 加入位置编码(同样为随机初始化后可训练的矩阵),给每个Token的向量添加位置信息,让模型区分Token在句子中的先后顺序(Transformer本身不具备位置感知能力);
4. 最终得到形状为[Token数量×768]的矩阵,作为12层Transformer的初始输入。

步骤3:12层Transformer逐层前向计算

每一层Transformer的处理逻辑完全一致,均遵循“前置归一化→多头自注意力→融合投影→残差连接→前置归一化→FFN前馈网络→残差连接”的流程,具体如下:

1. 前置归一化(LayerNorm):对本层输入的向量进行数值校准,将所有Token的向量分布调整为统一标准(均值、方差固定),避免数值波动影响训练稳定性;
2. 多头自注意力计算:将输入向量分别通过本层独立随机初始化的Wq、Wk、Wv权重矩阵,生成查询(Q)、键(K)、值(V)向量;将Q、K、V按头切分为12份(768维,12头,每头64维),每头独立计算Token间的关联分数(Q与K的点积,缩放后通过Softmax归一化得到注意力权重),再用权重与V进行加权求和,得到单头输出;
3. 多头融合投影:将12个头的输出按列拼接,得到[Token数量×768]的矩阵,再通过本层独立的Wo权重矩阵进行融合投影(这里就是把12个头合成1个头,否则12个头之间就没关系了),打破头与头的孤立性,混合全局特征;
4. 第一次残差连接:将本层的初始输入(未经过注意力计算的向量)与注意力融合后的输出相加,保留原始底层信息,同时为梯度反向传播提供直通通路,避免梯度消失;
5. 第二次前置归一化:对残差连接后的向量再次进行数值校准,为后续FFN计算做准备;
6. FFN前馈网络计算:通过两层线性变换(768维→3072维升维,再3072维→768维降维,通过缩放可以让模型不要太多的关注细节)+ GELU非线性激活,对每个Token的向量进行深度加工,筛选冗余细节、提炼关键语义特征(词与词之间不交互,仅单独提纯);
7. 第二次残差连接:将FFN的输入与FFN的输出相加,避免过度加工丢失核心特征,得到本层的最终输出;
8. 重复上述7个步骤,将第1层的输出作为第2层的输入,依次经过12层Transformer的加工,得到最终的特征矩阵。

步骤4:损失计算与反向传播
1. 输出层处理:将12层Transformer的最终输出,通过线性投影映射到词汇表维度,得到每个Token对应的下一个可能Token的概率分布;
2. 计算损失(Loss):以“预测下一个Token”为目标,对比模型预测的概率分布与文本的真实Token,计算全局唯一的损失值(损失越大,预测越不准);
3. 反向传播更新参数:通过链式法则,将损失值从输出层反向逐层传递,穿透12层Transformer的所有权重(Wq、Wk、Wv、Wo、FFN权重),一直传递到最开头的嵌入矩阵和位置编码矩阵;
4. 优化器迭代:通过Adam优化器,根据反向传播得到的梯度,微调所有可训练参数(包括嵌入矩阵、各层权重、位置编码),降低损失值;
5. 循环迭代:重复步骤1-4,用海量文本持续训练,直到损失值趋于稳定,模型能够准确预测下一个Token,此时嵌入矩阵的向量已具备语义(相似Token的向量距离相近),各层权重也已学会最优的特征提取逻辑。

一些细节
1. 所有可训练参数(嵌入矩阵、各层Wq/Wk/Wv/Wo、FFN权重、位置编码)均为随机初始化,同步参与反向传播更新;
2. 可复用同领域、同词表的预训练嵌入矩阵,大幅提升训练速度,减少“从零学习语义”的算力消耗;
3. 在GPT-2的12层Transformer中,浅层侧重学习语法、局部搭配,深层侧重学习语义、逻辑推理,均为训练中自动分化形成,无需人工设计;
4. 在GPT-2中,每一头侧重什么并不是人为设计的,而是通过训练自动生成的;
5. 先分别乘Wq、Wk、Wv,再拆分为12个头;先将12个头合并为1个头,再乘Wo;
6. 嵌入矩阵是大模型的一部分,也是通过训练,接收到反向反馈,逐步修正得到的;
7. 词嵌入矩阵和位置嵌入矩阵,也都是随机初始化,然后逐步训练出来的;模型支持的上下文越大,位置嵌入矩阵会不断变长;

二、GPT-2推理时训练步骤

推理的核心目标是利用训练好的模型参数,根据输入的文本(提示词),逐步生成符合语义、逻辑的后续文本,全程只有前向传播,无反向传播和参数更新,具体步骤如下:

步骤1:输入处理
1. 输入推理提示词(一段话),例如“猫坐在柔软的垫子上,它”;
2. 对提示词进行Token化处理(BPE切分→Token ID映射),得到与训练时格式一致的Token ID序列;
3. 从训练好的嵌入矩阵中,根据Token ID取出对应的768维向量,加入训练好的位置编码,得到[Token数量×768]的初始输入矩阵。

步骤2:12层Transformer逐层前向计算
1. 将初始输入矩阵依次传入12层Transformer,每层均执行“前置归一化→多头自注意力→Wo融合→残差连接→前置归一化→FFN→残差连接”的流程,全程使用训练好的固定参数(嵌入矩阵、各层权重、位置编码均不改变),最终得到提示词对应的特征矩阵。

步骤3:Token生成
1. 将12层Transformer的最终输出,通过输出层线性投影,映射到词汇表维度,得到当前最后一个Token对应的下一个Token的概率分布;
2. 按概率筛选(通常取概率最高的Token,或通过采样策略筛选),得到下一个Token的ID,再将其映射为对应的文字(例如筛选出“很”,此时提示词变为“猫坐在柔软的垫子上,它很”);
3. 迭代生成:将新生成的Token ID加入原有的Token ID序列,重新执行步骤2-3,重复该过程,直到生成预设长度的文本,或遇到结束符(EOS),停止生成。

一些细节
1. 推理时仅执行前向传播,速度远快于训练(推理时无需计算损失,也不会更新任何参数,所有权重均为训练好的最优值,仅做特征提取和概率预测)。
2. 推理时,选用不同的tempture,其实就是选用TopX的高概率token,进行随机挑选,从而输出会不完全一致,并影响后续token输出;
3. 生成的文本质量,直接依赖训练时的参数优化效果(嵌入矩阵的语义准确性、各层权重的特征提取能力),所以高水平的语料特别重要,很多时候是宁缺毋滥;
4. 为保证生成的逻辑性,推理时会使用掩码机制,确保模型生成当前Token时,无法看到后续未生成的Token(单向因果掩码);
5. 在生成一个token后,会将这个token补充到本轮输入的末尾,作为下一轮的输入,预测下一个token;
6. 当大模型反馈100个汉字,意味着返回了120~150个token(1 个汉字 ≈ 1.2~1.5 个 Token),也就是进行了120~150次推理,所以服务商需要大量高端计算显卡,支持大模型的运算;
7. 所以当25年初DeepSeek靠压缩注意力(大幅降低注意力计算开销)+ MoE稀疏激活(只计算少部分参数)+ KV量化缓存(大幅降低显存用量)+ 推测解码(一次生成多个token,预测成功保留,大幅提升推理效率)等方法,让推理成本大幅降低,着实让业绩震惊了一把;

Transformer03:自注意力机制

Transformer模型的核心是自注意力机制(Self-Attention),它允许模型在处理序列时,能够捕捉序列内部不同位置之间的依赖关系。自注意力机制的计算过程可以概括为以下几个步骤:

1. 查询(Query)、键(Key)、值(Value)的生成:
对于输入序列中的每个元素,模型会分别生成对应的查询(Q)、键(K)和值(V)。这通常是通过输入序列与三个不同的权重矩阵相乘来实现的。

2. 注意力分数的计算:
对于序列中的每个元素,计算其查询(Q)与序列中所有元素的键(K)的点积,然后除以一个缩放因子(通常是键向量维度的平方根),得到一个注意力分数。

Attention Score

其中,(Q)和(K)分别是查询和键的向量,\(d_k\) 是键向量的维度。

3. Softmax归一化:
使用Softmax函数对注意力分数进行归一化处理,使得所有元素的注意力分数之和为1。这表示每个元素对其他元素的注意力贡献是相对的。

Attention Weights

4. 加权求和:
将归一化后的注意力权重与对应的值(V)相乘,然后将所有元素的加权值相加,得到最终的输出。

Output

5. 多头注意力:
Transformer模型中的自注意力通常不是只计算一次,而是通过多头注意力(Multi-Head Attention)来实现。这意味着模型会并行地执行多次自注意力机制,每个头都有自己的查询、键和值权重矩阵。最后,这些头的输出会被拼接起来,并通过一个线性层来整合信息。

6. 残差连接和层归一化:
在自注意力层之后,通常会有一个残差连接,它将自注意力层的输入直接添加到输出上,然后通过一个层归一化(Layer Normalization)来稳定训练过程。

整个自注意力机制使得Transformer能够并行处理序列中的所有元素,并且能够捕捉到元素之间的长距离依赖关系,这是它在处理序列数据时非常有效的原因之一。

让我们通过一个简单的例子来说明自注意力机制的计算过程。假设我们有一个由3个词组成的序列:[“I”, “love”, “coding”],并且每个词的词嵌入维度是4。

步骤1: 词嵌入
首先,我们将每个词转换为词嵌入向量。假设词嵌入矩阵已经预先训练好,我们可以直接获取每个词的词嵌入向量:

– “I” -> [0.1, 0.2, 0.3, 0.4]
– “love” -> [0.5, 0.6, 0.7, 0.8]
– “coding” -> [0.9, 1.0, 1.1, 1.2]

步骤2: 添加位置编码
接下来,我们为每个词嵌入向量添加位置编码。假设我们使用标准的正弦和余弦函数生成位置编码,并且序列的最大长度是3。位置编码向量如下:

– 位置1的编码:[sin(0), cos(0), sin(8), cos(8)] (这里8是4*2,因为每个词嵌入维度是4)
– 位置2的编码:[sin(1), cos(1), sin(9), cos(9)]
– 位置3的编码:[sin(2), cos(2), sin(10), cos(10)]

将位置编码向量与词嵌入向量相加:

– “I” (位置1): [0.1+sin(0), 0.2+cos(0), 0.3+sin(8), 0.4+cos(8)]
– “love” (位置2): [0.5+sin(1), 0.6+cos(1), 0.7+sin(9), 0.8+cos(9)]
– “coding” (位置3): [0.9+sin(2), 1.0+cos(2), 1.1+sin(10), 1.2+cos(10)]

步骤3: 自注意力计算
现在我们开始自注意力的计算过程。首先,我们需要为每个词生成查询(Q)、键(K)和值(V)向量。假设我们使用相同的词嵌入向量作为Q、K和V的初始输入,并通过不同的权重矩阵进行转换:

– Q = W^Q * 输入向量
– K = W^K * 输入向量
– V = W^V * 输入向量

这里W^Q、W^K和W^V是模型的可学习参数。

步骤4: 计算注意力分数
对于序列中的每个词,我们计算其查询向量与序列中所有词的键向量的点积,然后除以键向量维度的平方根进行缩放:

– 对于词”I”,其注意力分数是它自己的Q与所有词的K的点积:

Attention Score

步骤5: Softmax归一化
使用Softmax函数对每个词的注意力分数进行归一化处理:

– 对于词”I”,归一化后的注意力权重是:

Attention Weights

步骤6: 加权求和
最后,将归一化后的注意力权重与对应的值向量相乘,并求和得到最终的输出:

– 对于词”I”,其输出是:

Output

这个过程对于序列中的每个词都要重复执行,以计算整个序列的输出。自注意力机制允许模型在处理每个词时,都能够考虑到序列中其他所有词的信息,从而捕捉词与词之间的复杂关系。

请注意,这个例子是一个简化的版本,实际的Transformer模型可能会使用多头自注意力机制,并且会有多个层来进一步处理信息。此外,词嵌入和位置编码通常是通过预训练得到的,而不是从头开始训练。

Transformer02:词嵌入及位置编码的计算

一句话经过分词和嵌入之后,输入到Transformer模型的过程如下:

1. 构建输入序列:
将分词后得到的词或字符序列转换为对应的词嵌入向量。每个词或字符都有一个对应的嵌入向量,这些向量通常通过预训练的词嵌入模型获得。

2. 添加位置编码:
由于Transformer模型本身不包含递归或卷积结构,因此它无法直接捕捉序列中的位置信息。为了解决这个问题,需要为每个词嵌入向量添加一个位置编码。位置编码通常是根据词在序列中的位置生成的,它与词嵌入向量相加,使得模型能够利用位置信息。

3. 输入到Transformer:
将包含位置编码的词嵌入向量作为输入序列送入Transformer模型。在Transformer模型中,输入序列被处理为一系列向量,每个向量对应序列中的一个元素(词或字符)。

4. 多头自注意力:
Transformer模型使用多头自注意力机制来处理输入序列。在自注意力层中,每个元素的嵌入向量都会与序列中所有其他元素的嵌入向量进行比较,以计算注意力权重。这个过程在多个“头”中并行进行,每个头都有自己的查询(Q)、键(K)和值(V)权重矩阵。

5. 层归一化和前馈网络:
自注意力层的输出会经过层归一化,然后送入前馈神经网络。前馈网络通常由两个线性变换和一个非线性激活函数组成。这个过程在每个Transformer层中重复进行。

6. 堆叠多个Transformer层:
Transformer模型通常由多个相同的层堆叠而成,每个层都包含自注意力机制和前馈网络。通过这种方式,模型可以在不同层捕捉不同级别的特征和依赖关系。

7. 输出处理:
经过多个Transformer层处理后,模型的输出可以用于各种NLP任务,如语言翻译、文本摘要、问答等。对于特定的任务,可能还需要在Transformer模型的顶部添加额外的层,如线性层或分类层。

总之,每个嵌入向量并不是有自己的Transformer,而是所有嵌入向量一起作为输入序列,被送入同一个Transformer模型中进行处理。通过多头自注意力机制,模型能够捕捉序列内部不同位置之间的依赖关系,从而实现对输入句子的深入理解。

通过一个简单的例子来说明词嵌入和位置编码的计算过程。

### 词嵌入(Word Embedding)

假设我们有一个句子:”I love natural language processing”。首先,我们需要将这个句子分词成单词列表:[“I”, “love”, “natural”, “language”, “processing”]。

接下来,每个单词将通过一个词嵌入矩阵转换成一个固定维度的向量。假设我们的词嵌入维度是4,那么每个单词将被映射到一个4维空间中。例如:

– “I” -> [0.1, 0.2, 0.3, 0.4]
– “love” -> [0.5, 0.6, 0.7, 0.8]
– “natural” -> [0.9, 1.0, 1.1, 1.2]
– “language” -> [1.3, 1.4, 1.5, 1.6]
– “processing” -> [1.7, 1.8, 1.9, 2.0]

这里的数字是随机生成的,实际的词嵌入向量是通过训练得到的,能够捕捉单词的语义信息。

### 位置编码(Positional Encoding)

Transformer模型不包含递归或卷积结构,因此无法直接捕捉序列中单词的顺序信息。为了解决这个问题,我们需要为每个词嵌入向量添加位置编码。

位置编码通常是通过正弦和余弦函数的组合来生成的,以确保不同维度的位置编码具有不同的频率。假设我们的词嵌入维度是4,我们可以为每个位置生成一个4维的位置编码向量:

– 位置1的编码:[sin(1/10000), cos(1/10000), sin(2/10000), cos(2/10000)]
– 位置2的编码:[sin(2/10000), cos(2/10000), sin(4/10000), cos(4/10000)]
– 以此类推…

将位置编码向量与相应的词嵌入向量相加,得到最终的输入向量:

– “I” (位置1): [0.1+sin(1/10000), 0.2+cos(1/10000), 0.3+sin(2/10000), 0.4+cos(2/10000)]
– “love” (位置2): [0.5+sin(2/10000), 0.6+cos(2/10000), 0.7+sin(4/10000), 0.8+cos(4/10000)]
– 以此类推…

这样,每个单词的嵌入向量都包含了其在句子中的位置信息,使得Transformer模型能够在处理序列时考虑到单词的顺序。

### 注意事项

– 词嵌入和位置编码的具体计算方法可能因不同的模型和实现而有所不同。
– 实际应用中,词嵌入通常是通过预训练模型(如Word2Vec、GloVe或BERT)得到的,而不是从头开始训练。
– 位置编码的生成方法在不同的Transformer变体中可能有所不同,例如Transformer-XL和XLNet采用了不同的方法来处理长序列。

这个例子展示了词嵌入和位置编码的基本计算过程,以及它们如何帮助Transformer模型理解和处理自然语言序列。

在实际应用中,词嵌入和位置编码可以预先计算并缓存,以提高效率。下面是一些具体的情况:

1. 词嵌入的缓存:
– 词嵌入通常是通过预训练语言模型得到的,这些模型在大规模语料库上训练,学习到的词嵌入向量能够捕捉丰富的语义信息。
– 一旦词嵌入矩阵训练完成,对于任何给定的单词,其对应的词嵌入向量就可以直接从预训练的模型中获取,而不需要每次重新计算。

2. 位置编码的缓存:
– 位置编码的生成方式是固定的,例如使用正弦和余弦函数的组合,这意味着对于给定的维度和最大序列长度,位置编码向量可以预先计算出来。
– 在模型初始化阶段,可以生成一个位置编码矩阵,其中每一行对应一个位置的位置编码。在处理输入序列时,只需根据序列中单词的位置索引来选择相应的位置编码向量。

3. 缓存的优势:
– 缓存词嵌入和位置编码可以显著减少模型在每次前向传播时的计算量,特别是对于大型模型和长序列。
– 缓存还可以减少模型的延迟,因为从内存中读取预先计算好的向量比实时计算要快得多。

4. 实际应用:
– 在实际的深度学习框架中,如TensorFlow或PyTorch,词嵌入和位置编码通常作为模型的参数或静态变量存储,以便在模型训练和推理过程中重复使用。

5. 灵活性:
– 虽然位置编码通常是固定的,但在某些情况下,如果模型需要处理可变长度的序列,位置编码也可以动态生成。但即使如此,对于常见的序列长度,位置编码的计算可以预先完成,并存储在查找表中以供快速访问。

通过这种方式,词嵌入和位置编码的预先计算和缓存,可以使得Transformer模型更加高效地处理输入数据,特别是在处理大量数据或需要快速响应的应用场景中。

Transformer01:总论

在处理自然语言处理(NLP)任务时,输入一句话通常需要经过以下步骤:

1. 分词(Tokenization):
首先,输入的句子需要被分词,即将句子拆分成更小的单元,这些单元可以是单词、字符或者其他语言单位。分词是处理自然语言的第一步,因为大多数模型都是基于离散的词或字符进行操作的。

2. 词嵌入(Embedding):
分词之后,每个词或字符会被转换成词嵌入(Word Embedding)。词嵌入是将离散的词或字符映射到连续的向量空间中的一种表示方法。这些向量能够捕捉词的语义信息,并且通常通过预训练模型(如Word2Vec、GloVe或BERT等)来获得。

3. 位置编码(Positional Encoding):
对于Transformer模型,由于其自注意力机制无法捕捉序列中元素的顺序信息,因此需要添加位置编码。位置编码是一种向量,它与词嵌入相加,以提供序列中每个元素的位置信息。

4. 序列化(序列化处理):
在某些情况下,如果输入序列超过了模型的最大长度限制,可能还需要进行序列化处理,如截断或填充。

5. 模型处理:
经过上述步骤处理后,得到的序列化、嵌入化和编码后的输入数据就可以被送入模型进行进一步的处理和学习了。

因此,当输入一句话时,通常是先进行分词,然后计算词嵌入,最后将分词后的词嵌入与位置编码相结合,形成模型的输入。这个过程使得模型能够理解句子的结构和语义信息,并在此基础上进行各种NLP任务。