Repository: pytorch/ignite

Issue:
How to save model with same filename?
## 🚀 Feature

<!-- A clear and concise description of the feature proposal -->
<!-- Please outline the motivation for the proposal. Is your feature request related to a problem? e.g., I'm always frustrated when [...]. If this is related to another GitHub issue, please link here too -->
<!-- A clear and concise description of what you want to happen. -->
<!-- A clear and concise description of any alternative solutions or features you've considered, if any. -->
<!-- Add any other context or screenshots about the feature request here. -->
Hi @vfdev-5 ,

I found that `CheckpointHandler` can't use the fixed filename to save latest model:
https://github.com/pytorch/ignite/blob/v0.4.2/ignite/handlers/checkpoint.py#L149
But this is a very important requirement on our side...How can I achieve that?
For example, I want to always save the best model as `model.pt` after validations during training.

Thanks.


Discussion:

Patch:
diff --git a/ignite/handlers/checkpoint.py b/ignite/handlers/checkpoint.py
index 83884ef5..9bb936ac 100644
--- a/ignite/handlers/checkpoint.py
+++ b/ignite/handlers/checkpoint.py
@@ -147,8 +147,8 @@ class Checkpoint(Serializable):
         ``30000-checkpoint-94.pt``
 
         **Warning:** Please, keep in mind that if filename collide with already used one to saved a checkpoint,
-        new checkpoint will not be stored. This means that filename like ``checkpoint.pt`` will be saved only once
-        and will not be overwritten by newer checkpoints.
+        new checkpoint will replace the older one. This means that filename like ``checkpoint.pt`` will be saved
+        every call and will always be overwritten by newer checkpoints.
 
     Note:
         To get the last stored filename, handler exposes attribute ``last_checkpoint``:
@@ -350,22 +350,26 @@ class Checkpoint(Serializable):
             }
             filename = filename_pattern.format(**filename_dict)
 
-            if any(item.filename == filename for item in self._saved):
-                return
-
             metadata = {
                 "basename": "{}{}{}".format(self.filename_prefix, "_" * int(len(self.filename_prefix) > 0), name),
                 "score_name": self.score_name,
                 "priority": priority,
             }
 
-            if not self._check_lt_n_saved():
-                item = self._saved.pop(0)
+            try:
+                index = list(map(lambda it: it.filename == filename, self._saved)).index(True)
+                to_remove = True
+            except ValueError:
+                index = 0
+                to_remove = not self._check_lt_n_saved()
+
+            if to_remove:
+                item = self._saved.pop(index)
                 if isinstance(self.save_handler, BaseSaveHandler):
                     self.save_handler.remove(item.filename)
 
             self._saved.append(Checkpoint.Item(priority, filename))
-            self._saved.sort(key=lambda item: item[0])
+            self._saved.sort(key=lambda it: it[0])
 
             if self.include_self:
                 # Now that we've updated _saved, we can add our own state_dict.


Test patch:
diff --git a/tests/ignite/handlers/test_checkpoint.py b/tests/ignite/handlers/test_checkpoint.py
index d646a486..479096c7 100644
--- a/tests/ignite/handlers/test_checkpoint.py
+++ b/tests/ignite/handlers/test_checkpoint.py
@@ -1309,6 +1309,9 @@ def test_checkpoint_filename_pattern():
     )
     assert res == "best_model_12_acc=0.9999.pt"
 
+    pattern = "{name}.{ext}"
+    assert _test(to_save, filename_pattern=pattern) == "model.pt"
+
     pattern = "chk-{name}--{global_step}.{ext}"
     assert _test(to_save, to_save, filename_pattern=pattern) == "chk-model--203.pt"
     pattern = "chk-{filename_prefix}--{name}--{global_step}.{ext}"
@@ -1446,3 +1449,25 @@ def test_checkpoint_load_state_dict():
     sd = {"saved": [(0, "model_0.pt"), (10, "model_10.pt"), (20, "model_20.pt")]}
     checkpointer.load_state_dict(sd)
     assert checkpointer._saved == true_checkpointer._saved
+
+
+def test_checkpoint_fixed_filename():
+    model = DummyModel()
+    to_save = {"model": model}
+
+    def _test(n_saved):
+        save_handler = MagicMock(spec=BaseSaveHandler)
+        checkpointer = Checkpoint(to_save, save_handler=save_handler, n_saved=n_saved, filename_pattern="{name}.{ext}")
+
+        trainer = Engine(lambda e, b: None)
+
+        for i in range(10):
+            trainer.state = State(epoch=i, iteration=i)
+            checkpointer(trainer)
+            assert save_handler.call_count == i + 1
+            metadata = {"basename": "model", "score_name": None, "priority": i}
+            save_handler.assert_called_with(model.state_dict(), "model.pt", metadata)
+
+    _test(None)
+    _test(1)
+    _test(3)