Decoder-Only架构下Decoder的学习
·
文章目录
推荐
一 Decoder-Only结构下的Decoder
- 在Decoder-Only结构下的Decoder是专门用于生成式任务,它从整个Transformer结构中抽离出来,有独特的训练流程与结构。

- Decoder-only状态下的Decoder不存在编码器-解码器注意力层,整个结构会更像编码器Encoder,但依然保留着Teacher forcing和掩码机制。
- 在Decoder-only结构中的Decoder大部分时候都采用“自回归"的训l练流程一自回归流程在时间序列预测中是一种常用的方法。它逐步生成未来的值,每一步的预测依赖于前一步的实际值或预测值。
- 在自回归场景中,Decoder的任务:
- 利用序列的前半段预测序列的后半段,因此Decoder的输入数据是一段时间序列、一段文字,输出的是对未来时间的预测、对未来文字的填补。
- 利用teacher forcing机制和自回归机制的本质,在训练和预测流程中使用标签来辅助预测。具体地来说,在训练流程中,Decoder利用teacher forcing机制、不断将正确的标签作为特征数据使用:在测试流程中,Decoder利用自回归的属性,将前一步的预测值作为特征数据来使用。
- 在生成式任务中,一般不再区分“特征”和“标签”两种不同的数据。在大多数生成式任务中,我们有且只有一种数据——需要继续生成,继续补充的序列。生成式任务带有一定的“自监督”属性,训练用的数据和要预测的数据都来自同一段序列,因此标签数据在下一个时间步就会成为特征数据,故而不再区分“特征”和“标签”两种不同的数据,仅仅区分输入和输出两种类型的序列。
- 架构图中除了右侧的Outputs(需要预测的序列)外,我们还需要通过左侧的Inputs给Decoder输入更多的额外信息(如:角色定义、任务提示、回答要求、场景假定等),帮助模型更好地进行信息的生成和填补。
二 Decoder-only下的解码器具体训练和推理流程
- 假设需要预测的序列为 y y y,编码好的结果为 e b d y ebd_y ebdy。取 e b d y ebd_y ebdy的前 n n n个字符作为输入, n n n个字符后的字符作为标签。
2.1 Decoder-only结构下的训练过程
- 第一步:输入
e
b
d
y
[
:
1
]
ebd_y[:1]
ebdy[:1]>>输出预测标签
y
h
a
t
[
0
]
yhat[0]
yhat[0],对应真实标签
y
[
0
]
y[0]
y[0]。

- 第二步:输入
e
b
d
y
[
:
2
]
ebd_y[:2]
ebdy[:2]>>输出预测标签
y
h
a
t
[
1
]
yhat[1]
yhat[1],对应真实标签
y
[
1
]
y[1]
y[1]。

- 以此类推,第 n + 1 n+1 n+1步,输入 e b d y [ : n ] ebd_y[:n] ebdy[:n]>>输出标签 y h a t [ n ] yhat[n] yhat[n],对应真实标签 y [ n ] y[n] y[n]。
2.2 Decoder-only结构下的推理过程
- 第一步,输入
e
b
d
y
ebd_y
ebdy(全部的数据)>>输出下一步的预测标签。

- 第二步,输入
e
b
d
y
ebd_y
ebdy(全部的数据)+预测的
y
h
a
t
yhat
yhat>>输出下一步的预测标签。

- 以此类推…
三 自定义掩码:从任意开始预测
- 从Decoder的掩码注意力层中输出的是经过掩码后,每行只携带特定时间段的信息
的结果 C d e c o d e r C_{decoder} Cdecoder:
C d e c o d e r = [ a 11 v 1 a 11 v 1 ⋯ a 11 v 1 a 21 v 1 + a 22 v 2 a 21 v 1 + a 22 v 2 ⋯ 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 ⋯ 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 ⋯ a 41 v 1 + a 42 v 2 + a 43 v 3 + a 44 v 4 ] \begin{align} \text C_{decoder} &= \begin{bmatrix} a_{11}v_1 & a_{11}v_1 & \cdots & a_{11}v_1 \\ a_{21}v_1 + a_{22}v_2 & a_{21}v_1 + a_{22}v_2 & \cdots & 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 & \cdots & 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 & \cdots & a_{41}v_1 + a_{42}v_2 + a_{43}v_3 + a_{44}v_4 \end{bmatrix} \end{align} Cdecoder= a11v1a21v1+a22v2a31v1+a32v2+a33v3a41v1+a42v2+a43v3+a44v4a11v1a21v1+a22v2a31v1+a32v2+a33v3a41v1+a42v2+a43v3+a44v4⋯⋯⋯⋯a11v1a21v1+a22v2a31v1+a32v2+a33v3a41v1+a42v2+a43v3+a44v4 - 使用覆盖的时间点作为脚标(脚标只代表时间维度/序列长度的维度,省略了特征维度上的脚标),简化为
C d e c o d e r = [ c 1 c 1 ⋯ c 1 c 1 → 2 c 1 → 2 ⋯ c 1 → 2 c 1 → 3 c 1 → 3 ⋯ c 1 → 3 c 1 → 4 c 1 → 4 ⋯ c 1 → 4 ] \begin{align} \text C_{decoder} &= \begin{bmatrix} c_1 & c_1 & \cdots & c_1 \\ c_{1 \to 2} & c_{1 \to 2} & \cdots &c_{1 \to 2} \\ c_{1 \to 3} & c_{1 \to 3} & \cdots & c_{1 \to 3} \\ c_{1 \to 4} & c_{1 \to 4} & \cdots & c_{1 \to 4} \end{bmatrix} \end{align} Cdecoder= c1c1→2c1→3c1→4c1c1→2c1→3c1→4⋯⋯⋯⋯c1c1→2c1→3c1→4 - 此时,我们发现必须从单词1开始预测:用单词1预测单词2,用单词1、2预测单词3,用单词1 - 3预测单词4。
- 但生成式例子中,我们更倾向一开始给较多信息,实际是用“句子前半段”预测“后半段”,很少仅用少量单词训练。常用流程为:用单词1:n预测单词n + 1,用单词1:n + 1预测单词n + 2。如果需要这种流程,可以通过移动前瞻掩码矩阵的对角线。
import torch
# 前瞻掩码 (seq_len, seq_len)
def create_look_ahead_mask(seq_len, start_seq=1):
mask = torch.triu(torch.ones((seq_len, seq_len)), diagonal=start_seq)
# mask = mask.float() * -1e9 # 将未来未知设置为负无穷大 方便查看,暂时注释
return mask
# 通过调节对角线,可以让掩码的区域缩小
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.]])
- 当前瞻掩码从第4个时间步开始时,掩码注意力层输出的结果覆盖的时间步为:
C d e c o d e r = [ c 1 → 4 c 1 → 4 ⋯ c 1 → 4 c 1 → 5 c 1 → 5 ⋯ c 1 → 5 c 1 → 6 c 1 → 6 ⋯ c 1 → 6 c 1 → 7 c 1 → 7 ⋯ c 1 → 7 ] \begin{align} \text C_{decoder} &= \begin{bmatrix} c_{1 \to 4} & c_{1 \to 4} & \cdots &c_{1 \to 4} \\ c_{1 \to 5} & c_{1 \to 5} & \cdots & c_{1 \to 5} \\ c_{1 \to 6} & c_{1 \to 6} & \cdots &c_{1 \to 6} \\ c_{1 \to 7} & c_{1 \to 7} & \cdots & c_{1 \to 7} \end{bmatrix} \end{align} Cdecoder= c1→4c1→5c1→6c1→7c1→4c1→5c1→6c1→7⋯⋯⋯⋯c1→4c1→5c1→6c1→7 - 这样可以第一次预测过程中所使用的标签为“前n个字"而不是“第一个字”。当然,这已经是属于“自定义掩码"的范围,在实际中并不多见。但通过这种掩码方式,可以要求解码器产出的注意力分数完整接收前几个字之间的相互关系、从而一开始就使用“前半段话"来进行训练。
更多推荐


所有评论(0)