Repository navigation
Basalt 3.10.0 — 底层算子补齐
Basalt 3.10.0
这一版补的是底层:让「库里没有的模型」也能自己写出来。
- 坐标 —
io.github.9178nekomc:basalt:3.10.0 - 在线文档 — https://9178nekomc.github.io/basalt/
- 运行要求 — Java 21,启动加
--add-modules jdk.incubator.vector - 规模 — 243 个公开类型 / 1595 个公开成员,全部带逐参数说明
为什么改这个
模型层不可能覆盖所有算法。决定上限的不是「内置了多少模型」,而是底层有多少可组合的算子。
3.10.0 之前,Tensor 只有 23 个公开算子,而且一个归约都没有。这意味着连
softmax 的分母都写不出来——不是效率问题,是根本写不出来。
新增
原生算子 22 → 31:
| 类别 | 算子 | 用途 |
|---|---|---|
| 数学函数 | exp log sqrt abs neg |
softmax 变体、对数似然、距离度量、自定义激活 |
| 二目运算 | div pow(走既有广播路径) |
归一化、幂律缩放、温度缩放 |
| 归约 | sumAll meanAll |
softmax 分母、任意损失压成标量 |
每个算子都是端到端接好的:枚举与操作码 → ops.rs 前向与反向 → engine.rs 分发 →
队列方法 → Tensor 方法 → Rust 单测 → Java 梯度检验。
手写 softmax 只要三行
Tensor numer = x.exp();
Tensor denom = numer.sumAll(); // 新增:全量归约
Tensor probs = numer.div(denom); // 新增:逐元素除法PrimitiveOpsTest 里就是用这三个算子拼出 softmax,断言概率之和为 1、
大输入得到大概率。库没有内置这个分支,走的是公开算子组合——
这是本版真正的验收标准:不给现成模型,也能写出来。
边界行为写在文档里
| 算子 | 边界 | 行为 |
|---|---|---|
log |
x ≤ 0 | 返回 -inf / NaN,不加 epsilon |
sqrt |
x < 0 | 返回 NaN |
div |
除数为 0 | 返回 inf / NaN |
pow |
负底数 + 非整数指数 | 返回 NaN |
abs |
x = 0 | 反向取次梯度 0 |
库不做隐式保护:加不加 epsilon 是算法决定,不是库决定。
还没做的
诚实列出来,不假装完备:
- 按轴归约:
sum(dim)/mean(dim)/max(dim),目前只有全量归约 - 索引算子:
gather/slice/cat,手写 embedding 变体与自定义注意力头需要 - 掩码:
where/maskedFill,变长序列与 causal mask 需要 - 自定义算子注册:用 Java 写前向与反向、不改 Rust 就能加算子。
这是「创造新算法」的最后一块,前置条件是延迟执行,否则每加一个算子都要多付一次 JNI
验证
- 25 个 Rust 单元测试(含 8 个新增:前向数值、反向、广播、非法形状拒绝)
PrimitiveOpsTest6 项:前向值、广播、跨上下文拒绝、逐个算子的中心差分梯度检验、手写 softmax./verify.sh全绿(9 组套件 + 6 个编译期拒绝用例,-Xlint:all零告警)- 文档:19 页 / 243 类型 / 1595 个成员全部有说明 / 0 占位符 / 0 断链
一个加载陷阱
NativeKernels 的查找顺序是 -Dbasalt.native.path → build/native/ → classpath。
build/native/ 优先于打包进 jar 的那份。本地留着一份旧库时,新编的库会被静默忽略,
报的还是旧版的错误信息(expected 1..=22)。
改完原生代码要么同步 build/native/,要么用 -Dbasalt.native.path 直接指过去。
产物
| 文件 | 说明 |
|---|---|
basalt-3.10.0.jar |
标准 jar,含四平台原生库 |
basalt-3.10.0-all.jar |
Fat jar,无外部依赖 |
basalt-3.10.0-sources.jar |
源码 |
basalt-docs-3.10.0.zip |
离线文档,19 页 |