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

code —— 生成物「二进制调用」示例(Java / Go / Rust / C++)

本目录演示同一件事:用 nrfunc 把权重函数化成「生成物」、打成二进制字节块, 然后由 Java / Go / Rust / C++ 四种语言读取这段二进制、执行前向推理

为什么是二进制?生成物默认走二进制(而非 JSON 文本),是为了降低 IO 频率与体积—— 同一层权重,二进制(INT8)比 JSON 文本小约 86%,一次读写即可,不必逐字段/逐数组小 IO。

一、产物是怎么来的

先跑(需 nrfunc 已安装或能 import):

python make_artifact.py

它用 nrfunc.regionify 造一个最小编码样例n=4 输出单元、D=3 输入、K=2 区域、 order=0),再用 nrfunc.to_bytes(..., bits=32) 打成二进制:

  • artifact.bin —— 二进制生成物(63 字节)
  • input.txt —— 一个输入 x(3 维)+ Python 算出的期望输出 y(4 维),供四种语言比对

二、二进制格式(四种语言共同的协议)

bits=32(float32 无损直存)时,artifact.bin 的字节布局(小端):

偏移 0..23   头部(23 字节,struct '<4sBBBiiii')
               magic "NRFN"(4B) | version(1B) | order(1B) | bits(1B)
               | K(4B) | n(4B) | D(4B) | r(4B)     ← 全 int,小端
偏移 23..     centroids:K×D 个 float32(小端,按 K 行、每行 D 个)
偏移 +..      assign:n 个 int32(小端,第 i 个 = 单元 i 所属区域号)
  • order=0 时只有 centroids(质心);order=1 还有 means + components + coeffs(本示例用最简单的 0 阶)。
  • bits=8/4 时会量化(int16 + float32 scale),bits=32 则直接 float32 无损,最易读。

三、四种语言要做的事(完全一致)

每个示例都做三步,逻辑一模一样,只看各语言写法:

  1. artifact.bin 全部字节;
  2. 按上面格式解析出 K / n / D / centroids / assign
  3. input.txt 里的 x 算 0 阶前向:
y[i] = Σ_j x[j] * centroids[assign[i]][j]

然后打印 y,对照 input.txt 里的 y_expected,四门语言的输出应完全一致

四、文件清单

文件 语言 说明
make_artifact.py Python 生成 artifact.bin + input.txt(上游,用 nrfunc)
forward.go Go 标准库读取 + 解析 + 前向
Forward.java Java DataInputStream 读取 + 解析 + 前向
forward.rs Rust 手写小端解析 + 前向(零依赖)
forward.cpp C++ fstream 读取 + 手写小端转换 + 前向

五、怎么跑

# Go
go run forward.go

# Java
javac Forward.java && java Forward

# Rust
rustc forward.rs -O -o forward && ./forward

# C++
g++ -O2 forward.cpp -o forward && ./forward

每个程序的输出都应打印出与 input.txty_expected 一致的 4 个数。

六、一个关键认知

这四种语言都没有重新实现 nrfunc——它们只是「消费生成物」:读取那段二进制、 做一次查表式的前向(0 阶就是把输入和它归属的那个质心点乘)。函数化真正的「聚类、 低秩、量化」都在上游 Python 里用 nrfunc 完成了。所以:

  • 上游nrfunc.regionify + nrfunc.to_bytes(训练好→函数化→打二进制)
  • 下游:任意语言读二进制 + 前向(本目录演示的就是这一半)

这正是库的职责边界:nrfunc 负责「函数化 + 生成物」,推理落到哪门语言由你决定。