Skip to content

Basalt 3.10.0 — 底层算子补齐

Choose a tag to compare

@github-actions github-actions released this 01 Nov 02:31
· 5 commits to main since this release

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 个新增:前向数值、反向、广播、非法形状拒绝)
  • PrimitiveOpsTest 6 项:前向值、广播、跨上下文拒绝、逐个算子的中心差分梯度检验、手写 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 页