[ini] Implement model save
authorJihoon Lee <jhoon.it.lee@samsung.com>
Sat, 28 Aug 2021 05:41:38 +0000 (14:41 +0900)
committerJijoong Moon <jijoong.moon@samsung.com>
Tue, 7 Sep 2021 10:13:50 +0000 (19:13 +0900)
This patch implements model section save in ini format

**Side Changes proposed in this PR:**
- Move `THROW_STATUS` and `RETURN_STATUS` to util_func.h

**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>
13 files changed:
api/capi/include/nntrainer.h
nntrainer/graph/network_graph.cpp
nntrainer/layers/input_layer.cpp
nntrainer/layers/layer_impl.cpp
nntrainer/models/model_common_properties.cpp
nntrainer/models/model_common_properties.h
nntrainer/models/neuralnet.cpp
nntrainer/models/neuralnet.h
nntrainer/utils/ini_wrapper.cpp
nntrainer/utils/ini_wrapper.h
nntrainer/utils/parse_util.h
nntrainer/utils/util_func.h
test/ccapi/unittest_ccapi.cpp

index b3c3522fb381afa3f3d19aa3f7d6689c0d972c8d..d8ebf0dbf9b3ed640077567c41347ef2365faeea 100644 (file)
@@ -553,6 +553,8 @@ int ml_train_dataset_set_property_for_mode(ml_train_dataset_h dataset,
  * configurations. Unless stated otherwise, ml_train_model_compile() has to
  * be called upon the @a model before calling this function.
  * @since_tizen 6.5
+ * @remarks Saved ini, if any, is not guaranteed to be identical to the original
+ * ini that maybe used to load the model.
  * @remarks If you want to access only internal storage by using this function,
  * you should add privilege %http://tizen.org/privilege/mediastorage. Or, if you
  * want to access only external storage by using this function, you should add
index cc7bac37085c342f06af6d81723ae8bc1c890d04..67468d15c8b7f9f38a19f9ec336138b89da8a868 100644 (file)
@@ -32,6 +32,7 @@
 #include <rnn.h>
 #include <split_layer.h>
 #include <time_dist.h>
+#include <util_func.h>
 
 #define LNODE(x) std::static_pointer_cast<LayerNode>(x)
 
index 77ff282e5d8287ff76be728b7f501e0f593a4fd6..fc8ca6093b02e053917a7a373b91731a76d1379a 100644 (file)
@@ -25,6 +25,7 @@
 #include <nntrainer_error.h>
 #include <nntrainer_log.h>
 #include <parse_util.h>
+#include <util_func.h>
 
 namespace nntrainer {
 
index 57896f3ec315be27d2d6045cf5054f1bd7b79de1..ef9af6eeee676488793dfc4920a156794e98957b 100644 (file)
@@ -20,7 +20,7 @@
 #include <nntrainer_log.h>
 #include <node_exporter.h>
 #include <parse_util.h>
-
+#include <util_func.h>
 namespace nntrainer {
 
 LayerImpl::LayerImpl() :
index d0be97fb111c584938d9bc5f58ffe43a0a588cdb..b12636b96c2fee9ccd89e212cf6af0b232d85af7 100644 (file)
@@ -23,7 +23,7 @@ bool LossType::isValid(const std::string &value) const {
   return istrequal(value, "cross") || istrequal(value, "mse");
 }
 
-BatchSize::BatchSize(unsigned int value) { set(value); }
+TrainingBatchSize::TrainingBatchSize(unsigned int value) { set(value); }
 
 ContinueTrain::ContinueTrain(bool value) { set(value); }
 
index fc5faa54cf0ee462d98cbb0faf7a7a7a881dd37a..cdb02159db4d3e59f8104fc11e59682200263828 100644 (file)
@@ -66,7 +66,7 @@ public:
  * @brief model batch size property
  *
  */
-class BatchSize : public PositiveIntegerProperty {
+class TrainingBatchSize : public PositiveIntegerProperty {
 public:
   static constexpr const char *key = "batch_size"; /**< unique key to access */
   using prop_tag = uint_prop_tag;                  /**< property type */
@@ -76,7 +76,7 @@ public:
    *
    * @param value value to set, defaults to 1
    */
-  BatchSize(unsigned int value = 1);
+  TrainingBatchSize(unsigned int value = 1);
 };
 
 /**
index 2749cb54fb858d4151bd65ae65b38a7ff5317df4..f7706b500378de39d04f7238d38ea63f21723963 100644 (file)
 
 #include <databuffer.h>
 #include <ini_interpreter.h>
+#include <ini_wrapper.h>
 #include <model_loader.h>
 #include <neuralnet.h>
 #include <nntrainer_error.h>
 #include <nntrainer_log.h>
 #include <node_exporter.h>
 #include <optimizer_context.h>
-#include <parse_util.h>
 #include <profiler.h>
 #include <util_func.h>
 
@@ -49,8 +49,8 @@ namespace nntrainer {
 
 NeuralNetwork::NeuralNetwork(AppContext app_context_, bool in_place_opt) :
   model_props(props::LossType()),
-  model_flex_props(props::Epochs(), props::BatchSize(), props::SavePath()),
-  continue_train(false),
+  model_flex_props(props::Epochs(), props::TrainingBatchSize(),
+                   props::SavePath(), props::ContinueTrain()),
   load_path(std::string()),
   epoch_idx(0),
   iter(0),
@@ -123,7 +123,8 @@ int NeuralNetwork::initialize() {
 
   ml_logd("initializing neural network, layer size: %d", n_layers);
 
-  model_graph.setBatchSize(std::get<props::BatchSize>(model_flex_props));
+  model_graph.setBatchSize(
+    std::get<props::TrainingBatchSize>(model_flex_props));
 
   status = model_graph.initialize();
   NN_RETURN_STATUS();
@@ -414,11 +415,27 @@ NeuralNetwork &NeuralNetwork::copy(NeuralNetwork &from) {
 }
 
 void NeuralNetwork::saveModelIni(const std::string &file_path) {
-  IniGraphInterpreter interpreter;
+  NNTR_THROW_IF(isFileExist(file_path), std::invalid_argument)
+    << "There is already a file, overriding to the exisiting file is not "
+       "permitted, path: "
+    << file_path;
+
+  IniSection model_section("model");
+  model_section.setEntry("type", "NeuralNetwork");
 
-  /// @note this is to ensure permission checks are done
-  checkedOpenStream<std::ofstream>(file_path, std::ios::out);
-  /// @todo serialize model props
+  Exporter e;
+  e.saveResult(model_props, ExportMethods::METHOD_STRINGVECTOR, this);
+  e.saveResult(model_flex_props, ExportMethods::METHOD_STRINGVECTOR, this);
+
+  const auto key_val_pairs = e.getResult<ExportMethods::METHOD_STRINGVECTOR>();
+  for (const auto &pair : *key_val_pairs) {
+    model_section.setEntry(pair.first, pair.second);
+  }
+
+  IniWrapper wrapper("model_saver", {model_section});
+  wrapper.save_ini(file_path);
+
+  IniGraphInterpreter interpreter;
   /// @todo serialize dataset props
   /// @todo serialize optimizer props
   interpreter.serialize(model_graph, file_path);
@@ -487,8 +504,7 @@ std::vector<float *> NeuralNetwork::inference(std::vector<float *> &input,
   for (unsigned int idx = 0; idx < in_dim.size(); idx++) {
     in_dim[idx].batch(batch_size);
     input_tensors.emplace_back(MAKE_SHARED_TENSOR(Tensor::Map(
-      input[idx], in_dim[idx].getDataLen() * sizeof(float),
-      in_dim[idx], 0)));
+      input[idx], in_dim[idx].getDataLen() * sizeof(float), in_dim[idx], 0)));
   }
 
   sharedConstTensors output_tensors = inference(input_tensors, false);
@@ -538,7 +554,8 @@ int NeuralNetwork::train(const std::vector<std::string> &values) {
   setTrainConfig(values);
 
   /** set batch size just before training */
-  model_graph.setBatchSize(std::get<props::BatchSize>(model_flex_props));
+  model_graph.setBatchSize(
+    std::get<props::TrainingBatchSize>(model_flex_props));
 
   status = allocate(true);
   NN_RETURN_STATUS();
@@ -561,7 +578,7 @@ int NeuralNetwork::train(const std::vector<std::string> &values) {
 int NeuralNetwork::train_run() {
   int status = ML_ERROR_NONE;
 
-  if (!continue_train.get()) {
+  if (!std::get<props::ContinueTrain>(model_flex_props)) {
     epoch_idx = 0;
     iter = 0;
   }
@@ -570,7 +587,7 @@ int NeuralNetwork::train_run() {
   auto const &last_layer_node =
     model_graph.getSortedLayerNode(model_graph.size() - 1);
 
-  auto batch_size = std::get<props::BatchSize>(model_flex_props);
+  auto batch_size = std::get<props::TrainingBatchSize>(model_flex_props);
 
   auto &output = last_layer_node->getOutput(0);
   auto &label = last_layer_node->getOutputGrad(0);
@@ -651,8 +668,8 @@ int NeuralNetwork::train_run() {
               << " - Training Loss: " << stat.loss;
   };
 
-  auto eval_for_iteration = [this, &output, &label, batch_size](RunStats &stat,
-                                                    DataBuffer &buffer) {
+  auto eval_for_iteration = [this, &output, &label,
+                             batch_size](RunStats &stat, DataBuffer &buffer) {
     forwarding(false);
     auto model_out = output.argmax();
     auto label_out = label.argmax();
@@ -698,7 +715,6 @@ void swap(NeuralNetwork &lhs, NeuralNetwork &rhs) {
 
     swap(lhs.model_props, rhs.model_props);
     swap(lhs.model_flex_props, rhs.model_flex_props);
-    swap(lhs.continue_train, rhs.continue_train);
     swap(lhs.load_path, rhs.load_path);
     swap(lhs.epoch_idx, rhs.epoch_idx);
     swap(lhs.iter, rhs.iter);
index 708037cd65bfd684de3fad3f9b8a74d0a1a1d471..0d41ba4cda5cd41cec766324b6300935aea0ce83 100644 (file)
@@ -490,14 +490,11 @@ public:
 
 private:
   using FlexiblePropTypes =
-    std::tuple<props::Epochs, props::BatchSize, props::SavePath>;
+    std::tuple<props::Epochs, props::TrainingBatchSize, props::SavePath, props::ContinueTrain>;
   using RigidPropTypes = std::tuple<props::LossType>;
 
   RigidPropTypes model_props;         /**< model props */
   FlexiblePropTypes model_flex_props; /**< model train props */
-  props::ContinueTrain
-    continue_train; /**< true if epochs and iteration should be retained during
-                       separate model run */
   std::string load_path; /**< path to load weights when initialize  */
 
   /**
index 0e30bcf572961b9ac2a777e8dd50bc8aa066d85d..2fe0015a475990fa7f97651ae8d0e82a6c18a6c2 100644 (file)
  */
 #include <ini_wrapper.h>
 
+#include <regex>
+
 #include <nntrainer_error.h>
 #include <parse_util.h>
-#include <regex>
+#include <util_func.h>
 
 namespace nntrainer {
 
@@ -116,15 +118,12 @@ void IniWrapper::updateSections(const Sections &sections_) {
 void IniWrapper::save_ini() { save_ini(getIniName()); }
 
 void IniWrapper::save_ini(const std::string &ini_name) {
-  std::ofstream out(ini_name.c_str(), std::ios_base::out);
-  NNTR_THROW_IF(!out.good(), std::runtime_error) << "cannot open ini";
+  auto out = checkedOpenStream<std::ofstream>(ini_name, std::ios_base::app);
 
   for (auto &it : sections) {
     it.print(out);
     out << std::endl;
   }
-
-  out.close();
 }
 
 } // namespace nntrainer
index 4ff051d17e63890d2fc95eb843fa2c6630dbf0a7..d12fa3cae1a65decadf9affef236853a82837ac0 100644 (file)
@@ -336,6 +336,7 @@ public:
    * @param ini_name ini name to svae
    */
   void save_ini(const std::string &ini_name);
+
   /**
    * @brief erase ini
    *
index 7f21f920e2d359c6b7a61879b0d37920d0e6f801..e517906f7f70334b0951b5bda0662ca99db3bc8a 100644 (file)
 
 namespace nntrainer {
 
-#define NN_RETURN_STATUS()         \
-  do {                             \
-    if (status != ML_ERROR_NONE) { \
-      return status;               \
-    }                              \
-  } while (0)
-
 /**
  * @brief     Enumeration for input configuration file parsing
  *            0. MODEL   ( Model Token )
@@ -61,28 +54,6 @@ typedef enum {
   TOKEN_UNKNOWN
 } InputType;
 
-/**
- * @brief convert integer based status to throw
- *
- * @param status status to throw
- */
-inline void throw_status(int status) {
-  switch (status) {
-  case ML_ERROR_NONE:
-    break;
-  case ML_ERROR_INVALID_PARAMETER:
-    throw std::invalid_argument("invalid argument from c style throw");
-  case ML_ERROR_OUT_OF_MEMORY:
-    throw std::bad_alloc();
-  case ML_ERROR_TIMED_OUT:
-    throw std::runtime_error("Timed out from c style throw");
-  case ML_ERROR_PERMISSION_DENIED:
-    throw std::runtime_error("permission denied from c style throw");
-  case ML_ERROR_UNKNOWN:
-  default:
-    throw std::runtime_error("unknown error from c style throw");
-  }
-}
 /**
  * @brief     Parsing Layer Property
  * @param[in] property string to be parsed
index 411c02a410caa4202120d2d03c332674aed1d704..9106087ba77c53163686027799bb9c7a663505a3 100644 (file)
 #include <tensor.h>
 namespace nntrainer {
 
+#define NN_RETURN_STATUS()         \
+  do {                             \
+    if (status != ML_ERROR_NONE) { \
+      return status;               \
+    }                              \
+  } while (0)
+
+/**
+ * @brief convert integer based status to throw
+ *
+ * @param status status to throw
+ */
+inline void throw_status(int status) {
+  switch (status) {
+  case ML_ERROR_NONE:
+    break;
+  case ML_ERROR_INVALID_PARAMETER:
+    throw std::invalid_argument("invalid argument from c style throw");
+  case ML_ERROR_OUT_OF_MEMORY:
+    throw std::bad_alloc();
+  case ML_ERROR_TIMED_OUT:
+    throw std::runtime_error("Timed out from c style throw");
+  case ML_ERROR_PERMISSION_DENIED:
+    throw std::runtime_error("permission denied from c style throw");
+  case ML_ERROR_UNKNOWN:
+  default:
+    throw std::runtime_error("unknown error from c style throw");
+  }
+}
+
 /**
  * @brief     get the seed
  * @return    seed
index 7779fe639e6bf9683d19fb080e30c99e1644460b..31868902c53b88f0d239d8409a58ce6405cd994c 100644 (file)
@@ -413,12 +413,12 @@ TEST(nntrainer_ccapi, save_ini_p) {
   EXPECT_EQ(model->compile(), ML_ERROR_NONE);
   EXPECT_EQ(model->initialize(), ML_ERROR_NONE);
   auto saved_ini_name = s.getIniName() + "_saved";
-  model->save(saved_ini_name, ml::train::ModelFormat::MODEL_FORMAT_INI);
-
   if (remove(saved_ini_name.c_str())) {
     std::cerr << "remove ini " << saved_ini_name
               << "failed, reason: " << strerror(errno);
   }
+
+  model->save(saved_ini_name, ml::train::ModelFormat::MODEL_FORMAT_INI);
 }
 
 /**