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

457 lines
19 KiB
C++

//
// FuseTemplateOp.cpp
// MNNConverter
//
// Created by MNN on 2021/04/15.
// Copyright © 2018, Alibaba Group Holding Limited
//
#include "../TemplateMerge.hpp"
#include "MNN/expr/MathOp.hpp"
#include "MNN/expr/NeuralNetWorkOp.hpp"
#include "MNN_generated.h"
#include "config.hpp"
namespace MNN {
namespace Express {
// left is pattern, right is dest
static bool isSameOp(const MNN::Op* op0, const MNN::Op* op1) {
if (op0->type() != op1->type()) {
return false;
}
if (op0->main_type() != op1->main_type()) {
return false;
}
if (op0->main_type() == OpParameter_NONE) {
return true;
}
if (op0->type() == OpType_ReLU) {
return op0->main_as_Relu()->slope() == op1->main_as_Relu()->slope();
}
if (op0->type() == OpType_ReLU6) {
return op0->main_as_Relu6()->maxValue() == op1->main_as_Relu6()->maxValue() && op0->main_as_Relu6()->minValue() == op1->main_as_Relu6()->minValue();
}
if (op0->main_type() == OpParameter_UnaryOp) {
return op0->main_as_UnaryOp()->opType() == op1->main_as_UnaryOp()->opType();
}
if (op0->main_type() == OpParameter_BinaryOp) {
return op0->main_as_BinaryOp()->opType() == op1->main_as_BinaryOp()->opType();
}
return false;
}
static bool isTheSameRec(EXPRP left, EXPRP right, std::map<EXPRP, VARP>& inputConst) {
auto lop = left->get();
auto rop = right->get();
if (nullptr == lop) {
if (nullptr != rop) {
return false;
}
}
if (left->inputs().size() != right->inputs().size()) {
return false;
}
if (left->outputSize() != right->outputSize()) {
return false;
}
if (nullptr != lop && nullptr == rop) {
return false;
}
if (nullptr == lop && nullptr == rop) {
// Constant
if (left->inputType() != right->inputType()) {
return false;
}
auto leftV = Variable::create(left);
auto rightV = Variable::create(right);
if (leftV->getInfo() == nullptr || rightV->getInfo() == nullptr) {
return false;
}
auto lInfo = leftV->getInfo();
auto rInfo = rightV->getInfo();
if (lInfo->type != rInfo->type) {
return false;
}
if (lInfo->dim != rInfo->dim) {
return false;
}
if (lInfo->size != rInfo->size) {
return false;
}
auto lPtr = leftV->readMap<void>();
auto rPtr = rightV->readMap<void>();
if (nullptr == lPtr || nullptr == rPtr) {
return false;
}
if (0 != ::memcmp(lPtr, rPtr, lInfo->size * lInfo->type.bytes())) {
return false;
}
return true;
}
// Check Op
if (!isSameOp(lop, rop)) {
return false;
}
for (int i=0; i<left->inputs().size(); ++i) {
auto leftExpr = left->inputs()[i]->expr();
auto rightExpr = right->inputs()[i]->expr();
auto subLop = leftExpr.first->get();
if (nullptr == subLop) {
if (leftExpr.first->inputType() == VARP::INPUT) {
auto iter = inputConst.find(leftExpr.first);
if (iter == inputConst.end()) {
inputConst.insert(std::make_pair(leftExpr.first, right->inputs()[i]));
continue;
}
auto iterExpr = iter->second->expr();
if (iterExpr.first.get() != rightExpr.first.get() || iterExpr.second != rightExpr.second) {
return false;
}
continue;
}
}
if (!isTheSameRec(left->inputs()[i]->expr().first, right->inputs()[i]->expr().first, inputConst)) {
return false;
}
}
return true;
}
static auto gRegister = []() {
{
// Turn DIV Const to Multi
auto match = [](EXPRP expr) {
if (expr->get() == nullptr) {
return false;
}
if (OpType_BinaryOp != expr->get()->type()) {
return false;
}
if (BinaryOpOperation_REALDIV != expr->get()->main_as_BinaryOp()->opType()) {
return false;
}
auto i1 = expr->inputs()[1];
auto i1Info = i1->getInfo();
if (nullptr == i1Info || i1Info->type.code != halide_type_float) {
return false;
}
auto i1Ptr = i1->readMap<void>();
if (nullptr == i1Ptr) {
return false;
}
return true;
};
auto transform = [](EXPRP expr) {
auto i1 = expr->inputs()[1];
i1 = _Reciprocal(i1);
i1.fix(VARP::CONSTANT);
auto newVar = _Multiply(expr->inputs()[0], i1);
newVar->setName(expr->name());
Expr::replace(expr, newVar->expr().first);
return true;
};
TemplateMerge::getInstance("Merge").insertTemplate("ConstDivToMul", match, transform);
}
{
auto input = _Input({}, NCHW);
auto left = _Relu6(_Add(input, _Scalar<float>(3)));
auto res = _Multiply(_Multiply(input, left), _Scalar<float>(1.0f/6.0f));
auto res2 = _Multiply(input, _Multiply(left, _Scalar<float>(1.0f/6.0f)));
std::vector<EXPRP> templatesExprs = {
res->expr().first,
res2->expr().first
};
auto transform2 = [templatesExprs, input](EXPRP expr) {
auto config = Global<modelConfig>::Get();
auto version = config->targetVersion;
if (version < 1.2f) {
// For target version < 1.2 , don't support hardswish
return false;
}
for (auto templateExpr : templatesExprs) {
std::map<EXPRP, VARP> inputConst;
if (isTheSameRec(templateExpr, expr, inputConst)) {
auto inputVarIter = inputConst.find(input->expr().first);
if (inputVarIter == inputConst.end()) {
MNN_ERROR("Invalid Match, may be something is wrong for Fuse\n");
continue;
}
auto inputVar = inputVarIter->second;
std::unique_ptr<MNN::OpT> newOp(new OpT);
newOp->type = OpType_UnaryOp;
newOp->main.value = new UnaryOpT;
newOp->main.type = OpParameter_UnaryOp;
newOp->main.AsUnaryOp()->opType = UnaryOpOperation_HARDSWISH;
auto newVar = Variable::create(Expr::create(newOp.get(), {inputVar}, 1));
newVar->setName(expr->outputName(0));
Expr::replace(expr, newVar->expr().first);
return true;
}
}
return false;
};
TemplateMerge::getInstance("Merge").insertTemplateV2("FuseHardSwish", transform2);
}
{
auto zero0 = _Scalar<int>(0);
auto zero1 = _Scalar<float>(0);
auto one0 = _Scalar<int>(1);
auto one1 = _Scalar<float>(1);
auto input0 = _Input({}, NHWC, halide_type_of<int>());
auto input1 = _Input({}, NHWC, halide_type_of<float>());
std::vector<MNN::Express::VARP> binaryAddZero({
zero0 + input1,
input1 + zero0,
zero1 + input1,
input1 + zero1,
input0 - zero0,
input1 - zero1,
input0 * one0,
one0 * input0,
input1 * one1,
one1 * input1,
});
auto transform2 = [binaryAddZero, input0, input1](EXPRP expr) {
std::map<EXPRP, VARP> inputConst;
for (int index=0; index<binaryAddZero.size(); ++index) {
auto res = binaryAddZero[index];
if (isTheSameRec(res->expr().first, expr, inputConst)) {
auto inputVarIter0 = inputConst.find(input0->expr().first);
auto inputVarIter1 = inputConst.find(input1->expr().first);
MNN::Express::VARP inputVar;
if (inputVarIter0 == inputConst.end() && inputVarIter1 == inputConst.end()) {
MNN_ERROR("Invalid Match, may be something is wrong for Fuse\n");
return false;
}
if (inputVarIter0 != inputConst.end()) {
inputVar = inputVarIter0->second;
} else {
inputVar = inputVarIter1->second;
}
std::unique_ptr<MNN::OpT> newOp(new OpT);
newOp->type = OpType_Identity;
newOp->main.type = OpParameter_NONE;
auto newVar = Variable::create(Expr::create(newOp.get(), {inputVar}, 1));
newVar->setName(expr->outputName(0));
Expr::replace(expr, newVar->expr().first);
return true;
}
}
return false;
};
TemplateMerge::getInstance("Merge").insertTemplateV2("RemoveUselessBinary", transform2);
}
{
auto input0 = _Input({}, NCHW);
auto input1 = _Input({}, NCHW);
auto diff = input0 - input1;
auto res0 = diff * diff;
auto transform2 = [res0, input0, input1](EXPRP expr) {
std::map<EXPRP, VARP> inputConst;
if (isTheSameRec(res0->expr().first, expr, inputConst)) {
auto inputVarIter0 = inputConst.find(input0->expr().first);
auto inputVarIter1 = inputConst.find(input1->expr().first);
if (inputVarIter0 == inputConst.end() || inputVarIter1 == inputConst.end()) {
MNN_ERROR("Invalid Match, may be something is wrong for Fuse\n");
return false;
}
auto inputVar = inputVarIter0->second;
std::unique_ptr<MNN::OpT> newOp(new OpT);
newOp->type = OpType_BinaryOp;
newOp->main.value = new BinaryOpT;
newOp->main.type = OpParameter_BinaryOp;
newOp->main.AsBinaryOp()->opType = BinaryOpOperation_SquaredDifference;
auto newVar = Variable::create(Expr::create(newOp.get(), {inputVarIter0->second, inputVarIter1->second}, 1));
newVar->setName(expr->outputName(0));
Expr::replace(expr, newVar->expr().first);
return true;
}
return false;
};
TemplateMerge::getInstance("Merge").insertTemplateV2("FuseSquaredDifference", transform2);
}
{
auto input = _Input({}, NCHW);
auto sigmoidVar = _Sigmoid(input);
auto res0 = input * sigmoidVar;
auto res1 = sigmoidVar * input;
std::vector<EXPRP> templatesExprs = {
res0->expr().first,
res1->expr().first
};
auto transform2 = [templatesExprs, input](EXPRP expr) {
for (auto templateExpr : templatesExprs) {
std::map<EXPRP, VARP> inputConst;
if (isTheSameRec(templateExpr, expr, inputConst)) {
auto inputVarIter = inputConst.find(input->expr().first);
if (inputVarIter == inputConst.end()) {
MNN_ERROR("Invalid Match, may be something is wrong for Fuse\n");
return false;
}
auto inputVar = inputVarIter->second;
std::unique_ptr<MNN::OpT> newOp(new OpT);
newOp->type = OpType_UnaryOp;
newOp->main.value = new UnaryOpT;
newOp->main.type = OpParameter_UnaryOp;
newOp->main.AsUnaryOp()->opType = UnaryOpOperation_SILU;
auto newVar = Variable::create(Expr::create(newOp.get(), {inputVar}, 1));
newVar->setName(expr->outputName(0));
Expr::replace(expr, newVar->expr().first);
return true;
}
}
return false;
};
TemplateMerge::getInstance("Merge").insertTemplateV2("FuseSilu", transform2);
}
{
auto input = _Input({}, NCHW);
auto sqr = _Sqrt(input);
auto sqrdiv = _Reciprocal(sqr);
auto sqrdiv2 = _Scalar<float>(1.0f) / sqr;
std::vector<EXPRP> templatesExprs = {
sqrdiv->expr().first,
sqrdiv2->expr().first
};
auto transform = [templatesExprs, input](EXPRP expr) {
for (auto templateExpr : templatesExprs) {
std::map<EXPRP, VARP> inputConst;
if (isTheSameRec(templateExpr, expr, inputConst)) {
auto inputVarIter = inputConst.find(input->expr().first);
if (inputVarIter == inputConst.end()) {
MNN_ERROR("Invalid Match, may be something is wrong for Fuse\n");
continue;
}
auto inputVar = inputVarIter->second;
std::unique_ptr<MNN::OpT> newOp(new OpT);
newOp->type = OpType_UnaryOp;
newOp->main.value = new UnaryOpT;
newOp->main.type = OpParameter_UnaryOp;
newOp->main.AsUnaryOp()->opType = UnaryOpOperation_RSQRT;
auto newVar = Variable::create(Expr::create(newOp.get(), {inputVar}, 1));
newVar->setName(expr->outputName(0));
Expr::replace(expr, newVar->expr().first);
return true;
}
}
return false;
};
TemplateMerge::getInstance("Merge").insertTemplateV2("FuseRsqrt", transform);
}
{
auto input = _Input({}, NCHW);
auto inputSquare = _Pow(input, _Scalar<float>(2.0f));
auto inputSquare2 = input * input;
auto inputSquare3 = _Pow(input, _Scalar<int>(2));
std::vector<EXPRP> templatesExprs = {
inputSquare->expr().first,
inputSquare2->expr().first,
inputSquare3->expr().first
};
auto transform = [templatesExprs, input](EXPRP expr) {
for (auto templateExpr : templatesExprs) {
std::map<EXPRP, VARP> inputConst;
if (isTheSameRec(templateExpr, expr, inputConst)) {
auto inputVarIter = inputConst.find(input->expr().first);
if (inputVarIter == inputConst.end()) {
MNN_ERROR("Invalid Match, may be something is wrong for Fuse\n");
continue;
}
auto inputVar = inputVarIter->second;
std::unique_ptr<MNN::OpT> newOp(new OpT);
newOp->type = OpType_UnaryOp;
newOp->main.value = new UnaryOpT;
newOp->main.type = OpParameter_UnaryOp;
newOp->main.AsUnaryOp()->opType = UnaryOpOperation_SQUARE;
auto newVar = Variable::create(Expr::create(newOp.get(), {inputVar}, 1));
newVar->setName(expr->outputName(0));
Expr::replace(expr, newVar->expr().first);
return true;
}
}
return false;
};
TemplateMerge::getInstance("Merge").insertTemplateV2("FusePow2ToSquare", transform);
}
{
auto input = _Input({}, NCHW);
auto input1 = _Input({}, NCHW);
auto res = _Divide(input, _Sqrt(input1));
std::vector<EXPRP> templatesExprs = {
res->expr().first,
};
auto transform = [templatesExprs, input, input1](EXPRP expr) {
for (auto templateExpr : templatesExprs) {
std::map<EXPRP, VARP> inputConst;
if (isTheSameRec(templateExpr, expr, inputConst)) {
auto inputVarIter = inputConst.find(input->expr().first);
if (inputVarIter == inputConst.end()) {
MNN_ERROR("Invalid Match, may be something is wrong for Fuse\n");
continue;
}
auto inputVar = inputVarIter->second;
auto input1VarIter = inputConst.find(input1->expr().first);
if (input1VarIter == inputConst.end()) {
MNN_ERROR("Invalid Match, may be something is wrong for Fuse\n");
continue;
}
auto input1Var = input1VarIter->second;
auto newVar = _Multiply(inputVar, _Rsqrt(input1Var));
newVar->setName(expr->outputName(0));
Expr::replace(expr, newVar->expr().first);
return true;
}
}
return false;
};
TemplateMerge::getInstance("Merge").insertTemplateV2("TurnDivSqrtToMulRSqrt", transform);
}
{
auto input = _Input({}, NHWC);
auto const707 = _Scalar<float>(0.707106769);
auto constOne = _Scalar<float>(1.0f);
auto constHalf = _Scalar<float>(0.5);
auto res = (MNN::Express::_Erf(input * const707) + constOne) * input * constHalf;
auto res2 = input * (MNN::Express::_Erf(input * const707) + constOne) * constHalf;
std::vector<EXPRP> templatesExprs = {
res->expr().first, res2->expr().first
};
auto transform = [templatesExprs, input](EXPRP expr) {
auto config = Global<modelConfig>::Get();
auto unaryType = UnaryOpOperation_GELU_STANDARD;
if (config->useGeluApproximation) {
unaryType = UnaryOpOperation_GELU;
}
for (auto templateExpr : templatesExprs) {
std::map<EXPRP, VARP> inputConst;
if (isTheSameRec(templateExpr, expr, inputConst)) {
auto inputVarIter = inputConst.find(input->expr().first);
if (inputVarIter == inputConst.end()) {
MNN_ERROR("Invalid Match, may be something is wrong for Fuse\n");
continue;
}
auto inputVar = inputVarIter->second;
std::unique_ptr<MNN::OpT> newUnary(new MNN::OpT);
newUnary->type = OpType_UnaryOp;
newUnary->main.type = OpParameter_UnaryOp;
newUnary->main.value = new UnaryOpT;
newUnary->main.AsUnaryOp()->opType = unaryType;
auto newVar = MNN::Express::Variable::create(MNN::Express::Expr::create(newUnary.get(), {inputVar}));
newVar->setName(expr->outputName(0));
Expr::replace(expr, newVar->expr().first);
return true;
}
}
return false;
};
TemplateMerge::getInstance("Merge").insertTemplateV2("FuseGELU", transform);
}
return true;
}();
}
} // namespace MNN