diff --git a/setup.py b/setup.py
index a36ca6a1accd4d20ecd5ad1d1e639eb23631ca42..2caab539931285064815bee69241105e8bd441d2 100644
--- a/setup.py
+++ b/setup.py
@@ -63,11 +63,12 @@ setup(
         ),
       Extension("xbob.learn.mlp._library",
         [
+          "xbob/learn/mlp/trainer.cpp",
           "xbob/learn/mlp/cxx/machine.cpp",
           "xbob/learn/mlp/cxx/cross_entropy.cpp",
           "xbob/learn/mlp/cxx/square_error.cpp",
           "xbob/learn/mlp/cxx/shuffler.cpp",
-          "xbob/learn/mlp/cxx/base_trainer.cpp",
+          "xbob/learn/mlp/cxx/trainer.cpp",
           "xbob/learn/mlp/cxx/backprop.cpp",
           "xbob/learn/mlp/cxx/rprop.cpp",
           "xbob/learn/mlp/shuffler.cpp",
diff --git a/xbob/learn/mlp/cost.cpp b/xbob/learn/mlp/cost.cpp
index 54bdbd8ece7739c81826780b67926b219c0d0322..fb200335a2bba2b7edca84be056e2ec5524462a0 100644
--- a/xbob/learn/mlp/cost.cpp
+++ b/xbob/learn/mlp/cost.cpp
@@ -516,7 +516,7 @@ static int PyBobLearnSquareError_init
 
   try {
     auto _actfun = reinterpret_cast<PyBobLearnActivationObject*>(actfun);
-    self->cxx = new bob::learn::mlp::SquareError(_actfun->cxx);
+    self->cxx.reset(new bob::learn::mlp::SquareError(_actfun->cxx));
   }
   catch (std::exception& ex) {
     PyErr_SetString(PyExc_RuntimeError, ex.what());
@@ -536,8 +536,8 @@ static int PyBobLearnSquareError_init
 static void PyBobLearnSquareError_delete
 (PyBobLearnSquareErrorObject* self) {
 
-  self->parent.cxx = 0;
-  delete self->cxx;
+  self->parent.cxx.reset();
+  self->cxx.reset();
   Py_TYPE(&self->parent)->tp_free((PyObject*)self);
 
 }
@@ -635,7 +635,7 @@ static int PyBobLearnCrossEntropyLoss_init
 
   try {
     auto _actfun = reinterpret_cast<PyBobLearnActivationObject*>(actfun);
-    self->cxx = new bob::learn::mlp::CrossEntropyLoss(_actfun->cxx);
+    self->cxx.reset(new bob::learn::mlp::CrossEntropyLoss(_actfun->cxx));
   }
   catch (std::exception& ex) {
     PyErr_SetString(PyExc_RuntimeError, ex.what());
@@ -655,8 +655,8 @@ static int PyBobLearnCrossEntropyLoss_init
 static void PyBobLearnCrossEntropyLoss_delete
 (PyBobLearnCrossEntropyLossObject* self) {
 
-  self->parent.cxx = 0;
-  delete self->cxx;
+  self->parent.cxx.reset();
+  self->cxx.reset();
   Py_TYPE(&self->parent)->tp_free((PyObject*)self);
 
 }
diff --git a/xbob/learn/mlp/cxx/backprop.cpp b/xbob/learn/mlp/cxx/backprop.cpp
index f6b3388e7b5d5dfcf6586c4ee6e9d8c00cfb4cd3..d881769d6b8f6c1ce44511b2fe44e1b694e56571 100644
--- a/xbob/learn/mlp/cxx/backprop.cpp
+++ b/xbob/learn/mlp/cxx/backprop.cpp
@@ -16,7 +16,7 @@
 
 bob::learn::mlp::BackProp::BackProp(size_t batch_size,
     boost::shared_ptr<bob::learn::mlp::Cost> cost):
-  bob::learn::mlp::BaseTrainer(batch_size, cost),
+  bob::learn::mlp::Trainer(batch_size, cost),
   m_learning_rate(0.1),
   m_momentum(0.0),
   m_prev_deriv(numberOfHiddenLayers() + 1),
@@ -28,7 +28,7 @@ bob::learn::mlp::BackProp::BackProp(size_t batch_size,
 bob::learn::mlp::BackProp::BackProp(size_t batch_size,
     boost::shared_ptr<bob::learn::mlp::Cost> cost,
     const bob::learn::mlp::Machine& machine):
-  bob::learn::mlp::BaseTrainer(batch_size, cost, machine),
+  bob::learn::mlp::Trainer(batch_size, cost, machine),
   m_learning_rate(0.1),
   m_momentum(0.0),
   m_prev_deriv(numberOfHiddenLayers() + 1),
@@ -40,7 +40,7 @@ bob::learn::mlp::BackProp::BackProp(size_t batch_size,
 bob::learn::mlp::BackProp::BackProp(size_t batch_size,
     boost::shared_ptr<bob::learn::mlp::Cost> cost,
     const bob::learn::mlp::Machine& machine, bool train_biases):
-  bob::learn::mlp::BaseTrainer(batch_size, cost, machine, train_biases),
+  bob::learn::mlp::Trainer(batch_size, cost, machine, train_biases),
   m_learning_rate(0.1),
   m_momentum(0.0),
   m_prev_deriv(numberOfHiddenLayers() + 1),
@@ -52,7 +52,7 @@ bob::learn::mlp::BackProp::BackProp(size_t batch_size,
 bob::learn::mlp::BackProp::~BackProp() { }
 
 bob::learn::mlp::BackProp::BackProp(const BackProp& other):
-  bob::learn::mlp::BaseTrainer(other),
+  bob::learn::mlp::Trainer(other),
   m_learning_rate(other.m_learning_rate),
   m_momentum(other.m_momentum)
 {
@@ -64,7 +64,7 @@ bob::learn::mlp::BackProp& bob::learn::mlp::BackProp::operator=
 (const bob::learn::mlp::BackProp& other) {
   if (this != &other)
   {
-    bob::learn::mlp::BaseTrainer::operator=(other);
+    bob::learn::mlp::Trainer::operator=(other);
     m_learning_rate = other.m_learning_rate;
     m_momentum = other.m_momentum;
 
@@ -147,7 +147,7 @@ void bob::learn::mlp::BackProp::setPreviousBiasDerivative(const blitz::Array<dou
 
 void bob::learn::mlp::BackProp::initialize(const bob::learn::mlp::Machine& machine)
 {
-  bob::learn::mlp::BaseTrainer::initialize(machine);
+  bob::learn::mlp::Trainer::initialize(machine);
 
   const std::vector<blitz::Array<double,2> >& machine_weight =
     machine.getWeights();
diff --git a/xbob/learn/mlp/cxx/rprop.cpp b/xbob/learn/mlp/cxx/rprop.cpp
index 1f080bfd084a57e135dd82e02ad337e46bc56392..09ff3fc655d67b4f4fb66b294eb948cd81f3a0c4 100644
--- a/xbob/learn/mlp/cxx/rprop.cpp
+++ b/xbob/learn/mlp/cxx/rprop.cpp
@@ -17,7 +17,7 @@
 
 bob::learn::mlp::RProp::RProp(size_t batch_size,
     boost::shared_ptr<bob::learn::mlp::Cost> cost):
-  bob::learn::mlp::BaseTrainer(batch_size, cost),
+  bob::learn::mlp::Trainer(batch_size, cost),
   m_eta_minus(0.5),
   m_eta_plus(1.2),
   m_delta_zero(0.1),
@@ -35,7 +35,7 @@ bob::learn::mlp::RProp::RProp(size_t batch_size,
 bob::learn::mlp::RProp::RProp(size_t batch_size,
     boost::shared_ptr<bob::learn::mlp::Cost> cost,
     const bob::learn::mlp::Machine& machine):
-  bob::learn::mlp::BaseTrainer(batch_size, cost, machine),
+  bob::learn::mlp::Trainer(batch_size, cost, machine),
   m_eta_minus(0.5),
   m_eta_plus(1.2),
   m_delta_zero(0.1),
@@ -53,7 +53,7 @@ bob::learn::mlp::RProp::RProp(size_t batch_size,
     boost::shared_ptr<bob::learn::mlp::Cost> cost,
     const bob::learn::mlp::Machine& machine,
     bool train_biases):
-  bob::learn::mlp::BaseTrainer(batch_size, cost, machine, train_biases),
+  bob::learn::mlp::Trainer(batch_size, cost, machine, train_biases),
   m_eta_minus(0.5),
   m_eta_plus(1.2),
   m_delta_zero(0.1),
@@ -70,7 +70,7 @@ bob::learn::mlp::RProp::RProp(size_t batch_size,
 bob::learn::mlp::RProp::~RProp() { }
 
 bob::learn::mlp::RProp::RProp(const RProp& other):
-  bob::learn::mlp::BaseTrainer(other),
+  bob::learn::mlp::Trainer(other),
   m_eta_minus(other.m_eta_minus),
   m_eta_plus(other.m_eta_plus),
   m_delta_zero(other.m_delta_zero),
@@ -91,7 +91,7 @@ bob::learn::mlp::RProp& bob::learn::mlp::RProp::operator=
 (const bob::learn::mlp::RProp& other) {
   if (this != &other)
   {
-    bob::learn::mlp::BaseTrainer::operator=(other);
+    bob::learn::mlp::Trainer::operator=(other);
 
     m_eta_minus = other.m_eta_minus;
     m_eta_plus = other.m_eta_plus;
@@ -187,7 +187,7 @@ void bob::learn::mlp::RProp::rprop_weight_update(bob::learn::mlp::Machine& machi
 
 void bob::learn::mlp::RProp::initialize(const bob::learn::mlp::Machine& machine)
 {
-  bob::learn::mlp::BaseTrainer::initialize(machine);
+  bob::learn::mlp::Trainer::initialize(machine);
 
   const std::vector<blitz::Array<double,2> >& machine_weight =
     machine.getWeights();
diff --git a/xbob/learn/mlp/cxx/base_trainer.cpp b/xbob/learn/mlp/cxx/trainer.cpp
similarity index 79%
rename from xbob/learn/mlp/cxx/base_trainer.cpp
rename to xbob/learn/mlp/cxx/trainer.cpp
index df88929d71ef0c61013055c1122c396953cfaa62..902986c5a7ad42acbebb93dd79bb16ea86cd86a3 100644
--- a/xbob/learn/mlp/cxx/base_trainer.cpp
+++ b/xbob/learn/mlp/cxx/trainer.cpp
@@ -11,9 +11,9 @@
 #include <bob/core/check.h>
 #include <bob/math/linear.h>
 
-#include <xbob.learn.mlp/base_trainer.h>
+#include <xbob.learn.mlp/trainer.h>
 
-bob::learn::mlp::BaseTrainer::BaseTrainer(size_t batch_size,
+bob::learn::mlp::Trainer::Trainer(size_t batch_size,
     boost::shared_ptr<bob::learn::mlp::Cost> cost):
   m_batch_size(batch_size),
   m_cost(cost),
@@ -31,7 +31,7 @@ bob::learn::mlp::BaseTrainer::BaseTrainer(size_t batch_size,
   reset();
 }
 
-bob::learn::mlp::BaseTrainer::BaseTrainer(size_t batch_size,
+bob::learn::mlp::Trainer::Trainer(size_t batch_size,
     boost::shared_ptr<bob::learn::mlp::Cost> cost,
     const bob::learn::mlp::Machine& machine):
   m_batch_size(batch_size),
@@ -46,7 +46,7 @@ bob::learn::mlp::BaseTrainer::BaseTrainer(size_t batch_size,
   initialize(machine);
 }
 
-bob::learn::mlp::BaseTrainer::BaseTrainer(size_t batch_size,
+bob::learn::mlp::Trainer::Trainer(size_t batch_size,
     boost::shared_ptr<bob::learn::mlp::Cost> cost,
     const bob::learn::mlp::Machine& machine,
     bool train_biases):
@@ -62,9 +62,9 @@ bob::learn::mlp::BaseTrainer::BaseTrainer(size_t batch_size,
   initialize(machine);
 }
 
-bob::learn::mlp::BaseTrainer::~BaseTrainer() { }
+bob::learn::mlp::Trainer::~Trainer() { }
 
-bob::learn::mlp::BaseTrainer::BaseTrainer(const BaseTrainer& other):
+bob::learn::mlp::Trainer::Trainer(const Trainer& other):
   m_batch_size(other.m_batch_size),
   m_cost(other.m_cost),
   m_train_bias(other.m_train_bias),
@@ -76,8 +76,8 @@ bob::learn::mlp::BaseTrainer::BaseTrainer(const BaseTrainer& other):
   bob::core::array::ccopy(other.m_output, m_output);
 }
 
-bob::learn::mlp::BaseTrainer& bob::learn::mlp::BaseTrainer::operator=
-(const bob::learn::mlp::BaseTrainer& other) {
+bob::learn::mlp::Trainer& bob::learn::mlp::Trainer::operator=
+(const bob::learn::mlp::Trainer& other) {
   if (this != &other)
   {
     m_batch_size = other.m_batch_size;
@@ -93,7 +93,7 @@ bob::learn::mlp::BaseTrainer& bob::learn::mlp::BaseTrainer::operator=
   return *this;
 }
 
-void bob::learn::mlp::BaseTrainer::setBatchSize (size_t batch_size) {
+void bob::learn::mlp::Trainer::setBatchSize (size_t batch_size) {
   // m_output: values after the activation function
   // m_error: error values;
 
@@ -108,7 +108,7 @@ void bob::learn::mlp::BaseTrainer::setBatchSize (size_t batch_size) {
   }
 }
 
-bool bob::learn::mlp::BaseTrainer::isCompatible(const bob::learn::mlp::Machine& machine) const
+bool bob::learn::mlp::Trainer::isCompatible(const bob::learn::mlp::Machine& machine) const
 {
   if (m_H != machine.numOfHiddenLayers()) return false;
 
@@ -125,7 +125,7 @@ bool bob::learn::mlp::BaseTrainer::isCompatible(const bob::learn::mlp::Machine&
   return true;
 }
 
-void bob::learn::mlp::BaseTrainer::forward_step(const bob::learn::mlp::Machine& machine,
+void bob::learn::mlp::Trainer::forward_step(const bob::learn::mlp::Machine& machine,
   const blitz::Array<double,2>& input)
 {
   const std::vector<blitz::Array<double,2> >& machine_weight = machine.getWeights();
@@ -147,7 +147,7 @@ void bob::learn::mlp::BaseTrainer::forward_step(const bob::learn::mlp::Machine&
   }
 }
 
-void bob::learn::mlp::BaseTrainer::backward_step
+void bob::learn::mlp::Trainer::backward_step
 (const bob::learn::mlp::Machine& machine,
  const blitz::Array<double,2>& input, const blitz::Array<double,2>& target)
 {
@@ -184,7 +184,7 @@ void bob::learn::mlp::BaseTrainer::backward_step
   }
 }
 
-double bob::learn::mlp::BaseTrainer::cost
+double bob::learn::mlp::Trainer::cost
 (const blitz::Array<double,2>& target) const {
   bob::core::array::assertSameShape(m_output[m_H], target);
   double retval = 0.0;
@@ -196,14 +196,14 @@ double bob::learn::mlp::BaseTrainer::cost
   return retval / target.extent(0);
 }
 
-double bob::learn::mlp::BaseTrainer::cost
+double bob::learn::mlp::Trainer::cost
 (const bob::learn::mlp::Machine& machine, const blitz::Array<double,2>& input,
  const blitz::Array<double,2>& target) {
   forward_step(machine, input);
   return cost(target);
 }
 
-void bob::learn::mlp::BaseTrainer::initialize(const bob::learn::mlp::Machine& machine)
+void bob::learn::mlp::Trainer::initialize(const bob::learn::mlp::Machine& machine)
 {
   const std::vector<blitz::Array<double,2> >& machine_weight =
     machine.getWeights();
@@ -225,7 +225,7 @@ void bob::learn::mlp::BaseTrainer::initialize(const bob::learn::mlp::Machine& ma
   reset();
 }
 
-void bob::learn::mlp::BaseTrainer::setError(const std::vector<blitz::Array<double,2> >& error) {
+void bob::learn::mlp::Trainer::setError(const std::vector<blitz::Array<double,2> >& error) {
   bob::core::array::assertSameDimensionLength(error.size(), m_error.size());
   for (size_t k=0; k<error.size(); ++k)
   {
@@ -234,9 +234,9 @@ void bob::learn::mlp::BaseTrainer::setError(const std::vector<blitz::Array<doubl
   }
 }
 
-void bob::learn::mlp::BaseTrainer::setError(const blitz::Array<double,2>& error, const size_t id) {
+void bob::learn::mlp::Trainer::setError(const blitz::Array<double,2>& error, const size_t id) {
   if (id >= m_error.size()) {
-    boost::format m("BaseTrainer: index for setting error array %lu is not on the expected range of [0, %lu]");
+    boost::format m("Trainer: index for setting error array %lu is not on the expected range of [0, %lu]");
     m % id % (m_error.size()-1);
     throw std::runtime_error(m.str());
   }
@@ -244,7 +244,7 @@ void bob::learn::mlp::BaseTrainer::setError(const blitz::Array<double,2>& error,
   m_error[id] = error;
 }
 
-void bob::learn::mlp::BaseTrainer::setOutput(const std::vector<blitz::Array<double,2> >& output) {
+void bob::learn::mlp::Trainer::setOutput(const std::vector<blitz::Array<double,2> >& output) {
   bob::core::array::assertSameDimensionLength(output.size(), m_output.size());
   for (size_t k=0; k<output.size(); ++k)
   {
@@ -253,9 +253,9 @@ void bob::learn::mlp::BaseTrainer::setOutput(const std::vector<blitz::Array<doub
   }
 }
 
-void bob::learn::mlp::BaseTrainer::setOutput(const blitz::Array<double,2>& output, const size_t id) {
+void bob::learn::mlp::Trainer::setOutput(const blitz::Array<double,2>& output, const size_t id) {
   if (id >= m_output.size()) {
-    boost::format m("BaseTrainer: index for setting output array %lu is not on the expected range of [0, %lu]");
+    boost::format m("Trainer: index for setting output array %lu is not on the expected range of [0, %lu]");
     m % id % (m_output.size()-1);
     throw std::runtime_error(m.str());
   }
@@ -263,7 +263,7 @@ void bob::learn::mlp::BaseTrainer::setOutput(const blitz::Array<double,2>& outpu
   m_output[id] = output;
 }
 
-void bob::learn::mlp::BaseTrainer::setDerivatives(const std::vector<blitz::Array<double,2> >& deriv) {
+void bob::learn::mlp::Trainer::setDerivatives(const std::vector<blitz::Array<double,2> >& deriv) {
   bob::core::array::assertSameDimensionLength(deriv.size(), m_deriv.size());
   for (size_t k=0; k<deriv.size(); ++k)
   {
@@ -272,9 +272,9 @@ void bob::learn::mlp::BaseTrainer::setDerivatives(const std::vector<blitz::Array
   }
 }
 
-void bob::learn::mlp::BaseTrainer::setDerivative(const blitz::Array<double,2>& deriv, const size_t id) {
+void bob::learn::mlp::Trainer::setDerivative(const blitz::Array<double,2>& deriv, const size_t id) {
   if (id >= m_deriv.size()) {
-    boost::format m("BaseTrainer: index for setting derivative array %lu is not on the expected range of [0, %lu]");
+    boost::format m("Trainer: index for setting derivative array %lu is not on the expected range of [0, %lu]");
     m % id % (m_deriv.size()-1);
     throw std::runtime_error(m.str());
   }
@@ -282,7 +282,7 @@ void bob::learn::mlp::BaseTrainer::setDerivative(const blitz::Array<double,2>& d
   m_deriv[id] = deriv;
 }
 
-void bob::learn::mlp::BaseTrainer::setBiasDerivatives(const std::vector<blitz::Array<double,1> >& deriv_bias) {
+void bob::learn::mlp::Trainer::setBiasDerivatives(const std::vector<blitz::Array<double,1> >& deriv_bias) {
   bob::core::array::assertSameDimensionLength(deriv_bias.size(), m_deriv_bias.size());
   for (size_t k=0; k<deriv_bias.size(); ++k)
   {
@@ -291,9 +291,9 @@ void bob::learn::mlp::BaseTrainer::setBiasDerivatives(const std::vector<blitz::A
   }
 }
 
-void bob::learn::mlp::BaseTrainer::setBiasDerivative(const blitz::Array<double,1>& deriv_bias, const size_t id) {
+void bob::learn::mlp::Trainer::setBiasDerivative(const blitz::Array<double,1>& deriv_bias, const size_t id) {
   if (id >= m_deriv_bias.size()) {
-    boost::format m("BaseTrainer: index for setting bias derivative array %lu is not on the expected range of [0, %lu]");
+    boost::format m("Trainer: index for setting bias derivative array %lu is not on the expected range of [0, %lu]");
     m % id % (m_deriv_bias.size()-1);
     throw std::runtime_error(m.str());
   }
@@ -301,7 +301,7 @@ void bob::learn::mlp::BaseTrainer::setBiasDerivative(const blitz::Array<double,1
   m_deriv_bias[id] = deriv_bias;
 }
 
-void bob::learn::mlp::BaseTrainer::reset() {
+void bob::learn::mlp::Trainer::reset() {
   for (size_t k=0; k<(m_H + 1); ++k) {
     m_deriv[k] = 0.;
     m_deriv_bias[k] = 0.;
diff --git a/xbob/learn/mlp/include/xbob.learn.mlp/api.h b/xbob/learn/mlp/include/xbob.learn.mlp/api.h
index 140df1d39d143869ccc907e1d34734568f04200e..d6ac3a88cc5a02635eaf1d932de488c0dee37674 100644
--- a/xbob/learn/mlp/include/xbob.learn.mlp/api.h
+++ b/xbob/learn/mlp/include/xbob.learn.mlp/api.h
@@ -7,6 +7,7 @@
 #define XBOB_LEARN_MLP_H
 
 #include <Python.h>
+#include <boost/shared_ptr.hpp>
 #include <xbob.learn.mlp/config.h>
 
 #include "machine.h"
@@ -14,6 +15,7 @@
 #include "square_error.h"
 #include "cross_entropy.h"
 #include "shuffler.h"
+#include "trainer.h"
 
 #define XBOB_LEARN_MLP_MODULE_PREFIX xbob.learn.mlp
 #define XBOB_LEARN_MLP_MODULE_NAME _library
@@ -23,7 +25,7 @@
  *******************/
 
 /* Enum defining entries in the function table */
-enum _PyBobLearnMLP_ENUM{
+enum _PyBobLearnMLP_ENUM {
   PyXbobLearnMLP_APIVersion_NUM = 0,
   // Bindings for xbob.learn.mlp.Machine
   PyBobLearnMLPMachine_Type_NUM,
@@ -37,6 +39,9 @@ enum _PyBobLearnMLP_ENUM{
   // Bindings for xbob.learn.mlp.DataShuffler
   PyBobLearnDataShuffler_Type_NUM,
   PyBobLearnDataShuffler_Check_NUM,
+  // Bindings for xbob.learn.mlp.Trainer
+  PyBobLearnMLPTrainer_Type_NUM,
+  PyBobLearnMLPTrainer_Check_NUM,
   // Total number of C API pointers
   PyXbobLearnMLP_API_pointers
 };
@@ -47,9 +52,9 @@ enum _PyBobLearnMLP_ENUM{
 
 #define PyXbobLearnMLP_APIVersion_TYPE int
 
-/******************************************
+/***************************************
  * Bindings for xbob.learn.mlp.Machine *
- ******************************************/
+ ***************************************/
 
 typedef struct {
   PyObject_HEAD
@@ -66,7 +71,7 @@ typedef struct {
 
 typedef struct {
   PyObject_HEAD
-  bob::learn::mlp::Cost* cxx;
+  boost::shared_ptr<bob::learn::mlp::Cost> cxx;
 } PyBobLearnCostObject;
 
 #define PyBobLearnCost_Type_TYPE PyTypeObject
@@ -76,14 +81,14 @@ typedef struct {
 
 typedef struct {
   PyBobLearnCostObject parent;
-  bob::learn::mlp::SquareError* cxx;
+  boost::shared_ptr<bob::learn::mlp::SquareError> cxx;
 } PyBobLearnSquareErrorObject;
 
 #define PyBobLearnSquareError_Type_TYPE PyTypeObject
 
 typedef struct {
   PyBobLearnCostObject parent;
-  bob::learn::mlp::CrossEntropyLoss* cxx;
+  boost::shared_ptr<bob::learn::mlp::CrossEntropyLoss> cxx;
 } PyBobLearnCrossEntropyLossObject;
 
 #define PyBobLearnCrossEntropyLoss_Type_TYPE PyTypeObject
@@ -98,6 +103,20 @@ typedef struct {
 #define PyBobLearnDataShuffler_Check_RET int
 #define PyBobLearnDataShuffler_Check_PROTO (PyObject* o)
 
+/***************************************
+ * Bindings for xbob.learn.mlp.Trainer *
+ ***************************************/
+
+typedef struct {
+  PyObject_HEAD
+  bob::learn::mlp::Trainer* cxx;
+} PyBobLearnMLPTrainerObject;
+
+#define PyBobLearnMLPTrainer_Type_TYPE PyTypeObject
+
+#define PyBobLearnMLPTrainer_Check_RET int
+#define PyBobLearnMLPTrainer_Check_PROTO (PyObject* o)
+
 #ifdef XBOB_LEARN_MLP_MODULE
 
   /* This section is used when compiling `xbob.learn.mlp' itself */
@@ -138,6 +157,14 @@ typedef struct {
 
   PyBobLearnDataShuffler_Check_RET PyBobLearnDataShuffler_Check PyBobLearnDataShuffler_Check_PROTO;
 
+  /***************************************
+   * Bindings for xbob.learn.mlp.Trainer *
+   ***************************************/
+
+  extern PyBobLearnMLPTrainer_Type_TYPE PyBobLearnMLPTrainer_Type;
+
+  PyBobLearnMLPTrainer_Check_RET PyBobLearnMLPTrainer_Check PyBobLearnMLPTrainer_Check_PROTO;
+
 #else
 
   /* This section is used in modules that use `xbob.learn.mlp's' C-API */
@@ -170,9 +197,9 @@ typedef struct {
 
 # define PyXbobLearnMLP_APIVersion (*(PyXbobLearnMLP_APIVersion_TYPE *)PyXbobLearnMLP_API[PyXbobLearnMLP_APIVersion_NUM])
 
-  /******************************************
+  /***************************************
    * Bindings for xbob.learn.mlp.Machine *
-   ******************************************/
+   ***************************************/
 
 # define PyBobLearnMLPMachine_Type (*(PyBobLearnMLPMachine_Type_TYPE *)PyXbobLearnMLP_API[PyBobLearnMLPMachine_Type_NUM])
 
@@ -200,6 +227,14 @@ typedef struct {
 
 # define PyBobLearnDataShuffler_Check (*(PyBobLearnDataShuffler_Check_RET (*)PyBobLearnDataShuffler_Check_PROTO) PyXbobLearnMLP_API[PyBobLearnDataShuffler_Check_NUM])
 
+  /***************************************
+   * Bindings for xbob.learn.mlp.Trainer *
+   ***************************************/
+
+# define PyBobLearnMLPTrainer_Type (*(PyBobLearnMLPTrainer_Type_TYPE *)PyXbobLearnMLP_API[PyBobLearnMLPTrainer_Type_NUM])
+
+# define PyBobLearnMLPTrainer_Check (*(PyBobLearnMLPTrainer_Check_RET (*)PyBobLearnMLPTrainer_Check_PROTO) PyXbobLearnMLP_API[PyBobLearnMLPTrainer_Check_NUM])
+
 # if !defined(NO_IMPORT_ARRAY)
 
   /**
diff --git a/xbob/learn/mlp/include/xbob.learn.mlp/backprop.h b/xbob/learn/mlp/include/xbob.learn.mlp/backprop.h
index 25c879f9d0dd67f75fa7e42ce18dfe100390f474..182db40d1661646416b2939f8712a2e4b328eb82 100644
--- a/xbob/learn/mlp/include/xbob.learn.mlp/backprop.h
+++ b/xbob/learn/mlp/include/xbob.learn.mlp/backprop.h
@@ -17,7 +17,7 @@
 #include <boost/function.hpp>
 
 #include "machine.h"
-#include "base_trainer.h"
+#include "trainer.h"
 
 namespace bob { namespace learn { namespace mlp {
 
@@ -26,7 +26,7 @@ namespace bob { namespace learn { namespace mlp {
    * back-propagation as defined in "Pattern Recognition and Machine Learning"
    * by C.M. Bishop, chapter 5.
    */
-  class BackProp: public BaseTrainer {
+  class BackProp: public Trainer {
 
     public: //api
 
diff --git a/xbob/learn/mlp/include/xbob.learn.mlp/rprop.h b/xbob/learn/mlp/include/xbob.learn.mlp/rprop.h
index ad34f632ceb3f15689cc85afe57380d877b42bd4..a95c39e2a3415bfc109c5888154c82dd2326b432 100644
--- a/xbob/learn/mlp/include/xbob.learn.mlp/rprop.h
+++ b/xbob/learn/mlp/include/xbob.learn.mlp/rprop.h
@@ -18,7 +18,7 @@
 #include <boost/function.hpp>
 
 #include "machine.h"
-#include "base_trainer.h"
+#include "trainer.h"
 
 namespace bob { namespace learn { namespace mlp {
 
@@ -28,7 +28,7 @@ namespace bob { namespace learn { namespace mlp {
    * by Martin Riedmiller and Heinrich Braun on IEEE International Conference
    * on Neural Networks, pp. 586--591, 1993.
    */
-  class RProp: public BaseTrainer {
+  class RProp: public Trainer {
 
     public: //api
 
diff --git a/xbob/learn/mlp/include/xbob.learn.mlp/base_trainer.h b/xbob/learn/mlp/include/xbob.learn.mlp/trainer.h
similarity index 93%
rename from xbob/learn/mlp/include/xbob.learn.mlp/base_trainer.h
rename to xbob/learn/mlp/include/xbob.learn.mlp/trainer.h
index 221921b337f6bb26b53d66858cc900d99d3cdeec..0188842d021ce610750007a953b15dbc7b36c41e 100644
--- a/xbob/learn/mlp/include/xbob.learn.mlp/base_trainer.h
+++ b/xbob/learn/mlp/include/xbob.learn.mlp/trainer.h
@@ -44,12 +44,12 @@ namespace bob { namespace learn { namespace mlp {
    *    \times a^{(l-1)}) + (\mu) \times w_{n-1}
    *    @f]
    */
-  class BaseTrainer {
+  class Trainer {
 
     public: //api
 
       /**
-       * @brief Initializes a new BaseTrainer trainer according to a given
+       * @brief Initializes a new Trainer trainer according to a given
        * training batch size.
        *
        * @param batch_size The number of examples passed at each iteration. If
@@ -67,11 +67,10 @@ namespace bob { namespace learn { namespace mlp {
        * @note Good values for batch sizes are tens of samples. This may affect
        * the convergence.
        */
-      BaseTrainer(size_t batch_size,
-          boost::shared_ptr<Cost> cost);
+      Trainer(size_t batch_size, boost::shared_ptr<Cost> cost);
 
       /**
-       * @brief Initializes a new BaseTrainer trainer according to a given
+       * @brief Initializes a new Trainer trainer according to a given
        * machine settings and a training batch size.
        *
        * @param batch_size The number of examples passed at each iteration. If
@@ -88,12 +87,11 @@ namespace bob { namespace learn { namespace mlp {
        * @note Good values for batch sizes are tens of samples. This may affect
        * the convergence.
        */
-      BaseTrainer(size_t batch_size,
-          boost::shared_ptr<Cost> cost,
+      Trainer(size_t batch_size, boost::shared_ptr<Cost> cost,
           const Machine& machine);
 
       /**
-       * @brief Initializes a new BaseTrainer trainer according to a given
+       * @brief Initializes a new Trainer trainer according to a given
        * machine settings and a training batch size.
        *
        * @param batch_size The number of examples passed at each iteration. If
@@ -110,25 +108,23 @@ namespace bob { namespace learn { namespace mlp {
        * @note Good values for batch sizes are tens of samples. This may affect
        * the convergence.
        */
-      BaseTrainer(size_t batch_size,
-          boost::shared_ptr<Cost> cost,
-          const Machine& machine,
-          bool train_biases);
+      Trainer(size_t batch_size, boost::shared_ptr<Cost> cost,
+          const Machine& machine, bool train_biases);
 
       /**
        * @brief Destructor virtualisation
        */
-      virtual ~BaseTrainer();
+      virtual ~Trainer();
 
       /**
        * @brief Copy construction.
        */
-      BaseTrainer(const BaseTrainer& other);
+      Trainer(const Trainer& other);
 
       /**
        * @brief Copy operator
        */
-      BaseTrainer& operator=(const BaseTrainer& other);
+      Trainer& operator=(const Trainer& other);
 
       /**
        * @brief Gets the batch size
diff --git a/xbob/learn/mlp/main.cpp b/xbob/learn/mlp/main.cpp
index 6ba1691f4d116e8b6e6770c917a2b5ebcab79cd8..da1b2adf0ccbf7ac9c8b4ce322ab93633c3f4b10 100644
--- a/xbob/learn/mlp/main.cpp
+++ b/xbob/learn/mlp/main.cpp
@@ -53,6 +53,9 @@ static PyObject* create_module (void) {
   PyBobLearnDataShuffler_Type.tp_new = PyType_GenericNew;
   if (PyType_Ready(&PyBobLearnDataShuffler_Type) < 0) return 0;
 
+  PyBobLearnMLPTrainer_Type.tp_new = PyType_GenericNew;
+  if (PyType_Ready(&PyBobLearnMLPTrainer_Type) < 0) return 0;
+
 # if PY_VERSION_HEX >= 0x03000000
   PyObject* m = PyModule_Create(&module_definition);
 # else
@@ -81,6 +84,9 @@ static PyObject* create_module (void) {
   Py_INCREF(&PyBobLearnDataShuffler_Type);
   if (PyModule_AddObject(m, "DataShuffler", (PyObject *)&PyBobLearnDataShuffler_Type) < 0) return 0;
 
+  Py_INCREF(&PyBobLearnMLPTrainer_Type);
+  if (PyModule_AddObject(m, "Trainer", (PyObject *)&PyBobLearnMLPTrainer_Type) < 0) return 0;
+
   static void* PyXbobLearnMLP_API[PyXbobLearnMLP_API_pointers];
 
   /* exhaustive list of C APIs */
@@ -121,6 +127,14 @@ static PyObject* create_module (void) {
 
   PyXbobLearnMLP_API[PyBobLearnDataShuffler_Check_NUM] = (void *)&PyBobLearnDataShuffler_Check;
 
+  /***************************************
+   * Bindings for xbob.learn.mlp.Trainer *
+   ***************************************/
+
+  PyXbobLearnMLP_API[PyBobLearnMLPTrainer_Type_NUM] = (void *)&PyBobLearnMLPTrainer_Type;
+
+  PyXbobLearnMLP_API[PyBobLearnMLPTrainer_Check_NUM] = (void *)&PyBobLearnMLPTrainer_Check;
+
 #if PY_VERSION_HEX >= 0x02070000
 
   /* defines the PyCapsule */
diff --git a/xbob/learn/mlp/trainer.cpp b/xbob/learn/mlp/trainer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a3e2a4ac0410055d60b51f5acabcb2c3922944ac
--- /dev/null
+++ b/xbob/learn/mlp/trainer.cpp
@@ -0,0 +1,205 @@
+/**
+ * @author Andre Anjos <andre.anjos@idiap.ch>
+ * @date Tue  6 May 12:32:39 2014 CEST
+ *
+ * @brief Bindings for an MLP
+ *
+ * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland
+ */
+
+#define XBOB_LEARN_MLP_MODULE
+#include <xbob.blitz/cppapi.h>
+#include <xbob.blitz/cleanup.h>
+#include <xbob.learn.mlp/api.h>
+#include <structmember.h>
+
+/****************************************
+ * Implementation of base Trainer class *
+ ****************************************/
+
+PyDoc_STRVAR(s_trainer_str, XBOB_EXT_MODULE_PREFIX ".Trainer");
+
+PyDoc_STRVAR(s_trainer_doc,
+"Trainer(batch_size, cost, [trainer, [train_biases]]) -> new Trainer\n\
+Trainer(other) -> new Trainer\n\
+\n\
+The base python class for MLP trainers based on cost derivatives.\n\
+\n\
+You should use this class when you want to create your own MLP\n\
+trainers and re-use the base infrastructured provided by this\n\
+module, such as the computation of partial derivatives (using\n\
+the :py:meth:`backward_step` method).\n\
+\n\
+To create a new trainer, either pass another trainer you'd like\n\
+the parameters copied from or pass the batch-size, cost functor,\n\
+machine and a biases-training flag.\n\
+");
+
+static int PyBobLearnMLPTrainer_init_discrete
+(PyBobLearnMLPTrainerObject* self, PyObject* args, PyObject* kwds) {
+
+  /* Parses input arguments in a single shot */
+  static const char* const_kwlist[] = {
+    "batch_size",
+    "cost",
+    "machine",
+    "train_biases",
+    0
+  };
+  static char** kwlist = const_cast<char**>(const_kwlist);
+
+  Py_ssize_t batch_size = 0;
+  PyBobLearnCostObject* cost = 0;
+  PyBobLearnMLPMachineObject* machine = 0;
+  PyObject* train_biases = 0;
+
+  if (!PyArg_ParseTupleAndKeywords(args, kwds, "nO!|O!O", kwlist,
+        &batch_size,
+        &PyBobLearnCost_Type, &cost,
+        &PyBobLearnMLPMachine_Type, &machine,
+        &train_biases)) return -1;
+
+  try {
+    if (machine && train_biases) {
+      self->cxx = new bob::learn::mlp::Trainer(batch_size, cost->cxx,
+          *machine->cxx, PyObject_IsTrue(train_biases));
+    }
+    else if (machine) {
+      self->cxx = new bob::learn::mlp::Trainer(batch_size, cost->cxx,
+          *machine->cxx);
+    }
+    else if (train_biases) {
+      PyErr_Format(PyExc_RuntimeError, "cannot provide a flag for `train_biases' and do not provide a `machine' upon initialisation of type `%s'", Py_TYPE(self)->tp_name);
+      return -1;
+    }
+    else {
+      self->cxx = new bob::learn::mlp::Trainer(batch_size, cost->cxx);
+    }
+  }
+  catch (std::exception& ex) {
+    PyErr_SetString(PyExc_RuntimeError, ex.what());
+    return -1;
+  }
+  catch (...) {
+    PyErr_Format(PyExc_RuntimeError, "cannot create new object of type `%s' - unknown exception thrown", Py_TYPE(self)->tp_name);
+    return -1;
+  }
+
+  return 0;
+
+}
+
+static int PyBobLearnMLPTrainer_init_copy
+(PyBobLearnMLPTrainerObject* self, PyObject* args, PyObject* kwds) {
+
+  /* Parses input arguments in a single shot */
+  static const char* const_kwlist[] = {"other", 0};
+  static char** kwlist = const_cast<char**>(const_kwlist);
+
+  PyObject* other = 0;
+
+  if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", kwlist,
+        &PyBobLearnMLPTrainer_Type, &other)) return -1;
+
+  auto copy = reinterpret_cast<PyBobLearnMLPTrainerObject*>(other);
+
+  try {
+    self->cxx = new bob::learn::mlp::Trainer(*(copy->cxx));
+  }
+  catch (std::exception& ex) {
+    PyErr_SetString(PyExc_RuntimeError, ex.what());
+    return -1;
+  }
+  catch (...) {
+    PyErr_Format(PyExc_RuntimeError, "cannot create new object of type `%s' - unknown exception thrown", Py_TYPE(self)->tp_name);
+    return -1;
+  }
+
+  return 0;
+
+}
+
+static int PyBobLearnMLPTrainer_init(PyBobLearnMLPTrainerObject* self,
+    PyObject* args, PyObject* kwds) {
+
+  Py_ssize_t nargs = (args?PyTuple_Size(args):0) + (kwds?PyDict_Size(kwds):0);
+
+  switch (nargs) {
+
+    case 1:
+
+      return PyBobLearnMLPTrainer_init_copy(self, args, kwds);
+
+    default:
+
+      return PyBobLearnMLPTrainer_init_discrete(self, args, kwds);
+  }
+
+  return -1;
+
+}
+
+static void PyBobLearnMLPTrainer_delete
+(PyBobLearnMLPTrainerObject* self) {
+
+  delete self->cxx;
+  Py_TYPE(self)->tp_free((PyObject*)self);
+
+}
+
+int PyBobLearnMLPTrainer_Check(PyObject* o) {
+  return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnMLPTrainer_Type));
+}
+
+static PyObject* PyBobLearnMLPTrainer_new
+(PyTypeObject* type, PyObject*, PyObject*) {
+
+  /* Allocates the python object itself */
+  PyBobLearnMLPTrainerObject* self = (PyBobLearnMLPTrainerObject*)type->tp_alloc(type, 0);
+
+  self->cxx = 0;
+
+  return reinterpret_cast<PyObject*>(self);
+
+}
+
+PyTypeObject PyBobLearnMLPTrainer_Type = {
+    PyVarObject_HEAD_INIT(0, 0)
+    s_trainer_str,                                 /* tp_name */
+    sizeof(PyBobLearnMLPTrainerObject),            /* tp_basicsize */
+    0,                                             /* tp_itemsize */
+    (destructor)PyBobLearnMLPTrainer_delete,       /* tp_dealloc */
+    0,                                             /* tp_print */
+    0,                                             /* tp_getattr */
+    0,                                             /* tp_setattr */
+    0,                                             /* tp_compare */
+    0, //(reprfunc)PyBobLearnMLPTrainer_Repr,           /* tp_repr */
+    0,                                             /* tp_as_number */
+    0,                                             /* tp_as_sequence */
+    0,                                             /* tp_as_mapping */
+    0,                                             /* tp_hash */
+    0,                                             /* tp_call */
+    0, //(reprfunc)PyBobLearnMLPTrainer_Repr,           /* tp_str */
+    0,                                             /* tp_getattro */
+    0,                                             /* tp_setattro */
+    0,                                             /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,      /* tp_flags */
+    s_trainer_doc,                                 /* tp_doc */
+    0,                                             /* tp_traverse */
+    0,                                             /* tp_clear */
+    0,                                             /* tp_richcompare */
+    0,                                             /* tp_weaklistoffset */
+    0,                                             /* tp_iter */
+    0,                                             /* tp_iternext */
+    0, //PyBobLearnMLPTrainer_methods,                  /* tp_methods */
+    0,                                             /* tp_members */
+    0, //PyBobLearnMLPTrainer_getseters,                /* tp_getset */
+    0,                                             /* tp_base */
+    0,                                             /* tp_dict */
+    0,                                             /* tp_descr_get */
+    0,                                             /* tp_descr_set */
+    0,                                             /* tp_dictoffset */
+    (initproc)PyBobLearnMLPTrainer_init,           /* tp_init */
+    0,                                             /* tp_alloc */
+    PyBobLearnMLPTrainer_new,                      /* tp_new */
+};