[init] Making random deterministic
authorParichay Kapoor <pk.kapoor@samsung.com>
Tue, 16 Jun 2020 09:10:21 +0000 (18:10 +0900)
committerJijoong Moon <jijoong.moon@samsung.com>
Wed, 17 Jun 2020 06:00:18 +0000 (15:00 +0900)
Adding determinism to the random number generators in the library
DataBuffer has multiple threads but single thread of train/valid/test
which run in sequence in my understanding

Resolves #167

Signed-off-by: Parichay Kapoor <pk.kapoor@samsung.com>
12 files changed:
nntrainer/include/databuffer.h
nntrainer/include/tensor.h
nntrainer/include/util_func.h
nntrainer/src/conv2d_layer.cpp
nntrainer/src/databuffer.cpp
nntrainer/src/fc_layer.cpp
nntrainer/src/layer.cpp
nntrainer/src/tensor.cpp
nntrainer/src/util_func.cpp
test/nntrainer_test_util.cpp
test/unittest/unittest_nntrainer_tensor.cpp
test/unittest/unittest_util_func.cpp

index 2398bc0..2a3806e 100644 (file)
 #include <functional>
 #include <iostream>
 #include <memory>
+#include <random>
 #include <tensor_dim.h>
 #include <thread>
+#include <util_func.h>
 #include <vector>
 
 /*
@@ -135,6 +137,7 @@ public:
     train_running = false;
     val_running = false;
     test_running = false;
+    rng.seed(getSeed());
   };
 
   /**
@@ -346,6 +349,8 @@ protected:
    * @retval    int return value
    */
   int rangeRandom(int min, int max);
+
+  std::mt19937 rng;
 };
 
 } // namespace nntrainer
index efb2d10..fee33e1 100644 (file)
@@ -351,7 +351,7 @@ public:
   int getBatch() const { return dim.batch(); };
 
   /**
-   * @brief     Set the elelemnt value
+   * @brief     Set the element value
    * @param[in] batch batch location
    * @param[in] c channel location
    * @param[in] i height location
@@ -362,6 +362,26 @@ public:
                 unsigned int j, float value);
 
   /**
+   * @brief     Fill the Tensor elements with value
+   * @param[in] value value to be stored
+   */
+  void setValue(float value);
+
+  /**
+   * @brief     Set the tensor with random normal distribution
+   * @param[in] mean mean of the distribution
+   * @param[in] std standard deviation of the distribution
+   */
+  void setRandNormal(float mean = 0.0, float std = 0.05);
+
+  /**
+   * @brief     Set the tensor with random uniform distribution
+   * @param[in] min minimum value for the distribution
+   * @param[in] max maximum value for the distribution
+   */
+  void setRandUniform(float min = -0.05, float max = 0.05);
+
+  /**
    * @brief     Copy the Tensor
    * @param[in] from Tensor to be Copyed
    * @retval    Matix
@@ -398,7 +418,6 @@ public:
    */
   float *getData() { return data.data(); }
 
-
   const float *getData() const { return data.data(); }
 
   /**
@@ -407,7 +426,6 @@ public:
    */
   float *getAddress(unsigned int i);
 
-
 private:
   /**< handle the data as a std::vector type */
   std::vector<float> data;
@@ -415,6 +433,8 @@ private:
 
   static constexpr float min_limits = std::numeric_limits<float>::min();
   static constexpr float max_limits = std::numeric_limits<float>::max();
+  template<typename T>
+    void setDist(T dist);
 };
 
 /**
index cf51f13..ef9db26 100644 (file)
 namespace nntrainer {
 
 /**
+ * @brief     get the seed
+ * @retVal    seed
+ */
+unsigned int getSeed();
+
+/**
  * @brief     derivative softmax function for Tensor Type
  * @param[in] x Tensor
  * @retVal    Tensor
@@ -44,9 +50,8 @@ Tensor softmax(Tensor t);
 
 /**
  * @brief     random function
- * @param[in] x float
  */
-float random(float x);
+float random();
 
 /**
  * @brief     sqrt function for float type
index 0618218..0ae1699 100644 (file)
@@ -17,7 +17,6 @@
 #include <nntrainer_error.h>
 #include <nntrainer_log.h>
 #include <parse_util.h>
-#include <random>
 #include <util_func.h>
 
 namespace nntrainer {
@@ -50,7 +49,7 @@ int Conv2DLayer::initialize(bool last) {
     if (init_zero) {
       B = 0.0;
     } else {
-      B = random(B);
+      B = random();
     }
     bias.push_back(B);
   }
index e7b5253..d6ebeda 100644 (file)
@@ -52,13 +52,8 @@ std::condition_variable cv_val;
 std::condition_variable cv_test;
 
 int DataBuffer::rangeRandom(int min, int max) {
-  int n = max - min + 1;
-  int remainder = RAND_MAX % n;
-  int x;
-  do {
-    x = rand();
-  } while (x >= RAND_MAX - remainder);
-  return min + x % n;
+  std::uniform_int_distribution<int> dist(min, max);
+  return dist(rng);
 }
 
 int DataBuffer::run(BufferType type) {
index 60d4c65..9faa29b 100644 (file)
@@ -54,7 +54,7 @@ int FullyConnectedLayer::initialize(bool last) {
   if (init_zero) {
     bias.setZero();
   } else {
-    bias = bias.apply(random);
+    bias.setRandUniform(-0.5, 0.5);
   }
   return status;
 }
index 940658c..2f560ee 100644 (file)
 #include <nntrainer_error.h>
 #include <nntrainer_log.h>
 #include <parse_util.h>
-#include <random>
 #include <util_func.h>
 
 namespace nntrainer {
 
-static auto rng = [] {
-  std::mt19937 rng;
-  rng.seed(std::random_device()());
-  return rng;
-}();
-
-template <typename... Args>
-static void RandNormal(unsigned int b_n, Tensor &w, Args &&... args) {
-  std::normal_distribution<float> dist(std::forward<Args>(args)...);
-  unsigned int channel = w.getChannel();
-  unsigned int width = w.getWidth();
-  unsigned int height = w.getHeight();
-
-  for (unsigned int k = 0; k < channel; ++k) {
-    for (unsigned int i = 0; i < width; ++i) {
-      for (unsigned int j = 0; j < height; ++j) {
-        w.setValue(b_n, k, j, i, dist(rng));
-      }
-    }
-  }
-}
-
-template <typename... Args>
-static void RandUniform(unsigned int b_n, Tensor &w, Args &&... args) {
-  std::uniform_real_distribution<float> dist(std::forward<Args>(args)...);
-
-  unsigned int channel = w.getChannel();
-  unsigned int width = w.getWidth();
-  unsigned int height = w.getHeight();
-
-  for (unsigned int k = 0; k < channel; ++k) {
-    for (unsigned int i = 0; i < width; ++i) {
-      for (unsigned int j = 0; j < height; ++j) {
-        w.setValue(b_n, k, j, i, dist(rng));
-      }
-    }
-  }
-}
-
 int Layer::setActivation(ActiType acti) {
   int status = ML_ERROR_NONE;
   if (acti == ACT_UNKNOWN) {
@@ -132,32 +92,30 @@ Tensor Layer::initializeWeight(TensorDim w_dim, WeightIniType init_type,
     init_type = WEIGHT_XAVIER_NORMAL;
   }
 
-  for (unsigned int i = 0; i < w_dim.batch(); ++i) {
-    switch (init_type) {
-    case WEIGHT_LECUN_NORMAL:
-      RandNormal(i, w, 0, sqrt(1.0 / dim.height()));
-      break;
-    case WEIGHT_XAVIER_NORMAL:
-      RandNormal(i, w, 0, sqrt(2.0 / (dim.width() + dim.height())));
-      break;
-    case WEIGHT_HE_NORMAL:
-      RandNormal(i, w, 0, sqrt(2.0 / (dim.height())));
-      break;
-    case WEIGHT_LECUN_UNIFORM:
-      RandUniform(i, w, -1.0 * sqrt(1.0 / dim.height()),
-                  sqrt(1.0 / dim.height()));
-      break;
-    case WEIGHT_XAVIER_UNIFORM:
-      RandUniform(i, w, -1.0 * sqrt(6.0 / (dim.height() + dim.width())),
-                  sqrt(6.0 / (dim.height() + dim.width())));
-      break;
-    case WEIGHT_HE_UNIFORM:
-      RandUniform(i, w, -1.0 * sqrt(6.0 / (dim.height())),
-                  sqrt(6.0 / (dim.height())));
-      break;
-    default:
-      break;
-    }
+  switch (init_type) {
+  case WEIGHT_LECUN_NORMAL:
+    w.setRandNormal(0, sqrt(1.0 / dim.height()));
+    break;
+  case WEIGHT_XAVIER_NORMAL:
+    w.setRandNormal(0, sqrt(2.0 / (dim.width() + dim.height())));
+    break;
+  case WEIGHT_HE_NORMAL:
+    w.setRandNormal(0, sqrt(2.0 / (dim.height())));
+    break;
+  case WEIGHT_LECUN_UNIFORM:
+    w.setRandUniform(-1.0 * sqrt(1.0 / dim.height()),
+        sqrt(1.0 / dim.height()));
+    break;
+  case WEIGHT_XAVIER_UNIFORM:
+    w.setRandUniform(-1.0 * sqrt(6.0 / (dim.height() + dim.width())),
+        sqrt(6.0 / (dim.height() + dim.width())));
+    break;
+  case WEIGHT_HE_UNIFORM:
+    w.setRandUniform(-1.0 * sqrt(6.0 / (dim.height())),
+        sqrt(6.0 / (dim.height())));
+    break;
+  default:
+    break;
   }
   return w;
 }
index 238c018..9e2c36f 100644 (file)
 #include <nntrainer_error.h>
 #include <nntrainer_log.h>
 #include <parse_util.h>
+#include <random>
 #include <sstream>
 #include <stdio.h>
 #include <tensor.h>
+#include <util_func.h>
 
 #include <lazy_tensor.h>
 
 
 namespace nntrainer {
 
+static auto rng = [] {
+  std::mt19937 rng;
+  rng.seed(getSeed());
+  return rng;
+}();
+
 Tensor::Tensor(const TensorDim d) {
   dim = d;
   this->data = std::vector<float>(dim.getDataLen());
@@ -95,6 +103,23 @@ void Tensor::setValue(unsigned int batch, unsigned int c, unsigned int h,
              c * dim.height() * dim.width() + h * dim.width() + w] = value;
 }
 
+template<typename T>
+void Tensor::setDist(T dist) {
+  for (unsigned int i = 0; i < dim.getDataLen(); ++i) {
+    data[i] = dist(rng);
+  }
+}
+
+void Tensor::setRandNormal(float mean, float std) {
+  setDist<std::normal_distribution<float>>(
+      std::normal_distribution<float> (mean, std));
+}
+
+void Tensor::setRandUniform(float min, float max) {
+  setDist<std::uniform_real_distribution<float>>(
+      std::uniform_real_distribution<float> (min, max));
+}
+
 Tensor::Tensor(std::vector<std::vector<float>> const &d) {
 
   dim.height(d.size());
@@ -814,8 +839,12 @@ Tensor Tensor::average(int axis) const {
   return result;
 }
 
+void Tensor::setValue(float val) {
+  memset(this->data.data(), val, sizeof(float) * dim.getDataLen());
+}
+
 void Tensor::setZero() {
-  memset(this->data.data(), 0, sizeof(float) * dim.getDataLen());
+  setValue(0);
 }
 
 int Tensor::argmax() {
index 01bbea1..35490a3 100644 (file)
 
 #include <assert.h>
 #include <math.h>
+#include <random>
 #include <tensor.h>
 #include <util_func.h>
 
 namespace nntrainer {
 
+static auto rng = [] {
+  std::mt19937 rng;
+  rng.seed(getSeed());
+  return rng;
+}();
+static std::uniform_real_distribution<float> dist(-0.5, 0.5);
+
+unsigned int getSeed() { return 0; }
+
 Tensor softmaxPrime(Tensor x) {
   int batch = x.getBatch();
   int channel = x.getChannel();
@@ -113,7 +123,7 @@ Tensor softmax(Tensor t) {
   return result;
 }
 
-float random(float x) { return (float)(rand() % 10000 + 1) / 10000 - 0.5; }
+float random() { return dist(rng); }
 
 float sqrtFloat(float x) { return (float)(sqrt(x)); };
 
index d8195cf..1ee66f8 100644 (file)
@@ -24,6 +24,7 @@
 #include "nntrainer_test_util.h"
 #include <climits>
 #include <iostream>
+#include <random>
 
 #define num_class 10
 #define mini_batch 16
@@ -33,6 +34,7 @@ static bool *duplicate;
 static bool *valduplicate;
 static bool alloc_train = false;
 static bool alloc_val = false;
+static std::mt19937 rng(0);
 
 /**
  * @brief replace string and save it in file
@@ -66,13 +68,8 @@ void replaceString(const std::string &from, const std::string &to,
  * @retval    min < random value < max
  */
 static int rangeRandom(int min, int max) {
-  int n = max - min + 1;
-  int remainder = RAND_MAX % n;
-  int x;
-  do {
-    x = rand();
-  } while (x >= RAND_MAX - remainder);
-  return min + x % n;
+  std::uniform_int_distribution<int> dist(min, max);
+  return dist(rng);
 }
 
 /**
index 79c5ef0..318c5cc 100644 (file)
@@ -799,6 +799,17 @@ end_transpose_01_p:
   EXPECT_EQ(status, ML_ERROR_NONE);
 }
 
+TEST(nntrainer_Tensor, set_01_p) {
+  nntrainer::Tensor tensor = nntrainer::Tensor(1, 1, 1, 1);
+
+  tensor.setZero();
+  EXPECT_EQ(tensor.getValue(0, 0, 0, 0), 0.0);
+
+  tensor.setRandUniform(-0.5, 0);
+  float val = tensor.getValue(0, 0, 0, 0);
+  EXPECT_TRUE(val >= -0.5 && val < 0);
+}
+
 /**
  * @brief Main gtest
  */
index 355ee96..10ce8f9 100644 (file)
@@ -95,15 +95,6 @@ TEST(nntrainer_util_func, softmax_prime_01_p) {
   }
 }
 
-TEST(nntrainer_util_func, random_01_p) {
-  int status = ML_ERROR_INVALID_PARAMETER;
-  srand(time(NULL));
-  float x = nntrainer::random(0.0);
-  if (-1.0 < x && x < 1.0)
-    status = ML_ERROR_NONE;
-  EXPECT_EQ(status, ML_ERROR_NONE);
-}
-
 TEST(nntrainer_util_func, sqrtFloat_01_p) {
   int status = ML_ERROR_INVALID_PARAMETER;