Skip to main content

Vyntri

Rapid analytic adaptation of pretrained vision representations, CPU-first.

Vyntri adapts pretrained image backbones to your classification task in seconds without gradient training loops. It extracts frozen features, applies FK discriminative whitening with diagonal covariance shrinkage, and solves an analytic ridge classifier in closed form.

from vyntri import Vyntri

model = Vyntri()
model.fit("./dataset")          # folder-per-class, ~1-2 s on a laptop CPU
print(model.evaluate("./test").accuracy)
model.predict("./image.jpg")

Why Vyntri

  • No training loop. Adaptation is linear algebra — milliseconds to a few seconds on CPU, no GPU required.
  • Simple by default, configurable by design.
model = Vyntri()                              # validated defaults

model = Vyntri(                               # controlled experiment
    backbone="mobilenet_v3_small",
    whitening="fk",
    shrinkage="diagonal",
    shrinkage_alpha=0.25,
    regularization=1e-3,
    projection_dim=32,
    seed=42,
)
  • Honest about trade-offs. Vyntri seeks a better accuracy/computation trade-off, not guaranteed maximum accuracy. In the reduced-sample regimes it targets, analytic adaptation beats last-layer fine-tuning in both speed and accuracy (research evidence, below); in regimes where fine-tuning wins, that result is reported, not hidden.

Installation

pip install vyntri

Requires Python ≥ 3.9. Dependencies: numpy, torch, torchvision, pillow. Vyntri is designed for CPU-first use; CUDA can be selected when available, but GPU acceleration is not required. The pretrained backbone weights download on first use.

Quick start

Dataset layout — one folder per class:

dataset/
  cat/
    img1.jpg
    img2.jpg
  dog/
    img1.jpg

Or explicit splits:

dataset/
  train/cat/...
  val/cat/...
  test/cat/...

Fit, evaluate, predict:

from vyntri import Vyntri

model = Vyntri()
model.fit("./dataset")          # single folder -> deterministic 80/20 split
result = model.evaluate("./test")
print(result.accuracy, result.macro_f1, result.confusion_matrix)

model.predict("./cat.jpg")      # -> PredictionResult(label, confidence)
model.predict_batch("./images") # -> BatchPredictionResult (CSV/JSON export)
model.analyze("./dataset")      # cheap dataset inspection, no extraction

model.save("model.vyntri")
model = Vyntri.load("model.vyntri")

fit() prints a concise summary; model.summary() returns the same information with full timing breakdown. Feature extraction is cached on disk (keyed by dataset fingerprint + backbone + preprocessing + dtype) — the second fit of the same data is much faster, and model.clear_cache() resets it. A cache hit/miss is always reported.

Continual learning (v0.2):

model.fit("./initial")            # classes: cat, dog
result = model.update("./new_data")  # adds dog examples + a new class: bird
print(result.new_classes)         # -> ["bird"]

update() maintains sufficient statistics (X^T X, per-class sums/counts) in the raw feature space and re-solves the FK projection and ridge classifier from them — old raw features are never retained. The result matches a joint refit over all data (measured, V3 Stage 7); updates are order-invariant and handle mixed old/new class batches. It does not claim "zero forgetting": per-task accuracy can shift because the joint solution legitimately re-allocates boundaries as classes arrive. Memory note: the statistics are O(d²), so they beat raw features when n >> d and are larger at n < d.

Features

Area What
Data folder-per-class and explicit train/val/test layouts, deterministic hash-based stratified split, tiny-class handling
Backbone MobileNetV3-Small + ResNet18 + ResNet50 (frozen, ImageNet pretrained); registry validated at construction
Whitening FK/discriminative whitening with eigenvalue flooring (float64-stable)
Shrinkage diagonal covariance shrinkage (shrinkage_alpha)
Classifier analytic ridge, float64 solve, condition diagnostics
Evaluation accuracy, macro/weighted F1, balanced accuracy, confusion matrix
Persistence .vyntri zip (JSON + npy) — schema-versioned, no pickle on load
Continual update() via sufficient statistics; stable class registry; sequential == joint (measured)
Config (v0.3) validated advanced controls (whitening/shrinkage/alpha/regularization/projection_dim/dtype/seed/device/cache), describe(), JSON export/import, interaction notes, non-default repr
Fine-tuning (v0.5) optional fine_tune() — linear head / last block / full backbone, validation-based checkpoint selection, full training metadata; kept separate from the analytic path
Reproducibility seed, resolved config stored with the fitted model

Roadmap

Future releases may add user-requested functionality, performance improvements, additional backbones, and additional training workflows. Research-only techniques and experimental algorithms remain in the separate vyntri-research repository unless they prove useful and appropriate for the public API.

Backbones

Backbone Features Params Pretrained weights CPU
mobilenet_v3_small (default) 576 2.5M MobileNet_V3_Small_Weights.IMAGENET1K_V1 ✅ fast
resnet18 512 11.7M ResNet18_Weights.IMAGENET1K_V1 ✅ ~4× slower than MobileNet
resnet50 2048 25.6M ResNet50_Weights.IMAGENET1K_V1 ✅ ~2× slower than ResNet18

All three share one ImageNet preprocessing system (resize-256 → center-crop → normalize) and expose a frozen feature endpoint (classifier/fc replaced with an identity). Switch with Vyntri(backbone="resnet18") or Vyntri(backbone="resnet50"). The registry validates names at construction and embeds the exact pretrained weights in the feature-cache key, so switching backbones never reuses another backbone's cached features.

Fine-tuning (v0.5)

Optional gradient fine-tuning, deliberately separate from the analytic path:

model.fit("./dataset")
result = model.fine_tune("./dataset", scope="last_layer", epochs=3)
print(result.best_validation_accuracy)  # best-on-validation checkpoint
model.evaluate("./test")                # now runs through the fine-tuned model

Scopes are architecture-aware (no module paths hardcoded):

Scope Trains
last_layer new linear head only, backbone frozen (linear probe)
last_block head + last feature block (ResNet layer4 / MobileNet's final block)
full head + the entire backbone

Defaults follow the V3 Stage 9 protocol (Adam, lr=1e-3, weight_decay=1e-4, 3 epochs, batch from config). Validation is used every epoch and the best-on-validation checkpoint is restored — the test set is never touched during fitting. Every run records epochs, gradient steps, trainable parameters, optimizer, learning rate, weight decay, and training / validation / total time. A fine-tuned model persists through save()/load() (weights stored as plain numpy in the no-pickle archive).

Honest trade-off: fine-tuning replaces the analytic classifier, needs enough gradient steps to converge (more than 3 epochs on small datasets), and on reduced-sample data the analytic path is typically both faster and more accurate (V3 head-to-head). Analytic update() is unavailable after fine_tune() until you re-fit().

Configuration

All defaults are documented with provenance:

  • backbone="mobilenet_v3_small" — chosen for the ordinary-laptop classroom use case (V3 research environment: CPU-only).
  • whitening="fk" + shrinkage="diagonal" — the configuration that won the V3 research matrix (fk+diag best in 14/20 dataset × backbone cells).
  • regularization=1e-4 — the V3 baseline lambda; a λ sweep is not yet part of the research archive, so this is a documented baseline, not a claimed optimum.
  • val_fraction=0.2 — deterministic 80/20 train/validation split; V3 used 60/20/20 including a research test fold.

Advanced controls (all validated at construction): whitening, shrinkage, shrinkage_alpha, regularization, projection_dim, dtype, eigenvalue_floor, floor_ratio, device, cache_dir, cache_enabled, batch_size, num_workers, input_size, seed. Every parameter is documented in code with type, valid values, example, interaction notes, and cache implications:

from vyntri import Config

model.config.describe_param("shrinkage_alpha")  # one parameter
Config.describe()                                # all parameters

# JSON export / import round-trips through validation
cfg_json = model.config.to_json()
restored = Config.from_json(cfg_json)

print(model.config)          # shows only non-default values
model.config.interaction_notes()  # e.g. shrinkage is ignored when whitening="none"

The resolved configuration (e.g. the auto-picked projection_dim) is stored with the fitted model and is authoritative for all post-fit operations: mutating model.config after fit() affects only the next fit(), never the interpretation of the current model (Master spec §18). model.config.classify_changes(other) reports which pipeline stages a config change would invalidate (extraction / fit / safe).

Research foundation

Vyntri's defaults and design are backed by the experiments in the separate research repository — github.com/AreebShahid07/vyntri-research (V3 protocol, 1,571 result rows): the FK + diagonal-shrinkage + analytic-ridge pipeline, the float32 numerical-stability fixes, the n≈d ridge-collapse regime, and the fine-tuning comparison are all measured there. The public library is a clean rebuild around stable interfaces; research-only algorithms (DS-AL, pooled shrinkage, SLCE, intrinsic-dimension diagnostics, zero-cost proxies) intentionally stay in the research repository.

Limitations

  • Image classification on folder-per-class data only (no detection / segmentation).
  • Three backbones (MobileNetV3-Small, ResNet18, ResNet50); MobileNetV3-Large is not currently planned.
  • CPU timings are single-machine; relative cost ratios are robust, absolute times vary.
  • In regimes the research did not cover (very large per-class sample counts, distribution shift), fine-tuning may be competitive or better — measured, not assumed.

Release notes

  • v0.5 — optional gradient fine-tuning (fine_tune): architecture-aware scopes (linear head / last block / full backbone), Adam protocol from V3 Stage 9, validation-based checkpoint selection, full training metadata, fine-tuned models save/load without pickle.
  • v0.4 — three backbones (MobileNetV3-Small, ResNet18, ResNet50) behind one registry and preprocessing system; backbone names validated at construction; cache keys proven distinct per backbone.
  • v0.3 — advanced configuration layer: validated controls for whitening, shrinkage, alpha, regularization, projection dimension, dtype, seed, device, cache; Config.describe(), JSON export/import, interaction notes, non-default repr; resolved config stored with the model.
  • v0.2 — continual learning: update() via sufficient statistics, stable class registry, mixed old/new classes, sequential == joint (measured).
  • v0.1 — clean core: folder datasets, deterministic split, frozen MobileNetV3-Small features, FK whitening, diagonal shrinkage, analytic ridge, fit/evaluate/predict/predict_batch, save/load, CPU-first.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

vyntri-0.5.0.tar.gz (66.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

vyntri-0.5.0-py3-none-any.whl (57.5 kB view details)

Uploaded Python 3

File details

Details for the file vyntri-0.5.0.tar.gz.

File metadata

  • Download URL: vyntri-0.5.0.tar.gz
  • Upload date:
  • Size: 66.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for vyntri-0.5.0.tar.gz
Algorithm Hash digest
SHA256 d315c951b8e17a7a77787372419280a65a3423468957a4885610a2204836f071
MD5 34b465710e1083f575ea3dee15435cbc
BLAKE2b-256 7e6824b465ede331e05ddaeeb61d733437509550aedc019b21a0fe8fdda97fd9

See more details on using hashes here.

Provenance

The following attestation bundles were made for vyntri-0.5.0.tar.gz:

Publisher: workflow.yml on AreebShahid07/vyntri

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file vyntri-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: vyntri-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 57.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for vyntri-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8c277acd30509683858fab8ff8922f13dfa9ad8595681c15c2e6a2ca2d902c6b
MD5 d4f397ab5a12241153786308ed0f6a9a
BLAKE2b-256 31291aa5dc590123bca97ca8c28984c2ca75a62e47582c239d182403545d8e8c

See more details on using hashes here.

Provenance

The following attestation bundles were made for vyntri-0.5.0-py3-none-any.whl:

Publisher: workflow.yml on AreebShahid07/vyntri

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

1.5.0

2 files

1.4.0

2 files

1.3.2

2 files

1.3.1

2 files

1.2.0

2 files

1.1.5

2 files

1.0.0

2 files

This release

0.5.0 This release

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page