# BitFlow MoE 架构设计文档 ## 1. 设计目标 在 BBC micro:bit(ARM Cortex-M4, 64MHz, 16KB RAM, 256KB Flash)上运行一个具有**理解能力**的微型中文语言模型。 核心挑战: - **内存限制**:可用 RAM ~108KB,模型权重 + 推理状态必须在这个范围内 - **计算限制**:64MHz CPU,无 FPU(但有硬件乘法) - **存储限制**:256KB Flash,预训练模型通常几百 MB,需极致压缩 ## 2. 架构选型 ### 2.1 为什么不用 Transformer? 标准 Transformer(即使是 2 层微型版)在 micro:bit 上存在问题: | 组件 | 标准 Attention | GLA (我们的选择) | |------|---------------|------------------| | 注意力矩阵 | O(n²) 状态矩阵 | O(d) 累积矩阵 | | 最大上下文 | ~16 tokens | 48 tokens | | 内存峰值 | 32KB+(仅注意力) | <5KB | | 推理速度 | 极慢(会卡死) | 实时 | **结论**:标准 Attention 的 O(n²) 状态维护在 micro:bit 上是灾难性的。GLA 将复杂度降到 O(n×d),状态矩阵从 n×n 压缩到 d×d。 ### 2.2 为什么不用纯 RNN? RNN 虽然内存 O(d),但: - 无法并行训练(梯度消失/爆炸) - 长程依赖能力弱 - 无法做到像我们这样的"理解"效果 GLA 是 RNN 的改进版,保留了 O(d) 内存,但增加了**可并行训练**的能力(通过离散时间注意力)。 ## 3. 核心架构 ### 3.1 门控线性注意力 (GLA) GLA 的核心公式: ``` S[t] = g[t] ⊙ S[t-1] + k[t] ⊗ v[t] z[t] = g[t] ⊙ z[t-1] + k[t] y[t] = Q[t] · S[t] · (Q[t] · z[t])⁻¹ ``` 其中: - **S** 是 d×d 累积状态矩阵(每次更新 O(d²)) - **k, q, v** 是通过 Linear 投影得到的向量 - **g** 是门控(gate),通过 sigmoid(log_decay) 控制衰减 - **⊙** 是 Hadamard 乘积,**⊗** 是外积 **关键优化**: 1. 不使用矩阵求逆,而是通过标量除法实现 2. 状态矩阵 S 只存储 d×d=40×40=1600 个浮点数(6.4KB) 3. 每次更新的复杂度是 O(d²) 而非 O(n²) ### 3.2 混合专家 (MoE) ``` 输入 → [Token Embedding] + [Position Embedding] ↓ [路由器: mean pooling → FC → argmax] ↓ 选择最优专家 ┌── Expert-0 (事实记忆) ├── Expert-1 (逻辑推理) └── Expert-2 (基础问答) ↓ [LayerNorm → GLA Block × 2] ↓ [LayerNorm → Output Head] ↓ 采样输出 ``` **为什么 MoE 有效**: - 每个专家专精一类任务,不需要一个"全能"模型 - 路由选择只激活一个专家,**推理计算量不增加** - 三个专家共享相同的结构(GLA Block × 2),代码复用率高 ### 3.3 路由器设计 ```python def router(x): # 1. 对所有 token 做 mean pooling → 固定维度 d=40 x_mean = x.mean(dim=1) # 2. 全连接层: 40 → 32 → 3 h = relu(fc1(x_mean)) # 40 → 32 logits = fc2(h) # 32 → 3 return logits # argmax 选专家 ``` **训练时**:使用交叉熵损失监督路由(labels 来自数据类别) **推理时**:只计算一次路由,选择最优专家 ## 4. 量化策略 ### 4.1 int8 量化 ``` scale = max(|w|) / 127 q_int8 = round(w / scale) # [-128, 127] w_rec = q_int8 × scale # 反量化 ``` - **量化精度**:int8(8-bit 有符号整数) - **量化感知训练 (QAT)**:训练后 2000 步(8000-10000 epoch)引入量化噪声,让模型适应 int8 精度 - **结果**:loss 从 0.0861 仅上升到 0.0863,影响极小 ### 4.2 内存分析 | 组件 | 全精度(float32) | int8 量化 | 压缩比 | |------|----------------|-----------|--------| | 模型权重 | 236.5KB | 57.7KB | **4.1×** | | 推理状态 | ~70KB | ~70KB | 无(运行时需要float) | | **总计** | **306.5KB** | **127.7KB** | **2.4×** | ## 5. 数据工程 ### 5.1 三层数据体系 ``` 基础层 (BASE, 69 条) └── 动物行为、颜色判断、位置关系 ↓ 提供基本语义 事实层 (FACT_QA, 76 条) └── 什么是XX、XX吃什么、XX在哪里 ↓ 提供知识推理 问答层 (CHAT_QA, 258 条) └── 问候、礼貌、自我介绍、喜好、情感、能力 ↓ 提供交互能力 ``` ### 5.2 数据增强 - 基础层 × 20 遍 - 事实层 × 40 遍 - 问答层 × 80 遍 总数据量:290,780 tokens,其中问答类占 74% ## 6. 训练流程 ``` [10,000 epochs] │ ├─ 0 - 8000 epoch: 标准训练 │ ├─ 学习率: 3e-3 (Cosine Annealing) │ ├─ Loss = 预测损失 + 0.5 × 路由损失 + 0.01 × 负载均衡损失 │ └─ 路由准确率: 34% → 100% │ └─ 8000 - 10000 epoch: QAT 量化感知训练 ├─ 每步对权重做 int8 量化 + 反量化 ├─ 模型适应量化噪声 └─ loss 微升后稳定 ``` ## 7. 推理流程 ``` [初始化] 加载 int8 权重 → 预解量化小权重 (SMW) │ ├─ [种子处理] 逐 token 前向传播,积累运行均值 │ └─ 路由器根据均值选择专家,激活对应 GLA Block │ ├─ [生成循环] │ ├─ 计算 token embedding │ ├─ 累加运行均值 → 路由 → 选专家 │ ├─ 专家 GLA Block × 2 层 │ ├─ LayerNorm → 输出头 │ ├─ 采样 (temperature=0.3) │ └─ 更新频率惩罚 (bcnt) │ └─ [输出] 字符 ID 列表 → 串口发送 ``` ## 8. micro:bit 内存布局 ``` 128KB Flash (总容量) ├── ~20KB: MicroPython 解释器 + 标准库 ├── ~58KB: int8 权重文件 ├── ~7KB: 推理代码 (bitflow_moe.py) └── ~43KB: 可用 (代码+数据段) 16KB RAM (总容量) ├── ~108KB: 实际可用 (nRF52833 有 256KB RAM, 但 MicroPython 用了一部分) ├── 58KB: 解量化后权重 (运行时) ├── 13KB: GLA 状态矩阵 (2层 × 40×40) ├── 4KB: 工作缓冲区 ├── 2KB: 预解量化小权重 └── ~30KB: 余量 (防止栈溢出) ``` ## 9. 设计决策总结 | 决策 | 选择 | 理由 | |------|------|------| | 注意力机制 | GLA | O(d) 内存,可并行训练 | | MoE 结构 | 3 专家硬路由 | 推理计算量不增加 | | 量化 | int8 + QAT | 体积压缩 4×,精度损失 <1% | | 训练策略 | 两步训练 + QAT | 先学语义,再适应量化 | | 路由方式 | 监督学习 | 保证每个专家都有任务 | | 采样温度 | 0.3 | 偏低,保证输出一致性 | ## 10. 局限性 1. **GLA 的衰减机制**:log_decay 是标量,无法按维度自适应(Mamba 可以做) 2. **硬路由**:每次只激活一个专家,无法融合多个专家的知识 3. **词表太小**:149 字符覆盖场景有限 4. **无上下文理解**:48 token 窗口限制了长对话能力 5. **未做消融实验**:无法精确量化每个模块的贡献