[WebNN EP] Support LpPool, GlobalLpPool, and Log ops (#16954)

BTW, reset minimal supported opset to 1, because with minimal supported
opset 7 will ignore all ops that have last since version less than 7.
e.g. GlobalLpPool, it only has two opset versions: 1, 2.
This commit is contained in:
Wanming Lin 2023-08-02 13:35:10 +08:00 committed by GitHub
parent 4a2a248dd7
commit ba49d64f67
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 56 additions and 23 deletions

View file

@ -153,6 +153,7 @@ static const InlinedHashMap<std::string, std::string> op_map = {
{"Gemm", "gemm"},
{"GlobalAveragePool", "averagePool2d"},
{"GlobalMaxPool", "maxPool2d"},
{"GlobalLpPool", "l2Pool2d"},
{"Greater", "greater"},
{"GroupNormalization", "meanVarianceNormalization"},
{"HardSigmoid", "hardSigmoid"},
@ -162,6 +163,8 @@ static const InlinedHashMap<std::string, std::string> op_map = {
{"LayerNormalization", "meanVarianceNormalization"},
{"LeakyRelu", "leakyRelu"},
{"Less", "lesser"},
{"Log", "log"},
{"LpPool", "l2Pool2d"},
{"MatMul", "matmul"},
{"Max", "max"},
{"MaxPool", "maxPool2d"},

View file

@ -100,8 +100,9 @@ bool BaseOpBuilder::HasSupportedOpSet(const Node& node,
const logging::Logger& logger) const {
auto since_version = node.SinceVersion();
if (since_version < GetMinSupportedOpSet(node) || since_version > GetMaxSupportedOpSet(node)) {
LOGS(logger, VERBOSE) << "Current opset is " << since_version << ", "
<< node.OpType() << " is only supported for opset ["
LOGS(logger, VERBOSE) << "Current opset since version of "
<< node.OpType() << " is " << since_version
<< ", WebNN EP only supports for its opset ["
<< GetMinSupportedOpSet(node) << ", "
<< GetMaxSupportedOpSet(node) << "]";
return false;

View file

@ -41,7 +41,11 @@ class BaseOpBuilder : public IOpBuilder {
// ONNX Runtime only *guarantees* support for models stamped
// with opset version 7 or above for opset domain 'ai.onnx'.
virtual int GetMinSupportedOpSet(const Node& /* node */) const { return 7; }
// WebNN EP ignores node support for opset less than 7 by
// default as which will be fallback earlier by ONNX Runtime.
// We still set the mininal supported opset to 1 as we couldn't
// get the model opset version at this stage.
virtual int GetMinSupportedOpSet(const Node& /* node */) const { return 1; }
virtual int GetMaxSupportedOpSet(const Node& /* node */) const { return 19; }
private:

View file

@ -38,15 +38,22 @@ Status PoolOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder,
emscripten::val input = model_builder.GetOperand(input_defs[0]->Name());
bool is_global_pooling = false;
bool is_average_pool = false;
std::string webnn_op_name;
if (op_type == "GlobalAveragePool") {
is_global_pooling = true;
is_average_pool = true;
webnn_op_name = "averagePool2d";
} else if (op_type == "GlobalMaxPool") {
is_global_pooling = true;
webnn_op_name = "maxPool2d";
} else if (op_type == "GlobalLpPool") {
is_global_pooling = true;
webnn_op_name = "l2Pool2d";
} else if (op_type == "AveragePool") {
is_average_pool = true;
webnn_op_name = "averagePool2d";
} else if (op_type == "MaxPool") {
webnn_op_name = "maxPool2d";
} else if (op_type == "LpPool") {
webnn_op_name = "l2Pool2d";
} else {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "PoolOpBuilder, unknown op: ", op_type);
}
@ -97,12 +104,7 @@ Status PoolOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder,
options.set("roundingType", ceil_mode == 0 ? emscripten::val("floor")
: emscripten::val("ceil"));
emscripten::val output = emscripten::val::object();
if (is_average_pool) {
output = model_builder.GetBuilder().call<emscripten::val>("averagePool2d", input, options);
} else {
output = model_builder.GetBuilder().call<emscripten::val>("maxPool2d", input, options);
}
emscripten::val output = model_builder.GetBuilder().call<emscripten::val>(webnn_op_name.c_str(), input, options);
model_builder.AddOperand(node.OutputDefs()[0]->Name(), std::move(output));
return Status::OK();
}
@ -127,21 +129,36 @@ bool PoolOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& /* initializer
return false;
}
if (op_type == "AveragePool" || op_type == "MaxPool") {
NodeAttrHelper helper(node);
const auto storage_order = helper.Get("storage_order", 0);
if (storage_order == 1) {
LOGS(logger, VERBOSE) << "storage_order == 1 is not supported";
return false;
}
NodeAttrHelper helper(node);
if (op_type == "AveragePool" || op_type == "LpPool" || op_type == "MaxPool") {
if (helper.Get("kernel_shape", std::vector<int32_t>{1, 1}).size() != 2) {
LOGS(logger, VERBOSE) << "Only pooling 2d is supported";
return false;
}
}
if (op_type == "AveragePool") {
if (helper.Get("count_include_pad", 0) != 0) {
LOGS(logger, VERBOSE) << "AveragePool only supports count_include_pad == 0";
return false;
}
}
if (op_type == "MaxPool") {
if (helper.Get("storage_order", 0) == 1) {
LOGS(logger, VERBOSE) << "MaxPool storage_order == 1 is not supported";
return false;
}
if (node.OutputDefs().size() != 1) {
LOGS(logger, VERBOSE) << "Argmax in maxpooling is not supported";
LOGS(logger, VERBOSE) << "MaxPool only supports one output";
return false;
}
}
if (op_type == "GlobalLpPool" || op_type == "LpPool") {
// WebNN only supports l2Pool2d.
if (helper.Get("p", 2) != 2) {
LOGS(logger, VERBOSE) << op_type << " only supports p == 2";
return false;
}
}
@ -155,9 +172,11 @@ void CreatePoolOpBuilder(const std::string& op_type, OpBuilderRegistrations& op_
static std::vector<std::string> op_types =
{
"AveragePool",
"GlobalAveragePool",
"GlobalMaxPool",
"AveragePool",
"GlobalLpPool",
"LpPool",
"MaxPool",
};

View file

@ -43,6 +43,8 @@ Status UnaryOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const
output = model_builder.GetBuilder().call<emscripten::val>("floor", input);
} else if (op_type == "Identity") {
output = model_builder.GetBuilder().call<emscripten::val>("identity", input);
} else if (op_type == "Log") {
output = model_builder.GetBuilder().call<emscripten::val>("log", input);
} else if (op_type == "Neg") {
output = model_builder.GetBuilder().call<emscripten::val>("neg", input);
} else if (op_type == "Not") {
@ -77,6 +79,7 @@ void CreateUnaryOpBuilder(const std::string& op_type, OpBuilderRegistrations& op
"Exp",
"Floor",
"Identity",
"Log",
"Neg",
"Not",
"Reciprocal",

View file

@ -23,6 +23,7 @@ static OpBuilderRegistrations CreateOpBuilderRegistrations() {
CreateUnaryOpBuilder("Exp", op_registrations);
CreateUnaryOpBuilder("Floor", op_registrations);
CreateUnaryOpBuilder("Identity", op_registrations);
CreateUnaryOpBuilder("Log", op_registrations);
CreateUnaryOpBuilder("Neg", op_registrations);
CreateUnaryOpBuilder("Not", op_registrations);
CreateUnaryOpBuilder("Reciprocal", op_registrations);
@ -117,9 +118,11 @@ static OpBuilderRegistrations CreateOpBuilderRegistrations() {
}
{ // Pool
CreatePoolOpBuilder("AveragePool", op_registrations);
CreatePoolOpBuilder("GlobalAveragePool", op_registrations);
CreatePoolOpBuilder("GlobalMaxPool", op_registrations);
CreatePoolOpBuilder("AveragePool", op_registrations);
CreatePoolOpBuilder("GlobalLpPool", op_registrations);
CreatePoolOpBuilder("LpPool", op_registrations);
CreatePoolOpBuilder("MaxPool", op_registrations);
}