[CCAPI] Implement basic load save
authorJihoon Lee <jhoon.it.lee@samsung.com>
Tue, 24 Aug 2021 08:05:10 +0000 (17:05 +0900)
committerJijoong Moon <jijoong.moon@samsung.com>
Fri, 27 Aug 2021 09:20:23 +0000 (18:20 +0900)
This patch adds basic load save from CCAPI.

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

Signed-off-by: Jihoon Lee <jhoon.it.lee@samsung.com>
14 files changed:
Applications/LogisticRegression/jni/main.cpp
Applications/LogisticRegression/res/LogisticRegression.ini
Applications/MNIST/jni/main.cpp
Applications/ProductRatings/jni/main.cpp
Applications/ReinforcementLearning/DeepQ/jni/main.cpp
Applications/TransferLearning/CIFAR_Classification/jni/main.cpp
Applications/TransferLearning/CIFAR_Classification/jni/main_func.cpp
Applications/VGG/jni/main.cpp
api/ccapi/include/model.h
nnstreamer/tensor_filter/tensor_filter_nntrainer.cc
nntrainer/layers/layer_impl.cpp
nntrainer/models/neuralnet.cpp
nntrainer/models/neuralnet.h
test/unittest/unittest_nntrainer_models.cpp

index 6a83d17123e2c8dd73bf7cd927c4c3fb186c8739..d2b304f5a5d61d61c4572b7724edaab7d6df47d2 100644 (file)
@@ -160,6 +160,8 @@ int main(int argc, char *argv[]) {
     exit(1);
   }
 
+  const std::string weight_path = "logistic_model.bin";
+
   const std::vector<std::string> args(argv + 1, argv + argc);
   std::string config = args[1];
   data_file = args[2];
@@ -196,16 +198,16 @@ int main(int argc, char *argv[]) {
                   std::move(data_train));
 
     try {
-      NN.train();
+      NN.train({"save_path=" + weight_path});
     } catch (...) {
       std::cerr << "Error during train" << std::endl;
       return 0;
     }
   } else {
     try {
-      NN.readModel();
+      NN.load(weight_path, ml::train::ModelFormat::MODEL_FORMAT_BIN);
     } catch (std::exception &e) {
-      std::cerr << "Error during readModel: " << e.what() << "\n";
+      std::cerr << "Error during loading weights: " << e.what() << "\n";
       return 1;
     }
     std::ifstream dataFile(data_file);
index fae74fb1be0837e82ba34142b3f156c7fd48183e..000d7352a2a25e913a3df90429dbed449f7941b3 100644 (file)
@@ -4,7 +4,6 @@ Type = Regression       # Network Type : Regression, KNN, NeuralNetwork
 Epochs = 100           # Epochs
 Loss = cross           # Loss function : mse (mean squared error)
                   #                 cross ( cross entropy )
-Save_Path = "logistic_model.bin" # model path to save / read
 batch_size = 16                # batch size
 
 [Optimizer]
index c5a3b0624d0dbb8a4236fbcffb08bf44f0a4cdfc..b11abc2015e0d73fc3b5ea74c496783f3b072097 100644 (file)
@@ -298,7 +298,7 @@ int main(int argc, char *argv[]) {
      * @brief     Neural Network Create & Initialization
      */
     model = createModel(ml::train::ModelType::NEURAL_NET);
-    model->loadFromConfig(config);
+    model->load(config, ml::train::ModelFormat::MODEL_FORMAT_INI_WITH_BIN);
   } catch (std::exception &e) {
     std::cerr << "Error during loadFromConfig " << e.what() << std::endl;
     return 1;
@@ -307,7 +307,6 @@ int main(int argc, char *argv[]) {
   try {
     model->compile();
     model->initialize();
-    model->readModel();
     model->setDataset(ml::train::DatasetModeType::MODE_TRAIN, dataset_train);
     model->setDataset(ml::train::DatasetModeType::MODE_VALID, dataset_val);
   } catch (std::exception &e) {
index 539c2ffda9b9e036fc500252f227cd9b5398e831..fd486fc3ae3658d723232c319b1d9cff692c5b6d 100644 (file)
@@ -163,6 +163,8 @@ int main(int argc, char *argv[]) {
     exit(1);
   }
 
+  std::string weight_path = "product_ratings_model.bin";
+
   const std::vector<std::string> args(argv + 1, argv + argc);
   std::string config = args[1];
   data_file = args[2];
@@ -209,7 +211,6 @@ int main(int argc, char *argv[]) {
       std::cerr << "Error during initialize" << std::endl;
       return 1;
     }
-    NN.readModel();
 
     std::cout << "Input dimension: " << NN.getInputDimension()[0];
 
@@ -243,9 +244,9 @@ int main(int argc, char *argv[]) {
     }
   } else {
     try {
-      NN.readModel();
+      NN.load(weight_path, ml::train::ModelFormat::MODEL_FORMAT_BIN);
     } catch (std::exception &e) {
-      std::cerr << "Error during readModel: " << e.what() << "\n";
+      std::cerr << "Error during loading weights: " << e.what() << "\n";
       return 1;
     }
     std::ifstream dataFile(data_file);
index f51dceb48d0eee7120cf6f1c0b70ab79c8b237f8..58d5ca11dc54fd002f9af9909551837190325917 100644 (file)
@@ -250,6 +250,7 @@ int main(int argc, char **argv) {
     std::cout << "./DeepQ Config.ini\n";
     exit(0);
   }
+  const std::string weight_file = "model_deepq.bin";
   const std::vector<std::string> args(argv + 1, argv + argc);
   std::string config = args[0];
 
@@ -295,17 +296,13 @@ int main(int argc, char **argv) {
    * @brief     Read Model Data if any
    */
   try {
-    mainNet.readModel();
+    mainNet.load(weight_file, ml::train::ModelFormat::MODEL_FORMAT_BIN);
+    targetNet.load(weight_file, ml::train::ModelFormat::MODEL_FORMAT_BIN);
   } catch (...) {
-    std::cerr << "Error during readModel\n";
+    std::cerr << "Error during readBin\n";
     return 1;
   }
 
-  /**
-   * @brief     Sync targetNet
-   */
-  targetNet.copy(mainNet);
-
   /**
    * @brief     Run Episode
    */
@@ -524,15 +521,12 @@ int main(int argc, char **argv) {
         std::cerr << "Error during getLoss: " << e.what() << "\n";
         return 1;
       }
-      /**
-       * @brief     copy targetNetwork
-       */
 
-      targetNet.copy(mainNet);
       try {
-        mainNet.saveModel();
+        targetNet.load(weight_file, ml::train::ModelFormat::MODEL_FORMAT_BIN);
+        mainNet.save(weight_file, ml::train::ModelFormat::MODEL_FORMAT_BIN);
       } catch (std::exception &e) {
-        std::cerr << "Error during saveModel: " << e.what() << "\n";
+        std::cerr << "Error during saveBin: " << e.what() << "\n";
         return 1;
       }
     }
index 4d210e549d10601cc78b657a9e79f414a59fa32a..b639bb4ab261aa4129141868bfe6344dc39f278e 100644 (file)
@@ -423,9 +423,8 @@ int main(int argc, char *argv[]) {
   nntrainer::NeuralNetwork NN;
   int status = ML_ERROR_NONE;
   try {
-    status = NN.loadFromConfig(config);
-    if (status != ML_ERROR_NONE)
-      return status;
+    NN.load(config, ml::train::ModelFormat::MODEL_FORMAT_INI);
+    // NN.load(weight_path, ml::train::ModelFormat::MODEL_FORMAT_BIN);
 
     status = NN.compile();
     if (status != ML_ERROR_NONE)
@@ -438,12 +437,6 @@ int main(int argc, char *argv[]) {
     std::cerr << "Error during init" << std::endl;
     return 1;
   }
-  try {
-    NN.readModel();
-  } catch (std::exception &e) {
-    std::cerr << "Error during readModel reason: " << e.what() << std::endl;
-    return 1;
-  }
 
   try {
     NN.train();
index 3bb3d8e10436f9a6face95984cdc62837e573414..d6ed74e0b3bc662fff2c4bddd6a7041ee88686a4 100644 (file)
@@ -290,7 +290,7 @@ int main(int argc, char *argv[]) {
    */
   try {
     model = createModel(ml::train::ModelType::NEURAL_NET);
-    model->loadFromConfig(config);
+    model->load(config, ml::train::ModelFormat::MODEL_FORMAT_INI_WITH_BIN);
   } catch (...) {
     std::cerr << "Error during loadFromConfig" << std::endl;
     return 1;
@@ -302,12 +302,6 @@ int main(int argc, char *argv[]) {
     std::cerr << "Error during init" << std::endl;
     return 1;
   }
-  try {
-    model->readModel();
-  } catch (std::exception &e) {
-    std::cerr << "Error during readModel, reason: " << e.what() << std::endl;
-    return 1;
-  }
   model->setDataset(ml::train::DatasetModeType::MODE_TRAIN, dataset_train);
   model->setDataset(ml::train::DatasetModeType::MODE_VALID, dataset_val);
 
index 1521f4a815876dc470c30dbe68d725f4e355cdf1..84777e3aef3e187949fef6dbbb250e66602dbd08 100644 (file)
@@ -414,7 +414,7 @@ int main(int argc, char *argv[]) {
    */
   nntrainer::NeuralNetwork NN;
   try {
-    NN.loadFromConfig(config);
+    NN.load(config, ml::train::ModelFormat::MODEL_FORMAT_INI_WITH_BIN);
   } catch (...) {
     std::cerr << "Error during loadFromConfig" << std::endl;
     return 0;
@@ -429,7 +429,6 @@ int main(int argc, char *argv[]) {
   }
 
   try {
-    NN.readModel();
     NN.setDataset(ml::train::DatasetModeType::MODE_TRAIN, std::move(db_train));
     NN.setDataset(ml::train::DatasetModeType::MODE_VALID, std::move(db_valid));
     NN.train();
index b6d767edd341fac4c2bf8943d8446487a96156c5..34ab8b1d4d3bd23b7e1896891f8e467e8293595a 100644 (file)
@@ -73,7 +73,7 @@ public:
    * @retval #ML_ERROR_NONE Successful.
    * @retval #ML_ERROR_INVALID_PARAMETER invalid parameter.
    */
-  virtual int loadFromConfig(std::string config) = 0;
+  virtual int loadFromConfig(const std::string &config) = 0;
 
   /**
    * @brief     Minimal set of properties that must be supported by the model
@@ -109,14 +109,6 @@ public:
    */
   virtual int initialize() = 0;
 
-  /**
-   * @brief     save model and training parameters into file
-   * @todo      deprecate this
-   */
-  [[deprecated("use saveModel(const std::string &path_prefix, "
-               "ModelFormat format)")]] virtual void
-  saveModel() = 0;
-
   /**
    * @brief  load model states and training parameters from a file
    * @param file_path file_path to save the model, if full path is not
@@ -124,13 +116,7 @@ public:
    * @param format format to save parameters
    */
   virtual void save(const std::string &file_path,
-                    ModelFormat format = ModelFormat::MODEL_FORMAT_BIN){};
-
-  /**
-   * @brief     read model and training parameters from file
-   * @todo      deprecate this
-   */
-  virtual void readModel() = 0;
+                    ModelFormat format = ModelFormat::MODEL_FORMAT_BIN) = 0;
 
   /**
    * @brief  load model with regard to the format
@@ -139,7 +125,7 @@ public:
    * @param format format to save parameters
    */
   virtual void load(const std::string &file_path,
-                    ModelFormat format = ModelFormat::MODEL_FORMAT_BIN){};
+                    ModelFormat format = ModelFormat::MODEL_FORMAT_BIN) = 0;
 
   /**
    * @brief     Run Model training and validation
index df4a8b225a38e37acfc69dbd91dc85d930f1cd4d..2c55a3f5660c4e41d453f6ba189445df0d484883 100644 (file)
@@ -83,7 +83,6 @@ NNTrainerInference::NNTrainerInference(const std::string &model_config_) :
   loadModel();
   model->compile();
   model->initialize();
-  model->readModel();
 }
 
 const char *NNTrainerInference::getModelConfig() {
@@ -95,7 +94,7 @@ void NNTrainerInference::loadModel() {
   gint64 start_time = g_get_real_time();
 #endif
   model = ml::train::createModel(ml::train::ModelType::NEURAL_NET);
-  model->loadFromConfig(model_config);
+  model->load(model_config, ml::train::ModelFormat::MODEL_FORMAT_INI_WITH_BIN);
 #if (DBG)
   gint64 stop_time = g_get_real_time();
   g_message("Model is loaded: %" G_GINT64_FORMAT, (stop_time - start_time));
index 4a22dc21e02537896b5312ec82a25e386cb202f4..57896f3ec315be27d2d6045cf5054f1bd7b79de1 100644 (file)
@@ -91,7 +91,8 @@ void LayerImpl::setProperty(const std::string &type_str,
     break;
   default:
     std::string msg =
-      "[Layer] Unknown Layer Property Key for value " + std::string(value);
+      "[Layer] Unknown Layer Property Key for value, key: " + type_str +
+      " value: " + value;
     throw exception::not_supported(msg);
   }
 }
index 5c5817bc0293273fbca36ec3363c47b12cde048e..9d6412c906e23241e81e7eea967034f5c4dce432 100644 (file)
@@ -47,7 +47,7 @@
 
 namespace nntrainer {
 
-int NeuralNetwork::loadFromConfig(std::string config) {
+int NeuralNetwork::loadFromConfig(const std::string &config) {
   if (loadedFromConfig == true) {
     ml_loge("cannnot do loadFromConfig twice");
     return ML_ERROR_INVALID_PARAMETER;
@@ -192,6 +192,11 @@ int NeuralNetwork::initialize() {
   }
 
   initialized = true;
+
+  if (!load_path.empty()) {
+    load(load_path, ml::train::ModelFormat::MODEL_FORMAT_BIN);
+  }
+
   return status;
 }
 
@@ -347,6 +352,91 @@ void NeuralNetwork::backwarding(sharedConstTensors label, int iteration) {
   backwarding(iteration);
 }
 
+void NeuralNetwork::save(const std::string &file_path,
+                         ml::train::ModelFormat format) {
+  NNTR_THROW_IF(!initialized, std::runtime_error)
+    << "Cannot save model if not initialized yet, path: " << file_path
+    << " format: " << static_cast<unsigned>(format);
+
+  /// @todo this switch case should be delegating the function call only. It's
+  /// not delegating for now as required logics are managable for now.
+  switch (format) {
+  case ml::train::ModelFormat::MODEL_FORMAT_BIN: {
+    std::ofstream model_file(file_path, std::ios::out | std::ios::binary);
+    /// @todo, if errno == EACCESS or EPERM, throw PERMISSION DENIED error
+    NNTR_THROW_IF(!model_file.good(), std::invalid_argument)
+      << "model file not opened, file path: " << file_path
+      << " reason: " << strerror(errno);
+    for (auto iter = model_graph.cbegin(); iter != model_graph.cend(); iter++) {
+      (*iter)->save(model_file);
+    }
+    model_file.write((char *)&epoch_idx, sizeof(epoch_idx));
+    model_file.write((char *)&iter, sizeof(iter));
+    model_file.close();
+    break;
+  }
+  case ml::train::ModelFormat::MODEL_FORMAT_INI:
+    [[fallthrough]]; // NYI
+  default:
+    throw nntrainer::exception::not_supported(
+      "saving with given format is not supported yet");
+  }
+}
+
+void NeuralNetwork::load(const std::string &file_path,
+                         ml::train::ModelFormat format) {
+  /// @todo this switch case should be delegating the function call only. It's
+  /// not delegating for now as required logics are managable for now.
+  switch (format) {
+  case ml::train::ModelFormat::MODEL_FORMAT_BIN: {
+    NNTR_THROW_IF(!initialized, std::runtime_error)
+      << "Cannot load if not initialized yet, path: " << file_path
+      << " format: " << static_cast<unsigned>(format);
+
+    std::ifstream model_file(file_path, std::ios::in | std::ios::binary);
+    /// @todo, if errno == EACCESS or EPERM, throw PERMISSION DENIED error
+    NNTR_THROW_IF(!model_file.good(), std::invalid_argument)
+      << "model file not opened, file path: " << file_path
+      << " reason: " << strerror(errno);
+
+    for (auto iter = model_graph.cbegin(); iter != model_graph.cend(); iter++) {
+      (*iter)->read(model_file);
+    }
+
+    try {
+      /// this is assuming that the failure is allowed at the end of the file
+      /// read. so, after this line, additional read shouldn't be called
+      checkedRead(model_file, (char *)&epoch_idx, sizeof(epoch_idx),
+                  "[NeuralNetwork::readModel] failed to read epoch_idx");
+      checkedRead(model_file, (char *)&iter, sizeof(iter),
+                  "[NeuralNetwork::readModel] failed to read iteration");
+    } catch (...) {
+      std::cerr << "failed to read epoch idx, proceeding with default index\n";
+    }
+
+    ml_logi("read modelfile: %s", file_path.c_str());
+    break;
+  }
+  case ml::train::ModelFormat::MODEL_FORMAT_INI_WITH_BIN: {
+    int ret = loadFromConfig(file_path);
+    throw_status(ret);
+    if (!save_path.empty()) {
+      /// @todo checkedOpenhere
+      load_path = save_path;
+    }
+    break;
+  }
+  case ml::train::ModelFormat::MODEL_FORMAT_INI: {
+    int ret = loadFromConfig(file_path);
+    throw_status(ret);
+    break;
+  }
+  default:
+    throw nntrainer::exception::not_supported(
+      "loading with given format is not supported yet");
+  }
+}
+
 float NeuralNetwork::getLoss() {
   loss = 0.0f;
 
@@ -369,83 +459,6 @@ NeuralNetwork &NeuralNetwork::copy(NeuralNetwork &from) {
   return *this;
 }
 
-/**
- * @brief     save model to file
- *            save Weight & Bias Data into file by calling save from layer
- *            save training parameters from the optimizer
- * @todo      saving order is based on the topological sort and this may
- *            not match with the ini order
- */
-void NeuralNetwork::saveModel() {
-  if (!initialized)
-    throw std::runtime_error("Cannot save the model before initialize.");
-
-  if (save_path == std::string()) {
-    return;
-  }
-
-  if (!initialized)
-    throw std::runtime_error("Cannot save the model before initialize.");
-
-  std::ofstream model_file(save_path, std::ios::out | std::ios::binary);
-
-  NNTR_THROW_IF(!model_file.good(), std::invalid_argument)
-    << "model file not opened, file path: " << save_path
-    << " reason: " << strerror(errno);
-
-  for (auto iter = model_graph.cbegin(); iter != model_graph.cend(); iter++) {
-    (*iter)->save(model_file);
-  }
-  model_file.write((char *)&epoch_idx, sizeof(epoch_idx));
-  model_file.write((char *)&iter, sizeof(iter));
-  model_file.close();
-}
-
-/**
- * @brief     read model from file
- *            read Weight & Bias Data into file by calling save from layer
- *            read training parameters from the optimizer if continuing train
- * @todo      reading order is based on the topological sort and this may
- *            not match with the ini order
- */
-void NeuralNetwork::readModel() {
-  if (!initialized)
-    throw std::runtime_error("Cannot read the model before initialize.");
-
-  if (save_path == std::string()) {
-    return;
-  }
-
-  if (!isFileExist(save_path)) {
-    ml_logd("skipping reading model, path is not valid: %s", save_path.c_str());
-    return;
-  }
-
-  if (!initialized)
-    throw std::runtime_error("Cannot save the model before initialize.");
-
-  std::ifstream model_file(save_path, std::ios::in | std::ios::binary);
-
-  for (auto iter = model_graph.cbegin(); iter != model_graph.cend(); iter++) {
-    (*iter)->read(model_file);
-  }
-
-  try {
-    /// this is assuming that the failure is allowed at the end of the file
-    /// read. so, after this line, additional read shouldn't be called
-    checkedRead(model_file, (char *)&epoch_idx, sizeof(epoch_idx),
-                "[NeuralNetwork::readModel] failed to read epoch_idx");
-    checkedRead(model_file, (char *)&iter, sizeof(iter),
-                "[NeuralNetwork::readModel] failed to read iteration");
-  } catch (...) {
-    model_file.close();
-    std::cerr << "failed to read epoch idx, proceeding with default index\n";
-  }
-
-  model_file.close();
-  ml_logi("read modelfile: %s", save_path.c_str());
-}
-
 void NeuralNetwork::setBatchSize(unsigned int batch) {
   batch_size = batch;
 
@@ -654,7 +667,9 @@ int NeuralNetwork::train_run() {
       throw std::runtime_error("No training data");
 
     training.loss /= count;
-    saveModel();
+    if (!save_path.empty()) {
+      save(save_path, ml::train::ModelFormat::MODEL_FORMAT_BIN);
+    }
 
     std::cout << "#" << epoch_idx << "/" << epochs
               << " - Training Loss: " << training.loss;
index 54a1096363316ea1c4fb7d603eaa3a40ade2f23e..71511744713c8730ab6a4bb88f713960ce63710b 100644 (file)
@@ -101,6 +101,7 @@ public:
     initialized(false),
     compiled(false),
     loadedFromConfig(false),
+    load_path(""),
     app_context(app_context_),
     in_place_optimization(in_place_opt) {}
 
@@ -146,7 +147,7 @@ public:
    * @retval #ML_ERROR_NONE Successful.
    * @retval #ML_ERROR_INVALID_PARAMETER invalid parameter.
    */
-  int loadFromConfig(std::string config);
+  int loadFromConfig(const std::string &config);
 
   /**
    * @brief     Compile the graph in the model
@@ -244,14 +245,20 @@ public:
   void backwarding(int iteration);
 
   /**
-   * @brief     save model and training parameters into file
+   * @copydoc Model::save(const std::string &file_path, ml::train::ModelFormat
+   * format);
    */
-  void saveModel();
+  void save(const std::string &file_path,
+            ml::train::ModelFormat format =
+              ml::train::ModelFormat::MODEL_FORMAT_BIN) override;
 
   /**
-   * @brief     read model and training parameters from file
+   * @copydoc Model::load(const std::string &file_path, ml::train::ModelFormat
+   * format);
    */
-  void readModel();
+  void load(const std::string &file_path,
+            ml::train::ModelFormat format =
+              ml::train::ModelFormat::MODEL_FORMAT_BIN) override;
 
   /**
    * @brief     get Epochs
@@ -557,6 +564,8 @@ private:
 
   bool loadedFromConfig; /**< Check if config is loaded to prevent load twice */
 
+  std::string load_path; /**< path to load weights when initialize  */
+
   RunStats validation; /** validation statistics of the model */
   RunStats training;   /** training statistics of the model */
   RunStats testing;    /** testing statistics of the model */
@@ -616,6 +625,7 @@ private:
     swap(lhs.model_graph, rhs.model_graph);
     swap(lhs.compiled, rhs.compiled);
     swap(lhs.loadedFromConfig, rhs.loadedFromConfig);
+    swap(lhs.load_path, rhs.load_path);
   }
 
   /**
index c322b195129647e7ba53c88e6a8100af9cc44114..b80b405dc3f13e8fc7966ccf958770e4e63b9e74 100644 (file)
@@ -1396,13 +1396,13 @@ TEST(nntrainerModels, read_save_01_n) {
   EXPECT_NO_THROW(NN.addLayer(layer_node));
   EXPECT_NO_THROW(NN.setProperty({"loss=mse"}));
 
-  EXPECT_THROW(NN.readModel(), std::runtime_error);
-  EXPECT_THROW(NN.saveModel(), std::runtime_error);
+  EXPECT_THROW(NN.load("model.bin"), std::runtime_error);
+  EXPECT_THROW(NN.save("model.bin"), std::runtime_error);
 
   EXPECT_EQ(NN.compile(), ML_ERROR_NONE);
 
-  EXPECT_THROW(NN.readModel(), std::runtime_error);
-  EXPECT_THROW(NN.saveModel(), std::runtime_error);
+  EXPECT_THROW(NN.load("model.bin"), std::runtime_error);
+  EXPECT_THROW(NN.save("model.bin"), std::runtime_error);
 }
 
 /**