KDA (Key-Dependent Attention) 是一种基于线性注意力和 Delta Rule 的高效推理算子。 它通过将标准注意力的复杂度从 O(N²) 降低到 O(N), 实现了对长序列的高效处理,同时保持了与标准注意力相当的表达能力。
KDA 的核心思想是:将注意力计算转化为状态更新问题,通过维护一个固定大小的隐状态 (h-state), 实现对序列信息的压缩存储,从而避免了标准注意力中需要存储完整 KV Cache 的问题。
复杂度:O(N² · D) 时间 + O(N²) 空间
公式:O = softmax(QKT · scale) · V
特点:需要存储完整 N×N 注意力矩阵,KV Cache 线性增长
复杂度:O(N · D²) 时间 + O(D²) 空间
公式:ht = ht-1 · exp(gt) + vt · ktT,Ot = ht · qt
特点:固定大小状态,KV Cache 恒定不变
学习内容:Q/K/V投影 + ShortConv卷积 + 门控生成网络(α, β)
Checkpoint保存:所有网络权重;S矩阵不保存
训练算法:Chunk分块并行,块间传递S做反向传播
训练目标:学会"哪些维度要保留(α≈1)、哪些要遗忘(α≈0)、何时写入记忆(β)"
运行时:S从零初始化,实时迭代更新
Prefill:分块处理prompt,S逐步压缩上下文
Decode:每token更新S,输出 o = ST · q
对话结束:S直接丢弃,下轮重新置零
fused_gdn_gating 计算)。
patch_qwen3_5.py:130if self.layer_type == "linear_attention": self.linear_attn(...)elif self.layer_type == "full_attention": self.self_attn(...)layer_type 字段决定(Kimi/Qwen3.5 模型)KDA 基于线性注意力理论,通过将 softmax(QKT)V 分解为递推形式实现高效计算。
A = I + K·KT,w = (I + A)-1 · K·β
KDA 的完整前向传播分为两个路径:Chunk-KDA(分块并行)和 Fused Recurrent KDA(循环融合)。
g = chunk_local_cumsum(g, chunk_size=64) * RCP_LN2A[i,j] = Σ_k β[k] · K[i,k] · K[j,k] · exp2(g[i] - g[j]) (块内 K·K^T)Aqk[i,j] = Σ_k Q[i,k] · K[j,k] · exp2(g[i] - g[j]) · scale (块内 Q·K^T)
w = A_inv · K · β (用于后续 h-state 更新)u = A_inv · V · β (用于后续输出计算)
h = chunk_gated_delta_rule_fwd_h_kda(k, w, u, gk)o = q · h + Aqk · VKDA 的所有核心计算都通过 Triton kernel 实现,充分利用了 Triton 的自动调优、内存管理和硬件抽象能力。
| Kernel 名称 | 功能 | 核心操作 |
|---|---|---|
chunk_kda_scaled_dot_kkt_fwd_kernel_intra_sub_inter |
计算块间 K·K^T 矩阵 | 跨子块的 β·K·K^T 计算,支持门控衰减 |
chunk_kda_scaled_dot_kkt_fwd_kernel_intra_sub_intra |
计算块内 K·K^T 和 Q·K^T 矩阵 | 子块内的并行 dot 运算 |
solve_tril_16x16_kernel_kda |
16×16 基础分块求逆 | 下三角矩阵逆的原子操作 |
merge_16x16_to_32x32_inverse_kernel_kda |
32×32 分块求逆 | 合并两个 16×16 块 |
merge_16x16_to_64x64_inverse_kernel_kda |
64×64 分块求逆 | 合并四个 16×16 块 |
chunk_gated_delta_rule_fwd_kernel_h_blockdim64_kda |
计算 h-state 隐状态 | 分块 delta rule 更新,支持向量门控 |
fused_recurrent_gated_delta_rule_fwd_kernel |
循环递推 KDA | 逐 token 递推,支持连续批处理 |
recompute_w_u_fwd_kernel |
重计算 w 和 u | 根据 A_inv 计算权重矩阵 |
chunk_gla_fwd_kernel_o |
计算最终输出 | 结合 h-state 和 Aqk 计算输出 |
layer_norm_gated_fwd_kernel |
融合 LayerNorm + 门控 | 单 kernel 完成归一化和门控激活 |
# kda.py - Chunk-KDA 入口函数 def chunk_kda(q, k, v, g, beta, scale=None, initial_state=None, ...): if scale is None: scale = k.shape[-1] ** -0.5 # 可选的 L2 归一化 if use_qk_l2norm_in_kernel: q = l2norm_fwd(q.contiguous(), use_tiled_kernel=True) k = l2norm_fwd(k.contiguous(), use_tiled_kernel=True) o, final_state = chunk_kda_fwd( q=q, k=k, v=v.contiguous(), g=g.contiguous(), beta=beta.contiguous(), scale=scale, ...) return o, final_state
# kda.py - 分块前向传播核心逻辑 def chunk_kda_fwd(q, k, v, g, beta, scale, initial_state, ...): chunk_size = FLA_CHUNK_SIZE # 默认 64 # Step 1: 累积门控和 g = chunk_local_cumsum(g, chunk_size=chunk_size, ...) g = g * RCP_LN2 # Step 2: 计算 K·K^T 和 Q·K^T 矩阵 A, Aqk = chunk_kda_scaled_dot_kkt_fwd(q=q, k=k, gk=g, beta=beta, ...) # Step 3: 求解下三角矩阵逆 A = solve_tril_kda(A=A, ...) # Step 4: 重计算 w 和 u w, u, _, kg = recompute_w_u_fwd(k=k, v=v, beta=beta, A=A, gk=g, ...) # Step 5: 计算 h-state 隐状态 h, v_new, final_state = chunk_gated_delta_rule_fwd_h_kda( k=kg, w=w, u=u, gk=g, initial_state=initial_state, ...) # Step 6: 计算最终输出 o = chunk_gla_fwd_o_gk(q=q, v=v_new, g=g, A=Aqk, h=h, scale=scale, ...) return o, final_state
# fused_recurrent_kda.py - 逐 token 循环递推 @triton.jit(do_not_specialize=["N", "T"]) def fused_recurrent_gated_delta_rule_fwd_kernel(q, k, v, g, beta, o, ...): # 初始化隐状态 b_h = tl.zeros([BV, BK], dtype=tl.float32) if USE_INITIAL_STATE: b_h += tl.load(p_h0, mask=mask_h, other=0).to(tl.float32) # 主循环:逐 token 递推 for i_t in range(0, T): b_q = tl.load(p_q, ...).to(tl.float32) b_k = tl.load(p_k, ...).to(tl.float32) b_v = tl.load(p_v, ...).to(tl.float32) # KDA 特殊处理:向量门控 if IS_KDA: b_gk = tl.load(p_gk).to(tl.float32) b_h *= tl.exp(b_gk[None, :]) # 门控衰减 # Delta Rule 更新 b_v -= tl.sum(b_h * b_k[None, :], 1) # 残差计算 b_v *= b_beta b_h += b_v[:, None] * b_k[None, :] # 状态更新 b_o = tl.sum(b_h * b_q[None, :], 1) # 输出计算 tl.store(p_o, b_o.to(p_o.dtype.element_ty), mask=mask_v)
# solve_tril.py - 下三角矩阵求逆 @triton.jit(do_not_specialize=["T"]) def solve_tril_16x16_kernel_kda(A, Ai, cu_seqlens, chunk_indices, T, H, BT): # 加载 16x16 子块 b_A = tl.load(p_A, boundary_check=(0, 1)).to(tl.float32) b_A = -tl.where(m_A, b_A, 0) # 取严格下三角部分 # 迭代求解逆矩阵 for i in range(2, min(16, T - i_t * 16)): b_a = -tl.load(A + (i_t * 16 + i) * H * BT + o_i + offset) b_a = b_a + tl.sum(b_a[:, None] * b_A, 0) b_A = tl.where((o_i == i)[:, None], b_a, b_A) b_A += m_I # 加上单位矩阵 tl.store(p_Ai, b_A.to(...))
KDA 的设计充分体现了 Triton 的核心优势:将复杂的硬件优化细节封装在 Python 接口中, 让算法工程师专注于数学逻辑,同时自动获得接近手写 CUDA 的性能。
| Triton 特性 | 在 KDA 中的应用 | 效果 |
|---|---|---|
@triton.jit |
定义所有核心 kernel,支持 JIT 编译 | 运行时编译为目标平台代码,无需预编译 |
@triton.autotune |
自动搜索最优 num_warps/num_stages 配置 | 不同硬件自动适配最优参数,性能提升 20-40% |
@triton.heuristics |
运行时判断 IS_VARLEN、USE_INITIAL_STATE 等 | 避免不必要的分支,减少 kernel 开销 |
tl.make_block_ptr |
管理 Q/K/V 张量的分块访问 | 自动处理边界检查,避免数组越界 |
tl.dot |
矩阵乘法运算 (K·K^T, Q·K^T, w·h) | 映射到 Tensor Core / 矩阵运算单元 |
tl.exp / tl.exp2 |
门控衰减计算 | 硬件加速的指数运算 |
tl.load / tl.store |
所有内存读写操作 | 自动向量化,合并内存访问 |
tl.constexpr |
标记编译期常量 (K, V, BT, BV) | 编译期展开循环,消除运行时开销 |
内存管理:自动分配 Shared Memory,规避 Bank Conflict
线程调度:自动分配 Warp,优化线程束执行
向量化:自动合并内存访问,提升带宽利用率
流水线:自动插入预取指令,隐藏内存延迟
数学逻辑:只需描述"做什么",无需关心"怎么做"
分块策略:通过参数控制粒度 (BT=32/64/128)
精度控制:支持 IEEE/TF32 精度选择
变量长度:通过 cu_seqlens 支持变长序列
# kda.py 中的自动调优配置 BT_LIST_AUTOTUNE = [32, 64, 128] # 块大小搜索空间 NUM_WARPS_AUTOTUNE = [4, 8, 16, 32] # Warp 数量搜索空间 @triton.autotune( configs=[ triton.Config({"BT": bt}, num_warps=nw, num_stages=ns) for bt in BT_LIST_AUTOTUNE for nw in NUM_WARPS_AUTOTUNE for ns in [2, 3] ], key=["H", "D"], ) @triton.jit def kda_gate_fwd_kernel(g, A, y, g_bias, beta, threshold, T, H, D, BT, BD): # Kernel 实现... pass
| 文件 | 功能 | 核心函数 |
|---|---|---|
kda.py |
主入口,包含 chunk_kda 和 fused_recurrent_kda | chunk_kda(), fused_recurrent_kda(), layer_norm_gated_fwd() |
chunk_delta_h.py |
分块 h-state 计算,KDA 特有的 [V, K] 状态布局 | chunk_gated_delta_rule_fwd_h_kda() |
solve_tril.py |
下三角矩阵逆求解,支持 16/32/64 分块 | solve_tril_kda() |
fused_recurrent_kda.py |
逐 token 循环递推,支持连续批处理 | fused_recurrent_gated_delta_rule_fwd_kernel() |
cumsum.py |
块内累积和计算 | chunk_local_cumsum() |
l2norm.py |
可选的 QK L2 归一化 | l2norm_fwd() |
utils.py |
工具函数:chunk 索引、偏移量、缓存装饰器 | prepare_chunk_indices(), prepare_chunk_offsets() |