Repository: pytorch/ignite

Issue:
Provide basic update + inference functions
See #37 for discussion on this. We should provide something for simple supervised training

cc @jasonkriss


Discussion:

Resolving patch:
diff --git a/ignite/trainer/trainer.py b/ignite/trainer/trainer.py
--- a/ignite/trainer/trainer.py
+++ b/ignite/trainer/trainer.py
@@ -5,9 +5,10 @@
 from collections import Iterable
 
 from enum import Enum
+from torch.autograd import Variable
 from ignite.trainer.history import History
 
-__all__ = ["TrainingEvents", "Trainer"]
+__all__ = ["TrainingEvents", "Trainer", "create_supervised"]
 
 
 class TrainingEvents(Enum):
@@ -212,3 +213,41 @@ def run(self, training_data, max_epochs=1):
             self._logger.error("Training is terminating due to exception: %s", str(e))
             self._fire_event(TrainingEvents.EXCEPTION_RAISED)
             raise e
+
+
+def create_supervised(model, optimizer, loss_fn, cuda=False):
+    """
+    Factory function for creating a trainer for supervised models
+
+    Args:
+        model (torch.nn.Module): the model to train
+        optimizer (torch.optim.Optimizer): the optimizer to use
+        loss_fn (torch.nn loss function): the loss function to use
+        cuda (bool, optional): whether or not to transfer batch to GPU (default: False)
+
+    Returns:
+        Trainer: a trainer instance with supervised update and inference functions
+    """
+    def _prepare_batch(batch, volatile=False):
+        x, y = batch
+        if cuda:
+            x, y = x.cuda(), y.cuda()
+        return Variable(x, volatile=volatile), Variable(y, volatile=volatile)
+
+    def _update(batch):
+        model.train()
+        optimizer.zero_grad()
+        x, y = _prepare_batch(batch)
+        y_pred = model(x)
+        loss = loss_fn(y_pred, y)
+        loss.backward()
+        optimizer.step()
+        return loss.data.cpu()[0]
+
+    def _inference(batch):
+        model.eval()
+        x, y = _prepare_batch(batch, volatile=True)
+        y_pred = model(x)
+        return y_pred.data.cpu(), y.data.cpu()
+
+    return Trainer(_update, _inference)


Test patch:
diff --git a/tests/ignite/trainer/test_trainer.py b/tests/ignite/trainer/test_trainer.py
--- a/tests/ignite/trainer/test_trainer.py
+++ b/tests/ignite/trainer/test_trainer.py
@@ -1,11 +1,16 @@
 from __future__ import division
 
 import uuid
+import torch
+from torch.nn import Linear
+from torch.optim import SGD
+from torch.autograd import Variable
+from torch.nn.functional import mse_loss
 import numpy as np
 from mock import call, MagicMock, Mock
-from pytest import raises
+from pytest import raises, approx
 
-from ignite.trainer import Trainer, TrainingEvents
+from ignite.trainer import Trainer, TrainingEvents, create_supervised
 
 
 class _PicklableMagicMock(object):
@@ -375,3 +380,33 @@ def test_validation_iteration_events_are_fired_when_validate_is_called_explicitl
     assert iteration_complete.call_count == num_batches
 
     # TODO add test to assure history is written to from trainer
+
+
+def test_create_supervised():
+    model = Linear(1, 1)
+    model.weight.data.zero_()
+    model.bias.data.zero_()
+    optimizer = SGD(model.parameters(), 0.1)
+    trainer = create_supervised(model, optimizer, mse_loss)
+
+    x = torch.FloatTensor([[1.0], [2.0]])
+    y = torch.FloatTensor([[3.0], [5.0]])
+    data = [(x, y)]
+
+    trainer.validate(data)
+    y_pred, y = trainer.validation_history[0]
+
+    assert y_pred[0, 0] == approx(0.0)
+    assert y_pred[1, 0] == approx(0.0)
+    assert y[0, 0] == approx(3.0)
+    assert y[1, 0] == approx(5.0)
+
+    assert model.weight.data[0, 0] == approx(0.0)
+    assert model.bias.data[0] == approx(0.0)
+
+    trainer.run(data)
+    loss = trainer.training_history[0]
+
+    assert loss == approx(17.0)
+    assert model.weight.data[0, 0] == approx(1.3)
+    assert model.bias.data[0] == approx(0.8)