rustyml 0.15.0

A high-performance machine learning & deep learning library in pure Rust, offering ML algorithms and neural network support
Documentation
# 1.1. 认识RustyML

RustyML 是一个完全用 Rust 编写的机器学习与深度学习库。它覆盖数据科学项目所需的完整流程(数据预处理、特征工程、模型训练与评估)。RustyML提供经典机器学习的估计器(线性模型、决策树、SVM、聚类、降维、异常检测),也提供Keras风格的神经网络框架。

本指南对应的版本是`0.15`。API正在趋于稳定,但次要版本仍可能引入破坏性更改。所以在生产环境里请在 `Cargo.toml` 中锁定具体版本,别用 `*`。权威的API参考在[docs.rs/rustyml](https://docs.rs/rustyml),源码在[github.com/SomeB1oody/RustyML](https://github.com/SomeB1oody/RustyML)。

## 1.1.1. 纯 Rust,端到端

RustyML特性是没有任何C/C++代码(不链接BLAS,没有LAPACK,没有CUDA)。这使得其可移植性强,不需要用户手动配置复杂的环境,并且不容易在编译阶段出现莫名其妙的错误,非常适合生产环境和新手入门。大部分代码都是用Safe Rust写成,其内存安全性因此得以保证。

为了保证性能,矩阵乘法使用纯Rust的[`gemmkit`](https://crates.io/crates/gemmkit) crate(经由零拷贝的[`gemmkit-ndarray`](https://crates.io/crates/gemmkit-ndarray)适配器接入`ndarray`)。它在运行时分派到当前CPU真正支持的最宽SIMD指令集(AVX-512F、AVX2+FMA、NEON、wasm `simd128`,并有标量兜底),还会自行判断一次乘法值不值得开线程、开几个,使得在不同硬件上都能获得极好的性能。

## 1.1.2. 并行

RustyML用Rayon并行化它的计算密集内核,但不会盲目地并行化。尺寸低于某个界限时,多线程的额外开销会让并行比串行还慢,所以RustyML每一类内核都有一个校准过的尺寸阈值,只有当并行速度实测超过串行时才切换到并行路径。这些阈值并非写死的常量,可以通过`rusyml::tuning`在运行时覆盖这些开关而无需重新编译。当你把同一个二进制部署到核心数差异很大的机器上时,这一点尤其关键。详细内容见[性能调优与并行](../Chapter-07/7.3._性能调优与并行.md)。

RustyML的设计优点:
- 并行归约是*确定性*的:分块折叠无论用多少线程都按固定顺序求和,所以结果不会随核心数增加而漂移。
- 性能可预测:没有垃圾回收带来的卡顿,没有JIT预热,也没有全局解释器锁把线程串行化。
- 几乎每个随机化组件都遵循一个全局种子(见[可复现性与随机种子](../Chapter-07/7.1._可复现性与随机种子.md)),一次运行在不同机器上都有可复现性。降维器里的迭代特征求解器是有意排除在外的,因为无论种子取什么它们都收敛到同一个结果。

## 1.1.3. Feature和模块

RustyML划分五个模块,每个都由一个Cargo feature控制(`prelude`是共享的)。你可以通过指定feature来只编译用得上的部分。`machine_learning`、`neural_network`、`utils`和`metrics`都会自动启用`math`。

| Feature / 模块     | 包含内容                                                                                                                                             |
|--------------------|------------------------------------------------------------------------------------------------------------------------------------------------------|
| `machine_learning` | 经典机器学习的估计器                                                                                                                                 |
| `neural_network`   | `Sequential`模型以及各种层(`Dense`、卷积、池化、循环、dropout、归一化)、激活函数、优化器(`SGD`、`Adam`、`AdamW`、`RMSprop`、`AdaGrad`)和损失函数 |
| `utils`            | 预处理(`StandardScaler`等缩放器、`to_categorical`等标签转化功能)与数据集划分(`train_test_split`、`train_test_split_stratified`)                  |
| `metrics`          | 面向回归、分类(`ConfusionMatrix`、ROC AUC、log loss...)和聚类(ARI、silhouette...)的评估指标                                                      |
| `math`             | 数学计算、`gemmkit`支持的矩阵乘积                                                                                                                    |

`default`feature启用打开全部功能。另有一个独立的`show_progress` feature用来显示训练进度条,详见[安装与 Feature 配置](./1.2._安装与Feature配置.md)。

## 1.1.4. 仿scikit-learn和Keras的API设计

经典估计器仿照scikit-learn,提供`fit`和`predict`等方法,神经网络仿照Keras的`Sequential`模型以及其`add`/`compile`/`fit`/`predict`方法,对于熟悉Python上数据科学生态环境的人来说更好入门。变的地方在于数据是使用`ndarray`数组而非NumPy (详见[使用 ndarray 准备数据](./1.3._使用ndarray准备数据.md)),可能失败的调用返回`Result`而不是抛异常。

以下是一个经典机器学习的代码例,你可以看出其API设计和scikit-learn类似:

```rust
use rustyml::prelude::machine_learning::*;
use ndarray::array;

fn main() {
    // new(fit_intercept);默认求解器是精确的闭式解
    let mut model = LinearRegression::new(true);

    let x = array![[1.0, 2.0], [2.0, 3.0], [3.0, 4.0]];
    let y = array![6.0, 9.0, 12.0];

    model.fit(&x, &y).unwrap();
    let predictions = model.predict(&x).unwrap();
    println!("predictions: {:?}", predictions);
}
```

接着是神经网络代码里,你可以看出其架构设计和Keras类似:

```rust
use rustyml::prelude::neural_network::*;
use ndarray::Array;

fn main() {
    // 4 个样本,8 个输入特征,1 个输出
    let x = Array::ones((4, 8)).into_dyn();
    let y = Array::ones((4, 1)).into_dyn();

    let mut model = Sequential::new();
    model
        .add(Dense::new(8, 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.summary(); // 打印网络结构,就像 Keras 一样
    model.fit(&x, &y, 5).unwrap();

    let predictions = model.predict(&x).unwrap();
    println!("prediction shape: {:?}", predictions.shape());
}
```

指标计算和scikit-learn也保持相似设计(每个指标都按 `(y_true, y_pred)` 这个顺序接收参数):

```rust
use rustyml::metrics::*;
use ndarray::array;

fn main() {
    let y_true = array![1.0, 0.0, 0.0, 1.0, 1.0];
    let y_pred = array![1.0, 0.0, 1.0, 1.0, 0.0];

    let cm = ConfusionMatrix::new(&y_true, &y_pred);
    println!("accuracy: {:.3}", cm.accuracy());
    println!("f1 score: {:.3}", cm.f1_score());
}
```

与Python不同,RustyML的错误传播设计会把一个可能失败的调用的结果用`Result<T, Error>`包裹,你可以使用`match`来对不同的可能返回值(要么是执行成功返回`T`,要么是执行失败返回`Error`)进行不同的处理,详见[错误处理](./1.6._错误处理.md)。

超参数在被传入的那一处就完成校验,非法的数值会当场被拒。配置采用builder模式:估计器的核心超参数在`new`里指明,再通过链式的`with_*`方法叠加可选设置,每一步各自校验自己收到的东西(例如`LinearRegression::new(true).with_regularization(..)?`)。

## 1.1.5. 纯Rust的优势

一个RustyML程序编译成单个自包含的二进制。不用装其他工具链和配置复杂的环境。训练好的经典模型和神经网络权重通过`save_to_path` / `load_from_path`序列化为二进制,详见[深入模型持久化](../Chapter-07/7.2._深入模型持久化.md)。因为没有GC、没有解释器并且没有预热,延迟是可预测的。

## 1.1.6. 关于适用范围

RustyML只跑在CPU上。它没有GPU或CUDA后端。这套神经网络框架适合中小规模模型以及贴近经典机器学习的深度学习,不适合训练大型视觉或语言模型。经典的`machine_learning`和`utils`估计器都接收元素为`f64`的特征矩阵,但`predict`吐回来的元素类型随模型而变,详见[使用 ndarray 准备数据](./1.3._使用ndarray准备数据.md)里的表格。神经网络栈工作在`f32`上,它的张量类型是 `Tensor = ArrayD<f32>`。这套神经网络框架不像PyTorch那样构建动态自动微分图。