VyntriVyntri
Notebooks

03 — Adaptation & Shrinkage

Compare FK, SLCE, and Ledoit-Wolf methods.

Before the analytic classifier, Vyntri can transform the frozen features. Two knobs control that stage:

  • adaptation — the projection: fk (default), slce (advanced), or none
  • shrinkage — covariance regularization: diagonal (default), ledoit_wolf (data-estimated), or none

Setup

imports
from pathlib import Path
import numpy as np
from PIL import Image
from vyntri import Vyntri

def make_noise(root, classes, per_class, seed=0):
    root = Path(root)
    rng = np.random.default_rng(seed)
    for i, name in enumerate(classes):
        folder = root / name
        folder.mkdir(parents=True, exist_ok=True)
        shift = i * 0.7
        for j in range(per_class):
            img = (rng.normal(shift, 1.0, (32, 32, 3)) * 40 + 128).clip(0, 255)
            Image.fromarray(img.astype(np.uint8)).save(folder / f'{name}_{j:02d}.png')
    return root

DATA = Path('whitening_data')
train = make_noise(DATA / 'train', ['cats', 'dogs', 'birds'], per_class=20, seed=1)
test = make_noise(DATA / 'test', ['cats', 'dogs', 'birds'], per_class=12, seed=99)

Adaptation comparison

Same backbone, same data, same seed — only adaptation changes.

adaptation_compare
from vyntri.data import split
s = split(str(train), train=0.7, val=0.1, test=0.2, seed=42)

def fit_and_report(**config):
    model = Vyntri(seed=42, **config)
    model.fit(train=s.train, val=s.val, progress=False)
    test_acc = model.evaluate(s.test).accuracy
    label = ', '.join(f'{k}={v}' for k, v in config.items())
    print(f'{label:<44} val={model.validation_accuracy_:.3f}  test={test_acc:.3f}')
    return model

for adaptation in ['fk', 'none', 'slce']:
    fit_and_report(adaptation=adaptation, shrinkage='none')

Shrinkage comparison

Now fix adaptation="fk" and vary the shrinkage target.

shrinkage_compare
for shrinkage in ['diagonal', 'ledoit_wolf', 'none']:
    m = fit_and_report(adaptation='fk', shrinkage=shrinkage)
    alpha = getattr(m.projection_, 'shrinkage_alpha_', None)
    if alpha is not None:
        print(f'    -> estimated shrinkage alpha: {alpha:.3f}')