DeltaNet:DeltaNet朴素

DeltaNet:DeltaNet朴素
DeltaNet朴素.h// DeltaNet朴素.h —— Gated DeltaNet门控线性注意力声明// 用途qwen35moe 30 个递归层的核心计算——门控线性注意力线性复杂度注意力// 说明与普通注意力O(序列长度)不同DeltaNet 用固定大小的状态矩阵O(1) 空间// 每 token 只需一次状态更新因此长上下文推理不随序列增长而变慢#pragmaonce// 引入基础类型浮点/索引/向量#include公共/基础定义.h// 引入权重加载权重视图#include模型/权重加载.h// 引入状态缓存S 状态与 conv 历史#include推理/DeltaNet状态缓存.h// DeltaNet层参数一个递归层所需的全部权重与维度常量// 说明指向权重视图由调用方从 权重存储 绑定本结构不持有权重数据structDeltaNet层参数{// 投影权重二维const权重视图*Wqkvnullptr;// [输入维 2048 → qkv 混合 8192]const权重视图*Wgatenullptr;// [输入维 2048 → z 门控 4096]const权重视图*Walphanullptr;// [输入维 2048 → 衰减参数 32]const权重视图*Wbetanullptr;// [输入维 2048 → 更新门 32]const权重视图*Wconvnullptr;// [核 4 × 通道 8192]const权重视图*Wnormnullptr;// [状态维 128]输出门控 RMSNorm 权重const权重视图*Woutnullptr;// [4096 → 输出维 2048]// 一维参数f32 直通const权重视图*衰减对数nullptr;// ssm_a [32]负对数衰减参数const权重视图*时间步偏置nullptr;// ssm_dt.bias [32]// 维度常量size_t 输入维0;// 2048size_t 内维0;// 4096value_dim 总维 V 头数 × 头维度size_t 键头数0;// 16QK 头size_t 值头数0;// 32V 头size_t 头维度0;// 128每头维度size_t 状态维0;// 128状态矩阵维度 头维度size_t 通道数0;// 8192conv 通道 键维×2 值维size_t 核长度0;// 4conv 核长度size_t 时间步秩0;// 32ssm_dt_rankα/β/衰减参数的维度浮点 epsilon1e-6f;// 归一化/数值稳定系数};// DeltaNet朴素单 token 的 Gated DeltaNet 前向计算自回归模式// 参数输入 f32 输入向量长度 输入维参数 层权重与维度// 缓存 状态缓存S 与 conv 历史就地更新层号 当前层索引// 输出 f32 输出向量长度 输入维// 说明完整计算链详见实现// 投影 → 门参数β/g→ conv1d → SiLU → 切分 q/k/v → L2Norm// → 递归状态更新S e^g·S k·vᵀ→ 读取状态o S·q (q·k)v// → 门控 RMSNorm×SiLU(z)→ 输出投影voidDeltaNet朴素(const浮点*输入,constDeltaNet层参数参数,DeltaNet状态缓存缓存,size_t 层号,浮点*输出);DeltaNet朴素.cpp// DeltaNet朴素.cpp —— Gated DeltaNet门控线性注意力实现// 用途qwen35moe 30 个递归层的核心计算——门控线性注意力线性复杂度注意力// 说明本实现是「自回归单 token」路径每步只处理一个新 token就地更新状态。// 数学依据Gated DeltaNet从论文推导// - 状态矩阵 S ∈ R^{d×d}每头一个共 值头数 个是模型的「记忆」// 以 O(1) 空间编码全部历史信息与普通注意力的 KV 缓存 O(序列长度) 不同// - 衰减门 g 控制旧记忆的遗忘速率更新门 β 控制新信息的写入速率// - 更新v v − SkS e^g·S k·vᵀ外积更新// - 读取o S·q (q·k)·v#include内核/DeltaNet/DeltaNet朴素.h#include内核/DeltaNet/Conv1D朴素.h#include内核/GEMV/GEMV朴素.h#include内核/归一化/激活函数.h#include内核/归一化/RMSNorm朴素.h#include内核/归一化/门控RMSNorm朴素.h#include内核/归一化/L2Norm朴素.h#include公共/错误处理.h// 引入标准头数学函数指数/开方#includecmath// L2Norm沿头对 q/k 向量按「头」分组归一化每头独立 L2 归一化// 说明q 与 k 的形状 [头维度 × 头数]每头一个 128 维向量独立归一化// q/k repeat 到 值头数键头数 16 → 值头数 32每个键头复制到相邻 2 个值头staticvoidL2Norm按头(const浮点*输入,浮点*输出,size_t 头数,size_t 头维度,size_t 目标头数,浮点 epsilon){// 对每个头独立归一化先把归一化结果放到输出缓冲的头位置for(size_t h0;h头数;h){// 调用 L2Norm朴素 处理单个头128 维切片L2Norm朴素(输入h*头维度,输出h*头维度,头维度,epsilon);}// repeat如果目标头数 源头数键头 16 → 值头 32把每个源头复制到相邻目标头if(目标头数头数){// 目标头数必须是源头数的整数倍constsize_t 重复倍数目标头数/头数;// 从后往前复制避免覆盖未复制数据for(size_t 源头头数;源头--0;){// 源头 归一化结果在 输出[源头*头维度 ..]// 复制到 目标头 [源头*重复倍数 .. (源头1)*重复倍数-1]for(size_t 倍1;倍重复倍数;倍){constsize_t 目标头源头*重复倍数倍;// 源数据必须从「已归一化的输出」取——但源头位置可能已被覆盖// 解决先把源头结果存到临时再广播// 由于 源头 目标头复制方向从后往前源头位置在目标头之前不会被后写覆盖// 但源头位置本身若被更早的源头覆盖则有问题——从后往前遍历保证源头位置安全std::copy(输出源头*头维度,输出(源头1)*头维度,输出目标头*头维度);}}}}// DeltaNet朴素单 token 的 Gated DeltaNet 前向计算自回归模式// 说明完整计算链每步注释对应数学公式。状态就地更新S 写回缓存。voidDeltaNet朴素(const浮点*输入,constDeltaNet层参数参数,DeltaNet状态缓存缓存,size_t 层号,浮点*输出){// 从参数取维度constsize_t 内维参数.内维;// 4096value_dimconstsize_t 键维参数.键头数*参数.头维度;// 2048key_dimconstsize_t 头维度参数.头维度;// 128constsize_t 键头数参数.键头数;// 16constsize_t 值头数参数.值头数;// 32constsize_t 通道数参数.通道数;// 8192constsize_t 核长度参数.核长度;// 4const浮点 epsilon参数.epsilon;// 1e-6if(!参数.Wqkv||!参数.Wgate||!参数.Walpha||!参数.Wbeta||!参数.Wconv||!参数.Wnorm||!参数.Wout||!参数.衰减对数||!参数.时间步偏置){抛出运行错误(DeltaNet朴素层权重未绑定完整);}// —— 第 1 步投影 ——// qkv Wqkv·x8192 维k[2048] | q[2048] | v[4096]// z Wgate·x4096 维输出门控向量浮点qkv(通道数);向量浮点z(内维);GEMV朴素(*参数.Wqkv,输入,qkv.data());GEMV朴素(*参数.Wgate,输入,z.data());// —— 第 2 步门参数 ——// β sigmoid(Wβ·x)32 维更新门控制状态写入量// α Wα·x ssm_dt.bias32 维// g ssm_a · softplus(α)衰减门负值 → e^g ∈ (0,1) 遗忘因子向量浮点alpha(参数.时间步秩);向量浮点beta(参数.时间步秩);GEMV朴素(*参数.Walpha,输入,alpha.data());GEMV朴素(*参数.Wbeta,输入,beta.data());// 加偏置 softplus 乘 ssm_assm_a 为负对数衰减参数须先指数化再取负// 注llama.cpp 中 ssm_a 张量存的是 -exp(A_log)即负衰减系数// 门 ssm_a · softplus(αbias)。本实现按此语义直接相乘负数×正数负数向量浮点衰减门(参数.时间步秩);for(size_t i0;i参数.时间步秩;i){// 取偏置// 时间步偏置ssm_dt.bias可能以 f32 直通存张量类型 f32const浮点 偏置参数.时间步偏置?reinterpret_castconst浮点*(参数.时间步偏置-数据)[i]:0.0f;衰减门[i]reinterpret_castconst浮点*(参数.衰减对数-数据)[i]*激活Softplus(alpha[i]偏置);}// —— 第 3 步conv1d SiLU ——// 取历史 当前拼接做深度卷积auto历史缓存.获取Conv历史(层号);向量浮点conv输出(通道数);Conv1D朴素(历史,qkv.data(),reinterpret_castconst浮点*(参数.Wconv-数据),核长度,通道数,conv输出.data());// SiLU 激活激活SiLU向量(conv输出.data(),conv输出.data(),通道数);// —— 第 4 步切分 q/k/v L2Norm ——// conv 输出布局k[0:2048] | q[2048:4096] | v[4096:8192]const浮点*k_convconv输出.data();const浮点*q_convconv输出.data()键维;const浮点*v_convconv输出.data()键维*2;// q/k L2 归一化按头并 repeat 到值头数向量浮点q_归一(值头数*头维度);向量浮点k_归一(值头数*头维度);L2Norm按头(q_conv,q_归一.data(),键头数,头维度,值头数,epsilon);L2Norm按头(k_conv,k_归一.data(),键头数,头维度,值头数,epsilon);// —— 第 5 步递归状态更新核心——// 取状态 S就地更新形状 [头维度 × 头维度 × 值头数] 展平autoS缓存.获取S(层号);// v v − S·k每头v[头] v[头] − S[头]·k[头]// o S·q (q·k)·v每头// S e^g·S k·vᵀ每头外积更新向量浮点v_prime(值头数*头维度);向量浮点状态输出(值头数*头维度);for(size_t h0;h值头数;h){// 当前头的状态矩阵视图S[h][头维度×头维度] 按行主序S[h][i*头维度j]constsize_t 头状态偏移h*头维度*头维度;// 当前头的 k/q/v 向量const浮点*kk_归一.data()h*头维度;const浮点*qq_归一.data()h*头维度;const浮点*vv_convh*头维度;浮点*vpv_prime.data()h*头维度;浮点*o状态输出.data()h*头维度;// 衰减因子e^gg 为负 → ∈(0,1)const浮点 衰减std::exp(衰减门[h%参数.时间步秩]);// v v − S·kfor(size_t i0;i头维度;i){长浮点 累加0.0;for(size_t j0;j头维度;j){累加static_cast长浮点(S[头状态偏移i*头维度j])*static_cast长浮点(k[j]);}vp[i]v[i]-static_cast浮点(累加);}// o S·q (q·k)·v// 先算标量 q·k长浮点 qk0.0;for(size_t j0;j头维度;j){qkstatic_cast长浮点(q[j])*static_cast长浮点(k[j]);}for(size_t i0;i头维度;i){长浮点 累加0.0;for(size_t j0;j头维度;j){累加static_cast长浮点(S[头状态偏移i*头维度j])*static_cast长浮点(q[j]);}o[i]static_cast浮点(累加)static_cast浮点(qk)*vp[i];}// S e^g·S k·vᵀ外积更新就地写回for(size_t i0;i头维度;i){for(size_t j0;j头维度;j){constsize_t 下标头状态偏移i*头维度j;S[下标]衰减*S[下标]k[i]*vp[j];}}}// —— 第 6 步门控 RMSNorm ——// 输出 RMSNorm(状态输出, Wnorm) × SiLU(z)// z 形状 [头维度 × 值头数]内维 4096 128×32向量浮点门控输出(内维);门控RMSNorm朴素(状态输出.data(),reinterpret_castconst浮点*(参数.Wnorm-数据),z.data(),门控输出.data(),内维,epsilon);// —— 第 7 步输出投影 ——// out Wout·门控输出4096 → 2048GEMV朴素(*参数.Wout,门控输出.data(),输出);// —— 收尾把 qkv 推入 conv 历史供下个 token 卷积——缓存.推入Conv历史(层号,qkv.data(),通道数);}