# 3. 神经网络
RustyML 内置了一个用纯 Rust 编写的小型深度学习框架,API 形态贴近 Keras。把层堆叠进一个 [`Sequential`](./3.1._Sequential模型.md) 模型,用一个优化器和一个损失函数 `compile`,再调用 `fit`/`predict`。框架里流动的每个张量都是 `Tensor`,它其实就是 `ndarray::ArrayD<f32>`。它是单精度、动态阶,不用 GPU,也没有 autograd 记录带。每一层都手写前向和反向传播,并通过一个扁平视图把参数交给优化器。这让整个框架保持确定、易于调试。用过 Keras 的话,你会一眼认出这套 API 的样子。真正的差异在于严格的 `f32` 精度、显式的输入维度,以及返回 `Result` 的构造函数,本章会逐一说明。
在开始本章前,请先读[第 1 章](../Chapter-01/1.0._快速上手.md)。也请读一下[使用 ndarray 准备数据](../Chapter-01/1.3._使用ndarray准备数据.md)和[安装与 Feature 配置](../Chapter-01/1.2._安装与Feature配置.md),因为 `neural_network` 这个 feature 控制着整个模块的编译。还要读[错误处理](../Chapter-01/1.6._错误处理.md),因为层和损失函数的构造函数都返回 `Result`。一个模型端到端的骨架长这样:
```rust
use rustyml::neural_network::{
sequential::Sequential,
layers::{Activation, Dense},
optimizers::Adam,
losses::MeanSquaredError,
};
use ndarray::Array;
fn main() {
let x = Array::ones((8, 4)).into_dyn(); // 8 个样本,4 个特征
let y = Array::ones((8, 1)).into_dyn(); // 8 个样本,1 个目标值
let mut model = Sequential::new();
model
.add(Dense::new(4, 16, Activation::ReLU).unwrap())
.add(Dense::new(16, 1, Activation::Linear).unwrap())
.compile(
Adam::new(0.001, 0.9, 0.999, 1e-8, 0.0).unwrap(),
MeanSquaredError::new(),
);
model.fit(&x, &y, 5).unwrap();
let preds = model.predict(&x).unwrap();
println!("prediction shape: {:?}", preds.shape());
}
```
[Sequential 模型](./3.1._Sequential模型.md)是把一堆层变成可训练网络的容器。它持有训练循环、优化器和损失函数,对外暴露 `add`、`compile`、`fit`、`train_batch`、`evaluate`、`predict`、`summary` 以及权重的保存/加载。批次打乱的种子(`set_seed`)和学习率的读写对(`learning_rate` / `set_learning_rate`)也归它管。就算你只是想看某一个具体的层,也请先读这一节。
[全连接层与激活函数](./3.2._全连接层与激活函数.md)讲的是全连接层——表格类模型的主力层,也是大多数网络的输出环节。这一节还讲 `Activation` 枚举(`ReLU`、`Sigmoid`、`Tanh`、`Softmax`、`Linear`),你可以把激活折叠进某一层,也可以把它当作独立的层来用。请把这一节放在第二个读。后面的内容都默认你已经清楚 `Dense` 层是怎么声明 `input_dim` 和 `units` 的。
[损失函数](./3.3._损失函数.md)是 `compile` 里“目标”的那一半。它涵盖回归用的均方误差和平均绝对误差,以及分类用的二分类、多分类和稀疏多分类交叉熵。请仔细读这一节。各个损失的平均约定是故意不一致的:有的按元素平均,有的按预测位置平均,在不同类别之间切换时,实际生效的学习率会被悄悄缩放。`CategoricalCrossEntropy` 和 `SparseCategoricalCrossEntropy` 都带一个 `from_logits` 标志,它决定了输出层还要不要再接一个 `Softmax`。`BinaryCrossEntropy` 没有这个标志,它始终要求输入是 `(0, 1)` 区间内的概率。
[优化器](./3.4._优化器.md)是 `compile` 里“更新”的那一半。它涵盖带动量的 SGD、Adam、AdamW、RMSprop 和 AdaGrad。这一节也涵盖按全局范数裁剪梯度(`global_clipnorm`)、耦合与解耦的权重衰减,以及训练途中调整学习率。3.1 到 3.4 这几节合起来,就是一个完整、可训练的前馈网络。
剩下的几节添加的是各种专用层,它们都能插进同一个 `Sequential`。[卷积层](./3.5._卷积层.md)提供 1D/2D/3D 卷积、把卷积反着跑从而把张量放大的转置卷积,以及面向空间数据的 depthwise 和 separable 变体。[池化层](./3.6._池化层.md)提供无参数的最大和平均下采样,以及它们的全局变体,并在最后讲把空间轴放大回去的 `UpSampling1D/2D/3D`。[循环层](./3.7._循环层.md)涵盖用于序列的 `SimpleRNN`、`LSTM` 和 `GRU`,并在最后讲把词索引变成这些层所读向量的 `Embedding`。[正则化与归一化层](./3.8._正则化与归一化层.md)涵盖 dropout(含 spatial dropout)、高斯噪声,以及 batch、layer、group、instance、unit 归一化。
其中只有最后一类的行为依赖模式:这类层在 `fit` 和 `predict` 下表现不同,而模型会替你切换模式。
[权重保存与加载](./3.9._权重保存与加载.md)为本章收尾。`save_to_path` 和 `load_from_path` 只持久化权重,格式是 postcard 二进制。它们不持久化网络结构。你需要在代码里重新搭出一模一样的层堆叠,再把权重加载进去。等你手里有了一个值得留存的模型,再读这一节。格式细节和版本兼容的注意事项,见[深入模型持久化](../Chapter-07/7.2._深入模型持久化.md)。