Instructions to use shubhxho/video-benchmark-compact-quality with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- MLX
How to use shubhxho/video-benchmark-compact-quality with MLX:
# Download the model from the Hub pip install huggingface_hub[hf_xet] huggingface-cli download --local-dir video-benchmark-compact-quality shubhxho/video-benchmark-compact-quality
- Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- LM Studio
- Atomic Chat
Distilling an operator-video quality stack into a ~2 MB on-device model
This is the design write-up for video_benchmark.distill β how a heavy, multi-model
video-quality scoring stack is compressed into a single ~2.5 M-parameter network
that reproduces it in one forward pass and ships at ~2 MB on device (candle /
Rust and Apple MLX), while staying honest about what it does and does not learn.
1. The problem
The production pipeline scores first-person / operator video frames (headband cameras, robotics teleop) on several quality axes. The accurate version runs a stack of models per frame:
- Classical OpenCV metrics β brightness, sharpness, blur, exposure anomalies.
- A 3-paradigm learned-IQA ensemble (via
pyiqa): TOPIQ (CNN), MUSIQ (ViT) and CLIP-IQA+ (CLIP) β three different inductive biases voting on perceptual quality. - A MobileCLIP zero-shot scene classifier β is this a usable operator scene?
That is great for an offline batch, but it is several hundred milliseconds per frame and many hundreds of MB of weights β a non-starter for on-device / real-time use.
Goal: one compact model that emits all eight signals at once, fast, small enough to embed, and faithful enough to trust.
2. The teacher β student design
ββββββββββββββββ teacher stack (label generator) ββββββββββββββββ
frame ββ¬ββββΊ OpenCV: brightness Β· sharpness Β· blur Β· anomaly β
βββββΊ pyiqa ensemble: TOPIQ(CNN) + MUSIQ(ViT) + CLIP-IQA+(CLIP) β
βββββΊ MobileCLIP zero-shot scene usability β
ββββββββββββββββββββββββββββΊ 8 per-frame targets (0..100) βββββββ
β (distillation labels)
frame βββΊ frozen tiny backbone βββΊ embedding ββ βΌ
ββββΊ fuse βββΊ residual-MLP trunk βββΊ 8 MLP heads βββΊ 8 scores
frame βββΊ 10 classical descriptors ββββββββββββ (100Β·Ο, bounded 0..100)
The student is CompactQualityNet (model.py):
- Frozen backbone β a
tinypreset MobileNetV3-Small (β2.45 M params). Frozen so the embedding can be precomputed once and every training epoch is a cheap matrix op on the cache (data.py). Presets:tiny(default),micro(LCNet-050, sub-2 MB at every precision),clip(the legacy MobileCLIP-S0, max fidelity). - Descriptor fusion (see Β§4) β the embedding is concatenated with 10 cheap classical descriptors before the trunk.
- Residual-MLP trunk β pre-norm residual blocks (
x + WβΒ·GELU(WβΒ·LN(x))). - Per-target heads β one small 2-layer MLP per signal, so each stays independently calibrated while sharing the trunk.
- Bounded output β
100Β·sigmoid(logit): scores start neutral at 50 and can never go negative or saturate the clamp, which keeps them calibrated.
The architecture (trunk dim, blocks, head dim, extra_dim) is persisted in the
checkpoint, so infer.py rebuilds the exact network even if defaults later change.
3. Training (train.py)
A real mini-batch loop, not a single least-squares step:
- kornia augmentation (
augment.py) β each sampled frame is expanded into several views: a log-uniform Gaussian-blur sweep (perceptual blur is multiplicative, so log-uniform spreads samples evenly across mildβheavy), a mild motion blur (camera shake), plus colour/gamma jitter and sensor noise. View 0 is the untouched original. This multiplies the data and injects spread into otherwise near-flat signals so their distilled correlations become meaningful instead ofn/a. Empirically, this lifts the rank-correlation metrics; aggressive degradations (strong motion blur, JPEG blocking) were measured to regress fidelity on this small corpus and are left out. - SGDR β
CosineAnnealingWarmRestarts: the LR periodically "resurges" to escape plateaus. - EMA β an exponential-moving-average shadow of the trainable weights for a smoother final model (LayerNorm-only trunk, so no BatchNorm running-stat hazard).
- Loss β
SmoothL1(Ξ²=0.1)(Ξ²=1.0 degenerates to plain MSE on [0,1] targets) plus a differentiable Pearson-correlation term, so we optimise the exact fidelity metric (PLCC) the evaluation reports. Per-target weighting counts the deep signals double. - Ship-by-deep-PLCC β on tiny data the EMA is not guaranteed to win, so we keep the best EMA and the best raw weights and ship whichever scores higher held-out deep-PLCC.
accelerateβ the same loop is correct on CPU / Apple MPS / CUDA.
4. The key idea: classical-descriptor fusion (descriptors.py)
A frozen ImageNet backbone is trained to be invariant to exactly what some quality
signals measure: it normalises away absolute exposure, global contrast and colour
cast. So brightness and exposure anomaly are nearly unrecoverable from its
embedding β their distilled correlation collapsed to ~0.
Fix: a hybrid hand-crafted + deep representation. We concatenate a fixed 14-D vector of normalised classical statistics to the embedding:
- Photometric / focus (10): luma mean/std, dark/bright fraction, RMS contrast, colourfulness (HaslerβSΓΌsstrunk), saturation, Laplacian-variance sharpness, Canny edge density, Tenengrad.
- BRISQUE natural-scene statistics (4): on the MSCN field
I = (luma β ΞΌ_local) / (Ο_local + 1), a pristine image's coefficients are ~unit-Gaussian; blur/noise/compression push the variance, excess kurtosis and the horizontal/vertical neighbour-product means off their natural values β a strong, classic no-reference quality cue (Mittal et al., 2012).
All are computed identically at cache-build and inference time with fixed scales (no data-dependent statistics), so the model stays self-contained and reproducible.
Two design choices make this strictly beneficial (model._FusedInput):
- Split projection. The embedding keeps its own projection; the descriptors enter
through a separate projection that is added on. This keeps the big embedding
matrix's row length block-aligned, so it int4/int8 block-quantises in GGUF/MLX
(a single fat
Linearover the concatenation would have an unaligned row and fall back to fp16 β ~4Γ larger; this is exactly the regression that pushed an early build to 2.38 MB). - Zero-initialised. The descriptor projection starts at zero, so fusion is an exact no-op at init and can only help β descriptors are used only insofar as they reduce the loss, never diluting the embedding the deep signals rely on.
Result: best composite scores of any build, clipiqa/anomaly/brightness lifted,
deep-signal PLCC held β at negligible size cost.
5. Evaluation (evaluate.py)
IQA-grade reporting, with rigor appropriate to a small held-out set:
- The standard quad per signal β PLCC (Pearson), SRCC (Spearman), KRCC (Kendall), plus MAE and RMSE (0β100 units).
- VQEG logistic-fitted PLCC / RMSE β the IQA convention (ITU-T P.1401 / VQEG) maps predictions through a monotonic 5-parameter logistic before correlating, so a model isn't penalised for an arbitrary nonlinear score scale when its ranking is right. Reported next to the raw numbers.
- Composite β agreement on the overall quality verdict (mean over signals that genuinely vary), the bottom-line number.
- Deep-signal aggregates β mean over the signals a learned model actually earns
(
iqa, musiq, clipiqa, scene). - BCa bootstrap 95 % CI on the composite PLCC β a single estimate on ~190 frames hides real uncertainty. We use the bias-corrected-and-accelerated bootstrap (Efron, 1987), which corrects the percentile interval for the median-bias and skew a correlation's sampling distribution has near Β±1; it degrades to a percentile bootstrap if BCa can't be formed.
- Robustness sweep β blur frames at increasing Ο and check the student tracks the teacher's degradation (not just degrades arbitrarily), reported as a tracking MAE.
- Fidelity vs Ο β deep-PLCC bucketed by blur severity: a clean frame is easy, heavy blur is the real test.
- Honesty guards β a near-flat teacher signal (std < 5 on 0β100) is reported as
n/a, never a fake correlation. The train/val split is leakage-free by clip.
All of this is rendered to report.md / report.txt and published to the Hub in the
modern .eval_results/ format (PLCC/SRCC/KRCC/MAE/RMSE per signal + composites),
keyed to a companion benchmark dataset.
6. Results (latest run)
Final tiny + fusion run on the operator-video corpus (see report.md for the full
per-signal table with KRCC/RMSE and the blur sweep):
| metric | value |
|---|---|
| Composite PLCC | 0.899 (95 % BCa CI [0.854, 0.925]) |
| Composite PLCC β VQEG logistic-fitted | 0.902 |
| Composite SRCC / KRCC | 0.896 / 0.714 |
| Deep-signal PLCC | 0.69 |
iqa / musiq PLCC |
0.88 / 0.84 |
sharpness / blur PLCC |
0.90 / 0.86 |
| Throughput | ~120Γ the teacher stack |
| Params / int4 weights | 2.45 M / ~1.9 MB |
| int4 GGUF / MLX file | 2.00 MB / 2.40 MB |
7. Deployment (quantize.py, export_hf.py)
Sub-fp16 packing with real, scale-aware size accounting (it models the fp16
fallback for tensors GGUF can't block-quantise, so the reported int4/int8 numbers
match what the files actually weigh β not an optimistic params Γ bytes guess):
- GGUF (candle / Rust) β ggml Q8_0 / Q4_0 + fp16 fallback. Arch + target order + descriptor names ride along as metadata so a candle program rebuilds the forward pass. int4 GGUF β 2.0 MB.
- MLX (Apple Silicon) β grouped affine quant, int8 + int4, at the empirically smallest group size per bit-width (smaller groups qualify more matrices and cut fp16 fallbacks). Verified with an MLX-native quantiseβdequantise round-trip self-test.
- safetensors /
.ptβ the canonical fp16 weightsinfer.pyloads. - LICENSE / NOTICE β backbone-aware: MIT for the trained trunk + heads; the timm MobileNetV3 backbone is Apache-2.0 (apple-amlr only when a MobileCLIP tower is actually bundled); teacher models (BSD pyiqa, apple-amlr MobileCLIP) are used only to generate labels and are not redistributed.
8. Reproduce
uv sync --group distill # gguf + mlx export backends (optional)
uv run python -m video_benchmark.distill --videos videos --epochs 300 \
--preset tiny --quantize int4 --gguf --mlx --fusion
uv run python -m video_benchmark.distill.infer frame.jpg # run the distilled model
uv run pytest tests/test_distill.py tests/test_distill_model.py
9. Honest limitations
sceneis flat on this corpus (only a handful of clips, all valid operator scenes), so its distilled correlation isn/aβ a data limitation, not a model one. More diverse footage would give it real spread.brightness/anomalyare exact OpenCV stats in production; the distilled head is only a unified read-out for them. Fusion lifts them but they are better computed directly.- The Hub benchmark uploads correctly, but HF only renders a live leaderboard
once it allow-lists the
eval.yamlβ and noevaluation_frameworkenum value fits a custom distillation-fidelity eval, so that field is a documented placeholder.
10. References
The design borrows established methods rather than inventing them:
- Knowledge distillation β Hinton, Vinyals & Dean, Distilling the Knowledge in a Neural Network, 2015.
- BRISQUE / MSCN natural-scene statistics β Mittal, Moorthy & Bovik, No-Reference Image Quality Assessment in the Spatial Domain, IEEE TIP 2012.
- VQEG logistic fit for IQA β VQEG, Final Report on the Validation of Objective Models of Video Quality Assessment, 2003; ITU-T Rec. P.1401.
- BCa bootstrap β Efron, Better Bootstrap Confidence Intervals, JASA 1987.
- SGDR warm restarts β Loshchilov & Hutter, SGDR: Stochastic Gradient Descent with Warm Restarts, ICLR 2017.
- Weight averaging (EMA / Polyak) β Polyak & Juditsky, 1992; Izmailov et al., SWA,
- Backbone & teachers β MobileNetV3 (Howard et al., 2019); MobileCLIP (Vasu et al.,
CVPR 2024); the learned-IQA ensemble TOPIQ (Chen et al., 2024), MUSIQ (Ke et al.,
ICCV 2021), CLIP-IQA (Wang et al., AAAI 2023), via
pyiqa. - Colourfulness β Hasler & SΓΌsstrunk, Measuring Colourfulness in Natural Images,
- Tenengrad focus β Krotkov, 1987.
- kornia β Riba et al., Kornia: an Open Source Differentiable Computer Vision Library for PyTorch, WACV 2020.
- GGUF / ggml k-quants (llama.cpp) and MLX (Apple) for the on-device exports.