神经网络研究-Transformer架构


神经网络研究-Transformer架构

介绍:是第一个完全基于自注意力机制的处理序列转换模型的神经网络架构,是大模型当前的主流架构。

典型神经网络优缺点

RNN的缺点:无法并行计算,长语句输入计算效率低;梯度消失&爆炸&链式信息损失,导致难以实现长期记忆
CNN的缺点:输入长度有限.优点是可并计算,计算效率高
Transformer相比RNN和CNN的特点:
和RNN相比,可并计算,计算效率高;有固有的全局视野,可捕获长距离依赖。本质是层内没有序列限制。
和CNN相比,感受长度不受限制,可灵活处理位置信息。本质是可以完整输入的感受野。

关键技术

  1. Self Attention(自注意力):
    意义:核心组件。
    用途:通过计算每个词和其他词之间的相似度来建立他们之间的关系,并根据这些关系加权计算每个词的表示。优点是能捕获序列中任意两个位置之间的关系,在序列建模任务中表现良好。使用自注意力的模型可以捕获长距离的依赖关系,提高并行计算效率。
    过程:由输入表示、权重计算和加权求和三部分组成。输入表示是指输入序列中的每个词通过词嵌入(Embedding)转换为向量表示。每个词有三种向量组成(查询、键、值向量),对于每个词,查询向量和键向量通过线性变换得到,然后通过相似度函数(点积或缩放点积)计算查询向量和键向量的相似度,将相似度归一化得到注意力权重,进而得到每个词和其他词的相关性权重。再使用这些权重对值向量加权求和,获得每个词的上下文表示。
    单头注意力机制计算公式如下:
    $$
    Attention(Q, K, V) = softmax(\frac{QK^T}{\sqrt{d_k}})V
    $$
    $Q$是query(查询向量),来自用户输入,计算公式$Q = ZW_{Q}$,$Z$是词嵌入矩阵$E$和位置编码矩阵$P$的和,都是$Td_{model}维度$。$K$是key(键向量),来自词库,计算公式$Q = ZW_{K}$。$V$是value(值向量),计算公式$Q = ZW_{V}$, Attention score高,value占比越大。这其中$Q$、$K$、$V$的维度分别是$Td_k$、$Td_k$、$Td_v$,$d_k$是键的维度,用于缩放点积,防止梯度消失,特别是维度比较大的时候。通常$d_k=d_v=d_{model/h}$,其中$h$是head的个数。
    多头注意力机制计算公式如下:
    $$
    MultiHead(Q, K, V) = Concat(head_1,…, head_h)W^{O}
    $$$$
    where head_i = Attention(QW^{Q}{i}, KW^{K}{i}, VW^{VkV}_{i})
    $$

  2. Multi-Head Attention(多头注意力层):
    过程:将输入数据划分为多个head,每个head独立关注输入的不同表示子空间,每个子空间独立计算注意力,从而使用模型并行捕获输入数据中的不同特征和模式信息,综合各项信息来更全面的理解文本。具体来说,就是通过初始权重随机分布,通过梯度下降优化过程,去推动每个head去适应其权重来学习不同的特征,进而减少总体损失。
    优势:允许模型关注序列中不同部分的信息;多个注意力头可以并行计算。

  3. Positional Encoding(位置编码)
    用途:为输入序列中的每个词添加位置信息,进而解决attention缺失词的位置信息的问题,进一步使用序列的顺序信息,在输入表示中添加位置信息编码来注入绝对或者相对位置编码,位置编码可以通过学习或者直接固定获得。
    典型的位置编码方法:基于正弦和余弦函数的固定位置编码
    $$
    PE_{(pos, 2i)} = sin(pos/10000^{2i/d_{model}})
    $$$$
    PE_{(pos, 2i+1)} = cos(pos/10000^{2i/d_{model}})
    $$
    其中,$pos$是词位置,$i$是维度索引。

  4. Feed Forward Neural Network(前馈层):通常由两个全连接层,中间使用Relu函数作为激活函数。
    $$
    FFN(x) = max(0, xW_1 + b_1)W_2 + b_2
    $$
    Feed Forward Neural Network(前馈层)目的:对序列中所有位置的表示进行变换,使用同一个多层感知机(MLP)。对同一个layer的不同position,其所作的线性变换是一致的。增加网络结构的非线性,增加参数量,提升模型复杂度和深度,使模型可以处理更复杂的任务。

  5. Residual Connectionz(残差连接)+Layer Normalization(归一层):用于稳定训练过程。

Transformer架构

Inputs(输入)/Output(输出)

Inputs(输入)/Output(输出,在训练过程中作为输入结果导入):在翻译第一个词时需要填充开始标签<BOS>

Tokenize(分词)

Tokenize(分词):是Transformer的第一关,是作为模型的输入。
含义:Token可理解为词元,英文token可视为单词的“拼图”,一个token大约对应于0.75个单词或者3-4个字母,或者对应1-1.8个汉字。Tokenize的过程就是根据词汇表,将文本映射到词ID序列,作为词嵌入的输入。Tokenizer是只分割词元的算法方法。Tokenization是指分割后的单词。

Tokenize的处理流程:Normalization(文本清洗、数字处理)->Pre-Tokenization(基于规则初步分割)->Model(使用分词算法做字词拆分)->Post-Tokenization(特殊处理和标记)

  1. Normalization(文本清洗、数字处理):包括文本清洗、标准化写法、安全和规范化操作。其中文本清洗一般是指去除无用字符、额外空白等,只保留对分词和模型训练有意义的内容。标准化写法是指统一大小写设置,将数字统一格式,确保文本采用统一的字符编码等。安全和规范化操作一般时过滤掉危险内容。
  2. Pre-Tokenization(基于规则初步分割):先基于简单规则对文本做初步分割,将文本初步拆分为更小的单元,如句子或者词语等。
  3. Model(使用分词算法做字词拆分):利用分词模型算法对文本做处理,生成词汇表(Vocabulary),利用词汇表(Vocabulary),将文本拆分为Token.例如训练好的大语言模型文件中的tokenizer.json/tokenizer_config.json/vocab.json文件。
  4. Post-Tokenization(特殊处理和标记):包括序列填充和截断、特殊token信息添加、构建注意力掩码等。保证输入序列长度一致,并在序列的适当位置添加特殊token(例如/),区分实际token和填充token.

核心参数:词汇表(Vocabulary).词汇表(Vocabulary)大小影响着模型的泛化能力和计算效率。大词汇表可以提高模型覆盖不同词汇和表达的能力,但也会影响模型处理的速度。

分词算法:Work-based(基于单词)、Character-based(基于字符)、Subword-based(子词分词)等

  1. Work-based(基于单词):按照单词进行分词,利用空格和标点符号做分割。好处就是简单直观,缺点就是词汇表爆炸、Out-ot-Vocabulary问题严重,同时不能学习到词缀之间的关系。
  2. Character-based(基于字符):以char为最小粒度,按照单字符做分词。好处是词汇表比较小(26个英文字符+标点符号),缺点就是丢失语义信息,每个token的信息密度过低,导致序列过长,解码效率低。
  3. Subword-based(子词分词):按照词的subword进行分词。对低频词保留完整词或者拆分为字符,对高频词拆分为更细粒度的子词。好处就是能平衡词汇表的大小和语义表达能力,词表大小适中,解码效率高,也能学习到词缀之间的关系。典型算法:BPE(使用最广泛的分词算法)、WordPrice、Unigram
    Byte Pair Encoding(BPE):来自论文Neural Machine Translation of Rare Words with Subword Units,当前LLM的主流方法之一。

过程如下:构建初始词表->识别最频繁出现的标记对->合并最频繁出现的token对->重复步骤2和3

  1. 构建初始词表:先按照字符粒度先分词,将文本中的每个字符先视为一个token,形成初始词表;
  2. 识别最频繁出现的标记对:扫描语料库,找到出现频率最高的token对,也就是字符或子词;
  3. 合并最频繁出现的token对:将2中频率最高的token对合并为一个新的token后,将新token添加到词表中,并删除被token完成覆盖的token,做词汇表的更新,并将token对的合并加入到合并规则列表中。
  4. 重复步骤2和3:迭代执行2和3步骤,直到达到指定词汇量或者token对不住频繁出现为止。

评估标准:词汇表覆盖率、token数量、OOV率.
词汇表覆盖率:用于检验时候可以处理多种语言和专业术语。
token数量:token过长会导致计算浪费,过短导致信息丢失。
OOV率:即Over of Vocabulary Rate.训练中未出现而测试时出现的单词概率。

Embedding(词嵌入)

含义:将离散的输入ID序列中的每个词转换为向量表示(一般为列向量),通过查表为每个字符赋予语义信息。
词嵌入维度:需要嵌入的一句句子中词汇的总数(sequence)* 嵌入向量的维度(d_model)
Embedding矩阵参数确定:先初始化,后训练迭代

Positional Encoding(位置编码)

Positional Encoding(位置编码):和输入的词向量维度相同
位置编码设计:唯一性:每个位置的编码是唯一的,这确保模型能够区分序列中的不同位置。周期性:能够根据相位捕获位置关系.正交性:偶数位置和奇数位置的编码是正交,增加编码区分度和信息丰富度。
典型方法:Absolute Positional Encoding(绝对位置编码, APE)、Relative Positional Encoding(相对位置编码,RPE)、Rotary Positional Embedding(旋转位置编码, RoPE)

Absolute Positional Encoding(绝对位置编码, APE):为序列中每个位置分配唯一的固定或可学习的向量,直接表征决定位置索引。分为Sinusoidal编码和Learnable编码。适用于短序列、对位置敏感的任务。缺点是难以反应序列字符之间的相对位置关系,表示不了比预训练文本长度更长的位置向量。

  1. Sinusoidal编码:Sinusoidal三角式绝对位置编码,也是Transformer论文中使用的方法,利用不同频率的正弦、余弦函数生成位置编码,偶数维度用正弦,奇数维度用余弦。可无需训练,就具有周期性外推能力,但是无法直接表达相对的位置关系,适用于短文本翻译,需要固定位置感知的序列任务,在处理长序列性能时会出现下降,需要进一步微调和扩展。
  2. Learnable编码:Learnable可学习绝对位置编码,随机初始化位置编码矩阵,作为可训练参数,模型只能感知每个词向量所处的绝对位置,无法感知词向量之间的相对位置。适用早期如BERT的预训练模型,不具备长度外推性。

Relative Positional Encoding(相对位置编码,RPE):建模序列中任意两个位置之间的相对距离而非绝对索引,增强模型对局部结构的感知能力。论文:Self-Attention with Relative Position Representations.

Rotary Positional Embedding(旋转位置编码, RoPE):将位置信息编码为旋转矩阵,用于查询向量和键向量的注意力计算中,隐式融合绝对位置和相对位置的双重特性,通过旋转位置编码将一个向量旋转到某一个角度,为其赋予位置信息。好处就是可支持超长序列的推理,可通过波长设计自动衰减远距离依赖,借助旋转操作实现相对位置偏移的数学等价。适用于需要处理超长上下文的长文本生成,也可审批不同模态的序列长度下的多模态任务。出自论文:Roformer: Enhanced Transformer With Rotray Position Embedding

Encoder Block(编码器模块)

用途:将源语言句子编码为一系列向量,理解和提取输入文本中的相关信息,捕获序列中各个元素的上下文信息,其输出的是输入文本的连续表示,通常称为嵌入(Embedding)。可以视为一个阅读者。
过程:输入带位置信息的Embedding矩阵,内部循环N次,输出key、value传递给Decoder Block.
结构:主要由两个子层组成。
Multi-Head Self-Attention(多头自注意力层):计算输入序列中每个词和其他词的相关性。
Feed Forward Neural Network(前馈层):对每个词都独立非线性变换。
组成:

  1. Multi-Head Self-Attention(多头自注意力层)+Residual Connectionz(残差连接)+Layer Normalization(归一层)。
  2. Feed Forward Neural Network(前馈层)+Residual Connectionz(残差连接)+Layer Normalization(归一层)。
  3. Residual Connectionz(残差连接)&Layer Normalization(相加+归一层)。

Residual Connectionz(残差连接)的目的:每次循环能充分挖掘特征,用于将以前循环挖掘结果一并处理。
Layer Normalization的目的:按照样本跨所有特征进行标准化,即对hidden的维度去做归一化,针对单样本的不同特征做操作,对特定层的每个输入,将消除对批数据的依赖。在归一化层中,特定层中的所有神经元在给定输入的所有特征中有效地具有相同的分布。稳定神经网络学习过程,减少训练时间,提高模型最终性能。

Decoder Block(解码器模块)

用途:使用向量生成目标语言的翻译,根据从编码器接收到的上下文信息,使用自注意力机制,自回归生生成与上下文相关的连贯输出序列,保证输出时序性。可类比一个口述者。
过程:输入带位置信息的Embedding矩阵,内部循环N次,输出信息传递给Linear(线性层).
结构:主要由三个子层组成。
Masked Multi-Head Self-Attention(遮蔽多头自注意力层):计算输出序列中每个词和前面词的相关性(使用掩码防止未来信息泄露)
Cross Attention(交叉注意力):或者也称为Encoder-Deconder Attention(编码器-解码器注意力机制):计算输入序列和输出序列的相关性。
Feed Forward Neural Network(前馈层):对每个词都独立非线性变换。

  1. Residual Connectionz(残差连接)&Layer Normalization(相加+归一层)+ Masked Multi-Head Self-Attention(遮蔽多头自注意力层):
    Masked Multi-Head Self-Attention(遮蔽多头自注意力层):确保解码器生成当前输出时,不会受到未来输出的影响。
    原理:对注意力分数矩阵应用一个遮蔽(一个上三角矩阵,其中未来位置的元素设置为负无穷),在计算softmax前有效将这些位置注意力分数降到零附近,从而实现在生成序列的每一步,模型只能注意当前位置之前的词和标记。
  2. Residual Connectionz(残差连接)&Layer Normalization(相加+归一层)+Multi-Head Attention(多头注意力层):
    Cross Attention(交叉注意力):输入:编码器Encoder的编码信息矩阵计算得出的key、value矩阵和上一个解码器Decoder block的输出计算得到的query
    目的:用于融合两个不同序列或信息源的特征
    原理:解码器使用当前状态作为查询,与编码器的输出(key和value)进行交互,通过注意力机制确定编码器输出中的哪些部分是重要的,并据此生成下一个输出元素。

Feed Forward(前馈层)

Residual Connectionz(残差连接)&Layer Normalization(相加+归一层)+Feed Forward(前馈层):

Linear(线性层)&Softmax(Softmax层)

Linear(线性层)&Softmax(Softmax层):
目的:解码器输出转换为能和词汇表对应的更高维空间。
原理:将解密器输出矩阵映射到[100,10000]中,每个维度值代表相应单词作为序列下一个单词的未归一化分数,再利用Softmax做归一,从而得到10000个词的概率。概率越高,表示该单词成为下一个输出单词的可能性越大。获得概率词表。

基于深度学习框架的Transformer模型

基于PyTorch的Transformer模型

必要库和模块

库和模块名:torch/torch.nn/torch.optim/math/copy
用途:构建Transformer模型架构、管理数据和训练过程。

模型基本模块

组成:位置编码、多头注意力、前馈神经网络

位置编码

用途:注入输入序列中词嵌入矩阵的位置信息。
构建方法:正弦和余弦函数,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import torch.nn as nn
import torch as torch
import math as math
class PositionalEncoding(nn.Module):
def __init__(self, d_model, max_seq_length):
super(PositionalEncoding, self).__init__()
# 用零矩阵初始化位置矩阵
pe = torch.zeros(max_seq_length, d_model)
# 生成从0到max_seq_length-1的浮点数序列,并转换为二维张量
position = torch.arange(0, max_seq_length, dtype = torch.float).unsqueeze(1)
# 生成频率项,math.log(10000.0) / d_model表示固定常数,用于控制频率项衰减速度
div_term = torch.exp(torch.arange(0, d_model, 2).float() * -(math.log(10000.0) / d_model))
# 按照正弦公式,在偶数位置使用正弦函数
pe[:, 0::2] = torch.sin(position * div_term)
# 按照余弦公式,在奇数位置使用余弦函数
pe[:, 1::2] = torch.cos(position * div_term)
# 讲位置编码pe注册为模型缓存区,随模型保存和加载,不参与梯度更新,位置编码一般在初始化时计算好并在训练中保持不变
self.register_buffer('pe', pe.unsqueeze(0))

def forward(self, x):
# 用于将位置编码信息添加到词嵌入矩阵中
return x + self.pe[:, :x.size(1)]

多头注意力

用途:计算序列中每个位置之间的关系,并捕获输入序列不同的特征和模式。
构建方法:$Q$是query(查询向量),$K$是key(键向量),$V$是value(值向量),将他们拆分为多个注意力head, 对每个head处理注意力,并在最后将结果concat,再linear处理。
代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import torch.nn as nn
import torch as torch
import math as math

class MultiHeadAttention(nn.Module):
def __init__(self, d_model, num_heads):
super(MultiHeadAttention, self).__init__()
# d_model是词嵌入向量的维度,必须确保d_model能被num_heads整除
assert d_model % num_heads == 0

# 设置基本参数 d_model为模型维度,num_heads为注意力head数, d_k为每个head的维度
self.d_model = d_model
self.num_heads = num_heads
self.d_k = d_model // num_heads

# 定义线性变换层,设置初始化的查询q/键k/值v和输出o的线性变换
self.W_q = nn.Linear(d_model, d_model)
self.W_k = nn.Linear(d_model, d_model)
self.W_v = nn.Linear(d_model, d_model)
self.W_o = nn.Linear(d_model, d_model)

# 按照注意力公式,计算缩放后的点积注意力
def scaled_dot_product_attention(self, Q, K, V, mask = None):
# 计算注意力分数, math.sqrt(self.d_k)用于缩放
attn_scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.d_k)
# 应用掩码,这是用于解码器中的掩码多头注意力机制中
if mask is not None:
attn_scores = attn_scores.masked_fill(mask == 0, -1e9)
# 按照注意力公式,使用softmax做归一化处理,计算注意力权重
attn_probs = torch.softmax(attn_scores, dim=-1)
# 按照注意力公式,将注意力权重和值向量做加权求和
output = torch.matmul(attn_probs, V)

# 将输入的词嵌入矩阵分割为多head
def split_heads(self, x):
batch_size, seq_lengh, d_model = x.size()
return x.view(batch_size, seq_lengh, self.num_heads, self.d_k).transpose(1, 2)

# 将输出多head合并为原始形状
def combine_heads(self, x):
batch_size, _, seq_legth, d_k = x.size()
# contiguous确保张量在内存中连续,view重塑张量外形
return x.transpose(1, 2).contiguous().view(batch_size, seq_legth, self.d_model)

# 前向传播
def forward(self, Q, K, V, mask = None):
# 对Q/K/V做线性变换后,再按照多head注意力机制分割为多head
Q = self.split_heads(self.W_q(Q))
K = self.split_heads(self.W_k(K))
V = self.split_heads(self.W_v(V))

# 编码器入口,对Q/K/V计算多头注意力,根据mask情况,计算掩码后的多头注意力机制
attn_output = self.scaled_dot_product_attention(Q, K, V, mask)

# 更加公式concat合并多头并线性输出
output = self.W_o(self.combine_heads(attn_output))
return output
前馈神经网络(Feed Forward)

用途:对每个位置的特征做非线性变换。
构建方法:共三层,第一层为线性变换,用于将输入特征升维,第二层为非线性激活函数,用于捕获更复杂的特征关系,常用ReLU函数,第三层为线性变换,用于降维到原始维度。
代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import torch.nn as nn

class PositionwiseFeedForward(nn.Module):
def __init__(self, d_model, d_ff):
super(PositionwiseFeedForward, self).__init__()
# 设置第一层全连接层
self.fc1 = nn.Linear(d_model, d_ff)
# 设置第二层非线性激活函数层
self.relu = nn.ReLU()
# 设置第三层层全连接层
self.fc2 = nn.Linear(d_ff, d_model)

def forward(self, x):
# FNN网络计算
return self.fc2(self.relu(self.fc1(x)))
模型构成模块

组成:编码器(Encoder)和解码器(Decoder)

编码器(Encoder)

构成:多头注意力机制、前馈神经网络、残差连接和归一化
代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Encoderlayer(nn.Module):
def __init__(self, d_model, d_ff, num_heads, d_ff, dropout):
super(Encoderlayer, self).__init__()
# 设置多头注意力机制
self.self_attn = MultiHeadAttention(d_model, num_heads)
# 设置前馈神经网络
self.feed_forward = PositionwiseFeedForward(d_model, d_ff)
# 设置归一化层
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
# 设置Dropout正则化,防止过拟合
self.dropout = nn.Dropout(dropout)

def forward(self, x, mask = None):
# 根据输入序列,运行多头注意力机制
attn_output = self.self_attn(x, x, x, mask)
# 先做利用dropout做残差连接,再做归一化。
x = self.norm1(x + self.dropout(attn_output))
# 运行前馈神经网络
ff_output = self.feed_forward(x)
# 先做利用dropout做残差连接,再做归一化。
x = self.norm2(x + self.dropout(ff_output))
return
解码器(Decoder)

构成:掩码多头注意力机制、交叉注意力机制、前馈神经网络、残差连接和归一化
代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Decoderlayer(nn.Module):
def __init__(self, d_model, num_heads, d_ff, dropout):
super(Decoderlayer, self).__init__()
# 设置多头注意力机制
self.self_attn = MultiHeadAttention(d_model, num_heads)
# 设置交叉注意力机制
self.cross_attn = MultiHeadAttention(d_model, num_heads)
# 设置前馈神经网络
self.feed_forward = PositionwiseFeedForward(d_model, d_ff)
# 设置归一化层
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.norm3 = nn.LayerNorm(d_model)
# 设置Dropout正则化,防止过拟合
self.dropout = nn.Dropout(dropout)

def forward(self, x, enc_output, src_mask, tgt_mask):
# 根据输入序列,运行掩码多头注意力机制
attn_output = self.self_attn(x, x, x, tgt_mask)
# 先做利用dropout做残差连接,再做归一化。
x = self.norm1(x + self.dropout(attn_output))
# 获取编码器的输出K/V,和解码器的q,计算交叉注意力机制
attn_output = self.cross_attn(x, enc_output, enc_output, src_mask)
# 先做利用dropout做残差连接,再做归一化。
x = self.norm2(x + self.dropout(attn_output))
# 运行前馈神经网络
ff_output = self.feed_forward(x)
# 先做利用dropout做残差连接,再做归一化。
x = self.norm3(x + self.dropout(ff_output))
return x
完整Transformer模型代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import torch
import torch.nn as nn
"""
其中模型初始化参数含义如下:
src_vocab_size, # 源语言词汇表大小(如英文单词数)
tgt_vocab_size, # 目标语言词汇表大小(如中文单词数)
d_model = 512, # 模型维度(每个词向量的长度)
num_heads = 8, # 多头注意力的头数
num_layers = 6, # 编码器/解码器的堆叠层数
d_ff = 2048, # 前馈网络隐藏层维度
max_seq_length = 100, # 最大序列长度(用于位置编码)
dropout = 0.1 # Dropout概率
"""
class Transformer(nn.Module):
def __init__(self, src_vocab_size, tgt_vocab_size, d_model, num_heads, num_layers, d_ff, max_seq_length, dropout):
super(Transformer, self).__init__()
# 将来源语句词汇映射到d_model维空间的嵌入层,构成来源语句序列编码器词嵌入矩阵
self.encoder_embedding = nn.Embedding(src_vocab_size, d_model)
# 将目标语句词汇映射到d_model维空间的嵌入层,构成目标语句序列解码器词嵌入矩阵
self.decoder_embedding = nn.Embedding(tgt_vocab_size, d_model)
# 设置初始化位置编码矩阵
self.positional_encoding = PositionalEncoding(d_model, max_seq_length)

# 根据num_layers编码器/解码器的堆叠层数,每层作为编码器/解码器对象,构建编码器/解码器列表
self.encoder_layers = nn.ModuleList(
[EncoderLayer(d_model, num_heads, d_ff, dropout) for _ in range(num_layers)])
self.decoder_layers = nn.ModuleList(
[DecoderLayer(d_model, num_heads, d_ff, dropout) for _ in range(num_layers)])

# 初始化最后的全连接层,将解码器的输出转换为目标词汇表大小的维度
self.fc = nn.Linear(d_model, tgt_vocab_size)
# Dropout
self.dropout = nn.Dropout(dropout)

# 生成掩码函数,用于屏蔽无效位置和未来信息
def generate_mask(self, src, tgt):
# 源掩码:屏蔽填充符(假设填充符索引为0)
# 形状:(batch_size, 1, 1, seq_length)
src_mask = (src != 0).unsqueeze(1).unsqueeze(2)

# 目标掩码:屏蔽填充符和未来信息
# 形状:(batch_size, 1, seq_length, 1)
tgt_mask = (tgt != 0).unsqueeze(1).unsqueeze(3)
seq_length = tgt.size(1)
# 生成上三角矩阵掩码,防止解码时看到未来信息
nopeak_mask = (1 - torch.triu(torch.ones(1, seq_length, seq_length), diagonal=1)).bool()
# 合并填充掩码和未来信息掩码
tgt_mask = tgt_mask & nopeak_mask
return src_mask, tgt_mask

# 主要执行逻辑,前向传播,依次通过编码、解码器,最后通过全连接层输出
def forward(self, src, tgt):
# 生成源掩码和目标掩码
src_mask, tgt_mask = self.generate_mask(src, tgt)

# 编码器部分,先做词嵌入、再做位置编码,最后通过enc_layer做编码层处理
src_embedded = self.dropout(self.positional_encoding(self.encoder_embedding(src)))
enc_output = src_embedded
for enc_layer in self.encoder_layers:
enc_output = enc_layer(enc_output, src_mask)

# 解码器部分,先做词嵌入、再做位置编码,最后通过dec_layer做解码层处理
tgt_embedded = self.dropout(self.positional_encoding(self.decoder_embedding(tgt)))
dec_output = tgt_embedded
for dec_layer in self.decoder_layers:
dec_output = dec_layer(dec_output, enc_output, src_mask, tgt_mask)

# 全连接层将解码器的输出转换为目标词汇表大小的维度,得到最终的输出
output = self.fc(dec_output)
return output

不同深度学习框架下的Transformer模块
深度学习框架 函数 支持平台
Pytorch torch.nn.Transformer() CPU/GPU/TPU/Ascend/AppleMetal
MindSpore mindspore.nn.Transformer() GPU/CPU/Ascend
Paddle paddle.nn.Transformer() CPU/GPU/A加速芯片(华为/海光/昆仑芯/寒武纪等)

可以进一步研究不同的深度学习框架对Transformer架构的实现方法之间的差别,和同一个深度学习框架在不同硬件平台上的实现差别和工作效率。

大语言模型分类(Transformer变体)

影响模型参数量的影响因素:Transformer层数、隐层维度、词向量维度、词典大小、输入token数
按照编码器和解密器分类:

只有编码器(encoder-only):自编码模型(Auto-encoder model),即先通过某种方式破坏句子,希望模型将被破坏的部分还原。需要从输入序列中提取有意义的上下文信息。
BERT(Bidirectional Encoder Representation from Transformers):训练时先基于大量未标注的语料库进行自监督学习(预训练),然后基于标注良好的数据集进行微调,其核心在自监督训练策略。包括Mask LM(掩码语言模型,Mask Language Model,随机将输入序列中的一些词汇遮挡或者随机替换为其他词,让模型预测被遮挡的词)和NSP(随机从语料库中抽取两个句子判断是否连续)。是双向模型,训练时需要利用上下文信息,各位复杂。BERT是自编码模型,需要经过预训练和微调两个阶段学习文本表示,只能完成特定的NLP任务。

只有解码器(decoder-only):自回归模型(Auto-regressive model),给出上文,预测下文。允许编码器聚焦于输入序列的相关部分,同时生成输出序列的每个词。
GPT(Generative Pretained Transformer):也使用Pre-training + Fine-tuning模式.是单向模型,不需要利用上下文信息,只能利用上文。GPT是基于自回归的模型,能够进行文本生成,可以利用Prompt应用到NLP任务中。

编码解码器混合(encoder decoder hybrid):理解输入的内容NLU,又能处理并生成内容NLG,特别是处理输入和输出序列之间存在复杂映射关系的任务,以及捕捉两个序列中元素之间关系至关重要的任务。
T5(Tranfer Text-to-Text Transformer):所有NLP任务都可以转化为Text-to-Text任务。

参考文献

[1] Attention is All You Need
[2] Transformer论文逐段精读【论文精读】
[3] 菜鸟教程
[4]Tensor2Tensor


文章作者: 青山生柳
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 青山生柳 !
 上一篇
大模型微调(Tuning)实践-基础篇 大模型微调(Tuning)实践-基础篇
大模型微调(Tuning)实践-基础篇,主要介绍大模型微调(Tuning)、微调分类、当前主要的参数高效微调方法(LoRA等)、微调数据准备、大模型能力评估指标、训练开发流程和微调开发流程和参考文献等。
2025-10-01
下一篇 
AI应用实践-智能BI(智能问数与智能数据分析) AI应用实践-智能BI(智能问数与智能数据分析)
AI应用实践-智能BI(智能问数与智能数据分析),主要介绍智能BI,包括商业智能(BI)定义、历史发展、智能BI核心技术、系统架构设计、关键技术、产品设计要点、设计实施过程、优秀实践产品、产品实践案例、当前技术挑战、长远设想和个人思考。
2025-09-18
  目录