本章将深入探讨注意力机制的高级原理和复杂变体,包括稀疏注意力、层级注意力、跨模态注意力等先进技术。这些技术在大规模模型和复杂任务中发挥着重要作用,代表了注意力机制发展的前沿方向。通过学习这些进阶原理,读者将能够理解和应用最先进的注意力技术。
随着深度学习的发展,基础的注意力机制已经不能满足复杂任务的需求。研究者们提出了各种改进和变体,以解决不同场景下的具体问题。本章将从稀疏性和层次性两个维度,介绍注意力机制的高级变体,并探讨跨模态注意力的应用。
本章将带领读者:
通过本章的学习,读者将能够掌握注意力机制的前沿技术,为构建高性能的AI系统奠定基础。
完成本章学习后,读者将能够:
基础自注意力机制的计算复杂度为O(n^2d),其中n是序列长度,d是向量维度。对于长序列(如n=10000),这将导致:
这些限制使得基础注意力机制难以应用于长序列处理任务。
稀疏注意力主要通过以下方式降低复杂度:
基本原理:
只关注当前位置附近的窗口:
其中w是窗口大小。
实现代码:
class LocalAttention(nn.Module): def __init__(self, d_model, window_size=128, dropout=0.1): super().__init__() self.d_model = d_model self.window_size = window_size self.dropout = nn.Dropout(dropout) # 线性变换 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) def forward(self, x, mask=None): batch_size, seq_len, d_model = x.shape # 线性变换 Q = self.W_q(x) K = self.W_k(x) V = self.W_v(x) # 计算局部注意力 output = torch.zeros_like(Q) attention_weights = torch.zeros(batch_size, seq_len, seq_len, device=x.device) for i in range(seq_len): # 计算窗口范围 start = max(0, i - self.window_size // 2) end = min(seq_len, i + self.window_size // 2 + 1) # 提取窗口 Q_window = Q[:, i:i+1, :] # [batch_size, 1, d_model] K_window = K[:, start:end, :] # [batch_size, window_len, d_model] V_window = V[:, start:end, :] # [batch_size, window_len, d_model] # 计算注意力 scores = torch.matmul(Q_window, K_window.transpose(-2, -1)) / torch.sqrt(self.d_model) if mask is not None: mask_window = mask[:, i:i+1, start:end] scores = scores.masked_fill(mask_window == 0, -1e9) attn_weights = F.softmax(scores, dim=-1) attn_weights = self.dropout(attn_weights) # 存储权重 attention_weights[:, i, start:end] = attn_weights.squeeze(1) # 计算输出 output[:, i:i+1, :] = torch.matmul(attn_weights, V_window) return output, attention_weights
基本原理:
以一定的间隔进行注意力计算:
其中d是膨胀因子。
实现代码:
class DilatedAttention(nn.Module): def __init__(self, d_model, dilation_rate=2, dropout=0.1): super().__init__() self.d_model = d_model self.dilation_rate = dilation_rate self.dropout = nn.Dropout(dropout) # 线性变换 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) def forward(self, x, mask=None): batch_size, seq_len, d_model = x.shape # 线性变换 Q = self.W_q(x) K = self.W_k(x) V = self.W_v(x) # 创建膨胀的注意力矩阵 attention_weights = torch.zeros(batch_size, seq_len, seq_len, device=x.device) for i in range(seq_len): # 计算膨胀位置 dilated_positions = list(range(0, i+1, self.dilation_rate)) # 提取膨胀位置 Q_pos = Q[:, i:i+1, :] # [batch_size, 1, d_model] K_pos = K[:, dilated_positions, :] # [batch_size, num_dilated, d_model] V_pos = V[:, dilated_positions, :] # [batch_size, num_dilated, d_model] # 计算注意力 scores = torch.matmul(Q_pos, K_pos.transpose(-2, -1)) / torch.sqrt(self.d_model) if mask is not None: mask_pos = mask[:, i:i+1, dilated_positions] scores = scores.masked_fill(mask_pos == 0, -1e9) attn_weights = F.softmax(scores, dim=-1) attn_weights = self.dropout(attn_weights) # 存储权重 attention_weights[:, i, dilated_positions] = attn_weights.squeeze(1) # 计算输出 output_pos = torch.matmul(attn_weights, V_pos) output[:, i:i+1, :] = output_pos return output, attention_weights
基本原理:
使用多个不同大小的窗口进行注意力计算:
其中\text{Attention}_w是窗口大小为w的局部注意力。
实现代码:
class SlidingWindowAttention(nn.Module): def __init__(self, d_model, window_sizes=[64, 128, 256], dropout=0.1): super().__init__() self.d_model = d_model self.window_sizes = window_sizes self.dropout = nn.Dropout(dropout) # 多个注意力头 self.attention_heads = nn.ModuleList([ LocalAttention(d_model, window_size=w, dropout=dropout) for w in window_sizes ]) # 融合层 self.fusion = nn.Linear(len(window_sizes) * d_model, d_model) def forward(self, x, mask=None): batch_size, seq_len, d_model = x.shape # 计算各窗口的注意力输出 outputs = [] for attention_head in self.attention_heads: output, _ = attention_head(x, mask) outputs.append(output) # 拼接所有输出 concatenated = torch.cat(outputs, dim=-1) # [batch_size, seq_len, len(window_sizes) * d_model] # 融合 output = self.fusion(concatenated) # 计算平均注意力权重 attention_weights = torch.zeros(batch_size, seq_len, seq_len, device=x.device) for i, attention_head in enumerate(self.attention_heads): _, weights = attention_head(x, mask) attention_weights += weights / len(self.attention_heads) return output, attention_weights
基本原理:
根据查询内容的相关性选择最相关的键值对:
其中\text{top-K}(q_i)是与q_i最相关的K个位置。
实现代码:
class TopKAttention(nn.Module): def __init__(self, d_model, top_k=64, dropout=0.1): super().__init__() self.d_model = d_model self.top_k = min(top_k, 1000) # 防止k过大 self.dropout = nn.Dropout(dropout) # 线性变换 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) def forward(self, x, mask=None): batch_size, seq_len, d_model = x.shape # 线性变换 Q = self.W_q(x) K = self.W_k(x) V = self.W_v(x) # 计算所有相似度 scores = torch.matmul(Q, K.transpose(-2, -1)) / torch.sqrt(self.d_model) if mask is not None: scores = scores.masked_fill(mask == 0, -1e9) # 获取top-k位置 top_k_values, top_k_indices = torch.topk(scores, k=self.top_k, dim=-1) # 创建稀疏注意力权重 attention_weights = torch.zeros(batch_size, seq_len, seq_len, device=x.device) for i in range(batch_size): for j in range(seq_len): # 获取当前query的top-k位置 k_indices = top_k_indices[i, j] # 计算softmax权重 k_scores = top_k_values[i, j] attn_weights = F.softmax(k_scores, dim=-1) attn_weights = self.dropout(attn_weights) # 存储权重 attention_weights[i, j, k_indices] = attn_weights # 计算输出 output = torch.matmul(attention_weights, V) return output, attention_weights
基本原理:
根据位置距离进行稀疏化:
其中\text{dist}(i,j)是距离函数,可以是线性、指数或其他形式。
实现代码:
class DistanceBasedAttention(nn.Module): def __init__(self, d_model, max_distance=512, distance_func='linear', dropout=0.1): super().__init__() self.d_model = d_model self.max_distance = max_distance self.distance_func = distance_func self.dropout = nn.Dropout(dropout) # 线性变换 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) # 距离权重 if distance_func == 'linear': self.distance_weights = torch.linspace(1.0, 0.1, max_distance) elif distance_func == 'exponential': self.distance_weights = torch.exp(-torch.linspace(0, 3, max_distance)) elif distance_func == 'gaussian': positions = torch.arange(max_distance) self.distance_weights = torch.exp(-(positions - max_distance//2)**2 / (2 * (max_distance//4)**2)) def forward(self, x, mask=None): batch_size, seq_len, d_model = x.shape # 线性变换 Q = self.W_q(x) K = self.W_k(x) V = self.W_v(x) # 计算距离矩阵 positions = torch.arange(seq_len, device=x.device) distance_matrix = torch.abs(positions.unsqueeze(0) - positions.unsqueeze(1)) # 应用距离权重 attention_mask = distance_matrix <= self.max_distance if self.distance_func != 'none': distance_weights = self.distance_weights[distance_matrix] attention_scores = Q * distance_weights.unsqueeze(-1) * K.transpose(-2, -1) else: attention_scores = torch.matmul(Q, K.transpose(-2, -1)) # 归一化 attention_scores = attention_scores / torch.sqrt(self.d_model) if mask is not None: attention_scores = attention_scores.masked_fill(mask == 0, -1e9) # 计算注意力权重 attention_weights = F.softmax(attention_scores, dim=-1) attention_weights = self.dropout(attention_weights) # 应用注意力掩码 attention_weights = attention_weights * attention_mask.float() # 计算输出 output = torch.matmul(attention_weights, V) return output, attention_weights
基本原理:
将注意力矩阵近似为低秩形式:
其中A和B是低秩矩阵。
实现代码:
class LowRankAttention(nn.Module): def __init__(self, d_model, rank=64, dropout=0.1): super().__init__() self.d_model = d_model self.rank = rank self.dropout = nn.Dropout(dropout) # 线性变换 self.W_q = nn.Linear(d_model, rank) self.W_k = nn.Linear(d_model, rank) self.W_v = nn.Linear(d_model, d_model) # 低秩矩阵 self.A = nn.Linear(rank, d_model) self.B = nn.Linear(rank, d_model) def forward(self, x, mask=None): batch_size, seq_len, d_model = x.shape # 线性变换到低秩空间 Q_low = self.W_q(x) # [batch_size, seq_len, rank] K_low = self.W_k(x) # [batch_size, seq_len, rank] V = self.W_v(x) # [batch_size, seq_len, d_model] # 计算低秩注意力 attention_scores = torch.matmul(Q_low, K_low.transpose(-2, -1)) / torch.sqrt(self.rank) if mask is not None: attention_scores = attention_scores.masked_fill(mask == 0, -1e9) attention_weights = F.softmax(attention_scores, dim=-1) attention_weights = self.dropout(attention_weights) # 应用低秩变换 output = torch.matmul(attention_weights, V) return output, attention_weights
基本原理:
将序列分成多个块,在每个块内进行密集计算,块间进行稀疏计算:
实现代码:
class BlockSparseAttention(nn.Module): def __init__(self, d_model, block_size=256, inter_block_sparsity=0.9, dropout=0.1): super().__init__() self.d_model = d_model self.block_size = block_size self.inter_block_sparsity = inter_block_sparsity self.dropout = nn.Dropout(dropout) # 线性变换 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.block_mask = self._create_block_mask() def _create_block_mask(self): block_mask = torch.ones(self.block_size, self.block_size) # 创建块间稀疏掩码 if self.inter_block_sparsity < 1.0: block_mask = torch.zeros(self.block_size, self.block_size) # 随机选择一些块间连接 num_connections = int(self.block_size * self.block_size * (1 - self.inter_block_sparsity)) indices = torch.randperm(self.block_size * self.block_size, dtype=torch.long)[:num_connections] block_mask.view(-1)[indices] = 1.0 # 确保对角块是密集的 for i in range(0, self.block_size, self.block_size): end = min(i + self.block_size, self.block_size) block_mask[i:end, i:end] = 1.0 return block_mask def forward(self, x, mask=None): batch_size, seq_len, d_model = x.shape # 线性变换 Q = self.W_q(x) K = self.W_k(x) V = self.W_v(x) # 计算注意力 attention_scores = torch.matmul(Q, K.transpose(-2, -1)) / torch.sqrt(self.d_model) if mask is not None: attention_scores = attention_scores.masked_fill(mask == 0, -1e9) # 应用块稀疏掩码 num_blocks = (seq_len + self.block_size - 1) // self.block_size # 填充到块大小 pad_len = self.block_size - (seq_len % self.block_size) if seq_len % self.block_size != 0 else 0 if pad_len > 0: attention_scores = F.pad(attention_scores, (0, pad_len, 0, pad_len), value=0) # 应用块稀疏 attention_scores_sparse = attention_scores * self.block_mask # 计算注意力权重 attention_weights = F.softmax(attention_scores_sparse, dim=-1) attention_weights = self.dropout(attention_weights) # 截断回原始大小 if pad_len > 0: attention_weights = attention_weights[:, :seq_len, :seq_len] # 计算输出 output = torch.matmul(attention_weights, V) return output, attention_weights
性能比较:
| 稀疏方法 | 时间复杂度 | 内存占用 | BLEU分数 | 适用场景 |
|---|---|---|---|---|
| 全注意力 | O(n²) | 高 | 最高 | 短序列 |
| 局部注意力 | O(n·w) | 中 | 较高 | 中等序列 |
| 滑动窗口 | O(n·w·k) | 中-高 | 中 | 长序列 |
| Top-K注意力 | O(n·k) | 中 | 中-高 | 内容相关性强 |
| 低秩近似 | O(n·r) | 低 | 中 | 资源受限 |
参数设置:
调试技巧: