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.state discovers 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.
PackagePurposev0.7 NuGet availability
ToroTensor alias, scoped { } CE, SafeTensors I/OIncluded
Toro.NNLayers, loss functions, optimizers, metricsIncluded
Toro.GNNGraph neural network layers and batchingIncluded
Toro.VisionImage I/O and transforms (SkiaSharp + Torch)Included
Toro.TextTokenization via Microsoft.ML.TokenizersIncluded
Toro.ModelsShared causal language-model contracts and generationRepository preview
Toro.Models.SmolLm2SmolLM2 architecture, local loader, and KV cacheRepository preview
Toro.Models.DistilGpt2DistilGPT-2 architecture, local loader, and KV cacheRepository preview
Toro.Extensions.AIIChatClient adapter for causal language modelsRepository preview
Toro.HubRevision-pinned Hugging Face downloads and local cachingRepository preview
Toro.MLShared tensor datasets and ML.NET interopRepository preview
Toro.ML.LinearLinear algorithm families, beginning with SDCA regressionRepository preview
Toro.ML.FastTreeFastTree regression and learning-to-rankRepository preview
Toro.ML.LightGbmLightGBM regression and learning-to-rankRepository 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

ExampleDescription
LinearRegressionGradient descent with raw tensors
SimpleTrainingXOR with sequential { } CE
MnistTrainingCNN image classification
MnistCnnCNN with BatchNorm, Dropout
MnistAutoencoderAutoencoder with image output
MnistGanGAN image generation
CharRnnCharacter-level text generation with LSTM
TextClassifierTransformer-based text classification
SimpleGcnGNN node classification with GCNConv
HubSentimentLoad DistilBERT from Hugging Face Hub for sentiment analysis