Tutorial 4: Data and Optimizers¶
Torchlight's data module gives you Dataset, TensorDataset, and
DataLoader — all implemented in pure Python/numpy on top of the Tensor API.
Dataset¶
The abstract base class: implement __len__ and __getitem__(idx).
from torchlight.data import Dataset
class MyData(Dataset):
def __init__(self, X, y):
self.X = tl.tensor(X)
self.y = tl.tensor(y)
def __len__(self):
return self.X.shape[0]
def __getitem__(self, idx):
return self.X[idx], self.y[idx]
TensorDataset¶
A ready-made dataset from parallel arrays:
from torchlight.data import TensorDataset
X = tl.tensor([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]])
y = tl.tensor([0.0, 1.0, 1.0])
ds = TensorDataset(X, y)
len(ds) # 3
ds[0] # (tensor([1., 2.]), tensor([0.])) — single element as tensors
ds.to_batch() # (X, y) as full tensors — handy for small datasets
DataLoader¶
Wraps a dataset with batching, shuffling, and dropping:
from torchlight.data import DataLoader
loader = DataLoader(ds, batch_size=2, shuffle=True, drop_last=False)
for batch_x, batch_y in loader:
print(batch_x.shape, batch_y.shape) # (2, 2) (2,) for full batch; (1, 2) for last
Key arguments:
| Parameter | Default | Meaning |
|---|---|---|
batch_size |
1 | elements per batch |
shuffle |
False |
randomise order each epoch |
drop_last |
False |
discard last incomplete batch |
seed |
None |
RNG seed for reproducibility |
Synthetic datasets¶
Pre-built problems for quick experiments:
from torchlight.data import make_synthetic
for name in ["simple", "diag", "split", "xor", "circle", "spiral"]:
ds = make_synthetic(name, n=200)
x, y = ds.to_batch()
print(name, x.shape, y.shape)
Optimizers¶
All optimizers follow the same interface: zero_grad() → forward →
loss.backward() → step().
SGD¶
Basic stochastic gradient descent (with optional momentum):
from torchlight.optim import SGD
opt = SGD(model.parameters(), lr=0.1) # vanilla
opt = SGD(model.parameters(), lr=0.1, momentum=0.9) # with momentum
opt = SGD(model.parameters(), lr=0.1, momentum=0.9, nesterov=True) # Nesterov
Adam¶
Adaptive learning rate with bias correction:
from torchlight.optim import Adam
opt = Adam(model.parameters(), lr=0.001) # default betas
opt = Adam(model.parameters(), lr=0.001, weight_decay=1e-4) # with L2 decay
AdamW¶
Decoupled weight decay (Loshchilov & Hutter 2019) — recommended for larger models:
from torchlight.optim import AdamW
opt = AdamW(model.parameters(), lr=0.001, weight_decay=0.01)
Full training example¶
import torchlight as tl
from torchlight.nn import Sequential, Linear, ReLU
from torchlight.nn.functional import cross_entropy
from torchlight.optim import Adam
from torchlight.data import make_synthetic, DataLoader
train_ds = make_synthetic("xor", n=200)
loader = DataLoader(train_ds, batch_size=32, shuffle=True)
model = Sequential(Linear(2, 32), ReLU(), Linear(32, 2))
opt = Adam(model.parameters(), lr=0.01)
for epoch in range(50):
epoch_loss = 0.0
for bx, by in loader:
opt.zero_grad()
loss = cross_entropy(model(bx), by)
loss.backward()
opt.step()
epoch_loss += float(loss.to_numpy().ravel()[0])
if epoch % 10 == 0:
print(f"epoch {epoch}: loss = {epoch_loss / len(loader):.4f}")
Next¶
Learn about swapping hardware with Backends.