Toro
PyTorch semantics, idiomatic F#. Powered by TorchSharp.
Toro wraps TorchSharp with an F#-idiomatic API. It keeps PyTorch's tensor semantics while using records, computation expressions, and function composition.
- Direct API -- operations throw on failure for concise method chaining, just like PyTorch.
- Records as models -- models are plain F# records. Built-in layers mark trainable state explicitly, and
Model.statediscovers it without a base class. - Ownership management --
scoped { }CE automatically disposes intermediate tensors. Return values are kept alive past the scope. - Computation expressions for composition --
sequential { }composes layers;pipeline { }enables composition for heterogeneous signatures. - PyTorch-compatible surface -- tensor operations, neural network layers, and training utilities mirror PyTorch naming and behavior.
| Package | Purpose | v0.7 NuGet availability |
|---|---|---|
Toro | Tensor alias, scoped { } CE, SafeTensors I/O | Included |
Toro.NN | Layers, loss functions, optimizers, metrics | Included |
Toro.GNN | Graph neural network layers and batching | Included |
Toro.Vision | Image I/O and transforms (SkiaSharp + Torch) | Included |
Toro.Text | Tokenization via Microsoft.ML.Tokenizers | Included |
Toro.Models | Shared causal language-model contracts and generation | Repository preview |
Toro.Models.SmolLm2 | SmolLM2 architecture, local loader, and KV cache | Repository preview |
Toro.Models.DistilGpt2 | DistilGPT-2 architecture, local loader, and KV cache | Repository preview |
Toro.Extensions.AI | IChatClient adapter for causal language models | Repository preview |
Toro.Hub | Revision-pinned Hugging Face downloads and local caching | Repository preview |
Toro.ML | Shared tensor datasets and ML.NET interop | Repository preview |
Toro.ML.Linear | Linear algorithm families, beginning with SDCA regression | Repository preview |
Toro.ML.FastTree | FastTree regression and learning-to-rank | Repository preview |
Toro.ML.LightGbm | LightGBM regression and learning-to-rank | Repository preview |
Install
dotnet add package Toro
dotnet add package Toro.NN
dotnet add package TorchSharp-cpu
# Optional domain packages
dotnet add package Toro.GNN # Graph neural networks
dotnet add package Toro.Vision # Image transforms
dotnet add package Toro.Text # Tokenization and text preprocessing
The v0.7.0 NuGet release is limited to the five packages marked as included above. Repository-preview packages can be used from source but are not part of the current NuGet release.
Train an XOR model
open TorchSharp
open Toro
open Toro.NN
let x =
torch.tensor (
array2D [| [| 0f; 0f |]; [| 0f; 1f |]; [| 1f; 0f |]; [| 1f; 1f |] |],
device = torch.CPU
)
let y =
torch.tensor (
array2D [| [| 0f |]; [| 1f |]; [| 1f |]; [| 0f |] |],
device = torch.CPU
)
let l1 = Linear.init 2 16 torch.float32 torch.CPU
let l2 = Linear.init 16 1 torch.float32 torch.CPU
let model = sequential { l1; Relu; l2 }
let state = Model.state model
let opt = AdamW.createWithLr 0.01 (ModelState.trainableParams state)
for epoch in 1..500 do
scoped {
opt.zeroGrad ()
let pred = model.forward x
let loss = Loss.mse pred y
loss.backward ()
opt.step ()
if epoch % 100 = 0 then
printfn "epoch %d loss=%.6f" epoch (loss.ToSingle())
}
Operations throw on failure. scoped { } disposes intermediate tensors at the end of each iteration.
Define a CNN with records
Models are plain F# records. Each record defines a forward member that composes layers with >>:
type Features = {
Conv1: Conv2d; Bn1: BatchNorm; Pool1: MaxPool2d
Conv2: Conv2d; Bn2: BatchNorm; Pool2: MaxPool2d
} with
member this.forward(train: bool) : Tensor -> Tensor =
this.Conv1.forward
>> this.Bn1.forwardT train
>> _.relu()
>> this.Pool1.forward
>> this.Conv2.forward
>> this.Bn2.forwardT train
>> _.relu()
>> this.Pool2.forward
type Classifier = { Fc1: Linear; Drop: Dropout; Fc2: Linear } with
member this.forward(train: bool) : Tensor -> Tensor =
_.flatten(1L, -1L)
>> this.Fc1.forward
>> _.relu()
>> this.Drop.forwardT train
>> this.Fc2.forward
type CnnModel = { Features: Features; Classifier: Classifier } with
member this.forward(train: bool) : Tensor -> Tensor =
this.Features.forward train >> this.Classifier.forward train
Nested records compose naturally. Model.state walks the supported model structure, and ModelState.trainableParams returns canonical named parameters.
Evaluate
let pred =
Toro.noGrad (fun () ->
let p = model.forward x
p.flatten ())
let labels = [| "0 XOR 0"; "0 XOR 1"; "1 XOR 0"; "1 XOR 1" |]
let expected = [| 0f; 1f; 1f; 0f |]
for i in 0..3 do
let v = pred[i].ToSingle()
let ok = if abs (v - expected[i]) < 0.5f then "OK" else "MISS"
printfn " %s = %.3f %s" labels[i] v ok
Examples
| Example | Description |
|---|---|
| LinearRegression | Gradient descent with raw tensors |
| SimpleTraining | XOR with sequential { } CE |
| MnistTraining | CNN image classification |
| MnistCnn | CNN with BatchNorm, Dropout |
| MnistAutoencoder | Autoencoder with image output |
| MnistGan | GAN image generation |
| CharRnn | Character-level text generation with LSTM |
| TextClassifier | Transformer-based text classification |
| SimpleGcn | GNN node classification with GCNConv |
| HubSentiment | Load DistilBERT from Hugging Face Hub for sentiment analysis |