mirror of
https://github.com/saymrwulf/onnxruntime.git
synced 2026-07-30 20:18:08 +00:00
Update Conv-Add-Relu Fusion Transformation (#15834)
…n name to make it more intuitive. ### Description Update Conv-Add-Relu Fusion Transformation to handle additional case where NhwcFusedConv is present. ### Motivation and Context Handle additional case where NhwcFusedConv is present.
This commit is contained in:
parent
02d94bcc8e
commit
c2e631d356
4 changed files with 110 additions and 18 deletions
|
|
@ -71,9 +71,9 @@ bool ConvFusionDataTypeCheck(const Node& conv_node) {
|
|||
return true;
|
||||
}
|
||||
|
||||
class ConvActivation : public NodeSelector {
|
||||
class ConvActivationSelector : public NodeSelector {
|
||||
public:
|
||||
ConvActivation() = default;
|
||||
ConvActivationSelector() = default;
|
||||
|
||||
std::optional<NodesToOptimizeIndices> Select(const GraphViewer& graph_viewer, const Node& node) const override {
|
||||
const std::string_view node_ep = node.GetExecutionProviderType();
|
||||
|
|
@ -172,7 +172,7 @@ class ConvAddRelu : public NodeSelector {
|
|||
namespace actions {
|
||||
using NTO = NodesToOptimize;
|
||||
|
||||
class FuseConvActivation : public ReplaceWithNew {
|
||||
class FuseConvActivationAction : public ReplaceWithNew {
|
||||
private:
|
||||
std::string OpType(const RuntimeState&) const override { return "FusedConv"; }
|
||||
|
||||
|
|
@ -258,9 +258,9 @@ class FuseConvAddRelu : public ReplaceWithNew {
|
|||
|
||||
void RegisterConvActivationFusionRules(SelectorActionRegistry& registry) {
|
||||
const auto name = "ConvAct";
|
||||
auto action = std::make_unique<actions::FuseConvActivation>();
|
||||
auto action = std::make_unique<actions::FuseConvActivationAction>();
|
||||
#if !defined(ORT_MINIMAL_BUILD)
|
||||
auto selector = std::make_unique<selectors::ConvActivation>();
|
||||
auto selector = std::make_unique<selectors::ConvActivationSelector>();
|
||||
registry.RegisterSelectorAndAction(name, {{"Conv", {1, 11}}},
|
||||
std::move(selector), std::move(action));
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -40,10 +40,9 @@ const Node* GetLoneConsumerNode(const GraphViewer& graph_viewer, const Node& nod
|
|||
return &*node.OutputNodesBegin();
|
||||
}
|
||||
|
||||
class ConvAddActivation : public NodeSelector {
|
||||
class ConvAddActivationSelector : public NodeSelector {
|
||||
public:
|
||||
ConvAddActivation() = default;
|
||||
|
||||
ConvAddActivationSelector() = default;
|
||||
std::optional<NodesToOptimizeIndices> Select(const GraphViewer& graph_viewer, const Node& node) const override {
|
||||
const std::string_view node_ep = node.GetExecutionProviderType();
|
||||
#ifdef MLAS_F16VEC_INTRINSICS_SUPPORTED
|
||||
|
|
@ -62,7 +61,7 @@ class ConvAddActivation : public NodeSelector {
|
|||
// 1. Its type is 'conv', 2. it has to satisfy the other requirements,like shape, please refer to SelectConvProducer for more info
|
||||
const Node* conv_node = nullptr;
|
||||
const auto* add_node = GetLoneConsumerNode(graph_viewer, node);
|
||||
if (!add_node) {
|
||||
if (add_node == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
// Let's support addition first, leave any-element-wise-op fusion in the future.
|
||||
|
|
@ -73,13 +72,13 @@ class ConvAddActivation : public NodeSelector {
|
|||
if (graph_utils::IsSupportedOptypeVersionAndDomain(*add_node, "Add", {7, 13, 14})) {
|
||||
conv_node = SelectProducerConv(*add_node);
|
||||
}
|
||||
if (!conv_node) {
|
||||
if (conv_node == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
// GetLoneConsumerNode will ensure outputedge_count is 1
|
||||
const auto* act_node = GetLoneConsumerNode(graph_viewer, *add_node);
|
||||
// even the next node is not a activation node, it's also fine.
|
||||
if (!act_node) {
|
||||
if (act_node == nullptr) {
|
||||
// we can't fuse add-activation when add_node has multiple consumer nodes
|
||||
act_node = nullptr;
|
||||
} else if (SelectActivation(graph_viewer, *act_node)) {
|
||||
|
|
@ -91,7 +90,7 @@ class ConvAddActivation : public NodeSelector {
|
|||
NodesToOptimizeIndicesBuilder builder{};
|
||||
builder.target_node = conv_node->Index();
|
||||
builder.output_nodes = {add_node->Index()};
|
||||
if (act_node) {
|
||||
if (act_node != nullptr) {
|
||||
builder.output_nodes.push_back(act_node->Index());
|
||||
}
|
||||
return builder.Build();
|
||||
|
|
@ -176,8 +175,8 @@ class ConvAddActivation : public NodeSelector {
|
|||
// Check if this is a single use convolution that hasn't already
|
||||
// been fused with another Add/Sum node. The Add/Sum can also only be
|
||||
// fused if the convolution isn't itself fused with an activation.
|
||||
if ((inputs_node[n]->OpType() == "Conv") && (pre_input_defs_count < 4) && (producer_input_args_count.size() < 4) &&
|
||||
(graph_utils::GetNodeAttribute(*inputs_node[n], "activation") == nullptr) && (inputs_node[n]->GetOutputEdgesCount() == 1)) {
|
||||
if (
|
||||
(inputs_node[n]->OpType() == "Conv" || inputs_node[n]->OpType() == "NhwcFusedConv") && (pre_input_defs_count < 4) && (producer_input_args_count.size() < 4) && (graph_utils::GetNodeAttribute(*inputs_node[n], "activation") == nullptr) && (inputs_node[n]->GetOutputEdgesCount() == 1)) {
|
||||
if (pre_input_defs_count < 3) {
|
||||
// The optional bias parameter is empty so set to an empty string.
|
||||
// TODO, add a new null arguments for bias
|
||||
|
|
@ -196,9 +195,14 @@ class ConvAddActivation : public NodeSelector {
|
|||
namespace actions {
|
||||
using NTO = NodesToOptimize;
|
||||
|
||||
class FuseConvAddActivation : public ReplaceWithNew {
|
||||
class FuseConvAddActivationAction : public ReplaceWithNew {
|
||||
public:
|
||||
FuseConvAddActivationAction() = default;
|
||||
|
||||
private:
|
||||
std::string OpType(const RuntimeState&) const override { return "FusedConv"; }
|
||||
std::string OpType(const RuntimeState& runtimeState) const override {
|
||||
return (runtimeState.selected_nodes.Target().OpType() == "Conv") ? "FusedConv" : "NhwcFusedConv";
|
||||
}
|
||||
|
||||
std::string Domain(const RuntimeState&) const override { return kMSDomain; }
|
||||
|
||||
|
|
@ -272,8 +276,8 @@ class FuseConvAddActivation : public ReplaceWithNew {
|
|||
|
||||
void RegisterConvAddActivationFusionRules(SelectorActionRegistry& registry) {
|
||||
const auto name = "ConvAddAct";
|
||||
auto action = std::make_unique<actions::FuseConvAddActivation>();
|
||||
auto selector = std::make_unique<selectors::ConvAddActivation>();
|
||||
auto action = std::make_unique<actions::FuseConvAddActivationAction>();
|
||||
auto selector = std::make_unique<selectors::ConvAddActivationSelector>();
|
||||
registry.RegisterSelectorAndAction(name, {{"Conv", {1, 11}}},
|
||||
std::move(selector), std::move(action));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,6 +112,23 @@ TEST_F(ResNet50FusionTests, FuseConvAddUnitTest) {
|
|||
ASSERT_STATUS_OK(Model::Save(*p_model, "conv_add_fp16_fused.onnx"));
|
||||
ASSERT_TRUE(op_to_count["Add"] == 0); // Add removed from graph
|
||||
}
|
||||
TEST_F(ResNet50FusionTests, FuseConvReluUnitTest) {
|
||||
constexpr const ORTCHAR_T* model_uri = MODEL_FOLDER "fusion/conv_relu_fp16.onnx";
|
||||
std::shared_ptr<Model> p_model;
|
||||
ASSERT_STATUS_OK(Model::Load(model_uri, p_model, nullptr, *logger));
|
||||
Graph& graph = p_model->MainGraph();
|
||||
for (auto& node : p_model->MainGraph().Nodes()) {
|
||||
node.SetExecutionProviderType(kCpuExecutionProvider);
|
||||
}
|
||||
std::map<std::string, int> op_to_count = CountOpsInGraph(graph);
|
||||
ASSERT_TRUE(op_to_count["Relu"] == 1);
|
||||
onnxruntime::GraphTransformerManager graph_transformation_mgr{5};
|
||||
ASSERT_STATUS_OK(graph_transformation_mgr.Register(std::make_unique<ConvActivationFusion>(), TransformerLevel::Level3));
|
||||
ASSERT_STATUS_OK(graph_transformation_mgr.ApplyTransformers(graph, TransformerLevel::Level3, *logger));
|
||||
op_to_count = CountOpsInGraph(graph);
|
||||
ASSERT_STATUS_OK(Model::Save(*p_model, "conv_relu_fp16_fused.onnx"));
|
||||
ASSERT_TRUE(op_to_count["Relu"] == 0); // Add removed from graph
|
||||
}
|
||||
#endif // defined(MLAS_F16VEC_INTRINSICS_SUPPORTED) && !defined(DISABLE_CONTRIB_OPS)
|
||||
} // namespace test
|
||||
} // namespace onnxruntime
|
||||
71
onnxruntime/test/testdata/transform/fusion/conv_relu_fp16.onnx
vendored
Normal file
71
onnxruntime/test/testdata/transform/fusion/conv_relu_fp16.onnx
vendored
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
onnx_conv_relu:Ë
|
||||
;
|
||||
Wgraph_input_cast_1graph_input_cast1"Cast*
|
||||
to
|
||||
|
||||
;
|
||||
Xgraph_input_cast_0graph_input_cast0"Cast*
|
||||
to
|
||||
|
||||
H
|
||||
graph_input_cast_0
|
||||
graph_input_cast_1Y"Conv*
|
||||
kernel_shape@@
|
||||
|
||||
Ygraph_output_cast_0"Relu
|
||||
=
|
||||
graph_output_cast_0Zgraph_output_cast0"Cast*
|
||||
to
|
||||
test-modelZ
|
||||
X
|
||||
|
||||
|
||||
|
||||
|
||||
Z
|
||||
W
|
||||
|
||||
|
||||
|
||||
|
||||
b
|
||||
Z
|
||||
|
||||
|
||||
|
||||
|
||||
j
|
||||
Y
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
j
|
||||
Z
|
||||
|
||||
|
||||
|
||||
|
||||
j,
|
||||
graph_input_cast_0
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
j,
|
||||
graph_input_cast_1
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
j-
|
||||
graph_output_cast_0
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
B
|
||||
Loading…
Reference in a new issue