Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions assets/architecture.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions src/autograd/anomaly.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ pub fn is_detecting() -> bool {
}

/// Run `f` with gradient checking on.
///
/// ```
/// # use fastnn::prelude::*;
/// let w = Param::new(Tensor::from_vec(vec![1.0, 2.0], &[2]));
/// detect_anomaly(|| w.tensor().exp().sum().backward());
/// ```
pub fn detect_anomaly<T>(f: impl FnOnce() -> T) -> T {
let _guard = Enabled::new();
f()
Expand Down
21 changes: 21 additions & 0 deletions src/autograd/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
//! Reverse-mode autodiff. Each differentiable tensor carries a [`Node`] with its
//! backward rule and inputs, so the graph is just the tensors and there is no tape.
//!
//! Weights live in [`Param`](crate::nn::Param) handles. Call `backward()` on a
//! scalar and each handle's gradient slot fills in.
//!
//! ```
//! use fastnn::prelude::*;
//!
//! let w = Param::new(Tensor::from_vec(vec![1.0, 2.0], &[2]));
//! let loss = w.tensor().mul_scalar(3.0).sum();
//! loss.backward();
//! assert_eq!(w.grad().unwrap().to_vec(), vec![3.0, 3.0]);
//! ```
//!
//! - [`no_grad`] runs a closure without building a graph. Use it for inference.
//! - [`detect_anomaly`] panics at the first rule that produces a NaN or inf, naming it.
//! - [`ops`] holds one backward rule per forward op, in files named like `tensor/ops`.
//!
//! A new op is a forward in `tensor/ops`, a rule in [`ops`] that returns gradients
//! in input order and saves only detached tensors, and a case in
//! `tests/gradcheck.rs`, which checks every rule against central finite differences
//! of its own forward.

pub mod anomaly;
pub mod engine;
Expand Down
11 changes: 10 additions & 1 deletion src/cuda/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
//! GPU layer: device setup, memory ([`buffer`]), raw bindings ([`ffi`]) and safe wrappers ([`kernels`]).
//!
//! Without the `cuda` feature, `cuda/stubs.c` stands in for the kernels and [`device_count`] is 0.
//! A training script never calls this module directly. Pick a device with
//! [`Device::best`](crate::tensor::Device::best) or
//! [`Device::cuda`](crate::tensor::Device::cuda) and move the model and the loader
//! to it.
//!
//! Without the `cuda` feature, `cuda/stubs.c` stands in for the kernels and
//! [`device_count`] is 0. With it, `build.rs` compiles `cuda/kernels.cu` with
//! `nvcc`. Matmul goes through cuBLAS. The other kernels use fast math, so GPU
//! results differ from the CPU in the last few digits, and `tests/cuda_parity.rs`
//! checks each one against the CPU path.

pub mod buffer;
pub mod ffi;
Expand Down
30 changes: 30 additions & 0 deletions src/data/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,34 @@
//! Datasets, batching, and MNIST.
//!
//! A [`Dataset`] writes one item into a slice. A [`DataLoader`] fills one buffer
//! per batch from it, shuffles, and can place batches on a device.
//! [`TensorDataset`] wraps two tensors, and [`Mnist`] downloads the digits once
//! and caches them.
//!
//! ```
//! use fastnn::data::{DataLoader, Dataset};
//!
//! struct Squares;
//!
//! impl Dataset for Squares {
//! fn len(&self) -> usize { 1000 }
//! fn input_shape(&self) -> Vec<usize> { vec![1] }
//! fn target_shape(&self) -> Vec<usize> { vec![1] }
//! fn write(&self, i: usize, input: &mut [f32], target: &mut [f32]) {
//! let x = i as f32 / 1000.0;
//! input[0] = x;
//! target[0] = x * x;
//! }
//! }
//!
//! let loader = DataLoader::new(&Squares, 100).shuffle(true);
//! assert_eq!(loader.batches(), 10);
//! for batch in loader.iter() {
//! // A [1] item batches to [batch], like a label vector, not [batch, 1].
//! assert_eq!(batch.inputs.shape(), &[100]);
//! assert_eq!(batch.targets.shape(), &[100]);
//! }
//! ```

pub mod dataset;
pub mod loader;
Expand Down
Loading
Loading