rf-detr-temporal / tests /training /test_drop_path_callback.py
bongkj's picture
Upload RF-DETR-Temporal code and model card
b08d258 verified
Raw
History Blame Contribute Delete
7.9 kB
# ------------------------------------------------------------------------
# RF-DETR
# Copyright (c) 2025 Roboflow. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 [see LICENSE for details]
# ------------------------------------------------------------------------
"""Unit tests for :class:`rfdetr.training.callbacks.drop_schedule.DropPathCallback`."""
from __future__ import annotations
from unittest.mock import MagicMock
import numpy as np
import pytest
from rfdetr.training.callbacks.drop_schedule import DropPathCallback
from rfdetr.training.drop_schedule import drop_scheduler
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def _make_mock_trainer(global_step: int = 0, estimated_stepping_batches: int = 50) -> MagicMock:
"""Create a minimal mock Trainer with controllable step metadata."""
trainer = MagicMock()
trainer.global_step = global_step
trainer.estimated_stepping_batches = estimated_stepping_batches
return trainer
def _make_mock_pl_module(epochs: int = 5) -> MagicMock:
"""Create a minimal mock RFDETRModule with ``train_config.epochs``."""
pl_module = MagicMock()
pl_module.train_config.epochs = epochs
return pl_module
# ---------------------------------------------------------------------------
# TestDropPathCallbackInit
# ---------------------------------------------------------------------------
class TestDropPathCallbackInit:
"""Verify constructor defaults."""
def test_default_args(self) -> None:
"""Default rates are zero and vit_encoder_num_layers is 12."""
cb = DropPathCallback()
assert cb._drop_path == 0.0
assert cb._dropout == 0.0
assert cb._vit_encoder_num_layers == 12
assert cb._dp_schedule is None
assert cb._do_schedule is None
# ---------------------------------------------------------------------------
# TestOnTrainStart
# ---------------------------------------------------------------------------
class TestOnTrainStart:
"""Verify schedule arrays built in ``on_train_start``."""
def test_dp_schedule_matches_drop_scheduler_standard(self) -> None:
"""drop_path schedule matches ``drop_scheduler`` for standard mode."""
cb = DropPathCallback(drop_path=0.3)
trainer = _make_mock_trainer(estimated_stepping_batches=50)
pl_module = _make_mock_pl_module(epochs=5)
cb.on_train_start(trainer, pl_module)
expected = drop_scheduler(0.3, 5, 10)
assert cb._dp_schedule is not None
np.testing.assert_array_equal(cb._dp_schedule, expected)
def test_do_schedule_matches_drop_scheduler_standard(self) -> None:
"""dropout schedule matches ``drop_scheduler`` for standard mode."""
cb = DropPathCallback(dropout=0.1)
trainer = _make_mock_trainer(estimated_stepping_batches=50)
pl_module = _make_mock_pl_module(epochs=5)
cb.on_train_start(trainer, pl_module)
expected = drop_scheduler(0.1, 5, 10)
assert cb._do_schedule is not None
np.testing.assert_array_equal(cb._do_schedule, expected)
def test_no_dp_schedule_when_rate_zero(self) -> None:
"""drop_path=0.0 leaves ``_dp_schedule`` as None."""
cb = DropPathCallback(drop_path=0.0)
trainer = _make_mock_trainer(estimated_stepping_batches=50)
pl_module = _make_mock_pl_module(epochs=5)
cb.on_train_start(trainer, pl_module)
assert cb._dp_schedule is None
def test_dp_schedule_early_mode(self) -> None:
"""Early mode: rates at step 0 and step 30 match ``drop_scheduler``."""
cb = DropPathCallback(drop_path=0.3, cutoff_epoch=2, mode="early")
trainer = _make_mock_trainer(estimated_stepping_batches=50)
pl_module = _make_mock_pl_module(epochs=5)
cb.on_train_start(trainer, pl_module)
expected = drop_scheduler(0.3, 5, 10, 2, "early")
assert cb._dp_schedule is not None
assert cb._dp_schedule[0] == expected[0]
assert cb._dp_schedule[30] == expected[30]
def test_dp_schedule_late_mode(self) -> None:
"""Late mode: rates at step 0 and step 30 match ``drop_scheduler``."""
cb = DropPathCallback(drop_path=0.3, cutoff_epoch=2, mode="late")
trainer = _make_mock_trainer(estimated_stepping_batches=50)
pl_module = _make_mock_pl_module(epochs=5)
cb.on_train_start(trainer, pl_module)
expected = drop_scheduler(0.3, 5, 10, 2, "late")
assert cb._dp_schedule is not None
assert cb._dp_schedule[0] == expected[0]
assert cb._dp_schedule[30] == expected[30]
# ---------------------------------------------------------------------------
# TestOnTrainBatchStart
# ---------------------------------------------------------------------------
class TestOnTrainBatchStart:
"""Verify model update calls in ``on_train_batch_start``."""
def test_update_drop_path_called_with_correct_rate(self) -> None:
"""``update_drop_path`` is called with the schedule value at step 0."""
cb = DropPathCallback(drop_path=0.3, vit_encoder_num_layers=6)
trainer = _make_mock_trainer(estimated_stepping_batches=50)
pl_module = _make_mock_pl_module(epochs=5)
cb.on_train_start(trainer, pl_module)
trainer.global_step = 0
cb.on_train_batch_start(trainer, pl_module, batch=None, batch_idx=0)
assert cb._dp_schedule is not None
pl_module.model.update_drop_path.assert_called_once_with(cb._dp_schedule[0], 6)
def test_update_dropout_called_with_correct_rate(self) -> None:
"""``update_dropout`` is called with the schedule value at step 0."""
cb = DropPathCallback(dropout=0.1)
trainer = _make_mock_trainer(estimated_stepping_batches=50)
pl_module = _make_mock_pl_module(epochs=5)
cb.on_train_start(trainer, pl_module)
trainer.global_step = 0
cb.on_train_batch_start(trainer, pl_module, batch=None, batch_idx=0)
assert cb._do_schedule is not None
pl_module.model.update_dropout.assert_called_once_with(cb._do_schedule[0])
def test_no_update_when_step_out_of_bounds(self) -> None:
"""No model updates when ``global_step`` exceeds schedule length."""
cb = DropPathCallback(drop_path=0.3, dropout=0.1)
trainer = _make_mock_trainer(estimated_stepping_batches=50)
pl_module = _make_mock_pl_module(epochs=5)
cb.on_train_start(trainer, pl_module)
trainer.global_step = 9999
cb.on_train_batch_start(trainer, pl_module, batch=None, batch_idx=0)
pl_module.model.update_drop_path.assert_not_called()
pl_module.model.update_dropout.assert_not_called()
@pytest.mark.parametrize(
"step",
[
pytest.param(0, id="first_step"),
pytest.param(5, id="mid_step"),
pytest.param(9, id="last_of_first_epoch"),
],
)
def test_drop_rates_at_multiple_steps_match_schedule(self, step: int) -> None:
"""Each step uses the correct value from the pre-computed schedule."""
cb = DropPathCallback(drop_path=0.3, vit_encoder_num_layers=6)
trainer = _make_mock_trainer(estimated_stepping_batches=50)
pl_module = _make_mock_pl_module(epochs=5)
cb.on_train_start(trainer, pl_module)
trainer.global_step = step
cb.on_train_batch_start(trainer, pl_module, batch=None, batch_idx=0)
assert cb._dp_schedule is not None
pl_module.model.update_drop_path.assert_called_once_with(cb._dp_schedule[step], 6)