[guide] Remove ambiguity lib vs. executable (#1649)

This commit is contained in:
Sylvain Benner 2024-04-26 15:42:02 -04:00 committed by GitHub
parent b7ab19ac71
commit 1f8b5d3efb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 169 additions and 84 deletions

View File

@ -5,6 +5,7 @@ authors = [
"Louis Fortier-Dubois",
"Dilshod Tadjibaev",
"Guillaume Lagrange",
"Sylvain Benner",
]
language = "en"
multilingual = false

View File

@ -6,9 +6,17 @@ train a simple convolutional neural network model on the MNIST dataset and prepa
For clarity, we sometimes omit imports in our code snippets. For more details, please refer to the
corresponding code in the `examples/guide` [directory](https://github.com/tracel-ai/burn/tree/main/examples/guide).
We reproduce this example in a step-by-step fashion, from dataset creation to modeling and training
in the following sections. It is recommended to use the capabilities of your IDE or editor to
in the following sections. It is recommended to use the capabilities of your IDE or text editor to
automatically add the missing imports as you add the code snippets to your code.
<div class="warning">
Be sure to checkout the git branch corresponding to the version of Burn you are using to follow
this guide.
The current version of Burn is `0.14` and the corresponding branch to checkout is `main`.
</div>
The code for this demo can be executed from Burn's base directory using the command:
```bash

View File

@ -7,74 +7,21 @@ entrypoint of our program, namely the `main` function defined in `src/main.rs`.
```rust , ignore
use burn::optim::AdamConfig;
use burn::backend::{Autodiff, Wgpu, wgpu::AutoGraphicsApi};
use guide::model::ModelConfig;
use crate::model::ModelConfig;
fn main() {
type MyBackend = Wgpu<AutoGraphicsApi, f32, i32>;
type MyAutodiffBackend = Autodiff<MyBackend>;
let device = burn::backend::wgpu::WgpuDevice::default();
guide::training::train::<MyAutodiffBackend>(
crate::training::train::<MyAutodiffBackend>(
"/tmp/guide",
guide::training::TrainingConfig::new(ModelConfig::new(10, 512), AdamConfig::new()),
crate::training::TrainingConfig::new(ModelConfig::new(10, 512), AdamConfig::new()),
device,
);
}
```
<details>
<summary><strong>🦀 Packages, Crates and Modules</strong></summary>
You might be wondering why we use the `guide` prefix to bring the different modules we just
implemented into scope. Instead of including the code in the current guide in a single file, we
separated it into different files which group related code into _modules_. The `guide` is simply the
name we gave to our _crate_, which contains the different files. Below is a brief explanation of the
different parts of the Rust module system.
A **package** is a bundle of one or more crates that provides a set of functionality. A package
contains a `Cargo.toml` file that describes how to build those crates. Burn is a package.
A **crate** is a compilation unit in Rust. It could be a single file, but it is often easier to
split up crates into multiple _modules_ and possibly multiple files. A crate can come in one of two
forms: a binary crate or a library crate. When compiling a crate, the compiler first looks in the
crate root file (usually `src/lib.rs` for a library crate or `src/main.rs` for a binary crate). Any
module declared in the crate root file will be inserted in the crate for compilation. For this demo
example, we will define a library crate where all the individual modules (model, data, training,
etc.) are listed inside `src/lib.rs` as follows:
```
pub mod data;
pub mod inference;
pub mod model;
pub mod training;
```
A **module** lets us organize code within a crate for readability and easy reuse. Modules also allow
us to control the _privacy_ of items. The `pub` keyword used above, for example, is employed to make
a module publicly available inside the crate.
The entry point of our program is the `main` function, defined in the `examples/guide.rs` file. The
file structure for this example is illustrated below:
```
guide
├── Cargo.toml
├── examples
│ └── guide.rs
└── src
├── data.rs
├── inference.rs
├── lib.rs
├── model.rs
└── training.rs
```
The source for this guide can be found in our
[GitHub repository](https://github.com/tracel-ai/burn/tree/main/examples/guide) which can be used to
run this basic workflow example end-to-end.\
</details><br>
In this example, we use the `Wgpu` backend which is compatible with any operating system and will
use the GPU. For other options, see the Burn README. This backend type takes the graphics api, the
float type and the int type as generic arguments that will be used during the training. By leaving
@ -87,6 +34,12 @@ the model (the number of digit classes is 10 and the hidden dimension is 512), t
configuration which in our case will be the default Adam configuration, and the device which can be
obtained from the backend.
When running the example, we can see the training progression through a basic CLI dashboard:
You can now train your freshly created model with the command:
```console
cargo run --release
```
When running the example, you should see the training progression through a basic CLI dashboard:
<img title="a title" alt="Alt text" src="./training-output.png">

View File

@ -35,3 +35,18 @@ Finally we can init the model with the configuration and the record. For simplic
same batcher used during the training to pass from a MnistItem to a tensor.
By running the infer function, you should see the predictions of your model!
Add the call to `infer` to the `main.rs` file after the `train` function call:
```rust , ignore
crate::inference::infer::<MyBackend>(
artifact_dir,
device,
burn::data::dataset::vision::MnistDataset::test()
.get(42)
.unwrap(),
);
```
The number `42` is the index of the image in the MNIST dataset. You can explore and verify them using
this [MNIST viewer](https://observablehq.com/@davidalber/mnist-viewer).

View File

@ -11,6 +11,7 @@ As [mentioned previously](../getting-started.md#creating-a-burn-application), th
your `guide` project directory with a `Cargo.toml` and a `src/main.rs` file.
In the `Cargo.toml` file, add the `burn` dependency with `train`, `wgpu` and `vision` features.
Then run `cargo build` to build the project and import all the dependencies.
```toml
[package]
@ -158,6 +159,14 @@ There are two major things going on in this code sample.
</details><br>
Note that each time you create a new file in the `src` directory you also need to add explicitly this
module to the `main.rs` file. For instance after creating the `model.rs`, you need to add the following
at the top of the main file:
```rust , ignore
mod model;
```
Next, we need to instantiate the model for training.
```rust , ignore

View File

@ -199,14 +199,86 @@ For the sake of simplicity, the subsequent chapters of this book will all use th
</div>
## Running examples
## Explore examples
In the [next chapter](./basic-workflow) you'll have the opportunity to implement the whole Burn
`guide` example yourself in a step by step manner.
Many additional Burn examples are available in the
[examples](https://github.com/tracel-ai/burn/tree/main/examples) directory. To run an example, please refer
to its README.md for the specific command to execute.
[examples](https://github.com/tracel-ai/burn/tree/main/examples) directory. Burn examples are
organized as library crates with one or more examples that are executable binaries. An example
can then be executed using the following cargo command line in the root of the Burn repository:
```bash
cargo run --example <example name>
```
To learn more about crates and examples, read the Rust section below.
<details>
<summary><strong>🦀 About Rust crates</strong></summary>
Each Burn example is a **package** which are subdirectories of the `examples` directory. A package
is composed of one or more **crates**.
A package is a bundle of one or more crates that provides a set of functionality. A package
contains a `Cargo.toml` file that describes how to build those crates.
A crate is a compilation unit in Rust. It could be a single file, but it is often easier to
split up crates into multiple **modules**.
A module lets us organize code within a crate for readability and easy reuse. Modules also allow
us to control the _privacy_ of items. For instance the `pub(crate)` keyword is employed to make
a module publicly available inside the crate. In the snippet below there are four modules declared,
two of them are public and visible to the users of the crates, one of them is public inside the crate
only and crate users cannot see it, at last one is private when there is no keyword.
These modules can be single files or a directory with a `mod.rs` file inside.
```rust, ignore
pub mod data;
pub mod inference;
pub(crate) mod model;
mod training;
```
A crate can come in one of two forms: a **binary crate** or a **library crate**. When compiling a crate,
the compiler first looks in the crate root file (`src/lib.rs` for a library crate and `src/main.rs`
for a binary crate). Any module declared in the crate root file will be inserted in the crate for
compilation.
All Burn examples are library crates and they can contain one or more executable examples that
uses the library. We even have some Burn examples that uses the library crate of other examples.
The examples are unique files under the `examples` directory. Each file produces an executable file
with the same name. Each example can then be executed with `cargo run --example <executable name>`.
Below is an file tree of a typical Burn example package:
```
examples/burn-example
├── Cargo.toml
├── examples
│ ├── example1.rs
│ ├── example2.rs
│ └── ...
└── src
├── lib.rs
├── module1.rs
├── module2.rs
└── ...
```
</details><br>
For more information on each example, see their respective `README.md` file.
<div class="warning">
Note that some examples use the
[`datasets` library by HuggingFace](https://huggingface.co/docs/datasets/index) to download the
datasets required in the examples. This is a Python library, which means that you will need to
install Python before running these examples. This requirement will be clearly indicated in the
example's README when applicable.
</div>

View File

@ -1,26 +1,16 @@
use burn::{
backend::{wgpu::AutoGraphicsApi, Autodiff, Wgpu},
data::dataset::Dataset,
optim::AdamConfig,
};
use guide::{model::ModelConfig, training::TrainingConfig};
//
// Note: If you are following the Burn Book guide this file can be ignored.
//
// This example file is added only for convenience and consistency so that
// the guide example can be executed like any other examples using:
//
// cargo run --release --example guide
//
use std::process::Command;
fn main() {
type MyBackend = Wgpu<AutoGraphicsApi, f32, i32>;
type MyAutodiffBackend = Autodiff<MyBackend>;
let device = burn::backend::wgpu::WgpuDevice::default();
let artifact_dir = "/tmp/guide";
guide::training::train::<MyAutodiffBackend>(
artifact_dir,
TrainingConfig::new(ModelConfig::new(10, 512), AdamConfig::new()),
device.clone(),
);
guide::inference::infer::<MyBackend>(
artifact_dir,
device,
burn::data::dataset::vision::MnistDataset::test()
.get(42)
.unwrap(),
);
Command::new("cargo")
.args(["run", "--bin", "guide"])
.status()
.expect("guide example should run");
}

View File

@ -1,3 +1,9 @@
//
// Note: If you are following the Burn Book guide this file can be ignored.
//
// This lib.rs file is added only for convenience so that the code in this
// guide can be reused.
//
pub mod data;
pub mod inference;
pub mod model;

View File

@ -0,0 +1,31 @@
mod data;
mod inference;
mod model;
mod training;
use crate::{model::ModelConfig, training::TrainingConfig};
use burn::{
backend::{wgpu::AutoGraphicsApi, Autodiff, Wgpu},
data::dataset::Dataset,
optim::AdamConfig,
};
fn main() {
type MyBackend = Wgpu<AutoGraphicsApi, f32, i32>;
type MyAutodiffBackend = Autodiff<MyBackend>;
let device = burn::backend::wgpu::WgpuDevice::default();
let artifact_dir = "/tmp/guide";
crate::training::train::<MyAutodiffBackend>(
artifact_dir,
TrainingConfig::new(ModelConfig::new(10, 512), AdamConfig::new()),
device.clone(),
);
crate::inference::infer::<MyBackend>(
artifact_dir,
device,
burn::data::dataset::vision::MnistDataset::test()
.get(42)
.unwrap(),
);
}