Skip to content

Commit 9c6351f

Browse files
authored
[WebNN] Support AveragePool with count_include_pad == 1 (#24465)
### Description WebNN doesn't support AveragePool with count_include_pad == 1. ### Motivation and Context Support it by adding a pad and calling averagePool2D with pads as 0's.
1 parent 6df6206 commit 9c6351f

File tree

2 files changed

+23
-13
lines changed

2 files changed

+23
-13
lines changed

Diff for: js/web/docs/webnn-operators.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ platforms. Check the [WebNN status](https://webmachinelearning.github.io/webnn-s
1818
| And | ai.onnx(7+) | logicalAnd | |
1919
| ArgMax | ai.onnx(7-10, 11, 12, 13+) | argMax | |
2020
| ArgMin | ai.onnx(7-10, 11, 12, 13+) | argMin | |
21-
| AveragePool | ai.onnx(7-9, 10, 11, 12-18, 19+) | averagePool2d | Only supports 4-D input, 2-D 'kernel_shape', 'count_include_pad' value is 0 |
21+
| AveragePool | ai.onnx(7-9, 10, 11, 12-18, 19+) | averagePool2d | Only supports 4-D input, 2-D 'kernel_shape' |
2222
| BatchNormalization | ai.onnx(7-8, 9-13, 14, 15+) | batchNormalization | Only supports 'training_mode' value is 0, one output |
2323
| Cast | ai.onnx(7-8, 9-12, 13-18, 19-20, 21+) | cast | |
2424
| Ceil | ai.onnx(7-12, 13+) | ceil | |

Diff for: onnxruntime/core/providers/webnn/builders/impl/pool_op_builder.cc

+22-12
Original file line numberDiff line numberDiff line change
@@ -60,16 +60,17 @@ Status PoolOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder,
6060
emscripten::val options = emscripten::val::object();
6161
options.set("label", node.Name());
6262
NodeAttrHelper helper(node);
63+
const bool is_nhwc = model_builder.GetPreferredLayout() == DataLayout::NHWC;
6364

64-
const auto kernel_shape = helper.Get("kernel_shape", std::vector<int32_t>{0, 0});
65+
const auto onnx_kernel_shape = helper.Get("kernel_shape", std::vector<int32_t>{0, 0});
6566
if (!is_global_pooling) {
66-
options.set("windowDimensions", emscripten::val::array(kernel_shape));
67+
options.set("windowDimensions", emscripten::val::array(onnx_kernel_shape));
6768
}
6869
const auto strides = helper.Get("strides", std::vector<int32_t>{1, 1});
6970
options.set("strides", emscripten::val::array(strides));
7071
const auto dilations = helper.Get("dilations", std::vector<int32_t>{1, 1});
7172
options.set("dilations", emscripten::val::array(dilations));
72-
if (model_builder.GetPreferredLayout() == DataLayout::NHWC) {
73+
if (is_nhwc) {
7374
options.set("layout", emscripten::val("nhwc"));
7475
} else {
7576
options.set("layout", emscripten::val("nchw"));
@@ -78,7 +79,6 @@ Status PoolOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder,
7879
// Add Padding.
7980
// Usually using autopadding is more efficient than using explicit padding.
8081
// Try to see if we can map explicit padding to auto padding.
81-
const auto onnx_kernel_shape = helper.Get("kernel_shape", std::vector<int64_t>{0, 0});
8282
const auto onnx_strides = helper.Get("strides", std::vector<int64_t>{1, 1});
8383
const auto onnx_pads = helper.Get("pads", std::vector<int64_t>{0, 0, 0, 0});
8484
auto pads = helper.Get("pads", std::vector<uint32_t>{0, 0, 0, 0});
@@ -93,7 +93,7 @@ Status PoolOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder,
9393
helper.Get("dilations", std::vector<int64_t>{1, 1}),
9494
auto_pad_type,
9595
pads_out,
96-
model_builder.GetPreferredLayout() == DataLayout::NCHW));
96+
!is_nhwc));
9797
pads = GetNarrowedIntfromInt64<uint32_t>(pads_out);
9898
}
9999
// Permute the ONNX's pads, which is [beginning_height, beginning_width, ending_height, ending_width],
@@ -105,6 +105,23 @@ Status PoolOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder,
105105
options.set("roundingType", ceil_mode == 0 ? emscripten::val("floor")
106106
: emscripten::val("ceil"));
107107

108+
// WebNN doesn't support AveragePool with count_include_pad == 1, emulate it by pad + averagePool2d.
109+
if (op_type == "AveragePool" && helper.Get("count_include_pad", 0) == 1) {
110+
std::vector<uint32_t> beginning_padding{0, 0, pads[0], pads[1]};
111+
std::vector<uint32_t> ending_padding{0, 0, pads[2], pads[3]};
112+
// Unset padding option, because we will use pad op instead.
113+
options.set("padding", emscripten::val::array(std::vector<uint32_t>{0, 0, 0, 0}));
114+
if (is_nhwc) {
115+
beginning_padding = {0, pads[0], pads[1], 0};
116+
ending_padding = {0, pads[2], pads[3], 0};
117+
}
118+
119+
emscripten::val pad_options = emscripten::val::object();
120+
pad_options.set("label", node.Name() + "_pad");
121+
input = model_builder.GetBuilder().call<emscripten::val>("pad", input, emscripten::val::array(beginning_padding),
122+
emscripten::val::array(ending_padding), pad_options);
123+
}
124+
108125
emscripten::val output = model_builder.GetBuilder().call<emscripten::val>(webnn_op_name.c_str(), input, options);
109126
model_builder.AddOperand(node.OutputDefs()[0]->Name(), std::move(output));
110127
return Status::OK();
@@ -138,13 +155,6 @@ bool PoolOpBuilder::IsOpSupportedImpl(const GraphViewer&,
138155
}
139156
}
140157

141-
if (op_type == "AveragePool") {
142-
if (helper.Get("count_include_pad", 0) != 0) {
143-
LOGS(logger, VERBOSE) << "AveragePool only supports count_include_pad == 0";
144-
return false;
145-
}
146-
}
147-
148158
if (op_type == "MaxPool") {
149159
if (helper.Get("storage_order", 0) == 1) {
150160
LOGS(logger, VERBOSE) << "MaxPool storage_order == 1 is not supported";

0 commit comments

Comments
 (0)