// // MobilenetV2.cpp // MNN // // Created by MNN on 2020/01/08. // Copyright © 2018, Alibaba Group Holding Limited // #include #include "MobilenetV2.hpp" namespace MNN { namespace Train { namespace Model { using namespace MNN::Express; class _ConvBnRelu : public Module { public: _ConvBnRelu(std::vector inputOutputChannels, int kernelSize = 3, int stride = 1, bool depthwise = false, bool useBn = true); virtual std::vector onForward(const std::vector &inputs) override; std::shared_ptr conv; std::shared_ptr bn; }; std::shared_ptr ConvBnRelu(std::vector inputOutputChannels, int kernelSize = 3, int stride = 1, bool depthwise = false, bool useBn = true) { return std::shared_ptr(new _ConvBnRelu(inputOutputChannels, kernelSize, stride, depthwise, useBn)); } class _BottleNeck : public Module { public: _BottleNeck(std::vector inputOutputChannels, int stride, int expandRatio, bool useBn = true); virtual std::vector onForward(const std::vector &inputs) override; std::vector > layers; bool useShortcut = false; }; std::shared_ptr BottleNeck(std::vector inputOutputChannels, int stride, int expandRatio, bool useBn) { return std::shared_ptr(new _BottleNeck(inputOutputChannels, stride, expandRatio, useBn)); } _ConvBnRelu::_ConvBnRelu(std::vector inputOutputChannels, int kernelSize, int stride, bool depthwise, bool useBn) { int inputChannels = inputOutputChannels[0], outputChannels = inputOutputChannels[1]; NN::ConvOption convOption; convOption.kernelSize = {kernelSize, kernelSize}; convOption.channel = {inputChannels, outputChannels}; convOption.padMode = Express::SAME; convOption.stride = {stride, stride}; convOption.depthwise = depthwise; conv.reset(NN::Conv(convOption, false, std::shared_ptr(Initializer::MSRA()))); if (useBn) { bn.reset(NN::BatchNorm(outputChannels)); registerModel({conv, bn}); } else { registerModel({conv}); } } std::vector _ConvBnRelu::onForward(const std::vector &inputs) { using namespace Express; VARP x = inputs[0]; x = conv->forward(x); if (nullptr != bn.get()) { x = bn->forward(x); } x = _Relu6(x); return {x}; } _BottleNeck::_BottleNeck(std::vector inputOutputChannels, int stride, int expandRatio, bool useBn) { int inputChannels = inputOutputChannels[0], outputChannels = inputOutputChannels[1]; int expandChannels = inputChannels * expandRatio; if (stride == 1 && inputChannels == outputChannels) { useShortcut = true; } if (expandRatio != 1) { layers.emplace_back(ConvBnRelu({inputChannels, expandChannels}, 1, 1, false, useBn)); } layers.emplace_back(ConvBnRelu({expandChannels, expandChannels}, 3, stride, true, useBn)); NN::ConvOption convOption; convOption.kernelSize = {1, 1}; convOption.channel = {expandChannels, outputChannels}; convOption.padMode = Express::SAME; convOption.stride = {1, 1}; convOption.depthwise = false; layers.emplace_back(NN::Conv(convOption, false, std::shared_ptr(Initializer::MSRA()))); if (useBn) { layers.emplace_back(NN::BatchNorm(outputChannels)); } registerModel(layers); } std::vector _BottleNeck::onForward(const std::vector &inputs) { using namespace Express; VARP x = inputs[0]; for (int i = 0; i < layers.size(); i++) { x = layers[i]->forward(x); } if (useShortcut) { x = x + inputs[0]; } return {x}; } MobilenetV2::MobilenetV2(int numClasses, float widthMult, int divisor, bool useBn) { int inputChannels = 32; int lastChannels = 1280; std::vector > invertedResidualSetting; invertedResidualSetting.push_back({1, 16, 1, 1}); invertedResidualSetting.push_back({6, 24, 2, 2}); invertedResidualSetting.push_back({6, 32, 3, 2}); invertedResidualSetting.push_back({6, 64, 4, 2}); invertedResidualSetting.push_back({6, 96, 3, 1}); invertedResidualSetting.push_back({6, 160, 3, 2}); invertedResidualSetting.push_back({6, 320, 1, 1}); inputChannels = makeDivisible(inputChannels * widthMult, divisor); lastChannels = makeDivisible(lastChannels * std::max(1.0f, widthMult), divisor); firstConv = ConvBnRelu({3, inputChannels}, 3, 2, false, useBn); for (int i = 0; i < invertedResidualSetting.size(); i++) { std::vector setting = invertedResidualSetting[i]; int t = setting[0]; int c = setting[1]; int n = setting[2]; int s = setting[3]; int outputChannels = makeDivisible(c * widthMult, divisor); for (int j = 0; j < n; j++) { int stride = 1; if (j == 0) { stride = s; } bottleNeckBlocks.emplace_back(BottleNeck({inputChannels, outputChannels}, stride, t, useBn)); inputChannels = outputChannels; } } lastConv = ConvBnRelu({inputChannels, lastChannels}, 1, 1, false, useBn); dropout.reset(NN::Dropout(0.1)); fc.reset(NN::Linear(lastChannels, numClasses, true, std::shared_ptr(Initializer::MSRA()))); registerModel({firstConv, lastConv, dropout, fc}); registerModel(bottleNeckBlocks); } std::vector MobilenetV2::onForward(const std::vector &inputs) { using namespace Express; VARP x = inputs[0]; x = firstConv->forward(x); for (int i = 0; i < bottleNeckBlocks.size(); i++) { x = bottleNeckBlocks[i]->forward(x); } x = lastConv->forward(x); // global avg pooling x = _AvePool(x, {-1, -1}); x = _Convert(x, NCHW); x = _Reshape(x, {0, -1}); x = dropout->forward(x); x = fc->forward(x); x = _Softmax(x, 1); return {x}; } } // namespace Model } // namespace Train } // namespace MNN