From 0da7cec739ca3cf0cf4dd8fa32b8ca8fe7717f76 Mon Sep 17 00:00:00 2001
From: Franck Dary <franck.dary@etu.univ-amu.fr>
Date: Fri, 11 Jan 2019 16:44:47 +0100
Subject: [PATCH] Added a skeleton for a GeneticAlgorithm class

---
 neural_network/include/GeneticAlgorithm.hpp |  89 ++++++++++++++
 neural_network/include/MLP.hpp              |  73 +-----------
 neural_network/include/NeuralNetwork.hpp    |  71 ++++++++++++
 neural_network/src/GeneticAlgorithm.cpp     |  96 +++++++++++++++
 neural_network/src/MLP.cpp                  | 122 --------------------
 neural_network/src/NeuralNetwork.cpp        | 122 ++++++++++++++++++++
 6 files changed, 379 insertions(+), 194 deletions(-)
 create mode 100644 neural_network/include/GeneticAlgorithm.hpp
 create mode 100644 neural_network/src/GeneticAlgorithm.cpp

diff --git a/neural_network/include/GeneticAlgorithm.hpp b/neural_network/include/GeneticAlgorithm.hpp
new file mode 100644
index 0000000..caf3e6e
--- /dev/null
+++ b/neural_network/include/GeneticAlgorithm.hpp
@@ -0,0 +1,89 @@
+#ifndef GENETICALGORITHM__H
+#define GENETICALGORITHM__H
+
+#include <dynet/nodes.h>
+#include <dynet/dynet.h>
+#include <dynet/training.h>
+#include <dynet/timing.h>
+#include <dynet/expr.h>
+#include <dynet/io.h>
+#include <string>
+#include "NeuralNetwork.hpp"
+#include "FeatureModel.hpp"
+
+class GeneticAlgorithm : public NeuralNetwork
+{
+  private :
+
+  /// @brief An individual is a MLP
+  class Individual
+  {
+    private :
+
+    /// @brief The Layers of the MLP.
+    std::vector<Layer> layers;
+    /// @brief The parameters corresponding to the layers of the MLP.
+    std::vector< std::vector<dynet::Parameter> > parameters;
+
+    public :
+
+    /// @brief Create a new individual for the population.
+    ///
+    /// @param topology The topology the underlying MLP will take.
+    /// @param model The Collection of parameters of the GeneticAlgorithm.
+    /// @param nbInputs The size of the input layer of the MLP.
+    /// @param nbOutputs The size of the output layer of the MLP.
+    Individual(const std::string & topology, dynet::ParameterCollection & model, int nbInputs, int nbOutputs);
+  };
+
+  private :
+
+  /// @brief Load this GeneticAlgorithm from a file.
+  ///
+  /// @param filename The name of the file where the GeneticAlgorithm is stored.
+  void load(const std::string & filename);
+
+  public :
+
+  /// @brief Create a new untrained GeneticAlgorithm from scratch.
+  GeneticAlgorithm();
+
+  /// @brief Create and load an already trained GeneticAlgorithm from a file.
+  ///
+  /// @param filename The file where the GeneticAlgorithm is stored.
+  GeneticAlgorithm(const std::string & filename);
+
+  /// @brief initialize a new untrained GeneticAlgorithm from a desired topology.
+  ///
+  /// @param nbInputs The size of the input.
+  /// @param topology Description of the GeneticAlgorithm.
+  /// @param nbOutputs The size of the output.
+  void init(int nbInputs, const std::string & topology, int nbOutputs) override;
+
+  /// @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 Save the GeneticAlgorithm to a file.
+  /// 
+  /// @param filename The file to write the GeneticAlgorithm to.
+  void save(const std::string & filename) override;
+
+  /// @brief Print the topology of the GeneticAlgorithm.
+  ///
+  /// @param output Where the topology will be printed.
+  void printTopology(FILE * output) override;
+};
+
+#endif
diff --git a/neural_network/include/MLP.hpp b/neural_network/include/MLP.hpp
index fab20cf..5c73975 100644
--- a/neural_network/include/MLP.hpp
+++ b/neural_network/include/MLP.hpp
@@ -15,62 +15,6 @@
 /// Once trained, it can also be used to predict the class of a certain input.
 class MLP : public NeuralNetwork
 {
-  public :
-
-  /// @brief Activation function for a MLP Layer.
-  enum Activation
-  {
-    SIGMOID,
-    TANH,
-    RELU,
-    ELU,
-    LINEAR,
-    SPARSEMAX,
-    CUBE,
-    SOFTMAX
-  };
-
-  /// @brief Get the string corresponding to an Activation.
-  ///
-  /// @param a The activation.
-  ///
-  /// @return The string corresponding to a.
-  static std::string activation2str(Activation a);
-
-  /// @brief Get the Activation corresponding to a string.
-  ///
-  /// @param s The string.
-  ///
-  /// @return The Activation corresponding to s. If s is unknown, the program abort.
-  static Activation str2activation(std::string s);
-
-  /// @brief A simple struct that represents a MLP Layer.
-  struct Layer
-  {
-    /// @brief Number of input neurons of this Layer.
-    int input_dim;
-    /// @brief Number of output neurons of this Layer.
-    int output_dim;
-
-    /// @brief The dropout rate to apply to this Layer when training.
-    float dropout_rate;
-    /// @brief The activation function for this Layer.
-    Activation activation;
-
-    /// @brief Construct a new Layer
-    ///
-    /// @param input_dim 
-    /// @param output_dim
-    /// @param dropout_rate
-    /// @param activation
-    Layer(int input_dim, int output_dim,
-      float dropout_rate, Activation activation);
-    /// @brief Print a description of this Layer.
-    ///
-    /// @param file Where to print the output.
-    void print(FILE * file);
-  };
-
   private :
 
   /// @brief The Layers of the MLP.
@@ -96,10 +40,6 @@ class MLP : public NeuralNetwork
   void addLayerToModel(Layer & layer);
   /// @brief Abort the program if the layers are not compatible.
   void checkLayersCompatibility();
-  /// @brief Set dynet and srand() seeds.
-  ///
-  /// @return The DynetParams containing the set seed.
-  dynet::DynetParams & getDefaultParams();
   /// @brief Compute the image of input x by the Multi Layer Perceptron.
   ///
   /// @param cg The current computation graph.
@@ -107,13 +47,6 @@ class MLP : public NeuralNetwork
   ///
   /// @return The result (values of the output Layer) of the computation of x by the Multi Layer Perceptron.
   dynet::Expression run(dynet::ComputationGraph & cg, dynet::Expression x);
-  /// @brief Compute the image of an expression by an activation function.
-  ///
-  /// @param h The expression we want the image of.
-  /// @param f The activation function.
-  ///
-  /// @return f(h)
-  inline dynet::Expression activate(dynet::Expression h, Activation f);
   /// @brief Print the parameters.
   ///
   /// @param output Where the parameters will be printed to.
@@ -146,10 +79,6 @@ class MLP : public NeuralNetwork
   /// This function will use loadStruct and loadParameters.
   /// @param filename The file from which the MLP will be loaded.
   void load(const std::string & filename);
-  /// @brief Initialize the dynet library.
-  ///
-  /// Must be called only once, and before any call to dynet functions.
-  void initDynet();
   /// @brief Get the loss expression 
   ///
   /// @param output Output from the neural network
@@ -170,7 +99,7 @@ class MLP : public NeuralNetwork
   /// @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);
+  void init(int nbInputs, const std::string & topology, int nbOutputs) override;
   /// @brief Construct a new MLP for training.
   MLP();
   /// @brief Read and construct a trained MLP from a file.
diff --git a/neural_network/include/NeuralNetwork.hpp b/neural_network/include/NeuralNetwork.hpp
index 00e2fd1..9f6a6df 100644
--- a/neural_network/include/NeuralNetwork.hpp
+++ b/neural_network/include/NeuralNetwork.hpp
@@ -14,6 +14,62 @@ class NeuralNetwork
 {
   protected :
 
+  /// @brief Activation function for a Layer.
+  enum Activation
+  {
+    SIGMOID,
+    TANH,
+    RELU,
+    ELU,
+    LINEAR,
+    SPARSEMAX,
+    CUBE,
+    SOFTMAX
+  };
+
+  /// @brief Get the string corresponding to an Activation.
+  ///
+  /// @param a The activation.
+  ///
+  /// @return The string corresponding to a.
+  static std::string activation2str(Activation a);
+
+  /// @brief Get the Activation corresponding to a string.
+  ///
+  /// @param s The string.
+  ///
+  /// @return The Activation corresponding to s. If s is unknown, the program abort.
+  static Activation str2activation(std::string s);
+
+  /// @brief A simple struct that represents a Layer.
+  struct Layer
+  {
+    /// @brief Number of input neurons of this Layer.
+    int input_dim;
+    /// @brief Number of output neurons of this Layer.
+    int output_dim;
+
+    /// @brief The dropout rate to apply to this Layer when training.
+    float dropout_rate;
+    /// @brief The activation function for this Layer.
+    Activation activation;
+
+    /// @brief Construct a new Layer
+    ///
+    /// @param input_dim 
+    /// @param output_dim
+    /// @param dropout_rate
+    /// @param activation
+    Layer(int input_dim, int output_dim,
+      float dropout_rate, Activation activation);
+    /// @brief Print a description of this Layer.
+    ///
+    /// @param file Where to print the output.
+    void print(FILE * file);
+  };
+
+  protected :
+
   /// @brief The seed that will be used by RNG (srand and dynet)
   static int randomSeed;
 
@@ -32,6 +88,21 @@ class NeuralNetwork
   ///
   /// @return A dynet Expression of value fv that can be used as an input in the NeuralNetwork 
   dynet::Expression featValue2Expression(dynet::ComputationGraph & cg, const FeatureModel::FeatureValue & fv);
+  /// @brief Set dynet and srand() seeds.
+  ///
+  /// @return The DynetParams containing the set seed.
+  dynet::DynetParams & getDefaultParams();
+  /// @brief Initialize the dynet library.
+  ///
+  /// Must be called only once, and before any call to dynet functions.
+  void initDynet();
+  /// @brief Compute the image of an expression by an activation function.
+  ///
+  /// @param h The expression we want the image of.
+  /// @param f The activation function.
+  ///
+  /// @return f(h)
+  dynet::Expression activate(dynet::Expression h, Activation f);
 
   public :
 
diff --git a/neural_network/src/GeneticAlgorithm.cpp b/neural_network/src/GeneticAlgorithm.cpp
new file mode 100644
index 0000000..6194b70
--- /dev/null
+++ b/neural_network/src/GeneticAlgorithm.cpp
@@ -0,0 +1,96 @@
+#include "GeneticAlgorithm.hpp"
+#include "ProgramParameters.hpp"
+
+GeneticAlgorithm::GeneticAlgorithm()
+{
+  randomSeed = ProgramParameters::seed;
+  initDynet();
+}
+
+GeneticAlgorithm::GeneticAlgorithm(const std::string & filename)
+{
+  randomSeed = ProgramParameters::seed;
+  initDynet();
+
+  load(filename);
+}
+
+void GeneticAlgorithm::init(int nbInputs, const std::string & topology, int nbOutputs)
+{
+  fprintf(stderr, "init of genetic\n");
+}
+
+std::vector<float> GeneticAlgorithm::predict(FeatureModel::FeatureDescription & fd)
+{
+
+}
+
+float GeneticAlgorithm::update(FeatureModel::FeatureDescription & fd, int gold)
+{
+
+}
+
+void GeneticAlgorithm::save(const std::string & filename)
+{
+
+}
+
+void GeneticAlgorithm::printTopology(FILE * output)
+{
+
+}
+
+void GeneticAlgorithm::load(const std::string & filename)
+{
+
+}
+
+GeneticAlgorithm::Individual::Individual(const std::string & topology, dynet::ParameterCollection & model, int nbInputs, int nbOutputs)
+{
+  std::string topo = topology;
+  std::replace(topo.begin(), topo.end(), '(', ' ');
+  std::replace(topo.begin(), topo.end(), ')', ' ');
+
+  auto groups = split(topo);
+  for (auto group : groups)
+  {
+    if(group.empty())
+      continue;
+
+    std::replace(group.begin(), group.end(), ',', ' ');
+    auto layer = split(group);
+
+    if (layer.size() != 2)
+    {
+      fprintf(stderr, "ERROR (%s) : invalid topology \'%s\'. Aborting.\n", ERRINFO, topology.c_str());
+      exit(1);
+    }
+
+    int input = layers.empty() ? nbInputs : layers.back().output_dim;
+    int output = std::stoi(layer[0]); 
+    layers.emplace_back(input, output, 0, str2activation(layer[1]));
+  }
+
+  layers.emplace_back(layers.back().output_dim, nbOutputs, 0.0, Activation::LINEAR);
+
+  if(layers.empty())
+  {
+    fprintf(stderr, "ERROR (%s) : constructed mlp with 0 layers. Aborting.\n", ERRINFO);
+    exit(1);
+  }
+
+  for(unsigned int i = 0; i < layers.size()-1; i++)
+    if(layers[i].output_dim != layers[i+1].input_dim)
+    {
+      fprintf(stderr, "ERROR (%s) : constructed mlp with incompatible layers. Aborting.\n", ERRINFO);
+      exit(1);
+    }
+
+  for (auto & layer : layers)
+  {
+    dynet::Parameter W = model.add_parameters({(unsigned)layer.output_dim, (unsigned)layer.input_dim});
+    dynet::Parameter b = model.add_parameters({(unsigned)layer.output_dim});
+    parameters.push_back({W,b});
+  }
+}
+
diff --git a/neural_network/src/MLP.cpp b/neural_network/src/MLP.cpp
index add22c6..39f6ecb 100644
--- a/neural_network/src/MLP.cpp
+++ b/neural_network/src/MLP.cpp
@@ -1,76 +1,5 @@
 #include "MLP.hpp"
 
-std::string MLP::activation2str(Activation a)
-{
-  switch(a)
-  {
-    case LINEAR :
-      return "LINEAR";
-      break;
-    case RELU :
-      return "RELU";
-      break;
-    case ELU :
-      return "ELU";
-      break;
-    case CUBE :
-      return "CUBE";
-      break;
-    case SIGMOID :
-      return "SIGMOID";
-      break;
-    case TANH :
-      return "TANH";
-      break;
-    case SOFTMAX :
-      return "SOFTMAX";
-      break;
-    case SPARSEMAX :
-      return "SPARSEMAX";
-      break;
-    default :
-      break;
-  }
-
-  return "UNKNOWN";
-}
-
-MLP::Activation MLP::str2activation(std::string s)
-{
-  if(s == "LINEAR")
-    return LINEAR;
-  else if(s == "RELU")
-    return RELU;
-  else if(s == "ELU")
-    return ELU;
-  else if(s == "CUBE")
-    return CUBE;
-  else if(s == "SIGMOID")
-    return SIGMOID;
-  else if(s == "TANH")
-    return TANH;
-  else if(s == "SOFTMAX")
-    return SOFTMAX;
-  else if(s == "SPARSEMAX")
-    return SPARSEMAX;
-  else
-  {
-    fprintf(stderr, "ERROR (%s) : invalid activation \'%s\'. Aborting\n",ERRINFO, s.c_str());
-    exit(1);
-  }
-
-  return LINEAR;
-}
-
-void MLP::initDynet()
-{
-  if(dynetIsInit)
-    return;
-
-  dynetIsInit = true;
-  dynet::initialize(getDefaultParams());
-}
-
 MLP::MLP()
 {
   randomSeed = ProgramParameters::seed;
@@ -162,15 +91,6 @@ void MLP::checkLayersCompatibility()
     }
 }
 
-MLP::Layer::Layer(int input_dim, int output_dim,
-                  float dropout_rate, Activation activation)
-{
-  this->input_dim = input_dim;
-  this->output_dim = output_dim;
-  this->dropout_rate = dropout_rate;
-  this->activation = activation;
-}
-
 std::vector<float> MLP::predict(FeatureModel::FeatureDescription & fd)
 {
   bool currentDropoutActive = dropoutActive;
@@ -284,16 +204,6 @@ dynet::Expression MLP::errorCorrectionLoss(dynet::ComputationGraph & cg, dynet::
   return dynet::sum(lossExpr);
 }
 
-dynet::DynetParams & MLP::getDefaultParams()
-{
-  static dynet::DynetParams params;
-  params.random_seed = randomSeed;
-
-  std::srand(params.random_seed);
-
-  return params;
-}
-
 dynet::Expression MLP::run(dynet::ComputationGraph & cg, dynet::Expression x)
 {
   static std::vector< std::pair<std::string,dynet::Expression> > exprForDebug;
@@ -378,38 +288,6 @@ dynet::Expression MLP::run(dynet::ComputationGraph & cg, dynet::Expression x)
   return h_cur;
 }
 
-inline dynet::Expression MLP::activate(dynet::Expression h, Activation f)
-{
-  switch(f)
-  {
-    case LINEAR :
-      return h;
-      break;
-    case RELU :
-      return rectify(h);
-      break;
-    case ELU :
-      return elu(h);
-      break;
-    case SIGMOID :
-      return logistic(h);
-      break;
-    case TANH :
-      return tanh(h);
-      break;
-    case SOFTMAX :
-      return softmax(h);
-      break;
-    default :
-      break;
-  }
-
-  fprintf(stderr, "ERROR (%s) : Activation not implemented \'%s\'. Aborting.\n", ERRINFO, activation2str(f).c_str());
-  exit(1);
-
-  return h;
-}
-
 void MLP::printParameters(FILE * output)
 {
   fprintf(output, "Parameters : NOT IMPLEMENTED\n");
diff --git a/neural_network/src/NeuralNetwork.cpp b/neural_network/src/NeuralNetwork.cpp
index d12adfc..75a8ccb 100644
--- a/neural_network/src/NeuralNetwork.cpp
+++ b/neural_network/src/NeuralNetwork.cpp
@@ -44,3 +44,125 @@ dynet::Expression NeuralNetwork::featValue2Expression(dynet::ComputationGraph &
   return dynet::concatenate(expressions);
 }
 
+dynet::DynetParams & NeuralNetwork::getDefaultParams()
+{
+  static dynet::DynetParams params;
+  params.random_seed = randomSeed;
+
+  std::srand(params.random_seed);
+
+  return params;
+}
+
+void NeuralNetwork::initDynet()
+{
+  if(dynetIsInit)
+    return;
+
+  dynetIsInit = true;
+  dynet::initialize(getDefaultParams());
+}
+
+std::string NeuralNetwork::activation2str(Activation a)
+{
+  switch(a)
+  {
+    case LINEAR :
+      return "LINEAR";
+      break;
+    case RELU :
+      return "RELU";
+      break;
+    case ELU :
+      return "ELU";
+      break;
+    case CUBE :
+      return "CUBE";
+      break;
+    case SIGMOID :
+      return "SIGMOID";
+      break;
+    case TANH :
+      return "TANH";
+      break;
+    case SOFTMAX :
+      return "SOFTMAX";
+      break;
+    case SPARSEMAX :
+      return "SPARSEMAX";
+      break;
+    default :
+      break;
+  }
+
+  return "UNKNOWN";
+}
+
+NeuralNetwork::Activation NeuralNetwork::str2activation(std::string s)
+{
+  if(s == "LINEAR")
+    return LINEAR;
+  else if(s == "RELU")
+    return RELU;
+  else if(s == "ELU")
+    return ELU;
+  else if(s == "CUBE")
+    return CUBE;
+  else if(s == "SIGMOID")
+    return SIGMOID;
+  else if(s == "TANH")
+    return TANH;
+  else if(s == "SOFTMAX")
+    return SOFTMAX;
+  else if(s == "SPARSEMAX")
+    return SPARSEMAX;
+  else
+  {
+    fprintf(stderr, "ERROR (%s) : invalid activation \'%s\'. Aborting\n",ERRINFO, s.c_str());
+    exit(1);
+  }
+
+  return LINEAR;
+}
+
+NeuralNetwork::Layer::Layer(int input_dim, int output_dim,
+                            float dropout_rate, Activation activation)
+{
+  this->input_dim = input_dim;
+  this->output_dim = output_dim;
+  this->dropout_rate = dropout_rate;
+  this->activation = activation;
+}
+
+dynet::Expression NeuralNetwork::activate(dynet::Expression h, Activation f)
+{
+  switch(f)
+  {
+    case LINEAR :
+      return h;
+      break;
+    case RELU :
+      return rectify(h);
+      break;
+    case ELU :
+      return elu(h);
+      break;
+    case SIGMOID :
+      return logistic(h);
+      break;
+    case TANH :
+      return tanh(h);
+      break;
+    case SOFTMAX :
+      return softmax(h);
+      break;
+    default :
+      break;
+  }
+
+  fprintf(stderr, "ERROR (%s) : Activation not implemented \'%s\'. Aborting.\n", ERRINFO, activation2str(f).c_str());
+  exit(1);
+
+  return h;
+}
+
-- 
GitLab