# EfficientAD in practice: visual anomaly detection on MVTec AD bottles with Anomalib

> A first run of EfficientAD-S on a partial MVTec AD bottle subset in Google Colab: how the method works, what the numbers are, and what they do not show.

Source: https://beyondpixels.dev/blog/efficientad-bottle-anomaly-detection/ · Published 2026-10-18 · Evidence: measured, published

Interactive instruments in the web version are shown here as their component tags with default parameters.

<Summary title="The short version">

- Visual anomaly detection trains on normal products only, then flags images and image regions that differ from what it learned. It suits inspection tasks where defects are rare or cannot all be labelled in advance.
- EfficientAD, from Batzner, Heckler and König (WACV 2024), pairs a teacher network, a student network and an autoencoder branch. It is designed to be accurate at low inference latency.
- We trained EfficientAD-S for 10 epochs on 209 normal bottle images and tested on 83 images, using Anomalib on an NVIDIA Tesla T4 in Google Colab.
- On that subset: 97.59 % image-level accuracy, 99.92 % image-level AUROC, 97.23 % pixel-level AUROC and 90.15 % AU-PRO at a false-positive-rate limit of 0.30.
- These are preliminary results on a partial subset from a mirror. They are not official benchmark results, they are not directly comparable with published scores, and they do not show that the approach is ready for production.
- No edge device was tested. The latency in the paper was measured on the authors' hardware and does not transfer to yours.

</Summary>

<KeyFinding
  finding="In a first, single run, EfficientAD-S separated normal bottles from defective ones almost perfectly on a small subset: 81 of 83 test images correct, image-level AUROC 99.92 %."
  explanation="The student network learns to reproduce the teacher's features on normal images only. Near a defect it may fail to match, and that disagreement can indicate an anomaly. Localization was useful but imperfect: pixel-level AUROC was 97.23 %, and the maps did not outline every defect."
  evidence="Measured on 83 test images from a partial MVTec AD bottle subset, one run, 10 epochs, Tesla T4. The description of the method and the latency claim are published results of the EfficientAD paper."
/>

<Note kind="limitations" title="Read this first">

- This is a preliminary run on a partial subset of the MVTec AD bottle category, taken from a Hugging Face mirror. It is not the complete official benchmark.
- The numbers are not official benchmark results and should not be compared directly with published full-benchmark scores. Report them as subset results.
- The test set has 83 images, which limits statistical confidence. One run and a small set of visualizations were analysed.
- The latency reported in the EfficientAD paper was measured under the authors' conditions. It should not be assumed for any other GPU, CPU, input resolution or edge device.
- An anomaly score is not a probability. It measures how unusual an input appears.

</Note>

## Why learn from normal images only

Supervised classification learns to separate a set of labelled classes. In industrial inspection, collecting examples of every defect is expensive or impractical, and a factory may meet defects that are rare or have never been seen.

Visual anomaly detection takes another route. It learns what normal products look like, then flags images or image regions that differ. A model trained on normal bottles may flag a broken rim or unexpected contamination at test time.

It gives two outputs, and they answer different questions.

- Image-level detection estimates whether an image as a whole looks anomalous.
- Pixel-level localization produces an anomaly map showing where unusual regions may be.

An anomaly score is not automatically a probability. It is a number for how unusual an input appears, and a decision threshold turns scores into normal or anomalous predictions.

## How EfficientAD works, in plain words

<Evidence kind="published" /> This section summarizes the paper: Kilian Batzner, Lars Heckler and Rebecca König, "EfficientAD: Accurate Visual Anomaly Detection at Millisecond-Level Latencies", WACV 2024, pp. 128–138 ([arXiv:2303.14535](https://arxiv.org/abs/2303.14535)).

The method combines an efficient feature extractor with a student–teacher approach and an autoencoder-based branch that looks at broader image context. It learns only from defect-free training images. At inference time it compares feature representations produced by different parts of the model, and disagreement can indicate an anomaly.

There are three main components.

- **Teacher network.** Provides target visual features. In the paper the teacher is a compact Patch Description Network (PDN), distilled from a larger pretrained network.
- **Student network.** Learns to predict the teacher's features on normal training images. At test time it may fail to match the teacher around anomalous regions.
- **Autoencoder branch.** Learns normal feature patterns and helps find certain global or logical anomalies: unusual combinations or arrangements that may not be visible from a small local patch alone.

The student–teacher comparison picks up local texture or structural deviations. The autoencoder branch adds broader context. The anomaly signals are calibrated and combined into a spatial anomaly map and an image-level score.

### Why efficiency matters

Some anomaly-detection methods need expensive searches through stored feature vectors, or use computationally heavy networks. EfficientAD is designed to cut inference cost with lightweight feature extraction and a compact student–teacher model. That makes it a candidate where inspection has to happen quickly. The authors evaluate it on industrial anomaly-detection datasets and report millisecond-level inference on modern GPU hardware.

That latency was measured under the authors' experimental conditions. It does not carry over automatically to every GPU, CPU, input resolution or edge device, so measure on the hardware you intend to use.

## What we ran

<Evidence kind="measured" /> The goal was to check a practical end-to-end workflow: prepare data, train EfficientAD-S, generate predictions, inspect anomaly maps, and calculate image-level and pixel-level metrics. It used Anomalib's EfficientAD implementation and was not a full reproduction of every setting in the paper.

### Environment

| Component                   | Configuration     |
| --------------------------- | ----------------- |
| Environment                 | Google Colab      |
| GPU                         | NVIDIA Tesla T4   |
| Python                      | 3.13              |
| PyTorch                     | 2.11.0+cu130      |
| Torchvision                 | 0.26.0+cu130      |
| Anomalib                    | 2.7.0             |
| Model                       | EfficientAD-S     |
| Requested training duration | 10 epochs         |
| Training batch size         | 1                 |
| Evaluation batch size       | 4                 |
| Data-loader workers         | 2                 |
| Random seed                 | 42                |

Colab package versions can change. Record the versions you actually get and check compatibility before you try to reproduce the experiment.

### Dataset

The experiment used the `bottle` category from a Hugging Face mirror of MVTec AD. The original download route through the library returned HTTP 404 during this run, so a mirror was used.

| Split or category                          | Images |
| ------------------------------------------ | ------ |
| `train/good`                               | 209    |
| `test/good`                                | 20     |
| `test/broken_large`                        | 20     |
| `test/broken_small`                        | 22     |
| `test/contamination`                       | 21     |
| Total test images                          | 83     |
| Ground-truth masks for defective test images | 63   |

Only normal images were used for training. Defective examples were kept for testing, and the ground-truth masks were used to evaluate localization.

This is a partial subset from a mirror, not the complete official MVTec AD bottle benchmark. Results must be reported as subset results and should not be compared directly with published full-benchmark scores. Check the dataset's current licence and usage conditions before redistributing it or using it commercially.

## Step by step in Google Colab

The steps below document the experiment. Dataset URLs and package dependencies can change, so treat the saved notebook as the source of truth for the original code cells, and check the directory structure before you run anything again. The code blocks here are the cells the tutorial reproduces. Steps 3 and 4 have no code in the tutorial, and we have not added any.

### Step 1: Create a GPU-enabled Colab notebook

Open Google Colab, create a notebook and select Runtime → Change runtime type → GPU. Then verify the runtime.

```python
import torch

print("PyTorch:", torch.__version__)
print("CUDA available:", torch.cuda.is_available())
if torch.cuda.is_available():
    print("GPU:", torch.cuda.get_device_name(0))
```

The experiment ran on an NVIDIA Tesla T4.

### Step 2: Install and verify dependencies

The recorded environment used Anomalib 2.7.0, PyTorch 2.11.0+cu130 and torchvision 0.26.0+cu130. Install compatible dependencies in a fresh runtime, then verify the imports. Colab's preinstalled PyTorch and CUDA combination may differ, so avoid mixing incompatible binary builds.

```python
import torch
import torchvision
import anomalib

print("PyTorch:", torch.__version__)
print("Torchvision:", torchvision.__version__)
print("Anomalib:", anomalib.__version__)
print("CUDA available:", torch.cuda.is_available())
```

### Step 3: Download and inspect the dataset

The experiment used `huggingface_hub.snapshot_download` to get the bottle category from a Hugging Face mirror. The resulting directory had to contain normal training images, test categories and ground-truth masks in a structure that Anomalib's MVTec AD data module accepts.

Simplified expected structure:

```text
bottle/
├── train/
│   └── good/
├── test/
│   ├── good/
│   ├── broken_large/
│   ├── broken_small/
│   └── contamination/
└── ground_truth/
    ├── broken_large/
    ├── broken_small/
    └── contamination/
```

Verify the file counts and the actual layout after downloading, because mirrors can change. The original download route returned HTTP 404 during this experiment. Use the exact source recorded in the notebook and check its licence.

### Step 4: Configure the data module

Anomalib's MVTec AD data module handles image loading, transforms, batching and the train/test split. Configure it for the bottle category and the dataset root you downloaded. The experiment used a training batch size of 1, an evaluation batch size of 4 and two data-loader workers.

The exact constructor arguments depend on the installed Anomalib version and on the path used in the notebook. Use the saved notebook's data-module cell instead of guessing version-specific arguments, and inspect a sample batch before training.

### Step 5: Initialise EfficientAD-S

The model was initialised through Anomalib.

```python
from anomalib.models import EfficientAd

model = EfficientAd(
    model_size="small",
    imagenet_dir="/content/datasets/imagenette",
)
```

The `imagenet_dir` argument points to auxiliary data used by this implementation. During setup, the experiment downloaded pretrained teacher weights and Imagenette data, calculated teacher-feature statistics and validation quantiles, and then went on to training. These details are specific to the implementation and version used.

### Step 6: Train the model

Anomalib's `Engine` coordinates training, validation, checkpointing and prediction. The experiment used one GPU, a maximum of 10 epochs and `/content/results/efficientad_bottle` as the results root.

```python
from anomalib.engine import Engine

engine = Engine(
    max_epochs=10,
    accelerator="gpu",
    devices=1,
    default_root_dir="/content/results/efficientad_bottle",
)

engine.fit(model=model, datamodule=datamodule)
```

Here `datamodule` is the data module configured in Step 4. Use the variable names and arguments from the saved notebook. Training completed after the requested 10 epochs.

### Step 7: Locate and preserve the checkpoint

The checkpoint was saved at:

```text
/content/results/efficientad_bottle/EfficientAd/MVTecAD/bottle/v1/weights/lightning/model.ckpt
```

It was about 73.4 MB. Colab's local storage is temporary, so copy the checkpoint and any important results to persistent storage before you disconnect or reset the runtime.

### Step 8: Run inference

Predictions were generated with the trained checkpoint.

```python
predictions = engine.predict(
    model=model,
    datamodule=datamodule,
    ckpt_path=checkpoint_path,
)
```

`checkpoint_path` points to the saved `model.ckpt`. The call returned 21 batches covering the 83 test images at an evaluation batch size of 4.

The prediction batches included these fields:

- `image_path`: source image location
- `pred_score`: image-level anomaly score
- `pred_label`: predicted normal or anomalous label
- `anomaly_map`: spatial anomaly scores
- `gt_label` and `gt_mask`: ground-truth labels and masks where available

The first batch's anomaly map had shape 4 × 256 × 256. A high-scoring region marks a potentially unusual region, not a confirmed defect.

### Step 9: Inspect anomaly maps

Anomaly maps show where the model believes unusual patterns occur. Compare each map with the original image and the ground-truth mask.

In the first four `broken_large` examples, the model responded around parts of the damaged bottle rims, but the predicted regions did not match every ground-truth defect perfectly. An image can be classified correctly even when its localization is imperfect.

Look at false positives, false negatives, small defects and normal images under varied lighting and positioning, not only a few successful examples.

### Step 10: Calculate evaluation metrics

The experiment calculated image-level accuracy, balanced accuracy, image-level AUROC, pixel-level AUROC and AU-PRO.

- **Accuracy:** the proportion of images classified correctly at the chosen threshold.
- **Balanced accuracy:** the average of sensitivity (the fraction of defective images detected) and specificity (the fraction of normal images correctly accepted).
- **AUROC:** how well the scores rank anomalous examples above normal ones across thresholds. Image-level AUROC uses one score per image. Pixel-level AUROC uses anomaly-map scores and pixel labels.
- **AU-PRO:** the overlap between predicted anomalous regions and ground-truth defect regions, while accounting for false-positive pixels. Here it used a false-positive-rate limit of 0.30.

Anomaly scores are not calibrated probabilities. Accuracy depends on the threshold selected by the model configuration, while AUROC evaluates ranking across thresholds.

## Results

<Evidence kind="measured" /> All numbers in this section are from one run on the 83-image subset described above. They are subset results, not official benchmark results.

### Overall metrics

| Metric                   | Result on 83-image subset |
| ------------------------ | ------------------------- |
| Image-level accuracy     | 97.59 % (81/83)           |
| Balanced accuracy        | 96.67 %                   |
| Image-level AUROC        | 99.92 %                   |
| Pixel-level AUROC        | 97.23 %                   |
| AU-PRO at FPR limit 0.30 | 90.15 %                   |

### Per-category results

| Test category   | Images | Correct | Accuracy | Mean anomaly score |
| --------------- | ------ | ------- | -------- | ------------------ |
| `broken_large`  | 20     | 20      | 100.0 %  | 0.5118             |
| `broken_small`  | 22     | 22      | 100.0 %  | 0.5143             |
| `contamination` | 21     | 20      | 95.2 %   | 0.5822             |
| `good`          | 20     | 19      | 95.0 %   | 0.4983             |
| Total           | 83     | 81      | 97.59 %  | —                  |

Mean anomaly scores are descriptive values, not probabilities.

### Confusion matrix

Rows are actual classes and columns are predicted classes. The class order is normal, anomaly.

| Actual / predicted | Normal | Anomaly |
| ------------------ | ------ | ------- |
| Normal             | 19     | 1       |
| Anomaly            | 1      | 62      |

There was one false positive among the 20 normal images and one false negative among the 63 defective images.

## What the results do and do not show

On this small subset the model ranked normal and defective images well and produced useful localization signals. The anomaly maps did not outline every defect perfectly, which is why image-level detection and pixel-level localization have to be evaluated separately.

These results do not establish production readiness. The test subset is small and may not represent factory conditions. Before anyone makes operational claims, the work needs more representative data, repeatable evaluation, threshold analysis, failure analysis, and testing under realistic lighting, camera and product variation.

## From bottles to packaging

The bottle experiment shows a workflow that could be adapted to packaging inspection. It does not validate packaging performance. A next phase could be:

- Define the packaging type and the defects of interest, such as damaged cartons, torn pouches, visible seal defects or label damage.
- Collect representative images with the intended camera angle, lighting, distance and production conditions.
- Train on normal examples and keep defective examples for validation and testing.
- Evaluate both image-level detection and defect localization, and inspect false alarms and missed defects.
- Choose decision thresholds from the cost of missed defects against the cost of unnecessary rejections.
- Test on the intended edge hardware and measure latency, throughput, memory use and prediction consistency.

EfficientAD can flag visible deviations. It cannot guarantee detection of defects that are invisible in an image. Exact text verification, hidden-leak detection and internal seal validation may need other inspection methods.

## The path to edge deployment

The present experiment ran on a Tesla T4 in Colab. It did not test deployment on a Raspberry Pi or an NVIDIA Jetson device. The proposed plan is:

1. Use the existing trained checkpoint to investigate export, instead of retraining at once on packaging data.
2. Check the export options of the installed Anomalib version and test ONNX export if it is supported.
3. Verify that exported inference gives anomaly scores and maps that are sufficiently consistent with the original model.
4. On the actual target device, measure warm-up and steady-state latency, throughput, memory use and CPU and GPU utilisation.
5. If you use compatible NVIDIA hardware, investigate TensorRT acceleration and verify output consistency after optimization.
6. Repeat the performance and detection tests at the intended image resolution and inspection rate.

A successful export on Colab does not prove that the model will run at the required speed on edge hardware. The target device and the operating constraints have to be known before anyone makes that claim.

## Limitations

- The experiment uses a partial MVTec AD bottle subset, not the complete official benchmark.
- The test set has only 83 images, which limits statistical confidence.
- Training was limited to 10 requested epochs. This was a check of the pipeline, not a hyperparameter study.
- Only one run and a small set of visualizations were analysed.
- No edge device was benchmarked.
- Performance on bottles does not guarantee performance on other products, packaging materials, lighting conditions or defect types.

These limitations should accompany the results whenever they are presented.

## Where this leaves us

The tutorial covered the core workflow: data preparation, training, checkpointing, inference, anomaly-map inspection and metric calculation. Because the dataset was limited and the localization was imperfect, the results should be treated as preliminary.

The next stage is to study failure cases, validate on a more representative dataset, and test export and inference on the actual edge hardware. Only then can the approach be assessed for real packaging inspection.

## References

- Batzner, K., Heckler, L., & König, R. (2024). EfficientAD: Accurate Visual Anomaly Detection at Millisecond-Level Latencies. Proceedings of the IEEE/CVF Winter Conference on Applications of Computer Vision (WACV), 128–138. [arXiv:2303.14535](https://arxiv.org/abs/2303.14535)
- [Open-access paper page (CVF)](https://openaccess.thecvf.com/content/WACV2024/html/Batzner_EfficientAD_Accurate_Visual_Anomaly_Detection_at_Millisecond-Level_Latencies_WACV_2024_paper.html)
- [Anomalib implementation and documentation](https://github.com/open-edge-platform/anomalib)