Skip to content

fix(weights): change how models are serialized and deserialized - #3801

Merged
ashwinvaidya17 merged 14 commits into
open-edge-platform:mainfrom
ashwinvaidya17:fix/engine/weights-loading
Sep 29, 2026
Merged

ashwinvaidya17 merged 14 commits into
open-edge-platform:mainfrom
ashwinvaidya17:fix/engine/weights-loading

Conversation

@ashwinvaidya17

Copy link
Copy Markdown
Contributor

📝 Description

  • Addresses PTK0009203
  • Currently anomalib engine loads checkpoints using weights_only=False. This will end up executing malicious coded if the checkpoint comes from untrusted sources. This PR proposes changes to AnomalibModule to mitigate this.
  • An alternative to this design is to use TRUST_REMOTE_CODE=True but this might make the users set this is their default env variable which is risky.

Design

This design introduces a scoped allowlist used at both checkpoint load seams:

  1. Shared first-party enums (ANOMALIB_SAFE_GLOBALS)
  2. Shared numpy leaf types (NUMPY_SAFE_GLOBALS)
  3. Optional per-model extras via AnomalibModule.checkpoint_safe_globals()

In Anomalib Module

Override

@classmethod
 def load_from_checkpoint(
      ...
  ) -> dict[str, Any]:
    with anomalib_safe_globals(extra=cls.checkpoint_safe_globals()):
            return super().load_from_checkpoint(

Introduce

@classmethod
def checkpoint_safe_globals(cls) -> Sequence[Any]:
    """Model-specific types persisted in ``hyper_parameters``.
    """
    return ()

Model specific globals live here.

Add new plugin for model checkpoint AnomalibCheckpointIO

Single context manager, layered allowlist

anomalib.utils.serialization.anomalib_safe_globals
merges three layers for the duration of a load:

ANOMALIB_SAFE_GLOBALS  +  NUMPY_SAFE_GLOBALS  +  extra (optional)
        │                         │                      │
        ▼                         ▼                      ▼
  PrecisionType            numpy scalar/ndarray     model enums
                           (scheduler / opt state)
with anomalib_safe_globals(extra=cls.checkpoint_safe_globals()):
    torch.load(..., weights_only=True)  # via Lightning / CheckpointIO

Rules for what may be added:

  • Shared anomalib list: first-party enums reused across models.
  • Numpy list: inert reconstruction helpers / dtypes required for scheduler and
    optimizer state under NumPy 2.x (numpy._core). Both numpy.core and
    numpy._core pickle path aliases are registered so older pickles still resolve.
    The project’s dependency floor is NumPy ≥ 2 (via opencv-python-headless), so
    importing numpy._core.multiarray is intentional.
  • Model extras: small first-party types persisted only by that model’s
    constructor hyperparameters. Not shared third-party types, not modules, not
    pathlib.Path.

Dual load seams

Checkpoint restore happens in two places; both must use the same allowlist policy.

AnomalibModule.load_from_checkpoint
        │
        └─ anomalib_safe_globals(extra=cls.checkpoint_safe_globals())

Engine → AnomalibCheckpointIO.load_checkpoint
        │
        └─ anomalib_safe_globals(extra=self.extra_safe_globals)
              ▲
              │ Engine._ensure_checkpoint_io_plugin(model)
              │ copies model.checkpoint_safe_globals()
  1. Direct API / export: AnomalibModule.load_from_checkpoint wraps Lightning’s
    loader with extra=cls.checkpoint_safe_globals().
  2. Trainer ckpt_path: AnomalibCheckpointIO wraps TorchCheckpointIO. The
    Engine installs it when the user has not supplied a CheckpointIO, and refreshes
    extra_safe_globals from the active model before fit / validate / test / predict.

Caller code stays unchanged:

model = VlmAd.load_from_checkpoint("model.ckpt")
engine.test(VlmAd(), ckpt_path="model.ckpt", datamodule=datamodule)

Per-model extension point

class AnomalibModule(...):
    @classmethod
    def checkpoint_safe_globals(cls) -> Sequence[Any]:
        return ()

class VlmAd(AnomalibModule):
    @classmethod
    def checkpoint_safe_globals(cls) -> Sequence[Any]:
        return (ModelName,)

✨ Changes

Select what type of change your PR is:

  • 🚀 New feature (non-breaking change which adds functionality)
  • 🐞 Bug fix (non-breaking change which fixes an issue)
  • 🔄 Refactor (non-breaking change which refactors the code base)
  • ⚡ Performance improvements
  • 🎨 Style changes (code style/formatting)
  • 🧪 Tests (adding/modifying tests)
  • 📚 Documentation update
  • 📦 Build system changes
  • 🚧 CI/CD configuration
  • 🔧 Chore (general maintenance)
  • 🔒 Security update
  • 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)

✅ Checklist

Before you submit your pull request, please make sure you have completed the following steps:

  • 📚 I have made the necessary updates to the documentation (if applicable).
  • 🧪 I have written tests that support my changes and prove that my fix is effective or my feature works (if applicable).
  • 🏷️ My PR title follows conventional commit format.

For more information about code review checklists, see the Code Review Checklist.

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The security goal is not reliably enforced yet (weights_only defaults still defer to upstream defaults), and there are import/compatibility and repo-header convention issues that should be addressed before approval.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

This PR hardens anomalib checkpoint loading by introducing a scoped allowlist mechanism intended to support torch.load(..., weights_only=True) without executing arbitrary code from untrusted checkpoints, applying the policy both to direct AnomalibModule.load_from_checkpoint usage and Trainer ckpt_path restores via a custom CheckpointIO plugin.

Changes:

  • Added anomalib_safe_globals context manager plus shared allowlists for first-party enums and NumPy leaf types used during optimizer/scheduler state restore.
  • Added an AnomalibCheckpointIO plugin and Engine wiring to ensure Trainer restores use the same allowlist policy.
  • Added per-model checkpoint_safe_globals() overrides for models that persist model-local enums in hyper_parameters.
File summaries
File Description
src/anomalib/utils/serialization.py New safe-globals allowlist/context manager used to constrain checkpoint unpickling under weights_only.
src/anomalib/models/components/base/anomalib_module.py Adds checkpoint_safe_globals() extension point and wraps load_from_checkpoint with the allowlist context.
src/anomalib/engine/plugins/checkpoint_io.py New TorchCheckpointIO subclass that wraps checkpoint loads in the allowlist context.
src/anomalib/engine/plugins/init.py Exposes the new AnomalibCheckpointIO plugin.
src/anomalib/engine/engine.py Installs/refreshes the CheckpointIO plugin automatically and removes explicit weights_only=False on Trainer calls.
src/anomalib/models/image/vlm_ad/lightning_model.py Adds model-specific checkpoint_safe_globals() to allowlist ModelName.
src/anomalib/models/image/reverse_distillation/lightning_model.py Adds model-specific checkpoint_safe_globals() to allowlist AnomalyMapGenerationMode.
src/anomalib/models/image/efficient_ad/lightning_model.py Adds model-specific checkpoint_safe_globals() to allowlist EfficientAdModelSize.
src/anomalib/models/image/dfkde/lightning_model.py Adds model-specific checkpoint_safe_globals() to allowlist FeatureScalingMethod.
Review details
  • Files reviewed: 9/9 changed files
  • Comments generated: 8
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/anomalib/engine/plugins/checkpoint_io.py
Comment thread src/anomalib/models/components/base/anomalib_module.py Outdated
Comment thread src/anomalib/utils/serialization.py
Comment thread src/anomalib/utils/serialization.py Outdated
Comment thread src/anomalib/models/components/base/anomalib_module.py Outdated
Comment thread src/anomalib/models/image/dfkde/lightning_model.py
Comment thread src/anomalib/models/image/reverse_distillation/lightning_model.py
Comment thread src/anomalib/models/image/vlm_ad/lightning_model.py
samet-akcay
samet-akcay previously approved these changes Sep 18, 2026

@rajeshgangireddy rajeshgangireddy left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor comments.

  • Can you also make sure tiling checkpoints are also loaded okay with this this new changes. get_ensemble_model() Tiling loading passes a preprocessor and i feel this might fail

  • Some files might need year update in copyright headers.

  • Do we need a similar weight_only plugin for TorchInferencer as well ? It still uses torch.load(path, map_location=self.device, weights_only=False) # nosec B614

self,
path: _PATH,
map_location: Callable | None = lambda storage, _loc: storage,
weights_only: bool | None = None,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similar to the copilot's comment.
I think it might be better to explicitly enforce weights_only=True by default
here and in
anomalib/src/anomalib/models/components/base/anomalib_module.py

Lightning might change None to False for HTTP/HTTPS checkpoint URLS?

https://docs.pytorch.org/docs/main/notes/serialization.html#id3

- fix pickle for some models
- address tiling based models

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
Copilot AI review requested due to automatic review settings September 23, 2026 08:22
Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
Comment thread tests/unit/engine/test_checkpoint_io.py Fixed
Comment thread tests/unit/engine/test_checkpoint_io.py Fixed
Comment thread tests/unit/utils/test_serialization.py Fixed
Comment thread tests/unit/utils/test_serialization.py Fixed
Comment thread tests/unit/utils/test_serialization.py Fixed
Comment thread tests/unit/utils/test_serialization.py Fixed
Comment thread tests/unit/utils/test_serialization.py Fixed
Comment thread tests/unit/utils/test_serialization.py Fixed
Comment thread src/anomalib/pre_processing/utils/spec.py Fixed
Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread src/anomalib/models/components/base/anomalib_module.py
Comment thread src/anomalib/models/components/base/anomalib_module.py
Comment thread src/anomalib/models/components/base/anomalib_module.py
Comment thread src/anomalib/pre_processing/utils/spec.py Outdated
Comment thread src/anomalib/utils/serialization.py
Copilot AI review requested due to automatic review settings September 23, 2026 08:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Unresolved checkpoint compatibility, serialization, and checkpoint-IO integration issues remain.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 6 High severity

Open (6)
Resolved since last review (1)

Comment thread src/anomalib/models/components/base/anomalib_module.py
Comment thread src/anomalib/pre_processing/utils/spec.py Outdated
Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
Copilot AI review requested due to automatic review settings September 23, 2026 09:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Critical checkpoint-save failures and unresolved restore and compatibility issues remain.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 5 High severity · 1 Low severity

Open (6)
Resolved since last review (3)
Previously missed (1)

In code that hasn't changed since last review

Medium severity Add Engine-level checkpoint restore test

src/​anomalib/​engine/​engine.py:460

The new tests exercise AnomalibCheckpointIO.load_checkpoint and plugin installation directly, but none drives Lightning's fit/validate/test/predict with ckpt_path through these changed Engine calls. Add one deterministic Engine-level restore test to catch plugin wiring or positional-argument regressions.

Comment thread src/anomalib/models/components/base/anomalib_module.py Outdated
Comment thread src/anomalib/pre_processing/utils/spec.py Outdated
Comment thread src/anomalib/models/components/base/anomalib_module.py
Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
Copilot AI review requested due to automatic review settings September 23, 2026 10:01

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Unresolved critical and moderate findings affect checkpoint restoration, compatibility, and safe plugin integration.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 2 High severity · 1 Medium severity

Open (3)
Resolved since last review (6)

Comment thread src/anomalib/models/components/base/anomalib_module.py
Comment thread src/anomalib/pre_processing/utils/spec.py Outdated
Comment thread tests/unit/pre_processing/test_spec.py Outdated
Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
Copilot AI review requested due to automatic review settings September 23, 2026 11:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Unresolved checkpoint compatibility, restore integration, component round-trip, and dependency issues remain.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 3 Medium severity · 2 Low severity

Open (5)
Resolved since last review (3)

Comment thread src/anomalib/engine/engine.py
Comment thread src/anomalib/post_processing/post_processor.py
Comment thread src/anomalib/pre_processing/pre_processor.py Outdated
Comment thread tests/unit/pipelines/tiled_ensemble/test_helper_functions.py
ashwinvaidya17 and others added 4 commits September 24, 2026 09:27
Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Component-state regressions, skipped subclass restoration hooks, legacy compatibility, and unsupported NumPy environments must be addressed.

Review effort: Balanced
Findings: 1 Medium severity · 2 Low severity

Open (3)
Resolved since last review (5)

self.save_hyperparameters()
# Components are restored from safe checkpoint data below; evaluator and
# visualizer configuration is intentionally rebuilt from model defaults.
self.save_hyperparameters(ignore=["pre_processor", "post_processor", "evaluator", "visualizer"])
def load_checkpoint(
self,
path: _PATH,
map_location: Callable | None = lambda storage, _loc: storage,
Comment on lines +78 to +80
Raises:
ValueError: If the specification is malformed, refers to an
unregistered transform, or has invalid constructor arguments.
samet-akcay
samet-akcay previously approved these changes Sep 29, 2026
Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
@ashwinvaidya17
ashwinvaidya17 merged commit 95fc959 into open-edge-platform:main Sep 29, 2026
47 of 70 checks passed
@ashwinvaidya17
ashwinvaidya17 deleted the fix/engine/weights-loading branch September 29, 2026 11:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants