A Rust ML-ökoszisztéma évek óta azzal küzd, hogy nincs egy olyan letisztult, éretten használható tenzorkönyvtára, amire ráépülhetne egy egészséges közösség. A tch-rs (LibTorch binding), a burn és a linfa mind próbálkoztak, de vagy C++ függőséghez kötöttek, vagy más filozófiát követnek. Ebbe a résbe lépett be most a Hugging Face a Candle nevű crate-tel, amelynek 0.1-es verziója pár napja jelent meg és futótűzként terjed a Rust és az ML közösségben egyaránt.

Mi az a Candle és miért fontos hír ez?

A Candle egy minimalista ML keretrendszer Rustban, aminek a fő ígérete a könnyű, szerver-oldali inferencia. A Hugging Face blogbejegyzése és a GitHub repo (huggingface/candle) szerint a motiváció nagyon konkrét: a Python + PyTorch stack production környezetben nehézkes — GIL, nagy Docker image-ek, lassú cold start szerverless környezetben, és a Python runtime általános overhead-je.

A Candle ezzel szemben egyetlen, kis méretű bináris, amit be lehet égetni egy konténerbe vagy akár WASM-be fordítani. Ez azoknak a csapatoknak szól elsősorban, akik már meglévő, betanított modelleket (BERT, Whisper, Llama-szerű architektúrák) szeretnének futtatni gyorsan és megbízhatóan, betanítás helyett fókuszban az inferenciával.

Megjegyzés

A Candle a 0.1.x verzióban még kifejezetten korai állapotban van — az API bármikor változhat, és a dokumentáció is hiányos. Ne cseréld le vele holnap a production PyTorch szolgáltatásod, de érdemes már most kísérletezni vele.

A Hugging Face motivációi a Rust választása mögött

A Hugging Face csapata több blogposztban és GitHub discussion-ben is kifejtette, miért esett a választás Rustra egy Python-központú cég esetében:

  • Memóriabiztonság ownership modellel: a borrow checker garantálja, hogy a tenzor buffer-ek élettartama helyesen legyen kezelve, adatverseny (data race) nélkül — ez különösen fontos, ha később GPU/multi-thread inferenciáról van szó.
  • Egyetlen bináris, minimális futásidejű overhead: nincs szükség Python interpreterre, nincs GIL, a végeredmény egy natív, statikusan linkelhető executable.
  • WASM-kompatibilitás: a Rust toolchain kiváló WebAssembly targetet biztosít, ami böngészőben futó inferenciát is lehetővé tesz.
  • Cargo és a crates.io ökoszisztéma: a függőségkezelés és a build folyamat sokkal reprodukálhatóbb, mint a Python pip/conda világ.

A csapat egyébként nem titkolja, hogy a tch-rs-szel (a LibTorch Rust binding-jával) is dolgoztak korábban, de az FFI réteg és a C++ függőség miatt a deployment sosem volt annyira egyszerű, mint amit egy natív Rust megoldástól elvárnának.

Fő funkciók az első, 0.1-es kiadásban

A Candle jelenleg a következő képességekkel rendelkezik:

  • Tenzor műveletek CPU-n, alapvető lineáris algebrával (matmul, broadcast, reduce operátorok).
  • Kezdeti CUDA support, ami GPU-n is futtatja a legfontosabb műveleteket — bár ez még kísérleti státuszú.
  • Modell betöltés safetensors formátumból, ami a Hugging Face Hub natív, biztonságos súlyformátuma (nem pickle, tehát nincs kódfuttatási kockázat betöltéskor).
  • Néhány előre elkészített modell implementáció, például BERT-szerű enkóderek, amivel gyorsan lehet embeddinget generálni.
  • candle-core és candle-nn külön crate-ekre bontva, hogy a réteg-építőelemek (Linear, LayerNorm, stb.) elkülönüljenek az alap tenzor logikától.

Íme egy egyszerű példa, ami két véletlen tenzort hoz létre és összeszorozza — ez már most lefordul és futtatható a Candle 0.1-gyel:

use candle_core::{Device, Tensor};

fn main() -> anyhow::Result<()> {
    let device = Device::Cpu;

    let a = Tensor::randn(0f32, 1f32, (3, 4), &device)?;
    let b = Tensor::randn(0f32, 1f32, (4, 5), &device)?;

    let c = a.matmul(&b)?;

    println!("eredmény shape: {:?}", c.shape());
    println!("{c}");

    Ok(())
}

A Device::Cpu helyett Device::new_cuda(0)?-t is megadhatnál, ha van elérhető GPU-d és a cuda feature-rel fordítottad a crate-et — ez már most is elérhető, csak instabil.

Tipp

Ha kipróbálod a Candle-t, érdemes a friss, 1.71-es Rust toolchaint használni. A let-else szintaxis nagyon jól jön a modell betöltő kódban, ahol gyakran kell early-return-ölni hibás/hiányzó tenzor esetén.

use candle_core::Tensor;

fn describe(t: Option<&Tensor>) -> anyhow::Result<()> {
    let Some(tensor) = t else {
        anyhow::bail!("hiányzó tenzor a modellben");
    };

    println!("dtype: {:?}, shape: {:?}", tensor.dtype(), tensor.shape());
    Ok(())
}

A Candle viszonya a PyTorchhoz és más ML keretrendszerekhez

Fontos leszögezni: a Candle nem akarja lecserélni a PyTorchot a kutatásban vagy a betanításban. A PyTorch dinamikus gráfja, a hatalmas ökoszisztémája (optimizerek, adatbetöltők, vizualizációs eszközök) és a Python interaktivitása egyelőre verhetetlen kutatói workflow-hoz.

A Candle célja szűkebb, de nagyon éles: a betanított modell futtatása egyszerű, gyors, alacsony overhead-ű környezetben. Ebben a szegmensben a versenytársak inkább az onnxruntime C API bindingjei, vagy a tch-rs LibTorch wrapper. A Candle előnye, hogy nincs C++ toolchain függősége — tisztán Rust (a CUDA kernel-eket leszámítva), ami drasztikusan egyszerűsíti a build és deployment folyamatot.

Érdemes még megemlíteni a burn crate-et is, amely hasonló célokat követ, de más architektúra mögé áll (backend-agnosztikus tenzor absztrakció, ami akár WGPU-t is használhat). A Candle inkább a Hugging Face Hub modellek gyors, minimalista futtatására fókuszál, kevesebb absztrakciós réteggel.

Jó tudni

Ha production rendszerbe tervezed beépíteni, számolj azzal, hogy a 0.1-es verzió API-ja instabil. A Cargo.lock rögzítése és a pontos verzió pinelése (candle-core = "=0.1.0") most különösen fontos, hogy egy patch release ne törje el a buildedet.

Első lépések: hello world modell futtatása Candle-lel

A leggyorsabb módja a kipróbálásnak, hogy klónozod a repót, és lefuttatod az egyik példát. Egy tipikus Cargo.toml a projekt gyökerében így nézhet ki:

[package]
name = "candle-hello-world"
version = "0.1.0"
edition = "2021"

[dependencies]
candle-core = "0.1"
anyhow = "1"

Ezután a src/main.rs-be bekerülhet egy egyszerű lineáris réteg is, ha a candle-nn crate-et is hozzáadod:

use candle_core::{Device, Tensor};
use candle_nn::{Linear, Module};

fn main() -> anyhow::Result<()> {
    let device = Device::Cpu;

    let weight = Tensor::randn(0f32, 1f32, (5, 3), &device)?;
    let bias = Tensor::randn(0f32, 1f32, (5,), &device)?;
    let layer = Linear::new(weight, Some(bias));

    let input = Tensor::randn(0f32, 1f32, (1, 3), &device)?;
    let output = layer.forward(&input)?;

    println!("kimenet: {output}");
    Ok(())
}

Ha a safetensors formátumú súlyokat szeretnéd betölteni egy Hugging Face Hub repóból, a candle-core::safetensors modul segít a beolvasásban — ez teszi lehetővé, hogy egy már meglévő, PyTorchban betanított BERT modellt közvetlenül Rust-ban futtass, konverzió nélkül.

Összefoglalás

A Candle 0.1 még gyerekcipőben jár, de a hír azért fontos, mert egy komoly, jól finanszírozott szereplő — a Hugging Face — most tette le a Rust melletti voksát az ML inferencia területén. Ha eddig azt gondoltad, hogy a Rust és a gépi tanulás nem fér össze jól, most van itt az idő, hogy átgondold: a memóriabiztonság, a kis binárisok és a natív teljesítmény kombinációja pontosan azt az űrt tölti ki, amit a Python-alapú stack nem tud. Érdemes követni a projekt fejlődését — az elkövetkező hónapokban valószínűleg gyors iterációkat fogunk látni.