File size: 17,695 Bytes
4e56b53 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 | /* runtime/lal_concept_attn.h — Concept-Aware Attention
*
* 基于「理解(概念-边界) + 推理(关系演化)」框架优化的注意力机制。
*
* 设计原点(从框架公理推导,非抄工程现成方案):
* - Attention 的本职:发现概念之间真实的关系,给关系分配权重,聚合V信息。
* - 算力浪费根源:标准自注意力强制对全部两两概念对执行 Q-K 匹配,
* 大量 token-pair 概念边界本身互相隔离,本来就几乎不会产生有效关系,
* 却依然完整计算 QK^T。
* - 优化目标:不破坏"识别关系强弱、绑定实体"这个理解能力,
* 砍掉大量无效概念对的匹配计算。
*
* 四层设计:
* Layer 1: 基于概念边界,区分「需要精细匹配」和「可以间接通信」的概念集
* - 片段内部:完整多头注意力
* - 片段之间:通过 segment-messenger 间接通信
* Layer 2: 关系强度门控,过滤本就边界隔离的概念对
* - 距离先验 + 粗粒度相似度快速预判
* - 软门控(保留极小概率回退通路,避免切断长距离指代)
* Layer 3: 多头的差异化算力分配(异构多头)
* - 局部语法头:只在局部窗口做匹配
* - 指代/因果头:可以访问窗口 + 信使
* Layer 4: 推理侧约束:区分训练阶段和推理 KV-Cache 的概念复用
* - 历史概念的 K/V 已编码完成,直接复用
* - 信使 token 也进入 KV-cache,保证间接长距离通路复用
*
* 与现有方案的本质区别:
* - Mistral 滑动窗口:单纯位置窗口,没有信使;长距离依赖能力弱。
* - Longformer global-token:全局固定几个 token 读取全部位置;
* 不是每个片段动态聚合本片段语义状态的信使。
* - Performer 线性注意力:纯数学核近似,不关心"概念边界、关系强弱"。
*
* 本优化只改造理解阶段(Attention)的信息交互通路,不改动 FFN 推理演化逻辑。
* 只要真实关系的匹配通路保留,上层推理能力就不会被破坏。
*
* Build: 与 lal_runtime.c 一起编译(#include "lal_concept_attn.h")
*/
#ifndef LAL_CONCEPT_ATTN_H
#define LAL_CONCEPT_ATTN_H
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/* ========================================================================
* Concept-Aware Attention Configuration
* ======================================================================== */
/* 头的访问域类型(Layer 3: 异构多头) */
typedef enum {
HEAD_LOCAL = 0, /* 局部语法关系(主谓宾、修饰):强局部性,小窗口 */
HEAD_MESSENGER = 1, /* 指代、实体绑定、因果时序:窗口 + 信使 */
HEAD_GLOBAL = 2, /* 全局兜底(少数头,访问全部历史 + 信使) */
} HeadAccessType;
/* 概念感知注意力配置 */
typedef struct {
/* === Layer 1: 片段 + 信使 === */
int segment_len; /* 语义片段长度 L(token 数)。0 = 禁用片段化 */
int num_messengers; /* 每个片段的信使数目 S (S << L)。默认 4 */
int messenger_neighbors; /* 邻近片段信使数(普通 token 可见的邻居片段数)。默认 2 */
int min_seg_len; /* 尾部封口最小片段长度:序列末尾累积 ≥ 此值时也触发信使生成。
默认 16。修复短样本(对话数据平均 11 token)信使永远不激活的 bug */
/* === Layer 2: 关系强度门控 === */
int gate_enable; /* 1 = 启用关系预筛选门控,0 = 禁用 */
int gate_window; /* 门控生效的局部窗口大小(窗口内才做门控) */
float gate_threshold; /* 概念边界相似度阈值,低于此值判定为边界隔离 */
float gate_fallback_prob; /* 回退概率(极小,避免硬切断长距离指代)。默认 0.01 */
int gate_distance_prior; /* 距离先验权重(>0 启用)。距离越远,门控越严 */
/* === Layer 3: 异构多头 === */
int hetero_enable; /* 1 = 启用异构多头,0 = 所有头同等访问 */
int n_local_heads; /* 局部语法头数量 */
int n_messenger_heads; /* 指代/因果头数量(访问窗口+信使) */
/* 剩余头 = HEAD_GLOBAL */
/* === Layer 4: 推理 KV-Cache === */
int cache_messenger; /* 1 = 信使也进入 KV-cache(保证间接长距离通路复用) */
/* === 主开关 === */
int enable; /* 1 = 启用概念感知注意力,0 = 回退到标准 attention_forward */
} ConceptAttnConfig;
/* 默认配置(唯一路线底座:CORE/BINARY/PRUNE + 浮点 + 概念注意力)
*
* 这是项目唯一保留的注意力路线——不再是"可选开关"。
* 四层优化(片段信使 / 关系门控 / 异构多头 / KV 复用)默认全开,
* 并按审查意见固化:去中心化门控、去中心化信使、窗口对称(128)、自适应阈值。
* 环境变量 LAL_CONCEPT_ATTN=0 仅作为调试逃生口(强制关,回退标准 attention)。 */
static inline ConceptAttnConfig concept_attn_default_config(void) {
ConceptAttnConfig c;
c.segment_len = 32; /* v21: 32 token 一个片段.
原 64 在对话数据(avg~11 token, 短样本多)下填不满,
信使机制空转、概念注意力收不到梯度 (探针实证).
改 32 后信使候选/质量/统计片段全部恢复(见 seg_len=32 测试).
注: segment_len 仍可由 LAL_CA_SEG_LEN 环境变量覆盖. */
c.num_messengers = 4; /* 每片段 4 个信使 */
c.min_seg_len = 4; /* 尾部封口最小片段长度:≥4 token 的短样本尾部也生成信使,
避免对话数据(avg 11 token)信使永远空转、概念注意力收不到梯度 */
c.messenger_neighbors = 2; /* 看 2 个邻近片段的信使 */
c.gate_enable = 1;
c.gate_window = 128; /* 窗口对称:概念路径与标准路径一致 (审查 Bug Fix 3: 32→128) */
/* 阈值语义:固定 0.1 在塌缩表征下失效(审查 Bug 2)。
* 运行时用门控分数分布的 P25 分位数自适应覆盖(概念注意力前向内维护 g_ca_quantile)。
* 此处的 0.1 仅作初始/兜底值,不再作为稳定工作点。 */
c.gate_threshold = 0.1f;
c.gate_fallback_prob = 0.01f; /* 1% 回退概率,避免硬切断长距离指代 */
c.gate_distance_prior = 1; /* 启用距离先验(去中心化后作为相对偏差项) */
c.hetero_enable = 1; /* 异构多头:局部/信使/全局分层算力 */
c.n_local_heads = -1; /* -1 = 自动:n_head 的一半 */
c.n_messenger_heads = -1; /* -1 = 自动:n_head 的 1/4 */
c.cache_messenger = 1; /* 信使进 KV-cache,保证间接长距离通路复用 */
c.enable = 1; /* 默认开启——概念注意力是基座路线,非可选 */
return c;
}
/* ========================================================================
* Messenger Cache (Layer 1 + Layer 4)
* ========================================================================
* 每层一个 MessengerCache,存储已聚合的片段信使 K/V。
* 信使是本片段全部概念与关系状态的压缩载体。
*
* 内存布局:
* messenger_k: [segment_capacity * num_messengers * n_embd] floats
* messenger_v: 同上
* segment_filled[segment_capacity]: 该片段的信使是否已生成
*/
typedef struct {
float *messenger_k; /* [segment_capacity * num_messengers * n_embd] */
float *messenger_v; /* 同上 */
uint8_t *segment_filled; /* [segment_capacity] 该片段信使是否已生成 */
int segment_capacity; /* 最大片段数(= n_ctx / segment_len + 1) */
int num_messengers; /* 每片段信使数 */
int n_embd; /* 嵌入维度 */
int n_filled; /* 已生成的片段数 */
} MessengerCache;
/* 分配信使缓存。在 model_load 后调用。 */
void messenger_cache_alloc(MessengerCache *mc, int segment_capacity,
int num_messengers, int n_embd);
/* 释放信使缓存 */
void messenger_cache_free(MessengerCache *mc);
/* 重置信使缓存(推理新会话开始时调用) */
void messenger_cache_reset(MessengerCache *mc);
/* ========================================================================
* Layer 1: Segment Messenger Generation
* ========================================================================
* 在每个 segment 内部,基于本片段全部 V,聚合生成少量信使向量。
* 信使是本片段全部概念与关系状态的压缩载体。
*
* 聚合策略:对片段内 V 做均匀分桶 + 均值池化
* - 将片段内 V[0..seg_len-1] 均匀分成 num_messengers 个桶
* - 每个桶内做均值池化,得到一个信使向量
* - 信使的 K = 信使的 V(自关联,简化)
*
* 数学复杂度:O(seg_len * n_embd),远小于注意力本身
*
* 语义意义:远方片段的整体语义,由信使代为表达。
* 普通token通过信使间接获得远方概念集合的状态,
* 而不是挨个访问每一个远方概念。
*
* 参数:
* v_seg: [seg_len * n_embd] — 本片段的 V 缓存
* seg_len: 片段长度
* out_k: [num_messengers * n_embd] — 输出信使 K
* out_v: [num_messengers * n_embd] — 输出信使 V
*/
void generate_segment_messengers(const float *v_seg, int seg_len, int n_embd,
int num_messengers,
float *out_k, float *out_v);
/* ========================================================================
* Layer 2: Concept Boundary Gate (关系强度门控)
* ========================================================================
* 给定 token-i(Q侧)、token-j(K侧),利用距离先验 + 粗粒度相似度
* 快速预判:如果预判两个概念边界隔离,潜在关系极弱,
* 直接把该位置置 -inf,不参与完整内积计算。
*
* 注意:不是简单按位置距离硬截断;是"概念边界是否可能产生关系"的软判断。
* 避免错误切断长距离指代这种真实强关系。
*
* 门控公式(软门控,保留回退通路):
* sim_coarse = <Q_i, K_j> / (||Q_i|| * ||K_j|| + eps) // 粗粒度余弦相似度
* dist_prior = exp(-distance / tau) // 距离先验
* gate_score = sim_coarse * (1 + gate_distance_prior * dist_prior)
* if gate_score < gate_threshold:
* 以 (1 - gate_fallback_prob) 概率置 -inf
* 以 gate_fallback_prob 概率保留(回退通路)
*
* 参数:
* q_i: [head_dim] — 当前 token 的 Q(某头)
* k_j: [head_dim] — 候选 token 的 K(某头)
* distance: i 和 j 的位置距离
* cfg: 概念感知注意力配置
* 返回:1 = 保留(参与完整 QK 计算),0 = 屏蔽(置 -inf)
*/
int concept_boundary_gate(const float *q_i, const float *k_j,
int head_dim, int distance,
const ConceptAttnConfig *cfg);
/* ========================================================================
* Layer 3: Heterogeneous Head Access Configuration
* ========================================================================
* 不同类型关系本身就有不同的"概念交互范围",不需要统一全序列扫描。
* - 头A:局部语法关系(主谓宾、修饰):强局部性,适合小窗口。
* - 头B:指代、实体绑定:偶尔需要长距离跳跃。
* - 头C:因果、时序关系:中等范围依赖。
*
* 配置:根据 n_head 自动分配头的访问域
* - 前 n_local_heads 个头 = HEAD_LOCAL
* - 接下来 n_messenger_heads 个头 = HEAD_MESSENGER
* - 剩余 = HEAD_GLOBAL
*/
HeadAccessType get_head_access_type(int head_idx, int n_head,
const ConceptAttnConfig *cfg);
/* 获取某头的实际访问窗口大小 */
int get_head_window(int head_idx, int n_head, int base_window,
const ConceptAttnConfig *cfg);
/* 某头是否可以访问信使 */
int head_can_access_messenger(int head_idx, int n_head,
const ConceptAttnConfig *cfg);
/* ========================================================================
* Layer 4: Concept-Aware Attention Forward (主入口)
* ========================================================================
* 概念感知注意力前向传播。整合四层优化:
*
* 1. 切成语义片段(segment_len)
* 2. 片段内部:完整 QKV,充分做片段内概念理解
* 3. 生成本片段信使:聚合本片段全部概念-关系状态
* 4. 本片段普通 token:只和【局部窗口 + 本片段信使 + 邻近片段信使】做匹配
* 5. 关系门控:过滤边界隔离的概念对(Layer 2)
* 6. 异构多头:不同头不同访问域(Layer 3)
* 7. KV-Cache:历史 K/V 直接复用,信使也进 cache(Layer 4)
*
* 参数:
* attn_out: [n_embd] — 输出
* qkv: [3 * n_embd] — Q | K | V 拼接,单 token
* n_embd, n_head: — 维度与头数
* seq_pos: — 当前序列位置
* k_cache, v_cache: — [n_ctx * n_embd] KV 缓存
* n_ctx: — 上下文长度
* cfg: — 概念感知注意力配置
* mc: — 信使缓存(可为 NULL,则不使用信使)
*
* 因果性:seq_pos 只关注 0..seq_pos(含)。
* 多头:head_dim = n_embd / n_head(必须整除)。
*/
void attention_forward_concept(float *attn_out, const float *qkv,
int n_embd, int n_head, int seq_pos,
float *k_cache, float *v_cache,
int n_ctx,
const ConceptAttnConfig *cfg,
MessengerCache *mc);
/* ========================================================================
* Layer 4: Concept-Aware Attention Backward
* ========================================================================
* 概念感知注意力反向传播。计算当前 token 的 Q/K/V 梯度。
* 缓存的 K/V(位置 0..seq_pos-1)视为常量(与 attention_backward 一致)。
*
* 参数:
* grad_qkv: [3 * n_embd] — 输出梯度(Q|K|V)
* grad_attn_out: [n_embd] — 来自上层的 attn_out 梯度
* qkv: [3 * n_embd] — 前向时用的 Q|K|V
* n_embd, n_head, seq_pos
* k_cache, v_cache, n_ctx
* cfg, mc
*/
void attention_backward_concept(float *grad_qkv, const float *grad_attn_out,
const float *qkv, int n_embd, int n_head,
int seq_pos,
const float *k_cache, const float *v_cache,
int n_ctx,
const ConceptAttnConfig *cfg,
MessengerCache *mc);
/* ========================================================================
* Transformer Layer Forward Integration
* ========================================================================
* trans_layer_forward 的实现包含比例缩放、残差归一化等复杂逻辑,
* 完整复制易引入 bug。推荐集成方式:
*
* 在现有 trans_layer_forward() 中,将 attention_forward 调用替换为:
* if (g_concept_attn_cfg.enable && g_messenger_caches) {
* attention_forward_concept(act->attn_out, qkv_ptr,
* n, cfg->n_head, abs_pos,
* tl->kv_k, tl->kv_v, cfg->n_ctx,
* &g_concept_attn_cfg,
* &g_messenger_caches[layer_idx]);
* } else {
* attention_forward(act->attn_out, qkv_ptr, n, cfg->n_head,
* abs_pos, tl->kv_k, tl->kv_v);
* }
*
* 反向传播同理:将 attention_backward 替换为 attention_backward_concept。
* 通过 model_set_concept_attn() 在运行时配置,无需修改模型结构。
*/
/* ========================================================================
* Global Config (便于全局开关,无需改 ModelConfig)
* ========================================================================
* 全局概念感知注意力配置。设为 enable=1 即启用。
* 优先级:ModelConfig 中的 concept_cfg > 全局 g_concept_attn_cfg。
*/
extern ConceptAttnConfig g_concept_attn_cfg;
/* 全局信使缓存(每层一个,按 layer_idx 索引)。
* 在 model_load 时分配,model_free 时释放。 */
extern MessengerCache *g_messenger_caches; /* [n_layer] */
/* Model-dependent functions (declared in lal_runtime.h after Model is defined):
* void model_messenger_caches_alloc(Model *m, const ConceptAttnConfig *cfg);
* void model_messenger_caches_free(void);
* void model_messenger_caches_reset(void);
* void model_set_concept_attn(Model *m, const ConceptAttnConfig *cfg);
*/
#ifdef __cplusplus
}
#endif
#endif /* LAL_CONCEPT_ATTN_H */
|