6.2 KiB
6.2 KiB
Project Setup
Best practices for structuring a RustyTorch++ project.
Recommended Project Structure
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
- Architecture Overview - Framework internals
- Performance Tuning - Optimize training
- Deployment - Deploy to production