Learn-to-Compress / headers / Layer.h
Layer.h
Raw
//============================================================================
// Name : Layer.h
// Author : David Nogueira
//============================================================================
#ifndef LAYER_H
#define LAYER_H

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <sstream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <cassert> // for assert()
#include "MLPNode.h"
#include "MLPUtils.h"

class Layer {
public:
  Layer() {
    m_num_nodes = 0;
    m_nodes.clear();
  };

  Layer(int num_inputs_per_node,
        int num_nodes,
        const std::string & activation_function,
        bool use_constant_weight_init = true,
        double constant_weight_init = 0.5) {
    m_num_inputs_per_node = num_inputs_per_node;
    m_num_nodes = num_nodes;
    m_nodes.resize(num_nodes);

    for (int i = 0; i < num_nodes; i++) {
      m_nodes[i].WeightInitialization(num_inputs_per_node,
                                      use_constant_weight_init,
                                      constant_weight_init);
    }

    std::pair<std::function<double(double)>,
      std::function<double(double)> > *pair;

    m_activation_function = (*pair).first;
    m_deriv_activation_function = (*pair).second;
    m_activation_function_str = activation_function;
  };

  ~Layer() {
    m_num_inputs_per_node = 0;
    m_num_nodes = 0;
    m_nodes.clear();
  };

  int GetInputSize() const {
    return m_num_inputs_per_node;
  };

  int GetOutputSize() const {
    return m_num_nodes;
  };

  const std::vector<MLPNode> & GetNodes() const {
    return m_nodes;
  }

  /**
   * Return the internal list of nodes, but modifiable.
   */
  std::vector<MLPNode> & GetNodesChangeable() {
    return m_nodes;
  }


  void GetOutputAfterActivationFunction(const std::vector<double> &input,
                                        std::vector<double> * output) const {
    assert(input.size() == m_num_inputs_per_node);

    output->resize(m_num_nodes);

    for (size_t i = 0; i < m_num_nodes; ++i) {
      m_nodes[i].GetOutputAfterActivationFunction(input,
                                                  m_activation_function,
                                                  &((*output)[i]));
    }
  }

  void UpdateWeights(const std::vector<double> &input_layer_activation,
                     const std::vector<double> &deriv_error,
                     double m_learning_rate,
                     std::vector<double> * deltas) {
    assert(input_layer_activation.size() == m_num_inputs_per_node);
    assert(deriv_error.size() == m_nodes.size());

    deltas->resize(m_num_inputs_per_node, 0);

    for (size_t i = 0; i < m_nodes.size(); i++) {
      double net_sum;
      m_nodes[i].GetInputInnerProdWithWeights(input_layer_activation,
                                              &net_sum);

      //dE/dwij = dE/doj . doj/dnetj . dnetj/dwij
      double dE_doj = 0.0;
      double doj_dnetj = 0.0;
      double dnetj_dwij = 0.0;

      dE_doj = deriv_error[i];
      doj_dnetj = m_deriv_activation_function(net_sum);

      for (size_t j = 0; j < m_num_inputs_per_node; j++) {
        (*deltas)[j] += dE_doj * doj_dnetj * m_nodes[i].GetWeights()[j];

        dnetj_dwij = input_layer_activation[j];

        m_nodes[i].UpdateWeight(j,
                                -(dE_doj * doj_dnetj * dnetj_dwij),
                                m_learning_rate);
      }
    }
  };


  void SetWeights( std::vector<std::vector<double>> & weights )
  {
      if( 0 <= weights.size() && weights.size() <= m_num_nodes )
      {
          // traverse the list of nodes
          size_t node_i = 0;
          for( MLPNode & node : m_nodes )
          {
              node.SetWeights( weights[node_i] );
              node_i++;
          }
      }
      else
          throw new std::logic_error("Incorrect layer number in SetWeights call");
  };

  void SaveLayer(FILE * file) const {
    fwrite(&m_num_nodes, sizeof(m_num_nodes), 1, file);
    fwrite(&m_num_inputs_per_node, sizeof(m_num_inputs_per_node), 1, file);

    size_t str_size = m_activation_function_str.size();
    fwrite(&str_size, sizeof(size_t), 1, file);
    fwrite(m_activation_function_str.c_str(), sizeof(char), str_size, file);

    for (size_t i = 0; i < m_nodes.size(); i++) {
      m_nodes[i].SaveNode(file);
    }
  };
  void LoadLayer(FILE * file) {
    m_nodes.clear();

    fread(&m_num_nodes, sizeof(m_num_nodes), 1, file);
    fread(&m_num_inputs_per_node, sizeof(m_num_inputs_per_node), 1, file);

    size_t str_size = 0;
    fread(&str_size, sizeof(size_t), 1, file);
    m_activation_function_str.resize(str_size);
    fread(&(m_activation_function_str[0]), sizeof(char), str_size, file);

    std::pair<std::function<double(double)>,
      std::function<double(double)> > *pair;

    m_activation_function = (*pair).first;
    m_deriv_activation_function = (*pair).second;
    
    m_nodes.resize(m_num_nodes);
    for (size_t i = 0; i < m_nodes.size(); i++) {
      m_nodes[i].LoadNode(file);
    }

  };

protected:
  size_t m_num_inputs_per_node{ 0 };
  size_t m_num_nodes{ 0 };
  std::vector<MLPNode> m_nodes;

  std::string m_activation_function_str;
  std::function<double(double)>  m_activation_function;
  std::function<double(double)>  m_deriv_activation_function;
};

#endif //LAYER_H