[nest] Add 'Conv2D' example (#732)
author박종현/동작제어Lab(SR)/Staff Engineer/삼성전자 <jh1302.park@samsung.com>
Fri, 20 Jul 2018 01:42:25 +0000 (10:42 +0900)
committerGitHub Enterprise <noreply-CODE@samsung.com>
Fri, 20 Jul 2018 01:42:25 +0000 (10:42 +0900)
* [nest] Add 'Conv2D' example

This commit adds an example how to describe simple Conv2D operation (without
bias and activation) with nest.

Signed-off-by: Jonghyun Park <jh1302.park@samsung.com>
* Add comments

contrib/nest/CMakeLists.txt
contrib/nest/examples/conv2d.cpp [new file with mode: 0644]

index 0731f22..42bfe33 100644 (file)
@@ -6,6 +6,15 @@ add_library(nest STATIC ${SOURCES})
 set_target_properties(nest PROPERTIES POSITION_INDEPENDENT_CODE ON)
 target_include_directories(nest PUBLIC include)
 
+file(GLOB EXAMPLE_FILES "examples/*.cpp")
+
+foreach(EXAMPLE_FILE IN ITEMS ${EXAMPLE_FILES})
+   get_filename_component(EXAMPLE_NAME ${EXAMPLE_FILE} NAME_WE)
+   set(TARGET_NAME nest_IR_example_${EXAMPLE_NAME})
+   add_executable(${TARGET_NAME} ${EXAMPLE_FILE})
+   target_link_libraries(${TARGET_NAME} nest)
+endforeach(EXAMPLE_FILE)
+
 nncc_find_package(GTest QUIET)
 
 if(NOT GTest_FOUND)
diff --git a/contrib/nest/examples/conv2d.cpp b/contrib/nest/examples/conv2d.cpp
new file mode 100644 (file)
index 0000000..dce022f
--- /dev/null
@@ -0,0 +1,136 @@
+#include <nest/Module.h>
+
+int main(int, char **)
+{
+  // This example shows how to specify convolution with IFM(1x3x3) and Kernel(1x1x3x3) with nest
+  // - STRIDE is 1, and there is no padding
+  //
+  // The below code corresponds to the following nest DSL code:
+  // ----------------------------------------------------------------------------------------------
+  // Domain ofm(1, 1, 1)
+  // Domain ifm(1, 3, 3)
+  // Domain ker(1, 1, 3, 3)
+  //
+  // Var ofm_ch  : { min = 0, max = 1 }
+  // Var ofm_row : { min = 0, max = 1 }
+  // Var ofm_col : { min = 0, max = 1 }
+  // Var ker_ch  : { min = 0, max = 1 }
+  // Var ker_row : { min = 0, max = 3 }
+  // Var ker_col : { min = 0, max = 3 }
+  //
+  // PUSH ifm(ker_ch, ker_row, ker_col) * ker(ofm_ch, ker_ch, ofm_row + ker_row, ofm_col + ker_col)
+  // RET  ofm(ofm_ch, ofm_row, ofm_col)
+  // ----------------------------------------------------------------------------------------------
+  //
+  // The first part declares Domain(s) which corresponds to a multi-dimensional array in C-style
+  // (without type). For example, 'Domain ofm(1, 3, 3)' corresponds to the
+  // following C array declaration.
+  //   float ofm[1][3][3];
+  // (Here we assume that domain type is 'float')
+  //
+  // The second part declares Var(s) which serves as a loop iteration variable. Basically, each
+  // variable emits one for loop and these loops are nested. As there are 6 variables in the above
+  // example, there will be 6 nested-loops.
+  //
+  // Each variable has a corresponding bound, and the bound of each variable states the starting /
+  // termination condition. For example, 'Var ofm_ch : { min = 0, max = 1 }' will introduce the
+  // following for loop:
+  // ----------------------------------------------------------------------------------------------
+  //   for (int ofm_ch = 0; ofm_ch < 1; ++ofm_ch) { ... }
+  // ----------------------------------------------------------------------------------------------
+  //
+  // The last part declares statement(s) which state the computation performed inside these nested
+  // loops. Nest is stack-based. There is a virtual stack inside nested loop, and the evaluation of
+  // each statement will update this stack.
+  //
+  // Each nest code has one return statement (RET). This return statement specifies where to write
+  // the computed result.
+  //
+  // PUSH 'expr' statement evaluates an arithmetic expression (specified by 'expr') and pushes the
+  // numeric result to the stack. When PUSH statement evaluates an arithmetic expression, variables
+  // that do not appear in RET statement are treated as reduction variables. For example,
+  // ker_ch, ker_row, and ker_col do not appear in RET statement. So, PUSH '...' statement in the
+  // above example corresponds to the following nested loops:
+  // ----------------------------------------------------------------------------------------------
+  // float value = 0.0f;
+  //
+  // for (int ker_ch = 0; ker_ch < 1; ++ker_ch) {
+  //   for (int ker_row = 0; ker_row < 3; ++ker_row) {
+  //     for (int ker_col = 0; ker_col < 3; ++ker_col) {
+  //       float ifm_value = ifm[ker_ch][ker_row][ker_col];
+  //       float ker_value = ker[ofm_ch][ker_ch][ofm_row + ker_row][ofm_col + ker_col];
+  //       value += ifm_value * ker_value;
+  //    }
+  //  }
+  // }
+  // ----------------------------------------------------------------------------------------------
+  //
+  // In summary, the above nest example corresponds to the following 2D convolution:
+  // ----------------------------------------------------------------------------------------------
+  // float ofm[1][1][1];
+  // float ifm[1][3][3];
+  // float ker[1][1][3][3];
+  //
+  // for (int ofm_ch = 0; ofm_ch < 1; ++ofm_ch) {
+  //   for (int ofm_row = 0; ofm_row < 1; ++ofm_row) {
+  //     for (int ofm_col = 0; ofm_col < 1; ++ofm_col) {
+  //       float value = 0.0f;
+  //
+  //       for (int ker_ch = 0; ker_ch < 1; ++ker_ch) {
+  //         for (int ker_row = 0; ker_row < 3; ++ker_row) {
+  //           for (int ker_col = 0; ker_col < 3; ++ker_col) {
+  //             float ifm_value = ifm[ker_ch][ker_row][ker_col];
+  //             float ker_value = ker[ofm_ch][ker_ch][ofm_row + ker_row][ofm_col + ker_col];
+  //             value += ifm_value * ker_value;
+  //           }
+  //         }
+  //       }
+  //
+  //       ofm[ofm_ch][ofm_col][ofm_row] = value;
+  //     }
+  //   }
+  // }
+  // ----------------------------------------------------------------------------------------------
+  //
+  nest::Module m;
+
+  //
+  // Domains
+  //
+  auto ofm = m.domain().make({1 /*C*/, 1 /*H*/, 1 /*W*/});
+  auto ifm = m.domain().make({1 /*C*/, 3 /*H*/, 3 /*W*/});
+  auto ker = m.domain().make({1 /*N*/, 1 /*C*/, 3 /*H*/, 3 /*W*/});
+
+  //
+  // Variables
+  //
+  auto ofm_ch = m.var().make();
+  auto ofm_row = m.var().make();
+  auto ofm_col = m.var().make();
+
+  auto ker_ch = m.var().make();
+  auto ker_row = m.var().make();
+  auto ker_col = m.var().make();
+
+  // Declare the bound of each variables
+  using nest::Bound;
+
+  m.var().bound(ofm_ch) = Bound{0, 1};
+  m.var().bound(ofm_row) = Bound{0, 1};
+  m.var().bound(ofm_col) = Bound{0, 1};
+
+  m.var().bound(ker_ch) = Bound{0, 1};
+  m.var().bound(ker_row) = Bound{0, 3};
+  m.var().bound(ker_col) = Bound{0, 3};
+
+  //
+  // Statement
+  //
+  auto ifm_value = ifm(ker_ch, ofm_row + ker_row, ofm_col + ker_col);
+  auto ker_value = ker(ofm_ch, ker_ch, ker_row, ker_col);
+
+  m.push(ifm_value * ker_value);
+  m.ret(ofm(ofm_ch, ofm_row, ofm_col));
+
+  return 0;
+}