Metadata-Version: 2.4
Name: vyntri
Version: 0.5.0
Summary: Rapid analytic adaptation of pretrained vision representations, CPU-first.
Author: Vyntri Contributors
License-Expression: MIT
Project-URL: Homepage, https://github.com/AreebShahid07/vyntri
Project-URL: Source Code, https://github.com/AreebShahid07/vyntri
Project-URL: Research, https://github.com/AreebShahid07/vyntri-research
Project-URL: Bug Tracker, https://github.com/AreebShahid07/vyntri/issues
Keywords: image-classification,analytic-learning,few-shot,continual-learning,pretrained-features,cpu,machine-learning
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Education
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Scientific/Engineering :: Image Recognition
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.24.0
Requires-Dist: torch>=2.0.0
Requires-Dist: torchvision>=0.15.0
Requires-Dist: pillow>=10.0.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Dynamic: license-file

# 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.

```python
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.**

```python
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

```bash
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:**

```python
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):**

```python
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:

```python
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:

```python
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
