Make nntrainer shared library
authorjijoong.moon <jijoong.moon@samsung.com>
Wed, 5 Feb 2020 08:04:33 +0000 (17:04 +0900)
committer문지중/On-Device Lab(SR)/Principal Engineer/삼성전자 <jijoong.moon@samsung.com>
Fri, 7 Feb 2020 05:09:02 +0000 (14:09 +0900)
Change Directory structure and make nntrainer shared library,
nntrainer.so

**Self evaluation:**
1. Build test:  [X]Passed [ ]Failed [ ]Skipped
2. Run test:  [X]Passed [ ]Failed [ ]Skipped

Signed-off-by: jijoong.moon <jijoong.moon@samsung.com>
CMakeLists.txt [new file with mode: 0644]
external/iniparser [new submodule]
include/layers.h [new file with mode: 0644]
include/matrix.h [new file with mode: 0644]
include/neuralnet.h [new file with mode: 0644]
src/layers.cpp [new file with mode: 0644]
src/matrix.cpp [new file with mode: 0644]
src/neuralnet.cpp [new file with mode: 0644]

diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..c12d7f4
--- /dev/null
@@ -0,0 +1,28 @@
+cmake_minimum_required(VERSION 2.8.3)
+
+project(nntrainer)
+
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror -g -pthread")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -g -std=c++11 -pthread")
+find_package(PkgConfig REQUIRED)
+
+pkg_check_modules(TFLITE tensorflow-lite)
+link_libraries(${TFLITE_LIBRARIES})
+
+set(INIPARSER ${PROJECT_SOURCE_DIR}/external/iniparser/src)
+
+include_directories( ${include_directories}
+                    ${CMAKE_CURRENT_SOURCE_DIR}
+                    ${TFLITE_INCLUDE_DIRS}
+                    ${INIPARSER}
+                    )
+
+set(SRCS
+       src/neuralnet.cpp
+       src/matrix.cpp
+       src/layers.cpp
+       ${INIPARSER}/iniparser.c
+       ${INIPARSER}/dictionary.c
+       )
+
+add_library( ${PROJECT_NAME} SHARED ${SRCS} )      
diff --git a/external/iniparser b/external/iniparser
new file mode 160000 (submodule)
index 0000000..f858275
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit f858275f7f307eecba84c2f5429483f9f28007f8
diff --git a/include/layers.h b/include/layers.h
new file mode 100644 (file)
index 0000000..a16f73f
--- /dev/null
@@ -0,0 +1,439 @@
+/**
+ * Copyright (C) 2019 Samsung Electronics Co., Ltd. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file       layers.h
+ * @date       04 December 2019
+ * @brief      This is Layer classes of Neural Network
+ * @see                https://github.sec.samsung.net/jijoong-moon/Transfer-Learning.git
+ * @author     Jijoong Moon <jijoong.moon@samsung.com>
+ * @bug                No known bugs except for NYI items
+ *
+ */
+#ifndef __LAYERS_H__
+#define __LAYERS_H__
+
+#include <fstream>
+#include <iostream>
+#include <vector>
+#include "matrix.h"
+
+/**
+ * @Namespace   Namespace of Layers
+ * @brief       Namespace for Layers
+ */
+namespace Layers {
+
+/**
+ * @brief     Enumeration of optimizer type
+ *            0. SGD ( Stocastic Gradient Descent )
+ *            1. ADAM ( Adaptive Moment Estimation )
+ *            2. Unknown
+ */
+typedef enum { OPT_SGD, OPT_ADAM, OPT_UNKNOWN } opt_type;
+
+/**
+ * @brief     Enumeration of cost(loss) function type
+ *            0. MSR ( Mean Squared Roots )
+ *            1. ENTROPY ( Categorical Cross Entropy )
+ *            2. Unknown
+ */
+typedef enum { COST_MSR, COST_ENTROPY, COST_UNKNOWN } cost_type;
+
+/**
+ * @brief     Enumeration of activation function type
+ *            0. tanh
+ *            1. sigmoid
+ *            2. Unknown
+ */
+typedef enum { ACT_TANH, ACT_SIGMOID, ACT_UNKNOWN } acti_type;
+
+/**
+ * @brief     Enumeration of layer type
+ *            0. Input Layer type
+ *            1. Fully Connected Layer type
+ *            2. Output Layer type
+ *            3. Unknown
+ */
+typedef enum { LAYER_IN, LAYER_FC, LAYER_OUT, LAYER_UNKNOWN } layer_type;
+
+/**
+ * @brief     type for the Optimizor to save hyper-parameter
+ */
+typedef struct {
+  opt_type type;
+  double learning_rate;
+  double beta1;
+  double beta2;
+  double epsilon;
+  acti_type activation;
+} Optimizer;
+
+/**
+ * @class   Layer Base class for layers
+ * @brief   Base class for all layers
+ */
+class Layer {
+ public:
+  /**
+   * @brief     Destructor of Layer Class
+   */
+  virtual ~Layer(){};
+
+  /**
+   * @brief     Forward Propation of neural Network
+   * @param[in] input Input Matrix taken by upper layer
+   * @retval    Output Matrix
+   */
+  virtual Matrix forwarding(Matrix input) = 0;
+
+  /**
+   * @brief     Back Propation of neural Network
+   * @param[in] input Input Matrix taken by lower layer
+   * @param[in] iteration Epoch value for the ADAM Optimizer
+   * @retval    Output Matrix
+   */
+  virtual Matrix backwarding(Matrix input, int iteration) = 0;
+
+  /**
+   * @brief     Initialize the layer
+   *            - Weight(Height, Width), Bias(1, Width)
+   * @param[in] b batch
+   * @param[in] h Height
+   * @param[in] w Width
+   * @param[in] id index of this layer
+   * @param[in] init_zero Bias initialization with zero
+   */
+  virtual void initialize(int b, int h, int w, int id, bool init_zero) = 0;
+
+  /**
+   * @brief     read layer Weight & Bias data from file
+   * @param[in] file input file stream
+   */
+  virtual void read(std::ifstream &file) = 0;
+
+  /**
+   * @brief     save layer Weight & Bias data from file
+   * @param[in] file output file stream
+   */
+  virtual void save(std::ofstream &file) = 0;
+
+  /**
+   * @brief     Optimizer Setter
+   * @param[in] opt Optimizer
+   */
+  virtual void setOptimizer(Optimizer opt) = 0;
+
+  /**
+   * @brief     Layer type Setter
+   * @param[in] type layer type
+   */
+  void setType(layer_type type) { this->type = type; }
+
+  /**
+   * @brief     Copy Layer
+   * @param[in] l Layer to be copied
+   */
+  virtual void copy(Layer *l) = 0;
+
+  /**
+   * @brief     Input Matrix
+   */
+  Matrix Input;
+
+  /**
+   * @brief     Hidden Layer Matrix which store the
+   *            forwading result
+   */
+  Matrix hidden;
+
+  /**
+   * @brief     Layer index
+   */
+  unsigned int index;
+
+  /**
+   * @brief     batch size of Weight Data
+   */
+  unsigned int batch;
+
+  /**
+   * @brief     width size of Weight Data
+   */
+  unsigned int width;
+
+  /**
+   * @brief     height size of Weight Data
+   */
+  unsigned int height;
+
+  /**
+   * @brief     Optimizer for this layer
+   */
+  Optimizer opt;
+
+  /**
+   * @brief     Boolean for the Bias to set zero
+   */
+  bool init_zero;
+
+  /**
+   * @brief     Layer type
+   */
+  layer_type type;
+
+  /**
+   * @brief     Activation function pointer
+   */
+  double (*activation)(double);
+
+  /**
+   * @brief     Activation Derivative function pointer
+   */
+  double (*activationPrime)(double);
+};
+
+/**
+ * @class   Input Layer
+ * @brief   Just Handle the Input of Network
+ */
+class InputLayer : public Layer {
+ public:
+  /**
+   * @brief     Constructor of InputLayer
+   */
+  InputLayer(){};
+
+  /**
+   * @brief     Destructor of InputLayer
+   */
+  ~InputLayer(){};
+
+  /**
+   * @brief     No Weight data for this Input Layer
+   */
+  void read(std::ifstream &file){};
+
+  /**
+   * @brief     No Weight data for this Input Layer
+   */
+  void save(std::ofstream &file){};
+
+  /**
+   * @brief     It is back propagation of input layer.
+   *            It return Input as it is.
+   * @param[in] input input Matrix from lower layer.
+   * @param[in] iteration Epoch Number for ADAM
+   * @retval
+   */
+  Matrix backwarding(Matrix input, int iteration) { return Input; };
+
+  /**
+   * @brief     foward propagation : return Input Matrix
+   *            It return Input as it is.
+   * @param[in] input input Matrix from lower layer.
+   * @retval    return Input Matrix
+   */
+  Matrix forwarding(Matrix input);
+
+  /**
+   * @brief     Set Optimizer
+   * @param[in] opt optimizer
+   */
+  void setOptimizer(Optimizer opt);
+
+  /**
+   * @brief     Initializer of Input Layer
+   * @param[in] b batch size
+   * @param[in] h height
+   * @param[in] w width
+   * @param[in] id index of this layer
+   * @param[in] init_zero boolean to set Bias zero
+   */
+  void initialize(int b, int h, int w, int id, bool init_zero);
+
+  /**
+   * @brief     Copy Layer
+   * @param[in] l layer to copy
+   */
+  void copy(Layer *l);
+};
+
+/**
+ * @class   FullyConnecedLayer
+ * @brief   fully connected layer
+ */
+class FullyConnectedLayer : public Layer {
+ public:
+  /**
+   * @brief     Constructor of Fully Connected Layer
+   */
+  FullyConnectedLayer(){};
+
+  /**
+   * @brief     Destructor of Fully Connected Layer
+   */
+  ~FullyConnectedLayer(){};
+
+  /**
+   * @brief     Read Weight & Bias Data from file
+   * @param[in] file input stream file
+   */
+  void read(std::ifstream &file);
+
+  /**
+   * @brief     Save Weight & Bias Data to file
+   * @param[in] file output stream file
+   */
+  void save(std::ofstream &file);
+
+  /**
+   * @brief     forward propagation with input
+   * @param[in] input Input Matrix from upper layer
+   * @retval    Activation(W x input + B)
+   */
+  Matrix forwarding(Matrix input);
+
+  /**
+   * @brief     back propagation
+   *            Calculate dJdB & dJdW & Update W & B
+   * @param[in] input Input Matrix from lower layer
+   * @param[in] iteration Number of Epoch for ADAM
+   * @retval    dJdB x W Matrix
+   */
+  Matrix backwarding(Matrix input, int iteration);
+
+  /**
+   * @brief     set optimizer
+   * @param[in] opt Optimizer
+   */
+  void setOptimizer(Optimizer opt);
+
+  /**
+   * @brief     copy layer
+   * @param[in] l layer to copy
+   */
+  void copy(Layer *l);
+
+  /**
+   * @brief     initialize layer
+   * @param[in] b batch size
+   * @param[in] h height
+   * @param[in] w width
+   * @param[in] id layer index
+   * @param[in] init_zero boolean to set Bias zero
+   */
+  void initialize(int b, int h, int w, int id, bool init_zero);
+
+ private:
+  Matrix Weight;
+  Matrix Bias;
+
+  /**
+   * @brief     First Momentum Matrix for the ADAM
+   */
+  Matrix M;
+
+  /**
+   * @brief     Second Momentum Matrix for the ADAM
+   */
+  Matrix V;
+};
+
+/**
+ * @class   OutputLayer
+ * @brief   OutputLayer (has Cost Function & Weight, Bias)
+ */
+class OutputLayer : public Layer {
+ public:
+  /**
+   * @brief     Constructor of OutputLayer
+   */
+  OutputLayer(){};
+
+  /**
+   * @brief     Destructor of OutputLayer
+   */
+  ~OutputLayer(){};
+
+  /**
+   * @brief     Read Weight & Bias Data from file
+   * @param[in] file input stream file
+   */
+  void read(std::ifstream &file);
+
+  /**
+   * @brief     Save Weight & Bias Data to file
+   * @param[in] file output stream file
+   */
+  void save(std::ofstream &flle);
+
+  /**
+   * @brief     forward propagation with input
+   * @param[in] input Input Matrix from upper layer
+   * @retval    Activation(W x input + B)
+   */
+  Matrix forwarding(Matrix input);
+
+  /**
+   * @brief     back propagation
+   *            Calculate dJdB & dJdW & Update W & B
+   * @param[in] input Input Matrix from lower layer
+   * @param[in] iteration Number of Epoch for ADAM
+   * @retval    dJdB x W Matrix
+   */
+  Matrix backwarding(Matrix label, int iteration);
+
+  /**
+   * @brief     set optimizer
+   * @param[in] opt Optimizer
+   */
+  void setOptimizer(Optimizer opt);
+
+  /**
+   * @brief     initialize layer
+   * @param[in] b batch size
+   * @param[in] h height
+   * @param[in] w width
+   * @param[in] id layer index
+   * @param[in] init_zero boolean to set Bias zero
+   */
+  void initialize(int b, int w, int h, int id, bool init_zero);
+
+  /**
+   * @brief     get Loss value
+   */
+  double getLoss() { return loss; }
+
+  /**
+   * @brief     set cost function
+   * @param[in] c cost function type
+   */
+  void setCost(cost_type c) { this->cost = c; };
+
+  /**
+   * @brief     copy layer
+   * @param[in] l layer to copy
+   */
+  void copy(Layer *l);
+
+ private:
+  Matrix Weight;
+  Matrix Bias;
+  Matrix M;
+  Matrix V;
+  double loss;
+  cost_type cost;
+};
+}
+
+#endif
diff --git a/include/matrix.h b/include/matrix.h
new file mode 100644 (file)
index 0000000..1042479
--- /dev/null
@@ -0,0 +1,235 @@
+/**
+ * Copyright (C) 2019 Samsung Electronics Co., Ltd. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ * @file       matrix.h
+ * @date       04 December 2019
+ * @brief      This is Matrix class for calculation
+ * @see                https://github.sec.samsung.net/jijoong-moon/Transfer-Learning.git
+ * @author     Jijoong Moon <jijoong.moon@samsung.com>
+ * @bug                No known bugs except for NYI items
+ *
+ */
+
+#ifndef __MATRIX_H__
+#define __MATRIX_H__
+
+#include <cmath>
+#include <fstream>
+#include <iostream>
+#include <vector>
+
+/**
+ * @class   Matrix Class for Calculation
+ * @brief   Matrix Class for Calculation
+ */
+class Matrix {
+ public:
+  /**
+   * @brief     Constructor of Matrix
+   */
+  Matrix(){};
+  
+  /**
+   * @brief     Constructor of Matrix with batch size one
+   * @param[in] heihgt Height of Matrix
+   * @param[in] width Width of Matrix
+   */  
+  Matrix(int height, int width);
+
+  /**
+   * @brief     Constructor of Matrix
+   * @param[in] batch Batch of Matrix
+   * @param[in] heihgt Height of Matrix
+   * @param[in] width Width of Matrix
+   */
+  Matrix(int batch, int height, int width);
+
+  /**
+   * @brief   Constructor of Matrix
+   * @param[in] data data for the Matrix with batch size one
+   */
+  Matrix(std::vector<std::vector<double>> const &data);
+
+  /**
+   * @brief     Constructor of Matrix
+   * @param[in] data data for the Matrix
+   */
+  Matrix(std::vector<std::vector<std::vector<double>>> const &data);
+
+  /**
+   * @brief     Multiply value element by element
+   * @param[in] value multiplier
+   * @retval    Calculated Matrix
+   */  
+  Matrix multiply(double const &value);
+
+  /**
+   * @brief     Divide value element by element
+   * @param[in] value Divisor
+   * @retval    Calculated Matrix
+   */  
+  Matrix divide(double const &value);
+
+  /**
+   * @brief     Add Matrix Element by Element
+   * @param[in] m Matrix to be added
+   * @retval    Calculated Matrix
+   */  
+  Matrix add(Matrix const &m) const;
+
+  /**
+   * @brief     Add value Element by Element
+   * @param[in] value value to be added
+   * @retval    Calculated Matrix
+   */  
+  Matrix add(double const &value);
+
+  /**
+   * @brief     Substract Matrix Element by Element
+   * @param[in] m Matrix to be added
+   * @retval    Calculated Matrix
+   */  
+  Matrix subtract(Matrix const &m) const;
+
+  /**
+   * @brief     Multiply Matrix Element by Element ( Not the MxM )
+   * @param[in] m Matrix to be multiplied
+   * @retval    Calculated Matrix
+   */  
+  Matrix multiply(Matrix const &m) const;
+
+  /**
+   * @brief     Divide Matrix Element by Element
+   * @param[in] m Divisor Matrix
+   * @retval    Calculated Matrix
+   */  
+  Matrix divide(Matrix const &m) const;
+
+  /**
+   * @brief     Dot Product of Matrix ( equal MxM )
+   * @param[in] m Matrix
+   * @retval    Calculated Matrix
+   */  
+  Matrix dot(Matrix const &m) const;
+
+  /**
+   * @brief     Transpose Matrix
+   * @retval    Calculated Matrix
+   */  
+  Matrix transpose() const;
+
+  /**
+   * @brief     sum all the Matrix elements according to the batch
+   * @retval    Calculated Matrix(batch, 1, 1)
+   */  
+  Matrix sum() const;
+
+  /**
+   * @brief     Averaging the Matrix elements according to the batch
+   * @retval    Calculated Matrix(1, height, width)
+   */  
+  Matrix average() const;
+
+  /**
+   * @brief     Softmax the Matrix elements
+   * @retval    Calculated Matrix
+   */  
+  Matrix softmax() const;
+
+  /**
+   * @brief     Fill the Matrix elements with zero
+   */  
+  void setZero();
+
+  /**
+   * @brief     Reduce Rank ( Matrix to Vector )
+   * @retval    Saved vector
+   */  
+  std::vector<double> Mat2Vec();
+  
+  /**
+   * @brief     Apply function element by element
+   * @param[in] *function function pointer applied
+   * @retval    Matrix
+   */  
+  Matrix applyFunction(double (*function)(double)) const;
+
+  /**
+   * @brief     Print element
+   * @param[in] out out stream
+   * @retval    Matrix
+   */    
+  void print(std::ostream &out) const;
+
+  /**
+   * @brief     Get Width of Matrix
+   * @retval    int Width
+   */  
+  int getWidth() { return width; };
+
+  /**
+   * @brief     Get Height of Matrix
+   * @retval    int Height
+   */  
+  int getHeight() { return height; };
+
+  /**
+   * @brief     Get Batch of Matrix
+   * @retval    int Batch
+   */  
+  int getBatch() { return batch; };
+
+  /**
+   * @brief     Set the elelemnt value
+   * @param[in] batch batch location
+   * @param[in] i height location
+   * @param[in] j width location
+   * @param[in] value value to be stored
+   */  
+  void setValue(int batch, int i, int j, double value);
+
+  /**
+   * @brief     Copy the Matrix
+   * @param[in] from Matrix to be Copyed
+   * @retval    Matix
+   */  
+  Matrix &copy(Matrix const &from);
+  
+  /**
+   * @brief     Save the Matrix into file
+   * @param[in] file output file stream
+   */  
+  void save(std::ofstream &file);
+
+  /**
+   * @brief     Read the Matrix from file
+   * @param[in] file input file stream
+   */  
+  void read(std::ifstream &file);
+
+private:
+/**< handle the data as a std::vector type */
+  std::vector<std::vector<std::vector<double>>> data;
+  int height;
+  int width;
+  int batch;
+  int dim;
+};
+
+/**
+ * @brief   Overriding output stream
+ */
+std::ostream &operator<<(std::ostream &out, Matrix const &m);
+
+#endif
diff --git a/include/neuralnet.h b/include/neuralnet.h
new file mode 100644 (file)
index 0000000..9b4a60f
--- /dev/null
@@ -0,0 +1,213 @@
+/**
+ * Copyright (C) 2019 Samsung Electronics Co., Ltd. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ * @file       neuralnet.h
+ * @date       04 December 2019
+ * @brief      This is Neural Network Class
+ * @see                https://github.sec.samsung.net/jijoong-moon/Transfer-Learning.git
+ * @author     Jijoong Moon <jijoong.moon@samsung.com>
+ * @bug                No known bugs except for NYI items
+ *
+ */
+#ifndef __NEURALNET_H__
+#define __NEURALNET_H__
+
+#include <fstream>
+#include <iostream>
+#include <vector>
+#include "layers.h"
+#include "matrix.h"
+
+/**
+ * @Namespace   Namespace of Network
+ * @brief       Namespace for Network
+ */
+namespace Network {
+
+/**
+ * @brief     Enumeration of Network Type
+ *            0. KNN ( k Nearest Neighbor )
+ *            1. REG ( Logistic Regression )
+ *            2. NEU ( Neural Network )
+ *            3. Unknown
+ */
+typedef enum { NET_KNN, NET_REG, NET_NEU, NET_UNKNOWN } net_type;
+
+/**
+ * @brief     Enumeration for input configuration file parsing
+ *            0. OPT     ( Optimizer Token )
+ *            1. COST    ( Cost Function Token )
+ *            2. NET     ( Network Token )
+ *            3. ACTI    ( Activation Token )
+ *            4. LAYER   ( Layer Token )
+ *            5. UNKNOWN
+ */
+typedef enum { TOKEN_OPT, TOKEN_COST, TOKEN_NET, TOKEN_ACTI, TOKEN_LAYER, TOKEN_UNKNOWN } input_type;
+
+/**
+ * @class   NeuralNetwork Class
+ * @brief   NeuralNetwork Class which has Network Configuration & Layers
+ */
+class NeuralNetwork {
+ public:
+  /**
+   * @brief     Constructor of NeuralNetwork Class
+   */
+  NeuralNetwork(){};
+
+  /**
+   * @brief     Constructor of NeuralNetwork Class with Configuration file path
+   */
+  NeuralNetwork(std::string config_path);
+
+  /**
+   * @brief     Destructor of NeuralNetwork Class
+   */
+  ~NeuralNetwork(){};
+
+  /**
+    * @brief     Get Loss
+    * @retval    loss value
+    */
+  double getLoss();
+
+  /**
+   * @brief     Set Loss
+   * @param[in] l loss value
+   */
+  void setLoss(double l);
+
+  /**
+   * @brief     Initialize Network
+   */
+  void init();
+
+  /**
+   * @brief     forward propagation
+   * @param[in] input Input Matrix X
+   * @retval    Output Matrix Y
+   */
+  Matrix forwarding(Matrix input);
+
+  /**
+   * @brief     back propagation to update W & B
+   * @param[in] input Input Matrix X
+   * @param[in] expectedOutput Lable Matrix Y
+   * @param[in] iteration Epoch Number for ADAM
+   */
+  void backwarding(Matrix input, Matrix expectedOutput, int iteration);
+
+  /**
+   * @brief     save W & B into file
+   */
+  void saveModel();
+
+  /**
+   * @brief     read W & B from file
+   */
+  void readModel();
+
+  /**
+   * @brief     set configuration file
+   * @param[in] config_path configuration file path
+   */
+  void setConfig(std::string config_path);
+
+  /**
+   * @brief     get Epoch
+   * @retval    epoch
+   */
+  unsigned int getEpoch() { return epoch; };
+
+  /**
+   * @brief     Copy Neural Network
+   * @param[in] from NeuralNetwork Object to copy
+   * @retval    NeuralNewtork Object copyed
+   */
+  NeuralNetwork &copy(NeuralNetwork &from);
+
+  /**
+   * @brief     finalize NeuralNetwork Object
+   */
+  void finalize();
+
+ private:
+  /**
+   * @brief     batch size
+   */
+  int batchsize;
+
+  /**
+   * @brief     function pointer for activation
+   */
+  double (*activation)(double);
+
+  /**
+   * @brief     function pointer for derivative of activation
+   */
+  double (*activationPrime)(double);
+
+  /**
+   * @brief     learning rate
+   */
+  double learning_rate;
+
+  /**
+   * @brief     Maximum Epoch
+   */
+  unsigned int epoch;
+
+  /**
+   * @brief     loss
+   */
+  double loss;
+
+  /**
+   * @brief     boolean to set the Bias zero
+   */
+  bool init_zero;
+
+  /**
+   * @brief     Cost Function type
+   */
+  Layers::cost_type cost;
+
+  /**
+   * @brief     Model path to save or read
+   */
+  std::string model;
+
+  /**
+   * @brief     Configuration file path
+   */
+  std::string config;
+
+  /**
+   * @brief     Optimizer
+   */
+  Layers::Optimizer opt;
+
+  /**
+   * @brief     Network Type
+   */
+  net_type nettype;
+
+  /**
+   * @brief     vector for store layer pointers.
+   */
+  std::vector<Layers::Layer *> layers;
+};
+}
+
+#endif
diff --git a/src/layers.cpp b/src/layers.cpp
new file mode 100644 (file)
index 0000000..009cf7a
--- /dev/null
@@ -0,0 +1,313 @@
+/**
+ * Copyright (C) 2019 Samsung Electronics Co., Ltd. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ * @file       layers.cpp
+ * @date       04 December 2019
+ * @brief      This is Layers Classes for Neural Network
+ * @see                https://github.sec.samsung.net/jijoong-moon/Transfer-Learning.git
+ * @author     Jijoong Moon <jijoong.moon@samsung.com>
+ * @bug                No known bugs except for NYI items
+ *
+ */
+
+#include "include/layers.h"
+#include <assert.h>
+
+/**
+ * @brief     random function
+ * @param[in] x double
+ */
+double random(double x) { return (double)(rand() % 10000 + 1) / 10000 - 0.5; }
+
+/**
+ * @brief     sigmoid activation function
+ * @param[in] x input
+ */
+
+double sigmoid(double x) { return 1 / (1 + exp(-x)); }
+
+/**
+ * @brief     derivative sigmoid function
+ * @param[in] x input
+ */
+double sigmoidePrime(double x) { return exp(-x) / (pow(1 + exp(-x), 2)); }
+
+/**
+ * @brief     derivative tanh function
+ * @param[in] x input
+ */
+double tanhPrime(double x) {
+  double th = tanh(x);
+  return 1.0 - th * th;
+}
+
+namespace Layers {
+
+void InputLayer::setOptimizer(Optimizer opt) {
+  this->opt = opt;
+  switch (opt.activation) {
+    case ACT_TANH:
+      activation = tanh;
+      activationPrime = tanhPrime;
+      break;
+    case ACT_SIGMOID:
+      activation = sigmoid;
+      activationPrime = sigmoidePrime;
+      break;
+    default:
+      break;
+  }
+}
+
+void InputLayer::copy(Layer *l) {
+  InputLayer *from = static_cast<InputLayer *>(l);
+  this->opt = from->opt;
+  this->index = from->index;
+  this->height = from->height;
+  this->width = from->width;
+  this->Input.copy(from->Input);
+  this->hidden.copy(from->hidden);
+}
+
+Matrix InputLayer::forwarding(Matrix input) {
+  Input = input;
+  return Input;
+}
+
+void InputLayer::initialize(int b, int h, int w, int id, bool init_zero) {
+  batch = b;
+  width = w;
+  height = h;
+  index = 0;
+}
+
+void FullyConnectedLayer::initialize(int b, int h, int w, int id, bool init_zero) {
+  this->batch = b;
+  this->width = w;
+  this->height = h;
+  this->index = id;
+  this->init_zero = init_zero;
+
+  Weight = Matrix(h, w);
+  Bias = Matrix(1, w);
+
+  Weight = Weight.applyFunction(random);
+  if (init_zero) {
+    Bias.setZero();
+  } else {
+    Bias = Bias.applyFunction(random);
+  }
+}
+
+void FullyConnectedLayer::setOptimizer(Optimizer opt) {
+  this->opt = opt;
+  switch (opt.activation) {
+    case ACT_TANH:
+      activation = tanh;
+      activationPrime = tanhPrime;
+      break;
+    case ACT_SIGMOID:
+      activation = sigmoid;
+      activationPrime = sigmoidePrime;
+      break;
+    default:
+      break;
+  }
+  if (opt.type == OPT_ADAM) {
+    M = Matrix(height, width);
+    V = Matrix(height, width);
+    M.setZero();
+    V.setZero();
+  }
+}
+
+Matrix FullyConnectedLayer::forwarding(Matrix input) {
+  Input = input;
+  hidden = Input.dot(Weight).add(Bias).applyFunction(activation);
+  return hidden;
+}
+
+void FullyConnectedLayer::read(std::ifstream &file) {
+  Weight.read(file);
+  Bias.read(file);
+}
+
+void FullyConnectedLayer::save(std::ofstream &file) {
+  Weight.save(file);
+  Bias.save(file);
+}
+
+void FullyConnectedLayer::copy(Layer *l) {
+  FullyConnectedLayer *from = static_cast<FullyConnectedLayer *>(l);
+  this->opt = from->opt;
+  this->index = from->index;
+  this->height = from->height;
+  this->width = from->width;
+  this->Input.copy(from->Input);
+  this->hidden.copy(from->hidden);
+  this->Weight.copy(from->Weight);
+  this->Bias.copy(from->Bias);
+}
+
+Matrix FullyConnectedLayer::backwarding(Matrix derivative, int iteration) {
+  Matrix dJdB = derivative.multiply(Input.dot(Weight).add(Bias).applyFunction(activationPrime));
+  Matrix dJdW = Input.transpose().dot(dJdB);
+  Matrix ret = dJdB.dot(Weight.transpose());
+
+  switch (opt.type) {
+    case OPT_SGD:
+      Weight = Weight.subtract(dJdW.average().multiply(opt.learning_rate));
+      break;
+    case OPT_ADAM:
+      M = M.multiply(opt.beta1).add(dJdW.average().multiply(1 - opt.beta1));
+      V = V.multiply(opt.beta2).add((dJdW.average().multiply(dJdW.average())).multiply(1 - opt.beta2));
+      M.divide(1 - pow(opt.beta1, iteration + 1));
+      V.divide(1 - pow(opt.beta2, iteration + 1));
+      Weight = Weight.subtract((M.divide(V.applyFunction(sqrt).add(opt.epsilon))).multiply(opt.learning_rate));
+      break;
+    default:
+      break;
+  }
+
+  if (!this->init_zero) {
+    Bias = Bias.subtract(dJdB.average().multiply(opt.learning_rate));
+  }
+
+  return ret;
+}
+
+void OutputLayer::initialize(int b, int h, int w, int id, bool init_zero) {
+  this->batch = b;
+  this->width = w;
+  this->height = h;
+  this->index = id;
+  this->init_zero = init_zero;
+  Weight = Matrix(h, w);
+  Bias = Matrix(1, w);
+  this->cost = cost;
+
+  Weight = Weight.applyFunction(random);
+  if (init_zero) {
+    Bias.setZero();
+  } else {
+    Bias = Bias.applyFunction(random);
+  }
+}
+
+Matrix OutputLayer::forwarding(Matrix input) {
+  Input = input;
+  if (cost == COST_ENTROPY)
+    hidden = input.dot(Weight).applyFunction(activation);
+  else
+    hidden = input.dot(Weight).add(Bias).applyFunction(activation);
+  return hidden;
+}
+
+void OutputLayer::read(std::ifstream &file) {
+  Weight.read(file);
+  Bias.read(file);
+}
+
+void OutputLayer::save(std::ofstream &file) {
+  Weight.save(file);
+  Bias.save(file);
+}
+
+void OutputLayer::copy(Layer *l) {
+  OutputLayer *from = static_cast<OutputLayer *>(l);
+  this->opt = from->opt;
+  this->index = from->index;
+  this->height = from->height;
+  this->width = from->width;
+  this->Input.copy(from->Input);
+  this->hidden.copy(from->hidden);
+  this->Weight.copy(from->Weight);
+  this->Bias.copy(from->Bias);
+  this->loss = from->loss;
+}
+
+void OutputLayer::setOptimizer(Optimizer opt) {
+  this->opt = opt;
+  switch (opt.activation) {
+    case ACT_TANH:
+      activation = tanh;
+      activationPrime = tanhPrime;
+      break;
+    case ACT_SIGMOID:
+      activation = sigmoid;
+      activationPrime = sigmoidePrime;
+      break;
+    default:
+      break;
+  }
+
+  if (opt.type == OPT_ADAM) {
+    M = Matrix(height, width);
+    V = Matrix(height, width);
+    M.setZero();
+    V.setZero();
+  }
+}
+
+Matrix OutputLayer::backwarding(Matrix label, int iteration) {
+  double lossSum = 0.0;
+  Matrix Y2 = label;
+  Matrix Y = hidden;
+  Matrix ret;
+  Matrix dJdB;
+
+  if (cost == COST_ENTROPY) {
+    dJdB = Y.subtract(Y2);
+    Matrix temp = ((Y2.multiply(-1.0).transpose().dot(Y.add(opt.epsilon).applyFunction(log)))
+                       .subtract(Y2.multiply(-1.0).add(1.0).transpose().dot(
+                           Y.multiply(-1.0).add(1.0).add(opt.epsilon).applyFunction(log))));
+    loss = (1.0 / Y.Mat2Vec().size()) * temp.Mat2Vec()[0];
+  } else {
+    Matrix sub = Y2.subtract(Y);
+    Matrix l = (sub.multiply(sub)).sum().multiply(0.5);
+    std::vector<double> t = l.Mat2Vec();
+    for (int i = 0; i < l.getBatch(); i++) {
+      lossSum += t[i];
+    }
+
+    loss = lossSum / (double)l.getBatch();
+
+    dJdB = Y.subtract(Y2).multiply(Input.dot(Weight).add(Bias).applyFunction(activationPrime));
+  }
+
+  Matrix dJdW = Input.transpose().dot(dJdB);
+  ret = dJdB.dot(Weight.transpose());
+
+  switch (opt.type) {
+    case Layers::OPT_SGD:
+      Weight = Weight.subtract(dJdW.average().multiply(opt.learning_rate));
+      break;
+    case Layers::OPT_ADAM:
+      M = M.multiply(opt.beta1).add(dJdW.average().multiply(1 - opt.beta1));
+      V = V.multiply(opt.beta2).add((dJdW.average().multiply(dJdW.average())).multiply(1 - opt.beta2));
+      M.divide(1 - pow(opt.beta1, iteration + 1));
+      V.divide(1 - pow(opt.beta2, iteration + 1));
+      Weight = Weight.subtract((M.divide(V.applyFunction(sqrt).add(opt.epsilon))).multiply(opt.learning_rate));
+      break;
+    default:
+      break;
+  }
+
+  if (!this->init_zero) {
+    Bias = Bias.subtract(dJdB.average().multiply(opt.learning_rate));
+  }
+
+  return ret;
+}
+}
diff --git a/src/matrix.cpp b/src/matrix.cpp
new file mode 100644 (file)
index 0000000..102ebcb
--- /dev/null
@@ -0,0 +1,424 @@
+/**
+ * Copyright (C) 2019 Samsung Electronics Co., Ltd. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ * @file       matrix.cpp
+ * @date       04 December 2019
+ * @brief      This is Matrix class for calculation
+ * @see                https://github.sec.samsung.net/jijoong-moon/Transfer-Learning.git
+ * @author     Jijoong Moon <jijoong.moon@samsung.com>
+ * @bug                No known bugs except for NYI items
+ *
+ */
+
+#include "include/matrix.h"
+#include <assert.h>
+#include <stdio.h>
+#include <sstream>
+
+Matrix::Matrix(int height, int width) {
+  this->height = height;
+  this->width = width;
+  this->batch = 1;
+  this->dim = 2;
+  this->data.push_back(std::vector<std::vector<double>>(height, std::vector<double>(width)));
+}
+
+Matrix::Matrix(int batch, int height, int width) {
+  this->height = height;
+  this->width = width;
+  this->batch = batch;
+  this->dim = 3;
+
+  for (int i = 0; i < batch; i++) {
+    this->data.push_back(std::vector<std::vector<double>>(height, std::vector<double>(width)));
+  }
+}
+
+Matrix::Matrix(std::vector<std::vector<double>> const &data) {
+  assert(data.size() != 0);
+  this->height = data.size();
+  this->width = data[0].size();
+  this->batch = 1;
+  this->dim = 2;
+  this->data.push_back(data);
+}
+
+Matrix::Matrix(std::vector<std::vector<std::vector<double>>> const &data) {
+  assert(data.size() != 0 && data[0].size() != 0);
+  this->batch = data.size();
+  this->height = data[0].size();
+  this->width = data[0][0].size();
+  this->dim = 3;
+  this->data = data;
+}
+
+Matrix Matrix::multiply(double const &value) {
+  Matrix result(batch, height, width);
+  int i, j, k;
+
+  for (k = 0; k < batch; k++) {
+    for (i = 0; i < height; i++) {
+      for (j = 0; j < width; j++) {
+        result.data[k][i][j] = data[k][i][j] * value;
+      }
+    }
+  }
+
+  return result;
+}
+
+Matrix Matrix::divide(double const &value) {
+  Matrix result(batch, height, width);
+  int i, j, k;
+
+  for (k = 0; k < batch; k++) {
+    for (i = 0; i < height; i++) {
+      for (j = 0; j < width; j++) {
+        result.data[k][i][j] = data[k][i][j] / value;
+      }
+    }
+  }
+
+  return result;
+}
+
+Matrix Matrix::add(double const &value) {
+  Matrix result(batch, height, width);
+  int i, j, k;
+
+  for (k = 0; k < batch; k++) {
+    for (i = 0; i < height; i++) {
+      for (j = 0; j < width; j++) {
+        result.data[k][i][j] = data[k][i][j] + value;
+      }
+    }
+  }
+
+  return result;
+}
+
+Matrix Matrix::add(Matrix const &m) const {
+  assert(height == m.height && width == m.width);
+
+  Matrix result(batch, height, width);
+  int i, j, k;
+  if (m.batch == 1) {
+    for (k = 0; k < batch; k++) {
+      for (i = 0; i < height; i++) {
+        for (j = 0; j < width; j++) {
+          result.data[k][i][j] = data[k][i][j] + m.data[0][i][j];
+        }
+      }
+    }
+  } else {
+    for (k = 0; k < batch; k++) {
+      for (i = 0; i < height; i++) {
+        for (j = 0; j < width; j++) {
+          result.data[k][i][j] = data[k][i][j] + m.data[k][i][j];
+        }
+      }
+    }
+  }
+  return result;
+}
+
+Matrix Matrix::subtract(Matrix const &m) const {
+  assert(height == m.height && width == m.width);
+  Matrix result(batch, height, width);
+  int i, j, k;
+
+  if (m.batch == 1) {
+    for (k = 0; k < batch; k++) {
+      for (i = 0; i < height; i++) {
+        for (j = 0; j < width; j++) {
+          result.data[k][i][j] = data[k][i][j] - m.data[0][i][j];
+        }
+      }
+    }
+  } else {
+    for (k = 0; k < batch; k++) {
+      for (i = 0; i < height; i++) {
+        for (j = 0; j < width; j++) {
+          result.data[k][i][j] = data[k][i][j] - m.data[k][i][j];
+        }
+      }
+    }
+  }
+  return result;
+}
+
+Matrix Matrix::multiply(Matrix const &m) const {
+  assert(height == m.height && width == m.width);
+  Matrix result(batch, height, width);
+
+  int i, j, k;
+
+  if (m.batch == 1) {
+    for (k = 0; k < batch; k++) {
+      for (i = 0; i < height; i++) {
+        for (j = 0; j < width; j++) {
+          result.data[k][i][j] = data[k][i][j] * m.data[0][i][j];
+        }
+      }
+    }
+  } else {
+    for (k = 0; k < batch; k++) {
+      for (i = 0; i < height; i++) {
+        for (j = 0; j < width; j++) {
+          result.data[k][i][j] = data[k][i][j] * m.data[k][i][j];
+        }
+      }
+    }
+  }
+
+  return result;
+}
+
+Matrix Matrix::divide(Matrix const &m) const {
+  assert(height == m.height && width == m.width);
+  Matrix result(batch, height, width);
+
+  int i, j, k;
+
+  if (m.batch == 1) {
+    for (k = 0; k < batch; k++) {
+      for (i = 0; i < height; i++) {
+        for (j = 0; j < width; j++) {
+          result.data[k][i][j] = data[k][i][j] / m.data[0][i][j];
+        }
+      }
+    }
+  } else {
+    for (k = 0; k < batch; k++) {
+      for (i = 0; i < height; i++) {
+        for (j = 0; j < width; j++) {
+          result.data[k][i][j] = data[k][i][j] / m.data[k][i][j];
+        }
+      }
+    }
+  }
+
+  return result;
+}
+
+/**
+ * This is to sum the Matrix data according to the batch.
+ * Therefore the result has M(batch, 1, 1) dimension.
+ */
+Matrix Matrix::sum() const {
+  int i, j, k;
+  Matrix ret(batch, 1, 1);
+
+  for (k = 0; k < batch; k++) {
+    ret.data[k][0][0] = 0.0;
+    for (i = 0; i < height; i++) {
+      for (j = 0; j < width; j++) {
+        ret.data[k][0][0] += data[k][i][j];
+      }
+    }
+  }
+
+  return ret;
+}
+
+/**
+ * If the batch sizeo of m is one, the it is reused for
+ * every calculation along with batch
+ */
+Matrix Matrix::dot(Matrix const &m) const {
+  assert(width == m.height);
+  int i, j, h, k, mwidth = m.width;
+  double w = 0;
+
+  Matrix result(batch, height, mwidth);
+  if (m.batch == 1) {
+    for (k = 0; k < batch; k++) {
+      for (i = 0; i < height; i++) {
+        for (j = 0; j < mwidth; j++) {
+          for (h = 0; h < width; h++) {
+            w += data[k][i][h] * m.data[0][h][j];
+          }
+          result.data[k][i][j] = w;
+          w = 0;
+        }
+      }
+    }
+  } else {
+    for (k = 0; k < batch; k++) {
+      for (i = 0; i < height; i++) {
+        for (j = 0; j < mwidth; j++) {
+          for (h = 0; h < width; h++) {
+            w += data[k][i][h] * m.data[k][h][j];
+          }
+          result.data[k][i][j] = w;
+          w = 0;
+        }
+      }
+    }
+  }
+
+  return result;
+}
+
+Matrix Matrix::transpose() const {
+  Matrix result(batch, width, height);
+  int i, j, k;
+  for (k = 0; k < batch; k++) {
+    for (i = 0; i < width; i++) {
+      for (j = 0; j < height; j++) {
+        result.data[k][i][j] = data[k][j][i];
+      }
+    }
+  }
+  return result;
+}
+
+Matrix Matrix::applyFunction(double (*function)(double)) const {
+  Matrix result(batch, height, width);
+  int i, j, k;
+
+  for (k = 0; k < batch; k++) {
+    for (i = 0; i < height; i++) {
+      for (j = 0; j < width; j++) {
+        result.data[k][i][j] = (*function)(data[k][i][j]);
+      }
+    }
+  }
+  return result;
+}
+
+void Matrix::print(std::ostream &out) const {
+  int i, j, k;
+  std::stringstream ss;
+  for (k = 0; k < batch; k++) {
+    for (i = 0; i < height; i++) {
+      for (j = 0; j < width; j++) {
+        out << data[k][i][j] << " ";
+      }
+      out << std::endl;
+    }
+    out << std::endl;
+  }
+}
+
+std::ostream &operator<<(std::ostream &out, Matrix const &m) {
+  m.print(out);
+  return out;
+}
+
+Matrix &Matrix::copy(const Matrix &from) {
+  if (this != &from && from.data.size() != 0) {
+    height = from.height;
+    width = from.width;
+    batch = from.batch;
+    for (int k = 0; k < batch; k++) {
+      for (int i = 0; i < height; i++) {
+        for (int j = 0; j < width; j++) {
+          data[k][i][j] = from.data[k][i][j];
+        }
+      }
+    }
+  }
+  return *this;
+}
+
+/**
+ * This generate one dimension vector has the every element in Matrix
+ */
+std::vector<double> Matrix::Mat2Vec() {
+  std::vector<double> ret;
+  for (int k = 0; k < batch; k++)
+    for (int i = 0; i < height; i++)
+      for (int j = 0; j < width; j++)
+        ret.push_back(data[k][i][j]);
+
+  return ret;
+}
+
+void Matrix::save(std::ofstream &file) {
+  for (int k = 0; k < batch; k++) {
+    for (int i = 0; i < height; i++) {
+      for (int j = 0; j < width; j++) {
+        file.write((char *)&data[k][i][j], sizeof(double));
+      }
+    }
+  }
+}
+
+void Matrix::read(std::ifstream &file) {
+  for (int k = 0; k < batch; k++) {
+    for (int i = 0; i < height; i++) {
+      for (int j = 0; j < width; j++) {
+        file.read((char *)&data[k][i][j], sizeof(double));
+      }
+    }
+  }
+}
+
+/**
+ * This calculates average value according to the batch direction.
+ * That is the why it has (1, height, width) dimension.
+ */
+Matrix Matrix::average() const {
+  if (batch == 1)
+    return *this;
+
+  Matrix result(1, height, width);
+  for (int i = 0; i < height; i++) {
+    for (int j = 0; j < width; j++) {
+      result.data[0][i][j] = 0.0;
+      for (int k = 0; k < batch; k++) {
+        result.data[0][i][j] += data[k][i][j];
+      }
+      result.data[0][i][j] = result.data[0][i][j] / (double)batch;
+    }
+  }
+  return result;
+}
+
+void Matrix::setZero() {
+  for (int k = 0; k < batch; k++) {
+    for (int i = 0; i < height; i++) {
+      for (int j = 0; j < width; j++) {
+        this->data[k][i][j] = 0.0;
+      }
+    }
+  }
+}
+
+Matrix Matrix::softmax() const {
+  Matrix result(batch, height, width);
+  Matrix divisor(batch, height, 1);
+
+  divisor.setZero();
+
+  for (int k = 0; k < batch; k++) {
+    for (int i = 0; i < height; i++) {
+      for (int j = 0; j < width; j++) {
+        divisor.data[k][i][0] += exp(this->data[k][i][j]);
+      }
+    }
+  }
+
+  for (int k = 0; k < batch; k++) {
+    for (int i = 0; i < height; i++) {
+      for (int j = 0; j < width; j++) {
+        result.data[k][i][j] = exp(this->data[k][i][j]) / divisor.data[k][i][0];
+      }
+    }
+  }
+  return result;
+}
+
+void Matrix::setValue(int batch, int height, int width, double value) { this->data[batch][height][width] = value; }
diff --git a/src/neuralnet.cpp b/src/neuralnet.cpp
new file mode 100644 (file)
index 0000000..7313c7f
--- /dev/null
@@ -0,0 +1,338 @@
+/**
+ * Copyright (C) 2019 Samsung Electronics Co., Ltd. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ * @file       neuralnet.cpp
+ * @date       04 December 2019
+ * @brief      This is Neural Network Class
+ * @see                https://github.sec.samsung.net/jijoong-moon/Transfer-Learning.git
+ * @author     Jijoong Moon <jijoong.moon@samsung.com>
+ * @bug                No known bugs except for NYI items
+ *
+ */
+
+#include "include/neuralnet.h"
+#include <assert.h>
+#include <stdio.h>
+#include <cmath>
+#include <sstream>
+#include "iniparser.h"
+
+/**
+ * @brief     compare character to remove case sensitivity
+ * @param[in] c1 characer #1
+ * @param[in] c2 characer #2
+ * @retval    boolean true if they are same
+ */
+bool compareChar(char &c1, char &c2) {
+  if (c1 == c2)
+    return true;
+  else if (std::toupper(c1) == std::toupper(c2))
+    return true;
+  return false;
+}
+
+/**
+ * @brief     compare string with case insensitive
+ * @param[in] str1 string #1
+ * @param[in] str2 string #2
+ * @retval    boolean true if they are same
+ */
+bool caseInSensitiveCompare(std::string &str1, std::string &str2) {
+  return ((str1.size() == str2.size()) && std::equal(str1.begin(), str1.end(), str2.begin(), &compareChar));
+}
+
+namespace Network {
+
+/**
+ * @brief     Optimizer String from configure file
+ *            "sgd"  : Stochestic Gradient Descent
+ *            "adam" : Adaptive Moment Estimation
+ */
+std::vector<std::string> Optimizer_string = {"sgd", "adam"};
+
+/**
+ * @brief     Cost Function String from configure file
+ *            "msr"  : Mean Squared Roots
+ *            "caterogical" : Categorical Cross Entropy
+ */
+std::vector<std::string> Cost_string = {"msr", "categorical"};
+
+/**
+ * @brief     Network Type String from configure file
+ *            "knn"  : K Neearest Neighbor
+ *            "regression" : Logistic Regression
+ *            "neuralnet" : Neural Network
+ */
+std::vector<std::string> NetworkType_string = {"knn", "regression", "neuralnet"};
+
+/**
+ * @brief     Activation Type String from configure file
+ *            "tanh"  : tanh
+ *            "sigmoid" : sigmoid
+ */
+std::vector<std::string> activation_string = {"tanh", "sigmoid"};
+
+/**
+ * @brief     Layer Type String from configure file
+ *            "InputLayer"  : InputLayer Object
+ *            "FullyConnectedLayer" : Fully Connected Layer Object
+ *            "OutputLayer" : Output Layer Object
+ */
+std::vector<std::string> layer_string = {"InputLayer", "FullyConnectedLayer", "OutputLayer"};
+
+/**
+ * @brief     Check Existance of File
+ * @param[in] filename file path to check
+ * @retval    boolean true if exists
+ */
+static bool is_file_exist(std::string filename) {
+  std::ifstream infile(filename);
+  return infile.good();
+}
+
+/**
+ * @brief     Parsing Layer Name
+ * @param[in] string layer name
+ * @retval    vector stored layer name
+ */
+std::vector<std::string> parseLayerName(std::string ll) {
+  std::vector<std::string> ret;
+  std::istringstream ss(ll);
+  do {
+    std::string word;
+    ss >> word;
+    if (word.compare("") != 0)
+      ret.push_back(word);
+  } while (ss);
+
+  return ret;
+}
+
+/**
+ * @brief     Parsing Configuration Token
+ * @param[in] ll string to be parsed
+ * @param[in] t  Token type
+ * @retval    int enumerated type
+ */
+unsigned int parseType(std::string ll, input_type t) {
+  int ret;
+  unsigned int i;
+
+  switch (t) {
+    case TOKEN_OPT:
+      for (i = 0; i < Optimizer_string.size(); i++) {
+        if (caseInSensitiveCompare(Optimizer_string[i], ll)) {
+          return (i);
+        }
+      }
+      ret = i - 1;
+      break;
+    case TOKEN_COST:
+      for (i = 0; i < Cost_string.size(); i++) {
+        if (caseInSensitiveCompare(Cost_string[i], ll)) {
+          return (i);
+        }
+      }
+      ret = i - 1;
+      break;
+    case TOKEN_NET:
+      for (i = 0; i < NetworkType_string.size(); i++) {
+        if (caseInSensitiveCompare(NetworkType_string[i], ll)) {
+          return (i);
+        }
+      }
+      ret = i - 1;
+      break;
+    case TOKEN_ACTI:
+      for (i = 0; i < activation_string.size(); i++) {
+        if (caseInSensitiveCompare(activation_string[i], ll)) {
+          return (i);
+        }
+      }
+      ret = i - 1;
+      break;
+    case TOKEN_LAYER:
+      for (i = 0; i < layer_string.size(); i++) {
+        if (caseInSensitiveCompare(layer_string[i], ll)) {
+          return (i);
+        }
+      }
+      ret = i - 1;
+      break;
+    case TOKEN_UNKNOWN:
+    default:
+      ret = 3;
+      break;
+  }
+  return ret;
+}
+
+NeuralNetwork::NeuralNetwork(std::string config) { this->config = config; }
+
+void NeuralNetwork::setConfig(std::string config) { this->config = config; }
+
+void NeuralNetwork::init() {
+  int w, h, id;
+  bool b_zero;
+  std::string l_type;
+  Layers::layer_type t;
+  std::string inifile = config;
+  dictionary *ini = iniparser_load(inifile.c_str());
+
+  if (ini == NULL) {
+    fprintf(stderr, "cannot parse file: %s\n", inifile.c_str());
+  }
+
+  nettype = (Network::net_type)parseType(iniparser_getstring(ini, "Network:Type", NULL), TOKEN_NET);
+  std::vector<std::string> layers_name = parseLayerName(iniparser_getstring(ini, "Network:Layers", NULL));
+  learning_rate = iniparser_getdouble(ini, "Network:Learning_rate", 0.0);
+  opt.learning_rate = learning_rate;
+  epoch = iniparser_getint(ini, "Network:Epoch", 100);
+  opt.type = (Layers::opt_type)parseType(iniparser_getstring(ini, "Network:Optimizer", NULL), TOKEN_OPT);
+  opt.activation = (Layers::acti_type)parseType(iniparser_getstring(ini, "Network:Activation", NULL), TOKEN_ACTI);
+  cost = (Layers::cost_type)parseType(iniparser_getstring(ini, "Network:Cost", NULL), TOKEN_COST);
+
+  model = iniparser_getstring(ini, "Network:Model", "model.bin");
+  batchsize = iniparser_getint(ini, "Network:minibatch", 1);
+
+  opt.beta1 = iniparser_getdouble(ini, "Network:beta1", 0.0);
+  opt.beta2 = iniparser_getdouble(ini, "Network:beta2", 0.0);
+  opt.epsilon = iniparser_getdouble(ini, "Network:epsilon", 0.0);
+
+  for (unsigned int i = 0; i < layers_name.size(); i++)
+    std::cout << layers_name[i] << std::endl;
+
+  loss = 100000.0;
+
+  for (unsigned int i = 0; i < layers_name.size(); i++) {
+    l_type = iniparser_getstring(ini, (layers_name[i] + ":Type").c_str(), NULL);
+    t = (Layers::layer_type)parseType(l_type, TOKEN_LAYER);
+    w = iniparser_getint(ini, (layers_name[i] + ":Width").c_str(), 1);
+    h = iniparser_getint(ini, (layers_name[i] + ":Height").c_str(), 1);
+    id = iniparser_getint(ini, (layers_name[i] + ":Id").c_str(), 0);
+    b_zero = iniparser_getboolean(ini, (layers_name[i] + ":Bias_zero").c_str(), true);
+    std::cout << l_type << " " << t << " " << w << " " << h << " " << b_zero << " " << id << std::endl;
+    switch (t) {
+      case Layers::LAYER_IN: {
+        Layers::InputLayer *inputlayer = new (Layers::InputLayer);
+        inputlayer->setType(t);
+        inputlayer->initialize(batchsize, h, w, id, b_zero);
+        inputlayer->setOptimizer(opt);
+        layers.push_back(inputlayer);
+      } break;
+      case Layers::LAYER_FC: {
+        Layers::FullyConnectedLayer *fclayer = new (Layers::FullyConnectedLayer);
+        fclayer->setType(t);
+        fclayer->initialize(batchsize, h, w, id, b_zero);
+        fclayer->setOptimizer(opt);
+        layers.push_back(fclayer);
+      } break;
+      case Layers::LAYER_OUT: {
+        Layers::OutputLayer *outputlayer = new (Layers::OutputLayer);
+        outputlayer->setType(t);
+        outputlayer->initialize(batchsize, h, w, id, b_zero);
+        outputlayer->setOptimizer(opt);
+        outputlayer->setCost(cost);
+        layers.push_back(outputlayer);
+      } break;
+      case Layers::LAYER_UNKNOWN:
+        break;
+      default:
+        break;
+    }
+  }
+
+  iniparser_freedict(ini);
+}
+
+/**
+ * @brief     free layers
+ */
+void NeuralNetwork::finalize() {
+  for (unsigned int i = 0; i < layers.size(); i++) {
+    delete layers[i];
+  }
+}
+
+/**
+ * @brief     forward propagation using layers object which has layer
+ */
+Matrix NeuralNetwork::forwarding(Matrix input) {
+  Matrix X = input;
+  for (unsigned int i = 0; i < layers.size(); i++) {
+    X = layers[i]->forwarding(X);
+  }
+  return X;
+}
+
+/**
+ * @brief     back propagation
+ *            Call backwarding function of layer in reverse order
+ *            No need to call at first Input Layer (No data to be updated)
+ */
+void NeuralNetwork::backwarding(Matrix input, Matrix expected_output, int iteration) {
+  Matrix Y2 = expected_output;
+  Matrix X = input;
+  Matrix Y = forwarding(X);
+
+  for (unsigned int i = layers.size() - 1; i > 0; i--) {
+    Y2 = layers[i]->backwarding(Y2, i);
+  }
+}
+
+double NeuralNetwork::getLoss() {
+  Layers::OutputLayer *out = static_cast<Layers::OutputLayer *>((layers[layers.size() - 1]));
+  return out->getLoss();
+}
+
+void NeuralNetwork::setLoss(double l) { loss = l; }
+
+NeuralNetwork &NeuralNetwork::copy(NeuralNetwork &from) {
+  if (this != &from) {
+    batchsize = from.batchsize;
+    learning_rate = from.learning_rate;
+    loss = from.loss;
+    opt = from.opt;
+
+    for (unsigned int i = 0; i < layers.size(); i++)
+      layers[i]->copy(from.layers[i]);
+  }
+  return *this;
+}
+
+/**
+ * @brief     save model
+ *            save Weight & Bias Data into file by calling save from layer
+ */
+void NeuralNetwork::saveModel() {
+  std::ofstream modelFile(model, std::ios::out | std::ios::binary);
+  for (unsigned int i = 0; i < layers.size(); i++)
+    layers[i]->save(modelFile);
+  modelFile.close();
+}
+
+/**
+ * @brief     read model
+ *            read Weight & Bias Data into file by calling save from layer
+ */
+void NeuralNetwork::readModel() {
+  if (!is_file_exist(model))
+    return;
+  std::ifstream modelFile(model, std::ios::in | std::ios::binary);
+  for (unsigned int i = 0; i < layers.size(); i++)
+    layers[i]->read(modelFile);
+  modelFile.close();
+  std::cout << "read model file \n";
+}
+}