diff --git a/neural_network/include/MultiMLP.hpp b/neural_network/include/MultiMLP.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..d3d8f741a33fe63886e9379507ce416296fb9509
--- /dev/null
+++ b/neural_network/include/MultiMLP.hpp
@@ -0,0 +1,93 @@
+/// \file MultiMLP.hpp
+/// \author Franck Dary
+/// @version 1.0
+/// @date 2019-08-12
+
+#ifndef MULTIMLP__H
+#define MULTIMLP__H
+
+#include "NeuralNetwork.hpp"
+#include "MLPBase.hpp"
+#include "ProgramParameters.hpp"
+
+/// @brief Classifier consisting in 1 MLP per possible class.
+///
+/// It is capable of training itself given a batch of examples.\n
+/// Once trained, it can also be used to predict the class of a certain input.
+class MultiMLP : public NeuralNetwork
+{
+  private :
+
+  /// @brief The mlps that will be trained to each recognize one class.
+  std::vector<MLPBase> mlps;
+  /// @brief The training algorithm that will be used.
+  std::unique_ptr<dynet::Trainer> trainer;
+
+  public :
+
+  /// @brief initialize a new untrained MultiMLP from a desired topology.
+  ///
+  /// topology example for 2 hidden layers : (150,RELU,0.3)(50,ELU,0.2)\n
+  /// Of sizes 150 and 50, activation functions RELU and ELU, and dropout rates
+  /// of 0.3 and 0.2.
+  /// @param nbInputs The size of the input layer of the MLP.
+  /// @param topology Description of each hidden Layer of the MLP.
+  /// @param nbOutputs The size of the output layer of the MLP.
+  void init(int nbInputs, const std::string & topology, int nbOutputs) override;
+  /// @brief Construct a new MultiMLP for training.
+  MultiMLP();
+  /// @brief Read and construct a trained MultiMLP from a file.
+  ///
+  /// The file must have been written by save.
+  /// @param filename The file to read the MultiMLP from.
+  MultiMLP(const std::string & filename);
+  /// @brief Give a score to each possible class, given an input.
+  ///
+  /// @param fd The input to use.
+  ///
+  /// @return A vector containing one score per possible class.
+  std::vector<float> predict(FeatureModel::FeatureDescription & fd) override;
+  /// @brief Update the parameters according to the given gold class.
+  ///
+  /// @param fd The input to use.
+  /// @param gold The gold class of this input.
+  ///
+  /// @return The loss.
+  float update(FeatureModel::FeatureDescription & fd, int gold) override;
+  /// @brief Get the loss according to the given gold class.
+  ///
+  /// @param fd The input to use.
+  /// @param gold The gold class of this input.
+  ///
+  /// @return The loss.
+  float update(FeatureModel::FeatureDescription & fd, const std::vector<float> & gold) override;
+  /// @brief Get the loss according to the given gold class.
+  ///
+  /// @param fd The input to use.
+  /// @param gold The gold class of this input.
+  ///
+  /// @return The loss.
+  float getLoss(FeatureModel::FeatureDescription & fd, int gold) override;
+  /// @brief Get the loss according to the given gold vector.
+  ///
+  /// @param fd The input to use.
+  /// @param gold The gold vector for this input.
+  ///
+  /// @return The loss.
+  float getLoss(FeatureModel::FeatureDescription & fd, const std::vector<float> & gold) override;
+  /// @brief Save the MultiMLP to a file.
+  /// 
+  /// @param filename The file to write the MultiMLP to.
+  void save(const std::string & filename) override;
+  /// @brief Print the topology (Layers) of the MultiMLP.
+  ///
+  /// @param output Where the topology will be printed.
+  void printTopology(FILE * output) override;
+  /// @brief Allocate the correct trainer type depending on the program parameters.
+  ///
+  /// @return A pointer to the newly allocated trainer.
+  dynet::Trainer * createTrainer();
+  void endOfIteration();
+};
+
+#endif
diff --git a/neural_network/src/MultiMLP.cpp b/neural_network/src/MultiMLP.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..03d4f73871bd9339663b333c00c2e5e6c7c71f4f
--- /dev/null
+++ b/neural_network/src/MultiMLP.cpp
@@ -0,0 +1,170 @@
+#include "MultiMLP.hpp"
+
+MultiMLP::MultiMLP()
+{
+  randomSeed = ProgramParameters::seed;
+  trainer.reset(createTrainer());
+  initDynet();
+}
+
+MultiMLP::MultiMLP(const std::string & filename)
+{
+  randomSeed = ProgramParameters::seed;
+  trainer.reset(createTrainer());
+  initDynet();
+
+  File * file = new File(filename, "r");
+  int nbMlps;
+  if (fscanf(file->getDescriptor(), "#NBMLP:%d# # {1,1} 0 _\n", &nbMlps) != 1)
+  {
+    fprintf(stderr, "ERROR (%s) : Ill formated model, aborting.\n", ERRINFO);
+    exit(1);
+  }
+  delete file;
+
+  for (int i = 0; i < nbMlps; i++)
+  {
+    mlps.emplace_back(std::string("MLP_")+std::to_string(i));
+    mlps.back().loadStruct(model, filename, i);
+    mlps.back().loadParameters(model, filename);   
+  }
+}
+
+void MultiMLP::init(int nbInputs, const std::string & topology, int nbOutputs)
+{
+  std::string safeTopology = "";
+  for (unsigned int i = 1; i < topology.size(); i++)
+    safeTopology.push_back(topology[i]);
+
+  setBatchSize(0);
+  if (mlps.empty())
+  {
+    for (int i = 0; i < nbOutputs; i++)
+      mlps.emplace_back(std::string("MLP_")+std::to_string(i));
+  }
+
+  for (auto & mlp : mlps)
+    mlp.init(model, nbInputs, safeTopology, 2);
+}
+
+dynet::Trainer * MultiMLP::createTrainer()
+{
+  auto optimizer = noAccentLower(ProgramParameters::optimizer);
+
+  dynet::Trainer * trainer = nullptr;
+
+  if (optimizer == "amsgrad")
+    trainer = new dynet::AmsgradTrainer(model, ProgramParameters::learningRate, ProgramParameters::beta1, ProgramParameters::beta2, ProgramParameters::bias);
+  else if (optimizer == "adam")
+    trainer =  new dynet::AdamTrainer(model, ProgramParameters::learningRate, ProgramParameters::beta1, ProgramParameters::beta2, ProgramParameters::bias);
+  else if (optimizer == "sgd")
+    trainer =  new dynet::SimpleSGDTrainer(model, ProgramParameters::learningRate);
+  else if (optimizer == "none")
+    return nullptr;
+
+  if (trainer)
+  {
+    trainer->sparse_updates_enabled = true;
+    return trainer;
+  }
+
+  fprintf(stderr, "ERROR (%s) : unknown optimizer \'%s\'. Aborting.\n", ERRINFO, optimizer.c_str());
+
+  exit(1);
+
+  return nullptr;
+}
+
+std::vector<float> MultiMLP::predict(FeatureModel::FeatureDescription & fd)
+{
+  std::vector<float> prediction(mlps.size());
+  for (unsigned int i = 0; i < mlps.size(); i++)
+  {
+    int id = std::stoi(split(mlps[i].name, '_')[1]);
+    auto value = mlps[i].predict(fd);
+    prediction[id] = value[1];
+  }
+
+  return prediction;
+}
+
+float MultiMLP::update(FeatureModel::FeatureDescription & fd, int gold)
+{
+  try
+  {
+    for (auto & mlp : mlps)
+    {
+      int id = std::stoi(split(mlp.name, '_')[1]);
+      float loss = 0.0;
+      mlp.setBatchSize(getBatchSize());
+      loss = mlp.update(fd, id == gold ? 1 : 0);
+
+      trainer->update();
+      return loss;
+    }
+  } catch (BatchNotFull &)
+  {
+    return 0.0;
+  }
+
+  return 0.0;
+}
+
+float MultiMLP::update(FeatureModel::FeatureDescription &, const std::vector<float> &)
+{
+  fprintf(stderr, "ERROR (%s) : only classification is supported. Aborting.\n", ERRINFO);
+  exit(1);
+  return 0.0;
+}
+
+float MultiMLP::getLoss(FeatureModel::FeatureDescription & fd, int gold)
+{
+  float loss = 0.0;
+  try
+  {
+    for (auto & mlp : mlps)
+    {
+      int id = std::stoi(split(mlp.name, '_')[1]);
+      mlp.setBatchSize(getBatchSize());
+      loss += mlp.getLoss(fd, id == gold ? 1 : 0);
+
+      trainer->update();
+    }
+  } catch (BatchNotFull &)
+  {
+    return 0.0;
+  }
+
+  return loss;
+}
+
+float MultiMLP::getLoss(FeatureModel::FeatureDescription &, const std::vector<float> &)
+{
+  fprintf(stderr, "ERROR (%s) : only classification is supported. Aborting.\n", ERRINFO);
+  exit(1);
+  return 0.0;
+}
+
+void MultiMLP::save(const std::string & filename)
+{
+  File * file = new File(filename, "w");
+  fprintf(file->getDescriptor(), "#NBMLP:%lu# # {1,1} 0 _\n", mlps.size());
+  delete file;
+  for (auto & mlp : mlps)
+  {
+    mlp.saveStruct(filename);
+    mlp.saveParameters(filename);
+  }
+}
+
+void MultiMLP::printTopology(FILE * output)
+{
+  mlps.back().printTopology(output);
+}
+
+void MultiMLP::endOfIteration()
+{
+  for (auto & mlp : mlps)
+    mlp.endOfIteration();
+}
+
diff --git a/transition_machine/src/Classifier.cpp b/transition_machine/src/Classifier.cpp
index da91fa8228bba87befa3459b89831c3be5f10b35..d56c666257dbe20e1ec81fa9a9ad7d3130dc1797 100644
--- a/transition_machine/src/Classifier.cpp
+++ b/transition_machine/src/Classifier.cpp
@@ -3,6 +3,7 @@
 #include "util.hpp"
 #include "MLP.hpp"
 #include "ReversedMLP.hpp"
+#include "MultiMLP.hpp"
 #include "GeneticAlgorithm.hpp"
 
 Classifier::Classifier(const std::string & filename, bool trainMode)
@@ -376,6 +377,9 @@ NeuralNetwork * Classifier::createNeuralNetwork()
   if (topology[0] == 'R')
     return new ReversedMLP();
 
+  if (topology[0] == 'M')
+    return new MultiMLP();
+
   return new MLP();
 }