Repository: pytorch/ignite

Issue:
Start current_epoch + current_iteration from 1 instead of 0.
Also increment these counters *after* the `ITERATION_COMPLETE`/`EPOCH_COMPLETE` events.

See discussion in #59 for more details


Discussion:

Resolving patch:
diff --git a/ignite/engine.py b/ignite/engine.py
--- a/ignite/engine.py
+++ b/ignite/engine.py
@@ -111,15 +111,16 @@ def _run_once_on_dataset(self, dataset):
         try:
             start_time = time.time()
             for batch in dataset:
+                self.current_iteration += 1
                 self._fire_event(Events.ITERATION_STARTED)
                 step_result = self._process_function(batch)
                 if step_result is not None:
                     self.history.append(step_result)
 
-                self.current_iteration += 1
                 self._fire_event(Events.ITERATION_COMPLETED)
                 if self.should_terminate:
                     break
+
             time_taken = time.time() - start_time
             hours, mins, secs = _to_hours_mins_secs(time_taken)
             return hours, mins, secs
diff --git a/ignite/trainer.py b/ignite/trainer.py
--- a/ignite/trainer.py
+++ b/ignite/trainer.py
@@ -67,12 +67,12 @@ def run(self, training_data, max_epochs=1):
 
             self._fire_event(Events.STARTED)
             while self.current_epoch < max_epochs and not self.should_terminate:
+                self.current_epoch += 1
                 self._fire_event(Events.EPOCH_STARTED)
                 self._train_one_epoch(training_data)
                 if self.should_terminate:
                     break
                 self._fire_event(Events.EPOCH_COMPLETED)
-                self.current_epoch += 1
 
             self._fire_event(Events.COMPLETED)
             time_taken = time.time() - start_time


Test patch:
diff --git a/tests/ignite/test_evaluator.py b/tests/ignite/test_evaluator.py
--- a/tests/ignite/test_evaluator.py
+++ b/tests/ignite/test_evaluator.py
@@ -14,7 +14,7 @@ def test_current_validation_iteration_counter_increases_every_iteration():
 
     class IterationCounter(object):
         def __init__(self):
-            self.current_iteration_count = 0
+            self.current_iteration_count = 1
             self.total_count = 0
 
         def __call__(self, evaluator):
@@ -23,7 +23,7 @@ def __call__(self, evaluator):
             self.total_count += 1
 
         def clear(self):
-            self.current_iteration_count = 0
+            self.current_iteration_count = 1
 
     iteration_counter = IterationCounter()
 
@@ -71,15 +71,15 @@ def test_terminate_stops_evaluator_when_called_during_iteration():
     iteration_to_stop = 3  # i.e. part way through the 3rd validation run
     evaluator = Evaluator(MagicMock(return_value=1))
 
-    def end_of_iteration_handler(evaluator):
+    def start_of_iteration_handler(evaluator):
         if evaluator.current_iteration == iteration_to_stop:
             evaluator.terminate()
 
-    evaluator.add_event_handler(Events.ITERATION_STARTED, end_of_iteration_handler)
+    evaluator.add_event_handler(Events.ITERATION_STARTED, start_of_iteration_handler)
     evaluator.run([None] * num_iterations)
 
-    # should complete the iteration when terminate called
-    assert evaluator.current_iteration == iteration_to_stop + 1
+    # should complete the iteration when terminate called but not increment counter
+    assert evaluator.current_iteration == iteration_to_stop
 
 
 def test_create_supervised():
diff --git a/tests/ignite/test_trainer.py b/tests/ignite/test_trainer.py
--- a/tests/ignite/test_trainer.py
+++ b/tests/ignite/test_trainer.py
@@ -60,7 +60,7 @@ def test_current_epoch_counter_increases_every_epoch():
 
     class EpochCounter(object):
         def __init__(self):
-            self.current_epoch_count = 0
+            self.current_epoch_count = 1
 
         def __call__(self, trainer):
             assert trainer.current_epoch == self.current_epoch_count
@@ -80,7 +80,7 @@ def test_current_iteration_counter_increases_every_iteration():
 
     class IterationCounter(object):
         def __init__(self):
-            self.current_iteration_count = 0
+            self.current_iteration_count = 1
 
         def __call__(self, trainer):
             assert trainer.current_iteration == self.current_iteration_count
@@ -115,7 +115,7 @@ def end_of_epoch_handler(trainer):
 
     trainer.run([1], max_epochs=max_epochs)
 
-    assert trainer.current_epoch == last_epoch_to_run + 1  # counter is incremented at end of loop
+    assert trainer.current_epoch == last_epoch_to_run
     assert trainer.should_terminate
 
 
@@ -139,24 +139,23 @@ def start_of_epoch_handler(trainer):
     assert trainer.current_epoch == epoch_to_terminate_on
     assert trainer.should_terminate
     # completes first iteration
-    assert trainer.current_iteration == (epoch_to_terminate_on * len(batches_per_epoch)) + 1
+    assert trainer.current_iteration == ((epoch_to_terminate_on - 1) * len(batches_per_epoch)) + 1
 
 
 def test_terminate_stops_training_mid_epoch():
     num_iterations_per_epoch = 10
-    iteration_to_stop = num_iterations_per_epoch + 3  # i.e. part way through the 2nd epoch
+    iteration_to_stop = num_iterations_per_epoch + 3  # i.e. part way through the 3rd epoch
     trainer = Trainer(MagicMock(return_value=1))
 
-    def end_of_iteration_handler(trainer):
+    def start_of_iteration_handler(trainer):
         if trainer.current_iteration == iteration_to_stop:
             trainer.terminate()
 
-    trainer.add_event_handler(Events.ITERATION_STARTED, end_of_iteration_handler)
+    trainer.add_event_handler(Events.ITERATION_STARTED, start_of_iteration_handler)
     trainer.run(training_data=[None] * num_iterations_per_epoch, max_epochs=3)
-    assert (trainer.current_iteration == iteration_to_stop +
-            1)  # completes the iteration when terminate called
-    assert trainer.current_epoch == np.ceil(
-        iteration_to_stop / num_iterations_per_epoch) - 1  # it starts from 0
+    # completes the iteration but doesn't increment counter (this happens just before a new iteration starts)
+    assert (trainer.current_iteration == iteration_to_stop)
+    assert trainer.current_epoch == np.ceil(iteration_to_stop / num_iterations_per_epoch)  # it starts from 0
 
 
 def _create_mock_data_loader(epochs, batches_per_epoch):
@@ -199,7 +198,7 @@ def test_training_iteration_events_are_fired():
     assert mock_manager.mock_calls == expected_calls
 
 
-def test_create_supervised():
+def test_create_supervised_trainer():
     model = Linear(1, 1)
     model.weight.data.zero_()
     model.bias.data.zero_()