From be7b03743925cbc8c01ae9f575316b79871b479f Mon Sep 17 00:00:00 2001 From: Jihoon Lee Date: Thu, 24 Jun 2021 20:05:45 +0900 Subject: [PATCH] [Resnet] Create resnet model This patch creates code to create resnet. **Self evaluation:** 1. Build test: [X]Passed [ ]Failed [ ]Skipped 2. Run test: [X]Passed [ ]Failed [ ]Skipped Signed-off-by: Jihoon Lee --- Applications/Resnet/jni/Android.mk | 13 +++- Applications/Resnet/jni/main.cpp | 150 +++++++++++++++++++++++++++++++++---- 2 files changed, 146 insertions(+), 17 deletions(-) diff --git a/Applications/Resnet/jni/Android.mk b/Applications/Resnet/jni/Android.mk index 7f85805..eaeb523 100644 --- a/Applications/Resnet/jni/Android.mk +++ b/Applications/Resnet/jni/Android.mk @@ -11,7 +11,7 @@ ifndef NNTRAINER_ROOT NNTRAINER_ROOT := $(LOCAL_PATH)/../../.. endif - ML_API_COMMON_INCLUDES := ${NNTRAINER_ROOT}/ml_api_common/include +ML_API_COMMON_INCLUDES := ${NNTRAINER_ROOT}/ml_api_common/include NNTRAINER_INCLUDES := $(NNTRAINER_ROOT)/nntrainer \ $(NNTRAINER_ROOT)/nntrainer/dataset \ $(NNTRAINER_ROOT)/nntrainer/models \ @@ -31,6 +31,13 @@ include $(PREBUILT_SHARED_LIBRARY) include $(CLEAR_VARS) +LOCAL_MODULE := ccapi-nntrainer +LOCAL_SRC_FILES := $(NNTRAINER_ROOT)/libs/$(TARGET_ARCH_ABI)/libccapi-nntrainer.so + +include $(PREBUILT_SHARED_LIBRARY) + +include $(CLEAR_VARS) + LOCAL_ARM_NEON := true LOCAL_CFLAGS += -std=c++17 -Ofast -mcpu=cortex-a53 -Ilz4-nougat/lib LOCAL_LDFLAGS += -Llz4-nougat/lib/obj/local/$(TARGET_ARCH_ABI)/ @@ -39,12 +46,12 @@ LOCAL_CFLAGS += -pthread -fexceptions LOCAL_LDFLAGS += -fexceptions LOCAL_MODULE_TAGS := optional LOCAL_ARM_MODE := arm -LOCAL_MODULE := nntrainer_logistic +LOCAL_MODULE := nntrainer_resnet LOCAL_LDLIBS := -llog -landroid LOCAL_SRC_FILES := main.cpp -LOCAL_SHARED_LIBRARIES := nntrainer +LOCAL_SHARED_LIBRARIES := nntrainer ccapi-nntrainer LOCAL_C_INCLUDES += $(NNTRAINER_INCLUDES) diff --git a/Applications/Resnet/jni/main.cpp b/Applications/Resnet/jni/main.cpp index cbfc116..debc95a 100644 --- a/Applications/Resnet/jni/main.cpp +++ b/Applications/Resnet/jni/main.cpp @@ -4,6 +4,7 @@ * * @file main.cpp * @date 24 Jun 2021 + * @todo move resnet model creating to separate sourcefile * @brief task runner for the resnet * @see https://github.com/nnstreamer/nntrainer * @author Jihoon Lee @@ -16,6 +17,7 @@ #include #include +#include using LayerHandle = std::shared_ptr; using ModelHandle = std::unique_ptr; @@ -31,23 +33,25 @@ using ModelHandle = std::unique_ptr; template static std::string withKey(const std::string &key, const T &value) { std::stringstream ss; - ss << "key=" << value; + ss << key << "=" << value; return ss.str(); } template static std::string withKey(const std::string &key, - const std::vector &value) { - if (value.empty()) { - throw std::invalid_argument("empty vector cannot be converted"); + std::initializer_list value) { + if (std::empty(value)) { + throw std::invalid_argument("empty data cannot be converted"); } std::stringstream ss; - ss << "key="; - for (unsigned int i = 0; i < value.size() - 1; ++i) { - ss << value.at(i) << ','; + ss << key << "="; + + auto iter = value.begin(); + for (; iter != value.end() - 1; ++iter) { + ss << *iter << ','; } - ss << value.back(); + ss << *iter; return ss.str(); } @@ -65,23 +69,141 @@ static std::string withKey(const std::string &key, std::vector resnetBlock(const std::string &block_name, const std::string &input_name, int filters, int kernel_size, bool downsample) { - return {}; + using ml::train::createLayer; + + auto scoped_name = [&block_name](const std::string &layer_name) { + return block_name + "/" + layer_name; + }; + auto with_name = [&scoped_name](const std::string &layer_name) { + return withKey("name", scoped_name(layer_name)); + }; + + auto create_conv = + [&with_name, filters](const std::string &name, int kernel_size, int stride, + int padding, const std::string &input_layer) { + std::vector props{ + with_name(name), + withKey("stride", {stride, stride}), + withKey("filters", filters), + withKey("kernel_size", {kernel_size, kernel_size}), + withKey("padding", {padding, padding}), + withKey("input_layers", input_layer)}; + + return createLayer("conv2d", props); + }; + + auto create_batch_relu = [&with_name](const std::string &name) { + return createLayer("batch_normalization", + {with_name(name), "activation=relu"}); + }; + + /** residual path */ + LayerHandle a1 = create_conv("a1", 3, downsample ? 2 : 1, 1, input_name); + LayerHandle a2 = create_batch_relu("a2"); + LayerHandle a3 = create_conv("a3", 3, 1, 1, scoped_name("a2")); + + /** skip path */ + LayerHandle b1 = nullptr; + if (downsample) { + b1 = create_conv("b1", 1, 2, 0, input_name); + } + + const std::string skip_name = b1 ? scoped_name("b1") : input_name; + + LayerHandle c1 = createLayer( + "Addition", + {with_name("c1"), withKey("input_layers", {scoped_name("a3"), skip_name})}); + + LayerHandle c2 = create_batch_relu(""); /// use block_name itself. + + if (downsample) { + return {a1, a2, a3, b1, c1, c2}; + } else { + return {a1, a2, a3, c1, c2}; + } } -/**s +/** * @brief Create resnet 18 * - * @return ModelHandle to create model + * @return vector of layers that contain full graph of resnet18 */ -ModelHandle createResnet18() { +std::vector createResnet18Graph() { + using ml::train::createLayer; + std::vector layers; - return nullptr; + + layers.push_back( + createLayer("conv2d", { + withKey("name", "conv0"), + withKey("kernel_size", {3, 3}), + withKey("stride", {1, 1}), + withKey("padding", {1, 1}), + withKey("bias_initializer", "zeros"), + withKey("weight_initializer", "xavier_uniform"), + })); + + layers.push_back( + createLayer("batch_normalization", {withKey("name", "first_bn_relu"), + withKey("activation", "relu")})); + + std::vector> blocks; + + blocks.push_back(resnetBlock("conv1_0", "first_bn_relu", 64, 3, false)); + blocks.push_back(resnetBlock("conv1_1", "conv1_0", 64, 3, false)); + blocks.push_back(resnetBlock("conv2_0", "conv1_1", 128, 3, true)); + blocks.push_back(resnetBlock("conv2_1", "conv2_0", 128, 3, false)); + blocks.push_back(resnetBlock("conv3_0", "conv2_1", 256, 3, true)); + blocks.push_back(resnetBlock("conv3_1", "conv3_0", 256, 3, false)); + blocks.push_back(resnetBlock("conv4_0", "conv3_1", 256, 3, true)); + blocks.push_back(resnetBlock("conv4_1", "conv4_0", 256, 3, false)); + + for (auto &block : blocks) { + layers.insert(layers.end(), block.begin(), block.end()); + } + + layers.push_back(createLayer("pooling2d", {withKey("name", "last_p1"), + withKey("pooling", "average"), + withKey("pool_size", {4, 4})})); + + layers.push_back(createLayer("flatten", {withKey("name", "last_f1")})); + layers.push_back( + createLayer("fully_connected", + {withKey("unit", 100), withKey("activation", "softmax")})); + + return layers; +} + +/// @todo update createResnet18 to be more generic +ModelHandle createResnet18() { + ModelHandle model = + ml::train::createModel(ml::train::ModelType::NEURAL_NET, + {withKey("loss", "cross"), + withKey("batch_size", 128), withKey("epochs", 60)}); + + for (auto layers : createResnet18Graph()) { + model->addLayer(layers); + } + + return model; } ml_train_datagen_cb train_cb, valid_cb; +void create_and_run() { + ModelHandle model = createResnet18(); + + auto optimizer = ml::train::createOptimizer("adam"); + model->setOptimizer(std::move(optimizer)); +} + int main() { - std::cout << "Hello world\n"; + try { + create_and_run(); + } catch (std::exception &e) { + std::cerr << "uncaught error! error: " << e.what(); + return 1; + } return 0; } -- 2.7.4