22 个版本 (7 个重大更新)
0.7.0 | 2022 年 12 月 21 日 |
---|---|
0.6.4 | 2022 年 9 月 5 日 |
0.5.0 | 2022 年 8 月 26 日 |
0.2.1 | 2022 年 7 月 31 日 |
#328 in 机器学习
71 每月下载量
345KB
6.5K SLoC
一个通过安全的 Rust 代码创建/训练/运行神经网络的 GPU 加速库。
目录
架构概述
Intricate 的布局与流行的库(如 Keras)非常相似。
它由一个 模型 组成,模型由 层 组成,可以使用 损失函数 进行调整,该损失函数还得到了 优化器 的帮助。
模型
正如之前所说,与 Keras 类似,Intricate 将模型定义为基本上是一个 层 的列表。
模型中逻辑不多,大部分工作都委托给了层,它所做的只是协调层如何协同工作以及数据如何从一个层传递到另一个层。
层
每个层都接收 输入 并返回 输出,遵循它们必须定义的一些规则。
它们还必须实现四个方法,这四个方法共同构成了反向传播
optimize_parameters
compute_gradients
apply_gradients
compute_loss_to_input_derivatives
正如之前所说,optimize_parameters 将依赖于一个 优化器,该优化器将尝试改进层允许其优化的参数。
这些方法将按顺序调用以在模型中执行反向传播,并使用来自 compute_loss_to_input_derivatives
的结果,然后对最后一层执行相同的操作,依此类推。
这些层可以是输入和输出的任何类型转换。一个例子是 Intricate 中的激活函数,它们实际上是层,而不是与其他层合并,这极大地简化了计算,并且工作得非常出色。
优化器
优化器就是你所想的,它们进行优化。
具体来说,它们优化层允许它们优化的参数,以及层的梯度,以便层可以使用它们来对自己的梯度进行优化。
这很有用,因为使用 Intricate 可以开发,也许还可以调试优化器,以查看它对特定用例的表现如何,这对于 Intricate 的未来发展非常有益。你所要做的就是创建一个实现 Optimizer
特性的结构体。
损失函数
损失函数基本上是一些用于确定模型好坏的特定特质的实现。
损失函数不用于层,而是用于模型本身。尽管层会使用与损失相关的导数,但它们实际上并不直接与损失函数通信。
使用 Intricate 实现 XoR
如果你查看存储库中的 examples/
,你会发现 XoR 是如何使用 Intricate 实现的。以下基本上只是这个示例,但有更多的解释。
设置训练数据
let training_inputs = vec![
vec![0.0, 0.0],
vec![0.0, 1.0],
vec![1.0, 0.0],
vec![1.0, 1.0],
];
let expected_outputs = vec![
vec![0.0],
vec![1.0],
vec![1.0],
vec![0.0],
];
设置层
use intricate::layers::{
activations::TanH,
Dense
};
let mut layers: Vec<ModelLayer> = vec![
Dense::new(2, 3), // inputs amount, outputs amount
TanH::new (3),
Dense::new(3, 1),
TanH::new (1),
];
创建具有层的模型
use intricate::Model;
// Instantiate our model using the layers
let mut xor_model = Model::new(layers);
我们使模型为 mut
,因为我们将在训练模型时调用 fit
,这将根据需要调整每个层。
设置 OpenCL 的状态
由于 Intricate 在底层使用 OpenCL 进行计算,我们确实需要初始化一个 OpenCLState
,它只是一个包含一些必要 OpenCL 信息的结构体。
use intricate::utils::{
setup_opencl,
DeviceType
}
// you can change this device type to GPU if you want
let opencl_state = setup_opencl(DeviceType::CPU).unwrap();
为了使我们的模型能够实际进行计算,我们需要将 OpenCL 状态传递到模型中的 init
方法,如下所示
xor_model.init(&opencl_state).unwrap();
调整我们的模型
为了训练我们的模型,我们只需调用 fit
方法,并传递一些参数,如下所示
use intricate::{
loss_functions::MeanSquared,
optimizers,
types::{TrainingOptions, TrainingVerbosity},
};
let mut loss = MeanSquared::new(); // the type of loss function that should be used for Intricate
// to determine how bad the Model is
let mut optimizer = optimizers::Basic::new(0.1); // the optimizer tries to use the gradients to optimize the training
// process
// Fit the model however many times we want
xor_model
.fit(
&training_inputs,
&expected_outputs,
&mut TrainingOptions::new(&mut loss, &mut optimizer)
.set_epochs(10000)
.set_batch_size(4) // the size of the mini-batch being used in Intricate's Mini-batch
// Gradient Descent
.should_compute_accuracy(true).unwrap() // if Intricate should compute the accuracy after each
// training step
.should_print_accuracy(true).unwrap() // should print the accuracy after each epoch
// a condition for stopping the training if a min accuracy is reached
.set_halting_condition(HaltingCondition::MinAccuracyReached(0.95)).unwrap()
.should_show_halting_condition_warning(true).unwrap(),
)
.unwrap();
正如你所看到的,创建这些模型非常简单,而且速度非常快。
如何保存和加载模型
对于保存和加载模型,Intricate 使用 savefile crate,这使得保存模型变得非常简单和快速。
保存模型
作为一个例子,让我们尝试保存和加载我们的 XoR 模型。
为此,我们首先需要将模型的所有相关层信息与 OpenCL 的 host
(或只是与 CPU)同步,然后我们需要调用 save_file
方法,如下所示
xor_model.sync_data_from_buffers_to_host().unwrap(); // sends the weights and biases from
// OpenCL buffers to Rust Vec's
save_file("xor-model.bin", 0, &xor_model).unwrap();
加载模型
至于加载我们的 XoR 模型,我们只需调用 save_file 方法的对应方法:load_file
。
let mut loaded_xor_model: Model = load_file("xor-model.bin", 0).unwrap();
当然,savefile crate 无法将数据加载到 GPU 中,因此如果您要在加载模型后使用模型,您必须在 loaded_xor_model
(在 examples/xor.rs 中完成)中调用 init
方法。
待完成事项
- 实现最大池化或其他类型的层;
- 在训练过程中添加一个回调闭包,该闭包在每次完成一个epoch或每个步骤时都会被调用,并提供一些有用的信息;
- 在上面的操作完成后,创建一个示例,该示例使用该函数实时绘制损失,使用类似
textplots
的 crate; - 添加用于文本的嵌入层,例如具有预期词汇量的词袋。
- 添加一种方式以更清晰地显示输入和输出不匹配的错误,甚至可能在编译时显示。
- 添加一种选择应计算哪种类型精度的方法,以避免计算出奇怪且无用的精度。
依赖项
~12–24MB
~388K SLoC