Files
alibaba--mnn/tools/converter/source/optimizer/merge/TransformSmallGroupConvolutionToDepthwise.cpp
T
2026-07-13 13:33:03 +08:00

136 lines
5.9 KiB
C++

#include "../TemplateMerge.hpp"
#include "MNN/expr/MathOp.hpp"
#include "MNN/expr/NeuralNetWorkOp.hpp"
#include "MNN_generated.h"
#include "config.hpp"
namespace MNN {
namespace Express {
static std::vector <VARP> _UnstackF(VARP value, int axis, int size) {
std::unique_ptr<OpT> op(new OpT);
op->type = OpType_Unpack;
MNN_ASSERT(size > 0);
auto axisParam = new AxisT;
axisParam->axis = axis;
op->main.type = OpParameter_Axis;
op->main.value = axisParam;
EXPRP expr = Expr::create(std::move(op), {value}, size);
std::vector<VARP> res;
for (int i = 0; i < size; ++i) {
res.emplace_back(Variable::create(expr, i));
}
return res;
}
static auto gRegister = []() {
auto transform = [](EXPRP expr) {
auto config = Global<modelConfig>::Get();
if(config->groupConvNative) {
return false;
}
if (expr->get() == nullptr) {
return false;
}
if (expr->get()->type() != OpType_Convolution) {
return false;
}
auto conv2d = expr->get()->main_as_Convolution2D();
auto common = conv2d->common();
if (common->group() <= 1) {
return false;
}
if (common->group() == common->inputCount() == common->outputCount()) {
// Depthwise
return false;
}
if (common->outputCount() / common->group() >= 4 || common->outputCount() / common->group() >= common->group() || common->inputCount() / common->group() >= common->group()) {
// Large Enough
return false;
}
if (conv2d->weight() == nullptr || conv2d->weight()->data() == nullptr) {
return false;
}
// Currnetly don't support other pad mode
if (common->padMode() != PadMode_CAFFE) {
return false;
}
// Split As ConvolutionDepthwise
MNN_ASSERT(conv2d->bias() != nullptr && conv2d->bias()->data() != nullptr);
auto input = expr->inputs()[0];
// Input: [b, c, h, w] -> [c/g, b, g, h, w] : [b, c, h, w] -> [b, g, c/g, h, w] -> [c/g, b, g, h, w]
auto one = _Unsqueeze(_Scalar<int32_t>(1), {0});
auto negone = _Unsqueeze(_Scalar<int>(-1), {0});
auto sx = _Shape(input, true);
auto kernelH = common->kernelY();
auto kernelW = common->kernelX();
auto inputChannel = common->inputCount();
auto outputChannel = common->outputCount();
auto g = _Unsqueeze(_Scalar<int32_t>(common->group()), {0});
auto icDivG = common->inputCount() / common->group();
auto ocDivG = common->outputCount() / common->group();
auto icdivgv = _Unsqueeze(_Scalar<int32_t>(icDivG), {0});
auto w = _Slice(sx, _Unsqueeze(_Scalar<int32_t>(3), {0}), one);
auto h = _Slice(sx, _Unsqueeze(_Scalar<int32_t>(2), {0}), one);
auto b = _Slice(sx, _Unsqueeze(_Scalar<int32_t>(0), {0}), one);
input = _Reshape(input, _Concat({b, g, icdivgv, h, w}, 0));
input = _Transpose(input, {2, 0, 1, 3, 4});
auto inputs = _UnstackF(input, 0, icDivG);
// Compute Outputs: [c/g, b, g, h, w] -> [oc/g, b, g, oh, ow]
std::vector<VARP> convMerge(ocDivG);
for (int y=0; y<ocDivG; ++y) {
VARP convSummer;
for (int x=0; x<icDivG; ++x) {
std::unique_ptr<OpT> op(new OpT);
op->type = OpType_ConvolutionDepthwise;
op->main.value = new Convolution2DT;
op->main.type = OpParameter_Convolution2D;
op->main.AsConvolution2D()->common.reset(common->UnPack());
op->main.AsConvolution2D()->common->inputCount = common->group();
op->main.AsConvolution2D()->common->outputCount = common->group();
op->main.AsConvolution2D()->common->relu = false;
op->main.AsConvolution2D()->common->relu6 = false;
// Copy Bias for the first input
op->main.AsConvolution2D()->bias = std::vector<float>(common->group(), 0.0f);
if (x == 0) {
for (int j=0; j<common->group(); ++j) {
op->main.AsConvolution2D()->bias[j] = conv2d->bias()->data()[ocDivG * j + y];
}
}
// Copy Weight
auto kxky = common->kernelX() * common->kernelY();
op->main.AsConvolution2D()->weight.resize(kxky * common->group());
for (int j=0; j<common->group(); ++j) {
::memcpy(op->main.AsConvolution2D()->weight.data() + j * kxky, conv2d->weight()->data() + kxky * (j * icDivG * ocDivG + x + y * icDivG), kxky * sizeof(float));
}
auto tmp = Variable::create(Expr::create(op.get(), {inputs[x]}));
if (0 == x) {
convSummer = tmp;
} else {
convSummer = convSummer + tmp;
}
}
convMerge[y] = convSummer;
}
auto dstFuse = _Stack(convMerge, 0);
// [oc/g, b, g, oh, ow] -> [b, g, oc/g, oh, ow] -> [b, oc, oh, ow]
dstFuse = _Transpose(dstFuse, {1, 2, 0, 3, 4});
auto dx = _Shape(dstFuse, true);
auto ow = _Slice(dx, _Unsqueeze(_Scalar<int32_t>(4), {0}), one);
auto oh = _Slice(dx, _Unsqueeze(_Scalar<int32_t>(3), {0}), one);
dstFuse = _Reshape(dstFuse, _Concat({b, negone, oh, ow}, 0));
if (common->relu()) {
dstFuse = _Relu6(dstFuse, 0.0f, 65504.0f);
} else if (common->relu6()) {
dstFuse = _Relu6(dstFuse);
}
auto groupResult = dstFuse;
groupResult->setName(expr->outputName(0));
Expr::replace(expr, groupResult->expr().first);
return true;
};
TemplateMerge::getInstance("Merge").insertTemplateV2("TransformSmallGroupConvolutionToDepthwise", transform);
return true;
}();
}
} // namespace MNN