LLM系列:4.transformer算法:1.Transformer原理(下)

④.编码器-解码器注意力层(Encoder-Decoder Attention)

这一层通常被称为交叉注意力层(Cross-Attention)询问矩阵 QQQ 来源于 Decoder 的上一层输出,而应答矩阵 KKK 与信息矩阵 VVV 均来自于 Encoder 端输出的深层特征。 这让 Decoder 在每一次输出时,都能够紧密且动态地"盯"住原输入语句中最值得关注的信息重点。

在Transformer模型的解码器部分,编码器-解码器注意力层(通常称为“交叉注意力”层)起着至关重要的作用。这一层允许解码器的每个位置访问整个编码器的输出,这对于将输入序列的上下文信息整合到输出序列的生成中是必需的。这个层的设计是为了确保解码器能够基于完整的输入序列信息来生成每个输出元素。

首先,编码器-解码器注意力层的输入是来自多头注意力机制的输出结果。从Decoder的掩码注意力层中输出的是经过掩码后、每一行只携带特定时间段信息的结果CdecoderC_{decoder}Cdecoder(每一列对应不同特征维度):

Cdecoder=[a11v1a11v1…a11v1a21v1+a22v2a21v1+a22v2…a21v1+a22v2a31v1+a32v2+a33v3a31v1+a32v2+a33v3…a31v1+a32v2+a33v3a41v1+a42v2+a43v3+a44v4a41v1+a42v2+a43v3+a44v4…a41v1+a42v2+a43v2+a44v4] C_{decoder} = \begin{bmatrix} a_{11}v_{1} & a_{11}v_{1} & \ldots & a_{11}v_{1} \\ a_{21}v_{1} + a_{22}v_{2} & a_{21}v_{1} + a_{22}v_{2} & \ldots & a_{21}v_{1} + a_{22}v_{2} \\ a_{31}v_{1} + a_{32}v_{2} + a_{33}v_{3} & a_{31}v_{1} + a_{32}v_{2} + a_{33}v_{3} & \ldots & a_{31}v_{1} + a_{32}v_{2} + a_{33}v_{3} \\ a_{41}v_{1} + a_{42}v_{2} + a_{43}v_{3} + a_{44}v_{4} & a_{41}v_{1} + a_{42}v_{2} + a_{43}v_{3} + a_{44}v_{4} & \ldots & a_{41}v_{1} + a_{42}v_{2} + a_{43}v_{2} + a_{44}v_{4} \end{bmatrix} Cdecoder=a11v1a21v1+a22v2a31v1+a32v2+a33v3a41v1+a42v2+a43v3+a44v4a11v1a21v1+a22v2a31v1+a32v2+a33v3a41v1+a42v2+a43v3+a44v4a11v1a21v1+a22v2a31v1+a32v2+a33v3a41v1+a42v2+a43v2+a44v4
当我们使用覆盖的时间点来作为脚标,则有:

Cdecoder=[c1c1…c1c1→2c1→2…c1→2c1→3c1→3…c1→3c1→4c1→4…c1→4] C_{decoder} = \begin{bmatrix} c_{1} & c_{1} & \ldots & c_{1} \\ c_{1 \to 2} & c_{1 \to 2} & \ldots & c_{1 \to 2} \\ c_{1 \to 3} & c_{1 \to 3} & \ldots & c_{1 \to 3} \\ c_{1 \to 4} & c_{1 \to 4} & \ldots & c_{1 \to 4} \end{bmatrix} Cdecoder=c1c12c13c14c1c12c13c14c1c12c13c14

同样的,这里出于方便阅读,省略了特征维度上的脚标。现在你所看到的脚标只代表时间维度/序列长度的维度。

从Encoder中输出的是没有掩码的注意力机制结果CencoderC_{encoder}Cencoder,由于没有掩码,所以Encoder中的注意力分数为
A=[a11a12a13a14a21a22a23a24a31a32a33a34a41a42a43a44] \text{A} = \begin{bmatrix} a_{11} & a_{12} & a_{13} & a_{14} \\ a_{21} & a_{22} & a_{23} & a_{24} \\ a_{31} & a_{32} & a_{33} & a_{34} \\ a_{41} & a_{42} & a_{43} & a_{44} \end{bmatrix} A=a11a21a31a41a12a22a32a42a13a23a33a43a14a24a34a44

同时,V矩阵为(省略了特征维度,脚标代表的是时间点、seq_len的信息)
V=[v1v1…v1v2v2…v2v3v3…v3v4v4…v4] V = \begin{bmatrix} v_{1} & v_{1} & \ldots & v_{1} \\ v_{2} & v_{2} & \ldots & v_{2} \\ v_{3} & v_{3} & \ldots & v_{3} \\ v_{4} & v_{4} & \ldots & v_{4} \end{bmatrix} V=v1v2v3v4v1v2v3v4v1v2v3v4

由于Cencoder=A×V\text{C}_{\text{encoder}} = \text{A} \times VCencoder=A×V,因此最终的结果矩阵 Cencoder\text{C}_{\text{encoder}}Cencoder 是:
Cencoder=[a11⋅v1+a12⋅v2+a13⋅v3+a14⋅v4a11⋅v1+a12⋅v2+a13⋅v3+a14⋅v4…a11⋅v1+a12⋅v2+a13⋅v3+a14⋅v4a21⋅v1+a22⋅v2+a23⋅v3+a24⋅v4a21⋅v1+a22⋅v2+a23⋅v3+a24⋅v4…a21⋅v1+a22⋅v2+a23⋅v3+a24⋅v4a31⋅v1+a32⋅v2+a33⋅v3+a34⋅v4a31⋅v1+a32⋅v2+a33⋅v3+a34⋅v4…a31⋅v1+a32⋅v2+a33⋅v3+a34⋅v4a41⋅v1+a42⋅v2+a43⋅v3+a44⋅v4a41⋅v1+a42⋅v2+a43⋅v3+a44⋅v4…a41⋅v1+a42⋅v2+a43⋅v3+a44⋅v4] \text{C}_{\text{encoder}} = \begin{bmatrix} a_{11} \cdot v_1 + a_{12} \cdot v_2 + a_{13} \cdot v_3 + a_{14} \cdot v_4 & a_{11} \cdot v_1 + a_{12} \cdot v_2 + a_{13} \cdot v_3 + a_{14} \cdot v_4 & \ldots & a_{11} \cdot v_1 + a_{12} \cdot v_2 + a_{13} \cdot v_3 + a_{14} \cdot v_4 \\ a_{21} \cdot v_1 + a_{22} \cdot v_2 + a_{23} \cdot v_3 + a_{24} \cdot v_4 & a_{21} \cdot v_1 + a_{22} \cdot v_2 + a_{23} \cdot v_3 + a_{24} \cdot v_4 & \ldots & a_{21} \cdot v_1 + a_{22} \cdot v_2 + a_{23} \cdot v_3 + a_{24} \cdot v_4 \\ a_{31} \cdot v_1 + a_{32} \cdot v_2 + a_{33} \cdot v_3 + a_{34} \cdot v_4 & a_{31} \cdot v_1 + a_{32} \cdot v_2 + a_{33} \cdot v_3 + a_{34} \cdot v_4 & \ldots & a_{31} \cdot v_1 + a_{32} \cdot v_2 + a_{33} \cdot v_3 + a_{34} \cdot v_4 \\ a_{41} \cdot v_1 + a_{42} \cdot v_2 + a_{43} \cdot v_3 + a_{44} \cdot v_4 & a_{41} \cdot v_1 + a_{42} \cdot v_2 + a_{43} \cdot v_3 + a_{44} \cdot v_4 & \ldots & a_{41} \cdot v_1 + a_{42} \cdot v_2 + a_{43} \cdot v_3 + a_{44} \cdot v_4 \end{bmatrix} Cencoder=a11v1+a12v2+a13v3+a14v4a21v1+a22v2+a23v3+a24v4a31v1+a32v2+a33v3+a34v4a41v1+a42v2+a43v3+a44v4a11v1+a12v2+a13v3+a14v4a21v1+a22v2+a23v3+a24v4a31v1+a32v2+a33v3+a34v4a41v1+a42v2+a43v3+a44v4a11v1+a12v2+a13v3+a14v4a21v1+a22v2+a23v3+a24v4a31v1+a32v2+a33v3+a34v4a41v1+a42v2+a43v3+a44v4
同样的,当我们使用覆盖的时间点来作为脚标,则有:

Cencoder=[c1→4c1→4…c1→4c1→4c1→4…c1→4c1→4c1→4…c1→4c1→4c1→4…c1→4] C_{encoder} = \begin{bmatrix} c_{1 \to 4} & c_{1 \to 4} & \ldots & c_{1 \to 4} \\ c_{1 \to 4} & c_{1 \to 4} & \ldots & c_{1 \to 4} \\ c_{1 \to 4} & c_{1 \to 4} & \ldots & c_{1 \to 4} \\ c_{1 \to 4} & c_{1 \to 4} & \ldots & c_{1 \to 4} \end{bmatrix} Cencoder=c14c14c14c14c14c14c14c14c14c14c14c14
同样的,这里出于方便阅读,省略了特征维度上的脚标。现在你所看到的脚标只代表时间维度/序列长度的维度。事实上4列C虽然覆盖的时间维度一致,但却归属于不同的特征维度。

此时,CencoderC_{encoder}Cencoder携带的是特征矩阵X的信息,CencoderC_{encoder}Cencoder中的每个元素都携带了全部时间步上的信息;CdecoderC_{decoder}Cdecoder携带的是真实标签的信息,CdecoderC_{decoder}Cdecoder中的元素则是每一行代表了一段时间的信息,随着行数的增加这段时间窗口越来越长。编码器-解码器注意力层负责整合这两部分信息。具体来说,编码器解码器输出的结果结合的方式是将解码器中的标签信息CdecoderC_{decoder}Cdecoder作为Q矩阵,将编码器中输出的特征信息CencoderC_{encoder}Cencoder作为K和V矩阵,使用每行Q与全部的K、V相乘,来执行一种特殊的注意力机制。

这种特殊注意力机制的公式如下:

Context1=∑iAttention(Q1,Ki)×Vi\text{Context}_1 = \sum_{i} \text{Attention}(Q_1, K_i) \times V_iContext1=iAttention(Q1,Ki)×Vi

Context2=∑iAttention(Q2,Ki)×Vi\text{Context}_2 = \sum_{i} \text{Attention}(Q_2, K_i) \times V_iContext2=iAttention(Q2,Ki)×Vi

Context3=∑iAttention(Q3,Ki)×Vi\text{Context}_3 = \sum_{i} \text{Attention}(Q_3, K_i) \times V_iContext3=iAttention(Q3,Ki)×Vi

………………

在这个公式中,Q与K转置相乘的地方不再是点积、而是按全新的加和规则相乘相加——转换成矩阵则有,Cdecoder(Q)C_{decoder}(Q)Cdecoder(Q)的第一行乘以Cencoder(K.T)C_{encoder}(K.T)Cencoder(K.T)的第一列,加上Cdecoder(Q)C_{decoder}(Q)Cdecoder(Q)的第一行乘以Cencoder(K.T)C_{encoder}(K.T)Cencoder(K.T)的第二列,加上Cdecoder(Q)C_{decoder}(Q)Cdecoder(Q)的第一行乘以Cencoder(K.T)C_{encoder}(K.T)Cencoder(K.T)的第三列……直到所有的列都被乘完为止。

在这个公式中,QQQ 矩阵与 K⊤K^\topK (K的转置) 的相乘,完全遵循标准的矩阵乘法(即向量点积)。具体过程分为两步:

  1. 第一步:算相关性得分 (生成Attention权重)

    Cdecoder(Q)C_{decoder}(Q)Cdecoder(Q) 的第 iii 行(代表前 iii 个decoder输入融合后的自相关度),去分别与 Cencoder(K⊤)C_{encoder}(K^\top)Cencoder(K) 的每一列(代表每一个encoder输出)进行点积。算出来的结果不是相加,而是排成一个横向的向量(代表当前decoder输入与所有encoder输出之间的交叉相关度)。

    (记忆口诀:拿着decoder输入的自相关度,去和encoder输出算交叉相关度。)

  2. 第二步:按得分提取原文信息 (对 VVV 进行加权求和)

    公式中的 ∑\sum 发生在这里!拿着第一步算出来的得分向量,去对 Cencoder(V)C_{encoder}(V)Cencoder(V) 矩阵的每一行(即原文每个词的实际特征)进行加权求和。

总结: Cdecoder(Q)C_{decoder}(Q)Cdecoder(Q) 的第 iii 行,通过与整个 K⊤K^\topK 矩阵相乘,得到一个长度为 src_len 的得分向量;然后再用这个得分向量与整个 VVV 矩阵相乘,最终融合坍缩成了一行全新的特征向量 Contexti\text{Context}_iContexti。最终把所有 ContextContextContext 按行拼接(Stack)在一起,得到的交叉注意力层的完整输出矩阵 Output\text{Output}Output


计算 Context1\text{Context}_1Context1(标签中的第一个时间步,扫过特征中的所有时间步):

VVV 是给加权求和用的权重

Context1=([c1c1…c1c1→2c1→2…c1→2c1→3c1→3…c1→3c1→4c1→4…c1→4]⋅[c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4])⏟红行与第1列点积,得到 Softmax 权重×[v1v1…v1]⏟V矩阵的第1行特征Context_1 = \underbrace{\left( \begin{bmatrix} \color{red}{c_{1}} & \color{red}{c_{1}} & \ldots & \color{red}{c_{1}} \\ c_{1 \to 2} & c_{1 \to 2} & \ldots & c_{1 \to 2} \\ c_{1 \to 3} & c_{1 \to 3} & \ldots & c_{1 \to 3} \\ c_{1 \to 4} & c_{1 \to 4} & \ldots & c_{1 \to 4} \end{bmatrix} \cdot \begin{bmatrix} \color{red}{c_{1 \to 4}} & c_{1 \to 4} & c_{1 \to 4} & c_{1 \to 4} \\ \color{red}{c_{1 \to 4}} & c_{1 \to 4} & c_{1 \to 4} & c_{1 \to 4} \\ \color{red}{c_{1 \to 4}} & c_{1 \to 4} & c_{1 \to 4} & c_{1 \to 4} \\ \color{red}{c_{1 \to 4}} & c_{1 \to 4} & c_{1 \to 4} & c_{1 \to 4} \end{bmatrix} \right)}_{\text{红行与第1列点积,得到 Softmax 权重}} \times \underbrace{\begin{bmatrix} \color{blue}{v_{1}} & \color{blue}{v_{1}} & \ldots & \color{blue}{v_{1}} \end{bmatrix}}_{V矩阵的第1行特征}Context1=红行与第1列点积,得到 Softmax 权重c1c12c13c14c1c12c13c14c1c12c13c14c14c14c14c14c14c14c14c14c14c14c14c14c14c14c14c14×V矩阵的第1行特征[v1v1v1]

+++

([c1c1…c1c1→2c1→2…c1→2c1→3c1→3…c1→3c1→4c1→4…c1→4]⋅[c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4])⏟红行与第2列点积,得到 Softmax 权重×[v2v2…v2]⏟V矩阵的第2行特征\underbrace{\left( \begin{bmatrix} \color{red}{c_{1}} & \color{red}{c_{1}} & \ldots & \color{red}{c_{1}} \\ c_{1 \to 2} & c_{1 \to 2} & \ldots & c_{1 \to 2} \\ c_{1 \to 3} & c_{1 \to 3} & \ldots & c_{1 \to 3} \\ c_{1 \to 4} & c_{1 \to 4} & \ldots & c_{1 \to 4} \end{bmatrix} \cdot \begin{bmatrix} c_{1 \to 4} & \color{red}{c_{1 \to 4}} & c_{1 \to 4} & c_{1 \to 4} \\ c_{1 \to 4} & \color{red}{c_{1 \to 4}} & c_{1 \to 4} & c_{1 \to 4} \\ c_{1 \to 4} & \color{red}{c_{1 \to 4}} & c_{1 \to 4} & c_{1 \to 4} \\ c_{1 \to 4} & \color{red}{c_{1 \to 4}} & c_{1 \to 4} & c_{1 \to 4} \end{bmatrix} \right)}_{\text{红行与第2列点积,得到 Softmax 权重}} \times \underbrace{\begin{bmatrix} \color{blue}{v_{2}} & \color{blue}{v_{2}} & \ldots & \color{blue}{v_{2}} \end{bmatrix}}_{V矩阵的第2行特征}红行与第2列点积,得到 Softmax 权重c1c12c13c14c1c12c13c14c1c12c13c14c14c14c14c14c14c14c14c14c14c14c14c14c14c14c14c14×V矩阵的第2行特征[v2v2v2]

+++

……(此处省略与第3列点积并乘以第3行的计算过程)……\dots \dots \quad \text{(此处省略与第3列点积并乘以第3行的计算过程)} \quad \dots \dots……(此处省略与第3列点积并乘以第3行的计算过程)……

+++

([c1c1…c1c1→2c1→2…c1→2c1→3c1→3…c1→3c1→4c1→4…c1→4]⋅[c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4])⏟红行与第4列点积,得到 Softmax 权重×[v4v4…v4]⏟V矩阵的第4行特征\underbrace{\left( \begin{bmatrix} \color{red}{c_{1}} & \color{red}{c_{1}} & \ldots & \color{red}{c_{1}} \\ c_{1 \to 2} & c_{1 \to 2} & \ldots & c_{1 \to 2} \\ c_{1 \to 3} & c_{1 \to 3} & \ldots & c_{1 \to 3} \\ c_{1 \to 4} & c_{1 \to 4} & \ldots & c_{1 \to 4} \end{bmatrix} \cdot \begin{bmatrix} c_{1 \to 4} & c_{1 \to 4} & c_{1 \to 4} & \color{red}{c_{1 \to 4}} \\ c_{1 \to 4} & c_{1 \to 4} & c_{1 \to 4} & \color{red}{c_{1 \to 4}} \\ c_{1 \to 4} & c_{1 \to 4} & c_{1 \to 4} & \color{red}{c_{1 \to 4}} \\ c_{1 \to 4} & c_{1 \to 4} & c_{1 \to 4} & \color{red}{c_{1 \to 4}} \end{bmatrix} \right)}_{\text{红行与第4列点积,得到 Softmax 权重}} \times \underbrace{\begin{bmatrix} \color{blue}{v_{4}} & \color{blue}{v_{4}} & \ldots & \color{blue}{v_{4}} \end{bmatrix}}_{V矩阵的第4行特征}红行与第4列点积,得到 Softmax 权重c1c12c13c14c1c12c13c14c1c12c13c14c14c14c14c14c14c14c14c14c14c14c14c14c14c14c14c14×V矩阵的第4行特征[v4v4v4]

(注:由于点积得分已经过 Softmax 归一化处理,权重之和严格等于 1,因此按比例缩放 VVV 并相加后,无需再除以 nnn。) 以上四个加权后的蓝色行向量相加,就坍缩成了全新的一行特征结果 Context1\text{Context}_1Context1


同理:计算 Context2\text{Context}_2Context2(标签中的前两个时间步,扫过特征中的所有时间步):

Context2=([c1c1…c1c1→2c1→2…c1→2c1→3c1→3…c1→3c1→4c1→4…c1→4]⋅[c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4])⏟提取第二行查询与第1列点积×[v1v1…v1]⏟V矩阵的第1行特征Context_2 = \underbrace{\left( \begin{bmatrix} c_{1} & c_{1} & \ldots & c_{1} \\ \color{red}{c_{1 \to 2}} & \color{red}{c_{1 \to 2}} & \ldots & \color{red}{c_{1 \to 2}} \\ c_{1 \to 3} & c_{1 \to 3} & \ldots & c_{1 \to 3} \\ c_{1 \to 4} & c_{1 \to 4} & \ldots & c_{1 \to 4} \end{bmatrix} \cdot \begin{bmatrix} \color{red}{c_{1 \to 4}} & c_{1 \to 4} & c_{1 \to 4} & c_{1 \to 4} \\ \color{red}{c_{1 \to 4}} & c_{1 \to 4} & c_{1 \to 4} & c_{1 \to 4} \\ \color{red}{c_{1 \to 4}} & c_{1 \to 4} & c_{1 \to 4} & c_{1 \to 4} \\ \color{red}{c_{1 \to 4}} & c_{1 \to 4} & c_{1 \to 4} & c_{1 \to 4} \end{bmatrix} \right)}_{\text{提取第二行查询与第1列点积}} \times \underbrace{\begin{bmatrix} \color{blue}{v_{1}} & \color{blue}{v_{1}} & \ldots & \color{blue}{v_{1}} \end{bmatrix}}_{V矩阵的第1行特征}Context2=提取第二行查询与第1列点积c1c12c13c14c1c12c13c14c1c12c13c14c14c14c14c14c14c14c14c14c14c14c14c14c14c14c14c14×V矩阵的第1行特征[v1v1v1]

+++

([c1c1…c1c1→2c1→2…c1→2c1→3c1→3…c1→3c1→4c1→4…c1→4]⋅[c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4])⏟提取第二行查询与第2列点积×[v2v2…v2]⏟V矩阵的第2行特征\underbrace{\left( \begin{bmatrix} c_{1} & c_{1} & \ldots & c_{1} \\ \color{red}{c_{1 \to 2}} & \color{red}{c_{1 \to 2}} & \ldots & \color{red}{c_{1 \to 2}} \\ c_{1 \to 3} & c_{1 \to 3} & \ldots & c_{1 \to 3} \\ c_{1 \to 4} & c_{1 \to 4} & \ldots & c_{1 \to 4} \end{bmatrix} \cdot \begin{bmatrix} c_{1 \to 4} & \color{red}{c_{1 \to 4}} & c_{1 \to 4} & c_{1 \to 4} \\ c_{1 \to 4} & \color{red}{c_{1 \to 4}} & c_{1 \to 4} & c_{1 \to 4} \\ c_{1 \to 4} & \color{red}{c_{1 \to 4}} & c_{1 \to 4} & c_{1 \to 4} \\ c_{1 \to 4} & \color{red}{c_{1 \to 4}} & c_{1 \to 4} & c_{1 \to 4} \end{bmatrix} \right)}_{\text{提取第二行查询与第2列点积}} \times \underbrace{\begin{bmatrix} \color{blue}{v_{2}} & \color{blue}{v_{2}} & \ldots & \color{blue}{v_{2}} \end{bmatrix}}_{V矩阵的第2行特征}提取第二行查询与第2列点积c1c12c13c14c1c12c13c14c1c12c13c14c14c14c14c14c14c14c14c14c14c14c14c14c14c14c14c14×V矩阵的第2行特征[v2v2v2]

+++

……(此处省略与第3列点积并乘以第3行的计算过程)……\dots \dots \quad \text{(此处省略与第3列点积并乘以第3行的计算过程)} \quad \dots \dots……(此处省略与第3列点积并乘以第3行的计算过程)……

+++

([c1c1…c1c1→2c1→2…c1→2c1→3c1→3…c1→3c1→4c1→4…c1→4]⋅[c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4c1→4])⏟提取第二行查询与第4列点积×[v4v4…v4]⏟V矩阵的第4行特征\underbrace{\left( \begin{bmatrix} c_{1} & c_{1} & \ldots & c_{1} \\ \color{red}{c_{1 \to 2}} & \color{red}{c_{1 \to 2}} & \ldots & \color{red}{c_{1 \to 2}} \\ c_{1 \to 3} & c_{1 \to 3} & \ldots & c_{1 \to 3} \\ c_{1 \to 4} & c_{1 \to 4} & \ldots & c_{1 \to 4} \end{bmatrix} \cdot \begin{bmatrix} c_{1 \to 4} & c_{1 \to 4} & c_{1 \to 4} & \color{red}{c_{1 \to 4}} \\ c_{1 \to 4} & c_{1 \to 4} & c_{1 \to 4} & \color{red}{c_{1 \to 4}} \\ c_{1 \to 4} & c_{1 \to 4} & c_{1 \to 4} & \color{red}{c_{1 \to 4}} \\ c_{1 \to 4} & c_{1 \to 4} & c_{1 \to 4} & \color{red}{c_{1 \to 4}} \end{bmatrix} \right)}_{\text{提取第二行查询与第4列点积}} \times \underbrace{\begin{bmatrix} \color{blue}{v_{4}} & \color{blue}{v_{4}} & \ldots & \color{blue}{v_{4}} \end{bmatrix}}_{V矩阵的第4行特征}提取第二行查询与第4列点积c1c12c13c14c1c12c13c14c1c12c13c14c14c14c14c14c14c14c14c14c14c14c14c14c14c14c14c14×V矩阵的第4行特征[v4v4v4]

这最终算出了第二行的结果 Context2\text{Context}_2Context2

以此类推下去。很显然,编码器-解码器注意力机制中的数学流程,正是【利用序列X + 序列y的前半段预测序列y的后半段】的计算方式!在这里每一步都是单独的方程,涉及到矩阵中不同的行,因此这里的所有时间步可以并行!

最终得到的交叉注意力层的完整输出矩阵 Output\text{Output}Output,其实就是把所有 ContextContextContext 按行拼接(Stack) 在了一起:

Output=[Context1 (基于词 1 查阅原文的特征向量)Context2 (基于词 1, 2 查阅原文的特征向量)Context3 (基于词 1, 2, 3 查阅原文的特征向量)Context4 (基于词 1, 2, 3, 4 查阅原文的特征向量)]\text{Output} = \begin{bmatrix} \text{Context}_1 \text{ (基于词 1 查阅原文的特征向量)} \\ \text{Context}_2 \text{ (基于词 1, 2 查阅原文的特征向量)} \\ \text{Context}_3 \text{ (基于词 1, 2, 3 查阅原文的特征向量)} \\ \text{Context}_4 \text{ (基于词 1, 2, 3, 4 查阅原文的特征向量)} \end{bmatrix}Output=Context1 (基于词 1 查阅原文的特征向量)Context2 (基于词 1, 2 查阅原文的特征向量)Context3 (基于词 1, 2, 3 查阅原文的特征向量)Context4 (基于词 1, 2, 3, 4 查阅原文的特征向量)


⑤.前馈神经网络网络、层归一化和残差链接

与 Encoder 中的前馈网络、层归一化以及残差链接相同,每个 Decoder 层包含一个前馈网络,该网络对每个位置应用相同的全连接层。这个网络通常包含两个线性变换,并在中间加入了一个激活函数,如 ReLU 或 GELU。


(3).Decoder-Only结构中的Decoder
①.结构
image-20260626183546153

如图所示,与原本的Decoder结构相比,Decoder-only状态下的Decoder不再存在编码器-解码器注意力层,整个结构会变得更像编码器Encoder,但依然保留着Teacher forcing和掩码机制。由于没有了编码器-解码器注意力层,因此原本依赖于编码器-解码器注意力层完成的整套训练和运算流程也都不再有效了,相对的,在Decoder-only结构中的Decoder大部分时候都采用“自回归”的训练流程。(自回归流程在时间序列预测中是一种常用的方法,它逐步生成未来的值,每一步的预测依赖于前一步的实际值或预测值,而Decoder-only状态下的训练、预测流程都是这样的流程。)在自回归场景中,Decoder的任务是:

  1. 利用序列的前半段预测序列的后半段,因此Decoder的输入数据是一段时间序列、一段文字,输出的是对未来时间的预测、对未来文字的填补

  2. 利用teacher forcing机制和自回归机制的本质,在训练和预测流程中使用标签来辅助预测。具体地来说,在训练流程中,Decoder利用teacher forcing机制、不断将正确的标签作为特征数据使用;在测试流程中,Decoder利用自回归的属性,将前一步的预测值作为特征数据来使用。

在生成式任务中,一般我们不再区分“特征和标签”这两种不同的数据,在大多数生成式任务中,我们有且只有一种数据——就是需要继续生成、继续补充的那段序列。生成式任务带有一定的“自监督”属性,我们训练用的数据、和要预测的数据都来自于同一段序列,因此标签数据在下一个时间步就会成为我们的特征数据,故而我们也不会特地再去区分特征和标签、而是会区分“输入”与“输出”。不过,从架构图上来看,除了要预测的序列本身之外,我们依然也可以给Decoder输入更多额外的信息(图上的inputs部分)。大部分时候,我们可以使用这条数据流线路向Decoder传递一些相应的“条件”与“背景知识”,可以帮助我们更好地进行信息的生成和填补。


②.训练流程

具体来看,Decoder-only状态下的训练流程如下:

假设需要预测的序列为y,编码好的结果为ebd_y,其中我们取ebd_y的前n个字符作为输入,n个字符后的字符作为标签:

  • 第1步,输入 ebd_y[0] >> 输出预测标签yhat[0],对应真实标签y[0]

    输入Decoder
    序列的前半段

    索引y1y2y3y4y5
    0"sos"0.18210.40000.22480.44400.7771

    预测出

    当前时间步的
    预测标签yhat

    索引y1y2y3y4y5
    0yyy0.56210.89200.73120.25430.1289

    对应

    真实标签y
    序列的后半段

    索引
    0
    1
    2最好的
    3时代
    4
    5
    6最坏的
    7时代
    8"eos"
  • ……

  • 第n+1步,输入 ebd_y[:n] >> 输出预测标签yhat[n],对应真实标签y[n]

    输入Decoder
    序列的前半段

    索引y1y2y3y4y5
    0"sos"0.18210.40000.22480.44400.7771
    10.18210.40000.22480.44400.7771
    20.17210.50300.89480.23850.0987
    3最好的0.13420.82970.29780.71200.2565
    4时代0.12480.50030.75590.48040.2593

    预测出

    当前时间步的
    预测标签yhat

    索引y1y2y3y4y5
    4yyy0.56210.89200.73120.25430.1289

    对应

    真实标签y
    序列的后半段

    索引
    0
    1
    2最好的
    3时代
    4
    5
    6最坏的
    7时代
    8"eos"
  • 第n+2步,输入 ebd_y[:n+1] >> 输出预测标签yhat[n+1],对应真实标签y[n+1]

    输入Decoder
    序列的前半段

    索引y1y2y3y4y5
    0"sos"0.18210.40000.22480.44400.7771
    10.18210.40000.22480.44400.7771
    20.17210.50300.89480.23850.0987
    3最好的0.13420.82970.29780.71200.2565
    4时代0.12480.50030.75590.48040.2593
    50.18210.40000.22480.44400.7771

    预测出

    当前时间步的
    预测标签yhat

    索引y1y2y3y4y5
    5yyy0.56210.89200.73120.25430.1289

    对应

    真实标签y
    序列的后半段

    索引
    0
    1
    2最好的
    3时代
    4
    5
    6最坏的
    7时代
    8"eos"
  • 第n+3步,输入 ebd_y[:n+2] >> 输出预测标签yhat[n+2],对应真实标签y[n+2]

    输入Decoder
    序列的前半段

    索引y1y2y3y4y5
    0"sos"0.18210.40000.22480.44400.7771
    10.18210.40000.22480.44400.7771
    20.17210.50300.89480.23850.0987
    3最好的0.13420.82970.29780.71200.2565
    4时代0.12480.50030.75590.48040.2593
    50.18210.40000.22480.44400.7771
    60.17210.50300.89480.23850.0987

    预测出

    当前时间步的
    预测标签yhat

    索引y1y2y3y4y5
    6yyy0.56210.89200.73120.25430.1289

    对应

    真实标签y
    序列的后半段

    索引
    0
    1
    2最好的
    3时代
    4
    5
    6最坏的
    7时代
    8"eos"
③.推理流程

而在推理流程中,Decoder中运行的流程如下所示

  • 第一步,输入 ebd_y(全部的数据) >> 输出下一步的预测标签

    输入Decoder
    全部的序列

    索引y1y2y3y4y5
    00.18210.40000.22480.44400.7771
    10.17210.50300.89480.23850.0987
    2最好的0.13420.82970.29780.71200.2565
    3时代0.12480.50030.75590.48040.2593

    预测出

    当前时间步的
    预测标签yhat

    索引y1y2y3y4y5
    4yyy0.56210.89200.73120.25430.1289
  • 第二步,输入 ebd_y(全部的数据)+ 预测的yhat >> 输出下一步的预测标签

    输入Decoder
    全部的序列

    索引y1y2y3y4y5
    00.18210.40000.22480.44400.7771
    10.17210.50300.89480.23850.0987
    2最好的0.13420.82970.29780.71200.2565
    3时代0.12480.50030.75590.48040.2593
    4yyy0.56210.89200.73120.25430.1289

    预测出

    当前时间步的
    预测标签yhat

    索引y1y2y3y4y5
    5yyy0.56210.89200.73120.25430.1289
  • 第三步,输入 ebd_y(全部的数据)+ 预测的yhat >> 输出下一步的预测标签

    输入Decoder
    全部的序列

    索引y1y2y3y4y5
    00.18210.40000.22480.44400.7771
    10.17210.50300.89480.23850.0987
    2最好的0.13420.82970.29780.71200.2565
    3时代0.12480.50030.75590.48040.2593
    4yyy0.56210.89200.73120.25430.1289
    5yyy0.56210.89200.73120.25430.1289

    预测出

    当前时间步的
    预测标签yhat

    索引y1y2y3y4y5
    6yyy0.56210.89200.73120.25430.1289
  • 以此类推,直到预测出“eos”后停止。与Transformer中的Decoder一致,训练流程是可以并行的,这一点通过带掩码的注意力机制来实现。而推理流程是必须严格遵守自回归要求的、在下一个时间步预测之前必须将上一个时间步的结果计算出来,因此推理流程中则需要使用循环的方式来进行预测。


④.自定义掩码

不从第一个样本开始训练的流程如何实现?

上面的流程中,我们必须从单词1开始预测,其流程为:

单词1 用于预测 单词2
单词1、2 用于预测 单词3
单词1、2、3 用于预测 单词4

但事实上,在生成式的例子中,我们可能会倾向于一开始就给与比较多的信息。我们真正要做的是“利用句子的前半段”去预测“句子的后半段”,大部分时候我们其实很少使用简单的几个单词、或1个单词来进行训练。而是倾向于使用下面的流程

单词1:n 用于预测 单词n+1
单词1:n+1 用于预测 单词n+2
单词1:n+2 用于预测 单词n+3

可以通过移动前瞻掩码矩阵的对角线来实现上面的流程

def create_look_ahead_mask(seq_len, start_seq = 1):
    mask = torch.triu(torch.ones((seq_len, seq_len)),diagonal=start_seq)  # triu 左下方的三角矩阵,diagonal控制对角线位置
    mask = mask.float() * -1e9  # 将未来的位置设置为负无穷大
    return mask

print(create_look_ahead_mask(10)) #为了观看,现在展示的是1和0,实际应该是右上角负无穷,左下角0
# tensor([[0., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
#         [0., 0., 1., 1., 1., 1., 1., 1., 1., 1.],
#         [0., 0., 0., 1., 1., 1., 1., 1., 1., 1.],
#         [0., 0., 0., 0., 1., 1., 1., 1., 1., 1.],
#         [0., 0., 0., 0., 0., 1., 1., 1., 1., 1.],
#         [0., 0., 0., 0., 0., 0., 1., 1., 1., 1.],
#         [0., 0., 0., 0., 0., 0., 0., 1., 1., 1.],
#         [0., 0., 0., 0., 0., 0., 0., 0., 1., 1.],
#         [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],
#         [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])
print(create_look_ahead_mask(10,start_seq=4)) #通过调节对角线,可以让掩码的区域缩小,从而可以允许更多信息的注入
# tensor([[0., 0., 0., 0., 1., 1., 1., 1., 1., 1.],
#         [0., 0., 0., 0., 0., 1., 1., 1., 1., 1.],
#         [0., 0., 0., 0., 0., 0., 1., 1., 1., 1.],
#         [0., 0., 0., 0., 0., 0., 0., 1., 1., 1.],
#         [0., 0., 0., 0., 0., 0., 0., 0., 1., 1.],
#         [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],
#         [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
#         [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
#         [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
#         [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])

当前瞻掩码从第一个时间步开始时,掩码注意力层输出的结果覆盖的时间步为:

Cdecoder=[c1c1…c1c1→2c1→2…c1→2c1→3c1→3…c1→3c1→4c1→4…c1→4] C_{decoder} = \begin{bmatrix} c_{1} & c_{1} & \ldots & c_{1} \\ c_{1 \to 2} & c_{1 \to 2} & \ldots & c_{1 \to 2} \\ c_{1 \to 3} & c_{1 \to 3} & \ldots & c_{1 \to 3} \\ c_{1 \to 4} & c_{1 \to 4} & \ldots & c_{1 \to 4} \end{bmatrix} Cdecoder=c1c12c13c14c1c12c13c14c1c12c13c14
当前瞻掩码从第4个时间步开始时,掩码注意力层输出的结果覆盖的时间步为:

Cdecoder=[c1→4c1→4…c1→4c1→5c1→5…c1→5c1→6c1→6…c1→6c1→7c1→7…c1→7] C_{decoder} = \begin{bmatrix} c_{1 \to 4} & c_{1 \to 4} & \ldots & c_{1 \to 4} \\ c_{1 \to 5} & c_{1 \to 5} & \ldots & c_{1 \to 5} \\ c_{1 \to 6} & c_{1 \to 6} & \ldots & c_{1 \to 6} \\ c_{1 \to 7} & c_{1 \to 7} & \ldots & c_{1 \to 7} \end{bmatrix} Cdecoder=c14c15c16c17c14c15c16c17c14c15c16c17
这样可以第一次预测过程中所使用的标签为“前n个字”而不是“第一个字”。当然,这已经是属于“自定义掩码”的范围,在实际中并不多见。但通过这种掩码方式,可以要求解码器产出的注意力分数完整接收前几个字之间的相互关系、从而一开始就使用“前半段话”来进行训练。在之后实现Decoder-only预测的过程中,我们将会更详细地讲解这个流程。


(4).Decoder作用

Decoder 的主要任务是将接收到的特征转化为实际的输出标签或自然语言序列。

三.总结

自注意力机制使模型能够灵活地捕捉序列内的长距离依赖,而无需依赖于递归网络结构,从而避免了梯度消失和计算效率低下的问题。编码器层通过逐层处理输入数据,有效地提取和聚合信息;而解码器层则利用编码器的输出,结合自回归的方式逐步构建输出序列。通过这种方式,Transformer能够在翻译、文本生成、摘要等任务中生成准确且连贯的文本。

此外,编码器-解码器注意力机制是理解输入与输出之间复杂关系的关键,它使得模型能够在生成每个输出时都考虑到与输入序列的具体关联。这种能力使得Transformer不仅适用于传统的NLP任务,还可以扩展到如图像处理和多模态任务中,展示了其极大的灵活性和广泛的适用性。

总的来说,Transformer的出现标志着深度学习在处理序列数据方面的一个重大进步。随着研究的深入和技术的发展,我们期待看到更多基于Transformer的创新应用,这将进一步推动人工智能领域的边界向前发展。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值