Source code for catalyst.core.callback
from typing import TYPE_CHECKING
from enum import IntFlag
if TYPE_CHECKING:
from catalyst.core.runner import IRunner
[docs]class ICallback:
"""A callable abstraction for deep learning runs."""
[docs] def on_experiment_start(self, runner: "IRunner") -> None:
"""Event handler for experiment start."""
pass
[docs] def on_epoch_start(self, runner: "IRunner") -> None:
"""Event handler for epoch start."""
pass
[docs] def on_loader_start(self, runner: "IRunner") -> None:
"""Event handler for loader start."""
pass
[docs] def on_batch_start(self, runner: "IRunner") -> None:
"""Event handler for batch start."""
pass
[docs] def on_batch_end(self, runner: "IRunner") -> None:
"""Event handler for batch end."""
pass
[docs] def on_loader_end(self, runner: "IRunner") -> None:
"""Event handler for loader end."""
pass
[docs] def on_epoch_end(self, runner: "IRunner") -> None:
"""Event handler for epoch end."""
pass
[docs] def on_experiment_end(self, runner: "IRunner") -> None:
"""Event handler for experiment end."""
pass
[docs] def on_exception(self, runner: "IRunner") -> None:
"""Event handler for exception case."""
pass
[docs]class CallbackOrder(IntFlag):
"""Callback usage order during training.
Catalyst executes Callbacks with low `CallbackOrder`
**before** Callbacks with high `CallbackOrder`.
Predefined orders:
- **Internal** (0) - some Catalyst Extras,
like PhaseCallbacks (used in GANs).
- **Metric** (10) - Callbacks with metrics and losses computation.
- **MetricAggregation** (20) - metrics aggregation callbacks,
like sum different losses into one.
- **Backward** (30) - backward step.
- **Optimizer** (40) - optimizer step,
requires computed metrics for optimization.
- **Scheduler** (50) - scheduler step,
in `ReduceLROnPlateau` case
requires computed validation metrics for optimizer schedule.
- **Checkpoint** (60) - checkpoint step.
- **External** (100) - additional callbacks with custom logic.
Nevertheless, you always can create CustomCallback with any order,
for example::
>>> class MyCustomCallback(Callback):
>>> def __init__(self):
>>> super().__init__(order=13)
>>> ...
# MyCustomCallback will be executed after all `Metric`-Callbacks
# but before all `MetricAggregation`-Callbacks.
"""
Internal = internal = 0
Metric = metric = 10
MetricAggregation = metric_aggregation = 20
Backward = backward = 30
Optimizer = optimizer = 40
Scheduler = scheduler = 50
Checkpoint = checkpoint = 50
External = external = 100
[docs]class Callback(ICallback):
"""
An abstraction that lets you customize your experiment run logic.
Args:
order: flag from ``CallbackOrder``
To give users maximum flexibility and extensibility Catalyst supports
callback execution anywhere in the training loop:
.. code:: bash
-- experiment start
---- epoch start
------ loader start
-------- batch start
---------- batch handler (Runner logic)
-------- batch end
------ loader end
---- epoch end
-- experiment end
exception – if an Exception was raised
Abstraction, please check out implementations for more details:
- :py:mod:`catalyst.callbacks.criterion.CriterionCallback`
- :py:mod:`catalyst.callbacks.optimizer.OptimizerCallback`
- :py:mod:`catalyst.callbacks.scheduler.SchedulerCallback`
- :py:mod:`catalyst.callbacks.checkpoint.CheckpointCallback`
.. note::
To learn more about Catalyst Core concepts, please check out
- :py:mod:`catalyst.core.runner.IRunner`
- :py:mod:`catalyst.core.engine.Engine`
- :py:mod:`catalyst.core.callback.Callback`
"""
def __init__(self, order: int):
"""Init."""
self.order = order
class IMetricCallback(Callback):
"""Metric callback interface, abstraction over metric step."""
def __init__(self):
"""Init."""
super().__init__(order=CallbackOrder.Metric)
class ICriterionCallback(IMetricCallback):
"""Criterion callback interface, abstraction over criterion step."""
pass
class IBackwardCallback(Callback):
"""Backward callback interface, abstraction over backward step."""
def __init__(self):
"""Init."""
super().__init__(order=CallbackOrder.Backward)
class IOptimizerCallback(Callback):
"""Optimizer callback interface, abstraction over optimizer step."""
def __init__(self):
"""Init."""
super().__init__(order=CallbackOrder.Optimizer)
class ISchedulerCallback(Callback):
"""Scheduler callback interface, abstraction over scheduler step."""
def __init__(self):
"""Init."""
super().__init__(order=CallbackOrder.Scheduler)
class ICheckpointCallback(Callback):
"""Checkpoint callback interface, abstraction over checkpoint step."""
def __init__(self):
"""Init."""
super().__init__(order=CallbackOrder.Checkpoint)
[docs]class CallbackWrapper(Callback):
"""Enable/disable callback execution.
Args:
base_callback: callback to wrap
enable_callback: indicator to enable/disable
callback, if ``True`` then callback will be enabled,
default ``True``
"""
def __init__(self, base_callback: Callback, enable_callback: bool = True):
"""Init."""
if base_callback is None or not isinstance(base_callback, Callback):
raise ValueError(f"Expected callback but got - {type(base_callback)}!")
super().__init__(order=base_callback.order)
self.callback = base_callback
self._is_enabled = enable_callback
def on_experiment_start(self, runner: "IRunner") -> None:
"""Event handler for experiment start."""
if self._is_enabled:
self.callback.on_experiment_start(runner)
def on_epoch_start(self, runner: "IRunner") -> None:
"""Event handler for epoch start."""
if self._is_enabled:
self.callback.on_epoch_start(runner)
def on_loader_start(self, runner: "IRunner") -> None:
"""Event handler for loader start."""
if self._is_enabled:
self.callback.on_loader_start(runner)
def on_batch_start(self, runner: "IRunner") -> None:
"""Event handler for batch start."""
if self._is_enabled:
self.callback.on_batch_start(runner)
def on_batch_end(self, runner: "IRunner") -> None:
"""Event handler for batch end."""
if self._is_enabled:
self.callback.on_batch_end(runner)
def on_loader_end(self, runner: "IRunner") -> None:
"""Event handler for loader end."""
if self._is_enabled:
self.callback.on_loader_end(runner)
def on_epoch_end(self, runner: "IRunner") -> None:
"""Event handler for epoch end."""
if self._is_enabled:
self.callback.on_epoch_end(runner)
def on_experiment_end(self, runner: "IRunner") -> None:
"""Event handler for experiment end."""
if self._is_enabled:
self.callback.on_experiment_end(runner)
[docs] def on_exception(self, runner: "IRunner") -> None:
"""Event handler for exception case."""
if self._is_enabled:
self.callback.on_exception(runner)
__all__ = [
"ICallback",
"Callback",
"CallbackOrder",
"IMetricCallback",
"ICriterionCallback",
"IBackwardCallback",
"IOptimizerCallback",
"ISchedulerCallback",
"ICheckpointCallback",
"CallbackWrapper",
]