01 — Your First Vyntri Model
Core workflow tutorial covering fit, evaluate, predict, and save/load.
This tutorial covers the core workflow: fit a dataset, evaluate, predict, and save/load.
Setup
pip install vyntrifrom pathlib import Path
import numpy as np
from PIL import Image
from vyntri import Vyntri
from vyntri.data import split1. Create a dataset
Vyntri expects one folder per class. Replace with your own photos — the layout is all Vyntri needs.
def make_solid(root, classes, per_class, seed=0):
colors = {'cats': (200, 90, 40), 'dogs': (40, 180, 70), 'birds': (50, 80, 200)}
root = Path(root)
rng = np.random.default_rng(seed)
for name in classes:
folder = root / name
folder.mkdir(parents=True, exist_ok=True)
for j in range(per_class):
noise = rng.normal(0, 6, (32, 32, 3))
base = np.asarray(colors[name], dtype=np.float64).reshape(1, 1, 3)
img = (base + noise).clip(0, 255).astype(np.uint8)
Image.fromarray(img).save(folder / f'{name}_{j:02d}.png')
return root
DATA = Path('cats_dogs')
train = make_solid(DATA / 'train', ['cats', 'dogs', 'birds'], per_class=12, seed=1)
test = make_solid(DATA / 'test', ['cats', 'dogs', 'birds'], per_class=8, seed=99)
print('train folder:', train)
print('test folder :', test)2. Create split and fit
Use split() to create explicit train/validation partitions. Then fit extracts frozen MobileNetV3-Small features (cached on disk), fits the FK projection, and solves the analytic ridge classifier.
# Create an explicit split from the folder-per-class training data
s = split(str(train), train=0.8, val=0.2, seed=42)
model = Vyntri(seed=42)
model.fit(train=s.train, val=s.val, progress=False)
print()
print(model.summary())3. Evaluate
evaluate() is observational — it never changes the model. It reports accuracy, macro/weighted F1, balanced accuracy, and a confusion matrix.
# Evaluate on the separate test set
from vyntri.data import FolderDataset
test_ds = FolderDataset(str(test))
result = model.evaluate(test_ds)
print(f'accuracy = {result.accuracy:.3f}')
print(f'macro F1 = {result.macro_f1:.3f}')
print(f'weighted F1 = {result.weighted_f1:.3f}')
print(f'balanced acc = {result.balanced_accuracy:.3f}')
print(f'samples = {result.n_samples}')
print('confusion matrix (rows=true, cols=pred):')
print(result.confusion_matrix)4. Predictions
A single image, or a whole folder — filenames are preserved and you can write a CSV.
img = str(train / 'cats' / 'cats_00.png')
pred = model.predict(img)
print(f"{img}: {pred.label} ({pred.confidence:.3f})")batch = model.predict_batch(str(test), out_path='predictions.csv')
print(f'{len(batch.paths)} predictions written to predictions.csv')
for p, label in list(zip(batch.paths, batch.labels))[:3]:
print(f' {Path(p).parent.name:>6} -> {label}')5. Save and load
Predictions after loading match the original model within numerical tolerance.
model.save('model.vyntri')
loaded = Vyntri.load('model.vyntri')
print(loaded.predict(img))