agentleFS
adxptivedadxptived/Rust-Skills/skills/rust-ml/SKILL.md

rust-ml

Skill1 starsChanged 6 months agoMITView on GitHub

Machine Learning, Data Science, and Tensor Computation in Rust. Use when: - Performing data analysis or data Wrangling with Polars dataframes - Doing numerical computation or matrix math using ndarray - Implementing or running Deep Learning model inference using Burn or Candle - Training models or parsing dataset features in Rust - Building feature pipelines, batch inference services, or online prediction endpoints - Comparing ML frameworks for deployment: ONNX, TFLite, or native Rust crates - Evaluating model performance, drift detection, or A/B experimentation in Rust services

The file

We split this skill into the rules it gives an agent

10 rules. About 0 are common, 6 are distinctive, and 4 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈6

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 4

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Avoid writing manual parsing and filtering loops.

    line 26
  2. Very specific:

    Use ndarray for multidimensional arrays, vector arithmetic, and linear algebra.

    line 45
  3. Very specific:

    Keep model weights out of memory allocations during parsing.

    line 128
  4. Distinctive:

    Avoid silent dtype conversions.

    line 227
  5. Distinctive:

    Test deterministic outputs for fixed seeds/fixtures.

    line 228
  6. Distinctive:

    Prefer columnar operations over loops.

    line 235
  7. Distinctive:

    Use memory maps for large weights.

    line 237
  8. Very specific:

    Use rayon for data-parallel preprocessing.

    line 240
  9. Distinctive:

    When the answer depends on library choice, first identify constraints: latency budget, data size, deployment target, auditability, and team …

    line 276
  10. Distinctive:

    When explaining this topic to a user, start with the invariant, show the safe/default approach, then mention advanced alternatives only …

    line 293

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Quick Navigation

  • references/dataframes.md
  • references/tensors.md

Rust Machine Learning & Data Science Guide

Strategies for high-performance numerical computing and model inference in Rust.

Core Libraries & Rules

1. High-Performance DataFrames: Polars

Avoid writing manual parsing and filtering loops. Use polars for lightning-fast, multi-threaded columnar data operations.

use polars::prelude::*;

fn analyze_data() -> Result<DataFrame, PolarsError> {
    // Read CSV, select columns, filter rows, and execute lazily
    LazyCsvReader::new("data.csv")
        .has_header(true)
        .finish()?
        .select([col("age"), col("salary"), col("department")])
        .filter(col("age").gt(30))
        .groupby([col("department")])
        .agg([col("salary").mean().alias("avg_salary")])
        .collect()
}

2. Matrix & Tensor Math: ndarray

Use ndarray for multidimensional arrays, vector arithmetic, and linear algebra.

use ndarray::{array, Array2, Array, IxDyn};

// Create a 2D matrix
let matrix_a: Array2<f64> = array![
    [1.0, 2.0],
    [3.0, 4.0]
];

let matrix_b: Array2<f64> = array![
    [2.0, 0.0],
    [1.0, 2.0]
];

// Perform matrix multiplication
let result = matrix_a.dot(&matrix_b);

// Dynamic-dimensional tensor for variable shapes
let dynamic: Array<f64, IxDyn> = Array::from_shape_vec(
    IxDyn(&[2, 3, 4]),
    (0..24).map(|x| x as f64).collect(),
)?;

3. Deep Learning Frameworks: Burn vs Candle

  • candle: HuggingFace's minimalist tensor library, optimized for lightweight CPU/GPU model inference (LLMs, Whisper, etc.).
  • burn: Flexible, production-grade deep learning framework supporting both training and inference with type-safe backend abstractions (WGPU, LibTorch, WebGPU).
  • ort: ONNX Runtime bindings for models exported from PyTorch, TensorFlow, or scikit-learn.
  • tract: Inference engine for ONNX and TensorFlow models, optimized for embedded/low-latency.

Framework Selection Guide

FrameworkBest ForGPU SupportTrainingModel Format
CandleInference, LLMsCUDANoSafeTensors, GGML
BurnFull training & inferenceWGPU, LibTorch, VulkanYesNative format, ONNX
ORTONNX model deploymentCUDA, DirectMLNoONNX
TractEmbedded, low-latencyCPU onlyNoONNX, TF
Tch-rsPyTorch bindingsCUDAYesPyTorch checkpoint

Example: Inference using Candle

use candle_core::{Device, Tensor};

fn run_inference() -> candle_core::Result<()> {
    let device = Device::Cpu;
    // Create inputs from slices
    let x = Tensor::new(&[1.0f32, 2.0f32, 3.0f32, 4.0f32], &device)?;
    
    // Perform operations
    let y = x.mul(2.0)?;
    println!("Output: {:?}", y.to_vec1::<f32>()?);
    Ok(())
}

Example: ONNX Inference with ORT

use ort::{Environment, Session, Value, inputs};

fn run_onnx(model_path: &str) -> ort::Result<()> {
    let environment = Environment::builder()
        .with_name("inference_env")
        .build()?;

    let session = Session::builder()?
        .with_model_from_file(model_path)?;

    let input_tensor = Value::from_tensor(
        ndarray::Array::from_shape_vec(
            (1, 3, 224, 224),
            vec![0.0f32; 3 * 224 * 224],
        )?
    )?;

    let outputs = session.run(inputs!["input" => input_tensor]?)?;
    Ok(())
}

4. Efficient Model Loading

Keep model weights out of memory allocations during parsing. Load model weights using Memory Maps (memmap2) to let the OS handle virtual memory paging efficiently.

use memmap2::Mmap;

pub struct ModelWeights {
    data: Mmap,
}

impl ModelWeights {
    pub fn from_file(path: &str) -> Result<Self, Box<dyn std::error::Error>> {
        let file = std::fs::File::open(path)?;
        // SAFETY: file is not modified while mapped; data is read-only.
        let data = unsafe { Mmap::map(&file)? };
        Ok(Self { data })
    }

    pub fn get_slice(&self, offset: usize, len: usize) -> &[u8] {
        &self.data[offset..offset + len]
    }
}

Feature Engineering Pipelines

use polars::prelude::*;

fn build_features(path: &str) -> Result<DataFrame, PolarsError> {
    LazyCsvReader::new(path)
        .has_header(true)
        .finish()?
        .filter(col("label").is_not_null())
        .with_column(
            col("timestamp")
                .str()
                .to_datetime(None, None, lit(""),
                    StrptimeOptions::default())
                .alias("parsed_ts")
        )
        .with_column(
            (col("feature_a") - col("feature_a").mean())
                / col("feature_a").std()
                .alias("feature_a_scaled")
        )
        .select([
            col("feature_a_scaled"),
            col("feature_b"),
            col("label"),
        ])
        .collect()
}

Inference Service Pattern

use std::sync::Arc;

pub struct InferenceService {
    model: Arc<ModelWeights>,
    config: ModelConfig,
}

impl InferenceService {
    pub fn new(model_path: &str, config: ModelConfig) -> Result<Self, Error> {
        let model = ModelWeights::from_file(model_path)?;
        Ok(Self { model: Arc::new(model), config })
    }

    pub async fn predict(&self, input: &[f32]) -> Result<Vec<f32>, Error> {
        // Batch input validation
        if input.len() != self.config.input_size {
            return Err(Error::InvalidInputShape);
        }

        // Run inference (could be spawned on a dedicated thread for CPU-bound work)
        let model = self.model.clone();
        let config = self.config.clone();
        let result = tokio::task::spawn_blocking(move || {
            run_inference_sync(&model, &config, input)
        }).await??;

        Ok(result)
    }
}

Key points:

  • Load model once at startup.
  • Validate model metadata and input schema.
  • Batch requests where latency budget allows.
  • Bound queue size.
  • Record model version in every prediction log.

Numerical Correctness

  • Track shapes at boundaries.
  • Normalize inputs the same way in training and serving.
  • Avoid silent dtype conversions.
  • Test deterministic outputs for fixed seeds/fixtures.
  • Monitor drift after deployment.
  • Compare floating-point results within an epsilon, not for equality.
  • Log input statistics (min, max, mean, NaN count) for early drift detection.

Performance Rules

  • Prefer columnar operations over loops.
  • Reuse tensor buffers when possible.
  • Use memory maps for large weights.
  • Choose CPU/GPU backend based on measured workload.
  • Separate preprocessing latency from model latency.
  • Use rayon for data-parallel preprocessing.
  • Profile with cargo flamegraph to find actual bottlenecks before optimizing.

Anti-Patterns

// Bad: loading a model per request.
async fn handle(req: Request) -> Response {
    let model = load_model("model.bin").await?; // expensive I/O on every call
    predict(model, req).await
}

// Good: load once at startup, share via Arc.
static MODEL: OnceLock<Arc<InferenceService>> = OnceLock::new();
// Bad: serializing the full dataframe to JSON for every response.
let json = serde_json::to_string(&df)?;

// Good: convert only what the client needs.
let summary = df.select(&["id", "score"])?.head(Some(100));
  • Accepting arbitrary tensor shapes from clients.
  • Training/serving preprocessing skew.
  • Logging raw sensitive training data.
  • Using floats for IDs/categories without encoding discipline.
  • Silent NaN propagation through the pipeline.

Review Prompt

For Rust ML, check data schema, preprocessing parity, model loading lifecycle, batch/latency tradeoff, backend choice, and observability for model version and drift.

Deep Review Heuristics

When the answer depends on library choice, first identify constraints: latency budget, data size, deployment target, auditability, and team familiarity. Then recommend the smallest tool that satisfies the constraint instead of the most powerful tool.

Production Readiness Checklist

  • Inputs are validated at boundaries.
  • Errors preserve enough context for debugging.
  • Expensive work is measured or bounded.
  • Examples avoid hidden global state.
  • Security and privacy assumptions are stated.
  • Tests cover edge cases and failure paths.
  • Model version is logged with every prediction.
  • Input/output schemas are versioned and documented.
  • Preprocessing matches training pipeline (test with saved examples).
  • Batch size, queue depth, and timeout are configurable without code change.

Teaching Guidance

When explaining this topic to a user, start with the invariant, show the safe/default approach, then mention advanced alternatives only when the user's constraints justify them.

References

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.