Skip to content

Commit 79d14c5

Browse files
sgbihuCopilot
andauthored
Fix the accuracy issue by update celu formula based on spec (#33500)
### Details: - The implementation didn't align with [ONNX spec](https://onnx.ai/onnx/operators/onnx__Celu.html). ``` max(0,x) + min(0,alpha*(exp(x/alpha)-1)) ``` ### Tickets: - [CVS-178958](https://jira.devtools.intel.com/browse/CVS-178958) --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent dd2c8cb commit 79d14c5

File tree

4 files changed

+122
-4
lines changed

4 files changed

+122
-4
lines changed

src/frontends/onnx/frontend/src/op/celu.cpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
// Copyright (C) 2018-2026 Intel Corporation
22
// SPDX-License-Identifier: Apache-2.0
3+
//
34

45
#include <memory>
56

67
#include "core/operator_set.hpp"
78
#include "exceptions.hpp"
9+
#include "openvino/op/add.hpp"
810
#include "openvino/op/constant.hpp"
911
#include "openvino/op/divide.hpp"
1012
#include "openvino/op/elu.hpp"
13+
#include "openvino/op/exp.hpp"
14+
#include "openvino/op/maximum.hpp"
15+
#include "openvino/op/minimum.hpp"
1116
#include "openvino/op/multiply.hpp"
17+
#include "openvino/op/subtract.hpp"
1218
#include "utils/common.hpp"
1319
using namespace ov::op;
1420

@@ -19,12 +25,20 @@ namespace ai_onnx {
1925
namespace opset_1 {
2026
ov::OutputVector celu(const ov::frontend::onnx::Node& node) {
2127
auto alpha_node = node.get_attribute_as_constant<float>("alpha", 1.0f);
28+
auto zero = v0::Constant::create(alpha_node->get_element_type(), ov::Shape{}, {0.0f});
2229
auto x_celu = node.get_ov_inputs().at(0);
2330

31+
auto x_max = std::make_shared<v1::Maximum>(x_celu, zero);
32+
2433
auto divide_node = std::make_shared<v1::Divide>(x_celu, alpha_node);
25-
auto elu_node = std::make_shared<v0::Elu>(divide_node, 1.0);
34+
auto exp_node = std::make_shared<v0::Exp>(divide_node);
35+
auto exp_minus_one =
36+
std::make_shared<v1::Subtract>(exp_node,
37+
v0::Constant::create(exp_node->get_element_type(), ov::Shape{}, {1.0f}));
38+
auto celu_temp = std::make_shared<v1::Multiply>(alpha_node, exp_minus_one);
39+
auto min_node = std::make_shared<v1::Minimum>(celu_temp, zero);
2640

27-
return {std::make_shared<v1::Multiply>(alpha_node, elu_node)};
41+
return {std::make_shared<v1::Add>(x_max, min_node)};
2842
}
2943
ONNX_OP("Celu", OPSET_SINCE(1), ai_onnx::opset_1::celu);
3044
} // namespace opset_1
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
ir_version: 12
2+
producer_name: "ONNX Celu"
3+
model_version: 0
4+
graph {
5+
name: "test"
6+
node {
7+
input: "X"
8+
output: "Y"
9+
name: "node1"
10+
op_type: "Celu"
11+
attribute {
12+
name: "alpha"
13+
f: -0.5
14+
type: FLOAT
15+
}
16+
doc_string: "Celu"
17+
}
18+
input {
19+
name: "X"
20+
type {
21+
tensor_type {
22+
elem_type: 1
23+
shape {
24+
dim {
25+
dim_value: 13
26+
}
27+
}
28+
}
29+
}
30+
}
31+
output {
32+
name: "Y"
33+
type {
34+
tensor_type {
35+
elem_type: 1
36+
shape {
37+
dim {
38+
dim_value: 13
39+
}
40+
}
41+
}
42+
}
43+
}
44+
}
45+
opset_import {
46+
version: 12
47+
}

src/frontends/onnx/tests/onnx_import.in.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2674,6 +2674,48 @@ OPENVINO_TEST(${BACKEND_NAME}, onnx_model_matmul_vec_ten3d) {
26742674
test_case.run();
26752675
}
26762676

2677+
OPENVINO_TEST(${BACKEND_NAME}, onnx_activation_celu) {
2678+
auto model = convert_model("activation_celu.onnx");
2679+
2680+
// -1.0f, 0, 1.0f, 10.f, normal input values for activation
2681+
// 100.0f, -100.0f, 1000.0f, -1000.0f, input values that leads to exp() overflow
2682+
// FLT_MIN, FLT_MIN / 16, -FLT_MIN / 16, min, denorm, -denorm
2683+
// FLT_MAX, -FLT_MAX, max, -max;
2684+
Inputs inputs{std::vector<float>{-1.0f,
2685+
0,
2686+
1.0f,
2687+
10.f,
2688+
100.0f,
2689+
-100.0f,
2690+
1000.0f,
2691+
-1000.0f,
2692+
FLT_MIN,
2693+
FLT_MIN / 16,
2694+
-FLT_MIN / 16,
2695+
FLT_MAX,
2696+
-FLT_MAX}};
2697+
2698+
const auto inf = std::numeric_limits<float>::infinity();
2699+
std::vector<float> output{-3.194527864456177f,
2700+
0.0f,
2701+
1.f,
2702+
10.f,
2703+
100.0f,
2704+
-inf,
2705+
1000.0f,
2706+
-inf,
2707+
1.175494350822288e-38f,
2708+
7.346839692639297e-40f,
2709+
0.0f,
2710+
inf,
2711+
-inf};
2712+
2713+
auto test_case = ov::test::TestCase(model, s_device);
2714+
test_case.add_multiple_inputs(inputs);
2715+
test_case.add_expected_output(output);
2716+
test_case.run();
2717+
}
2718+
26772719
OPENVINO_TEST(${BACKEND_NAME}, onnx_model_softplus) {
26782720
auto model = convert_model("softplus.onnx");
26792721

src/frontends/pytorch/src/op/celu.cpp

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,14 @@
33
//
44

55
#include "openvino/frontend/pytorch/node_context.hpp"
6+
#include "openvino/op/add.hpp"
67
#include "openvino/op/divide.hpp"
78
#include "openvino/op/elu.hpp"
9+
#include "openvino/op/exp.hpp"
10+
#include "openvino/op/maximum.hpp"
11+
#include "openvino/op/minimum.hpp"
812
#include "openvino/op/multiply.hpp"
13+
#include "openvino/op/subtract.hpp"
914
#include "utils.hpp"
1015

1116
namespace ov {
@@ -26,11 +31,21 @@ OutputVector translate_celu(const NodeContext& context) {
2631
alpha = context.get_input(1);
2732
}
2833

34+
// CELU(x)=max(0,x)+min(0,a*(exp(x/a)-1))
35+
auto zero = context.mark_node(v0::Constant::create(element::f32, Shape{}, {0.}));
36+
zero = context.mark_node(std::make_shared<v1::ConvertLike>(zero, x));
37+
auto x_max = context.mark_node(std::make_shared<v1::Maximum>(x, zero));
38+
2939
alpha = context.mark_node(std::make_shared<v1::ConvertLike>(alpha, x));
3040
auto divide_node = context.mark_node(std::make_shared<v1::Divide>(x, alpha));
31-
auto elu_node = context.mark_node(std::make_shared<v0::Elu>(divide_node, 1.));
41+
auto exp_node = context.mark_node(std::make_shared<v0::Exp>(divide_node));
42+
auto one = context.mark_node(v0::Constant::create(element::f32, Shape{}, {1.}));
43+
one = context.mark_node(std::make_shared<v1::ConvertLike>(one, x));
44+
auto exp_minus_one = context.mark_node(std::make_shared<v1::Subtract>(exp_node, one));
45+
auto elu_node = context.mark_node(std::make_shared<v1::Multiply>(alpha, exp_minus_one));
46+
auto min_node = context.mark_node(std::make_shared<v1::Minimum>(elu_node, zero));
3247

33-
auto elu = context.mark_node(std::make_shared<v1::Multiply>(alpha, elu_node));
48+
auto elu = context.mark_node(std::make_shared<v1::Add>(x_max, min_node));
3449
return {elu};
3550
};
3651

0 commit comments

Comments
 (0)