diff --git a/neural_network/include/ReversedMLP.hpp b/neural_network/include/ReversedMLP.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..e2287c37e07aca1798104009425b5e5ee94d92a2
--- /dev/null
+++ b/neural_network/include/ReversedMLP.hpp
@@ -0,0 +1,95 @@
+/// \file ReversedMLP.hpp
+/// \author Franck Dary
+/// @version 1.0
+/// @date 2019-08-08
+
+#ifndef REVERSEDMLP__H
+#define REVERSEDMLP__H
+
+#include "NeuralNetwork.hpp"
+#include "MLPBase.hpp"
+#include "ProgramParameters.hpp"
+
+/// @brief Classifier consisting in 2 MLP, can be trained with negative or positive classes.
+///
+/// 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 ReversedMLP : public NeuralNetwork
+{
+  private :
+
+  /// @brief The mlp that will be trained using positive gold classes.
+  MLPBase mlpPos;
+  /// @brief The mlp that will be trained using negative gold classes.
+  MLPBase mlpNeg;
+  /// @brief The training algorithm that will be used.
+  std::unique_ptr<dynet::Trainer> trainer;
+
+  public :
+
+  /// @brief initialize a new untrained ReversedMLP 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 ReversedMLP for training.
+  ReversedMLP();
+  /// @brief Read and construct a trained ReversedMLP from a file.
+  ///
+  /// The file must have been written by save.
+  /// @param filename The file to read the ReversedMLP from.
+  ReversedMLP(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 ReversedMLP to a file.
+  /// 
+  /// @param filename The file to write the ReversedMLP to.
+  void save(const std::string & filename) override;
+  /// @brief Print the topology (Layers) of the ReversedMLP.
+  ///
+  /// @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/MLPBase.cpp b/neural_network/src/MLPBase.cpp
index fe11c48ec3e7a605a59a956d266532f94ba77872..5ff466ed177a87f264a13314145830f6dc2c22fc 100644
--- a/neural_network/src/MLPBase.cpp
+++ b/neural_network/src/MLPBase.cpp
@@ -170,7 +170,7 @@ float MLPBase::update(FeatureModel::FeatureDescription & fd, const std::vector<f
     goldExpressions.emplace_back(dynet::input(cg, dynet::Dim({(unsigned int)gold.size()}), gold));
  
   dynet::Expression batchedGold = dynet::concatenate_to_batch(goldExpressions);
-  batchedLoss = dynet::sum_batches(dynet::l1_distance(output, batchedGold));
+  batchedLoss = dynet::sum_batches(dynet::squared_distance(output, batchedGold));
 
   cg.backward(batchedLoss);
 
@@ -260,7 +260,7 @@ float MLPBase::getLoss(FeatureModel::FeatureDescription & fd, const std::vector<
     goldExpressions.emplace_back(dynet::input(cg, dynet::Dim({1,(unsigned int)gold.size()}), gold));
  
   dynet::Expression batchedGold = dynet::concatenate_to_batch(goldExpressions);
-  batchedLoss = dynet::sum_batches(dynet::l1_distance(output, batchedGold));
+  batchedLoss = dynet::sum_batches(dynet::squared_distance(output, batchedGold));
 
   checkGradients();
 
diff --git a/neural_network/src/ReversedMLP.cpp b/neural_network/src/ReversedMLP.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8ed5aead75440e478cef89533592687c36900798
--- /dev/null
+++ b/neural_network/src/ReversedMLP.cpp
@@ -0,0 +1,158 @@
+#include "ReversedMLP.hpp"
+
+ReversedMLP::ReversedMLP() : mlpPos("MLP_POS"), mlpNeg("MLP_NEG")
+{
+  randomSeed = ProgramParameters::seed;
+  trainer.reset(createTrainer());
+  initDynet();
+}
+
+ReversedMLP::ReversedMLP(const std::string & filename) : mlpPos("MLP_POS"), mlpNeg("MLP_NEG")
+{
+  randomSeed = ProgramParameters::seed;
+  trainer.reset(createTrainer());
+  initDynet();
+
+  mlpPos.loadStruct(model, filename, 0);
+  mlpPos.loadParameters(model, filename);
+
+  mlpNeg.loadStruct(model, filename, 1);
+  mlpNeg.loadParameters(model, filename);
+}
+
+void ReversedMLP::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);
+  mlpPos.init(model, nbInputs, safeTopology, nbOutputs);
+  mlpNeg.init(model, nbInputs, safeTopology, nbOutputs);
+}
+
+dynet::Trainer * ReversedMLP::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> ReversedMLP::predict(FeatureModel::FeatureDescription & fd)
+{
+  auto predPos = mlpPos.predict(fd);
+  auto predNeg = mlpNeg.predict(fd);
+
+  for (unsigned int i = 0; i < predPos.size(); i++)
+    predPos[i] -= predNeg[i];
+
+  return predPos;
+}
+
+float ReversedMLP::update(FeatureModel::FeatureDescription & fd, int gold)
+{
+  mlpPos.setBatchSize(getBatchSize());
+  mlpNeg.setBatchSize(getBatchSize());
+  try
+  {
+    float loss = 0.0;
+    if (gold >= 0)
+    {
+      loss = mlpPos.update(fd, gold);
+    }
+    else
+    {
+      gold = -gold;
+      gold--;
+      loss = mlpPos.update(fd, gold);
+    }
+
+    trainer->update();
+    return loss;
+  } catch (BatchNotFull &)
+  {
+    return 0.0;
+  }
+}
+
+float ReversedMLP::update(FeatureModel::FeatureDescription &, const std::vector<float> &)
+{
+  fprintf(stderr, "ERROR (%s) : only classification is supported. Aborting.\n", ERRINFO);
+  exit(1);
+  return 0.0;
+}
+
+float ReversedMLP::getLoss(FeatureModel::FeatureDescription & fd, int gold)
+{
+  mlpPos.setBatchSize(getBatchSize());
+  mlpNeg.setBatchSize(getBatchSize());
+  try
+  {
+    float loss = 0.0;
+    if (gold >= 0)
+    {
+      loss = mlpPos.getLoss(fd, gold);
+    }
+    else
+    {
+      gold = -gold;
+      gold--;
+      loss = mlpPos.getLoss(fd, gold);
+    }
+    return loss;
+  } catch (BatchNotFull &)
+  {
+    return 0.0;
+  }
+}
+
+float ReversedMLP::getLoss(FeatureModel::FeatureDescription &, const std::vector<float> &)
+{
+  fprintf(stderr, "ERROR (%s) : only classification is supported. Aborting.\n", ERRINFO);
+  exit(1);
+  return 0.0;
+}
+
+void ReversedMLP::save(const std::string & filename)
+{
+  File * file = new File(filename, "w");
+  delete file;
+  mlpPos.saveStruct(filename);
+  mlpPos.saveParameters(filename);
+
+  mlpNeg.saveStruct(filename);
+  mlpNeg.saveParameters(filename);
+}
+
+void ReversedMLP::printTopology(FILE * output)
+{
+  mlpPos.printTopology(output);
+}
+
+void ReversedMLP::endOfIteration()
+{
+  mlpPos.endOfIteration();
+  mlpNeg.endOfIteration();
+}
+
diff --git a/trainer/src/Trainer.cpp b/trainer/src/Trainer.cpp
index 38c6a35a41b03a4287aa3bc1f98f47b490aa8a52..6e906845bb30db4dda380ad1889e9c34826832a3 100644
--- a/trainer/src/Trainer.cpp
+++ b/trainer/src/Trainer.cpp
@@ -404,26 +404,38 @@ void Trainer::doStepTrain()
 
         if (newCost >= lastCost)
         {
-//          loss = tm.getCurrentClassifier()->trainOnExample(pendingFD[tm.getCurrentClassifier()->name], tm.getCurrentClassifier()->getActionIndex("EPSILON"));
-          int nbActions = tm.getCurrentClassifier()->getNbActions();
-          int backIndex = tm.getCurrentClassifier()->getActionIndex(trainConfig.getCurrentStateHistory().top());
-          float value = 1.0 / (nbActions-1);
-          std::vector<float> goldOutput(nbActions, value);
-          goldOutput[backIndex] = 0.0;
+          if (true)
+          {
+            loss = tm.getCurrentClassifier()->trainOnExample(pendingFD[tm.getCurrentClassifier()->name], -(tm.getCurrentClassifier()->getActionIndex(trainConfig.getCurrentStateHistory().top())+1));
+          }
+          else
+          {
+            int nbActions = tm.getCurrentClassifier()->getNbActions();
+            int backIndex = tm.getCurrentClassifier()->getActionIndex(trainConfig.getCurrentStateHistory().top());
+            float value = 1.0 / (nbActions-1);
+            std::vector<float> goldOutput(nbActions, value);
+            goldOutput[backIndex] = 0.0;
 
-          loss = tm.getCurrentClassifier()->trainOnExample(pendingFD[tm.getCurrentClassifier()->name], goldOutput);
+            loss = tm.getCurrentClassifier()->trainOnExample(pendingFD[tm.getCurrentClassifier()->name], goldOutput);
+          }
 
           updateInfos = "predicted : <"+trainConfig.getCurrentStateHistory().top()+">, bad decision";
         }
         else
         {
-//loss = tm.getCurrentClassifier()->trainOnExample(pendingFD[tm.getCurrentClassifier()->name], tm.getCurrentClassifier()->getActionIndex(trainConfig.getCurrentStateHistory().top()));
-          int nbActions = tm.getCurrentClassifier()->getNbActions();
-          int backIndex = tm.getCurrentClassifier()->getActionIndex(trainConfig.getCurrentStateHistory().top());
-          std::vector<float> goldOutput(nbActions, 0.0);
-          goldOutput[backIndex] = 1.0;
+          if (true)
+          {
+            loss = tm.getCurrentClassifier()->trainOnExample(pendingFD[tm.getCurrentClassifier()->name], tm.getCurrentClassifier()->getActionIndex(trainConfig.getCurrentStateHistory().top()));
+          }
+          else
+          {
+            int nbActions = tm.getCurrentClassifier()->getNbActions();
+            int backIndex = tm.getCurrentClassifier()->getActionIndex(trainConfig.getCurrentStateHistory().top());
+            std::vector<float> goldOutput(nbActions, 0.0);
+            goldOutput[backIndex] = 1.0;
 
-          loss = tm.getCurrentClassifier()->trainOnExample(pendingFD[tm.getCurrentClassifier()->name], goldOutput);
+            loss = tm.getCurrentClassifier()->trainOnExample(pendingFD[tm.getCurrentClassifier()->name], goldOutput);
+          }
 
           updateInfos = "predicted : <"+trainConfig.getCurrentStateHistory().top()+">, good decision";
         }
diff --git a/transition_machine/src/Classifier.cpp b/transition_machine/src/Classifier.cpp
index ba054bd67bd0d65290121cf0afeb6f04e4569170..4468a93328bf83c6654b76a3e9cc3e41732a24c2 100644
--- a/transition_machine/src/Classifier.cpp
+++ b/transition_machine/src/Classifier.cpp
@@ -2,6 +2,7 @@
 #include "File.hpp"
 #include "util.hpp"
 #include "MLP.hpp"
+#include "ReversedMLP.hpp"
 #include "GeneticAlgorithm.hpp"
 
 Classifier::Classifier(const std::string & filename, bool trainMode)
@@ -372,6 +373,9 @@ NeuralNetwork * Classifier::createNeuralNetwork()
   if (splited.size() == 2)
     return new GeneticAlgorithm();
 
+  if (topology[0] == 'R')
+    return new ReversedMLP();
+
   return new MLP();
 }