[ GTEST ] Add gtest for NEON fp16 tensor unittest in Android
authorDebadri Samaddar <s.debadri@samsung.com>
Thu, 3 Aug 2023 14:25:17 +0000 (19:55 +0530)
committerJijoong Moon <jijoong.moon@samsung.com>
Mon, 21 Aug 2023 06:29:23 +0000 (15:29 +0900)
Enables the gtest for half precision NEON functions in Android(ARM).

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

Signed-off-by: Debadri Samaddar <s.debadri@samsung.com>
test/include/nntrainer_test_util.h
test/jni/Android.mk
test/unittest/unittest_nntrainer_tensor_neon_fp16.cpp [new file with mode: 0644]

index 68e6fd3..d32d978 100644 (file)
@@ -258,5 +258,36 @@ nntrainer::GraphRepresentation makeCompiledGraph(
 void sizeCheckedReadTensor(nntrainer::Tensor &t, std::ifstream &file,
                            const std::string &error_msg = "");
 
+template <typename Ta = float, typename Tb = float>
+double cosine_similarity(Ta *A /*Predict*/, Tb *B /*Reference */,
+                         uint32_t size) {
+  double dot = 0.0, denom_a = 0.0, denom_b = 0.0;
+  for (uint32_t i = 0u; i < size; ++i) {
+    Ta pred = A[i];
+    Tb ref = B[i];
+    dot += pred * ref;
+    denom_a += pred * pred;
+    denom_b += ref * ref;
+  }
+
+  double cosine_sim = dot / (sqrt(denom_a) * sqrt(denom_b));
+  return cosine_sim;
+}
+
+template <typename Ta = float, typename Tb = float>
+float mse(Ta *A /* Predicted */, Tb *B /* Reference */, uint32_t size) {
+  Ta pred;
+  Tb ref;
+  float mse_error = 0;
+  for (int i = 0; i < size; i++) {
+    pred = A[i];
+    ref = B[i];
+    float diff = pred - ref;
+    mse_error += pow(diff, 2);
+  }
+  float mse = mse_error / size;
+  return mse;
+}
+
 #endif /* __cplusplus */
 #endif /* __NNTRAINER_TEST_UTIL_H__ */
index a22202c..b24f2f0 100644 (file)
@@ -8,7 +8,7 @@ $(error ANDROID_NDK is not defined!)
 endif
 
 ifndef NNTRAINER_ROOT
-NNTRAINER_ROOT := $(LOCAL_PATH)/../../..
+NNTRAINER_ROOT := $(LOCAL_PATH)/../..
 endif
 
 ML_API_COMMON_INCLUDES := ${NNTRAINER_ROOT}/ml_api_common/include
@@ -96,6 +96,23 @@ LOCAL_STATIC_LIBRARIES := googletest_main test_util
 include $(BUILD_EXECUTABLE)
 
 
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := unittest_nntrainer_tensor_neon_fp16
+LOCAL_CFLAGS := -Igoogletest/include -I../include -pthread -fexceptions -fopenmp -static-openmp -DMIN_CPP_VERSION=201703L -DNNTR_NUM_THREADS=1 -D__LOGGING__=1 -DENABLE_TEST=1 -DREDUCE_TOLERANCE=1 -march=armv8.2-a+fp16 -mfpu=neon-fp16 -mfloat-abi=softfp -O3 -frtti -DENABLE_FP16=1
+LOCAL_CXXFLAGS      += -std=c++17 -frtti -fexceptions
+LOCAL_LDLIBS        := -llog -landroid -fopenmp -static-openmp 
+
+LOCAL_SRC_FILES := \
+    ../unittest/unittest_nntrainer_tensor_neon_fp16.cpp
+
+LOCAL_C_INCLUDES += $(NNTRAINER_INCLUDES)
+
+LOCAL_SHARED_LIBRARIES := nntrainer ccapi-nntrainer
+LOCAL_STATIC_LIBRARIES := googletest_main test_util
+include $(BUILD_EXECUTABLE)
+
+
 # include $(CLEAR_VARS)
 
 # LOCAL_MODULE := unittest_ccapi
diff --git a/test/unittest/unittest_nntrainer_tensor_neon_fp16.cpp b/test/unittest/unittest_nntrainer_tensor_neon_fp16.cpp
new file mode 100644 (file)
index 0000000..5d4e71a
--- /dev/null
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: Apache-2.0
+/**
+ * Copyright (C) 2023 Debadri Samaddar <s.debadri@samsung.com>
+ *
+ * @file        unittest_nntrainer_tensor_neon_fp16.cpp
+ * @date        03 August 2023
+ * @brief       Unit test utility for tensor with NEON __fp16 support for ARM.
+ * @see         https://github.com/nnstreamer/nntrainer
+ * @author      Debadri Samaddar <s.debadri@samsung.com>
+ * @bug         No known bugs
+ */
+#include <gtest/gtest.h>
+
+#include "nntrainer_test_util.h"
+#include "util_func.h"
+#include <cmath>
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+#include <nntrainer_error.h>
+#include <tensor.h>
+#include <tensor_dim.h>
+
+#define EXPECT_IN_RANGE(VAL, MIN, MAX) \
+  EXPECT_GE((VAL), (MIN));             \
+  EXPECT_LE((VAL), (MAX))
+
+TEST(nntrainer_Tensor, add_i) {
+  int batch = 1;
+  int channel = 1;
+  int height = 2;
+  int width = 11;
+
+  nntrainer::TensorDim::TensorType t_type_nchw_fp16 = {
+    nntrainer::Tformat::NCHW, nntrainer::Tdatatype::FP16};
+
+  nntrainer::TensorDim::TensorType t_type_nchw_fp32 = {
+    nntrainer::Tformat::NCHW, nntrainer::Tdatatype::FP32};
+
+  nntrainer::Tensor input(batch, channel, height, width, t_type_nchw_fp16);
+  nntrainer::Tensor input_copy(batch, channel, height, width, t_type_nchw_fp16);
+  nntrainer::Tensor input_fp32(batch, channel, height, width, t_type_nchw_fp32);
+
+  const float alpha = 1e-5;
+  const float epsilon = 1e-4;
+
+  GEN_TEST_INPUT(input, i * (batch * height * channel) * alpha +
+                          j * (batch * height) * alpha + k * (width)*alpha + l +
+                          1);
+  GEN_TEST_INPUT(input_copy, i * (batch * height * channel) * alpha +
+                               j * (batch * height) * alpha +
+                               k * (width)*alpha + l + 1);
+  GEN_TEST_INPUT(input_fp32, i * (batch * height * channel) * alpha +
+                               j * (batch * height) * alpha +
+                               k * (width)*alpha + l + 1);
+
+  // NEON fp16
+  int result = input.add_i(input_copy);
+
+  // fp32
+  result = input_fp32.add_i(input_fp32);
+
+  float mseErrorNeon = mse<__fp16>(input.getData<__fp16>(),
+                                   input_fp32.getData<float>(), input.size());
+
+  double cosSimNeon = cosine_similarity<__fp16>(
+    input.getData<__fp16>(), input_fp32.getData<float>(), input.size());
+  
+  EXPECT_IN_RANGE(mseErrorNeon, 0, epsilon);
+  EXPECT_IN_RANGE(cosSimNeon, 0.99, 1);
+}
+
+GTEST_API_ int main(int argc, char **argv) {
+  int result = -1;
+
+  try {
+    testing::InitGoogleTest(&argc, argv);
+  } catch (...) {
+    std::cerr << "Error duing InitGoogleTest" << std::endl;
+    return 0;
+  }
+
+  try {
+    result = RUN_ALL_TESTS();
+  } catch (...) {
+    std::cerr << "Error duing RUN_ALL_TESTS()" << std::endl;
+  }
+
+  return result;
+}