Files
2026-07-13 13:33:03 +08:00

196 lines
6.1 KiB
C++

//
// MobilenetV2.cpp
// MNN
//
// Created by MNN on 2020/01/08.
// Copyright © 2018, Alibaba Group Holding Limited
//
#include <algorithm>
#include "MobilenetV2.hpp"
namespace MNN {
namespace Train {
namespace Model {
using namespace MNN::Express;
class _ConvBnRelu : public Module {
public:
_ConvBnRelu(std::vector<int> inputOutputChannels, int kernelSize = 3, int stride = 1, bool depthwise = false, bool useBn = true);
virtual std::vector<Express::VARP> onForward(const std::vector<Express::VARP> &inputs) override;
std::shared_ptr<Module> conv;
std::shared_ptr<Module> bn;
};
std::shared_ptr<Module> ConvBnRelu(std::vector<int> inputOutputChannels, int kernelSize = 3, int stride = 1,
bool depthwise = false, bool useBn = true) {
return std::shared_ptr<Module>(new _ConvBnRelu(inputOutputChannels, kernelSize, stride, depthwise, useBn));
}
class _BottleNeck : public Module {
public:
_BottleNeck(std::vector<int> inputOutputChannels, int stride, int expandRatio, bool useBn = true);
virtual std::vector<Express::VARP> onForward(const std::vector<Express::VARP> &inputs) override;
std::vector<std::shared_ptr<Module> > layers;
bool useShortcut = false;
};
std::shared_ptr<Module> BottleNeck(std::vector<int> inputOutputChannels, int stride, int expandRatio, bool useBn) {
return std::shared_ptr<Module>(new _BottleNeck(inputOutputChannels, stride, expandRatio, useBn));
}
_ConvBnRelu::_ConvBnRelu(std::vector<int> 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>(Initializer::MSRA())));
if (useBn) {
bn.reset(NN::BatchNorm(outputChannels));
registerModel({conv, bn});
} else {
registerModel({conv});
}
}
std::vector<Express::VARP> _ConvBnRelu::onForward(const std::vector<Express::VARP> &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<int> 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>(Initializer::MSRA())));
if (useBn) {
layers.emplace_back(NN::BatchNorm(outputChannels));
}
registerModel(layers);
}
std::vector<Express::VARP> _BottleNeck::onForward(const std::vector<Express::VARP> &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<std::vector<int> > 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<int> 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>(Initializer::MSRA())));
registerModel({firstConv, lastConv, dropout, fc});
registerModel(bottleNeckBlocks);
}
std::vector<Express::VARP> MobilenetV2::onForward(const std::vector<Express::VARP> &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