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

278 lines
8.3 KiB
C++

//
// FuseGroupNorm.cpp
// MNNConverter
//
// Created by MNN on 2024/01/08.
// Copyright © 2018, Alibaba Group Holding Limited
//
#include <unordered_map>
#include "../TemplateMerge.hpp"
#include "MNN/expr/ExprCreator.hpp"
#include "MNN_generated.h"
#include "MergeHelpers.hpp"
namespace MNN {
namespace Express {
class FuseGroupNormWithSwish {
public:
FuseGroupNormWithSwish();
protected:
bool match_group_norm(EXPRP expr, bool testSwish);
bool fold_group_norm(EXPRP expr, bool testSwish);
private:
VARP group_norm_input_0;
VARP group_norm_input_1;
float mEpsilon;
int mSwish = 0;
bool mHasPrefixAdd = false;
VARP gamma_var_;
VARP beta_var_;
EXPRP mGroupNorm;
int mGroup;
};
bool IsSigmoid(EXPRP expr) {
const Op* op = expr->get();
if (op == nullptr) {
return false;
}
if (op->type() == OpType_Sigmoid) {
return true;
}
if (op->type() == OpType_UnaryOp && op->main_as_UnaryOp()->opType() == UnaryOpOperation_SIGMOID) {
return true;
}
return false;
}
bool IsLayerNorm(EXPRP expr) {
const Op* op = expr->get();
if (op == nullptr) {
return false;
}
if (op->type() == OpType_LayerNorm) {
return true;
}
return false;
}
bool IsGroupNormNoSwish(EXPRP expr) {
const Op* op = expr->get();
if (op == nullptr) {
return false;
}
if (op->type() == OpType_GroupNorm && op->main_as_GroupNorm()->bSwish() == 0) {
return true;
}
return false;
}
bool FuseGroupNormWithSwish::match_group_norm(EXPRP expr, bool testSwish) {
auto config = Global<modelConfig>::Get();
if(!config->transformerFuse) {
return false;
}
// mul
if (!expr->get()) {
return false;
}
EXPRP x, y, z;
if (testSwish && helpers::IsBinaryMul(expr)) {
// mul
x = expr->inputs().at(0)->expr().first;
y = expr->inputs().at(1)->expr().first;
if (IsGroupNormNoSwish(x) && IsSigmoid(y) && (x == y->inputs().at(0)->expr().first)) {
z = x;
} else if (IsGroupNormNoSwish(y) && IsSigmoid(x) && (y == x->inputs().at(0)->expr().first)) {
z = y;
} else {
return false;
}
mSwish = 1;
mGroupNorm = z;
return true;
} else if (!testSwish && helpers::IsBinaryAdd(expr)) {
z = expr;
} else {
return false;
}
// x * a
x = z->inputs().at(0)->expr().first;
y = z->inputs().at(1)->expr().first;
if (helpers::IsBinaryMul(x) && helpers::IsConstant(y)) {
beta_var_ = z->inputs().at(1);
z = x;
} else if (helpers::IsBinaryMul(y) && helpers::IsConstant(x)) {
beta_var_ = z->inputs().at(0);
z = y;
} else {
return false;
}
// reshape
x = z->inputs().at(0)->expr().first;
y = z->inputs().at(1)->expr().first;
if (helpers::IsReshape(x) && helpers::IsConstant(y)) {
gamma_var_ = z->inputs().at(1);
z = x;
} else if (helpers::IsReshape(y) && helpers::IsConstant(x)) {
gamma_var_ = z->inputs().at(0);
z = y;
} else {
return false;
}
// reshape -> LayerNorm
x = z->inputs().at(0)->expr().first;
if(helpers::IsReshape(x) && IsLayerNorm(x->inputs().at(0)->expr().first)) {
z = x->inputs().at(0)->expr().first;
mEpsilon = z->get()->main_as_LayerNorm()->epsilon();
} else {
return false;
}
// reshape -> Reshape
x = z->inputs().at(0)->expr().first;
if(helpers::IsReshape(x) && helpers::IsReshape(x->inputs().at(0)->expr().first)) {
z = x->inputs().at(0)->expr().first;
} else {
return false;
}
x = z->inputs().at(0)->expr().first;
auto var_reshape_group = z->inputs().at(1);
mGroup = var_reshape_group->readMap<int32_t>()[1];
if(mGroup < 1) {
return false;
}
mHasPrefixAdd = false;
if(helpers::IsBinaryAdd(x)) {
auto add_ = x;
x = add_->inputs().at(0)->expr().first;
y = add_->inputs().at(1)->expr().first;
if(helpers::IsConvolution(x) && helpers::IsUnsqueeze(y)) {
x = y->inputs().at(0)->expr().first;
if(helpers::IsUnsqueeze(x)) {
mHasPrefixAdd = true;
group_norm_input_0 = add_->inputs().at(0);
group_norm_input_1 = x->inputs().at(0);
return true;
}
}
}
group_norm_input_0 = z->inputs().at(0);
return true;
}
bool FuseGroupNormWithSwish::fold_group_norm(EXPRP expr, bool testSwish) {
auto config = Global<modelConfig>::Get();
auto version = config->targetVersion;
if (version < 2.8f) {
// For target version < 2.8 , don't support group_norm
return false;
}
std::unique_ptr<MNN::GroupNormT> group_norm(new MNN::GroupNormT);
if(mSwish) {
auto gn = mGroupNorm->get()->main_as_GroupNorm();
group_norm->epsilon = gn->epsilon();
group_norm->bSwish = 1;
group_norm->group = gn->group();
int size = gn->gamma()->size();
group_norm->gamma.resize(size);
group_norm->beta.resize(size);
memcpy(group_norm->gamma.data(), gn->gamma()->data(), size * sizeof(float));
memcpy(group_norm->beta.data(), gn->beta()->data(), size * sizeof(float));
std::unique_ptr<OpT> group_norm_op(new OpT);
group_norm_op->name = mGroupNorm->name();
group_norm_op->type = OpType_GroupNorm;
group_norm_op->main.type = OpParameter_GroupNorm;
group_norm_op->main.value = group_norm.release();
auto group_norm_expr = Variable::create(Expr::create(group_norm_op.get(), mGroupNorm->inputs(), 1));
group_norm_expr->setName("GroupNorm_" + expr->name());
Expr::replace(expr, group_norm_expr->expr().first);
return true /*modified*/;
}
group_norm->epsilon = mEpsilon;
group_norm->bSwish = mSwish;
group_norm->group = mGroup;
auto* gamma_info = gamma_var_->getInfo();
auto* beta_info = beta_var_->getInfo();
const float* gamma = gamma_var_->readMap<float>();
const float* beta = beta_var_->readMap<float>();
if (!gamma_info || !beta_info || !gamma || !beta || gamma_info->size != beta_info->size) {
return false;
}
int size = gamma_info->size;
group_norm->gamma.resize(size);
group_norm->beta.resize(size);
memcpy(group_norm->gamma.data(), gamma, size * sizeof(float));
memcpy(group_norm->beta.data(), beta, size * sizeof(float));
std::unique_ptr<OpT> group_norm_op(new OpT);
group_norm_op->name = expr->name();
group_norm_op->type = OpType_GroupNorm;
group_norm_op->main.type = OpParameter_GroupNorm;
group_norm_op->main.value = group_norm.release();
if(mHasPrefixAdd) {
auto group_norm_expr = Variable::create(Expr::create(group_norm_op.get(), {group_norm_input_0, group_norm_input_1 }, 1));
group_norm_expr->setName("GroupNorm_" + expr->name());
Expr::replace(expr, group_norm_expr->expr().first);
} else {
auto group_norm_expr = Variable::create(Expr::create(group_norm_op.get(), { group_norm_input_0 }, 1));
group_norm_expr->setName("GroupNorm_" + expr->name());
Expr::replace(expr, group_norm_expr->expr().first);
}
return true /*modified*/;
}
FuseGroupNormWithSwish::FuseGroupNormWithSwish() {
auto match_with_swish = [this](EXPRP expr) -> bool {
return match_group_norm(expr, true);
};
auto fold_with_swish = [this](EXPRP expr) -> bool {
return fold_group_norm(expr, true);
};
TemplateMerge::getInstance("Merge").insertTemplate("FuseGroupNormWithSwish", match_with_swish, fold_with_swish);
}
class FuseGroupNormNoSwish : public FuseGroupNormWithSwish {
public:
FuseGroupNormNoSwish();
};
FuseGroupNormNoSwish::FuseGroupNormNoSwish() {
auto match_no_swish = [this](EXPRP expr) -> bool {
return match_group_norm(expr, false);
};
auto fold_no_swish = [this](EXPRP expr) -> bool {
return fold_group_norm(expr, false);
};
TemplateMerge::getInstance("Merge").insertTemplate("FuseGroupNormNoSwish", match_no_swish, fold_no_swish);
}
//static FuseGroupNormWithSwish g_fuse_groupnorm_with_swish;
//static FuseGroupNormNoSwish g_fuse_groupnorm_no_swish;
} // namespace Express
} // namespace MNN