I·Infra Notes AI Infra 工程实践指南 ← 返回主博客
模型与缓存 / READING NOTES

4.1 从矩阵算子拼成 Decoder Block

RMSNorm、RoPE、MRoPE 与算子融合

x → RMSNorm → QKV Linear → RoPE(Q,K) → Attention(KV 写入/读取)
  → Output Linear → Residual → RMSNorm → Gate/Up Linear
  → SiLU(gate) * up → Down Linear → Residual
import torch

def rmsnorm(x, weight, eps=1e-6):
    xf = x.float()
    inv = torch.rsqrt(xf.square().mean(dim=-1, keepdim=True) + eps)
    return (xf * inv).to(x.dtype) * weight

def apply_rope(x, cos, sin):
    # x: [..., head_dim];约定相邻两维组成一对
    even, odd = x[..., 0::2], x[..., 1::2]
    y = torch.empty_like(x)
    y[..., 0::2] = even * cos - odd * sin
    y[..., 1::2] = even * sin + odd * cos
    return y

RoPE 必须明确布局约定:有的模型将前半维和后半维配对,不能把相邻维实现直接套用。cos/sin 的位置索引来自请求的真实 position_id,Prefix Cache、Chunked Prefill、抢占恢复、padding/packed batch 都可能改变“当前 token 在批中的列号”,但不能改变其在序列中的位置。MRoPE 对多模态位置使用多个坐标轴(如时间/高度/宽度);引擎必须把每轴位置元数据传到模型,不能用一个递增标量代替。模型配置决定具体轴布局。

优化:RMSNorm 的平方和用 FP32 累加,输出转回模型 dtype;融合 residual/RMSNorm 减少一次中间张量 HBM 往返。若 fused Kernel 与 PyTorch 不一致,先分别比较归一化前的 residual、平方均值、rsqrt、最终乘 weight。追问:为什么 RoPE 应加在 Q/K 而不是缓存后的 K 再重复加?历史 K 只需按原位置旋转一次。