5 个不稳定版本

0.22.0 2024 年 6 月 28 日
0.21.1 2024 年 3 月 5 日
0.21.0 2024 年 2 月 23 日
0.20.1 2024 年 2 月 7 日
0.20.0 2023 年 9 月 29 日

#115机器学习

每月 35 次下载

MIT/Apache

330KB
7K SLoC

affinitree

crates.io docs.rs github build

affinitree crate 提供数据结构和算法,以高效地从分段线性神经网络中提取决策树。

功能特性

目前支持以下功能

  • 从一系列分段线性层(例如,ReLU、leaky ReLU、hard tanh、hard sigmoid)构建决策树
  • 使用组合合并决策树实例
  • 使用 Graphviz 的 DOT 语言可视化决策树
  • 使用不可行路径消除优化决策树
  • 手动构建决策树以表示任何分段线性函数(如自定义激活函数)

以下提供了一个简要指南。

请随时贡献新的功能!

与 Cargo 一起使用

[dependencies]
affinitree = "0.22.0"

支持 Rust 1.64 及更高版本。

技术细节

crate 分为四个部分

  1. tree:决策树的数据结构和算法
  2. linalg:线性函数、多面体和线性规划
  3. pwl:存储为决策树的分段线性函数
  4. distill:将分段线性神经网络提炼为决策树

此 crate 侧重于使用决策树高效表示分段线性函数。决策树是在 slab crate 提供的竞技场之上实现的。树中的元素在其生命周期内具有唯一的索引。然而,在删除后,索引可以被重用。树 API 面向 petgraph

此 crate 需要基本的线性代数功能,如矩阵存储和乘法。为此,使用 crate ndarray

第一步

要开始使用 affinitree,最好是首先创建一个新的 AffTree 实例。AffTree 可以通过将它们存储为决策树来表示任何分段线性函数。它们是这个库的重要组成部分,并在许多上下文中使用。要构造一个基本的 AffTree,可以调用以下构造函数之一

use affinitree::pwl::afftree::AffTree;

let dim: usize = 4;
// Crate a new AffTree instance representing the identity function with input dimension 4
let dd1 = AffTree::<2>::new(dim);
// Same as above, but also allocate space for 31 additional nodes in the tree
let dd2 = AffTree::<2>::with_capacity(dim, 32);

生成的决策树简单地编码了恒等函数 $\mathbb{R}^{dim} \to \mathbb{R}^{dim}$。接下来,我们想要更新决策树。为此,让我们假设以下玩具示例:我们想要引入超平面 $x_1 - x_3 \leq 1$ 作为区分规则来将输入空间分为两个区域。让我们手动编码这样一个层。

use ndarray::{arr1, arr2};
use affinitree::{aff, linalg::affine::AffFunc};

// Crate a new affine function
let func1 = AffFunc::from_mats(arr2(&[[1., 0., -1., 0.]]), arr1(&[1.]));
// Same as above, but using the aff macro for convenience
let func2 = aff!([[1., 0., -1., 0.]] + [1.]);

assert_eq!(func1, func2);

现在将这个函数应用到我们的树上是很直接的。

dd1.apply_func(&func2);

然而,大多数神经网络的使用场景都包括具有非线性激活函数的更深层架构。为了将 ReLU 应用到我们的线性函数上,我们首先必须构建一个决策树,该树将 ReLU 函数编码为 AffTree 实例。Affinitree 随带一组预定义的分段线性函数,包括 ReLU。

use affinitree::distill::schema::ReLU;

let relu = ReLU(1);
dd.compose(&relu);

为了构建更深的架构,两种方法(apply_func 和 compose)都可以按顺序使用。可以以类似的方式使用其他分段线性激活函数。只需将函数编码到 AffTree 实例中,然后使用 compose 方法将激活函数应用到树上。

最后,由于手动构建 AffTree 实例对于大型网络来说可能会变得繁琐,因此提供了一个高级便利函数。此函数仅需要一个包含神经网络层的列表。此类列表可以通过使用 .npz 格式从文件系统读取或明确指定。对于其测试用例,affinitree 随带一些以这种格式存储的预训练网络。例如,mnist.npz 文件包含一个预训练网络,该网络在 MNIST 数据集的前七个主成分上运行,层结构为 7-5-5-5-10。

use affinitree::distill::builder::{read_layers, afftree_from_layers};

// Load a sequence of pretrained layers from a numpy file
let layers = read_layers(&"res/nn/mnist-5-5.npz").unwrap();
// Distill the sequence of layers with input dimension 7 into an AffTree without a precondition
let dd = afftree_from_layers(7, &layers, None);

有关更多示例,请参阅 测试用例

开发

该项目使用 Rust 生态系统开发,以确保效率、代码质量和一致性。要运行所有包含的单元测试,请在您的终端中执行以下命令

cargo test

为了保持代码格式的一致性,我们使用 rustfmt。我们的格式化规则定义在 rustfmt.toml 文件中。可以使用以下命令应用自动格式化

cargo +nightly fmt

这两个命令都会在我们的 GitHub 管道中自动对新提交调用。

对于微基准测试,我们依赖于 criterion。要测量当前工作目录并保存结果以供稍后比较,可以使用

cargo bench --bench distillation -- --save-baseline "$(date +%Y-%m-%d)_$(git rev-parse --short HEAD)"

并且对于代码分析和 linting,我们使用 clippy。它可以自动对检测到的问题应用修复

cargo clippy --fix 

许可

版权所有 2022–2024 affinitree 开发者。

由 Maximilian Schlüter、Jan Feider 和 Gerrit Nolte 想象和开发。

根据您的选择,许可协议为 Apache 许可协议第 2 版MIT 许可协议。您只能在不遵守这些条款的情况下使用此项目。

贡献

请随时创建问题、分支项目或提交拉取请求。

除非您明确说明,否则任何提交给作品以供包含的贡献,根据 Apache-2.0 许可协议定义,应按上述方式双许可,不附加任何额外条款或条件。

行为

请遵循 Rust 代码行为准则

依赖关系

~10–20MB
~267K SLoC