| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 22 天前 | ||
| 22 天前 | ||
| 22 天前 | ||
| 22 天前 | ||
| 22 天前 | ||
| 22 天前 |
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 无损,最易读。
三、四种语言要做的事(完全一致)
每个示例都做三步,逻辑一模一样,只看各语言写法:
- 读
artifact.bin全部字节; - 按上面格式解析出
K / n / D / centroids / assign; - 对
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.txt 中 y_expected 一致的 4 个数。
六、一个关键认知
这四种语言都没有重新实现 nrfunc——它们只是「消费生成物」:读取那段二进制、
做一次查表式的前向(0 阶就是把输入和它归属的那个质心点乘)。函数化真正的「聚类、
低秩、量化」都在上游 Python 里用 nrfunc 完成了。所以:
- 上游:
nrfunc.regionify+nrfunc.to_bytes(训练好→函数化→打二进制) - 下游:任意语言读二进制 + 前向(本目录演示的就是这一半)
这正是库的职责边界:nrfunc 负责「函数化 + 生成物」,推理落到哪门语言由你决定。