Skip to content

Commit ddfb31b

Browse files
Honryguschmue
authored andcommitted
[WebNN] Add limit to QDQ ops (#23076)
WebNN requires the `scale_shape` to be a subsample of the `input_shape`.
1 parent a2bd150 commit ddfb31b

File tree

2 files changed

+36
-2
lines changed

2 files changed

+36
-2
lines changed

js/web/docs/webnn-operators.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ operators and the supported opset domain/versions in **WebNN EP** by ONNX Runtim
2727
| Cos | ai.onnx(7+) | cos ||| |
2828
| CumSum | ai.onnx(11-13, 14+) | cumulativeSum ||| 'axis' input should be a constant |
2929
| Div | ai.onnx(7-12, 13, 14+) | div ||| |
30-
| DequantizeLinear | ai.onnx(10-12, 13-18, 19-20, 21-22, 23+) | dequantizeLinear | || |
30+
| DequantizeLinear | ai.onnx(10-12, 13-18, 19-20, 21-22, 23+) | dequantizeLinear | || The shape of x_scale should be a subsample of the shape of input |
3131
| Dropout | ai.onnx(7-9, 10-11, 12, 13-21, 22+) | identity ||| Only supports test mode |
3232
| Einsum | ai.onnx(12+) | reshape, transpose, matmul, reduceSum, mul, triangular ||| |
3333
| Elu | ai.onnx(7+) | elu ||| WebNN CPU backend only supports 'alpha' value is 1.0 |
@@ -71,7 +71,7 @@ operators and the supported opset domain/versions in **WebNN EP** by ONNX Runtim
7171
| Pad | ai.onnx(7-10, 11-12, 13-17, 18, 19-20, 21+) | pad ||| modes == 'wrap' is not supported |
7272
| Pow | ai.onnx(7-11, 12, 13-14, 15+) | pow ||| |
7373
| PRelu | ai.onnx(7-8, 9-15, 16+) | prelu ||| WebNN CPU backend restricts the last dimension of input and slope to be same (Chromium issue: https://issues.chromium.org/issues/335517470) |
74-
| QuantizeLinear | ai.onnx(10-12, 13-18, 19-20, 21-22, 23+) | quantizeLinear | || |
74+
| QuantizeLinear | ai.onnx(10-12, 13-18, 19-20, 21-22, 23+) | quantizeLinear | || The shape of x_scale should be a subsample of the shape of input |
7575
| Reciprocal | ai.onnx(7-12, 13+) | reciprocal ||| |
7676
| ReduceL1 | ai.onnx(7-10, 11-12, 13-17, 18+) | reduceL1 ||| Input 'axes' if present should be a constant |
7777
| ReduceL2 | ai.onnx(7-10, 11-12, 13-17, 18+) | reduceL2 ||| Input 'axes' if present should be a constant |

onnxruntime/core/providers/webnn/builders/impl/qdq_op_builder.cc

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ class QDQOpBuilder : public BaseOpBuilder {
2222
const logging::Logger& logger) const override ORT_MUST_USE_RESULT;
2323

2424
// Operator support related.
25+
bool IsOpSupportedImpl(const InitializedTensorSet& /* initializers */, const Node& node,
26+
const WebnnDeviceType /* device_type */, const logging::Logger& logger) const override;
2527
bool HasSupportedInputsImpl(const InitializedTensorSet& /* initializers */, const Node& node,
2628
const emscripten::val& wnn_limits, const logging::Logger& logger) const override;
2729
};
@@ -118,6 +120,38 @@ Status QDQOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder,
118120
return Status::OK();
119121
}
120122

123+
// Operator support related.
124+
bool QDQOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& /* initializers */,
125+
const Node& node,
126+
const WebnnDeviceType /* device_type */,
127+
const logging::Logger& logger) const {
128+
const auto& input_defs = node.InputDefs();
129+
130+
std::vector<int64_t> input_shape;
131+
std::vector<int64_t> scale_shape;
132+
133+
if (!GetShape(*input_defs[0], input_shape, logger) || !GetShape(*input_defs[1], scale_shape, logger)) {
134+
return false;
135+
}
136+
137+
// WebNN requires the scale_shape to be a subsample of the input_shape.
138+
if (scale_shape.size() > input_shape.size()) {
139+
LOGS(logger, VERBOSE) << "The rank of scale is larger than the rank of input";
140+
return false;
141+
}
142+
143+
for (size_t i = 0; i < scale_shape.size(); ++i) {
144+
auto scale_dim = scale_shape[scale_shape.size() - i - 1];
145+
auto input_dim = input_shape[input_shape.size() - i - 1];
146+
if (input_dim % scale_dim != 0) {
147+
LOGS(logger, VERBOSE) << "The shape of scale is not a subsample of the shape of input";
148+
return false;
149+
}
150+
}
151+
152+
return true;
153+
}
154+
121155
bool QDQOpBuilder::HasSupportedInputsImpl(const InitializedTensorSet& /* initializers */, const Node& node,
122156
const emscripten::val& wnn_limits, const logging::Logger& logger) const {
123157
const auto& input_defs = node.InputDefs();

0 commit comments

Comments
 (0)