-
Notifications
You must be signed in to change notification settings - Fork 48
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
Support for LocalResponseNormalization (LRN) operation #228
Comments
I prototyped Known Models
BehaviorLocal response normalization produces an output the same size as the input, using a sliding window where each output element equals the corresponding input element divided by an adjusted averaged window around it. The shape of that sliding window can vary in size and rank, along a single axis or more. Although not obvious at first, the operator is really a variation of pooling, with the general form: function localResponseNormalization(input, axes, windowLength, scale, bias, exponent)
{
let leadingPadding = floor((windowLength - 1) / 2); // Center halfway around sliding window
let trailingPadding = ceil((windowLength - 1) / 2); // Center halfway around sliding window
let padding = new Array(axes.size() * 2).fill([leadingPadding, trailingPadding]).flat();
// 1D padding = [leadingPadding, trailingPadding]
// 2D padding = [leadingPadding, trailingPadding, leadingPadding, trailingPadding]
// 3D padding = [leadingPadding, trailingPadding, leadingPadding, trailingPadding, ...]
let windowDimensions = new Array(axes.size()).fill([windowLength]).flat();
// 1D windowDimensions = [windowLength]
// 2D windowDimensions = [windowLength, windowLength]
// 3D windowDimensions = [windowLength, windowLength, windowLength]
let regionAverages = averagePoolND(pow(input, 2), axes, windowsDimensions, padding);
output = input / pow((regionAverages * scale + bias), exponent);
} Where function averagePoolND(input, axes, ...)
{
// e.g. Given input rank=4 and axes=[1], returns [0,2,3,1].
// Given input rank=3 and axes=[0,1], returns [2,0,1].
let permutation = GetPermutationToRightmostAxes(input.rank, axes);
let inversePermutation = GetInversePermutation(axes);
let poolingOperator;
switch (axes.size())
{
case 1: poolingOperator = averagePool1D; break;
case 1: poolingOperator = averagePool2D; break;
default: throw ... // Unsupported axis count
}
return transpose(poolingOperator(transpose(input, permutation), inversePermutation);
} Note if you only have averagePool2D to work with (WebNN lacks an averagePool1D), then you can just set the padding for the first dimension to [0,0,*,*] and windowDimensions = [1,*]. ImplementationsImplementations consistently:
Implementations differ in:
CoreML (1D normalization)
TensorFlow (1D normalization)
PyTorch or Caffe or NCNN or ONNX (1D normalization)
DirectML (1D normalization)
Caffe and NCNN (2D normalization)
DirectML (2D normalization)
Possible IDLpartial interface MLGraphBuilder {
...
MLOperand batchNormalization(MLOperand input, MLOperand mean, MLOperand variance, optional MLBatchNormalizationOptions options = {});
MLOperand instanceNormalization(MLOperand input, optional MLInstanceNormalizationOptions options = {});
MLOperand layerNormalization(MLOperand input, optional MLLayerNormalizationOptions options = {});
+ MLOperand localResponseNormalization(MLOperand input, optional MLLocalResponseNormalizationOptions options = {});
...
}; +dictionary MLLocalResponseNormalizationOptions {
+ sequence<unsigned long> axes;
+ unsigned long windowLength; // 1 up to input size or more
+ float scale = 1.0; // Sometimes labeled alpha.
+ float bias = 1.0; // Sometimes labeled k
+ float exponent = 0.5; // Sometimes labeled beta.
+}; Data Types
|
@fdwr mentioned in #375 (comment)
+1 There is an example of LRN decomposition in torch: https://pytorch.org/docs/stable/_modules/torch/nn/functional.html#local_response_norm Please note the decomposition requires the However, the default behavior of WebNN averagePool2d doesn't count the padding elements, as Chromium prototype:
To support LRN decomposition, WebNN averagePool2d may need to support dictionary MLAveragePool2dOptions : MLPool2dOptions {
// Indicates whether to include the zero-padding in the averaging calculation.
boolean includePadding = false;
};
MLOperand averagePool2d(MLOperand input, optional MLAveragePool2dOptions options = {}) Any thoughts? |
Excluding padding in the averaging window ( @Honry's ONNX decomposition should work by adding a |
Great idea!
AFAIK, TFLite
Agreed, it could be handled by framework. |
SGTM. Let's close this issue as not planned? |
Using decomposition in higher layers (e.g. ORT's WebNN EP) for localResponseNormalization rather than a dedicated WebNN operator due to the rarity of the operator in models and the awkward backend differences. |
Hi all,
I'm a student intern working on GSoC 2021 project "OpenCV.js: Accelerate OpenCV.js DNN via WebNN" (opencv/opencv#20406). Here is the proposal. In this project, I will improve the performance of OpenCV.js DNN Module using WebNN.
Here is a brief result of the improvements:
However, I found that there is a performance gap of GoogleNet between OpenCV OpenVINO and OpenCV WebNN, while this gap doesn't exist for SqueezeNet. This is mainly because LRN layer is not supported by WebNN. Thus, the GoogleNet is divided into four parts, which slows down the inference speed. After a further investigation, I found that both ONNX (link) and TFLite (link) support LRN and both GoogleNet and AlexNet need LRN layer. Thus, I think it is useful for WebNN to support this frequently used LRN op.
The text was updated successfully, but these errors were encountered: