文件最后提交记录最后更新时间
18 天前
18 天前
18 天前
README

project —— 成熟模型两种部署方式的实质四维对比(多对象检测大模型)

本项目是 nrfunc 库能力的端到端实跑 demo:用真实 MNIST 训练一个「单图多对象检测」大模型(13.77M 参数),然后走两条部署路线做实质性的四维(体积/内存/CPU/精度)对比。

        训练(Python)── 模型 ── ... ── 成熟模型(13.77M)
                                            │
                  ┌─────────────────────────┴─────────────────────────┐
                  │                                                     │
         路线 A(传统)                                       路线 B(函数化)
      成熟模型直接部署                                   函数化 → 生成物 → 部署
           │                                                  │
      model.bin(全稠密)                      functional.bin(fc2 区域函数)
           │                                                  │
           └────────────── Rust 独立二进制对比 ──────────────┘
                                  │
                        comparison.log(四维对比)

任务:MNIST 单图多对象检测(真实数据)

  • 数据:官方 MNIST,每张 28×28 画布随机放 1~2 个数字(各缩放到 8px),各落到 2×2 网格的不同格。
  • 输出:YOLO 式网格头,每格预测 [objectness(1) + 类别 one-hot(10) + bbox 偏移(4)],共 2×2×15 = 60 维。
  • 模型:共享骨架 784 → 4096 → 2048 → 1024,网格头 → 60。13.77M 参数,适配本机(RTX 5070 12G)可训练的大模型。

两条路线

路线 部署方式 产物 说明
A 传统 成熟模型直接部署 model.bin 全部层稠密权重(int8 量化)
B 函数化 函数化 → 生成物 → 部署 functional.bin fc2 层用区域函数(int8 量化),其余层稠密

两侧都极限压制:原型与优化物都量化到 int8(而非刻意让优化物迁就原型保持 f32), 在各自极限下对比谁更优。量化粒度实测 per-array 单 scale 优于 per-row(掉点 -0.08pt vs +0.03pt,见 train.py 量化评估)。

函数化对象是 fc2(4096→2048 宽层)——这是本项目最有价值的发现:

函数化掉点 结论
fc1(首层,784→4096) 30~80pt(灾难) 首层是特征提取层,函数化必崩,auto_alloc 正确判 skip
fc2(中间层,4096→2048) ≈0pt 宽层冗余足,函数化几乎无损,是正收益层

复现

# 1. 训练 + 函数化 + 导出产物(Python)
cd z:/Projects/AIG/nrfunc/examples/project
& z:\Projects\AIG\.venv\Scripts\python.exe -X utf8 train.py

# 2. Rust 部署对比(读产物,出比较日志)
cd deploy
cargo build --release
.\target\release\nrfunc-deploy.exe ..\artifacts

产物

文件 内容
artifacts/model.bin 成熟模型完整权重(路线 A,二进制)
artifacts/functional.bin 函数化生成物(路线 B:fc2 区域函数 + 其余稠密,二进制 NRFN 格式)
artifacts/stats.json Python 侧训练/函数化统计(配置,非生成物)
artifacts/comparison.log Rust 独立二进制产出的四维对比日志

生成物采用二进制(禁用 JSON):functional.bin 的 fc2 函数参数走 nrfunc.io.to_bytes (NRFN 自描述字节,int8 量化);model.bin 全距密权重 int8 量化(per-array 单 scale)。 stats.json 是配置/统计,非生成物,保留 JSON。

本轮实测结论(大模型,都 int8 极限压制,见 comparison.log)

维度 路线 A(int8 稠密) 路线 B(int8 函数化) 对比
fc2 层内存 67,125,248 B 6,325,256 B 省 90.6%
全模型内存 110,125,536 B 49,325,544 B 省 55.2%
体积(二进制) 13.77 MB 6.17 MB 省 55.1%
精度(Python 真实掉点) 74.76% cls acc 74.76% 掉 -0.005pt(无损)
CPU 单样本 6,962 µs 3,219 µs 快 2.16x
CPU 批量(64) 385,295 µs 136,865 µs 快 2.82x
GPU 单样本 557 µs 387 µs 快 1.44x
GPU 批量(64) 6,146 µs 4,342 µs 快 1.42x
  • 两路线输出 100% objectness 一致、99.22% 分类 argmax 一致;函数化 K=1 r=128 (auto_alloc signal='auto' 探测出的全局低秩形态)几乎无损,掉点 -0.005pt
  • 两侧都 int8 后:体积省 55.1%(13.77MB→6.17MB),CPU 快 2.16~2.82x,GPU 快 1.42~1.44x; K=1 全局低秩(K·r=128≪n=2048)共享计算收益在 CPU/GPU 都兑现(GPU 数值自检 1.2e-13)

关键洞察

  1. 模型够大,函数化收益才足:小 MLP(128×257 层)函数化全模型只省 11.6%,换成大模型 fc2(2048×4097 宽层)后省 55.2%,CPU 从"不省"翻成"快 2.2~2.8 倍"。
  2. 函数化层选择:首层(特征提取)函数化必崩,中间宽层(冗余足)才是正收益对象,auto_alloc 的逐层自适应 + eval_fn 真实掉点 <3pt 红线是可靠保障。
  3. 编译语言才兑现 CPU 提速:numpy 参考实现测不出函数前向提速(Python 循环 + gather 反而慢),Rust 下才干净兑现 2x+。
  4. GPU 端也能兑现:配 cuBLAS(共享 GEMM)+ 自定义 gather kernel,函数化 fc2 这层省算力(K·r=128≪n=2048)超过 H2D/D2H 边角传输,整网 GPU 前向快 1.42~1.44x。
  5. 两侧极限压制(int8)后优化物仍全面占优:体积省 55.1%、CPU 快 2.8x、GPU 快 1.44x,量化粒度 per-array 单 scale 掉点 -0.08pt(比 per-row 更好)。

设计要点

  • 训练/函数化用 Pythontrain.py,auto_alloc signal='auto' 自适应 + 真实掉点 <3pt 红线)
  • 生成物用二进制(禁用 JSON):model.bin(稠密 int8)+ functional.bin(fc2 走 to_bytes NRFN 格式 int8)
  • 部署用 Rust 独立二进制deploy/src/main.rs,零依赖 nrfunc_rust 库 + cudarc/cuBLAS,自带 register-blocking 稠密 GEMM + 8 路展开点积 + GPU 前向,手写二进制解析器读 .bin 含 int8 反量化)
  • Rust 自带确定性随机生成同一批测试输入,两路线输出直接对比

二进制格式(小端,跨语言自描述)

model.bin      :n_layers(4B) + 每层 [n_out(4B) n_in(4B) bits(1B) + 权重]
                 bits=32 直存 f32;bits=8/4 为 打包 int8/int4 字节 + scale(1×f32 在段尾)
                 (层序 fc1/fc2/fc3/head,per-array 对称量化)
functional.bin :fc1/fc3/head 三层的 [同上 int8] + func_blob_len(8B) + NRFN 块
NRFN 块(bits=8):magic'NRFN'(4B) version=2(1B) order(1B) bits(1B) K(4B) n(4B) D(4B) r(4B)
                 + signal_index(1B) op_version(1B)   ← v2 头共 25B
                 + means 打包 + scale(段尾) [+ components 打包 + scale + coeffs 打包 + scale if order=1]
                 + assign(n×i32)