当前位置: 首页 > article >正文

Transformer 从0到1:注意力机制的数学形式——Query, Key, Value 三元组

# Transformer 从0到1注意力机制的数学形式——Query, Key, Value 三元组## 1. 引言从序列建模的困境到注意力机制的诞生在深度学习的发展历程中处理序列数据如文本、音频、时间序列一直是核心挑战之一。早期的循环神经网络RNN及其变体LSTM、GRU通过隐状态的逐层传递试图捕捉序列中的时间依赖关系。然而这种循环结构存在两个根本性缺陷其一长距离依赖问题——当序列长度增加时梯度消失或爆炸使得网络难以学习相隔较远的元素之间的关系其二计算固有的顺序性——每个时间步的计算必须等待前一步完成无法充分利用现代并行计算设备如GPU的算力。卷积神经网络CNN虽然可以并行处理但通过堆叠卷积层来扩大感受野的方式本质上仍然是一种“局部”操作——要建立长距离依赖需要网络深度随着距离线性增长。这种“距离”与“计算路径长度”成正比的特性使得学习远距离交互变得困难。注意力机制的诞生打破了这一僵局。其核心思想简单而深刻**允许模型在处理序列的每一个位置时直接“关注”到序列中所有其他位置并根据内容动态地决定关注的程度**。这意味着任意两个位置之间的交互无论它们在序列中相距多远都只需要一次计算操作即点积计算路径长度被缩短为常数。2017年Vaswani等人发表的论文《Attention Is All You Need》提出了Transformer架构将注意力机制推向了极致。该论文的核心贡献在于**完全摒弃了循环和卷积仅依赖注意力机制构建整个序列转换模型**。其中注意力机制的数学实现被巧妙地形式化为 **Query查询、Key键、Value值三元组**。本文的目标是“从0到1”彻底拆解这一数学形式。我们将从最直观的直觉出发逐步构建其数学表达式解释每一个符号的含义剖析缩放点积注意力的原理并通过代码从零实现每一个细节。我们不仅会展示“如何做”更会深入探讨“为什么这样做”。全文力求详尽无篇幅限制旨在让读者真正理解注意力机制的本质。## 2. 从直觉到形式理解Query, Key, Value### 2.1 一个类比信息检索系统要理解Query、Key、Value最直观的方式是将其类比为经典的信息检索系统。想象你有一个庞大的数据库其中存储着许多文档。当你在搜索框中输入一个查询Query时检索系统会执行以下步骤1. 它将你的 **Query**比如几个关键词与数据库中每个文档的 **Key**比如文档的标题或摘要进行匹配。2. 匹配的过程通常计算Query与每个Key之间的**相关性分数**例如TF-IDF、BM25或向量相似度。3. 根据相关性分数系统对这些文档进行排序选出最相关的几个。4. 最后系统返回这些最相关文档的 **Value**比如文档的完整内容。在注意力机制中这个过程被完全“内化”为神经网络的运算。模型不再是检索外部文档而是在自身的特征空间中进行检索。具体来说- **Query**代表当前时刻“想要寻找什么”的表示。它源于当前正在处理的位置例如在机器翻译中当前要生成的目标词。- **Key**代表序列中每个位置“能够提供什么”的标识。它源于序列中所有位置例如源句子中的每个词。每个Key都可以被理解为该位置内容的“标签”或“索引”。- **Value**代表序列中每个位置“实际携带的信息内容”。它也与序列中所有位置一一对应。当某个位置的Key与Query匹配度高时其对应的Value就会被“提取”出来并参与到后续计算中。注意力机制的本质就是**使用Query去“检索”所有Key得到一组权重即注意力分布然后用这组权重对所有的Value进行加权求和作为最终的输出**。### 2.2 为什么要引入Q、K、V三元组为什么不像更早期的注意力机制如Bahdanau注意力那样直接用一个对齐函数计算当前状态与所有编码器状态的相关性引入Q、K、V三元组并将其作为独立的可学习投影的产物是Transformer实现高效、灵活、并行化建模的关键。关键在于Q、K、V通常是通过对同一个输入序列或不同序列进行**线性变换**得到的。假设输入序列的表示为 $X \in \mathbb{R}^{L \times d_{model}}$其中 $L$ 是序列长度$d_{model}$ 是模型维度那么$$Q XW^Q, \quad K XW^K, \quad V XW^V$$其中 $W^Q, W^K, W^V \in \mathbb{R}^{d_{model} \times d_k}$为了简化通常 $d_k$ 等于 $d_{model}$ 除以头数我们将在后续多头部分详述。这种设计的精妙之处在于1. **灵活的角色分离**同样的输入 $X$通过不同的投影矩阵被映射到三个不同的空间。Query空间负责“询问”Key空间负责“应答”Value空间负责“内容”。这种角色分离允许模型学习到更丰富的表示使得“询问-应答”的匹配机制与“内容”的提取机制解耦。2. **可学习的匹配函数**匹配函数Query与Key的相关性计算不再是固定的如余弦相似度或简单的拼接后线性映射而是通过可学习的投影矩阵 $W^Q$ 和 $W^K$ 隐式地学习得到的。实际上$\text{Attention}(Q,K,V) \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V$其中的核心 $QK^T$ 是一个双线性形式相当于对输入 $X$ 应用了一个可学习的马氏距离度量$(XW^Q)(XW^K)^T X (W^Q {W^K}^T) X^T$。3. **并行化与效率**通过矩阵运算一次性计算出所有位置对之间的注意力分数使得整个计算可以高度并行化这是Transformer能够成功扩展到大规模数据和模型的关键。## 3. 缩放点积注意力的数学形式### 3.1 基本公式Transformer中使用的注意力机制被称为**缩放点积注意力**Scaled Dot-Product Attention。其核心公式如下$$\text{Attention}(Q, K, V) \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V$$让我们逐一剖析这个公式中的各个部分。假设我们有一个长度为 $n$ 的序列作为“源”以及一个长度为 $m$ 的序列作为“目标”。在实际的自注意力中$nm$。但为了通用性我们保持区分。- $Q \in \mathbb{R}^{m \times d_k}$Query矩阵每一行代表一个查询向量目标序列中某个位置的表示。- $K \in \mathbb{R}^{n \times d_k}$Key矩阵每一行代表一个键向量源序列中某个位置的表示。- $V \in \mathbb{R}^{n \times d_v}$Value矩阵每一行代表一个值向量源序列中某个位置的内容表示。通常 $d_v d_k$。- $QK^T \in \mathbb{R}^{m \times n}$这是点积运算的结果。它的第 $i$ 行第 $j$ 列的元素 $(QK^T)_{ij}$ 表示第 $i$ 个Query与第 $j$ 个Key的点积。点积的几何意义是衡量两个向量在方向上的相似性——值越大表示两个向量的夹角越小方向越一致相似度越高。- $\sqrt{d_k}$缩放因子。$d_k$ 是每个Key/Query向量的维度。- $\text{softmax}(\cdot)$对矩阵的每一行应用Softmax函数将每一行的点积结果转化为一个概率分布和为1。即对于第 $i$ 行$\text{softmax}(z_i)_j \frac{e^{z_{ij}}}{\sum_{k1}^{n} e^{z_{ik}}}$。这样对于每个Query我们得到了一组关于所有Key的非负权重且总和为1。- $\text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right) \in \mathbb{R}^{m \times n}$注意力权重矩阵。- $\text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right) V \in \mathbb{R}^{m \times d_v}$最终的输出矩阵。其第 $i$ 行是第 $i$ 个Query对所有Value的加权求和即 $\sum_{j1}^{n} a_{ij} v_j$其中 $a_{ij}$ 是注意力权重。### 3.2 缩放因子 $\sqrt{d_k}$ 的作用这是公式中一个看似微小却至关重要的细节。为什么需要除以 $\sqrt{d_k}$其根本原因在于**控制点积的方差避免Softmax函数进入梯度极小的饱和区**。假设Query和Key的每个分量都是独立的随机变量均值为0方差为1。那么对于两个维度为 $d_k$ 的向量 $q$ 和 $k$点积 $q \cdot k \sum_{i1}^{d_k} q_i k_i$ 的均值为0方差为$$\text{Var}(q \cdot k) \sum_{i1}^{d_k} \text{Var}(q_i k_i) \sum_{i1}^{d_k} \mathbb{E}[q_i^2] \mathbb{E}[k_i^2] - (\mathbb{E}[q_i]\mathbb{E}[k_i])^2 \sum_{i1}^{d_k} (1 \cdot 1 - 0) d_k$$因此点积的方差随维度 $d_k$ 线性增长。当 $d_k$ 较大时例如在Transformer中常见为64、512甚至更高点积的数值会变得很大正或负。将这些大数值输入Softmax函数会产生什么后果Softmax函数 $\text{softmax}(z)_i e^{z_i} / \sum_j e^{z_j}$ 对输入值的大小非常敏感。如果某个 $z_i$ 远大于其他值那么其指数 $e^{z_i}$ 将主导分母使得该位置的softmax输出接近1而其他位置的输出接近0。这导致注意力权重几乎退化为一个**独热分布**one-hot distribution。更严重的是当 $z_i$ 的绝对值很大时其梯度 $\frac{\partial \text{softmax}(z)_i}{\partial z_j}$ 会变得非常小因为梯度中包含因子 $\text{softmax}(z)_i (1 - \text{softmax}(z)_i)$当概率接近0或1时该项趋近于0导致反向传播时梯度消失模型难以训练。除以 $\sqrt{d_k}$ 后点积的方差被归一化为1从而将点积的数值控制在合理的范围内通常介于 $[-3, 3]$ 或更小使得Softmax函数能够产生平滑的、有区分度但不极端的注意力分布有利于梯度流动和稳定训练。### 3.3 Softmax 与 加权求和在得到注意力权重矩阵 $A \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)$ 后我们将其与 $V$ 相乘。这一步骤有两个关键解释1. **软性信息选择**传统的“硬性”注意力hard attention是从多个Value中选出一个而这里采用的是“软性”注意力即对所有Value进行加权平均。每个Value的权重由其对应的Key与Query的匹配程度决定。这使得整个操作是可微的能够通过反向传播进行端到端的训练。2. **信息聚合**最终输出是Value向量的凸组合因为权重和为1。这使得输出向量位于所有Value向量张成的凸包内起到了信息聚合和平滑的作用。从信号处理的角度看这类似于一个**非线性的、内容自适应的滤波器**——滤波器系数注意力权重取决于输入信号本身通过Q和K。## 4. 代码实现从零构建缩放点积注意力理论阐述完毕现在让我们通过代码来具体实现这一机制。我们将使用Python和NumPy或PyTorch来构建目的是展示最纯粹的计算逻辑。pythonimport numpy as npimport scipy.special as spdef scaled_dot_product_attention(Q, K, V, maskNone, scaleTrue):缩放点积注意力的纯NumPy实现参数:Q: Query矩阵形状 (m, d_k)K: Key矩阵形状 (n, d_k)V: Value矩阵形状 (n, d_v)mask: 掩码矩阵形状 (m, n)可选。用于屏蔽某些位置如未来信息scale: 是否使用缩放因子默认为True返回:output: 注意力输出形状 (m, d_v)attention_weights: 注意力权重形状 (m, n)d_k Q.shape[-1]# 步骤1计算点积 QK^T# (m, d_k) (d_k, n) - (m, n)scores np.dot(Q, K.T)# 步骤2缩放可选if scale:scores scores / np.sqrt(d_k)# 步骤3应用掩码如果提供if mask is not None:# 将掩码中为0的位置设置为一个非常大的负数-1e9使得softmax后这些位置的权重趋近于0# 这里假设mask的形状与scores相同且mask中1表示保留0表示屏蔽scores scores (mask - 1) * 1e9 # 巧妙地将mask0的位置加上 -1e9# 步骤4对每一行应用Softmax# 使用scipy的softmax函数axis1表示对每一行独立操作attention_weights sp.softmax(scores, axis1)# 步骤5加权求和# (m, n) (n, d_v) - (m, d_v)output np.dot(attention_weights, V)return output, attention_weights# 示例演示if __name__ __main__:# 设置随机种子以保证可复现性np.random.seed(42)# 定义参数m 3 # Query数量例如目标序列长度n 5 # Key/Value数量例如源序列长度d_k 4 # Query/Key维度d_v 6 # Value维度# 随机初始化Q, K, VQ np.random.randn(m, d_k)K np.random.randn(n, d_k)V np.random.randn(n, d_v)# 计算注意力output, weights scaled_dot_product_attention(Q, K, V)print(Q (Query矩阵):)print(Q)print(\nK (Key矩阵):)print(K)print(\nV (Value矩阵):)print(V)print(\n注意力权重矩阵 (每一行是一个概率分布):)print(weights)print(\n每一行的和:, np.sum(weights, axis1))print(\n输出矩阵:)print(output)print(\n验证第一个输出应该等于第一个Query对应的注意力权重与V的加权和)print(手动计算第一个输出:, np.dot(weights[0], V))print(输出矩阵第一行:, output[0])运行上述代码我们可以清晰地看到计算过程scores是点积结果缩放后经过softmax转化为权重最后加权求和得到输出。这个简单的实现已经包含了注意力机制的核心。## 5. 自注意力与多头注意力### 5.1 自注意力当Q、K、V源自同一序列在上述例子中我们假设Q、K、V可以来自不同的序列。但在Transformer中最核心的应用是**自注意力**Self-Attention。在自注意力中Q、K、V均源自同一个输入序列 $X$。也就是说序列中的每个位置都可以“关注”到序列中的所有位置包括自身从而捕捉序列内部的依赖关系。在编码器中自注意力是**双向**的即每个位置可以关注到其左侧和右侧的所有位置。而在解码器中自注意力被设计为**因果**的causal即每个位置只能关注到它自身及其左侧的位置以防止未来信息泄露。这种因果约束通常通过**掩码**masking来实现我们将在后续的代码中展示。### 5.2 多头注意力从单视角到多视角单头注意力虽然强大但存在一个局限它只能学习到一种“匹配-聚合”模式。然而语言和序列数据中往往存在多种不同类型的关系。例如在一个句子中一个词可能同时与语法主语、宾语、修饰语等存在不同类型的依赖关系。**多头注意力**Multi-Head Attention的提出正是为了解决这一问题。其核心思想是将Q、K、V分别通过多组不同的线性投影投影到多个不同的表示子空间称为“头”在每个头中独立地执行缩放点积注意力然后将所有头的输出拼接起来再经过一次线性投影得到最终结果。数学形式为$$\text{MultiHead}(Q, K, V) \text{Concat}(\text{head}_1, \dots, \text{head}_h) W^O$$其中$$\text{head}_i \text{Attention}(QW_i^Q, KW_i^K, VW_i^V)$$这里- $W_i^Q \in \mathbb{R}^{d_{model} \times d_k}$$W_i^K \in \mathbb{R}^{d_{model} \times d_k}$$W_i^V \in \mathbb{R}^{d_{model} \times d_v}$$W^O \in \mathbb{R}^{h d_v \times d_{model}}$ 都是可学习的投影矩阵。- 通常我们设置 $d_k d_v d_{model} / h$这样每个头的维度降低总计算量与单头注意力大致相当但通过多头机制获得了更强的表示能力。多头注意力的优势在于1. **多角度表征**不同的头可以关注到不同类型的依赖关系。例如在机器翻译中某些头可能关注语法结构某些头可能关注语义相似性某些头可能关注长距离依赖。2. **增强稳定性**多个头的输出平均可以降低单个头随机性带来的影响使得整体表示更加鲁棒。### 5.3 代码实现多头注意力下面我们实现一个完整的多头注意力模块并集成掩码机制。pythonimport numpy as npclass MultiHeadAttention:多头注意力机制的NumPy实现def __init__(self, d_model, num_heads, dropout_rate0.0):初始化参数参数:d_model: 模型总维度num_heads: 头数dropout_rate: Dropout比率此处简化不实现实际dropout仅保留接口assert d_model % num_heads 0, d_model must be divisible by num_headsself.d_model d_modelself.num_heads num_headsself.d_k d_model // num_heads # 每个头的维度self.d_v self.d_k# 初始化权重矩阵# 为了简化这里使用标准差为0.01的随机初始化# 实际应用中通常会使用Xavier/Glorot初始化self.W_q np.random.randn(d_model, d_model) * 0.01self.W_k np.random.randn(d_model, d_model) * 0.01self.W_v np.random.randn(d_model, d_model) * 0.01self.W_o np.random.randn(d_model, d_model) * 0.01# 简化不实现真正的dropoutself.dropout_rate dropout_ratedef _split_heads(self, x, batch_size):将最后一维分割为 (num_heads, d_k) 并转置以方便并行计算输入形状: (batch_size, seq_len, d_model)输出形状: (batch_size, num_heads, seq_len, d_k)# 重塑: (batch_size, seq_len, num_heads, d_k)x x.reshape(batch_size, -1, self.num_heads, self.d_k)# 转置: (batch_size, num_heads, seq_len, d_k)return x.transpose(0, 2, 1, 3)def _combine_heads(self, x, batch_size):将多头表示合并回原始形状输入形状: (batch_size, num_heads, seq_len, d_k)输出形状: (batch_size, seq_len, d_model)# 转置: (batch_size, seq_len, num_heads, d_k)x x.transpose(0, 2, 1, 3)# 重塑: (batch_size, seq_len, d_model)return x.reshape(batch_size, -1, self.d_model)def scaled_dot_product_attention(self, Q, K, V, maskNone):缩放点积注意力批量化版本参数:Q: (batch_size, num_heads, seq_len_q, d_k)K: (batch_size, num_heads, seq_len_k, d_k)V: (batch_size, num_heads, seq_len_v, d_v) # 通常 seq_len_k seq_len_vmask: 可选掩码形状 (batch_size, 1, 1, seq_len_k) 或 (batch_size, seq_len_q, seq_len_k)返回:output: (batch_size, num_heads, seq_len_q, d_v)attention_weights: (batch_size, num_heads, seq_len_q, seq_len_k)d_k Q.shape[-1]# 计算点积# (batch, h, q_len, d_k) (batch, h, d_k, k_len) - (batch, h, q_len, k_len)scores np.matmul(Q, K.transpose(0, 1, 3, 2))# 缩放scores scores / np.sqrt(d_k)# 应用掩码if mask is not None:# 将掩码中为0的位置加上一个很大的负数# 扩展掩码维度以匹配 scores: (batch, 1, 1, k_len) 或 (batch, q_len, k_len)scores scores (mask - 1) * 1e9# Softmax# 为了避免数值溢出先减去每行的最大值稳定softmax# 为了简化直接使用scipy的softmax但这里我们手动实现# 注意对最后一个维度进行softmaxmax_vals np.max(scores, axis-1, keepdimsTrue)exp_scores np.exp(scores - max_vals)attention_weights exp_scores / np.sum(exp_scores, axis-1, keepdimsTrue)# 应用dropout略# 加权求和output np.matmul(attention_weights, V)return output, attention_weightsdef forward(self, Q, K, V, maskNone):多头注意力的前向传播参数:Q: Query形状 (batch_size, seq_len_q, d_model)K: Key形状 (batch_size, seq_len_k, d_model)V: Value形状 (batch_size, seq_len_v, d_model) # 通常 seq_len_k seq_len_vmask: 可选掩码返回:output: (batch_size, seq_len_q, d_model)attention_weights: 可选用于可视化batch_size Q.shape[0]# 步骤1线性投影# (batch, seq_len, d_model) (d_model, d_model) - (batch, seq_len, d_model)Q_proj np.matmul(Q, self.W_q)K_proj np.matmul(K, self.W_k)V_proj np.matmul(V, self.W_v)# 步骤2分割为多头# (batch, num_heads, seq_len, d_k)Q_heads self._split_heads(Q_proj, batch_size)K_heads self._split_heads(K_proj, batch_size)V_heads self._split_heads(V_proj, batch_size)# 步骤3在每个头上执行缩放点积注意力# 注意这里我们直接使用上面定义的方法attn_output, attn_weights self.scaled_dot_product_attention(Q_heads, K_heads, V_heads, mask)# 步骤4合并多头# (batch, seq_len, d_model)concat_output self._combine_heads(attn_output, batch_size)# 步骤5最终线性投影output np.matmul(concat_output, self.W_o)return output, attn_weights# 示例多头自注意力if __name__ __main__:# 设置参数batch_size 2seq_len 5d_model 16num_heads 4# 随机生成输入np.random.seed(42)X np.random.randn(batch_size, seq_len, d_model)# 创建多头注意力实例mha MultiHeadAttention(d_model, num_heads)# 自注意力Q、K、V都来自Xoutput, weights mha.forward(X, X, X)print(输入形状:, X.shape)print(输出形状:, output.shape)print(注意力权重形状多头:, weights.shape) # (batch, num_heads, seq_len_q, seq_len_k)# 检查每个头的注意力分布第一个batch第一个头print(\n第一个batch第一个头的注意力权重矩阵每行和为1:)head_weights weights[0, 0]print(head_weights)print(行和:, np.sum(head_weights, axis1))## 6. 深入数学细节梯度与性质分析### 6.1 注意力作为可微的索引机制从数学上看注意力机制可以视为一种**可微的字典查询**differentiable dictionary lookup。传统字典查询是离散的给定一个键 $k$从字典中返回对应的值 $v$。而在注意力中我们通过softmax将这种离散选择连续化。连续化的好处在于整个操作可微允许通过梯度下降来学习投影矩阵 $W^Q$、$W^K$、$W^V$。实际上我们可以将注意力输出对输入 $X$ 的导数展开来分析梯度如何流动。假设 $Q XW^Q$$K XW^K$$V XW^V$。则输出 $O \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right) V$。反向传播时梯度会通过三条路径流回 $X$通过 $W^Q$、$W^K$ 和 $W^V$。其中通过 $W^V$ 的路径相对直接而通过 $W^Q$ 和 $W^K$ 的路径涉及softmax的梯度这会产生一种**竞争性**的更新提升某个位置的权重会抑制其他位置的权重。### 6.2 对排列等变性与不变性的理解**自注意力**对于输入序列的排列具有**等变性**equivariance。这意味着如果我们将输入序列 $X$ 的各个位置进行任意排列那么经过自注意力模块后输出序列的对应位置也会按照同样的排列进行重排。这是因为点积 $QK^T$ 在行和列上同时重排softmax逐行操作最后的加权求和也遵循同样的重排。这种等变性使得Transformer在处理集合数据时天然适用。然而由于Transformer通常与**位置编码**positional encoding结合使用位置编码打破了排列不变性使得模型能够利用序列的顺序信息。### 6.3 计算复杂度分析缩放点积注意力的计算主要包含三个部分- $QK^T$ 的计算复杂度为 $O(m \cdot n \cdot d_k)$其中 $m$ 和 $n$ 分别是Q和K的序列长度。- Softmax复杂度为 $O(m \cdot n)$。- 加权求和复杂度为 $O(m \cdot n \cdot d_v)$。在自注意力中$m n L$序列长度因此总复杂度为 $O(L^2 \cdot d)$。其中 $L^2$ 是主导项。这意味着当序列长度 $L$ 增大时计算量呈二次增长这是Transformer在处理长序列如长文档、高分辨率图像时的主要瓶颈。这也是后来许多变体如Longformer、BigBird、Linformer等致力于将复杂度降低到 $O(L \log L)$ 或 $O(L)$ 的原因。## 7. 变体与扩展掩码、交叉注意力与位置编码### 7.1 掩码Masking的作用与实现在Transformer中掩码主要有两种应用场景1. **填充掩码Padding Mask**当序列中存在填充padding位置时这些位置不应该参与注意力计算因为它们是人为添加的不包含有效信息。填充掩码将这些位置对应的注意力分数设为 $-\infty$使得softmax后的权重为0。2. **前瞻掩码Look-ahead Mask**在解码器的自注意力中为了保证自回归性质即当前位置的预测只能依赖之前已生成的位置需要屏蔽未来位置的信息。前瞻掩码通常是一个上三角矩阵其中上三角部分不包括对角线被设为 $-\infty$。掩码在代码中通常与注意力分数相加如前面代码所示利用softmax的性质将屏蔽位置的权重置零。### 7.2 交叉注意力Cross-Attention在编码器-解码器架构中除了编码器内的自注意力和解码器内的自注意力外还存在**交叉注意力**。在交叉注意力中**Query 来自解码器的上一层的输出而 Key 和 Value 来自编码器的输出**。这样解码器在生成每个词时可以动态地关注到源序列中的相关信息这正是序列到序列seq2seq任务如机器翻译的核心。交叉注意力的数学形式与自注意力完全相同只是输入来源不同。这也是Transformer能够灵活处理不同模态信息如文本到图像的基础。### 7.3 位置编码Positional Encoding的必要性正如前文所述自注意力本身是排列等变的即它不包含任何关于位置顺序的信息。然而对于序列数据如自然语言顺序至关重要。因此Transformer需要一种方式来注入位置信息。Vaswani等人提出了**正弦位置编码**sinusoidal positional encoding。对于位置 $pos$ 和维度 $i$编码值为$$PE_{(pos, 2i)} \sin\left(\frac{pos}{10000^{2i/d_{model}}}\right)$$$$PE_{(pos, 2i1)} \cos\left(\frac{pos}{10000^{2i/d_{model}}}\right)$$这种编码具有以下性质- 每个位置的编码是唯一的。- 不同位置之间的编码可以通过线性变换相关联这有助于模型学习相对位置关系。- 编码值被限定在 $[-1, 1]$ 之间与词嵌入相加时不会造成数值不稳定。在实际应用中位置编码通常与输入嵌入相加然后送入后续的注意力层。## 8. 从零实现一个微型Transformer块为了将上述所有概念融会贯通我们现在从零实现一个完整的Transformer编码器块Encoder Block。它由两个主要子层组成**多头自注意力**和**前馈网络**Feed-Forward NetworkFFN。每个子层都应用了**残差连接**Residual Connection和**层归一化**Layer Normalization。pythonimport numpy as npclass FeedForward:前馈网络两个线性层中间使用ReLU激活def __init__(self, d_model, d_ff):self.W1 np.random.randn(d_model, d_ff) * 0.01self.b1 np.zeros(d_ff)self.W2 np.random.randn(d_ff, d_model) * 0.01self.b2 np.zeros(d_model)def forward(self, x):# x: (batch, seq_len, d_model)# 第一层线性 ReLUhidden np.matmul(x, self.W1) self.b1hidden np.maximum(0, hidden) # ReLU# 第二层线性output np.matmul(hidden, self.W2) self.b2return outputclass LayerNorm:层归一化简化版不含可学习的缩放和偏移实际应用中包含可学习的gamma和beta此处仅做演示def __init__(self, d_model, eps1e-6):self.eps eps# 可学习参数self.gamma np.ones(d_model)self.beta np.zeros(d_model)def forward(self, x):# x: (batch, seq_len, d_model)mean np.mean(x, axis-1, keepdimsTrue)variance np.var(x, axis-1, keepdimsTrue)x_norm (x - mean) / np.sqrt(variance self.eps)return self.gamma * x_norm self.betaclass EncoderBlock:一个完整的Transformer编码器块def __init__(self, d_model, num_heads, d_ff, dropout_rate0.0):self.mha MultiHeadAttention(d_model, num_heads, dropout_rate)self.ffn FeedForward(d_model, d_ff)self.layernorm1 LayerNorm(d_model)self.layernorm2 LayerNorm(d_model)# 简化未实现dropoutdef forward(self, x, maskNone):# 子层1多头自注意力 残差连接 层归一化attn_output, _ self.mha.forward(x, x, x, mask)# 残差连接x x attn_output# 层归一化x self.layernorm1.forward(x)# 子层2前馈网络 残差连接 层归一化ffn_output self.ffn.forward(x)x x ffn_outputx self.layernorm2.forward(x)return x# 测试编码器块if __name__ __main__:# 参数设置batch_size 2seq_len 5d_model 16num_heads 4d_ff 32# 随机输入X np.random.randn(batch_size, seq_len, d_model)# 创建编码器块encoder_block EncoderBlock(d_model, num_heads, d_ff)# 前向传播output encoder_block.forward(X)print(输入形状:, X.shape)print(输出形状:, output.shape)## 9. 总结与展望### 9.1 核心回顾本文从最基础的直觉出发逐步推导并实现了Transformer中注意力机制的数学形式——Query、Key、Value三元组。我们详细分析了缩放点积注意力的每一个组成部分点积度量相似性缩放因子 $\sqrt{d_k}$ 控制方差Softmax将分数转化为概率分布加权求和完成信息聚合。我们深入探讨了多头注意力的设计动机——捕捉多种依赖关系并通过代码完整实现了从单头到多头的过渡。我们还讨论了掩码机制、交叉注意力、位置编码以及完整的编码器块展示了这些组件如何协同工作构成一个强大的序列建模模块。### 9.2 注意力机制的哲学意义注意力机制之所以如此成功其背后蕴含的哲学思想值得深思。它让模型摆脱了“必须通过固定结构如循环或卷积来建模关系”的束缚转而采用一种“按需连接”的范式。模型不再被动地按照预设的拓扑结构传递信息而是根据内容本身动态地决定信息流动的路径和强度。这种动态性使得Transformer能够适应多种任务和数据结构从文本到图像从图到多模态。### 9.3 局限性与未来方向尽管Transformer取得了巨大成功但它并非完美无缺。我们已提及的 $O(L^2)$ 计算复杂度限制了其在超长序列上的应用。此外Transformer需要大量数据和大规模计算资源进行预训练对硬件要求较高。同时其对位置信息的处理仍然存在争议——绝对位置编码是否最优相对位置编码如RoPE、ALiBi能否更好地建模长度泛化未来的研究方向包括但不限于- **高效注意力**如稀疏注意力、低秩近似、核方法等旨在降低复杂度。- **结构化状态空间模型**如Mamba试图在保持线性复杂度的同时具备长距离依赖建模能力成为Transformer的有力竞争者。- **更长上下文的建模**随着序列长度增加到百万级别如何设计稳定且高效的模型成为关键。- **多模态统一架构**将Transformer进一步扩展在单一模型中处理文本、图像、视频、音频等多种模态。### 9.4 结束语从最初的“注意力”概念到如今主宰深度学习多个领域的Transformer架构Query、Key、Value三元组以其简洁而深刻的数学形式展现了一种优雅的设计智慧。理解这一机制不仅有助于掌握当前最先进的模型更能够为未来探索新的架构奠定坚实的基础。本文力求从数学和代码两个维度完整地呈现注意力机制的“从0到1”过程。希望读者在阅读之后不仅能够熟练使用现成的Transformer库更能洞悉其底层原理并在此基础上进行创新和拓展。

相关文章:

Transformer 从0到1:注意力机制的数学形式——Query, Key, Value 三元组

# Transformer 从0到1:注意力机制的数学形式——Query, Key, Value 三元组## 1. 引言:从序列建模的困境到注意力机制的诞生在深度学习的发展历程中,处理序列数据(如文本、音频、时间序列)一直是核心挑战之一。早期的循…...

BI 项目交付 SOP

...

dig (Domain Information Groper):从命令行到自动化运维的DNS探秘

1. 从命令行工具到运维利器的dig进化史 第一次接触dig命令时,我正被一个诡异的域名解析问题困扰。当时作为新手运维,只会用ping和nslookup反复测试,直到同事甩给我一行dig trace example.com——瞬间看到了完整的DNS解析链条,那种…...

机器学习在医疗诊断中的应用

机器学习在医疗诊断中的应用 【免费下载链接】Zettlr Your One-Stop Publication Workbench 项目地址: https://gitcode.com/GitHub_Trending/ze/Zettlr 背景 [[医疗诊断现状分析]]显示当前诊断方法的局限性。 方法 基于[[机器学习基础概念]]中的监督学习方法。 应用…...

llama-index 数据清洗示例、数据清洗等

文章目录示例数据清洗常见的需要清洗的数据数据清洗知识llama的一小块功能,主文章内容太多了,拆出来单独说下。示例 环境还基于之前的环境。 1、新建python文件clean_demo.py,代码: import os from llama_index.core import Do…...

基于OpenCASCADE7.4+OSG3.6.3+Qt5.12.7的多文档初级CAD/CAE...

基于opencascade7.4osg3.6.3qt5.12.7的多文档初级Cad/cae平台,支持十几种格式文件,包括step,igs,stl,obj,3ds,osg等,支持视角切换,显示模式切换,仿Cad命令注册机制,装配体显示,模型高…...

三极管信号滤波原理与工程实践

1. 三极管在信号滤波中的独特应用作为一名嵌入式硬件工程师,我经常需要处理各种传感器信号。最近在无刷电机驱动项目中,遇到了霍尔信号毛刺干扰的问题。传统教科书上总是强调三极管的放大作用,但实际工程中,我发现三极管在信号滤波…...

快马平台十分钟速建:openclaw机器人抓取参数可视化配置原型

最近在做一个机器人抓取控制的项目,需要快速搭建一个openclaw的参数配置界面。作为一个前端开发经验不多的工程师,我惊喜地发现InsCode(快马)平台可以帮我快速实现这个需求。下面分享下我的实现过程。 首先明确需求 这个配置工具需要实现五个核心功能&a…...

基于Maxwell的750W内转子伺服电机设计:14极12槽优化方案解析

基于maxwwell设计的经典750W,3000RPM 内转子 私服电机,14极12槽,外径76 轴向长度56.7 ,转矩1Nm,直流母线12V,辅助槽优化了齿槽转矩,特色是转子加工方便,永磁同步电机(PMSM BLDC&…...

【手把手教学】使用stitch 生成ui图,导入figma,再用codebuddy生成工程代码

目录 一.stich使用 1.1 关键词生成 1.2 生成ui图 1.3 导出figma​编辑 二. codebuddy使用 ​编辑2.1打开figma ​编辑 2.2 复制ui到设计面板 2.3生成工程代码 三. 结语 一.stich使用 stich官网地址 Google Stitch 是 Google Labs 推出的、基于 Gemini 大模型驱动的A…...

Java继承详解:从基础到实战,吃透面向对象核心特性

哈喽,各位Java学习者!今天咱们深入拆解面向对象编程(OOP)的三大核心特性之一——继承。作为Java开发的基础重点,继承不仅能帮我们实现代码复用、简化开发,更是后续理解多态、抽象类、接口的关键前提。不管你…...

QModMaster:5分钟掌握免费开源ModBus调试工具终极指南

QModMaster:5分钟掌握免费开源ModBus调试工具终极指南 【免费下载链接】qModbusMaster 项目地址: https://gitcode.com/gh_mirrors/qm/qModbusMaster 你是否在为工业设备调试而烦恼?面对复杂的ModBus通信协议,商业软件价格昂贵&#…...

告别预烘焙!在UE材质编辑器中实时生成FlowMap和法线贴图(附节点图)

实时材质魔法:UE引擎中FlowMap与法线贴图的动态生成技术 在游戏开发与动态视觉创作领域,材质表现的真实感与动态效果一直是技术美术师们追求的核心目标。传统工作流中,FlowMap(流场图)和法线贴图的生成往往依赖于外部软…...

AI辅助架构设计:让快马平台智能规划trae状态管理方案

用AI辅助设计trae状态管理方案:以博客后台系统为例 最近在开发一个博客后台管理系统时,遇到了状态管理的难题。系统需要处理文章列表、编辑草稿、用户评论和系统设置等多种数据,如何合理组织这些状态让我头疼不已。幸运的是,在In…...

群晖更换RAID类型无需重建服务,保持Volume磁盘盘符不变

我的环境:DSM型号:DS3617xs(黑群晖)系统版本:DSM 7.1.1-42962 Update 6硬盘数据库更新时间:2026-01-23更改前磁盘序号(btrfs):Raid1(volume1)&…...

K8s中pod的创建与销毁

刚开始学习,整了一下流程图1.pod的创建2.pod的销毁有不对的地方,大家共同探讨...

seo规则中的内容创作有哪些注意事项

SEO规则中的内容创作有哪些注意事项 在当今互联网时代,搜索引擎优化(SEO)已成为网站流量和曝光度提升的关键手段。其中,内容创作是SEO的核心要素之一。仅仅创作大量内容并不能保证网站的高排名和高流量。要想在百度等搜索引擎上取…...

在wsl中利用快马平台五分钟搭建flask博客后端原型

最近在Windows系统下折腾WSL(Windows Subsystem for Linux)时,发现结合InsCode(快马)平台可以快速搭建项目原型,特别适合需要Linux环境特性的开发验证。就拿搭建一个Flask博客后端来说,传统方式从零开始配置环境、编写…...

WSL+VSCode+Jupyter+R配置总结(2026年)

题记:网上相关的资料很多了,现阶段跟随AI也能少走很多弯路,但体验下来依旧有些细节没有被很好的提及,故写本文一方面作为自己的备忘录,一方面希望帮助更多像我一样的新手。 用了上述的配置跑了scanpy一年多了&#xf…...

告别重复编码:用Copaw结合快马平台,自动化生成你的常用工具模块

作为一名经常需要整理会议纪要的开发者,我一直在寻找能提升效率的工具。最近尝试用Copaw结合InsCode(快马)平台做了一个会议纪要自动生成器,效果出乎意料地好。整个过程几乎没写代码,却实现了核心功能,分享下具体实现思路&#xf…...

从 14 万美元支付事故看:AI 写的代码过了所有测试,为什么活不过生产?

我审计过的一家科技公司,曾因一段 AI 生成的异步支付处理代码,遭遇了一场灾难性的生产事故。这段代码完美通过了所有自动化检查、单元测试与集成测试,标注着「All checks passed」被顺利合并到生产环境,最终却触发了竞态条件与重复…...

FastAPI + SQLite:从基础CRUD到安全并发的实战指南

核心摘要本文将带你超越FastAPI SQLite的基础CRUD搭建,聚焦于安全防护(认证、授权、输入验证)与并发处理(数据库连接池、异步优化)两大实战痛点。你会获得一套可直接复用的项目骨架,并理解其背后的设计逻辑…...

MySQL

我目前正在学习SQL语句,我所了解到的MySQL其实是一堆服务器,在下载服务器的时候,可以选择下载一些客户端,MySQL会自带一些客户端,像类似于终端的小黑框,还有什么bench;我还是喜欢外观好看的客户端 !我学SQL语句目前学到了数据类型,有数值型的,字符型的,二进制型的,值得一提的是…...

OpenCore Legacy Patcher终极指南:让老旧Mac焕发新生的完整方案

OpenCore Legacy Patcher终极指南:让老旧Mac焕发新生的完整方案 【免费下载链接】OpenCore-Legacy-Patcher Experience macOS just like before 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher OpenCore Legacy Patcher&#xf…...

N_m3u8DL-CLI-SimpleG:Windows平台最简M3U8视频下载工具完全指南

N_m3u8DL-CLI-SimpleG:Windows平台最简M3U8视频下载工具完全指南 【免费下载链接】N_m3u8DL-CLI-SimpleG N_m3u8DL-CLIs simple GUI 项目地址: https://gitcode.com/gh_mirrors/nm3/N_m3u8DL-CLI-SimpleG 如果你正在寻找一款简单易用的M3U8视频下载工具&…...

一步步教你:星图平台部署Qwen3-VL:30B完整流程,Clawdbot飞书集成实战

一步步教你:星图平台部署Qwen3-VL:30B完整流程,Clawdbot飞书集成实战 想象一下这个场景:你的团队在飞书群里讨论产品设计,有人发了一张UI截图问“这个按钮位置是不是太靠下了?”;财务同事上传了一张发票照…...

Python智能内存回收实战:3种GC策略对比+4个生产级调优参数配置(附压测数据)

第一章:Python智能体内存管理策略生产环境部署在高并发、长生命周期的Python智能体服务中,内存管理直接影响系统稳定性与响应延迟。默认的CPython引用计数循环垃圾回收(GC)机制在动态对象频繁创建销毁的场景下易引发内存抖动和不可…...

如何用GPU加速的MediaPipe TouchDesigner插件实现实时视觉交互

如何用GPU加速的MediaPipe TouchDesigner插件实现实时视觉交互 【免费下载链接】mediapipe-touchdesigner GPU Accelerated MediaPipe Plugin for TouchDesigner 项目地址: https://gitcode.com/gh_mirrors/me/mediapipe-touchdesigner MediaPipe TouchDesigner插件是一…...

实战派指南:用MaPLe思路优化你的CLIP下游任务,附关键配置与避坑建议

实战派指南:用MaPLe思路优化你的CLIP下游任务,附关键配置与避坑建议 当CLIP遇上业务场景,90%的开发者都会遇到相同的问题:模型在新类别上的表现总是不尽如人意。上周团队用默认参数跑跨模态检索任务时,基类准确率82%的…...

大模型RL算法梳理:从全量词元到部分词元的路径演化

一、 引言:大模型强化学习算法的演化格局 近年来,以 OpenAI 的 o1 系列、DeepSeek 的 R1,以及 Qwen 系列模型为代表,大语言模型在数学证明、代码生成等长链路推理任务中展现出更强的稳定性与推理深度。 在这一背景下,面…...