Vyntri
Classify images without training. Extract features from a pretrained backbone, project them analytically, and classify -- all in 4 lines of Python.
from vyntri import Vyntri
from vyntri.data import split
s = split("./my-dataset", train=0.7, test=0.2, seed=42)
model = Vyntri()
model.fit(s)
model.predict("./image.jpg") # Prediction(label=cat, confidence=0.94)
No GPU required. No training loops. Useful defaults require little or no tuning.
Table of Contents
- Why Vyntri?
- Quick Start
- How It Works
- After Fitting
- Configuration
- Custom Backbones
- Fine-Tuning
- API Reference
- Examples
- Installation
Why Vyntri?
| Training-based | Vyntri | |
|---|---|---|
| Time to first prediction | Minutes to hours | ~4 seconds |
| Data needed | Hundreds+ per class | Works with 10-50 per class |
| GPU required | Essentially yes | No |
| Hyperparameters to tune | Dozens | Few (sensible defaults) |
| Overfitting risk | High on small data | Low (analytic, no gradient loops) |
Vyntri replaces the training loop with analytic (closed-form) projection and classification. The result is fast to fit, resistant to overfitting (no gradient loops to memorize noise), and works on small datasets where traditional training fails.
Quick Start
1. Prepare your dataset
Organize images into folders by class name:
my-dataset/
cats/
cat_001.jpg
cat_002.jpg
dogs/
dog_001.jpg
birds/
bird_001.jpg
Recommended: At least 10 images per class for good results.
2. Split, fit, and predict
from vyntri import Vyntri
from vyntri.data import split
# Create an explicit train/test split (no files copied)
s = split("./my-dataset", train=0.7, test=0.2, seed=42)
model = Vyntri()
model.fit(s)
result = model.predict("./test-photo.jpg")
print(result.label) # cats
print(result.confidence) # 0.94
3. Evaluate on the held-out test set
result = model.evaluate(s.test)
print(result.accuracy) # 0.91
print(result.macro_f1) # Per-class F1 (macro-averaged)
print(result.per_class) # Per-class precision/recall/F1
4. Save and reload
model.save("./my-model.vyntri")
model = Vyntri.load("./my-model.vyntri")
model.predict("./new-image.jpg")
How It Works
Image
|
Pretrained backbone (frozen) <- extracts features, no training
|
Analytic projection <- closed-form dim reduction
|
Shrinkage estimation <- stabilizes covariance
|
Analytic Ridge classifier <- closed-form weights
|
Prediction
Every step is analytic -- mathematically derived, not learned through gradient descent:
- Low overfitting risk -- analytic fitting avoids iterative gradient-based optimization
- No GPU needed -- CPU matrix ops are fast enough
- Sensible defaults -- few or no parameters to tune for common workflows
- Designed for determinism -- analytic fitting on fixed data and config produces consistent results
The pretrained backbone provides general visual features. Vyntri never modifies it -- it only learns the projection and classifier on top.
After Fitting
Model info
model.classes_ # ["cats", "dogs", "birds"]
model.class_to_idx_ # {"cats": 0, "dogs": 1, "birds": 2}
model.feature_dim_ # 576 (backbone feature dimension)
model.state # "fitted", "fine_tuned", etc.
Predictions
result = model.predict("./image.jpg")
result.label # cats
result.confidence # 0.94
Batch predictions
results = model.predict_batch(["./img1.jpg", "./img2.jpg", "./img3.jpg"])
for path, label, conf in zip(results.paths, results.labels, results.confidences):
print(f"{label}: {conf:.1%}")
Inspect internals
model.config # Current configuration
model.fitted_config # Config that produced this fit
model.projection_ # Learned projection matrix
model.classifier_ # Learned classifier weights
model.validation_accuracy_
Configuration
from vyntri import Vyntri
from vyntri.data import split
# All parameters can be passed directly as kwargs
model = Vyntri(
backbone="auto",
whitening="fk",
shrinkage="diagonal",
cache_dir="./vyntri-cache",
)
s = split("./dataset", train=0.7, val=0.1, test=0.2, seed=42)
model.fit(s)
Config.describe() # Returns structured docs for all parameters
Key configuration groups
| Group | Options | What they control |
|---|---|---|
| Backbone | backbone | Feature extraction model |
| Projection | whitening, shrinkage, shrinkage_alpha, projection_dim | Dim reduction |
| Classifier | regularization | Ridge classification strength |
| Validation | val_fraction | Legacy only — use split() instead |
| Cache | cache_dir, cache_enabled | Feature caching |
| Execution | batch_size, num_workers, device, dtype | Runtime behavior |
Custom Backbones
from vyntri import Vyntri
from vyntri.data import split
# Use a larger backbone by name
s = split("./dataset", train=0.7, test=0.2, seed=42)
model = Vyntri(backbone="resnet50")
model.fit(s)
Fine-Tuning
For the highest accuracy on your specific domain:
from vyntri.data import split
s = split("./dataset", train=0.7, test=0.2, seed=42)
model.fit(s)
model.fine_tune(str(s.train), epochs=10, lr=1e-3, scope="last_layer")
| Scope | What unfreezes | When to use |
|---|---|---|
| last_layer | Final classification head | Default -- safe, fast |
| last_block | Final feature extraction block | 50+ images/class |
| full | Entire backbone | 1000+ images/class |
Note: Fine-tuning requires a GPU for reasonable speed.
API Reference
| Method | Description |
|---|---|
vyntri.data.split(path, train, test, seed) |
Create explicit train/val/test split |
| Vyntri(**kwargs) | Create model instance |
| model.fit(split_result or path) | Fit on dataset |
| model.predict(image) | Classify single image |
| model.predict_batch(images) | Batch classify |
| model.evaluate(path or FolderDataset) | Evaluate on test set |
| model.save(path) / Vyntri.load(path) | Serialize/deserialize |
| model.fine_tune(dataset, scope, epochs, lr) | Optional fine-tuning |
| model.update(dataset) | Add new data/classes |
| model.analyze(dataset) | Inspect dataset |
| model.select_backbone(dataset) | Auto-select backbone |
| model.clear_cache() | Remove feature cache |
| Property | Description |
|---|---|
| model.classes_ | Class names list |
| model.class_to_idx_ | Class to index mapping |
| model.state | Current model state |
| model.config | Current configuration |
| model.fitted_config | Config used for last fit |
| model.feature_dim_ | Backbone feature dimension |
| model.validation_accuracy_ | Validation accuracy from fit |
Examples
Image classification
from vyntri import Vyntri
from vyntri.data import split
s = split("./flowers", train=0.7, test=0.2, seed=42)
model = Vyntri()
model.fit(s)
print(model.classes_) # ["daisy", "rose", "sunflower", "tulip"]
result = model.predict("./test-rose.jpg")
print(result.label, result.confidence)
Incremental learning
from vyntri import Vyntri
from vyntri.data import split
s = split("./dataset-v1", train=0.7, test=0.2, seed=42)
model = Vyntri()
model.fit(s)
model.update("./dataset-v2") # Adds new data without forgetting
Comparing backbones
from vyntri import Vyntri
from vyntri.data import split
s = split("./dataset", train=0.7, test=0.2, seed=42)
for name, bb in [("mobile", "mobilenet_v3_small"), ("resnet", "resnet50")]:
model = Vyntri(backbone=bb)
model.fit(s)
print(name, model.validation_accuracy_)
Installation
pip install vyntri
Requirements: Python 3.9+, PyTorch, torchvision
Optional for fine-tuning: CUDA-capable GPU
Migrating from v1.1.x
v1.2.0 introduces vyntri.data.split() as the recommended way to partition datasets. Passing a folder-per-class path directly to fit() still works but emits a DeprecationWarning.
# Old (deprecated)
model.fit("./my-dataset")
# New (recommended)
from vyntri.data import split
s = split("./my-dataset", train=0.7, test=0.2, seed=42)
model.fit(s)
Vyntri -- classify images without training.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file vyntri-1.2.0.tar.gz.
File metadata
- Download URL: vyntri-1.2.0.tar.gz
- Upload date:
- Size: 104.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
251ef1a7beefdc4158b28ea223090d877b278683cafd69f837303971a3af3fc9
|
|
| MD5 |
87705057be51460e1f4080b157f5585a
|
|
| BLAKE2b-256 |
e711a1fe208af3007ead89c1e1bbffd433596c8726fae99c3e6efd9483adba32
|
Provenance
The following attestation bundles were made for vyntri-1.2.0.tar.gz:
Publisher:
workflow.yml on AreebShahid07/vyntri
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
vyntri-1.2.0.tar.gz -
Subject digest:
251ef1a7beefdc4158b28ea223090d877b278683cafd69f837303971a3af3fc9 - Sigstore transparency entry: 2534644467
- Sigstore integration time:
-
Permalink:
AreebShahid07/vyntri@2bb4019bd463654c8574c8670ca1dce1c27949f9 -
Branch / Tag:
refs/tags/v1.2.0 - Owner: https://github.com/AreebShahid07
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
workflow.yml@2bb4019bd463654c8574c8670ca1dce1c27949f9 -
Trigger Event:
push
-
Statement type:
File details
Details for the file vyntri-1.2.0-py3-none-any.whl.
File metadata
- Download URL: vyntri-1.2.0-py3-none-any.whl
- Upload date:
- Size: 81.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
73a944e2035395d47e5a3e64ce987ce9cda09c7289d8bc0c1e192314d732e044
|
|
| MD5 |
7cff6c79d48959d86dae3ca5124b3e07
|
|
| BLAKE2b-256 |
555e48684edafd3a35b0da9cf6e961584ed3e007a42a9f78a30d9a4ef63195d6
|
Provenance
The following attestation bundles were made for vyntri-1.2.0-py3-none-any.whl:
Publisher:
workflow.yml on AreebShahid07/vyntri
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
vyntri-1.2.0-py3-none-any.whl -
Subject digest:
73a944e2035395d47e5a3e64ce987ce9cda09c7289d8bc0c1e192314d732e044 - Sigstore transparency entry: 2534644862
- Sigstore integration time:
-
Permalink:
AreebShahid07/vyntri@2bb4019bd463654c8574c8670ca1dce1c27949f9 -
Branch / Tag:
refs/tags/v1.2.0 - Owner: https://github.com/AreebShahid07
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
workflow.yml@2bb4019bd463654c8574c8670ca1dce1c27949f9 -
Trigger Event:
push
-
Statement type: