Getting Started
This guide walks you through installing RUMUS, defining your first neural network, training it, and saving the results. By the end you will have a working multi-layer perceptron trained on synthetic data.
Installation
RUMUS is published on crates.io. Add it to an existing Cargo project with a single command:
"token">-comment"># Add RUMUS to your project
"token">-function">cargo add rumusYour First Model
Models in RUMUS are plain Rust structs annotated with the #[derive(Module)] proc macro. This automatically implements parameter collection, device transfer, and serialization for every field that is itself a module.
Below is a three-layer MLP that takes 784-dimensional inputs (e.g. flattened 28x28 images) and produces 10 class logits. Each hidden layer is followed by a ReLU activation.
use rumus::nn::{type">Module, type">Linear};
use rumus::type">Tensor;
"token-attribute">#[derive(type">Module)]
struct MLP {
fc1: type">Linear,
fc2: type">Linear,
fc3: type">Linear,
}
impl MLP {
fn new() -> type">Self {
type">Self {
fc1: type">Linear::new(784, 256),
fc2: type">Linear::new(256, 128),
fc3: type">Linear::new(128, 10),
}
}
fn forward(&self, x: &type">Tensor) -> type">Tensor {
let x = self.fc1.forward(x).relu();
let x = self.fc2.forward(&x).relu();
self.fc3.forward(&x)
}
}Key Concepts
- Linear is a fully connected layer with learnable weights and bias.
- #[derive(Module)] auto-generates
parameters(),to_device(), and trait implementations so you never write boilerplate. - .relu() is called directly on a Tensor, returning a new Tensor with the computation graph attached for autograd.
Training
Training follows the familiar zero-grad, forward, loss, backward, step loop. RUMUS provides first-class optimizers (SGD, Adam, AdamW) and common loss functions (MSE, cross-entropy) out of the box.
use rumus::nn::{type">Module, type">Linear};
use rumus::optim::type">Adam;
use rumus::loss::cross_entropy_loss;
use rumus::type">Tensor;
fn main() {
"token-comment">// Create dummy training data
let inputs = type">Tensor::randn(&[64, 784]); "token-comment">// batch of 64 images
let targets = type">Tensor::randint(0, 10, &[64]); "token-comment">// 10-class labels
"token-comment">// Instantiate the model and optimizer
let model = MLP::new();
let optimizer = type">Adam::new(model.parameters(), 1e-3);
"token-comment">// Training loop
for epoch in 0..100 {
"token-comment">// Forward pass
let predictions = model.forward(&inputs);
let loss = cross_entropy_loss(&predictions, &targets);
"token-comment">// Backward pass
optimizer.zero_grad();
loss.backward();
optimizer.step();
if epoch % 10 == 0 {
println!("Epoch {epoch}: loss = {:.4}", loss.item());
}
}
}What is happening here?
- Data creation -- we generate random inputs and integer labels. In a real project you would load a dataset.
- Optimizer --
Adam::newtakes the model's parameters and a learning rate. - Forward pass -- the model produces predictions, and
cross_entropy_losscomputes a scalar loss. - Backward pass --
loss.backward()traverses the computation graph and fills in gradients for every parameter. - Optimizer step --
optimizer.step()updates parameters using the computed gradients.
Saving & Loading
RUMUS uses the safetensors format for model serialization. It is fast, memory-mapped, and safe against arbitrary code execution -- a perfect fit for Rust's safety philosophy.
use rumus::serialization::{save_safetensors, load_safetensors};
"token-comment">// Save model weights to disk
save_safetensors(&model, "model.safetensors")
.expect("Failed to save model");
println!("Model saved successfully.");Loading weights back is just as straightforward. Create a new model instance and populate it from the file:
"token-comment">// Load weights back into a fresh model
let mut model = MLP::new();
load_safetensors(&mut model, "model.safetensors")
.expect("Failed to load model");
println!("Model loaded. Ready for inference.");What's Next?
You have built, trained, and saved your first RUMUS model. Dive deeper into the framework with these guides: