Basalt»3.4.0»API 参考»basalt.nn
模块 索引 GitHub

模块 basalt.nn

36 个公开类型 · 1 个包内可见类型

手写神经网络层:Dense / Conv2D / RNN / LSTM / GRU / 注意力、序列训练与序列化。

类型概览

类型种类说明
Activationenum强类型激活函数枚举,SOFTMAX 按批内每一行独立归一化。
CNNclassBCHW 输入、BC 输出的卷积网络,由分阶段 Builder 构建。
Conv2DclassBCHW 布局的二维卷积层,权重按 OIHW 排布。
Denseclass全连接层,权重形状为 (units, inputFeatures)。
Flattenclass把 BCHW 摊平成 BC,通道、高、宽依次排列。
GRUclass门控循环层:显式 BPTT 的 many-to-many GRU,输入输出均为 BSF。
GradientCheckclass用中心差分校验层与序列网络的解析梯度。
LSTMclass门控循环层:显式 BPTT 的 many-to-many LSTM,输入输出均为 BSF。
Layerinterface带编译期输入输出布局参数的可微变换接口。
Lossenum批量-通道布局上的损失函数,同时产出损失值与输出梯度。
LossEvaluationrecord标量损失与输出梯度的不可变结果,可选携带 softmax-logit 梯度。
MaxPooling2Dclass二维最大池化,缓存 argmax 索引以精确回传梯度。
ModelSerializerclassCNN 的 .bsl 持久化门面,负责层载荷的读写。
NeuralNetworkclass分阶段构建 CNN:图像层在 Flatten 之前,向量层在其后。
Optimizerenum一阶优化算法枚举:SGD、ADAM 与 ADAMW。
OptimizerConfigrecord训练共用的不可变优化器超参。
Paddingenum卷积补零模式:VALID 不补,SAME 补到按步长整除。
ParameterRefrecord单个可训练张量的实时视图:参数值与最近一次回传的梯度。
SelfAttentionclass单头缩放点积自注意力,支持因果掩码与完整 Softmax 反向传播。
SequenceNetworkclass端到端序列分类器:BSF → 若干序列层 → 池化 → Dense 头 → BC。
SequencePoolclass沿时间轴把 BSF 序列压缩为每个样本一个 BC 向量。
TrainingHistoryrecord逐轮训练损失的不可变快照。
VanillaRNNclass最简 Elman 循环层:h_t = tanh(W x_t + U h_{t-1} + b)。
Conv2D.BuilderclassConv2D 的分阶段构建器,默认 1 个 3×3 卷积核。
Dense.BuilderclassDense 的构建器,默认 1 个单元且无激活。
Flatten.BuilderclassFlatten 的构建器,无任何可配置项。
GRU.BuilderclassGRU 构建器,隐藏单元数默认 16,随机种子默认 42。
GradientCheck.Reportrecord梯度检验的最大相对误差与检查次数。
LSTM.BuilderclassLSTM 构建器,隐藏单元数默认 16,随机种子默认 42。
MaxPooling2D.BuilderclassMaxPooling2D 的构建器,默认 2×2 且不重叠。
NeuralNetwork.ImageBuilderclass图像阶段构建器,累积 Conv2D 与 MaxPooling2D。
NeuralNetwork.VectorBuilderclass向量阶段构建器,累积 Dense 并配置损失与优化器。
SelfAttention.Builderclass自注意力构建器,dimension 默认 16、seed 默认 42、非因果。
SequenceNetwork.Builderclass整网构建器;LAST 池化、分类交叉熵、Adam 0.001、裁剪 5.0。
SequencePool.Poolingenum时间轴归约策略。
VanillaRNN.BuilderclassVanillaRNN 构建器,隐藏单元数默认 16,随机种子默认 42。
enum Activation

强类型激活函数枚举,SOFTMAX 按批内每一行独立归一化。

枚举常量覆盖恒等、ReLU、Sigmoid、Tanh、Softmax、GELU、SiLU 七种。前六种可逐元素计算,SOFTMAX 需要整行数据才能归一化,因此不走标量路径。

三个计算方法都是包级可见:applyScalar 处理单个标量,applyRow 就地处理数组的一段连续区间,derivative 给出局部导数。Dense 与 Conv2D 在各自的前向、反向中调用它们,外部代码只通过 Builder 的 activation 参数间接选择。

GELU 使用 tanh 近似式 0.5x(1 + tanh(√(2/π)(x + 0.044715x³))),其导数按该近似解析求导,不是精确 erf 形式;SILU 为 x·σ(x),导数由 σ 与 x 组合得到。

示例
Dense output = Dense.builder()
        .units(3)
        .activation(Activation.SOFTMAX)
        .seed(7L)
        .build();

Tensor<BC> features = Tensor.ofBC(2, 4, new double[2 * 4]);
Tensor<BC> probabilities = output.forward(features);

Dense hidden = Dense.builder()
        .units(8)
        .activation(Activation.RELU)
        .build();

注意

applyRow 就地改写 values 的 [offset, offset + length) 区间,调用方需保证区间完整覆盖一行

注意

SOFTMAX 的 applyScalar 与 derivative 都会抛 IllegalStateException,其反向必须走完整雅可比,由 Dense 内部处理

注意

applyRow 的 softmax 先减去行内最大值再取指数,归一化因子非有限或非正时抛 IllegalStateException

注意

sigmoid 按输入符号分支计算,负输入不会出现 exp 溢出

参见:basalt.nn.Dense, basalt.nn.Conv2D

枚举成员

NONE()

恒等激活,输出等于输入,导数为 1。

用于输出层或调试,不引入非线性。

RELU()

修正线性单元,负值截断为 0。

导数按激活前取值判断:大于 0 时为 1,否则为 0,因此 0 点导数为 0。

注意

该常量会让 Dense 与 Conv2D 改用 √(6/fanIn) 的初始化幅度

SIGMOID()

逻辑函数 1/(1+e^-x),输出落在 (0,1)。

导数由激活值给出:a·(1-a),无需重新计算指数。

TANH()

双曲正切,输出落在 (-1,1)。

导数由激活值给出:1 - a²。

SOFTMAX()

按行归一化的 softmax,仅支持整行计算。

沿最后一维把一行映射为概率分布,行内元素之和为 1。标量接口不可用,Dense 的反向会用整行点积修正梯度。

注意

Conv2D.Builder 在 build 时拒绝该常量,卷积层只接受逐元素激活

GELU()

tanh 近似的 Gaussian Error Linear Unit。

近似常数 0.044715 与 √(2/π) 直接写入前向与导数,两者一致,梯度检查可通过。

SILU()

SiLU(Swish):x·σ(x)。

导数为 σ(1 + x(1 - σ)),只需一次 sigmoid 计算。

final class CNN implements Persistable

BCHW 输入、BC 输出的卷积网络,由分阶段 Builder 构建。

CNN 持有一个不可变的有序 Layer 列表、一个 Loss 与一个 OptimizerConfig。forward 顺序执行各层,backward 逆序回传,参数更新交给各层自身的 update,因此网络只做调度,不持有梯度。

fit 是全批量训练:每轮执行一次 forward、一次 Loss.evaluate、一次 backward,然后对每层各调用一次 update,并把该轮损失记入 TrainingHistory。没有小批量切分、学习率调度或早停,轮数完全由调用方给定。

训练路径对 Softmax 末端有专门处理:当最后一层是 Activation.SOFTMAX 的 Dense 且损失结果携带 softmax-logit 梯度时,直接把 (p - t) / batch 交给该层,省去在 Softmax 输出上再乘一次雅可比。

示例
CNN model = NeuralNetwork.builder()
        .add(Conv2D.builder().filters(4).kernelSize(3, 3)
                .activation(Activation.RELU).padding(Padding.SAME).build())
        .add(MaxPooling2D.builder().poolSize(2, 2).build())
        .add(Flatten.builder().build())
        .add(Dense.builder().units(3).activation(Activation.SOFTMAX).build())
        .loss(Loss.CATEGORICAL_CROSSENTROPY)
        .optimizer(Optimizer.ADAM.withLearningRate(0.01))
        .build();

Tensor<BCHW> features = Tensor.ofBCHW(8, 1, 4, 4, new double[8 * 16]);
double[] labels = new double[8 * 3];
for (int row = 0; row < 8; row++) {
    labels[row * 3] = 1.0;
}
Tensor<BC> targets = Tensor.ofBC(8, 3, labels);

TrainingHistory history = model.fit(features, targets, 5);
double finalLoss = history.lastLoss();

注意

构造器与 layers()、loss()、optimizer() 为包内可见,外部只能经 NeuralNetwork 的 Builder 创建模型,读层数用 layerCount()

注意

fit 不校验 features 是否为 BCHW 布局,形状错误会在首次 forward 时由具体层抛出

注意

fit 要求 features 与 targets 的第 0 维一致;损失出现非有限值时抛 IllegalStateException 并中止训练

注意

构建期不校验 loss 与末端激活是否匹配(SequenceNetwork 会校验),用 CATEGORICAL_CROSSENTROPY 时需自行把末端 Dense 设为 SOFTMAX

参见:basalt.nn.NeuralNetwork, basalt.nn.Loss, basalt.nn.ModelSerializer, basalt.nn.TrainingHistory

方法

predict(Tensor<BCHW> input) → Tensor<BC>

forward 的别名,用于推理。

与 forward 完全等价,不切换训练或评估状态,也不影响后续 backward。

参数
  • input (Tensor<BCHW>) – 形状 (batch, channels, height, width) 的输入张量

返回

形状 (batch, classes) 的预测

抛出
  • NullPointerException – input 为 null

fit(Tensor<BCHW> features, Tensor<BC> targets, int epochs) → TrainingHistory

执行全批量训练,返回逐轮损失。

每轮按 forward、evaluate、非有限检查、backward、逐层 update 的顺序推进,该轮损失写入结果数组。整批样本一次参与更新,轮数等于返回值的长度。

注意

训练就地修改各层参数,重复调用 fit 会从当前参数继续训练

注意

每轮只有一次参数更新,等效于整批梯度下降

参数
  • features (Tensor<BCHW>) – 训练输入,形状 (batch, channels, height, width)

  • targets (Tensor<BC>) – 训练目标,形状 (batch, classes),第 0 维须与 features 相同

  • epochs (int) – 训练轮数,必须为正

返回

长度为 epochs 的逐轮损失快照

抛出
  • IllegalArgumentException – epochs 不大于 0,或 features 与 targets 的批大小不一致

  • IllegalStateException – 某一轮损失出现 NaN 或无穷

evaluate(Tensor<BCHW> features, Tensor<BC> targets) → LossEvaluation

只计算损失与梯度,不更新参数。

先 forward 再调用 loss.evaluate;不执行 backward,因此不会写入任何层的梯度缓冲。

参数
  • features (Tensor<BCHW>) – 输入,形状 (batch, channels, height, width)

  • targets (Tensor<BC>) – 目标,形状须与预测完全一致

返回

标量损失与同形状输出梯度;分类交叉熵还带 softmax-logit 梯度

抛出
  • IllegalArgumentException – 预测与目标形状不一致,或取值超出所选损失的定义域

layerCount() → int

返回层数,含 Flatten 过渡层。

返回

层列表长度

kind() → ModelKind

返回模型种类,用于 .bsl 容器头部。

返回

常量 ModelKind.CNN

write(ModelWriter writer)

把模型载荷写入 ModelWriter。

转发给 ModelSerializer.writeCNN,依次写出损失序号、优化器类型与四个超参、层数以及每层的配置与已初始化的权重。

注意

尚未见过输入的层只写结构,参数以 -1 标记,加载后需重新 forward 才会初始化权重

参数
  • writer (ModelWriter) – 目标写入器,容器头由 basalt.io.ModelCodec 负责

final class Conv2D implements Layer<BCHW, BCHW>

BCHW 布局的二维卷积层,权重按 OIHW 排布。

输入按 (batch, channels, height, width) 组织,输出为 (batch, filters, outputHeight, outputWidth)。卷积核按 OIHW 顺序连续存放,长度 filters·channels·kH·kW,偏置长度 filters;前向与反向都是显式多重循环。

参数在首次 forward 时惰性创建并锁定通道数,不符时抛 IllegalArgumentException。权重由 Random(seed) 在 [-limit, limit] 上均匀取值,limit 在激活为 RELU 时取 √(6/fanIn),否则取 √(6/(fanIn + fanOut)),偏置置 0。

backward 返回对输入的梯度,同时把卷积核与偏置梯度累加到内部缓冲(每次反向先清零)。update 消费这些缓冲并推进优化器状态,之后梯度标记失效,须重新执行 forward 与 backward。

示例
Conv2D conv = Conv2D.builder()
        .filters(8)
        .kernelSize(3, 3)
        .strides(1, 1)
        .padding(Padding.SAME)
        .activation(Activation.RELU)
        .seed(7L)
        .build();

Tensor<BCHW> input = Tensor.ofBCHW(2, 3, 8, 8, new double[2 * 3 * 8 * 8]);
Tensor<BCHW> featureMap = conv.forward(input);

Tensor<BCHW> gradient = Tensor.zerosBCHW(2, 8, 8, 8);
conv.backward(gradient);
conv.update(new OptimizerConfig(Optimizer.ADAM, 0.001, 0.9, 0.999, 1e-8));

注意

输入必须是 BCHW,即单样本 NCHW 的批形式;层内不校验 rank,按位置读取第 1 至第 3 维

注意

SAME 的补零是隐式的:越界索引直接跳过,不物化填充缓冲区,补零不占额外内存

注意

反向依赖最近一次 forward 缓存的输入与预激活,连续两次 forward 会覆盖缓存

注意

fanIn 为 channels·kH·kW,fanOut 为 filters·kH·kW,两者都计入卷积核面积

注意

前向与反向都是逐输出位置的显式循环,不做 im2col 或频域变换,因此除输出与输入梯度外不再分配中间缓冲区

注意

filters、卷积核与步长在 build 时校验为正,activation 为 SOFTMAX 时拒绝构建

注意

参数更新不做 batch 缩放,卷积核梯度是批内求和结果

参见:basalt.nn.Layer, basalt.nn.Padding, basalt.nn.MaxPooling2D, basalt.nn.Flatten, basalt.nn.Activation

方法

builder() → Builderstatic

创建 Conv2D 的分阶段构建器。

返回

新的 Builder,默认 1 个 3×3 卷积核、步长 1、VALID 补零、RELU 激活、seed 42

forward(Tensor<BCHW> input) → Tensor<BCHW>

执行前向卷积,并缓存反向所需状态。

首次调用时按输入通道数创建卷积核与偏置;随后按 padding 计算输出尺寸与前侧、左侧补零量,逐输出位置累加通道与窗口内的乘积。

注意

输出尺寸公式见 Padding;SAME 下只取 total/2 作为前侧与左侧偏移

参数
  • input (Tensor<BCHW>) – 形状 (batch, channels, height, width) 的输入张量

返回

新分配的形状 (batch, filters, outputHeight, outputWidth) 张量

抛出
  • IllegalArgumentException – 通道数与首次 forward 锁定的值不一致,或 VALID 下卷积核大于输入空间尺寸

backward(Tensor<BCHW> gradient) → Tensor<BCHW>

回传梯度,累加卷积核与偏置梯度。

对每个输出位置先用激活导数缩放上游梯度,再按窗口把梯度累加进卷积核缓冲与输入梯度。

注意

每次反向开始时清零卷积核与偏置梯度缓冲,结果不跨批累积

参数
  • gradient (Tensor<BCHW>) – 损失对输出的梯度,形状须为 (batch, filters, outputHeight, outputWidth)

返回

对输入的梯度,形状 (batch, channels, height, width)

抛出
  • IllegalStateException – 尚未执行 forward

  • IllegalArgumentException – 梯度形状与最近一次前向输出不一致

update(OptimizerConfig optimizer)

用最近的梯度更新卷积核与偏置。

按 optimizer 指定的算法就地更新两个参数数组,并推进其内部迭代步。

注意

更新后梯度标记失效,连续两次调用会抛异常

参数
  • optimizer (OptimizerConfig) – 优化器超参数;SGD 只用学习率,ADAM 与 ADAMW 另用 beta1、beta2、epsilon

抛出
  • IllegalStateException – 参数尚未创建,或未完成一次 forward 加 backward

final class Dense implements Layer<BC, BC>

全连接层,权重形状为 (units, inputFeatures)。

Dense 把 (batch, features) 映射为 (batch, units)。权重按 [unit][feature] 行主序连续存放,长度为 units·features;偏置长度 units。每个输出单元先做点积加偏置,再按整行应用激活函数。

参数在首次 forward 时按输入第 1 维创建并锁定特征数。权重在 [-limit, limit] 上均匀取值,limit 在激活为 RELU 时取 √(6/features),否则取 √(6/(features + units));偏置初始化为 0。

反向有两条路径:常规 backward 用激活导数逐元素求局部梯度;backwardFromSoftmaxLoss 把传入梯度视为损失对 logits 的梯度,跳过 softmax 雅可比。SOFTMAX 的常规反向用整行点积 g·a 修正,等价于乘完整雅可比。

示例
Dense dense = Dense.builder()
        .units(10)
        .activation(Activation.RELU)
        .seed(1L)
        .build();

Tensor<BC> input = Tensor.ofBC(4, 6, new double[4 * 6]);
Tensor<BC> hidden = dense.forward(input);

dense.backward(Tensor.zerosBC(4, 10));
dense.update(Optimizer.SGD.withLearningRate(0.01));

注意

特征数在首次 forward 后固定,之后不匹配抛 IllegalArgumentException

注意

权重与偏置梯度按批求和,不做 1/batch 缩放,学习率需自行折算

注意

权重初始化使用 java.util.Random(seed),同一种子与同一形状可复现

注意

层内不校验输入 rank,只读取第 0 维与第 1 维

注意

反向依赖最近一次 forward 缓存的输入、预激活与激活值

参见:basalt.nn.Layer, basalt.nn.Activation, basalt.nn.ParameterRef, basalt.nn.OptimizerConfig

方法

builder() → Builderstatic

创建 Dense 的分阶段构建器。

返回

新的 Builder,默认 1 个单元、NONE 激活、seed 42

forward(Tensor<BC> input) → Tensor<BC>

计算线性组合并应用激活。

按行做权重与输入的点积加偏置,得到预激活,再复制一份并就地应用激活;两份缓冲都保留供反向使用。

注意

首次调用创建权重、偏置与梯度缓冲,返回的张量是激活缓冲的副本

参数
  • input (Tensor<BC>) – 形状 (batch, features) 的输入张量;features 在首次调用时被锁定

返回

新分配的形状 (batch, units) 张量

抛出
  • IllegalArgumentException – 输入特征数与首次 forward 锁定的值不一致

backward(Tensor<BC> gradient) → Tensor<BC>

回传梯度,累加权重与偏置梯度。

先按激活求局部梯度(SOFTMAX 用整行点积修正),再累加偏置梯度、权重梯度与输入梯度。

注意

每次反向开始时清零权重与偏置梯度缓冲

参数
  • gradient (Tensor<BC>) – 损失对输出的梯度,形状须为 (batch, units)

返回

对输入的梯度,形状 (batch, inputFeatures)

抛出
  • IllegalStateException – 尚未执行 forward

  • IllegalArgumentException – 梯度形状与最近一次前向输出不一致

update(OptimizerConfig optimizer)

用最近的梯度更新权重与偏置。

按 optimizer 指定的算法就地更新两个参数数组,并推进其内部迭代步。

注意

更新后梯度标记失效,连续两次调用会抛异常

参数
  • optimizer (OptimizerConfig) – 优化器超参数;SGD 只用学习率,ADAM 与 ADAMW 另用 beta1、beta2、epsilon

抛出
  • IllegalStateException – 参数尚未创建,或未完成一次 forward 加 backward

parameters() → List<ParameterRef>

暴露权重与偏置的实时参数视图。

返回两个 ParameterRef,名称分别为 dense.weights 与 dense.bias,各含参数数组与最近的梯度缓冲,供梯度检查、梯度裁剪与持久化使用。

注意

数组由层持有,不可重新分配;写入 values 即改变参数值

返回

两个参数视图;参数尚未创建时返回空列表

units() → int

返回输出单元数。

注意

公开访问器,序列化与网络构建也会读取

返回

build 时给定的 units 值

final class Flatten implements Layer<BCHW, BC>

把 BCHW 摊平成 BC,通道、高、宽依次排列。

Flatten 是无参数的布局转换层。前向把 (batch, channels, height, width) 重排为 (batch, channels·height·width),展平顺序为通道优先:先取通道 0 的全部 height×width 个元素,再取通道 1,依此类推。

BCHW 本身按行主序连续存储,展平不改变元素顺序,只重建形状元信息;实现仍经 Tensor.ofBC 复制一份数据,因此返回值与输入不共享缓冲区。反向按相反方向重建 BCHW 形状,同样复制。

前向记录输入的四维尺寸供反向使用,反向要求梯度第 1 维等于 channels·height·width,否则视为形状不匹配。

示例
Flatten flatten = Flatten.builder().build();

Tensor<BCHW> featureMap = Tensor.ofBCHW(2, 4, 3, 3, new double[2 * 4 * 3 * 3]);
Tensor<BC> flat = flatten.forward(featureMap);

Tensor<BCHW> restored = flatten.backward(flat);

注意

前向与反向各复制一次数据,代价与元素总数成正比

注意

反向依赖最近一次前向记录的形状,未前向时抛 IllegalStateException

注意

该层是 Layer<BCHW, BC>,在编译期把卷积链与全连接链衔接起来

参见:basalt.nn.Layer, basalt.nn.Dense, basalt.nn.Conv2D

方法

builder() → Builderstatic

创建 Flatten 的构建器。

返回

新的 Builder,无任何可配置项

forward(Tensor<BCHW> input) → Tensor<BC>

把四维张量摊平为二维。

记录输入的 batch、channels、height、width,再用同一份数据的副本构造 (batch, channels·height·width) 张量。

注意

返回值是输入数据的副本,修改它不会影响输入张量

参数
  • input (Tensor<BCHW>) – 形状 (batch, channels, height, width) 的输入张量

返回

新分配的形状 (batch, channels·height·width) 张量,元素顺序与原数据一致

backward(Tensor<BC> gradient) → Tensor<BCHW>

把二维梯度还原为四维。

按前向记录的形状把梯度重塑回 BCHW,元素顺序不变。

参数
  • gradient (Tensor<BC>) – 损失对摊平输出的梯度,形状须为 (batch, channels·height·width)

返回

新分配的形状 (batch, channels, height, width) 梯度张量

抛出
  • IllegalStateException – 尚未执行 forward,未记录输入形状

  • IllegalArgumentException – 梯度的 batch 或特征数与记录不符

final class GRU implements Layer<BSF, BSF>, Persistable

门控循环层:显式 BPTT 的 many-to-many GRU,输入输出均为 BSF。

门顺序固定为 update(z)、reset(r)、candidate(n)。z 与 r 经 sigmoid 得到,候选状态经 tanh 得到,最终状态按 h' = (1 - z) * n + z * h 组合,其中 z 越大越保留旧状态。

reset 门按前一时刻隐藏向量的分量逐元素作用于 U_n h,而非整段向量缩放。forward 返回整段序列的全部时间步输出,而不是只返回最后一步;h_0 为零向量,序列之间不携带状态。

示例
GRU gru = GRU.builder().hiddenSize(8).seed(42).build();
Tensor<BSF> input = Tensor.zerosBSF(2, 5, 3);
Tensor<BSF> output = gru.forward(input);
Tensor<BSF> inputGradient = gru.backward(Tensor.zerosBSF(2, 5, 8));
gru.update(Optimizer.ADAM.withLearningRate(0.01));

注意

参数规模为 3 * hiddenSize * (features + hiddenSize + 1),权重按门分块连续存储,门块内再按隐藏单元分行。

注意

bias 以全零初始化,因此 z 与 r 在训练开始时的输出均为 0.5。

注意

输入特征数在首次 forward 时确定,之后传入不同特征数会抛出异常。

注意

必须按 forward → backward → update 的顺序调用;update 消费梯度后需重新前向才能再次更新。

参见:basalt.nn.LSTM, basalt.nn.VanillaRNN, basalt.nn.SequenceNetwork

方法

builder() → Builderstatic

创建构建器,hiddenSize 默认 16、seed 默认 42。

返回

新的 Builder 实例

forward(Tensor<BSF> input) → Tensor<BSF>

沿时间轴前向传播,返回全序列隐藏状态。

注意

返回的张量是内部缓冲的副本,后续调用不会改写已返回的结果。

注意

内部逐时间步缓冲在批量与序列长度不变时复用,避免重复分配。

参数
  • input (Tensor<BSF>) – 形状 (batch, sequence, features) 的 BSF 张量;features 在首次调用时确定并据此分配参数

返回

形状 (batch, sequence, hiddenSize) 的张量,含每个时间步的隐藏状态,t=0 的状态由零初始状态算出

抛出
  • IllegalArgumentException – features 与首次 forward 时确定的值不一致

backward(Tensor<BSF> gradient) → Tensor<BSF>

沿时间轴反向传播,累积参数梯度并返回输入梯度。

注意

梯度写入 inputWeights、recurrentWeights、bias 的缓冲,需再调用 update 才作用于参数。

注意

反向按时间步逆序进行,逐时刻交换隐藏梯度缓冲,不递归。

参数
  • gradient (Tensor<BSF>) – 上游梯度,形状必须为 (batch, sequence, hiddenSize),与最近一次 forward 的输出对齐

返回

对输入的梯度,形状 (batch, sequence, features)

抛出
  • IllegalStateException – 尚未调用 forward

  • IllegalArgumentException – gradient 的形状与最近一次 forward 不匹配

update(OptimizerConfig optimizer)

用累积梯度对三组参数执行一次优化器更新。

注意

更新后清除梯度就绪标记,重复调用会抛出异常。

参数
  • optimizer (OptimizerConfig) – 优化器超参数,同时作用于输入权重、循环权重与偏置

抛出
  • IllegalStateException – 未完成 forward 与 backward,或梯度已被上一次 update 消费

parameters() → List<ParameterRef>

返回三组参数的实时视图。

注意

视图直接引用层内数组,可用于梯度检查、裁剪与持久化,不应调整长度。

返回

参数视图列表,名称依次为 gru.inputWeights、gru.recurrentWeights、gru.bias;首次 forward 之前为空列表

hiddenSize() → int

返回配置的隐藏单元数。

返回

构建时指定的 hiddenSize

kind() → ModelKind

返回模型族标识。

返回

固定为 ModelKind.GRU

write(ModelWriter writer)

把配置与权重写入模型流。

注意

未初始化时 inputFeatures 写为 -1,且不写权重数组。

参数
  • writer (ModelWriter) – 目标写入器;依次写入 hiddenSize、seed、inputFeatures,已初始化时再写三组权重

read(ModelReader reader) → GRUstatic

从模型流恢复 GRU 实例。

参数
  • reader (ModelReader) – 源读取器,读取顺序须与 write 一致

返回

恢复后的 GRU;若流中记录了特征数,则同时重建并覆盖权重

抛出
  • ModelFormatException – hiddenSize 非正,或特征标记既非 -1 也非正数

final class GradientCheck

用中心差分校验层与序列网络的解析梯度。

对目标 L = <upstream, f(x)>,把解析方向导数与 (L(x+eps) - L(x-eps)) / (2 eps) 比较。相对误差按 max(1, |analytic| + |numeric|) 归一化,因此判定与数值量级无关,量级很小的梯度不会被误判。

输入维度逐元素全量检查;参数维度按 samplesPerParameter 均匀抽样,步长为 max(1, 长度 / samplesPerParameter),以免大层耗时失控。检查过程直接改写 ParameterRef 暴露的实时数组,测完立即恢复原值,末尾再跑一次前向,把层留在前向完成状态。

示例
Layer<BSF, BSF> layer = VanillaRNN.builder().hiddenSize(3).seed(7).build();
double[] values = new double[2 * 3 * 2];
for (int i = 0; i < values.length; i++) {
    values[i] = 0.1 * (i + 1);
}
double[] weights = new double[2 * 3 * 3];
java.util.Arrays.fill(weights, 1.0);

Tensor<BSF> input = Tensor.ofBSF(2, 3, 2, values);
Tensor<BSF> upstream = Tensor.ofBSF(2, 3, 3, weights);
GradientCheck.Report report = GradientCheck.checkLayer(layer, input, upstream, 1e-6);
boolean ok = report.passed(1e-6);

注意

samplesPerParameter 默认 8,只在调用不带该参数的重载时生效

注意

epsilon 必须为正;实现按 (plus - minus) / (2 epsilon) 计算,epsilon 过小会放大浮点相消误差

注意

Report.passed 的容差由调用方给出,类内没有内置阈值;测试套件使用 epsilon = 1e-6 与 tolerance = 1e-6

注意

checkNetwork 只校验参数,返回的 Report 中 inputChecks 为 0、maxInputError 为 0.0

注意

扰动依赖层暴露的实时数组,因此要求 Layer.parameters() 返回的视图确实指向内部权重

参见:basalt.nn.ParameterRef, basalt.nn.Layer, basalt.nn.SequenceNetwork

方法

checkLayer(Layer<BSF, BSF> layer, Tensor<BSF> input, Tensor<BSF> upstream, double epsilon) → Reportstatic

以默认抽样数 8 检查单个层的梯度。

注意

等价于 checkLayer(layer, input, upstream, epsilon, 8)

参数
  • layer (Layer<BSF, BSF>) – 待检查的层,输入输出均为 BSF 布局

  • input (Tensor<BSF>) – 前向输入,作为输入梯度的扰动基点

  • upstream (Tensor<BSF>) – 加权目标所用的上游梯度,尺寸须与层输出一致

  • epsilon (double) – 中心差分步长,必须为正

返回

输入与参数两个维度的最大相对误差及检查次数

抛出
  • IllegalArgumentException – epsilon 不大于 0,或 upstream 与层输出尺寸不符

  • NullPointerException – layer、input 或 upstream 为 null

checkLayer(Layer<BSF, BSF> layer, Tensor<BSF> input, Tensor<BSF> upstream, double epsilon, int samplesPerParameter) → Reportstatic

按指定抽样数检查层的输入梯度与参数梯度。

先 forward 取输出并与 upstream 比尺寸,解析输入梯度来自 layer.backward(upstream)。随后逐个输入元素做加减扰动并重算加权和,得到方向导数的中心差分;参数部分从 ParameterRef.gradient() 取解析值,按步长抽样扰动实时数组。

注意

输入检查是逐元素全量扫描,成本与输入规模成正比,每个元素需要两次前向

注意

检查结束后恢复全部参数并重跑一次前向,层可继续用于训练

参数
  • layer (Layer<BSF, BSF>) – 待检查的层,输入输出均为 BSF 布局

  • input (Tensor<BSF>) – 前向输入,元素个数即输入检查次数

  • upstream (Tensor<BSF>) – 加权目标所用的上游梯度,尺寸须与层输出一致

  • epsilon (double) – 中心差分步长,必须为正

  • samplesPerParameter (int) – 每个参数张量最多抽样的点数;≤0 时按 1 处理,实际步长取 max(1, 长度 / 该值)

返回

inputChecks 等于输入元素总数,parameterChecks 等于实际扰动次数

抛出
  • IllegalArgumentException – epsilon 不大于 0,或 upstream 与层输出尺寸不符

  • NullPointerException – layer、input 或 upstream 为 null

checkNetwork(SequenceNetwork network, Tensor<BSF> input, Tensor<BC> target, double epsilon) → Reportstatic

以默认抽样数 8 检查序列网络的全部参数。

注意

等价于 checkNetwork(network, input, target, epsilon, 8)

参数
  • network (SequenceNetwork) – 待检查的序列网络

  • input (Tensor<BSF>) – 前向输入,形状 (batch, sequence, features)

  • target (Tensor<BC>) – 损失计算所用的目标,形状 (batch, classes)

  • epsilon (double) – 中心差分步长,必须为正

返回

参数维度的最大相对误差与检查次数

抛出
  • IllegalArgumentException – epsilon 不大于 0

  • IllegalStateException – 当前参数下损失不是有限值

checkNetwork(SequenceNetwork network, Tensor<BSF> input, Tensor<BC> target, double epsilon, int samplesPerParameter) → Reportstatic

按指定抽样数检查序列网络的参数梯度。

先 evaluate 得到损失并检查有限性,再 backward 填充各层梯度缓冲。之后对每个参数张量抽样扰动,用两次 evaluate 的损失差做中心差分,与解析梯度比较。

注意

每次扰动都重新执行完整前向,成本约为 2 × 抽样点数 × 单次前向

注意

网络支持全局范数裁剪,但本方法只做数值比对,不修改梯度以外的状态

参数
  • network (SequenceNetwork) – 待检查的序列网络

  • input (Tensor<BSF>) – 前向输入,形状 (batch, sequence, features)

  • target (Tensor<BC>) – 损失计算所用的目标,形状 (batch, classes)

  • epsilon (double) – 中心差分步长,必须为正

  • samplesPerParameter (int) – 每个参数张量最多抽样的点数;≤0 时按 1 处理

返回

Report,其中 inputChecks 恒为 0、maxInputError 恒为 0.0

抛出
  • IllegalArgumentException – epsilon 不大于 0

  • IllegalStateException – 评估得到的损失不是有限值

final class LSTM implements Layer<BSF, BSF>, Persistable

门控循环层:显式 BPTT 的 many-to-many LSTM,输入输出均为 BSF。

门顺序固定为 input(i)、forget(f)、cell(g)、output(o)。i、f、o 经 sigmoid,g 经 tanh;单元状态按 c_t = f * c_{t-1} + i * g 更新,隐藏状态为 o * tanh(c_t)。

单元状态沿时间轴的雅可比就是 forget 门本身,梯度不必连乘被压缩的导数,这是长序列上梯度得以保留的结构原因。forward 返回整段序列,h_0 与 c_0 均为零。

示例
LSTM lstm = LSTM.builder().hiddenSize(8).seed(42).build();
Tensor<BSF> input = Tensor.zerosBSF(2, 5, 3);
Tensor<BSF> output = lstm.forward(input);
Tensor<BSF> inputGradient = lstm.backward(Tensor.zerosBSF(2, 5, 8));
lstm.update(Optimizer.ADAM.withLearningRate(0.01));

注意

参数规模为 4 * hiddenSize * (features + hiddenSize + 1),四门权重在同一矩阵内按 i、f、g、o 分块连续存放。

注意

bias 以全零初始化,因此 forget 门的初始输出为 0.5,而非饱和在 1 附近。

注意

输入特征数在首次 forward 时确定,之后传入不同特征数会抛出异常。

注意

每个时间步的门与状态缓冲在批量形状不变时复用,序列较长时内存占用为 batch * sequence * hiddenSize 的若干倍。

参见:basalt.nn.GRU, basalt.nn.SequenceNetwork, basalt.nn.SequencePool

方法

builder() → Builderstatic

创建构建器,hiddenSize 默认 16、seed 默认 42。

返回

新的 Builder 实例

forward(Tensor<BSF> input) → Tensor<BSF>

沿时间轴前向传播,返回全序列隐藏状态。

注意

批次内各样本独立计算,互不共享状态。

注意

返回的张量是内部缓冲的副本,后续调用不会改写已返回的结果。

参数
  • input (Tensor<BSF>) – 形状 (batch, sequence, features) 的 BSF 张量;features 在首次调用时确定并据此分配参数

返回

形状 (batch, sequence, hiddenSize) 的张量,逐时间步给出 o * tanh(c_t)

抛出
  • IllegalArgumentException – features 与首次 forward 时确定的值不一致

backward(Tensor<BSF> gradient) → Tensor<BSF>

沿时间轴反向传播,累积参数梯度并返回输入梯度。

注意

反向按时间步逆序进行,同时传播隐藏梯度与单元梯度,隐藏与单元缓冲逐时刻交换。

注意

梯度写入三组参数缓冲,需再调用 update 才作用于参数。

参数
  • gradient (Tensor<BSF>) – 上游梯度,形状必须为 (batch, sequence, hiddenSize),与最近一次 forward 的输出对齐

返回

对输入的梯度,形状 (batch, sequence, features)

抛出
  • IllegalStateException – 尚未调用 forward

  • IllegalArgumentException – gradient 的形状与最近一次 forward 不匹配

update(OptimizerConfig optimizer)

用累积梯度对三组参数执行一次优化器更新。

注意

更新后清除梯度就绪标记,重复调用会抛出异常。

参数
  • optimizer (OptimizerConfig) – 优化器超参数,同时作用于输入权重、循环权重与偏置

抛出
  • IllegalStateException – 未完成 forward 与 backward,或梯度已被上一次 update 消费

parameters() → List<ParameterRef>

返回三组参数的实时视图。

注意

视图直接引用层内数组,可用于梯度检查、裁剪与持久化,不应调整长度。

返回

参数视图列表,名称依次为 lstm.inputWeights、lstm.recurrentWeights、lstm.bias;首次 forward 之前为空列表

hiddenSize() → int

返回配置的隐藏单元数。

返回

构建时指定的 hiddenSize

kind() → ModelKind

返回模型族标识。

返回

固定为 ModelKind.LSTM

write(ModelWriter writer)

把配置与权重写入模型流。

注意

未初始化时 inputFeatures 写为 -1,且不写权重数组。

参数
  • writer (ModelWriter) – 目标写入器;依次写入 hiddenSize、seed、inputFeatures,已初始化时再写三组权重

read(ModelReader reader) → LSTMstatic

从模型流恢复 LSTM 实例。

参数
  • reader (ModelReader) – 源读取器,读取顺序须与 write 一致

返回

恢复后的 LSTM;若流中记录了特征数,则同时重建并覆盖权重

抛出
  • ModelFormatException – hiddenSize 非正,或特征标记既非 -1 也非正数

interface Layer<I extends Layout, O extends Layout>

带编译期输入输出布局参数的可微变换接口。

Layer<I, O> 用两个布局类型参数约束数据流:forward 接收 Tensor<I> 并返回 Tensor<O>,backward 接收 Tensor<O> 并返回 Tensor<I>。I 与 O 必须实现 sealed 接口 Layout,当前允许 BCHW、BHW、BC、BSF 四种。

布局链在编译期拼接:Conv2D 与 MaxPooling2D 是 Layer<BCHW, BCHW>,Flatten 是 Layer<BCHW, BC>,Dense 是 Layer<BC, BC>,因此把 BCHW 层接在 BC 层之后无法通过编译,不需要运行期检查。

接口只规定前向、反向与更新三件事;参数暴露由 parameters 提供默认空实现,无参数层(Flatten、MaxPooling2D)不必覆写 update 与 parameters。

示例
Layer<BC, BC> identity = new Layer<>() {
    @Override
    public Tensor<BC> forward(Tensor<BC> input) {
        return input;
    }

    @Override
    public Tensor<BC> backward(Tensor<BC> gradient) {
        return gradient;
    }
};

Tensor<BC> output = identity.forward(Tensor.zerosBC(2, 3));
int parameterCount = identity.parameters().size();

注意

backward 不是自动微分:各层在 forward 中缓存中间量,调用方必须按相反顺序回传

注意

update 的默认实现为空,无参数层可安全调用

注意

parameters 返回的数组是层内实时存储,写入即改变参数值

注意

同一层的 forward 与 backward 通常不可重入,缓存状态会被下一次调用覆盖

参见:basalt.core.Layout, basalt.core.Tensor, basalt.nn.Dense, basalt.nn.Conv2D

enum Loss

批量-通道布局上的损失函数,同时产出损失值与输出梯度。

枚举常量有 CATEGORICAL_CROSSENTROPY、BINARY_CROSSENTROPY 与 MEAN_SQUARED_ERROR。evaluate 要求 prediction 与 target 形状完全一致、元素全部有限,并且按元素总数(batch × classes)而非批大小取平均。

CATEGORICAL_CROSSENTROPY 把预测当作概率:元素须在 [0, 1],每行目标和与 1 的偏差不得超过 1e-6,并额外给出 (p - t) / batch 的 softmax-logit 梯度。BINARY_CROSSENTROPY 要求预测与目标都在 [0, 1],概率先夹到 [1e-12, 1 - 1e-12] 再取对数。MEAN_SQUARED_ERROR 只要求有限,不限制范围。

梯度统一按元素总数缩放,与 CNN.fit 的全批量语义一致:一次 update 即等价于整批的一次梯度步。

示例
Tensor<BC> prediction = Tensor.ofBC(2, 3,
        0.7, 0.2, 0.1,
        0.1, 0.8, 0.1);
Tensor<BC> target = Tensor.ofBC(2, 3,
        1.0, 0.0, 0.0,
        0.0, 1.0, 0.0);

LossEvaluation evaluation = Loss.CATEGORICAL_CROSSENTROPY.evaluate(prediction, target);
double loss = evaluation.loss();
boolean hasLogitGradient = evaluation.hasSoftmaxLogitGradient();

注意

概率下界 1e-12 用于避免 log(0);预测恰好为 0 时分类交叉熵的梯度会达到 -t / 1e-12 量级

注意

行和校验逐行进行,任一行不满足即整体失败,不会只跳过该行

注意

evaluate 不修改输入张量,返回的梯度是新分配的张量

参见:basalt.nn.LossEvaluation, basalt.nn.CNN

方法

evaluate(Tensor<BC> prediction, Tensor<BC> target) → LossEvaluationabstract

计算损失值与同形状的输出梯度。

形状校验后按所选常量逐元素累加损失并写出梯度;分类交叉熵还会生成 softmax-logit 梯度,使 CNN 能在 Softmax 末端跳过雅可比乘法。

参数
  • prediction (Tensor<BC>) – 网络输出,形状 (batch, classes);分类与二元损失要求元素在 [0, 1]

  • target (Tensor<BC>) – 目标,形状须与 prediction 完全一致;分类交叉熵要求每行和为 1,二元损失要求元素在 [0, 1]

返回

标量损失与同形状梯度;分类交叉熵额外携带 softmax-logit 梯度

抛出
  • IllegalArgumentException – 形状不一致、元素非有限、取值越界,或分类目标的某一行和不为 1

  • NullPointerException – prediction 或 target 为 null

record LossEvaluation

标量损失与输出梯度的不可变结果,可选携带 softmax-logit 梯度。

记录有三个组件:loss、gradient 与 softmaxLogitGradient。两参构造把第三项置为 null,hasSoftmaxLogitGradient 据此判断是否可用简化路径。

只有 CATEGORICAL_CROSSENTROPY 会填充 softmax-logit 梯度,其值已把 Softmax 的雅可比合并进 (p - t) / batch;CNN 与 SequenceNetwork 在末端为 SOFTMAX 的 Dense 上优先使用它,避免数值放大。

注意

gradient 不可为 null,紧凑构造器会校验;softmaxLogitGradient 允许为 null

注意

记录不复制张量,gradient 与 softmaxLogitGradient 的所有权属于创建方

注意

标准三参构造器同样可用,用于显式携带 softmax-logit 梯度

参见:basalt.nn.Loss, basalt.nn.CNN

构造器

LossEvaluation(double loss, Tensor<BC> gradient)

构造只带输出梯度的评估结果。

注意

softmaxLogitGradient 置为 null,hasSoftmaxLogitGradient 返回 false

参数
  • loss (double) – 标量损失,通常已按元素总数平均

  • gradient (Tensor<BC>) – 与预测同形状的输出梯度,不可为 null

抛出
  • NullPointerException – gradient 为 null

方法

hasSoftmaxLogitGradient() → boolean

是否携带 softmax-logit 梯度。

注意

为 true 只说明梯度存在,末端层是否 SOFTMAX 由网络自行判断

返回

softmaxLogitGradient 非 null 时为 true

final class MaxPooling2D implements Layer<BCHW, BCHW>

二维最大池化,缓存 argmax 索引以精确回传梯度。

在 (batch, channels, height, width) 上做窗口取最大,输出空间尺寸为 (in - pool) / stride + 1,向下取整。层内没有补零,窗口必须完全落在输入内,pool 大于输入空间尺寸时抛 IllegalArgumentException。

步长默认等于窗口尺寸,因此默认行为是不重叠池化;Builder 允许单独指定步长以得到重叠窗口。窗口内出现 NaN 时立即抛 IllegalArgumentException,避免最大值比较被污染。

前向为每个输出位置记录取得最大值的输入线性下标,反向只把这些位置的梯度累加回输入,其余位置为零。并列最大值取扫描顺序中先遇到的那个,比较使用严格大于。

示例
MaxPooling2D pool = MaxPooling2D.builder()
        .poolSize(2, 2)
        .strides(2, 2)
        .build();

Tensor<BCHW> input = Tensor.ofBCHW(1, 1, 4, 4, new double[16]);
Tensor<BCHW> pooled = pool.forward(input);

Tensor<BCHW> gradient = Tensor.zerosBCHW(1, 1, 2, 2);
Tensor<BCHW> inputGradient = pool.backward(gradient);

注意

无参数层,update 使用接口默认的空实现

注意

argmax 缓存以最近一次前向为准,重复前向会覆盖

注意

反向要求梯度形状与最近一次前向输出完全一致

注意

窗口不补零,右侧与下侧不足一个窗口的像素会被丢弃

参见:basalt.nn.Layer, basalt.nn.Conv2D, basalt.nn.Flatten

方法

builder() → Builderstatic

创建 MaxPooling2D 的构建器。

返回

新的 Builder,默认 2×2 窗口且步长等于窗口

forward(Tensor<BCHW> input) → Tensor<BCHW>

执行最大池化并缓存 argmax 下标。

逐窗口扫描取最大值,同时记录该值在输入缓冲区中的线性下标,供反向路由使用。

注意

输出尺寸为 (in - pool) / stride + 1,除法向下取整

参数
  • input (Tensor<BCHW>) – 形状 (batch, channels, height, width) 的输入张量

返回

新分配的形状 (batch, channels, outputHeight, outputWidth) 张量

抛出
  • IllegalArgumentException – 窗口大于输入空间尺寸,或窗口内含 NaN

backward(Tensor<BCHW> gradient) → Tensor<BCHW>

按缓存的 argmax 把梯度回传到输入。

对每个输出位置把上游梯度累加到前向记录的最大值位置,其余输入位置保持为零。

注意

同一输入位置可能被多个窗口命中(重叠池化时),梯度会累加

参数
  • gradient (Tensor<BCHW>) – 损失对池化输出的梯度,形状须与最近一次前向输出一致

返回

新分配的形状 (batch, channels, inputHeight, inputWidth) 梯度张量

抛出
  • IllegalStateException – 尚未执行 forward,或 argmax 缓存不完整

  • IllegalArgumentException – 梯度形状或元素总数与前向输出不一致

final class ModelSerializer

CNN 的 .bsl 持久化门面,负责层载荷的读写。

容器格式由 basalt.io.ModelCodec 负责,包含 magic、版本号与 CRC32C 校验和;本类只处理 CNN 载荷:损失序号、优化器类型与四个超参、层数,然后逐层写类型标记、配置,以及已初始化层的参数值。

readCNN 重建各层后校验拓扑:必须恰好出现一个 Flatten,Flatten 之前只允许 Conv2D 与 MaxPooling2D,之后只允许 Dense,层数须落在 1 到 100000 之间。层类型标记为 1 到 4,分别对应卷积、最大池化、展平与全连接。

示例
CNN model = NeuralNetwork.builder()
        .add(Conv2D.builder().filters(4).kernelSize(3, 3).padding(Padding.SAME).build())
        .add(MaxPooling2D.builder().poolSize(2, 2).build())
        .add(Flatten.builder().build())
        .add(Dense.builder().units(3).activation(Activation.SOFTMAX).build())
        .build();
model.predict(Tensor.ofBCHW(2, 1, 4, 4, new double[2 * 16]));

Path path = Path.of("model.bsl");
ModelSerializer.save(path, model);
CNN restored = ModelSerializer.loadCNN(path);

注意

save 与 loadCNN 走 ModelCodec,加载时内存映射文件并在不复制载荷的情况下校验 CRC32C

注意

UncheckedIOException 表示文件系统失败,ModelFormatException 表示容器或载荷格式错误

注意

参数只在层已见过输入(inputChannels 或 inputFeatures 大于 0)时写出,未初始化的层以 -1 标记,加载后需重新前向才会生成权重

注意

损失与优化器以枚举序号写入,调整枚举常量顺序会使旧文件解析错误

参见:basalt.nn.CNN, basalt.io.ModelCodec

方法

save(Path target, CNN model)static

把 CNN 写入 .bsl 文件。

先让模型把载荷写进内存缓冲,再由 ModelCodec 补齐 24 字节头部并一次性写入文件;目标文件已存在时被截断覆盖。

注意

未初始化的层只写结构,加载后需重新前向才会生成权重

参数
  • target (Path) – 目标路径,父目录须已存在

  • model (CNN) – 待保存的网络,不可为 null

抛出
  • UncheckedIOException – 文件打开或写入失败

  • ModelFormatException – 载荷长度超出 .bsl 容器上限

  • NullPointerException – model 为 null

loadCNN(Path source) → CNNstatic

从 .bsl 文件读出 CNN。

注意

读取后要求载荷被完整消费,多余字节视为格式错误

参数
  • source (Path) – 源文件路径

返回

重建的 CNN,参数值已恢复

抛出
  • ModelFormatException – magic 或版本不符、CRC32C 校验失败、载荷长度不符、文件内模型不是 CNN,或层与拓扑非法

  • UncheckedIOException – 文件打开或映射失败

writeCNN(ModelWriter output, CNN model)static

把 CNN 载荷写入已有 ModelWriter。

写出损失序号、优化器序号、learningRate、beta1、beta2、epsilon、层数,再逐层写出类型标记与配置。

注意

遇到 Conv2D、MaxPooling2D、Flatten、Dense 之外的层会抛 IllegalStateException

参数
  • output (ModelWriter) – 目标写入器,调用方负责容器头

  • model (CNN) – 待写入的网络

readCNN(ModelReader input) → CNNstatic

从 ModelReader 读取 CNN 载荷并校验拓扑。

依次读取损失与优化器序号并做范围检查,重建优化器配置与各层,最后校验 Flatten 的位置约束。

注意

拓扑校验与 Builder 的编译期约束一致,因此手写或损坏的文件无法绕过布局检查

参数
  • input (ModelReader) – 载荷读取器,位置须在 CNN 载荷起点

返回

重建的 CNN

抛出
  • ModelFormatException – 枚举序号越界、层数不在 1 到 100000 之间、未知层类型、缺少 Flatten、Flatten 重复,或 Flatten 前后出现非法层

final class NeuralNetwork

分阶段构建 CNN:图像层在 Flatten 之前,向量层在其后。

两个阶段各是一个 final 嵌套类。ImageBuilder 只接受 Conv2D 与 MaxPooling2D,唯一出口是 add(Flatten),它返回 VectorBuilder;VectorBuilder 只接受 Dense,并提供 loss、optimizer 与 build。

Dense 没有进入 ImageBuilder 的重载,ImageBuilder 也没有 build 方法,因此“图像层后直接接全连接层”或“忘记 Flatten”都无法通过编译,而不是等到训练时才因维度不符失败。

示例
CNN model = NeuralNetwork.builder()
        .add(Conv2D.builder().filters(8).kernelSize(3, 3).activation(Activation.RELU).build())
        .add(MaxPooling2D.builder().poolSize(2, 2).build())
        .add(Flatten.builder().build())
        .add(Dense.builder().units(10).activation(Activation.SOFTMAX).build())
        .loss(Loss.CATEGORICAL_CROSSENTROPY)
        .optimizer(Optimizer.SGD.withLearningRate(0.05))
        .build();
int layers = model.layerCount();

注意

build 不做额外校验,直接包装层列表、损失与优化器

注意

默认损失为 CATEGORICAL_CROSSENTROPY,默认优化器为 ADAM 且学习率 0.001

注意

阶段之间没有回退路径:进入 VectorBuilder 后不能再添加卷积或池化层

参见:basalt.nn.CNN, basalt.nn.Conv2D, basalt.nn.Dense, basalt.nn.Flatten

方法

builder() → ImageBuilderstatic

创建图像阶段的构建器。

返回

层列表为空的 ImageBuilder

enum Optimizer

一阶优化算法枚举:SGD、ADAM 与 ADAMW。

withLearningRate 是唯一的便捷入口:固定 beta1 = 0.9、beta2 = 0.999、epsilon = 1e-8,只替换学习率,返回新的 OptimizerConfig。

SGD 直接执行 values -= learningRate × gradient,且不递增步计数;ADAM 与 ADAMW 走同一条偏差修正路径,即维护一阶与二阶矩的指数滑动平均,再除以 1 - beta^step。当前内核没有实现解耦权重衰减,两个常量在 Parameter.update 中的分支相同。

示例
OptimizerConfig adam = Optimizer.ADAM.withLearningRate(0.001);
OptimizerConfig sgd = Optimizer.SGD.withLearningRate(0.05);
OptimizerConfig custom = new OptimizerConfig(Optimizer.ADAMW, 0.002, 0.85, 0.995, 1e-7);

注意

枚举序号会写进 .bsl 载荷,调整常量顺序会破坏旧文件的兼容性

注意

步计数只在非 SGD 更新时递增,混用 SGD 与 Adam 时偏差修正会沿用此前计数

注意

beta 与 epsilon 对 SGD 分支没有作用,但仍须通过 OptimizerConfig 的校验

参见:basalt.nn.OptimizerConfig, basalt.nn.Parameter

枚举成员

SGD()

随机梯度下降,直接按学习率缩放梯度。

注意

不维护动量,也不递增 Parameter 的步计数

ADAM()

Adam,一阶与二阶矩估计加偏差修正。

注意

每个参数各自保存 firstMoment 与 secondMoment,与参数数组等长

ADAMW()

AdamW 常量,当前内核与 ADAM 共用更新路径。

该常量可正常构造配置并参与序列化,但 Parameter.update 只区分 SGD 与“非 SGD”,因此不含解耦权重衰减项,数值结果与 ADAM 相同。

注意

需要权重衰减时须在更新前自行把衰减项并入梯度

方法

withLearningRate(double learningRate) → OptimizerConfig

以指定学习率创建其余取默认值的配置。

注意

每次调用都新建记录,不复用或缓存

参数
  • learningRate (double) – 学习率,须为正且有限;非正值在构造 OptimizerConfig 时被拒绝

返回

beta1 = 0.9、beta2 = 0.999、epsilon = 1e-8 的新配置

抛出
  • IllegalArgumentException – learningRate 不是正有限值

record OptimizerConfig

训练共用的不可变优化器超参。

记录组件依次为 optimizer、learningRate、beta1、beta2、epsilon。紧凑构造器强制 optimizer 非 null、learningRate 为正有限值、两个 beta 落在 [0, 1)、epsilon 为正有限值。

CNN 与 SequenceNetwork 各持有一个实例,并在每次更新时把它传给全部可训练层的 update;beta 与 epsilon 对 SGD 无意义,但仍需满足同一套校验。

注意

记录本身没有默认值,默认组合来自 Optimizer.withLearningRate:beta1 0.9、beta2 0.999、epsilon 1e-8

注意

beta 取 1.0 会被拒绝,因为 1 - beta^step 会退化为 0

注意

全部校验集中在构造器,训练期间不重复检查,因此超参无法在训练中变更

注意

序列化时按 optimizer 序号与四个 double 写入,加载时同样经过构造器校验

参见:basalt.nn.Optimizer, basalt.nn.Parameter

enum Padding

卷积补零模式:VALID 不补,SAME 补到按步长整除。

VALID 不做补零,输出尺寸为 (in - k) / s + 1,要求 in ≥ k,否则 Conv2D 抛 IllegalArgumentException。

SAME 让输出尺寸只由步长决定:out = ceil(in / s),即 (in + s - 1) / s。所需补零总量为 max(0, (out - 1)·s + k - in),前侧与左侧各取 total / 2(向下取整),余下的一个像素落在后侧与下侧,因此 total 为奇数时上下、左右不对称。

补零是隐式的:Conv2D 不为填充分配缓冲区,只在采样时跳过越界索引。卷积核不大于步长时 total 常为 0,但 SAME 与 VALID 的输出尺寸仍可能不同,例如 in = 7、k = 2、s = 2 时分别为 4 与 3。

示例
Conv2D same = Conv2D.builder()
        .filters(4)
        .kernelSize(3, 3)
        .padding(Padding.SAME)
        .build();

Conv2D valid = Conv2D.builder()
        .filters(4)
        .kernelSize(3, 3)
        .padding(Padding.VALID)
        .build();

Tensor<BCHW> input = Tensor.zerosBCHW(1, 2, 7, 7);
int sameWidth = same.forward(input).dimension(3);
int validWidth = valid.forward(input).dimension(3);

注意

由 Conv2D.Builder.padding 选择,默认 VALID

注意

SAME 下输出空间尺寸与卷积核大小无关,只取决于输入与步长

注意

补零值恒为 0,不参与权重更新

注意

该枚举没有方法,仅作为模式标记

参见:basalt.nn.Conv2D, basalt.nn.Conv2D.Builder

枚举成员

VALID()

不补零,输出 (in − k) / stride + 1

SAME()

补零使输出为 ceil(in / stride)

record ParameterRef

单个可训练张量的实时视图:参数值与最近一次回传的梯度。

记录组件为 name、values、gradient,三者都不复制,数组由层持有,本记录只是别名。层通过 Layer.parameters() 暴露这些视图:GradientCheck 借它做中心差分扰动,SequenceNetwork.update 借它做全局范数裁剪,序列化时也经它读写权重。

紧凑构造器要求 name 非空白、values 与 gradient 等长。长度在创建后不可变,使用方只能改写元素,不能扩容。

示例
Layer<BSF, BSF> layer = VanillaRNN.builder().hiddenSize(4).build();
List<ParameterRef> parameters = layer.parameters();
for (ParameterRef parameter : parameters) {
    double[] values = parameter.values();
    double[] gradient = parameter.gradient();
    double scale = Math.sqrt(parameter.size());
    for (int i = 0; i < values.length; i++) {
        values[i] -= 0.01 * gradient[i] / scale;
    }
}

注意

梯度缓冲在每次 backward 中被层清零后重写,读到的始终是最近一次回传的结果

注意

改动 values 会立即影响模型,GradientCheck 依赖这一点并在检查后恢复原值

注意

empty(name) 用于尚未初始化的层,size() 返回 0

注意

无参数层应返回 none() 而不是 null,Layer.parameters() 的默认实现即如此

参见:basalt.nn.Layer, basalt.nn.GradientCheck, basalt.nn.SequenceNetwork, basalt.nn.Parameter

方法

size() → int

返回参数元素个数。

返回

values 的长度,与 gradient 长度相同

empty(String name) → ParameterRefstatic

创建零长度的占位视图。

注意

适合表示尚未初始化的参数,size() 返回 0

参数
  • name (String) – 参数名,不可为 null 或空白

返回

values 与 gradient 均为空数组的实例

抛出
  • IllegalArgumentException – name 为 null 或空白

none() → List<ParameterRef>static

返回不可变的空列表。

注意

每次调用返回同一个共享的空列表实例

返回

List.of() 产生的空列表,供无参数层直接返回

final class SelfAttention implements Layer<BSF, BSF>, Persistable

单头缩放点积自注意力,支持因果掩码与完整 Softmax 反向传播。

对每个时间步做三次线性投影得到 Q、K、V,按 A = softmax(Q K^T / sqrt(d)) 归一化后加权求和 V,输出与输入同为 (batch, sequence, dimension),每个时间步输出仍对应同一位置。

causal 为 true 时第 t 行只在键 0..t 上归一化,t 之后的注意力权重被置零,既不参与输出也不接收梯度;因此位置 t 的输出与未来时间步的输入无关。

示例
SelfAttention attention = SelfAttention.builder()
        .dimension(4)
        .causal(true)
        .seed(7)
        .build();
Tensor<BSF> input = Tensor.zerosBSF(2, 6, 4);
Tensor<BSF> output = attention.forward(input);
Tensor<BSF> inputGradient = attention.backward(Tensor.zerosBSF(2, 6, 4));
attention.update(Optimizer.ADAM.withLearningRate(0.001));

注意

输入特征数必须严格等于 dimension,不做隐式投影;需要改变维度时应在之前串联 LSTM、GRU 或 Dense。

注意

注意力缓冲为 batch * sequence * sequence 个 double,序列长度增加时内存按平方增长。

注意

缩放因子 1 / sqrt(dimension) 在构造时算好,不随输入变化。

注意

三组权重按 query、key、value 顺序用同一随机源连续采样,均匀分布于 ±sqrt(3 / dimension);无偏置项。

参见:basalt.nn.LSTM, basalt.nn.SequenceNetwork, basalt.nn.SequencePool

方法

builder() → Builderstatic

创建构建器,dimension 默认 16、seed 默认 42、非因果。

返回

新的 Builder 实例

forward(Tensor<BSF> input) → Tensor<BSF>

计算自注意力输出,形状与输入一致。

注意

首次调用时初始化三组 dimension × dimension 权重矩阵。

注意

Softmax 按行减去该行最大值后再归一化,避免指数溢出。

参数
  • input (Tensor<BSF>) – 形状 (batch, sequence, dimension) 的 BSF 张量,特征轴必须等于 dimension

返回

形状 (batch, sequence, dimension) 的注意力输出

抛出
  • IllegalArgumentException – 输入特征数不等于 dimension

backward(Tensor<BSF> gradient) → Tensor<BSF>

反传注意力,累积 Q/K/V 权重梯度并返回输入梯度。

注意

Softmax 使用精确雅可比 A * (dA - rowsum(dA * A)),再乘缩放因子,而不是近似导数。

注意

causal 模式下被掩码的键不参与求和,其梯度恒为零。

参数
  • gradient (Tensor<BSF>) – 上游梯度,形状必须为 (batch, sequence, dimension)

返回

对输入的梯度,形状 (batch, sequence, dimension)

抛出
  • IllegalStateException – 尚未调用 forward

  • IllegalArgumentException – gradient 的形状与最近一次 forward 不匹配

update(OptimizerConfig optimizer)

用累积梯度更新 Q/K/V 三组权重。

参数
  • optimizer (OptimizerConfig) – 优化器超参数,分别作用于 query、key、value 权重

抛出
  • IllegalStateException – 未完成 forward 与 backward,或梯度已被上一次 update 消费

parameters() → List<ParameterRef>

返回 Q/K/V 权重的实时视图。

返回

参数视图列表,名称依次为 attention.query、attention.key、attention.value;首次 forward 之前为空列表

dimension() → int

返回每个位置的投影维度。

返回

构建时指定的 dimension,同时是输入输出的特征数

causal() → boolean

返回是否启用因果掩码。

返回

启用时为 true,此时位置 t 只关注键 0..t

kind() → ModelKind

返回模型族标识。

返回

固定为 ModelKind.SELF_ATTENTION

write(ModelWriter writer)

把配置与权重写入模型流。

参数
  • writer (ModelWriter) – 目标写入器;依次写入 dimension、seed、因果标志、是否已初始化,已初始化时再写三组权重

read(ModelReader reader) → SelfAttentionstatic

从模型流恢复自注意力层。

参数
  • reader (ModelReader) – 源读取器,读取顺序须与 write 一致

返回

恢复后的实例,未初始化时保留惰性初始化状态

抛出
  • ModelFormatException – dimension 非正,或因果标志、初始化标志不是 0 或 1

final class SequenceNetwork implements Persistable

端到端序列分类器:BSF → 若干序列层 → 池化 → Dense 头 → BC。

前向按 add 顺序串联主体层,再经池化把时间轴压缩为一个向量,最后过 Dense 头得到 (batch, classes) 输出。训练时一次完整前向、计算损失梯度、逆序反传头部与各主体层,再做全局范数裁剪与逐参数更新。

fit 在批量轴上连续切片做小批量训练,时间轴与特征轴保持不动;每轮记录按样本数加权的平均损失。主体层只接受 Layer<BSF, BSF>,因此 Dense 不能放入主体,只能作为头部。

示例
SequenceNetwork network = SequenceNetwork.builder()
        .add(LSTM.builder().hiddenSize(8).seed(1).build())
        .add(SelfAttention.builder().dimension(8).causal(true).seed(2).build())
        .pooling(SequencePool.Pooling.LAST)
        .head(Dense.builder().units(2).activation(Activation.SOFTMAX).seed(3).build())
        .loss(Loss.CATEGORICAL_CROSSENTROPY)
        .optimizer(Optimizer.ADAM.withLearningRate(0.01))
        .gradientClip(5.0)
        .build();
Tensor<BSF> features = Tensor.zerosBSF(16, 10, 4);
Tensor<BC> scores = network.predict(features);

注意

head 必填;CATEGORICAL_CROSSENTROPY 要求 SOFTMAX 头且 units 不少于 2,BINARY_CROSSENTROPY 要求 SIGMOID 头。

注意

默认配置为 LAST 池化、分类交叉熵、Adam(学习率 0.001)、梯度裁剪阈值 5.0。

注意

update 返回裁剪前的全局梯度范数,可用于监控训练稳定性。

注意

backward 只接受最近一次 evaluate 返回的同一个 LossEvaluation 实例,其余会抛出异常。

注意

fit 不打乱样本顺序,批次按批量轴顺序连续切分。

参见:basalt.nn.SequencePool, basalt.nn.LSTM, basalt.nn.Dense, basalt.nn.Loss

方法

builder() → Builderstatic

创建构建器,使用 LAST 池化与分类交叉熵等默认值。

返回

新的 Builder 实例

predict(Tensor<BSF> features) → Tensor<BC>

对整批序列推理,输出各类别得分。

注意

会写入各层的前向缓存,但不改动参数。

参数
  • features (Tensor<BSF>) – 形状 (batch, sequence, features) 的 BSF 张量

返回

形状 (batch, classes) 的输出,等价于 forward

forward(Tensor<BSF> features) → Tensor<BC>

执行完整前向:主体层、池化与头部。

注意

各主体层与池化层缓存本次前向的中间量,供紧随其后的 backward 使用。

参数
  • features (Tensor<BSF>) – 形状 (batch, sequence, features) 的 BSF 张量,不能为 null

返回

形状 (batch, classes) 的头部输出

evaluate(Tensor<BSF> features, Tensor<BC> targets) → LossEvaluation

前向并计算损失,缓存结果供 backward 使用。

注意

每次调用覆盖上一次缓存的评估结果,旧实例随后不能再传给 backward。

参数
  • features (Tensor<BSF>) – 形状 (batch, sequence, features) 的 BSF 张量

  • targets (Tensor<BC>) – 形状 (batch, classes) 的目标;分类交叉熵要求每行之和为 1

返回

损失标量与输出梯度;SOFTMAX 头配分类交叉熵时另带简化的 logit 梯度

backward(LossEvaluation evaluation) → Tensor<BSF>

反传缓存的损失梯度,返回对输入的梯度。

注意

头部为 SOFTMAX 且评估携带 logit 梯度时,走 p - t 形式的简化路径,数值更稳定。

注意

反传顺序为头部、池化层,再由主体层逆序逐层回传。

参数
  • evaluation (LossEvaluation) – 最近一次 evaluate 返回的实例,按引用比较

返回

形状与 forward 输入相同的 BSF 梯度

抛出
  • IllegalArgumentException – evaluation 不是最近一次 evaluate 的实例

update() → double

全局范数裁剪后对全部参数执行一次更新。

注意

范数超过 gradientClip 时,所有参数梯度按 clip / norm 等比缩放,方向不变。

注意

逐主体层调用 update,最后更新头部;随后各层清除梯度就绪标记。

返回

裁剪前的全局梯度范数

fit(Tensor<BSF> features, Tensor<BC> targets, int epochs) → TrainingHistory

全批量训练指定轮数。

注意

等价于 batchSize 取 batch 的重载,即每轮只做一次参数更新。

参数
  • features (Tensor<BSF>) – 形状 (batch, sequence, features) 的训练序列

  • targets (Tensor<BC>) – 形状 (batch, classes) 的目标

  • epochs (int) – 训练轮数,须为正

返回

长度为 epochs 的历史,每项为该轮的加权平均损失

抛出
  • IllegalArgumentException – epochs 非正,或特征与目标的批量不一致

  • IllegalStateException – 某一轮损失出现非有限值

fit(Tensor<BSF> features, Tensor<BC> targets, int epochs, int batchSize) → TrainingHistory

按小批量训练指定轮数。

注意

每个批次执行一次 evaluate、backward 与 update,因此每轮的更新次数为 ceil(batch / batchSize)。

参数
  • features (Tensor<BSF>) – 形状 (batch, sequence, features) 的训练序列

  • targets (Tensor<BC>) – 形状 (batch, classes) 的目标

  • epochs (int) – 训练轮数,须为正

  • batchSize (int) – 每批样本数,须为正;沿批量轴顺序连续切片,最后一批可能不足

返回

长度为 epochs 的历史,每项为按样本数加权的平均损失

抛出
  • IllegalArgumentException – epochs 或 batchSize 非正,或特征与目标的批量不一致

  • IllegalStateException – 某一轮损失出现非有限值

parameters() → List<ParameterRef>

汇总主体层与头部的全部参数视图。

返回

主体层参数在前、头部参数在后;尚未初始化的层不贡献条目

loss() → Loss

返回配置的损失函数。

返回

构建时指定的 Loss 枚举值

optimizer() → OptimizerConfig

返回配置的优化器超参数。

返回

构建时指定的 OptimizerConfig

pooling() → SequencePool.Pooling

返回时间轴池化策略。

返回

构建时指定的 SequencePool.Pooling

bodySize() → int

返回主体层数量。

返回

add 调用所加入的层数

kind() → ModelKind

返回模型族标识。

返回

固定为 ModelKind.SEQUENCE_NETWORK

write(ModelWriter writer)

把整网结构与参数写入模型流。

注意

层类型以字节标记区分:1 为 LSTM、2 为 GRU、3 为 VanillaRNN、4 为 SelfAttention。

参数
  • writer (ModelWriter) – 目标写入器;依次写入主体层数与各层(带类型标记)、池化策略、头部、损失、优化器与裁剪阈值

抛出
  • IllegalStateException – 主体包含 LSTM、GRU、VanillaRNN、SelfAttention 之外的层类型

read(ModelReader reader) → SequenceNetworkstatic

从模型流重建整网。

参数
  • reader (ModelReader) – 源读取器,读取顺序须与 write 一致

返回

重建后的网络,主体层按流中顺序恢复

抛出
  • ModelFormatException – 主体层数超出 0..1000、层类型未知,或池化、损失、优化器序号越界

final class SequencePool implements Layer<BSF, BC>

沿时间轴把 BSF 序列压缩为每个样本一个 BC 向量。

Pooling.LAST 取最后一个时间步,是分类任务常用的 many-to-one 头,代价是只使用末步信息;Pooling.MEAN 对全部时间步求平均,每个时间步都参与结果。

反向传播与正向严格对应:LAST 把梯度原样写回最后一个时间步,其余时间步为零;MEAN 把梯度除以序列长度后平均分配到每个时间步。

示例
SequencePool pool = SequencePool.of(SequencePool.Pooling.MEAN);
Tensor<BSF> sequence = Tensor.zerosBSF(4, 6, 3);
Tensor<BC> pooled = pool.forward(sequence);
Tensor<BSF> gradient = pool.backward(Tensor.zerosBC(4, 3));

注意

只提供 LAST 与 MEAN 两种策略,没有 MAX 归约。

注意

MEAN 对全部时间步取平均,不区分填充位置;变长序列需自行保证有效长度一致。

注意

backward 依赖最近一次 forward 记录的形状,未前向即调用会因形状不匹配而失败。

注意

本层无可训练参数,parameters 继承默认实现返回空列表。

参见:basalt.nn.SequenceNetwork, basalt.nn.LSTM

方法

of(Pooling pooling) → SequencePoolstatic

按指定策略创建池化层。

参数
  • pooling (Pooling) – 时间轴归约策略,不能为 null

返回

新的 SequencePool 实例

抛出
  • IllegalArgumentException – pooling 为 null

forward(Tensor<BSF> input) → Tensor<BC>

按策略把序列归约为单个向量。

注意

记录 batch、sequence、features 三个维度,供 backward 校验与还原。

参数
  • input (Tensor<BSF>) – 形状 (batch, sequence, features) 的 BSF 张量,sequence 须至少为 1

返回

形状 (batch, features) 的 BC 张量;LAST 取末步,MEAN 取时间均值

backward(Tensor<BC> gradient) → Tensor<BSF>

把池化梯度还原到时间轴。

注意

LAST 只写末步,MEAN 对每个时间步写入 gradient / sequence。

参数
  • gradient (Tensor<BC>) – 形状 (batch, features) 的上游梯度

返回

形状 (batch, sequence, features) 的梯度,未参与归约的时间步为零

抛出
  • IllegalArgumentException – gradient 形状与最近一次 forward 不匹配,包括尚未调用 forward 的情况

pooling() → Pooling

返回池化策略。

返回

创建时指定的 Pooling 枚举值

record TrainingHistory

逐轮训练损失的不可变快照。

记录组件是一个 double[],紧凑构造器与访问器都做 clone,因此构造后外部持有的数组无法影响快照,取出后修改也不影响内部状态。epochs() 等于数组长度,lastLoss() 取最后一个元素。

注意

每次调用 losses() 都复制数组,循环中反复取值会产生额外分配

注意

长度为 0 时 lastLoss 抛 IllegalStateException

注意

由 CNN.fit 与 SequenceNetwork.fit 返回,长度分别等于 epochs 与轮数

参见:basalt.nn.CNN

方法

losses() → double[]

返回逐轮损失的副本。

返回

按轮次排列的损失副本,修改不影响记录本身

epochs() → int

返回记录的轮数。

返回

losses 数组长度

lastLoss() → double

返回最后一轮的损失。

返回

数组最后一个元素

抛出
  • IllegalStateException – 没有任何轮次记录

final class VanillaRNN implements Layer<BSF, BSF>, Persistable

最简 Elman 循环层:h_t = tanh(W x_t + U h_{t-1} + b)。

只有一组输入权重、一组循环权重与一个偏置,没有门控。h_0 为零向量,forward 返回整段序列的全部隐藏状态,形状为 (batch, sequence, hiddenSize)。

该层保留作对照基线:BPTT 的雅可比是 U^T diag(1 - h^2) 的连乘,谱范数通常小于 1,早期时间步的梯度随序列长度按几何级数衰减。

示例
VanillaRNN rnn = VanillaRNN.builder().hiddenSize(8).seed(42).build();
Tensor<BSF> input = Tensor.zerosBSF(2, 5, 3);
Tensor<BSF> output = rnn.forward(input);
Tensor<BSF> inputGradient = rnn.backward(Tensor.zerosBSF(2, 5, 8));
rnn.update(Optimizer.SGD.withLearningRate(0.05));

注意

参数规模为 hiddenSize * (features + hiddenSize + 1),约为同规模 LSTM 的四分之一。

注意

bias 以全零初始化;输入权重与循环权重分别按 ±sqrt(6 / (features + hiddenSize)) 与 ±sqrt(6 / (2 * hiddenSize)) 的均匀分布采样。

注意

不提供 hiddenSize() 访问器,隐藏单元数只在构建时可见。

注意

必须按 forward → backward → update 的顺序调用。

参见:basalt.nn.LSTM, basalt.nn.GRU, basalt.nn.SequenceNetwork

方法

builder() → Builderstatic

创建构建器,hiddenSize 默认 16、seed 默认 42。

返回

新的 Builder 实例

forward(Tensor<BSF> input) → Tensor<BSF>

沿时间轴前向传播,返回全序列隐藏状态。

注意

隐藏缓冲每次前向先清零,t=0 不使用循环项。

参数
  • input (Tensor<BSF>) – 形状 (batch, sequence, features) 的 BSF 张量;features 在首次调用时确定并据此分配参数

返回

形状 (batch, sequence, hiddenSize) 的张量,每步为 tanh(W x_t + U h_{t-1} + b)

抛出
  • IllegalArgumentException – features 与首次 forward 时确定的值不一致

backward(Tensor<BSF> gradient) → Tensor<BSF>

沿时间轴反向传播,累积参数梯度并返回输入梯度。

注意

每步梯度乘以 1 - h^2,长序列上乘积累积会使早期时间步的梯度迅速趋近零。

参数
  • gradient (Tensor<BSF>) – 上游梯度,形状必须为 (batch, sequence, hiddenSize)

返回

对输入的梯度,形状 (batch, sequence, features)

抛出
  • IllegalStateException – 尚未调用 forward

  • IllegalArgumentException – gradient 的形状与最近一次 forward 不匹配

update(OptimizerConfig optimizer)

用累积梯度对三组参数执行一次优化器更新。

参数
  • optimizer (OptimizerConfig) – 优化器超参数,同时作用于输入权重、循环权重与偏置

抛出
  • IllegalStateException – 未完成 forward 与 backward,或梯度已被上一次 update 消费

parameters() → List<ParameterRef>

返回三组参数的实时视图。

返回

参数视图列表,名称依次为 rnn.inputWeights、rnn.recurrentWeights、rnn.bias;首次 forward 之前为空列表

kind() → ModelKind

返回模型族标识。

返回

固定为 ModelKind.VANILLA_RNN

write(ModelWriter writer)

把配置与权重写入模型流。

注意

未初始化时 inputFeatures 写为 -1,且不写权重数组。

参数
  • writer (ModelWriter) – 目标写入器;依次写入 hiddenSize、seed、inputFeatures,已初始化时再写三组权重

read(ModelReader reader) → VanillaRNNstatic

从模型流恢复 VanillaRNN 实例。

参数
  • reader (ModelReader) – 源读取器,读取顺序须与 write 一致

返回

恢复后的实例;若流中记录了特征数,则同时重建并覆盖权重

抛出
  • ModelFormatException – hiddenSize 非正,或特征标记既非 -1 也非正数

static final class Conv2D.Builder

Conv2D 的分阶段构建器,默认 1 个 3×3 卷积核。

所有 setter 返回构建器自身,可链式调用;字段默认值为 filters = 1、kernelSize = (3, 3)、strides = (1, 1)、padding = VALID、activation = RELU、seed = 42。

build 时校验 filters、卷积核与步长均为正,并拒绝 SOFTMAX:卷积层按输出位置逐元素激活,无法对单点做整行归一化。

示例
Conv2D conv = Conv2D.builder()
        .filters(16)
        .kernelSize(5, 5)
        .strides(2, 2)
        .padding(Padding.SAME)
        .activation(Activation.GELU)
        .seed(2024L)
        .build();

注意

输入通道数不在构建期指定,由首次 forward 从输入推断

注意

每次 build 生成独立的 Conv2D 实例,参数在首次前向时才分配

参见:basalt.nn.Conv2D, basalt.nn.Padding, basalt.nn.Activation

方法

filters(int filters) → Builder

设置输出通道数。

注意

默认 1;同时决定偏置长度与 fanOut

注意

取值在 build 时校验,setter 本身不做检查

参数
  • filters (int) – 卷积核数量,即输出通道数,须为正

返回

当前构建器,便于链式调用

kernelSize(int height, int width) → Builder

设置卷积核的空间尺寸。

注意

默认 (3, 3);非方形核会同时影响两个方向的输出尺寸与 fanIn、fanOut

注意

取值在 build 时校验,setter 本身不做检查

参数
  • height (int) – 卷积核高度,须为正

  • width (int) – 卷积核宽度,须为正

返回

当前构建器,便于链式调用

strides(int height, int width) → Builder

设置两个方向的滑动步长。

注意

默认 (1, 1);SAME 下输出尺寸为 ceil(in / stride),与步长直接相关

注意

取值在 build 时校验,setter 本身不做检查

参数
  • height (int) – 垂直步长,须为正

  • width (int) – 水平步长,须为正

返回

当前构建器,便于链式调用

padding(Padding padding) → Builder

设置补零模式。

注意

默认 VALID

参数
  • padding (Padding) – Padding.VALID 或 Padding.SAME

返回

当前构建器,便于链式调用

抛出
  • NullPointerException – padding 为 null,由 Objects.requireNonNull 立即抛出

activation(Activation activation) → Builder

设置卷积后逐元素应用的激活函数。

注意

默认 RELU;该选择同时影响权重初始化幅度

参数
  • activation (Activation) – 任意 Activation 常量,SOFTMAX 除外

返回

当前构建器,便于链式调用

抛出
  • NullPointerException – activation 为 null,立即抛出

  • IllegalArgumentException – 在 build 时为 SOFTMAX

seed(long seed) → Builder

设置权重初始化的随机种子。

注意

默认 42;相同种子与相同形状会得到相同初值

参数
  • seed (long) – 传给 java.util.Random 的种子

返回

当前构建器,便于链式调用

build() → Conv2D

校验配置并创建 Conv2D。

注意

不校验输入通道数,通道数在首次 forward 时确定

返回

新的 Conv2D 实例,参数尚未分配

抛出
  • IllegalArgumentException – filters、卷积核或步长非正,或激活为 SOFTMAX

static final class Dense.Builder

Dense 的构建器,默认 1 个单元且无激活。

字段默认值为 units = 1、activation = NONE、seed = 42。输入特征数不在构建期指定,由首次 forward 从输入第 1 维推断并锁定。

build 只校验 units 为正,特征数一致性留到前向阶段检查;构建期不分配任何数组,因此先建层、后决定输入宽度的写法是安全的。

示例
Dense dense = Dense.builder()
        .units(64)
        .activation(Activation.TANH)
        .seed(11L)
        .build();

Dense head = Dense.builder()
        .units(3)
        .activation(Activation.SOFTMAX)
        .build();

注意

默认 NONE 激活意味着构建后即为纯线性层,需要非线性时显式指定

注意

每次 build 生成独立实例,参数在首次前向时才分配

参见:basalt.nn.Dense, basalt.nn.Activation

方法

units(int units) → Builder

设置输出单元数。

注意

默认 1;同时决定偏置长度与 Glorot 初始化的 fanOut

注意

取值在 build 时校验,setter 本身不做检查

参数
  • units (int) – 输出维度,须为正

返回

当前构建器,便于链式调用

activation(Activation activation) → Builder

设置该层的激活函数。

注意

默认 NONE;RELU 会改用 √(6/features) 的初始化幅度

参数
  • activation (Activation) – 任意 Activation 常量

返回

当前构建器,便于链式调用

抛出
  • NullPointerException – activation 为 null,立即抛出

seed(long seed) → Builder

设置权重初始化的随机种子。

注意

默认 42;相同种子与相同形状会得到相同初值

参数
  • seed (long) – 传给 java.util.Random 的种子

返回

当前构建器,便于链式调用

build() → Dense

校验配置并创建 Dense。

注意

特征数由首次 forward 决定,构建期无法校验

返回

新的 Dense 实例,参数尚未分配

抛出
  • IllegalArgumentException – units 非正

static final class Flatten.Builder

Flatten 的构建器,无任何可配置项。

Flatten 没有参数与超参数,Builder 只为与其它层保持一致的构造入口而存在,build 不接受任何前置调用。

示例
Flatten flatten = Flatten.builder().build();

注意

每次 build 返回新的无状态 Flatten 实例,形状在首次 forward 时记录

参见:basalt.nn.Flatten

方法

build() → Flatten

创建 Flatten 实例。

注意

形状缓存由 forward 填充,与构建过程无关

返回

新的 Flatten,无状态且可重复使用

static final class GRU.Builder

GRU 构建器,隐藏单元数默认 16,随机种子默认 42。

仅暴露隐藏单元数与随机种子两项配置,其余结构由层自身固定。构建出的 GRU 在首次 forward 前不持有参数,因此构建器不校验输入特征数。

注意

校验发生在 build:hiddenSize 非正时抛出 IllegalArgumentException,而非在 setter 中。

参见:basalt.nn.GRU

方法

hiddenSize(int hiddenSize) → Builder

设置隐藏单元数,默认 16。

参数
  • hiddenSize (int) – 隐藏状态维度;必须为正,在 build 时校验

返回

this,便于链式调用

seed(long seed) → Builder

设置权重初始化随机种子,默认 42。

参数
  • seed (long) – 传给 java.util.Random 的种子;相同种子与相同特征数可复现初始权重

返回

this,便于链式调用

build() → GRU

校验配置并构造 GRU。

返回

新的 GRU 实例,参数尚未分配

抛出
  • IllegalArgumentException – hiddenSize 小于等于 0

record GradientCheck.Report

梯度检验的最大相对误差与检查次数。

记录组件为 maxInputError、maxParameterError、inputChecks 与 parameterChecks,分别是输入维度、参数维度的最大相对误差,以及两个维度的实际检查次数。

注意

只做数据承载,不校验容差;判定交给 passed

注意

checkNetwork 产生的报告 inputChecks 恒为 0、maxInputError 恒为 0.0

参见:basalt.nn.GradientCheck

方法

passed(double tolerance) → boolean

判断两个最大相对误差是否都不超过容差。

注意

容差取 0 时只有误差恰为 0 才通过

注意

容差由调用方决定,类内没有推荐值

参数
  • tolerance (double) – 相对误差上界,按 max(1, |analytic| + |numeric|) 归一化后的量

返回

maxInputError 与 maxParameterError 均不大于 tolerance 时为 true

toString() → String

返回检查次数与最大误差的单行摘要。

注意

使用 Locale.ROOT 格式化,避免区域设置影响小数点

返回

形如 inputs=12 maxErr=1.234e-08, parameters=16 maxErr=5.678e-09 的字符串

static final class LSTM.Builder

LSTM 构建器,隐藏单元数默认 16,随机种子默认 42。

仅暴露隐藏单元数与随机种子两项配置。构建出的 LSTM 在首次 forward 前不持有参数,输入特征数与三组权重的形状都推迟到首次前向时确定。

注意

校验发生在 build:hiddenSize 非正时抛出 IllegalArgumentException,而非在 setter 中。

参见:basalt.nn.LSTM

方法

hiddenSize(int hiddenSize) → Builder

设置隐藏单元数,默认 16。

参数
  • hiddenSize (int) – 隐藏状态与单元状态的维度;必须为正,在 build 时校验

返回

this,便于链式调用

seed(long seed) → Builder

设置权重初始化随机种子,默认 42。

参数
  • seed (long) – 传给 java.util.Random 的种子;相同种子与相同特征数可复现初始权重

返回

this,便于链式调用

build() → LSTM

校验配置并构造 LSTM。

返回

新的 LSTM 实例,参数尚未分配

抛出
  • IllegalArgumentException – hiddenSize 小于等于 0

static final class MaxPooling2D.Builder

MaxPooling2D 的构建器,默认 2×2 且不重叠。

字段默认值为 poolSize = (2, 2),strideHeight 与 strideWidth 为 0,表示“未指定”。构造层时若步长为 0,则替换为对应的窗口尺寸,从而得到不重叠池化。

build 校验窗口尺寸为正、步长非负,且两个步长必须同时缺省或同时为正;只给一个方向指定步长会被拒绝,负步长同样拒绝。

示例
MaxPooling2D pool = MaxPooling2D.builder()
        .poolSize(3, 3)
        .strides(1, 1)
        .build();

注意

步长小于窗口尺寸即得到重叠池化,反向时同一输入位置会累加多个窗口的梯度

参见:basalt.nn.MaxPooling2D

方法

poolSize(int height, int width) → Builder

设置池化窗口尺寸。

注意

默认 (2, 2);未指定步长时步长取该尺寸

注意

取值在 build 时校验,setter 本身不做检查

参数
  • height (int) – 窗口高度,须为正

  • width (int) – 窗口宽度,须为正

返回

当前构建器,便于链式调用

strides(int height, int width) → Builder

设置两个方向的池化步长。

注意

默认两个方向都为 0,即不重叠池化

注意

取值在 build 时校验,setter 本身不做检查

参数
  • height (int) – 垂直步长;0 表示取 poolHeight

  • width (int) – 水平步长;0 表示取 poolWidth

返回

当前构建器,便于链式调用

build() → MaxPooling2D

校验配置并创建 MaxPooling2D。

注意

构建期不校验输入尺寸,窗口是否放得下在 forward 时检查

返回

新的 MaxPooling2D 实例,步长已按窗口尺寸补齐

抛出
  • IllegalArgumentException – 窗口非正、步长为负,或两个步长只指定了一个

static final class NeuralNetwork.ImageBuilder

图像阶段构建器,累积 Conv2D 与 MaxPooling2D。

内部维护一个层列表。两个 add 重载追加层后返回自身以支持链式调用;add(Flatten) 追加后把列表复制给新的 VectorBuilder。该阶段不提供 build,因此缺少 Flatten 的模型无法产出。

注意

没有接受 Dense 的重载,全连接层在类型上无法出现在 Flatten 之前

注意

进入 VectorBuilder 时复制列表,两个构建器不再共享可变状态

参见:basalt.nn.NeuralNetwork.VectorBuilder, basalt.nn.Conv2D, basalt.nn.MaxPooling2D

方法

add(Conv2D layer) → ImageBuilder

追加一个卷积层。

参数
  • layer (Conv2D) – 卷积层,不可为 null

返回

this,便于继续链式调用

抛出
  • NullPointerException – layer 为 null

add(MaxPooling2D layer) → ImageBuilder

追加一个最大池化层。

参数
  • layer (MaxPooling2D) – 池化层,不可为 null

返回

this,便于继续链式调用

抛出
  • NullPointerException – layer 为 null

add(Flatten layer) → VectorBuilder

追加 Flatten 并切换到向量阶段。

注意

这是图像阶段唯一的出口,之后的构建器不再接受卷积或池化层

参数
  • layer (Flatten) – 展平层,不可为 null

返回

持有已累积层列表副本的 VectorBuilder

抛出
  • NullPointerException – layer 为 null

static final class NeuralNetwork.VectorBuilder

向量阶段构建器,累积 Dense 并配置损失与优化器。

构造时复制图像阶段累积的层列表,之后只接受 Dense。loss 与 optimizer 各自持有默认值,build 时原样传给 CNN 构造器,因此模型创建后这两项不可再改。

注意

默认损失 CATEGORICAL_CROSSENTROPY,默认优化器 Optimizer.ADAM.withLearningRate(0.001)

注意

build 不校验损失与末端激活是否匹配,也不检查层数是否为空

参见:basalt.nn.NeuralNetwork.ImageBuilder, basalt.nn.CNN

方法

add(Dense layer) → VectorBuilder

追加一个全连接层。

参数
  • layer (Dense) – 全连接层,不可为 null;单位数在构建该层时已校验

返回

this,便于继续链式调用

抛出
  • NullPointerException – layer 为 null

loss(Loss loss) → VectorBuilder

设置训练损失,默认 CATEGORICAL_CROSSENTROPY。

注意

与末端激活的匹配关系不在构建期检查

参数
  • loss (Loss) – 损失函数,不可为 null

返回

this

抛出
  • NullPointerException – loss 为 null

optimizer(OptimizerConfig optimizer) → VectorBuilder

设置优化器超参,默认 ADAM 学习率 0.001。

参数
  • optimizer (OptimizerConfig) – 超参配置,不可为 null;其字段在构造时已校验

返回

this

抛出
  • NullPointerException – optimizer 为 null

build() → CNN

用当前层列表、损失与优化器创建 CNN。

注意

不做损失与激活的匹配校验,也不校验 Flatten 之后是否至少有一个 Dense

返回

层列表已复制为不可变列表的 CNN 实例

static final class SelfAttention.Builder

自注意力构建器,dimension 默认 16、seed 默认 42、非因果。

投影维度同时决定输入特征数、输出特征数与权重矩阵尺寸,三者被强制一致,因此该层不改变特征轴长度。因果标志只影响注意力掩码,不改变参数量。

注意

缩放因子 1 / sqrt(dimension) 在构造时计算,之后不可更改。

参见:basalt.nn.SelfAttention

方法

dimension(int dimension) → Builder

设置投影维度,默认 16。

参数
  • dimension (int) – 每个位置的 Q/K/V 维度,也是输入输出特征数;必须为正,在 build 时校验

返回

this,便于链式调用

seed(long seed) → Builder

设置权重初始化随机种子,默认 42。

参数
  • seed (long) – 传给 java.util.Random 的种子;Q、K、V 三组权重依次从同一随机源采样

返回

this,便于链式调用

causal(boolean causal) → Builder

设置是否启用因果掩码,默认关闭。

注意

掩码方向为“看过去”:行索引小于列索引的位置被置零。

参数
  • causal (boolean) – true 时位置 t 只对键 0..t 归一化,屏蔽未来时间步;false 时对全部键归一化

返回

this,便于链式调用

build() → SelfAttention

校验配置并构造自注意力层。

返回

新的 SelfAttention 实例,权重在首次 forward 时分配

抛出
  • IllegalArgumentException – dimension 小于等于 0

static final class SequenceNetwork.Builder

整网构建器;LAST 池化、分类交叉熵、Adam 0.001、裁剪 5.0。

主体层按 add 调用顺序串联,池化与头部各自只有一个实例。所有 setter 以 requireNonNull 拒绝 null 引用,校验集中在 build,因此配置错误在构建阶段一次性暴露。

注意

build 会交叉校验损失与头部激活:分类交叉熵配 SOFTMAX,二元交叉熵配 SIGMOID。

注意

默认池化 LAST 适合分类;需要利用全部时间步时可改为 MEAN。

参见:basalt.nn.SequenceNetwork, basalt.nn.SequencePool, basalt.nn.Dense

方法

<L extends Layer<BSF, BSF>> add(L layer) → Builder

向主体追加一个序列层,按调用顺序串联。

注意

泛型约束使 Dense 等 BC 布局的层无法加入主体,编译期即被拒绝。

参数
  • layer (L) – 类型为 Layer<BSF, BSF> 的层,如 LSTM、GRU、VanillaRNN、SelfAttention;不能为 null

返回

this,便于链式调用

pooling(SequencePool.Pooling pooling) → Builder

设置时间轴池化策略,默认 LAST。

参数
  • pooling (SequencePool.Pooling) – LAST 取末步,MEAN 取时间均值;不能为 null

返回

this,便于链式调用

head(Dense head) → Builder

设置输出头,必填项。

注意

头部的 units 决定输出类别数,激活函数需与损失匹配。

参数
  • head (Dense) – Dense 层;其输入维度须等于池化后的特征数,不能为 null

返回

this,便于链式调用

loss(Loss loss) → Builder

设置损失函数,默认分类交叉熵。

参数
  • loss (Loss) – Loss 枚举值,不能为 null

返回

this,便于链式调用

optimizer(OptimizerConfig optimizer) → Builder

设置优化器超参数,默认 Adam、学习率 0.001。

参数
  • optimizer (OptimizerConfig) – 优化器配置;可用 Optimizer.ADAM.withLearningRate(...) 等工厂方法构造,不能为 null

返回

this,便于链式调用

gradientClip(double gradientClip) → Builder

设置全局梯度范数裁剪阈值,默认 5.0。

注意

裁剪作用于整网所有参数的拼接梯度范数,而不是逐参数单独裁剪。

参数
  • gradientClip (double) – 范数上限;须为有限正数,在 build 时校验

返回

this,便于链式调用

build() → SequenceNetwork

校验配置并构造整网。

返回

新的 SequenceNetwork 实例;主体层列表被复制,后续修改构建器不影响已建网络

抛出
  • IllegalStateException – 未设置 head

  • IllegalArgumentException – gradientClip 非正、损失与头部激活不匹配,或分类头 units 小于 2

enum SequencePool.Pooling

时间轴归约策略。

LAST 取最后一个时间步的向量,是分类任务默认的 many-to-one 头;MEAN 对全部时间步求平均,每个时间步都影响结果。两者都保持特征轴不变。

枚举声明顺序为 LAST、MEAN,持久化时按序号写入,序号 0 对应 LAST、1 对应 MEAN。反向传播只使用策略本身,不需要额外状态。

注意

调整枚举常量顺序会改变序列化序号,导致旧模型文件被解析为另一种策略。

注意

没有 MAX 归约;需要最大值池化时应在层外自行实现。

参见:basalt.nn.SequencePool, basalt.nn.SequenceNetwork

枚举成员

LAST()

取时间步序列的最后一步

MEAN()

对时间步维取平均

static final class VanillaRNN.Builder

VanillaRNN 构建器,隐藏单元数默认 16,随机种子默认 42。

仅暴露隐藏单元数与随机种子。参数在首次 forward 时按输入特征数分配,构建阶段不校验特征数,因此同一构建器产出的层可以适配不同输入宽度。

注意

校验发生在 build:hiddenSize 非正时抛出 IllegalArgumentException。

参见:basalt.nn.VanillaRNN

方法

hiddenSize(int hiddenSize) → Builder

设置隐藏单元数,默认 16。

参数
  • hiddenSize (int) – 隐藏状态维度;必须为正,在 build 时校验

返回

this,便于链式调用

seed(long seed) → Builder

设置权重初始化随机种子,默认 42。

参数
  • seed (long) – 传给 java.util.Random 的种子;相同种子与相同特征数可复现初始权重

返回

this,便于链式调用

build() → VanillaRNN

校验配置并构造 VanillaRNN。

返回

新的 VanillaRNN 实例,参数尚未分配

抛出
  • IllegalArgumentException – hiddenSize 小于等于 0

包内可见类型

以下类型没有 public 修饰符,但出现在公开签名中,因此一并列出。它们不是稳定 API,跨版本可能变化。

final class Parameter

带私有 SGD/Adam 状态的训练参数数组

每个实例持有一份 double[] 权重与两份同长的一阶、二阶矩缓冲,Dense、Conv2D、GRU、LSTM 等层在构造时创建,训练循环里调用 update 原地更新。

update 按 OptimizerConfig 选择 SGD 或 Adam:两者都用 VectorSpecies 做向量化循环并以标量循环收尾;Adam 路径每更新一次把 step 加一,并按 1 - beta^step 做偏差校正。

层的 parameters() 通过 ParameterRef 暴露 values() 数组,因此这一类型会出现在参数列表相关的签名链上,调用方据此读取或检查权重。