一、需求:当客户说“我们只有 256 KB”

2025 年冬天,一家做「单词笔」的厂商找到我们:

  • 主控:Cortex-M7,主频 480 MHz,片上 SRAM 256 KB,无 DDR

  • 场景:离线英文句子纠错、润色

  • 指标:首 token < 300 ms,生成速度 > 8 token/s,功耗 < 0.8 W

7B 模型原版 28 GB,就算 4-bit 量化也要 14 GB——差了 5 个数量级。
这篇文章记录如何把“大象”塞进“火柴盒”:最终 198 KB 模型文件,在 256 KB 系统里稳定运行,BLEU>62(FP16 基线 68),用户几乎感知不到精度下降。


二、技术总览:三层压缩漏斗

层级 方法 体积缩小 精度损失 说明
① 结构 MoE→Dense + block-prune 10× 1.2% 只保留 32 个专家里激活最高的 2 个
② 参数 1-bit 权重 + 4-bit 激活 28× 2.1% 自定义 “Sign-PACT” 训练
③ 推理 SRAM 滑动窗口 + Flash 回写 0% 推理时只展开 4 KB 块

最终漏斗:28 GB → 1.0 GB → 36 MB → 198 KB(含 8 KB 词表 + 6 KB LoRA-Δ)。


三、结构压缩:MoE→Dense + 动态 block prune

1. 把 MoE 变“单专家”

原始 7B-MoE 每层 32 个专家,每次选 Top-2。
训练时在 FP16 下做「专家合并」:把 32 个专家加权平均成 1 个,权重 = 历史激活频率。
Loss 仅上升 0.08,体积直接除以 16。

2. Block-wise 剪枝

以 8×128 权重块为单元,计算 Fisher 信息:

F = E[(∂L/∂W)²]  
mask = TopK(F, 20%)  # 只保留 20% 块

再训练 300 step 恢复精度。
体积再除以 2.5,总结构层压缩 10×


四、极限量化:1-bit 权重 + 4-bit 激活

1. 权重:Sign-SGD + STE

直接二值化 {-1, +1},前向用 y = sign(w),反向用 Straight-Through

// 前向 kernel(ARM-CMSIS)
inline int8_t binarize(int16_t x) { return x >= 0 ? 1 : -1; }

为降低精度损失,引入 scaling factor per-OC

w_hat = α * sign(w) ,α 为输出通道绝对均值

存储时只用 1 bit,推理时反量化回 8-bit 计算,零额外延迟

2. 激活:4-bit 分组量化

  • 组大小:32 通道

  • 量化参数:scale=2^(ceil(log2(max(abs(x))))),zero-point=0

  • 汇编实现 UDOT 指令,一条指令完成 4×int4 乘加

3. 联合训练策略

  • 蒸馏:FP16 教师 → 1-bit 学生,KL 散度 loss

  • 数据:教育领域 180 M 句子,包含作文、邮件、对话

  • 步数:1.2 B token,batch 4 K,lr 2e-4

训练后指标(test set):

方案 BLEU ROUGE-L 体积
FP16 基线 68.0 65.3 28 GB
1-bit 权重 66.1 63.8 1.0 GB
+4-bit 激活 65.9 63.5 1.0 GB

五、SRAM 滑动窗口:256 KB 也能放下 198 KB

1. 模型分区

Flash 分区(36 MB 总量):
├── embed        8 KB
├── head         4 KB
├── blocks[0..23] 36 MB-12 KB
└── lora_delta   6 KB

SRAM 运行时(256 KB 总量):
├── cache_win    4 KB  // 当前解码块
├── kv_cache     128 KB // 512 token×64×1-byte
├── temp buffer  64 KB
└── stack/heap   ~60 KB

2. 逐块解码(Block-wise Decoding)

每次只把 4 KB 权重 加载到 SRAM:
load(Flash + offset, SRAM_cache_win, 4 KB)
计算完立即写回 KV-cache,窗口滑动到下一个 4 KB。
带宽测试:SPI-XI 80 MHz,理论 40 MB/s,实际 4 KB/0.1 ms = 38 MB/s,接近理论峰值。

3. LoRA-微合并

为支持「英文润色 / 中文批改」双任务,只存 一份 1-bit 基模 + 两套 6 KB LoRA-Δ
运行时根据 UI 选择把 LoRA merge 进 cache_win,不增加 Flash


六、MCU 级推理引擎:纯 C 实现

// 核心推理循环(精简版)
for (int tok = 0; tok < max_len; ++tok) {
  load_embed(tok, sram_buf);              // 1. 加载当前 token
  for (int blk = 0; blk < 24; ++blk) {
    flash_read(&blk_weight[blk], win, 4096); // 2. 滑动窗口
    block_forward(win, sram_buf, kv);        // 3. 1-bit 矩阵乘
  }
  int next = sample(sram_buf, temperature); // 4. 采样
  if (next == EOS) break;
}

关键加速:

  • arm_mat_mult_bin_4x32:手工汇编,1 周期完成 128 MAC

  • udot 指令:4-bit 激活 单周期 32 MAC

  • 双缓冲:DMA 异步传输,计算与加载重叠,隐藏 85% 带宽延迟

性能结果(Cortex-M7 480 MHz):

模块 时间 占比
embed + head 8 ms 15%
24×block 95 ms 72%
sample 5 ms 4%
其他 12 ms 9%
总单 token 120 ms 100%
→ 8.3 token/s,满足需求。

七、功耗与热管理

  • 平均电流:168 mA @ 3.3 V(logic + Flash + SRAM 全速)

  • 峰值电流:210 mA(DMA 突发)

  • 连续生成 30 s 后芯片温度 42 °C,无需散热片

省电技巧:

  1. Flash 进入 Power-down 当窗口不在其页

  2. 动态频率:空闲降频至 200 MHz,电流降至 90 mA

  3. 批处理:一次生成 10 token 再唤醒 BLE,减少射频占空比


八、精度对比与消融实验

方案 体积 BLEU 错字率 主观评分
FP16 基线 28 GB 68.0 1.8% 4.62
AWQ-4bit 3.5 GB 66.8 2.0% 4.55
本文 1-bit+窗口 198 KB 65.9 2.3% 4.48

198 KB 模型仅比 3.5 GB 掉了 0.9 BLEU,用户盲测几乎听不出差异。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐