openvinotoolkit / anomalib

An anomaly detection library comprising state-of-the-art algorithms and features such as experiment management, hyper-parameter optimization, and edge inference.
https://anomalib.readthedocs.io/en/latest/
Apache License 2.0
3.64k stars 647 forks source link

[Bug]: Training a model without validation #977

Open hgaiser opened 1 year ago

hgaiser commented 1 year ago

Describe the bug

Most algorithms seem to assume that there is data available for automatically computing the threshold. In my case I only want to train an anomaly model, without validation or evaluation. This is as far as I can tell theoretically possible, however the way some post-epoch values are computed makes this impossible.

Dataset

Folder

Model

PatchCore

Steps to reproduce the behavior

  1. I used main branch (commit ) but I would expect it to be the same on the latest released version.
  2. I made some adjustments to config.yaml:
    1. Set val_split_mode: none.
    2. Set export_mode: onnx.

This will produce the following error:

RuntimeError: cdist only supports at least 2D tensors, X2 got: 1D

Which comes from this line, because self.memory_back is an empty tensor at that point. This is because this never gets called, because there's no validation being performed.

Changing that function to def on_train_epoch_end(self) -> None: resolves the issue, and the process seems to complete successfully.

This change was introduced here. Maybe @samet-akcay can shed some light on this?

OS information

OS information:

Expected behavior

I expect training and exporting to complete, regardless of validation being performed or not.

Screenshots

No response

Pip/GitHub

GitHub

What version/branch did you use?

main

Configuration YAML

dataset:
  name: mvtec
  format: mvtec
  path: ./datasets/MVTec
  task: segmentation
  category: bottle
  train_batch_size: 32
  test_batch_size: 32
  num_workers: 8
  image_size: 256 # dimensions to which images are resized (mandatory)
  center_crop: 224 # dimensions to which images are center-cropped after resizing (optional)
  normalization: imagenet # data distribution to which the images will be normalized: [none, imagenet]
  transform_config:
    train: null
    eval: null
  test_split_mode: from_dir # options: [from_dir, synthetic]
  test_split_ratio: 0.2 # fraction of train images held out testing (usage depends on test_split_mode)
  val_split_mode: none # options: [same_as_test, from_test, synthetic]
  val_split_ratio: 0.5 # fraction of train/test images held out for validation (usage depends on val_split_mode)
  tiling:
    apply: false
    tile_size: null
    stride: null
    remove_border_count: 0
    use_random_tiling: False
    random_tile_count: 16

model:
  name: patchcore
  backbone: wide_resnet50_2
  pre_trained: true
  layers:
    - layer2
    - layer3
  coreset_sampling_ratio: 0.1
  num_neighbors: 9
  normalization_method: min_max # options: [null, min_max, cdf]

metrics:
  image:
    - F1Score
    - AUROC
  pixel:
    - F1Score
    - AUROC
  threshold:
    method: adaptive #options: [adaptive, manual]
    manual_image: null
    manual_pixel: null

visualization:
  show_images: False # show images on the screen
  save_images: True # save images to the file system
  log_images: True # log images to the available loggers (if any)
  image_save_path: null # path to which images will be saved
  mode: full # options: ["full", "simple"]

project:
  seed: 0
  path: ./results

logging:
  logger: [] # options: [comet, tensorboard, wandb, csv] or combinations.
  log_graph: false # Logs the model graph to respective logger.

optimization:
  export_mode: onnx # options: onnx, openvino

# PL Trainer Args. Don't add extra parameter here.
trainer:
  enable_checkpointing: true
  default_root_dir: null
  gradient_clip_val: 0
  gradient_clip_algorithm: norm
  num_nodes: 1
  devices: 1
  enable_progress_bar: true
  overfit_batches: 0.0
  track_grad_norm: -1
  check_val_every_n_epoch: 1 # Don't validate before extracting features.
  fast_dev_run: false
  accumulate_grad_batches: 1
  max_epochs: 1
  min_epochs: null
  max_steps: -1
  min_steps: null
  max_time: null
  limit_train_batches: 1.0
  limit_val_batches: 1.0
  limit_test_batches: 1.0
  limit_predict_batches: 1.0
  val_check_interval: 1.0 # Don't validate before extracting features.
  log_every_n_steps: 50
  accelerator: auto # <"cpu", "gpu", "tpu", "ipu", "hpu", "auto">
  strategy: null
  sync_batchnorm: false
  precision: 32
  enable_model_summary: true
  num_sanity_val_steps: 0
  profiler: null
  benchmark: false
  deterministic: false
  reload_dataloaders_every_n_epochs: 0
  auto_lr_find: false
  replace_sampler_ddp: true
  detect_anomaly: false
  auto_scale_batch_size: false
  plugins: null
  move_metrics_to_cpu: false
  multiple_trainloader_mode: max_size_cycle

Logs

RuntimeError: cdist only supports at least 2D tensors, X2 got: 1D


### Code of Conduct

- [X] I agree to follow this project's Code of Conduct
samet-akcay commented 1 year ago

@hgaiser, thanks for you patience! Looks like this is indeed a problem when there is no validation set. Thanks for reporting this!

The reason for changing the hook to validation_start was because the hook order has changed on the Lightning side. For more details, you could refer to this: https://lightning.ai/docs/pytorch/stable/common/lightning_module.html#hooks

When we change the hook to on_train_epoch_end, it completes the training and the export, but fails to do trainer.test(). It is probably because ModelCheckpoint does not handle the loading the model properly and memory_bank is not being loaded properly.

We will investigate this and provided an update here

hgaiser commented 7 months ago

@samet-akcay is there any update on this issue?

samet-akcay commented 7 months ago

@hgaiser, there has been a bit of change in these files as part of the v1 development. Tbh, I haven't checked this for a while. Let me check..