自回归(Autoregressive, AR)是序列生成任务中的核心范式,广泛应用于自然语言处理(如GPT)、语音合成、时间序列预测等领域。
其核心思想是:序列中每个位置的生成仅依赖于之前已生成的部分,即通过逐步预测下一个元素的方式构建完整序列。
1. 自回归的核心原理
2. 自回归模型的典型结构
(1) 基于RNN的结构(如LSTM、GRU)
-
原理:
通过循环单元维护隐状态(Hidden State),逐步传递历史信息。-
隐状态 ht=f(ht−1,xt),其中 xt 是当前输入(通常是上一步的输出 yt−1)。
-
-
缺点:
串行计算导致训练和推理速度慢,长距离依赖易丢失。
(2) 基于Transformer的结构(如GPT、T5)
-
原理:
使用**掩码自注意力(Masked Self-Attention)**实现并行训练,同时保证自回归特性。-
训练时,通过下三角掩码矩阵限制每个位置只能关注其左侧的上下文(如图1)。
-
推理时,逐步生成序列,每一步将新生成的词追加到输入中。
-
-
优点:
-
并行计算加速训练;
扫描二维码关注公众号,回复: 17598898 查看本文章 -
长距离依赖捕捉能力强。
-
3. 自回归 vs 非自回归
特性 | 自回归(AR) | 非自回归(NAR) |
---|---|---|
生成方式 | 逐位置生成,依赖历史信息 | 并行生成所有位置 |
速度 | 慢(串行生成) | 快(一步或少量步骤生成) |
生成质量 | 高(上下文连贯性强) | 可能较低(依赖后处理或迭代优化) |
典型模型 | GPT、Transformer解码器 | NAT(Non-Autoregressive Translation) |
适用场景 | 文本生成、对话系统、高精度合成任务 | 实时翻译、语音合成等对速度敏感的任务 |
4. 自回归的关键技术
(1) 掩码机制(Masking)
-
目标:确保训练时模型无法“偷看”未来信息。
-
实现:
(2) 输入偏移(Shifted Inputs)
-
目标:对齐训练与推理时的输入分布。
-
实现:
-
训练时,将目标序列右移一位,并添加起始符
<sos>
。 -
例:目标序列
[A, B, C]
→ 输入[<sos>, A, B]
,输出[A, B, C]
。
-
(3) 解码策略
-
贪婪搜索(Greedy Search):每一步选择概率最高的词,速度快但可能陷入局部最优。
-
束搜索(Beam Search):保留多个候选序列,平衡质量与计算成本。
-
采样(Sampling):按概率分布随机选择,增加多样性(可调节温度系数)。
5. 自回归模型的优缺点
优点:
-
生成质量高:依赖完整历史信息,输出逻辑连贯;
-
灵活性:适用于变长序列生成;
-
可解释性:生成过程透明,便于调试。
缺点:
-
速度慢:无法并行生成,长序列推理延迟高;
-
误差累积:早期生成错误会影响后续步骤;
-
固定顺序依赖:生成顺序可能影响结果(如从左到右 vs 从右到左)。
6. 自回归模型的应用场景
-
文本生成:故事创作、对话系统(如ChatGPT)、代码生成。
-
语音合成:将文本逐帧转换为语音信号(如Tacotron)。
-
时间序列预测:股票价格预测、天气预测。
-
图像生成:按像素或块逐步生成图像(如PixelRNN)。
7. 优化自回归生成效率的方法
-
缓存机制(KV Cache):在Transformer推理时缓存已计算的键值对(Key-Value),避免重复计算。
-
分块生成(Chunkwise Generation):将长序列分为多个块,逐块生成(如Transformer-XL)。
-
模型蒸馏:将大型自回归模型的知识迁移到小型非自回归模型中。
8. 代码示例(PyTorch实现掩码自注意力)
import torch
import torch.nn as nn
import torch.nn.functional as F
class MaskedSelfAttention(nn.Module):
def __init__(self, embed_size, heads):
super().__init__()
self.embed_size = embed_size
self.heads = heads
self.head_dim = embed_size // heads
self.query = nn.Linear(embed_size, embed_size)
self.key = nn.Linear(embed_size, embed_size)
self.value = nn.Linear(embed_size, embed_size)
self.fc_out = nn.Linear(embed_size, embed_size)
def forward(self, x, mask):
# x: [batch_size, seq_len, embed_size]
batch_size, seq_len, _ = x.shape
Q = self.query(x) # [batch_size, seq_len, embed_size]
K = self.key(x) # [batch_size, seq_len, embed_size]
V = self.value(x) # [batch_size, seq_len, embed_size]
# 分割多头
Q = Q.view(batch_size, seq_len, self.heads, self.head_dim).permute(0, 2, 1, 3)
K = K.view(batch_size, seq_len, self.heads, self.head_dim).permute(0, 2, 1, 3)
V = V.view(batch_size, seq_len, self.heads, self.head_dim).permute(0, 2, 1, 3)
# 计算注意力分数
energy = torch.matmul(Q, K.permute(0, 1, 3, 2)) / (self.head_dim ** 0.5)
# 应用掩码
if mask is not None:
energy = energy.masked_fill(mask == 0, float("-1e20"))
# Softmax + 注意力权重
attention = F.softmax(energy, dim=-1)
# 加权求和
out = torch.matmul(attention, V)
out = out.permute(0, 2, 1, 3).contiguous().view(batch_size, seq_len, self.embed_size)
out = self.fc_out(out)
return out
# 使用示例
embed_size = 512
heads = 8
seq_len = 10
batch_size = 4
model = MaskedSelfAttention(embed_size, heads)
x = torch.randn(batch_size, seq_len, embed_size)
mask = torch.tril(torch.ones(seq_len, seq_len)).unsqueeze(0).unsqueeze(0) # 下三角掩码
output = model(x, mask)