From 8c494f450085b4018db6fcbf60cd2c8aa8f3ac31 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=EB=B0=95=EC=A2=85=ED=98=84/=EB=8F=99=EC=9E=91=EC=A0=9C?= =?utf8?q?=EC=96=B4Lab=28SR=29/Staff=20Engineer/=EC=82=BC=EC=84=B1?= =?utf8?q?=EC=A0=84=EC=9E=90?= Date: Fri, 20 Jul 2018 10:42:25 +0900 Subject: [PATCH] [nest] Add 'Conv2D' example (#732) * [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 * Add comments --- contrib/nest/CMakeLists.txt | 9 +++ contrib/nest/examples/conv2d.cpp | 136 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 contrib/nest/examples/conv2d.cpp diff --git a/contrib/nest/CMakeLists.txt b/contrib/nest/CMakeLists.txt index 0731f22..42bfe33 100644 --- a/contrib/nest/CMakeLists.txt +++ b/contrib/nest/CMakeLists.txt @@ -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 index 0000000..dce022f --- /dev/null +++ b/contrib/nest/examples/conv2d.cpp @@ -0,0 +1,136 @@ +#include + +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; +} -- 2.7.4