-
Notifications
You must be signed in to change notification settings - Fork 2.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
【PaddlePaddle Hackathon 3】Add Paddle group_norm operator #12329
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
c7a4981
add paddle group norm
Patrick-Star125 60fe811
Merge remote-tracking branch 'open/master'
Patrick-Star125 5acf012
remove unecessary code
Patrick-Star125 7bbfd3f
remove unecessary code
Patrick-Star125 dc33017
Merge remote-tracking branch 'open/master'
Patrick-Star125 484e0f1
remove unecessary code
Patrick-Star125 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
70 changes: 70 additions & 0 deletions
70
src/core/tests/frontend/paddle/test_models/gen_scripts/generate_group_norm.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. | ||
luo-cheng2021 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if don't we have defined layout in the model?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In line 29, the default value of
data_layout
is "NCHW", which is same as Paddle doc description.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ilyachur Some Paddle operations have this data_layout attribute, while some are not. For NHWC layout, it is okay to use this layout conversion for the op mapper, I think. We could apply a transformation from model perspective to eliminate the conversions between operations later.