fix(train): guard checkpoint saving against save_freq=0 (#4112)

is_saving_step was the only step-frequency check without a `> 0` guard,
so save_freq=0 raised ZeroDivisionError from `step % 0` on the first
step. Route the decision through a should_save_checkpoint helper that
treats a non-positive save_freq as "save only the final checkpoint",
matching how log_freq/eval_freq handle non-positive values.

Co-authored-by: Steven Palma <imstevenpmwork@ieee.org>
This commit is contained in:
Ahmet Faruk GÜMÜŞTAŞ
2026-07-30 18:56:56 +03:00
committed by GitHub
parent bd2a796217
commit 1fe58f2d3a
4 changed files with 27 additions and 1 deletions
+13
View File
@@ -30,6 +30,7 @@ from lerobot.common.train_utils import (
save_checkpoint,
save_training_state,
save_training_step,
should_save_checkpoint,
update_last_checkpoint,
)
from lerobot.utils.constants import (
@@ -50,6 +51,18 @@ def test_get_step_identifier():
assert get_step_identifier(456789, 1_000_000) == "0456789"
def test_should_save_checkpoint():
# Periodic checkpoints land on multiples of save_freq.
assert should_save_checkpoint(10, save_freq=10, total_steps=100) is True
assert should_save_checkpoint(5, save_freq=10, total_steps=100) is False
# The final step always saves, even when it is not a multiple of save_freq.
assert should_save_checkpoint(100, save_freq=30, total_steps=100) is True
# save_freq <= 0 disables periodic saving without raising ZeroDivisionError.
assert should_save_checkpoint(1, save_freq=0, total_steps=100) is False
assert should_save_checkpoint(100, save_freq=0, total_steps=100) is True
assert should_save_checkpoint(1, save_freq=-1, total_steps=100) is False
def test_get_step_checkpoint_dir():
output_dir = Path("/checkpoints")
step_dir = get_step_checkpoint_dir(output_dir, 1000, 5)