Files
rustytorch/docs/book/src/getting-started/project-setup.md
T
2026-03-04 00:08:42 +00:00

6.2 KiB

Project Setup

Best practices for structuring a RustyTorch++ project.

my-ml-project/
├── Cargo.toml
├── src/
│   ├── main.rs
│   ├── lib.rs
│   ├── models/
│   │   ├── mod.rs
│   │   ├── classifier.rs
│   │   └── transformer.rs
│   ├── data/
│   │   ├── mod.rs
│   │   ├── dataset.rs
│   │   └── transforms.rs
│   ├── training/
│   │   ├── mod.rs
│   │   ├── trainer.rs
│   │   └── optimizer.rs
│   └── utils/
│       ├── mod.rs
│       └── metrics.rs
├── configs/
│   ├── model.toml
│   └── training.toml
├── data/
│   └── .gitkeep
├── checkpoints/
│   └── .gitkeep
└── benches/
    └── model_bench.rs

Cargo.toml Configuration

[package]
name = "my-ml-project"
version = "0.1.0"
edition = "2024"

[dependencies]
# Core ML functionality
rtx-tensor = { version = "1.0", features = ["cuda"] }
rtx-autograd = "1.0"
rtx-nn = "1.0"

# Training utilities
rtx-transformers = "1.0"
rtx-distributed = { version = "1.0", optional = true }

# Production serving
rtx-inference = "1.0"
rtx-serving-api = { version = "1.0", optional = true }

# Data processing
rtx-preprocessing = "1.0"
rtx-data-validation = "1.0"

# Configuration and logging
anyhow = "1.0"
thiserror = "1.0"
tracing = "0.1"
tracing-subscriber = "0.3"
serde = { version = "1.0", features = ["derive"] }
toml = "0.8"

# Async runtime
tokio = { version = "1.0", features = ["full"] }

[dev-dependencies]
criterion = { version = "0.5", features = ["html_reports"] }
proptest = "1.4"
tempfile = "3.0"

[features]
default = ["cuda"]
cuda = ["rtx-tensor/cuda"]
metal = ["rtx-tensor/metal"]
distributed = ["dep:rtx-distributed"]
serving = ["dep:rtx-serving-api"]

[[bench]]
name = "model_bench"
harness = false

[profile.release]
lto = "thin"
codegen-units = 1
panic = "abort"

[profile.dev]
opt-level = 1  # Faster debug builds

Configuration Files

Model Configuration (configs/model.toml)

[model]
name = "my-classifier"
version = "1.0"

[model.architecture]
type = "transformer"
hidden_size = 768
num_layers = 12
num_heads = 12
intermediate_size = 3072
dropout = 0.1

[model.input]
image_size = 224
patch_size = 16
num_channels = 3

[model.output]
num_classes = 1000

Training Configuration (configs/training.toml)

[training]
epochs = 100
batch_size = 64
gradient_accumulation_steps = 4

[training.optimizer]
type = "adamw"
learning_rate = 1e-4
weight_decay = 0.01
beta1 = 0.9
beta2 = 0.999
epsilon = 1e-8

[training.scheduler]
type = "cosine"
warmup_steps = 1000
min_lr = 1e-6

[training.checkpoint]
save_every = 10
keep_last = 5
path = "./checkpoints"

[training.logging]
log_every = 100
tensorboard = true

Loading Configuration

use serde::Deserialize;
use std::fs;

#[derive(Debug, Deserialize)]
pub struct ModelConfig {
    pub model: ModelSettings,
}

#[derive(Debug, Deserialize)]
pub struct ModelSettings {
    pub name: String,
    pub version: String,
    pub architecture: ArchitectureConfig,
}

#[derive(Debug, Deserialize)]
pub struct ArchitectureConfig {
    pub r#type: String,
    pub hidden_size: usize,
    pub num_layers: usize,
    pub num_heads: usize,
    pub intermediate_size: usize,
    pub dropout: f32,
}

impl ModelConfig {
    pub fn load(path: &str) -> anyhow::Result<Self> {
        let content = fs::read_to_string(path)?;
        let config: ModelConfig = toml::from_str(&content)?;
        Ok(config)
    }
}

Logging Setup

use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};

pub fn setup_logging() {
    let filter = EnvFilter::try_from_default_env()
        .unwrap_or_else(|_| EnvFilter::new("info"));

    tracing_subscriber::registry()
        .with(filter)
        .with(tracing_subscriber::fmt::layer())
        .init();
}

Error Handling

use thiserror::Error;

#[derive(Error, Debug)]
pub enum ProjectError {
    #[error("Model error: {0}")]
    Model(#[from] rtx_nn::NNError),

    #[error("Tensor error: {0}")]
    Tensor(#[from] rtx_tensor::TensorError),

    #[error("Configuration error: {0}")]
    Config(String),

    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),

    #[error("Data loading error: {0}")]
    DataLoading(String),
}

pub type Result<T> = std::result::Result<T, ProjectError>;

Main Entry Point

mod models;
mod data;
mod training;
mod utils;

use anyhow::Result;
use clap::Parser;

#[derive(Parser)]
#[command(name = "my-ml-project")]
#[command(about = "Train and serve ML models")]
struct Cli {
    #[command(subcommand)]
    command: Command,
}

#[derive(clap::Subcommand)]
enum Command {
    /// Train a model
    Train {
        #[arg(short, long, default_value = "configs/training.toml")]
        config: String,
    },
    /// Run inference
    Infer {
        #[arg(short, long)]
        model: String,
        #[arg(short, long)]
        input: String,
    },
    /// Start serving API
    Serve {
        #[arg(short, long, default_value = "8080")]
        port: u16,
    },
}

#[tokio::main]
async fn main() -> Result<()> {
    utils::setup_logging();

    let cli = Cli::parse();

    match cli.command {
        Command::Train { config } => {
            training::run_training(&config).await?;
        }
        Command::Infer { model, input } => {
            let result = models::run_inference(&model, &input)?;
            println!("Result: {:?}", result);
        }
        Command::Serve { port } => {
            // Requires "serving" feature
            #[cfg(feature = "serving")]
            {
                use rtx_serving_api::Server;
                Server::new(port).run().await?;
            }
            #[cfg(not(feature = "serving"))]
            {
                eprintln!("Serving feature not enabled. Rebuild with --features serving");
            }
        }
    }

    Ok(())
}

Next Steps