Get Started with Lighter
This guide takes you from installation to running experiments in 15 minutes, using proper project structure from the start.
Installation
The Core Idea
Traditional PyTorch Lightning requires writing training loops:
class MyModule(LightningModule):
def __init__(self):
self.model = Model()
self.criterion = nn.CrossEntropyLoss()
def training_step(self, batch, batch_idx):
x, y = batch
pred = self.model(x)
loss = self.criterion(pred, y)
return loss
def configure_optimizers(self):
return torch.optim.Adam(self.model.parameters(), lr=0.001)
trainer = Trainer(max_epochs=10)
trainer.fit(module, train_loader, val_loader)
Lighter replaces this with configuration:
trainer:
_target_: pytorch_lightning.Trainer
max_epochs: 10
system:
_target_: lighter.System
model:
_target_: MyModel
criterion:
_target_: torch.nn.CrossEntropyLoss
optimizer:
_target_: torch.optim.Adam
params: "$@system::model.parameters()"
lr: 0.001
dataloaders:
train: ...
val: ...
Step 1: Create Your Project
Set up a proper project structure (this will pay off as you add experiments):
Your project structure:
Step 2: Minimal Example
Create experiments/minimal.yaml:
project: . # Import from my_experiments/
trainer:
_target_: pytorch_lightning.Trainer
max_epochs: 3
system:
_target_: lighter.System
model:
_target_: torch.nn.Linear
in_features: 784 # MNIST: 28x28 flattened
out_features: 10 # 10 digits
criterion:
_target_: torch.nn.CrossEntropyLoss
optimizer:
_target_: torch.optim.Adam
params: "$@system::model.parameters()"
lr: 0.001
dataloaders:
train:
_target_: torch.utils.data.DataLoader
batch_size: 64
dataset:
_target_: torchvision.datasets.MNIST
root: ./data
train: true
download: true
transform:
_target_: torchvision.transforms.Compose
transforms:
- _target_: torchvision.transforms.ToTensor
- _target_: torchvision.transforms.Lambda
lambd: "$lambda x: x.view(-1)" # Flatten to 784
Run it:
You just trained a neural network using only YAML configuration.
Step 3: Real Example (CIFAR-10)
Create experiments/cifar10.yaml:
project: .
trainer:
_target_: pytorch_lightning.Trainer
max_epochs: 10
accelerator: auto
system:
_target_: lighter.System
model:
_target_: torchvision.models.resnet18
num_classes: 10
criterion:
_target_: torch.nn.CrossEntropyLoss
optimizer:
_target_: torch.optim.Adam
params: "$@system::model.parameters()"
lr: 0.001
metrics:
train:
- _target_: torchmetrics.Accuracy
task: multiclass
num_classes: 10
val: "%system::metrics::train" # Reuse train metrics
dataloaders:
train:
_target_: torch.utils.data.DataLoader
batch_size: 128
shuffle: true
num_workers: 4
dataset:
_target_: torchvision.datasets.CIFAR10
root: ./data
train: true
download: true
transform:
_target_: torchvision.transforms.Compose
transforms:
- _target_: torchvision.transforms.RandomHorizontalFlip
- _target_: torchvision.transforms.RandomCrop
size: 32
padding: 4
- _target_: torchvision.transforms.ToTensor
- _target_: torchvision.transforms.Normalize
mean: [0.4914, 0.4822, 0.4465]
std: [0.2470, 0.2435, 0.2616]
val:
_target_: torch.utils.data.DataLoader
batch_size: 256
num_workers: 4
dataset:
_target_: torchvision.datasets.CIFAR10
root: ./data
train: false
download: true
transform:
_target_: torchvision.transforms.Compose
transforms:
- _target_: torchvision.transforms.ToTensor
- _target_: torchvision.transforms.Normalize
mean: [0.4914, 0.4822, 0.4465]
std: [0.2470, 0.2435, 0.2616]
Run it:
You now have automatic: - Training and validation loops - Metrics computation and logging - Loss tracking - Checkpointing
Step 4: Add Custom Models
Now here's why we set up a proper project structure. Let's add a custom CNN.
Create models/__init__.py and models/simple_cnn.py:
import torch.nn as nn
class SimpleCNN(nn.Module):
def __init__(self, num_classes=10):
super().__init__()
self.conv1 = nn.Conv2d(3, 32, 3, padding=1)
self.relu = nn.ReLU()
self.pool = nn.MaxPool2d(2)
self.conv2 = nn.Conv2d(32, 64, 3, padding=1)
self.fc = nn.Linear(64 * 8 * 8, num_classes)
def forward(self, x):
x = self.pool(self.relu(self.conv1(x)))
x = self.pool(self.relu(self.conv2(x)))
x = x.view(x.size(0), -1)
return self.fc(x)
Your project now looks like:
my_experiments/
├── __init__.py
├── experiments/
│ ├── minimal.yaml
│ └── cifar10.yaml
├── models/
│ ├── __init__.py
│ └── simple_cnn.py
└── data/ # Created by datasets
Create experiments/custom_model.yaml:
project: . # This imports my_experiments/
trainer:
_target_: pytorch_lightning.Trainer
max_epochs: 10
system:
_target_: lighter.System
model:
_target_: models.simple_cnn.SimpleCNN # Your custom model!
num_classes: 10
criterion:
_target_: torch.nn.CrossEntropyLoss
optimizer:
_target_: torch.optim.Adam
params: "$@system::model.parameters()"
lr: 0.001
dataloaders:
train:
_target_: torch.utils.data.DataLoader
batch_size: 128
shuffle: true
dataset:
_target_: torchvision.datasets.CIFAR10
root: ./data
train: true
download: true
transform:
_target_: torchvision.transforms.ToTensor
Run it:
This is the key insight: By setting up proper structure from the start, adding custom components is natural, not a separate concept to learn.
Understanding the Syntax
Lighter uses Sparkwheel for configuration. Here are the essentials:
_target_: Instantiate a Class
Equivalent to: model = torch.nn.Linear(in_features=784, out_features=10)
Works with any Python class—PyTorch, third-party, or your custom code.
project: Import Custom Modules
This makes models/, datasets/, transforms/ etc. importable via _target_.
$ Evaluate Python Expression
@ Resolved Reference
Gets the instantiated object (after _target_ processing).
% Raw Reference
metrics:
train:
- _target_: torchmetrics.Accuracy
task: multiclass
num_classes: 10
val: "%system::metrics::train" # Gets raw YAML, creates new instance
Gets the unprocessed YAML configuration (before instantiation).
:: Path Notation
Navigate nested config with :: separator—more concise than ["system"]["model"].
Learn More
For complete Sparkwheel documentation including advanced features, see Sparkwheel docs.
CLI Overrides
Change hyperparameters without editing files:
# Change learning rate
lighter fit experiments/cifar10.yaml system::optimizer::lr=0.01
# Train longer
lighter fit experiments/cifar10.yaml trainer::max_epochs=100
# Use multiple GPUs
lighter fit experiments/cifar10.yaml trainer::devices=2
# Combine multiple overrides
lighter fit experiments/cifar10.yaml \
trainer::max_epochs=100 \
system::optimizer::lr=0.001 \
trainer::devices=4
Organizing Multiple Experiments
As your project grows, organize configs by purpose:
my_experiments/
├── __init__.py
├── experiments/
│ ├── baselines/
│ │ ├── resnet18.yaml
│ │ └── resnet50.yaml
│ ├── ablations/
│ │ ├── no_augmentation.yaml
│ │ └── different_optimizer.yaml
│ └── production/
│ └── final_model.yaml
├── models/
│ ├── __init__.py
│ └── simple_cnn.py
└── datasets/ # Add custom datasets here
├── __init__.py
└── my_dataset.py
Merging Configs
Create reusable config components:
project: .
trainer:
_target_: pytorch_lightning.Trainer
accelerator: auto
system:
_target_: lighter.System
criterion:
_target_: torch.nn.CrossEntropyLoss
system:
model:
_target_: torchvision.models.resnet18
num_classes: 10
optimizer:
_target_: torch.optim.Adam
params: "$@system::model.parameters()"
lr: 0.001
Combine them:
Later configs override earlier ones, enabling modular experiment design.
Testing and Prediction
# Test trained model
lighter test experiments/cifar10.yaml args::test::ckpt_path=checkpoints/best.ckpt
# Generate predictions
lighter predict experiments/cifar10.yaml args::predict::ckpt_path=checkpoints/best.ckpt
When You Need Adapters
Your dataset returns a dict but your model needs tensors? Use adapters:
system:
adapters:
train:
batch:
_target_: lighter.adapters.BatchAdapter
input_accessor: "image" # Extract from dict
target_accessor: "label"
Your loss expects (target, pred) instead of (pred, target)? Swap them:
system:
adapters:
train:
criterion:
_target_: lighter.adapters.CriterionAdapter
pred_argument: 1
target_argument: 0
Adapters make Lighter task-agnostic—they connect any data format to any model/loss/metric.
Continue Learning
- Configuration Guide - Complete syntax reference
- Adapters - Handle any data format
- Recipes - Ready-to-use patterns
- Architecture - How Lighter works internally
Quick Reference
# Essential Sparkwheel syntax
project: . # Import custom modules
_target_: module.ClassName # Instantiate class
$expression # Evaluate Python expression
@path::to::object # Resolved reference (instantiated object)
%path::to::config # Raw reference (unprocessed YAML)
path::nested::key # Path notation (navigate config)
::sibling::key # Relative reference (sibling in same section)
=key: # Replace operator (override default merge)
~key: null # Delete entire key
~key::1: null # Delete single list item
~key: [0, 2] # Delete multiple items (batch syntax)
# Lighter CLI commands
lighter fit experiments/config.yaml # Train
lighter validate experiments/config.yaml # Validate
lighter test experiments/config.yaml # Test
lighter predict experiments/config.yaml # Predict
# Override from CLI
lighter fit experiments/cfg.yaml key::path=value
# Merge configs (automatic by default)
lighter fit experiments/base.yaml,experiments/exp.yaml
Project Structure
my_experiments/
├── __init__.py # Make it a module
├── experiments/ # All configs here
│ ├── base.yaml
│ ├── exp1.yaml
│ └── exp2.yaml
├── models/ # Custom models
│ ├── __init__.py
│ └── my_model.py
├── datasets/ # Custom datasets
│ ├── __init__.py
│ └── my_dataset.py
└── transforms/ # Custom transforms
├── __init__.py
└── my_transform.py
Getting Help
- Stuck? Troubleshooting Guide
- Questions? FAQ
- Coming from Lightning? Migration Guide
- Community? Discord
Complete Example
See full examples with this structure in the repository's projects directory.