大模型Transformer入门(2)-演算过程

前言

        Transformer是许多大模型的基础模型,理解Transformer是学习大模型原理一个很好的开始。目前的资料分散且不容易理解,本文通过简单通俗的方式描写,希望即使是算法小白也能够了解。本文分为三部分,包括零模型理解、算法演算、微调应用。

文字接龙

        简单来说Transformer模型就像一个文字接龙游戏,输入前边的文字机器会给你接上后边的文字。在这个过程中,机器通过训练好的模型计算找到最有可能的输出,再将输出在作为输入计算下一个输出,如此反复直到结束。

        Transformer模型是一个seq2seq(序列转序列)模型,任何能够把输入和输出转化为序列的模型都能够使用。文字可以,语音可以、图片视频也可以。

什么是模型

        机器学习为什么称之为学习是因为模拟了人类学习的过程。人类学习的过程就是学习、做题、纠错,再做题,直到最终达到满意分数,通过大脑掌握知识。机器学习的过程类似,只不过我们最终得到的是复合函数,以及相关的最优的参数集合。所以说机器学习的过程就是通过训练寻找最优复合函数及其参数的过程。

  • 正向传播就是做题的过程,
  • 损失计算、反向传播就是根据分数巩固知识,纠错的过程。

        本文以中英文翻译场景为例,模拟Transformer模型训练和推理的过程。在训练中,中文语料是“大语言模型”,英文语料是“Large Language Models “。在推理过程中,模拟输入中文“大语言模型”,输出英文“Large Language Models “。

参数设定

        以 transformer-pytorch 项目中英文翻译场景为例,模拟transformer的训练和推理过程。以下是参数情况。

训练和推理过程

        如上图所示,两个橙色部分“训练语料库”和“模型参数集合θ”,总的来说,训练的核心通过准备好的“训练语料库”,训练出我们需要的“参数集合θ”。大体可分为两个阶段,正向传播和反相传播。

  • 正向传播:通过计算语料库中的“源语言句子”和“目标语言真实标签句子”产生“预测词的词汇概率表”;
  • 损失计算:通过损失函数计算出“真实标签序列概率分布矩阵”和“预测序列概率分布矩阵”之间的交叉熵损失值;
  • 反向传播:基于损失值,通过链式法则反向更新模型参数;
  • 如此反复直到达到我们满意的结果。

如上图所示,无论编码器还是解码器,都能很好的并行计算性能。

        如上图,训练过程时对准备好的语料库训练,在解码过程中输入真实标签数据。而推理,是基于上一个产生的“预测词”作为输入推理新的“预测词”,如此反复直到结束。

数据准备

案例项目中,数据主要包含在4个文件内,即cn.txt, en.txt, cn.txt.vocab.tsv, en.txt.vocab.tsv。

1、数据收集与预处理

  • 收集数据:首先,需要大量双语(中文-英文)语料库,本例中语料来自2000年左右的中国新闻。
  • 文本清洗:去除无关符号、数字、标点(除非它们对翻译有特殊意义),统一文本格式,如全部转为小写(英文部分)。
  • 中文分词:中文文本需要进行分词,常用的工具有jieba分词等,将句子切分为词语序列;
  • 英文分词:英文一般按空格自然分词,但可能也需要处理一些特殊情况,如连写词、缩写等。

2、构建词汇表

  • 频率统计:统计每种语言中单词出现的频率。高频词将直接进入词汇表,而低频词可能被替换为UNK(未知词)标记。
  • 制定词汇表大小:基于资源和模型需求决定词汇表的大小。大型词汇表能覆盖更多词汇,但也会增加模型复杂度和计算成本;小型词汇表则相反。
  • 特殊标记:除了正常词汇,词汇表还应包含开始符号<S>、结束符号</S>、<PAD>、<UNK>等特殊标记。
  • 词汇映射:为词汇表中的每个词分配一个唯一的整数索引 ID,这将用于后续模型训练中的输入和输出表示。上述词汇表中词汇的顺序即为词的正数索引。

例子格式如下:

<PAD>	1000000000
<UNK>	1000000000
<S>	1000000000
</S>	1000000000
的	8461
是	2047
和	1836
在	1784
了	1426
中国	1336
<PAD>	1000000000
<UNK>	1000000000
<S>	1000000000
</S>	1000000000
the	13680
and	6845
of	6259
to	4292
in	3745
a	2467

模型参数

参数集合

  • 词嵌入层(可训练可使用开源):词嵌入矩阵E_{4000\times512}通常被随机初始化,其中 4000 是词汇表的大小,512是嵌入向量的维度。
  • 位置编码层(可训练可固定):位置编码参数矩阵W^P_{50\times512} ,其中 50 是序列的最大长度,512 是嵌入向量的维度。
  • 编码器层堆, i代表堆层序号,本例中有6层
    • 多头注意力子层(线性层+注意力计算+线性层)): 参数矩阵W1^{i}_{512\times512}W2^{i}_{512\times512}
    • 层归一化:尺度参数(gamma,\gamma 1^i)、偏移参数(beta,\beta1^i
    • 前馈网络子层(线性层+Relu+线性层)): 参数矩阵W3^{i}_{512\times2048}B3_{2048\times1}W4^{i}_{2048\times512}B4_{512\times1}
    • 层归一化:尺度参数(gamma,\gamma 2^i)、偏移参数(beta,\beta2^i
  • 解码器层堆, i代表堆层序号,本例中有6层
    • 掩码多头注意力子层(线性层+注意力计算+线性层)): 参数矩阵W5^{i}_{512\times512}W6^{i}_{512\times512}
    • 层归一化:尺度参数(gamma,\gamma 3^i)、偏移参数(beta,\beta3^i
    • 编码器解码器多头注意力子层(线性层+注意力计算+线性层)): 参数矩阵
    • 层归一化:尺度参数(gamma,\gamma 4^i)、偏移参数(beta,\beta4^i
    • 前馈网络子层参数(线性层+Relu+线性层)): 参数矩阵
    • 层归一化:尺度参数(gamma,\gamma 5^i)、偏移参数(beta,\beta5^i
  • 线性层参数:(线性层):参数矩阵W11_{512\times2048}

参数量计算

        以GPT-3 的前馈层为例,如上图,隐藏层中有 49,152 个神经元,每个神经元有 12,288 个输入。同时有12,288 个输出神经元,每个神经元有 49,152 个输入值。这意味着每个前馈层都有 49,152 * 12,288 + 12,288 * 49,152 = 12 亿个权重参数。GPT-3 有 96 个前馈层,总共有 12 亿 * 96 = 1160 亿个参数!这几乎占 GPT-3 总共 1750 亿个参数的三分之二。

前向传播

已知:

  • 训练源语言句子:“大语言模型”
  • 训练目标语言真实标签句子:“<S>  large  language model  </S>”
  • 编码器输入矩阵: X_{50\times512}
  • 编码器输出矩阵: O^E_{50\times512}
  • 解码器输入矩阵: Y_{50\times512}
  • 解码器输出向量: O^D_{50\times512}
  • 预测序列概率分布矩阵: P_{50\times11370}
  • 真实标签序列概率分布矩阵: Q_{50\times11370}
  • 模型参数集合: \theta 

输入编码

对源语言句子和目标语言句子进行编码。

  • 首先,将每个词转换为其在词汇表中的ID。 输入序列的最大长度50。超过这个长度的序列会被截断,低于这个长度的序列会被填充。填充数据是通过掩码和其他机制来确保模型能够正确地忽略无效信息,同时保持输入数据的一致性和模型的高效训练。
  • 其次,为源语言句子每个词ID生成词嵌入向量,并加上位置编码向量,形成编码器的输入。
  • 最后,为目标语言句子的每个词ID生成词嵌入向量,并加上位置编码向量,形成编码器的输入。

词嵌入

        假设我们已经有一个训练好的词嵌入模型库,该步骤是“大语言模型”这句话进行分词,并输出词512维的向量集合,可表示为3行512列的矩阵X

大    \mathbf{X0} = \begin{pmatrix} x_{01 }& x_{02} & x_{03 } & x_{04 } & x_{05 } & ···& x_{0511} \end{pmatrix}

语言 \mathbf{X1} = \begin{pmatrix} x_{11 }& x_{12} & x_{13 } & x_{14 } & x_{15 } & ···& x_{1511} \end{pmatrix}

模型 \mathbf{X2} = \begin{pmatrix} x_{21 }& x_{22} & x_{23 } & x_{24 } & x_{25 } & ···& x_{2511} \end{pmatrix}

        “大语言模型”表示的编码器输入矩阵: ,前三个词向量是“大语言模型”对应的词向量,后边的数据是用<PAD>填补的词向量数据。

\mathbf{X}_{50\times512} = \begin{pmatrix} X0\\ X1 \\ \vdots\\ X49 \end{pmatrix} = \begin{pmatrix} x_{01} & x_{02} & x_{03}& ··· & x_{0511}\\ x_{11} & x_{12} & x_{13}& ··· & x_{1511} \\ \vdots\\ x_{491} & x_{492} & x_{493} & ··· & x_{49511} \end{pmatrix}

        同理,解码器“<S>  large  language model  </S>”表示的解码器输入矩阵: 

\mathbf{Y}_{50\times512} = \begin{pmatrix} Y0\\ Y1 \\ \vdots\\ Y49 \end{pmatrix} = \begin{pmatrix} y_{01} & y_{02} & y_{03} & ··· & y_{0511}\\ y_{11} & x_{12} & x_{13} & ··· & y_{1511} \\ \vdots\\ y_{491} & x_{492} & x_{493} & ··· & y_{49511} \end{pmatrix}

位置编码

        正弦和余弦函数位置编码是一种在神经网络中常用的嵌入技术,尤其是在Transformer架构中,用于给输入序列中的每个位置提供一个独特的、周期性的表示。这种表示方式可以帮助模型理解序列中元素的位置关系,而不仅仅是它们的值或者内容。

        正弦和余弦函数位置编码是基于数学公式预先计算的,它不随训练过程更新,也就是说,在训练Transformer模型时,位置编码是固定的。这种设计既保持了模型对位置信息的敏感性,又避免了额外的训练负担。

公式:对于一个序列中的第 pos 个位置,其位置编码可以定义为一个向量,其中每个维度 i 可以通过正弦或余弦函数来计算

PE(pos,2i)=sin(\frac{pos}{10000^\frac{2i}{d}})

PE(pos,2i+1)=cos(\frac{pos}{10000^\frac{2i}{d}})

  • pos是位置的索引(从0开始)
  • i是维度的索引(从0开始)
  • 𝑑 是嵌入空间的维度(向量的长度),本例中词向量长度为12
  • 常数 10000 是一个缩放因子,用来控制不同维度上的周期性

第一步:计算𝑚𝑎𝑥𝑙𝑒𝑛为50的正弦和余弦函数位置编码矩阵P

        以此类推,会生成包含50个位置向量的固定位置编码表,在模型计算过程中直接使用。生成的位置向量和词向量维度相同,位置向量和词向量相加组成了包含位置信息的的新的向量集合,用来进一步的计算。即:

\mathbf{P} _{50\times 512}= \begin{pmatrix} P0\\ P1 \\ P2\\ \vdots\\ P511 \end{pmatrix}

第二步:计算词矩阵X、Y与位置矩阵相加的结果

\mathbf{X}_{50\times 512} = \begin{pmatrix} X0+P0\\ X1+P1 \\ \vdots\\ X50+Pi \end{pmatrix}             \mathbf{Y}_{50\times 512} = \begin{pmatrix} Y0+P0\\ Y1+P1 \\ \vdots\\ Y50+Pi \end{pmatrix}

至此,输入编码完成。

编码器

多头注意力子层(MHA)

        注意力机制的核心是为输入数据的不同部分分配不同的权重,这些权重反映了模型认为这些部分对于当前任务的重要程度。通过这种方式,模型能够专注于关键信息,忽略或减少对不相关信息的依赖,从而提高计算效率和任务表现。

        在自注意力(Self-Attention)机制中,这种关联性是通过各个词向量的查询(Query)、键(Key)、值(Value)三元组的相互作用来实现的:

  • 查询(Query):代表模型当前关注的内容或信息需求。
  • 键(Key):用于与查询进行匹配,以衡量序列中各部分与当前查询的相关度。         
  • 值(Value):包含实际需要根据查询的重要性加权聚合的信息。

        通过计算查询Q与各个键向量K之间的相似度(通常为点积),模型可以得到一个注意力分布,该分布表明了在给定查询下,输入序列中各部分的相对重要性。然后,利用这个分布对值向量进行加权求和,得到的加权和向量即为注意力机制的输出,该输出反映了模型如何“聚焦”于输入序列的关键部分。

 如上图流程,多头注意力处理,需要做两次线性变化,第一次线性变化,得到查询(Query)、键(Key)、值(Value)三元组计算权重参数矩阵集合;第二次线性变化,得到该层最终处理结果;

已知:

  • 编码器输入矩阵:X_{50\times512} ,意味着有50个样本,每个样本包含512个特征
  • 注意力头数量:8个头,每个头特征维度为 64
  • 查询(Query)、键(Key)、值(Value)三元组计算权重参数矩阵集合:W^Q_{512\times 512}W^K_{512\times 512}W^V_{512\times 512}
  • 多头输出融合权重参数矩阵:W^O_{512\times 512}

模拟多头自注意力子层计算过程。

第一步:线性变化:得到查询矩阵Q_{50\times 512}, 键矩阵K_{50\times 512}, 值矩阵V_{50\times 512}

第二步:分割头

以查询矩阵为例,它的尺寸为 50×512,根据头的数量将按照64维度均匀分割成8个查询矩阵同理分割和。       

表示为8组矩阵集合,分别代表每个头有Q、K、V三个矩阵。

第三步:注意力计算

1、对于每个头,分别通过点积计算注意力分数Score_{50\times 50},i表示多头的顺序号

Score^i_{50\times 50}=\frac{Q^i_{50\times 64},K_{64\times 50}^{iT}}{\sqrt{d_k}}

2、使用Softmax函数转化为注意力权重Weight^i_{3\times 3},i表示多头的顺序号

Weight^i_{50\times 50}=softmax(Score^i_{50\times 50})

3、将每个头的“注意力权重矩阵”与其“值矩阵”相乘,得到加权后的输出矩阵Output^i_{50\times 64},i表示多头的顺序号

Output^i_{50\times 64}=Weight^i_{50\times 50}·V^i_{50\times 64}

第四步:多头融合得到输出

        将多个头处理后的加权输出进行融合,得到一个加权后和词向量维度一致的,加权输出矩阵

Output_{50\times 512}=Concat(Output^0_{50\times 64},Output^1_{50\times 64},Output^2_{50\times 64},···,Output^{7}_{50\times 64})

以上数学公式表达不够严谨,表示为通过每个头的输出依次拼接,组成最终的输出。

第五步:线性变换:多头融合再经过一次线性变换,得到该层最终处理结果

Output_{50\times 512}=Output_{50\times 512}·W^O_{512\times 512}

残差链接&层归一化(Add&Norm)

        在多头注意力子层之后,通过残差连接将原始输入与注意力输出相加,保持了信息的连续性并有助于梯度流动,随后的层归一化处理稳定了训练过程,使模型对训练数据的分布变化更加鲁棒。这个过程在Transformer的每一层中反复进行,不仅限于多头注意力层,还包括后续的前馈网络层等,以构建起强大的序列建模能力。

第一步:残差链接

        残差连接的目的是将当前层的输出 与未经该层变换的输入直接相加。公式如下:

Y=Z+X

即:

AddOutput_{50\times 512}=Output_{50\times 512}+X_{50\times 512}

第二步:归一化

        层归一化(LayerNorm)目的是为了加速训练过程并提高模型性能,其对每个样本的每个特征维度独立进行,公式如下:

其中:

  • Y(即AddOutput_{3\times 12})是残差连接后的输出矩阵,即3个样本,12个特征。
  •  \mu_Y是 在每个样本上所有特征维度上的均值,每个样本会有12个均值。
  • \sigma_Y是 在每个样本上所有特征维度上的标准差,同样每个样本会有12个标准差。
  • \gamma和 \beta是可学习的缩放和偏移参数,形状为 1×512,分别用于重新缩放和偏移归一化后的输出,以维持网络的表达能力。

 即

前馈网络子层(FFN)

        前馈网络子层的第一层会将输入向量映射到更高维度的空间,然后第二层再将其映射回原始维度或另一指定维度。这种“升维-非线性变换-降维”的设计,有助于模型学习到更丰富的表示。同样前馈网络子层通过引入非线性,增强模型表达能力,对自注意力层输出的特征进行深度加工,有效提升模型捕捉复杂模式的能力。

第一步:输入映射

        首先,前馈网络会对多头注意力的输出矩阵AttentionOutput_{50\times 512}应用一个线性变换(权重矩阵乘法),用于增加模型的表达能力。通过一个权重矩阵 W^{F1}_{512\times 2048}(模型维度增加四倍)和偏置项 b^1_{2048\times1},得到:

Z_{50\times2048}=AttentionOutput_{50\times 512}\cdot W^F_{512\times 2048}+b^1_{2048\times1}

        权重矩阵和偏置向量的相加利用了“广播机制”(Broadcasting)。广播机制是一种在不同形状的数组之间执行数学运算的规则,

第二步:非线性激活

        接下来应用一个非线性激活函数,如ReLU、Tanh或GELU等,来引入非线性特性。这有助于模型学习更复杂的模式。例如,如果使用ReLU函数,则有:

H_{50\times 2048}=ReLU(Z_{50 \times 2048})

第三步:输出映射

        最后,再进行一次线性变换,将中间的高维表示映射回需要的输出维度,这可能与输入维度相同或不同,取决于模型的具体设计。使用另一个权重矩阵 ​W^{F2}_{2048\times 512} (从 64 维映射回到 12 维)和偏置项 b^2_{512\times1},得到最终输出 :

O_{50\times{512}}=H_{50\times{2048}}\cdot W^{F2}_{2048\times 512}+b^2_{512\times1}

        权重矩阵和偏置向量的相加利用了“广播机制”(Broadcasting)。广播机制是一种在不同形状的数组之间执行数学运算的规则。

残差链接&层归一化(Add&Norm)

        同多头注意力子层后的操作,在前馈网络子层之后,同样通过残差连接将原始输入与注意力输出相加,保持了信息的连续性并有助于梯度流动,随后的层归一化处理稳定了训练过程,使模型对训练数据的分布变化更加鲁棒。

重复执行

经过反复执行编码器堆层6次,得到最终编码器输出矩阵O^E_{50\times512},用做后续解码器使用。

O^E_{50\times512}

解码器

        解码器负责使用“编码器输入”和“解码器输入”,即语料库中的“源语言句子”和“目标语言真实标签句子”进行预测输出,训练和推理过程有差异。

        在训练阶段会把“目标语言真实标签句子”一次性作为编码器输入进行处理,最终得到“预测词的词汇概率表”。而在推理阶段解码器会用“开始符号<S>”开始,而后逐个词作为输入进行预测下一个词,如此反复,直到遇到“结束符号</S>”完成推理。

掩码多头注意力子层(MMHA)

        掩码多头注意力子层,该子层的核心是为解码器输入数据的不同部分分配不同的权重。同时引入掩码机制,确保在模型训练过程中生成下一个词时,模型只能看到过去的词,而不能看到当前词或未来的词。

        具体来说使用的是未来掩码(Future Masking),即在当前时间步的计算中,对后续时间步的位置赋予非常小的值(通常是负无穷),使得这些位置在经过softmax函数后得到的权重接近于0,从而被忽略。

已知:

  • 编码器输入矩阵: X_{50\times512}
  • 解码器输入矩阵: Y_{50\times512}
  • 注意力头数量:8个头,每个头特征维度为 64
  • 查询(Query)、键(Key)、值(Value)三元组计算权重参数矩阵集合:W^Q_{512\times 512}W^K_{512\times 512}W^V_{512\times 512}
  • 多头输出融合权重参数矩阵:W^O_{512\times 512}

假设,现在要训练第三个词“language”。以下是模拟掩码自注意力子层计算过程。

第一步:线性变化:得到查询矩阵Q_{512} , 键矩阵K_{512}  ,值矩阵V_{512}(同编码器多头注意力子层处理)

        设T_{3\times512}= \begin{pmatrix} Y^0\\Y^1\\Y^2 \end{pmatrix}  ,即已依次完成Y^0 Y^1前两个词训练,计算当前词Y^2相关的Q、K、V值。其中Y^0 Y^1 Y^2均为真实标签数据。

Q_{ 3\times64}=T_{3\times512}\cdot W^Q_{512\times 512}

K_{ 3\times64}=T_{3\times512}\cdot W^K_{512\times 512}

V_{ 3\times64}=T_{3\times512}\cdot W^V_{512\times 512}

第二步:分割头(同编码器多头注意力子层处理)

        以查询矩阵为例,根据头的数量,按照64维度分割成8个查询矩阵Q^0_{64}Q^1_{64}、```、Q^7_{ 64}同理分割和。

 \begin{pmatrix} Q^0_{3\times64}\\ K^0_{50\times64}\\ V^0_{50\times64} \end{pmatrix}\mathbf\in {H^0}           \begin{pmatrix} Q^1_{3\times64}\\ K^1_{50\times64}\\ V^1_{50\times64} \end{pmatrix}\mathbf\in {H^1}             ·······              \begin{pmatrix} Q^7_{3\times64}\\ K^7_{50\times64}\\ V^7_{50\times64} \end{pmatrix}\mathbf\in {H^7}

表示为8组矩阵集合,分别代表每个头有Q、K、V三个矩阵。

第三步:注意力计算,此处有所不同,使用一种称为“未来掩码”(Future Masking)的技术,以确保预测的顺序性

1、注意力分数计算:对于每个头,分别通过查询矩阵Q^i_{ 64}和键矩阵K^i_{64}点积,计算注意力分数Score^i_{3\times 3},i表示多头的顺序号。

Score_{3\times 3}=\frac{Q^i_{3\times64},K_{64\times 3}^{iT}}{\sqrt{d_k}}\begin{pmatrix} s11& s12 & s13 \\ s21& s22 & s23 \\ s31& s32 & s33 \\ \end{pmatrix}

2、构建掩码矩阵:根据注意力分数矩阵或输入序列长度,构建一个尺寸为 3×3下三角掩码矩阵。掩码矩阵的上三角(不包括对角线)被设置为0,其余位置为-∞,以确保softmax后这些位置的权重接近于0。

M_{3\times3}=\begin{pmatrix} -\infty& 0 & 0 \\ -\infty & -\infty& 0 \\ -\infty& -\infty & -\infty \end{pmatrix}

3、结合掩码:在计算注意力分数之后,将得到的分数矩阵与上述掩码矩阵逐元素相加。这意味着对于任何给定的时间步𝑡,当计算它对后续时间步的注意力权重时,由于相加了-∞,这些位置的权重实际上会被“掩蔽”,在应用后几乎为0。

Score_{3\times 3}=Score_{3\times 3}+M_{3\times3}=\begin{pmatrix} -\infty& s12 & s13 \\ -\infty & -\infty& s23\\ -\infty& -\infty & -\infty \end{pmatrix}

4、注意力权重计算:再使用Softmax函数,将结果转化为注意力权重Weight^i_{3\times 3},i表示多头的顺序号。

Weight^i_{3\times 3}=softmax(Score^i_{3\times 3})=\begin{pmatrix} 0& w12& w13\\ 0&0& w23 \\ 0& 0 & 0 \end{pmatrix}

5、通过将每个头的注意力权重矩阵与其值矩阵V相乘、加权后,计算出输出矩阵,i表示多头的顺序号。

Output^i_{3\times 4}=Weight^i_{3\times 3}·V^i_{3\times 64}=\begin{pmatrix} o11& o12& o13&···&o064\\ o21&o21& o23&···&o164 \\ 0& 0 & 0&···&0 \\ \end{pmatrix}

我们看到,通过掩码我们屏蔽了要预测的数据。

第四步:多头融合

        由于我们使用了多个头,每个头都会产生自己的输出矩阵。这些输出矩阵通常会被拼接(concatenated)起来形成一个更宽的矩阵,以包含来自不同注意力视角的信息。对于我们的简化模型,如果每个头处理4个特征,且有3个头,则拼接后的输出维度应为3×12。

Output_{3\times 512}=Concat(Output^0_{3\times 64},Output^1_{3\times 64},···,Output^{63}_{3\times 64})=\begin{pmatrix} o11& o12&···& o1512\\ o21&o21& ···&o2512\\ 0& 0 & ···&0 \end{pmatrix}

通过掩码技术,在预测第三个token的时候,屏蔽了训练数据的已知信息

以上数学公式表达不够严谨,可以理解为最终的输出是由每个头的输出依次拼接组成。

第五步:线性变换:多头融合再经过一次线性变换,得到该层最终处理结果

Output_{50\times 512}=Output_{50\times 512}·W^O_{512\times 512}+b^O_{512\times1}

残差链接&层归一化(Add&Norm)

        同编码器相关操作,同样通过残差连接将原始输入与注意力输出相加,保持了信息的连续性并有助于梯度流动,随后的层归一化处理稳定了训练过程,使模型对训练数据的分布变化更加鲁棒。

        当在掩码多头注意力子层之后应用残差连接时,会将MHA的输出(已经根据掩码规则处理过,不包含“未来”信息)与输入(同样遵循相同掩码规则处理过的前一层输出)相加。这里的关键点是,不论是MHA的输入还是输出,都已经在校正过程中考虑了相同的掩码条件。得到:

AddOutput_{3\times 512}=Output_{3\times 512}+T_{3\times 512}

MAttentionOutput_{3\times 512}=\gamma \cdot ( \frac{AddOutput_{1\times 12}- \mu_{AddOutput}}{\sigma_{AddOutput}} )+\beta

编码器-解码器多头注意力子层(Enc-Dec MHA)

        编码器-解码器多头注意力子层,用于实现解码器对编码器输出信息的依赖和整合。它帮助解码器在生成目标序列的每个部分时,考虑由编码器处理的全局上下文。

        简单来说,就是在多头注意力计算时,使用解码器的输出(Query)与编码器的输出(Key和Value)进行注意力计算,从而实现解码器对编码器输出信息的依赖和整合。

已知:

  • 编码器输入矩阵: X_{50\times512}
  • 解码器输入矩阵: Y_{50\times512}
  • 注意力头数量:8个头,每个头特征维度为 64
  • 查询(Query)、键(Key)、值(Value)三元组计算权重参数矩阵集合:W^{Qdec}_{512\times512} W^{Kenc}_{512\times512} W^{Venc}_{512\times512}
  • 多头输出融合权重参数矩阵:W^O_{512\times 512}

求,模拟编码器-解码器多头注意力子层的算法过程。

第一步:线性变化

  • 解码器侧:通过“掩码自注意力子层”的输出AttentionOutput^1_{3\times5 12},即新输入词汇“language”产生的向量来生成查询权重向量 

Q_{1\times512}=AttentionOutput^1_{1\times5 12}·W^{Qdec}_{512\times512}

  • 编码器侧:通过编码器输入矩阵 ,计算编码器输出的键矩阵和值矩阵 

K_{50\times512}=O^E_{50\times512}·W^{Kdec}_{50\times512}

V_{50\times512}=O^E_{50\times512}·W^{Venc}_{512\times512}

第二步:分割头

以查询矩阵为例,根据头的数量,按照64维度分割成8个查询矩阵同理分割和

.   \begin{pmatrix} Q^0_{3\times64}\\ K^0_{50\times64}\\ V^0_{50\times64} \end{pmatrix}\mathbf\in {H^0}           \begin{pmatrix} Q^1_{3\times64}\\ K^1_{50\times64}\\ V^1_{50\times64} \end{pmatrix}\mathbf\in {H^1}             ·······              \begin{pmatrix} Q^7_{3\times64}\\ K^7_{50\times64}\\ V^7_{50\times64} \end{pmatrix}\mathbf\in {H^7}             

表示为8组矩阵集合,分别代表每个头有Q、K、V三个矩阵。

第三步:注意力计算

1、对于每个头,分别通过点积计算注意力分数Score^i_{1\times 50},i表示多头的顺序号

Score^i_{1\times 50}=\frac{Q^i_{1\times 64},K_{64\times 50}^{iT}}{\sqrt{d_k}}

2、再使用Softmax函数转化为注意力权重Weight^i_{3\times 3},i表示多头的顺序号

Weight^i_{1\times 50}=softmax(Score^i_{1\times 50})

3、通过将每个头的注意力权重矩阵与其值矩阵相乘,得到加权后的输出矩阵Output^i_{3\times 64},i表示多头的顺序号

Output^i_{1\times 64}=Weight^i_{1\times 50}·V^i_{50\times 64}

第四步:多头融合

        由于我们使用了多个头,每个头都会产生自己的输出矩阵。这些输出矩阵通常会被拼接(concatenated)起来形成一个更宽的矩阵,以包含来自不同注意力视角的信息。对于我们的简化模型,如果每个头处理4个特征,且有3个头,则拼接后的输出维度应为3×12。

Output_{1\times 512}=Concat(Output^0_{1\times 64},Output^1_{1\times 64},···,Output^63_{1\times 64})

第五步:线性变换:多头融合再经过一次线性变换,得到该层最终处理结果

Output_{1\times 512}=Output_{1\times 512}·W^O_{512\times 512}

残差链接&层归一化(Add&Norm)

        同编码器相关操作,同样通过残差连接将原始输入与注意力输出相加,保持了信息的连续性并有助于梯度流动,随后的层归一化处理稳定了训练过程,使模型对训练数据的分布变化更加鲁棒。

前馈网络子层(FFN)

        同编码器相关操作,前馈网络的第一层会将输入向量映射到更高维度的空间,然后第二层再将其映射回原始维度或另一指定维度。这种“升维-非线性变换-降维”的设计,有助于模型学习到更丰富的表示。同样前馈网络子层通过引入非线性,增强模型表达能力,对自注意力层输出的特征进行深度加工,有效提升模型捕捉复杂模式的能力。

残差链接&层归一化(Add&Norm)

        同编码器相关操作,同样通过残差连接将原始输入与注意力输出相加,保持了信息的连续性并有助于梯度流动,随后的层归一化处理稳定了训练过程,使模型对训练数据的分布变化更加鲁棒。

重复执行

经过反复执行解码器堆层6次,得到最终解码器输出矩阵O^D_{1\times512},用做后续解码器使用。

O^D_{1\times512}

输出转化

        负责将“解码器处理得到的结果”转化为“词汇预测概率表”。其中线性层负责将这些高维、抽象的隐藏状态映射到目标词汇表的维度。再经过分类概率函数Softmax处理,转变为词汇预测概率。通常会选择概率最高的作为预测内容,预测内容再作为解码器的输入,如此反复,直到遇到结束标记。

线性层&分类概率处理(SoftMax)

线性层(也称为全连接层或稠密层)的计算过程可以概括为以下步骤:

第一步:权重矩阵乘法:解码器的输出向量记为O^D_{1\times512} ,线性层有一个预先学习到的权重矩阵W ,其维度通常是d_{model} \times vocab_{size},其中 是词向量维度即512, 是目标词汇表的大小即4000。

        将“输入向量”与“权重矩阵”进行矩阵乘法运算,将解码器的高维表示映射到目标词汇表的潜在表示空间。

Z_{1\times4000}=O^D_{1\times512}· W_{512\times4000}

第二步:偏置项相加:将上述结果与偏置向量相加,得到针对每个词汇的未归一化预测得分

Y_{1\times4000}=O^D_{1\times512}· W_{512\times4000}+b

第三步: 通过Softmax函数将分数转化为概率

Q_{1\times4000}=Softmax(Y_{1\times4000})=[0.1,0.03,0.07,1.7,0.05,-1.05]

最终得到预测的下一个输出在词汇表里的概率情况。

重复执行

        经过重复执行,完成目标语言真实标签句子“<S>  large  language model  </S>”的训练,得到各个词的词汇概率预测矩阵,即目标语言对应的5个词汇概率预测情况。

Q_{5\times4000}

损失计算

        损失函数会对这些“预测结果”和“真实的标签(或目标值)”进行比较,计算出一个总的损失值。接下来反向传播算法会使用这个损失值来计算模型参数的梯度,进而更新模型参数,目标是逐步减小损失,提高模型在训练数据上的表现,达到期望。

已知:

  • 预测序列概率分布矩阵:Q_{5\times4000}
  • 真实标签序列概率分布矩阵:P_{5\times4000},one-hot编码矩阵,即在对应的词上概率是1,其与位置概率是0

模拟通过交叉熵损失函数计算损失值的过程。

交叉熵损失函数定义

交叉熵损失函数衡量的是预测概率分布与真实概率分布之间的差异,其公式为:

L = -\frac{1}{N} \sum_{i=1}^{N} \sum_{j=1}^{C} p_{ij} \log(q_{ij})

其中:

  • (N)是样本数量(在这里是4000)。
  • (C)是类别数量(在这里是5,因为矩阵是5x4000的)。
  • (p_{ij})是真实标签概率分布矩阵 (P) 中第 (i) 个样本的第 (j) 个类别的概率。
  • (q_{ij})是预测概率分布矩阵 (Q) 中第 (i) 个样本的第 (j) 个类别的概率。

        这里的L是整个数据集上的平均交叉熵损失。这个公式的输出是一个标量值,表示了模型预测与真实标签之间的不匹配程度。这个值越小,说明模型的预测结果与真实标签越接近;反之,值越大,说明模型的预测结果与真实标签相差越远。

反向传播

梯度计算

        梯度计算是指计算模型参数调整的大小和方向,目的是为了优化模型,使损失函数最小化。梯度的数据结构与模型参数的数据结构相匹配,这是因为梯度本质上是损失函数相对于每个模型参数的偏导数。

        具体来说,如果你有一个模型参数\theta,那么梯度\bigtriangledown_\theta L就是损失函数关于参数的偏导数。这个梯度将告诉我们需要如何调整参数来减少损失。

Softmax层梯度

        假设我们有Softmax层的输出概率分布p,真实标签的one-hot编码为y,交叉熵损失函数L。

公式:

损失函数关于Softmax输出的梯度为:

\frac{\partial L}{\partial pi}=pi-qi

其中,是Softmax输出向量p的第i个元素,是真实标签向量y的第i个元素(注意y是one-hot编码的,所以只有一个元素为1,其余为0)。

计算过程:

  • 计算Softmax输出p。
  • 计算p与y的差值,得到梯度向量。

输出层梯度(线性层)

假设线性层的输出为z,权重为W_{out},偏置为b_{out},前一层输出为h。

公式:

  • 损失函数关于线性层输出的梯度(与Softmax层梯度相同):

\frac{\partial L}{\partial pi}=pi-qi

  • 损失函数关于线性层权重的梯度:

\frac{\partial L}{\partial W_{out_{ij}}} = \frac{\partial L}{\partial z_i} \cdot h_j

其中,W_{out_{ij}}是权重矩阵W_{out}的第i行第j列元素,是前一层输出h的第j个元素。

损失函数关于线性层偏置的梯度:

\frac{\partial L}{\partial b_{out_i}} = \sum_j \frac{\partial L}{\partial z_j}

其中,求和是对所有样本的输出单元进行的。

计算过程:

  • 使用Softmax层的梯度作为线性层输出的梯度。
  • 使用上述公式计算权重和偏置的梯度。

前馈神经网络层梯度

公式:

  • ReLU激活函数的导数:

\text{ReLU}'(x) = \begin{cases} 1, & \text{if } x > 0 \\ 0, & \text{otherwise} \end{cases}

  • 损失函数关于前馈层权重的梯度(假设有权重和偏置):

\frac{\partial L}{\partial W_{ff_{ij}}} = \frac{\partial L}{\partial a_i} \cdot \text{ReLU}'(x_i) \cdot h_j

其中,a_i是ReLU激活后的输出,x_i是ReLU激活前的输入(即线性层的输出),h_j是前一层输出。

损失函数关于前馈层偏置的梯度:

\frac{\partial L}{\partial b_{ff_i}} = \sum_j \frac{\partial L}{\partial a_j} \cdot \text{ReLU}'(x_j)

计算过程:

  • 计算ReLU激活函数的导数。
  • 使用上述公式计算权重和偏置的梯度。

多头注意力层梯度

由于多头注意力层的计算较为复杂,这里只给出大致的梯度计算思路。

公式(以自注意力机制为例):

        注意力权重A通常是通过Query(Q)、Key(K)和Value(V)的点积和softmax操作计算得到的:

A = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)

其中,d_k是Key的维度。

输出通常是注意力权重与Value的加权和:

\text{Output} = AV

梯度计算思路:

  • 从输出开始,计算损失函数关于输出的梯度。
  • 使用链式法则,计算损失函数关于注意力权重A的梯度。
  • 由于注意力权重A是通过softmax函数得到的,需要计算softmax函数的梯度。
  • 使用链式法则,继续反向传播到Query、Key和Value的权重矩阵。

        注意:由于多头注意力层包含多个“头”,每个头都有自己独立的Query、Key和Value权重矩阵,因此需要对每个头分别进行梯度计算。此外,多头注意力层还可能包含额外的线性层和激活函数,这些都需要考虑在内。

模型参数更新

        优化算法是深度学习训练流程中的关键要素,直接影响模型的训练速度、精度和泛化能力。在Transformer模型中,使用了Adam(Adaptive Moment Estimation)优化算法。

        Adam结合了动量(Momentum)和RMSprop的优点,能有效处理稀疏梯度和非平稳目标函数,非常适合深度学习模型的训练。

Adam优化算法

Adam算法的主要步骤如下:

  • 初始化:为模型参数\theta,初始化一阶矩估计m_t和二阶矩估计v_t,通常都初始化为0。
  • 计算梯度:在每个时间步t,计算损失函数相对于参数的梯度 \nabla_\theta L_t
  • 更新一阶矩估计:更新m_t,这是梯度的指数加权移动平均:

m_t = \beta_1 m_{t-1} + (1 - \beta_1) \nabla_\theta L_t,其中\beta_1通常是 0.9。

  • 更新二阶矩估计:更新v_t,这是梯度平方的指数加权移动平均:

v_t = \beta_2 v_{t-1} + (1 - \beta_2) (\nabla_\theta L_t)^2,其中\beta_2通常是 0.98。

  • 偏差校正:由于初始时刻的一阶和二阶矩估计偏向于0,需要进行偏差校正:

\hat{m}_t = \frac{m_t}{1 - \beta_1^t}, \quad \hat{v}_t = \frac{v_t}{1 - \beta_2^t}

  • 更新参数:最后,使用学习率和上述修正后的估计值来更新参数 :

\theta_{t+1} = \theta_t - \frac{\alpha}{\sqrt{\hat{v}_t} + \epsilon} \hat{m}_t

                   其中,\epsilon是一个很小的数(例如10^{-9}),用于防止除以零的情况。

学习率调度

        在训练Transformer模型时,学习率调度策略非常重要。一种广泛采用的学习率调度策略是“warm-up”和“decay”,即在训练开始时逐渐增加学习率(warm-up),之后逐渐降低学习率(decay)。这种策略有助于模型在训练初期快速学习,同时避免在后期学习率过高导致的震荡。

具体的学习率\alpha可以定义为:

\alpha = \alpha_{\text{peak}} \cdot \min\left(\frac{t}{t_{\text{warmup}}}, \frac{1}{\sqrt{t}}\right)

其中,\alpha_{\text{peak}}是峰值学习率,t是当前时间步,t_{\text{warmup}}是warm-up阶段的时间步数。

        通过Adam优化算法和适当的学习率调度策略,Transformer模型能在训练过程中高效地更新参数,以最小化损失函数,从而提高模型的预测性能。在实际应用中,这些超参数通常需要通过实验来调整,以达到最优的训练效果。

重复执行 训练循环

深度学习模型的训练过程可以概括为以下几步:

1、正向传播(Forward Propagation):

        输入数据通过模型的各层进行计算,每一层的输出作为下一层的输入,直到得到最终的预测结果。这一过程计算出模型的输出,用于与实际标签对比,进而计算损失函数的值。

2、计算损失(Compute Loss):

        比较模型预测的结果与实际标签,通过损失函数(如交叉熵损失、均方误差等)量化预测误差的大小。

3、反向传播(Backward Propagation):

        损失函数被计算出来,就会开始反向传播过程,即从输出层向输入层逐层计算损失函数关于各层参数的梯度。这一过程利用链式法则,将损失函数的梯度分解到每一个参数上,为下一步参数更新做准备。

4、参数更新(Parameter Update):

        有了损失函数关于参数的梯度后,就可以使用优化算法(如SGD、Adam等)来更新模型参数。优化算法根据梯度的大小和方向,调整参数值,目标是最小化损失函数。

5、重复训练(Iterative Training):

        上述过程在训练集上的每一批数据上都会重复执行,直到模型收敛或者达到预定的训练轮次。每次迭代后,模型参数都会得到更新,理论上模型的表现也会逐渐改进。

6、验证与测试(Validation and Testing):

        在训练过程中,通常还会在验证集上评估模型的性能,以便调整超参数或提前终止训练。最终,模型会在测试集上进行评估,以检查模型的泛化能力。

        整个训练过程就是这样一个循环迭代的过程,通过不断地调整模型参数,使得模型能够从数据中学习到有用的特征,并最终达到较好的预测性能。

引用

1、transformer-pytorch

2、Large language models, explained with a minimum of math and jargon

3、李宏毅_Transformer

4、Attention Is All You Need

  • 25
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值