【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:
parent
5061e72d39
commit
089955f862
@ -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"),
|
||||
|
@ -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()
|
119
src/frontends/paddle/src/op/group_norm.cpp
Normal file
119
src/frontends/paddle/src/op/group_norm.cpp
Normal 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
|
@ -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},
|
||||
|
Loading…
Reference in New Issue
Block a user