Overview
The scoring process converts feature distances into interpretable anomaly measures:1
Feature Extraction
Extract patch embeddings from the test image using the same pipeline as training
2
Nearest Neighbor Search
Find the k-nearest neighbors in the memory bank for each test patch
3
Distance Computation
Compute Euclidean distances to nearest neighbors
4
Score Aggregation
Aggregate patch scores into image-level scores and pixel-level masks
NearestNeighbourScorer
The core scoring class that manages the memory bank and computes anomaly scores:common.py
Key Parameters
int
default:"1"
Number of nearest neighbors to consider for scoring. Typical values:
- 1 (default): Uses distance to single nearest neighbor - most common
- 3-5: Averages distances to multiple neighbors - more robust but slower
FaissNN
The nearest neighbor search backend. Options:
FaissNN(on_gpu=False): CPU-based exact searchFaissNN(on_gpu=True): GPU-accelerated exact search (recommended)ApproximateFaissNN(): Approximate search for very large memory banks
FAISS Integration
PatchCore uses Facebook’s FAISS library for efficient similarity search:common.py
Index Creation
common.py
IndexFlatL2 performs exact nearest neighbor search using L2 (Euclidean) distance. This ensures optimal detection quality.Fitting the Memory Bank
common.py
Running Nearest Neighbor Search
common.py
Prediction Pipeline
Thepredict() method of NearestNeighbourScorer computes anomaly scores:
common.py
Score Computation
Single Nearest Neighbor (k=1)
Single Nearest Neighbor (k=1)
For each test patch, the anomaly score is simply the distance to its nearest neighbor:Interpretation: Higher distance = more anomalous (further from normal training patches)
Multiple Nearest Neighbors (k>1)
Multiple Nearest Neighbors (k>1)
When using k>1, scores are averaged:Benefit: More robust to noise, but slightly slower
From Patches to Images
PatchCore computes both patch-level and image-level scores:patchcore.py
Image-Level Scoring
ThePatchMaker.score() method aggregates patch scores using max pooling:
patchcore.py
Segmentation Mask Generation
TheRescaleSegmentor converts patch scores into smooth segmentation masks:
common.py
Segmentation Pipeline
1
Spatial Reshaping
Convert flat patch scores back to 2D grid (e.g., 56×56)
2
Bilinear Upsampling
Interpolate to original image resolution (e.g., 224×224)
3
Gaussian Smoothing
Apply Gaussian filter with σ=4 for smooth boundaries
The smoothing step is crucial for visualization and removes blocky artifacts from patch-based scoring.
Feature Merging
Before scoring, features from multiple layers are concatenated:common.py
When using multiple backbone layers (e.g., layer2 + layer3), their features are flattened and concatenated before distance computation.
ApproximateFaissNN
For very large memory banks (>1M patches), approximate search can be used:common.py
IndexIVFPQ Parameters
int
default:"512"
Number of Voronoi cells for inverted index. More centroids = better accuracy but slower.
int
default:"64"
Number of sub-quantizers for product quantization. Higher = better approximation.
int
default:"8"
Bits per sub-quantizer code. Typically 8 bits (256 values).
Complete Scoring Example
Here’s a complete example of the scoring process:Scoring Metrics
PatchCore is evaluated using several metrics:Image-level AUROC
Image-level AUROC
Area under ROC curve for binary classification (normal vs. anomalous)Target: >99% for MVTec AD
Pixel-level AUROC
Pixel-level AUROC
Area under ROC curve for pixel-wise anomaly localizationTarget: >98% for MVTec AD
PRO Score
PRO Score
Per-Region-Overlap score measuring localization qualityTarget: >95% for MVTec AD
Optimization Tips
Saving and Loading
The scorer can be saved and loaded for deployment:common.py
nnscorer_search_index.faiss: FAISS index containing memory banknnscorer_features.pkl(optional): Raw feature vectorspatchcore_params.pkl: Model configuration
Visualization Example
Next Steps
PatchCore Algorithm
Review the complete algorithm pipeline
Quick Start
Start building your own anomaly detector
