Files
BitFlow-MoE/architecture.md
T

214 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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. **未做消融实验**:无法精确量化每个模块的贡献