【FHE】我们如何实现同态加密推理(一):把 2B 大模型拆成 28 段密文链条来算(纯 C11 · 零依赖)

项目仓库
Gitee 主仓:https://gitee.com/pei-xiaoguang/kestrel-llm
GitHub 镜像:https://github.com/m13253246268-ship-it/kestrel-llm

相关文档(文中口径均以这些页面为准)
术语与数据口径
性能与基准
构建与复现
快速上手
架构总览

0. 一句话结论

我们让一个 2B 参数的大模型(Qwen3-VL-2B)在密文上做前向推理,做法不是"造一台更强的机器",而是把模型切成 28 段,每段各自成为一个可交接、可独立验证的密文包——于是一台普通 CPU、跑 1.8 小时,就能为整条链贡献一段。

成功的部分:前 5 层(层 0–4)已在纯 CPU 上可复现、可独立验证;链尾(层 26–27 + 收尾)也已跑通;交接是逐字节可验证的——第三方不需要信任打包者。

没做到的部分:参数是机制验证级,不是安全级;精度余量不宽;中间 21 层(层 5–25)在写这篇时仍待认领。


1. 为什么"单机跑完"从一开始就不是目标

同态加密推理的工程现实是这样的:模型有多大,密文就有多大;层数有多深,噪声预算就有多紧。

以我们这个配置为例(§2 会给完整口径):

模型 Qwen3-VL-2B-Instruct,dim=2048 layers=28 heads=16 ff=6144 vocab=151936
多项式次数 n=2048
槽位数 1024
缩放因子 scale = 2^60
层内前向链长(编译期) 112 个素数
自举链长(编译期) 2100 个素数

一枚"密文"是一个 RNS 表示的多项式环元素。n=2048、112 个 60-bit 素数、2 个分量的密文,落盘就是 ≈3.5 MB。而一层要处理 8 枚(4 个 token 位置 × 2 个分量)——一层就是几十 MB 的密文,而且噪声预算会随着深度被吃掉,必须周期性地"自举"把链刷回去。

换句话说:这条链的瓶颈不是算力总量,而是噪声预算的消耗速度与单机内存。

所以我们的设计选择是——按层横向切开


2. 观测口径(先交代前提,再上数字)

按本项目《术语与数据口径》§2.5 的纪律,先声明口径。

2.1 硬件与软件

开发/基准平台 AMD Ryzen 7 9800X3D(8 核 16 线程)
编译器 gcc,-O2 -fopenmp
依赖 libc + libm无 GPU / 无 NPU / 无第三方 FHE 库
引擎 纯 C11 自研,NTT / CKKS / 线程池均为本仓源码

2.2 参数口径

说明
CKKS_N 2048 编译期覆盖(头文件默认 1024)
CKKS_NPRIMES lay=112 / boot=2100 / verify_layer=112 编译期量,指该程序支持的最大链长
交接密文链长 np=112 落盘的 u{L}r112_*.ct
scale 2^60
线程数 T23_NT ⚠️ 线程数是结果变量,见 §4

2.3 可比性纪律(重要)

  • 跨轮次的绝对耗时不可比。本文所有耗时均为同轮交错 A/B,不引用历史会话的绝对值。
  • 耗时数字必须与线程数绑定陈述,否则无意义。

3. 一次"跳"(hop)的契约

整条链的定义非常窄,窄到可以写在信封背面:

layL :  u(L-1)r112  ──→  uL
bootL:  uL           ──→  u(L)r112
  • uX 表示"第 X 层的输出密文";后缀 r112 表示已刷新、链长为 112
  • 一跳 = lay(层内前向:QKV、attention、FFN、激活)+ boot(自举刷新)。
  • 一跳产出 8 枚密文(4 个 token 位置 × 2 个分量),打包交给下一位接力者。
  • 交接包是自描述的:密文 + 日志 + 元数据 + manifest.sha256

为什么必须交替?因为 lay 会把模数链吃掉,而 boot 把它刷回去。"链长"就是这条流水线上的油箱表

阶段 链长(素数个数) 编译期链长
输入(上一跳产出) 112
lay 消费后 16 112(t23lay
boot 刷新后 112 2100(t23boot

boot 之所以需要 2100 这么长的编译期链,是因为自举内部的折叠逼近需要很深的乘法深度;而落到盘上的交接密文只需要 112。

未核实项boot 日志里会打出 out np=2083 作为"刷回满链"的标志串(每层 8 次)。这个 2083 与编译期 2100、落盘 112 之间的精确对应关系,本文不做断言——引用时请以源码为准。


4. 为什么"拆开"不等于"不可信"

这是整个设计里最容易被质疑的一点:把密文交给不认识的人算,凭什么信?

我们的答案不是"信",而是让信任不必要

  1. 逐文件 SHA256。交接包内每个密文、每个日志都有哈希,manifest.sha256 自校验。归档实测:主包 104/104、链尾 45/45、工具包 6/6
  2. 独立复核工具verify_layer 自己做"解密 → 解码 → 对明文参考算 max|err|",刻意不采信驱动的 RESULT=PASS(这一点值得单独写一篇,见本系列第 14 篇)。
  3. 位级可复现。同一份源码、同一线程数,在 x86-64 与 aarch64 上产出逐系数位级一致
  4. 数据可自助生成。约 7 GB 的数据包不随仓库分发,但可以用 tools/preproc/ 的脚本从模型自助生成——我们验证过生成物与分发物逐字节相同(权重 9/9、26 个逐层模式文件 26/26)。

一个必须讲清的坑:线程数是结果变量

T23_NT=4T23_NT=8 跑出来的结果在位级上不同

根因不是"浮点误差",而是:vllm_ckks.c 里使用了 _Thread_local 随机数状态、并以固定常量播种——哪个线程抽到哪几个随机数,取决于调度器。于是"同一个计算"在不同线程数下消耗了不同的随机性,结果自然位级不同。

修复方案是按自同构指数 k 播种,让输出成为数学的函数,而不是调度器的函数

但必须如实说明:截至本文写作,这个修复还没有落地。源码里 ckks_rng_state 仍然由固定常量初始化:

/* vllm_ckks.c */
static _Thread_local uint64_t ckks_rng_state = 0x243F6A8885A308D3ull;

(文件里唯一一处时间相关播种在 ckks_self_test() 内,不在推理路径上。)所以"不同线程数结果位级不同"这条限制当前仍然成立,我们的交付口径也因此绑定线程数:所有可复现性声明都必须在固定 T23_NT 的前提下讲。

这件事的教训比 bug 本身重要:任何"并行 + 随机化 + 比对结果"的流水线,都有同一个陷阱,而且它不会体现在 max|err| 上——你不去比字节,就永远看不见它。(详见本系列第 13 篇。)


5. 安全边界(务请读完)

本文所述参数为机制验证级(n=2048、112 素数内层链、2100 素数自举链),
远低于 HE 参数标准的 128-bit 水平,不得用于保护真实数据。
本系列主张的是:可复现性与工程量账本。
本系列不主张:安全强度、性能优越性、与任何其他方案的优劣对比。

补充两点,避免任何误读:

  • n=2048 与自研的非密码学 RNG(原型级 xorshift)意味着这套东西不具备密码学安全强度。它的价值在于验证"机制能不能跑通、能不能被第三方复现"。
  • 我们不声称比任何其他同态加密推理方案更快或更优。本文出现的所有耗时数字,都是我们自己这份实现在我们自己这台机器上的账本。

6. 现在的进度(照实写)

部分 状态
层 0–4(链头) ✅ 可复现、可独立验证;lay 判据 RESULT=PASSboot 判据 BOOT=PASS
层 26–27 + 收尾 ✅ 已跑通并归档
层 5–25(21 跳) ⏳ 待认领
单跳耗时(实测) lay0 1920 s、boot0 4285 s → 一跳约 1.8 小时(8 核 CPU)
精度余量 ⚠️ 不宽。链尾抽样中 u27 为 8 项通过 6 项,其中 t1h1 = 3.82e-2t3h1 = 7.97e-2超出 verify_layer 默认容差 3e-2

最后一行我们本来可以不写。但按本项目的纪律——不漂亮的结论照实写


7. 这一篇的未解问题

  1. 自举是绝对瓶颈boot0 的分段 profile 显示,coeff_to_slot(1688 s)与 slot_to_coeff(1716 s)合计占了 约 80% 的时间,sin_fold 约 850 s。也就是说,继续优化乘法几乎不解决问题,瓶颈在域变换。
  2. 误差预算缺少统一理论:我们目前是逐层实测 + 逐层定标(tools/preproc/_adap_scale.py),没有一套"从 28 层反推每层可用误差预算"的推导。链尾那两项超阈,是否应该判定为失败而不是记账,我们还没定论。
  3. "下一棒不信任上一棒"在极端情况下是否成立:目前的验证是"产出对不对",还没有做到"上一棒的输入有没有被替换"——这一点需要额外的机制。

下一篇我们讲一个更底层的问题:为什么我们最终放弃了 BFV 方案——以及一个看上去无关紧要的细节(明文模上的除法语义)是怎么把 GELU 逼死的。

posted @ 2026-09-18 16:31  haliu  阅读(4)  评论(0)    收藏  举报