Case study

InspectRT

Reproducible feature-memory runtime for industrial visual inspection

I built InspectRT to make a reduced industrial anomaly-detection baseline inspectable and reproducible. It freezes the method and run artifacts, compares reviewed executions across several devices, and records the numerical differences that remain.

  • Open-source ML systems project
  • 2026-present
  • Active, v0.1.0 released
  • Python
  • PyTorch
  • ONNX Runtime
  • Anomaly detection
  • Reproducibility
  • MVTec AD
  • Benchmarking
  • ML systems

Overview

Project summary

What it is
A public Python package for one frozen, reduced feature-memory anomaly-detection profile.
My role
I built the method contract, CLI, strict run bundles, synthetic retrieval fixture, reviewed runtime results, ONNX feature boundary, documentation and v0.1.0 release.
Core stack
Python 3.11 and 3.12, PyTorch, torchvision, ResNet-50, NumPy, scikit-learn and ONNX Runtime.
Public boundary
The accepted baseline runs cover bottle and leather, one MVTec AD category per run.
Open repository

Why I built it

I wanted a small anomaly-detection runtime whose method and outputs could be inspected precisely. Method changes, hidden defaults, runtime differences and performance measurements are easy to mix together when an experiment leaves only its final metrics behind.

I built InspectRT around one frozen reduced profile, inspectrt_feature_memory_v1. It records the resolved method, environment, sample inventory and intermediate outputs so I can review an execution later without reconstructing those details from memory.

Runtime path

The profile resizes each image directly to 256 x 256, applies ImageNet normalization and extracts layer2 features from a frozen ResNet-50 with IMAGENET1K_V2 weights. Padded 3 x 3 average pooling produces row-major FP32 patch embeddings.

Every nominal train/good patch enters the memory bank. A test image follows the same feature path, then exact chunked top-1 squared-L2 retrieval produces its image score and raw anomaly map. InspectRT reports image AUROC, image AP and pixel AUROC without selecting a threshold.

Runtime path

Nominal and test images through one frozen feature path

The optional ONNX graph covers feature extraction, pooling and patch layout only. Every other stage remains outside it.
Pipeline stageOptional ONNX graph boundary
01

Nominal path

Build the reference memory for one category.

  1. train/good images

    Ordered nominal images from one MVTec AD category.

  2. Preprocessing

    Decode, direct resize to 256 x 256 and ImageNet normalization.

  3. Optional ONNX graph
    1. Frozen ResNet-50 layer2

      IMAGENET1K_V2 features with a fixed tensor contract.

    2. Pool and patch layout

      Padded 3 x 3 average pooling and row-major FP32 patches.

  4. Complete nominal bank

    Every row-major FP32 patch is retained.

02

Evaluation path

Score one test image through the same frozen feature path.

  1. Test image

    One image from the selected category.

  2. Same preprocessing

    The decode, resize and normalization contract stays fixed.

  3. Optional ONNX graph
    1. Frozen ResNet-50 layer2

      IMAGENET1K_V2 features with a fixed tensor contract.

    2. Pool and patch layout

      Padded 3 x 3 average pooling and row-major FP32 patches.

  4. Exact retrieval

    Chunked top-1 squared-L2 search against the full bank.

  5. Scores and raw map

    Patch distances produce the image score and anomaly map.

  6. Metrics and run artifacts

    Threshold-free metrics and persisted run artifacts.

The deliberate reference-method trade-off

Decision record

Keep the complete nominal bank

Context
Approximate retrieval, coreset selection or unrelated optimizations would change the method while I was trying to compare executions.
Decision
For v0.1.0, I kept every nominal FP32 patch and used exact chunked top-1 squared-L2 retrieval with lower-index handling for exact ties.
Result
The contract stays simple to inspect, but the accepted banks use 418 MiB for bottle and 490 MiB for leather. On the accepted T1000 benchmark, retrieval accounts for most of the measured batch-1 latency.

Reproducibility and run records

Details

What the published runs include

InspectRT keeps the method, intermediate tensors and measurement scope together so each result can be interpreted against the workload that produced it.

Run contract

Seven or eight files

Evaluation writes a strict seven-file bundle. Benchmarking adds benchmark.json, including its methodology and timing record.

Same stack

Exact persisted outputs

A second T1000 execution reproduced the ordered inventory, bank, nearest indices, scores, distances, maps and metrics exactly on the same host and locked stack.

Performance record

Six timing records

The current v2 performance record contains six descriptive timing rows across the T1000 reference and repeat, i7 CPU, RTX 4080 Super under WSL2, and M1 Pro CPU and MPS paths.

Cross-device review

Index differences recorded

Structural gates and reviewed floating-point and metric bounds held. Some nearest-neighbour indices changed, so the four cross-device records retain drift_detected status.

ONNX policy v2

Calibration and holdout

The CPU feature boundary passed in two calibration environments and an independent Ryzen 7 9700X WSL2 CPU holdout.

InspectRT records the numerical differences alongside the workload and run conditions. The full comparisons are in the cross-platform results and ONNX feature checks guides.

ONNX boundary

The optional ONNX artifact stops at the feature boundary. The reviewed direct consumer runs ONNX Runtime 1.28.0 on CPU and requests CPUExecutionProvider as its only active provider.

Generated graph and PyTorch pipeline

Inside the ONNX graphOutside the graphNote
Frozen ResNet-50 through layer2Image decoding, direct resize and ImageNet normalizationPreprocessing supplies the graph input.
Padded 3 x 3 average poolingComplete nominal-bank construction and exact retrievalThe retrieval contract remains in PyTorch.
Row-major FP32 patch embeddingsImage scoring, raw anomaly maps and threshold-free metricsThe graph returns feature tensors only.
Static batch 1, FP32, 256 x 256 inputPackaging or redistribution of generated model bytesGenerated model bytes stay local.

Limits

  • InspectRT implements a reduced feature-memory baseline. It is not an official or paper-compatible PatchCore reproduction.
  • Accepted public baseline runs cover bottle and leather, one category per run. Benchmarking is frozen to the bottle workload.
  • The complete FP32 bank has a substantial memory and retrieval cost. Performance records apply to their named hardware, software stack and run conditions.
  • Cross-device nearest-index identity is not exact, even where structures and reviewed numerical bounds hold.
  • ONNX support ends at the feature boundary, and direct Python APIs remain experimental.
  • InspectRT carries no production or hard-real-time certification and makes no industrial-safety claim. Users obtain MVTec AD and pretrained weights separately.