【PaddlePaddle Hackathon 3】Add Paddle group_norm operator (#12329)

* add paddle group norm

* remove unecessary code

* remove unecessary code

* remove unecessary code
This commit is contained in:
Netpunk 2022-09-13 19:57:52 +08:00 committed by GitHub
parent 5061e72d39
commit 089955f862
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 194 additions and 0 deletions

View File

@ -176,6 +176,9 @@ static const std::vector<std::string> models{
std::string("greater_than_float32"),
std::string("greater_than_int32"),
std::string("greater_than_int64"),
std::string("group_norm_1"),
std::string("group_norm_2"),
std::string("group_norm_3"),
std::string("hard_sigmoid"),
std::string("hard_swish"),
std::string("layer_norm"),

View File

@ -0,0 +1,70 @@
# Copyright (C) 2018-2022 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
#
# group norm paddle model generator
#
import numpy as np
from save_model import saveModel
import paddle
import sys
data_type = "float32"
def group_norm(name: str, x, groups, epsilon, scale, bias, data_layout):
paddle.enable_static()
with paddle.static.program_guard(paddle.static.Program(), paddle.static.Program()):
node_x = paddle.static.data(name="x", shape=x.shape, dtype=data_type)
if scale is False:
scale_attr = scale
else:
scale_attr = paddle.ParamAttr(name="scale1", initializer=paddle.nn.initializer.Assign(scale))
if bias is False:
bias_attr = bias
else:
bias_attr = paddle.ParamAttr(name="bias1", initializer=paddle.nn.initializer.Assign(bias))
out = paddle.static.nn.group_norm(node_x, groups=groups,
epsilon=epsilon,
param_attr=scale_attr,
bias_attr=bias_attr,
data_layout=data_layout)
cpu = paddle.static.cpu_places(1)
exe = paddle.static.Executor(cpu[0])
exe.run(paddle.static.default_startup_program())
outs = exe.run(feed={"x": x}, fetch_list=[out])
saveModel(name, exe, feedkeys=['x'], fetchlist=[out], inputs=[x], outputs=[outs[0]], target_dir=sys.argv[1])
return outs[0]
def main():
# data layout is NCHW
data = np.random.random((2, 4, 3, 4)).astype(np.float32)
groups = 2
epsilon = 1e-05
scale = np.random.random(4).astype(np.float32)
bias = np.random.random(4).astype(np.float32)
group_norm("group_norm_1", data, groups, epsilon, scale, bias, "NCHW")
# data layout is NHWC
data = np.random.random((2, 4, 3, 4)).astype(np.float32)
groups = 2
epsilon = 1e-05
scale = np.random.random(4).astype(np.float32)
bias = np.random.random(4).astype(np.float32)
group_norm("group_norm_2", data, groups, epsilon, scale, bias, "NHWC")
# scale and bias are None
scale = False
bias = False
group_norm("group_norm_3", data, groups, epsilon, scale, bias, "NHWC")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,119 @@
// Copyright (C) 2018-2022 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "default_opset.hpp"
#include "openvino/frontend/paddle/node_context.hpp"
#include "openvino/frontend/paddle/visibility.hpp"
namespace ov {
namespace frontend {
namespace paddle {
namespace op {
Output<ov::Node> reshape_channel_shaped_node_to_nchw(const Output<ov::Node>& node,
const Output<ov::Node>& expected_rank) {
const auto one_const = default_opset::Constant::create(element::i64, Shape{1}, {1});
const auto two_const = default_opset::Constant::create(element::i64, Shape{1}, {2});
const auto tail_shape_rank = std::make_shared<default_opset::Subtract>(expected_rank, two_const);
const auto tail_shape = std::make_shared<default_opset::Broadcast>(one_const, tail_shape_rank);
const auto C_dim = std::make_shared<default_opset::ShapeOf>(node);
const auto new_shape = std::make_shared<default_opset::Concat>(OutputVector{one_const, C_dim, tail_shape}, 0);
return std::make_shared<default_opset::Reshape>(node, new_shape, false);
}
NamedOutputs group_norm(const NodeContext& node) {
auto data = node.get_input("X");
size_t num_groups = static_cast<size_t>(node.get_attribute<int32_t>("groups"));
auto epsilon = node.get_attribute<float>("epsilon", 1e-5);
auto data_layout = node.get_attribute<std::string>("data_layout", "NCHW");
const auto& pshape = data.get_partial_shape();
PADDLE_OP_CHECK(node, pshape.rank().is_static());
size_t rank_size = pshape.rank().get_length();
PADDLE_OP_CHECK(node, rank_size >= 2, "2-D and above tensors supported only");
if (data_layout == "NHWC") {
auto values = std::vector<size_t>{0, rank_size - 1};
for (size_t i = 1; i < rank_size - 1; i++) {
values.push_back(i);
}
auto perm1 = default_opset::Constant::create(element::i64, Shape{rank_size}, values);
data = std::make_shared<default_opset::Transpose>(data, perm1);
}
// The process below creates a shape to which we need to reshape the input before normalization.
auto num_groups_const = default_opset::Constant::create(element::i64, Shape{1}, {num_groups});
auto data_shape_node = std::make_shared<default_opset::ShapeOf>(data);
auto shape = std::make_shared<default_opset::ShapeOf>(data);
auto axis_node = default_opset::Constant::create(element::i64, Shape{}, {0});
auto split = std::make_shared<default_opset::Split>(shape, axis_node, rank_size);
auto splits = split->outputs();
ov::OutputVector new_shape{std::make_shared<default_opset::Multiply>(splits[0], num_groups_const),
std::make_shared<default_opset::Divide>(splits[1], num_groups_const)};
for (size_t i = 2; i < rank_size; i++) {
new_shape.push_back(splits[i]);
}
// The 4D shape: [N * num_groups, C // num_groups, H, W] is created
// instead of 5D shape: [N, num_groups, C // num_groups, H, W].
// The reason is the lack of support for 5D MVN input by some plugins.
auto reshaped_ = std::make_shared<default_opset::Concat>(new_shape, 0);
auto data_reshaped = std::make_shared<default_opset::Reshape>(data, reshaped_, true);
const Output<ov::Node> data_reshaped_value = data_reshaped;
PADDLE_OP_CHECK(node, data_reshaped_value.get_partial_shape().rank().is_static());
size_t reshape_rank = data_reshaped_value.get_partial_shape().rank().get_length();
std::vector<size_t> range_value;
for (size_t i = 1; i < reshape_rank; i++)
range_value.push_back(i);
const auto reduction_axes = default_opset::Constant::create(element::i64, {range_value.size()}, range_value);
auto mvn = std::make_shared<default_opset::MVN>(data_reshaped,
reduction_axes,
true,
epsilon,
ov::op::MVNEpsMode::INSIDE_SQRT);
std::shared_ptr<ov::Node> result = std::make_shared<default_opset::Reshape>(mvn, data_shape_node, true);
// The process below reshape the result that become standrd output after normalization.
const auto data_rank = std::make_shared<default_opset::ShapeOf>(data_shape_node);
if (node.has_input("Scale")) {
auto scale = node.get_input("Scale");
const auto& scale_shape = scale.get_partial_shape();
PADDLE_OP_CHECK(node, scale_shape.rank().is_static());
auto scale_rank = scale_shape.rank().get_length();
if (scale_rank == 1) {
result =
std::make_shared<default_opset::Multiply>(result,
op::reshape_channel_shaped_node_to_nchw(scale, data_rank));
} else {
result = std::make_shared<default_opset::Multiply>(result, scale);
}
}
if (node.has_input("Bias")) {
auto bias = node.get_input("Bias");
const auto& bias_shape = bias.get_partial_shape();
PADDLE_OP_CHECK(node, bias_shape.rank().is_static());
auto bias_rank = bias_shape.rank().get_length();
if (bias_rank == 1) {
result =
std::make_shared<default_opset::Add>(result, op::reshape_channel_shaped_node_to_nchw(bias, data_rank));
} else {
result = std::make_shared<default_opset::Add>(result, bias);
}
}
if (data_layout == "NHWC") {
auto values = std::vector<size_t>{0};
for (size_t i = 2; i < rank_size; i++) {
values.push_back(i);
}
values.push_back(1);
auto perm2 = default_opset::Constant::create(element::i64, Shape{rank_size}, values);
result = std::make_shared<default_opset::Transpose>(result, perm2);
}
return node.default_single_output_mapping({result}, {"Y"});
}
} // namespace op
} // namespace paddle
} // namespace frontend
} // namespace ov

View File

@ -42,6 +42,7 @@ OP_CONVERTER(floor);
OP_CONVERTER(gather);
OP_CONVERTER(gelu);
OP_CONVERTER(greater_than);
OP_CONVERTER(group_norm);
OP_CONVERTER(hard_sigmoid);
OP_CONVERTER(hard_swish);
OP_CONVERTER(layer_norm);
@ -136,6 +137,7 @@ std::map<std::string, CreatorFunction> get_supported_ops() {
{"gelu", op::gelu},
{"greater_equal", op::elementwise_greater_equal},
{"greater_than", op::greater_than},
{"group_norm", op::group_norm},
{"hard_sigmoid", op::hard_sigmoid},
{"hard_swish", op::hard_swish},
{"layer_norm", op::layer_norm},