Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

qc-vision-inspector

Binary visual defect detector (good / bad) for industrial quality control, built on the MVTec AD transistor subset.

Results (5-fold CV, 273 good / 40 bad)

Phase Method Mean F1 Mean AUROC Notes
A DINOv2-base linear probe (logReg, Optuna-tuned) ~0.76 ~0.98 Production backbone; cut 0.046 = mean of FAR@5% fold cuts
B PatchCore + WideResNet-50 ~0.60 ~0.89 Beats ResNet-18 bank; FAR-calibrated cuts
C EfficientAD (em size) ~0.77 ~0.97 Best single model; fold 3 reaches F1 1.0
D Fusion: DINOv2 + EfficientAD (stacked LR) ~0.81 ~0.99 Best overall operating point
F Frozen fusion D-alpha + rescue head ~0.82 ~0.99 Rescue hits recall 1.0 on all 5 folds
G Repeated 5×5 CV + inference-only stress ~0.80 ~0.99 Stress: brightness/shift/zoom/crop; honest report at n=40
H Geometric normalization + TTA recall 1.0 — Centroid-aligned data/interim/transistor_norm/; TTA holds recall, FAR varies
E Threshold sweep + per-defect autopsy F1 0.87 — global-max cut: recall 0.925 @ FAR 0.029

DINOv3 variants were evaluated as gated backbones but DINOv2-base (public) won on stability and stayed the production pick.

Production run (SAM3.1, single 80/20 split, 250 train / 63 test)

notebooks/industrial_sam31.py (SAM3.1 sam3.1_multiplex.pt + DINOv2-masked + EfficientAD-medium 5000 steps + alpha fusion, models/transistor_binary/prod_sam31/):

Method AUROC Recall (BAD) FAR Notes
DINO-masked + LogReg 0.9977 1.000 0.109 Best single arm; cut q95 train-GOOD
EfficientAD (original) 0.9773 0.875 0.091 Lowest FAR
Fusion (alpha=0.05) 0.9955 1.000 0.127 EffAD-dominated; McNemar dino-vs-fused p=1.0

SAM runs lazy with unload_sam() after viz (7.2 GB → 0.01 GB VRAM) so DINO/EffAD train uncontended; §8 reloads it transparently for live inference. Demo: GOOD→GOOD, BAD→BAD.

Dataset

data/processed/transistor_binary/ (tracked with Git LFS) is derived from MVTec AD transistor:

  • good/ — 273 images, bad/ — 40 images, plus labels.csv (filename,label,defect,source,original_path)
  • Defect types: bent_lead, cut_lead, damaged_case, misplaced
  • The original MVTec archive (~5 GB) is not versioned; re-derive with your own download (research-only license, see below)

Setup

Requires Python 3.12 and uv. Torch ships with CUDA 12.8 via the pinned pytorch-cu128 index in pyproject.toml — do not remove it.

uv sync
cp .env.example .env   # add HF_TOKEN only if you need gated HF models (DINOv3)
uv run marimo edit --no-token --port 2718 notebooks/fase_a.py   # always from repo root (or `make notebooks`)
uv run mlflow ui       # sqlite:///mlflow.db, experiment "transistor-binary"

Verify GPU: uv run python -c "import torch; print(torch.__version__, torch.cuda.is_available())" → +cu128 True.

Pipeline

Each notebooks/fase_*.py is a marimo notebook (run, don't import). Metrics/CSVs land in reports/, figures in reports/figures/, model files in models/:

Notebook What it does Key outputs
eda.py Exploratory analysis of the binary split reports/figures/mlfigs/
fase_a.py DINOv2/DINOv3 embeddings + Optuna + logistic probe reports/fase_a_fold_metrics*.csv, reports/fase_a_optuna*.csv, models/thresholds_dinov2_folds.json
fase_b.py PatchCore memory banks (R18 / WRN-50) reports/fase_b_fold_metrics_*.csv, models/thresholds_patchcore_*.json
fase_c.py EfficientAD via anomalib (em / es) reports/fase_c_fold_metrics_*.csv, models/thresholds_efficientad_*.json
fase_d.py Score fusion: alpha-blend + stacked LR reports/fase_d_fold_metrics.csv
fase_e.py Operating-point sweep + per-defect autopsy reports/fase_e_sweep.csv, reports/fase_e_autopsia.csv
fase_f.py Frozen D-alpha fusion + rescue head reports/fase_f_fold_metrics.csv, models/thresholds.json
fase_g.py Robustness: repeated 5×5 CV + inference stress reports/fase_g_repeated.csv, reports/fase_g_stress.csv
fase_h.py Geometric norm (data/interim/transistor_norm/) + TTA eval reports/fase_h_repeated.csv, reports/fase_h_tta.csv
industrial_master.py Reusable GOOD/BAD template consolidating the A–H winner (DINOv2 + LogReg + EfficientAD-medium + D-alpha + local q99 rescue). Also exported as industrial_master.ipynb models/<project>/ (cache, OOF, onnx/, config_freeze.json), reports/figures/<project>/
industrial_sam.py SAM3 + DINO-masked + EffAD-original + fusion, ONNX export models/transistor_binary/prod/
industrial_sam31.py SAM3.1 multiplex (ultralytics) variant of the above, lazy VRAM. Also exported as industrial_sam31.ipynb models/transistor_binary/prod_sam31/, reports/figures/transistor_binary_sam31/

Reuse with a new part: point PROJECT_NAME/DATASET_DIR (§1) at dataset/{labels.csv,good/,bad/} — everything else (splits, train-only calibration, namespaced caches, MLflow-by-name) is generic. Current verdict on transistor (repeated 5×5): rescue BAD recall 1.000±0.000 (25/25 folds), FAR 0.097 → below the default pilot bar, see summary cell.

Pilot inference without notebooks: uv run python -m betterclasificator.modeling.train --logreg --bank (materializes models/), then uv run python -m betterclasificator.modeling.predict IMG....

Metrics and thresholds are committed; bulky artifacts (models/*.pt, *.ckpt, results*/, mlruns/, mlflow.db) are git-ignored and regenerated by re-running the notebooks.

Reproducibility

  • uv.lock is committed; uv sync restores the exact env.
  • data/processed/transistor_binary/ + labels.csv are in LFS so folds/splits rebuild identically.
  • Notebook convention: one marimo cell per step; re-running cascades downstream (see AGENTS.md).

Licenses

  • Code: MIT (see LICENSE).
  • Data: MVTec AD is released under CC BY-NC-SA 4.0 — research / non-commercial use only. data/processed/transistor_binary/ inherits that restriction.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages