-
Notifications
You must be signed in to change notification settings - Fork 8
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
completed l2pool2d #68
Changes from 4 commits
aa60c41
a51db0a
de9785c
de57d69
46ce01c
ef5af87
5d9c2f3
3b3e02a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -143,6 +143,20 @@ export function reduceL1(input, options = {}) { | |
return reduceSum(abs(input), options); | ||
} | ||
|
||
/* The l2 reducer */ | ||
export function l2Reducer(previousValue, currentValue, currentIndex, array) { | ||
if (currentIndex== 1) { | ||
const sumOfSquares = previousValue*previousValue + currentValue * currentValue; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [nit] There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, if this is the first index, wouldn't the previousValue be ignorable? Also, does this work correctly if there is only one element being reduced? Otherwise the final sqrt isn't applied. Let's be sure to include a test case with exactly one element @BruceDai. I would have expected:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In this function, currentIndex starts from 1 by default, and the number with index=0 at the beginning defaults to previousValue. So I consider this situation: if (currentIndex== 1) { E.g. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Huh, why doesn't the first reduced element start at 0? Don't JS arrays start at 0? Btw, just to catch any interesting boundary cases, I'd include one test where the values don't start at 1 (e.g. [1, 2, 3, 4, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
return sumOfSquares; | ||
} else if (currentIndex === array.length - 1) { | ||
const sumOfSquares = previousValue + currentValue * currentValue; | ||
return Math.sqrt(sumOfSquares); | ||
} else { | ||
const sumOfSquares = previousValue + currentValue * currentValue; | ||
return sumOfSquares; | ||
} | ||
} | ||
|
||
/** | ||
* Compute the L2 norm of all the input values along the axes. | ||
* @param {Tensor} input | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,264 @@ | ||
'use strict'; | ||
import {l2Pool2d} from '../src/pool2d.js'; | ||
import {Tensor} from '../src/lib/tensor.js'; | ||
|
||
import * as utils from './utils.js'; | ||
|
||
describe('test pool2d', function() { | ||
it('l2Pool2d default', function() { | ||
const x = new Tensor([1, 1, 4, 4], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); | ||
const windowDimensions = [3, 3]; | ||
const y = l2Pool2d(x, {windowDimensions}); | ||
utils.checkShape(y, [1, 1, 2, 2]); | ||
utils.checkValue(y, [ | ||
20.639767440550294, | ||
23.302360395462088, | ||
31.654383582688826, | ||
34.51086785347479, | ||
]); | ||
}); | ||
|
||
it('l2Pool2d nhwc', function() { | ||
const x = new Tensor([1, 4, 4, 1], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); | ||
const windowDimensions = [3, 3]; | ||
const layout = 'nhwc'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (update) I found a use case for normalizing across batches and spatial dimensions - this may inform future decisions: https://ieeexplore.ieee.org/document/8648206 🤔 I've often wondered why pooling in ML doesn't have variations that can pool across batches or channel dimensions too, not only the "spatial" dimensions. e.g. One could generalize pooling consistently like reduction (which can apply from all dimensions to none), and pass windowDimensions of rank 4 with *No action expected - just musing after noticing this. |
||
const y = l2Pool2d(x, {windowDimensions, layout}); | ||
utils.checkShape(y, [1, 2, 2, 1]); | ||
utils.checkValue(y, [ | ||
20.639767440550294, | ||
23.302360395462088, | ||
31.654383582688826, | ||
34.51086785347479, | ||
]); | ||
}); | ||
|
||
it('l2Pool2d dilations default', function() { | ||
const x = new Tensor([1, 1, 4, 4], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); | ||
const windowDimensions = [2, 2]; | ||
const dilations = [2, 2]; | ||
const y = l2Pool2d(x, {windowDimensions, dilations}); | ||
utils.checkShape(y, [1, 1, 2, 2]); | ||
utils.checkValue(y, [ | ||
14.560219778561036, | ||
16.24807680927192, | ||
21.633307652783937, | ||
23.49468024894146, | ||
]); | ||
}); | ||
|
||
it('l2Pool2d dilations nhwc', function() { | ||
const x = new Tensor([1, 4, 4, 1], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); | ||
const windowDimensions = [2, 2]; | ||
const dilations = [2, 2]; | ||
const layout = 'nhwc'; | ||
const y = l2Pool2d(x, {windowDimensions, dilations, layout}); | ||
utils.checkShape(y, [1, 2, 2, 1]); | ||
utils.checkValue(y, [ | ||
14.560219778561036, | ||
16.24807680927192, | ||
21.633307652783937, | ||
23.49468024894146, | ||
]); | ||
}); | ||
|
||
it('l2Pool2d pads default', function() { | ||
const x = new Tensor([1, 1, 5, 5], [ | ||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, | ||
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, | ||
]); | ||
const windowDimensions = [5, 5]; | ||
const padding = [2, 2, 2, 2]; | ||
const y = l2Pool2d(x, {windowDimensions, padding}); | ||
utils.checkShape(y, [1, 1, 5, 5]); | ||
const expected = [ | ||
24.43358344574123, 29.832867780352597, | ||
35.21363372331802, 32.89376840679705, | ||
29.748949561287034, 38.28837943815329, | ||
46.04345773288535, 53.5723809439155, | ||
49.558046773455466, 44.384682042344295, | ||
54.037024344425184, 64.42049363362563, | ||
74.33034373659252, 68.33739825307956, | ||
60.8276253029822, 53.907327887774215, | ||
64.18722614352485, 73.95944834840239, | ||
67.94115100585212, 60.41522986797286, | ||
52.507142371300304, 62.369864518050704, | ||
71.69379331573968, 65.7419196555744, | ||
58.35237784358063, | ||
]; | ||
utils.checkValue(y, expected); | ||
}); | ||
|
||
it('l2Pool2d pads nhwc', function() { | ||
const x = new Tensor([1, 5, 5, 1], [ | ||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, | ||
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, | ||
]); | ||
const windowDimensions = [5, 5]; | ||
const padding = [2, 2, 2, 2]; | ||
const layout = 'nhwc'; | ||
const y = l2Pool2d(x, {windowDimensions, padding, layout}); | ||
utils.checkShape(y, [1, 5, 5, 1]); | ||
const expected = [ | ||
24.43358344574123, 29.832867780352597, | ||
35.21363372331802, 32.89376840679705, | ||
29.748949561287034, 38.28837943815329, | ||
46.04345773288535, 53.5723809439155, | ||
49.558046773455466, 44.384682042344295, | ||
54.037024344425184, 64.42049363362563, | ||
74.33034373659252, 68.33739825307956, | ||
60.8276253029822, 53.907327887774215, | ||
64.18722614352485, 73.95944834840239, | ||
67.94115100585212, 60.41522986797286, | ||
52.507142371300304, 62.369864518050704, | ||
71.69379331573968, 65.7419196555744, | ||
58.35237784358063, | ||
]; | ||
utils.checkValue(y, expected); | ||
}); | ||
|
||
it('l2Pool2d autoPad same-upper default', function() { | ||
const x = new Tensor([1, 1, 5, 5], [ | ||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, | ||
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, | ||
]); | ||
const windowDimensions = [5, 5]; | ||
const autoPad = 'same-upper'; | ||
const y = l2Pool2d(x, {windowDimensions, autoPad}); | ||
utils.checkShape(y, [1, 1, 5, 5]); | ||
const expected = [ | ||
24.43358344574123, 29.832867780352597, | ||
35.21363372331802, 32.89376840679705, | ||
29.748949561287034, 38.28837943815329, | ||
46.04345773288535, 53.5723809439155, | ||
49.558046773455466, 44.384682042344295, | ||
54.037024344425184, 64.42049363362563, | ||
74.33034373659252, 68.33739825307956, | ||
60.8276253029822, 53.907327887774215, | ||
64.18722614352485, 73.95944834840239, | ||
67.94115100585212, 60.41522986797286, | ||
52.507142371300304, 62.369864518050704, | ||
71.69379331573968, 65.7419196555744, | ||
58.35237784358063, | ||
]; | ||
utils.checkValue(y, expected); | ||
}); | ||
|
||
it('l2Pool2d autoPad explicit nhwc', function() { | ||
const x = new Tensor([1, 7, 7, 1], [ | ||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, | ||
18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, | ||
35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, | ||
]); | ||
const windowDimensions = [4, 4]; | ||
const padding = [2, 1, 2, 1]; | ||
const strides = [2, 2]; | ||
const autoPad = 'explicit'; | ||
const layout = 'nhwc'; | ||
const y = l2Pool2d( | ||
x, {windowDimensions, autoPad, padding, strides, layout}); | ||
utils.checkShape(y, [1, 4, 4, 1]); | ||
const expected = [ | ||
12.24744871391589, 19.8997487421324, | ||
24.899799195977465, 24.879710609249457, | ||
40.54626986542659, 60.860496218811754, | ||
67.82329983125268, 63.324560795950255, | ||
76.81145747868608, 112.5344391730816, | ||
120.2331069215131, 109.1146186356347, | ||
90.50414355155237, 131.4610208388783, | ||
138.31124321616085, 124.21352583354198, | ||
]; | ||
utils.checkValue(y, expected); | ||
}); | ||
|
||
it('l2Pool2d autoPad same-lower nhwc', function() { | ||
const x = new Tensor([1, 7, 7, 1], [ | ||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, | ||
18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, | ||
35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, | ||
]); | ||
const windowDimensions = [4, 4]; | ||
const strides = [2, 2]; | ||
const autoPad = 'same-lower'; | ||
const layout = 'nhwc'; | ||
const y = | ||
l2Pool2d(x, {windowDimensions, autoPad, strides, layout}); | ||
utils.checkShape(y, [1, 4, 4, 1]); | ||
const expected = [ | ||
12.24744871391589, 19.8997487421324, | ||
24.899799195977465, 24.879710609249457, | ||
40.54626986542659, 60.860496218811754, | ||
67.82329983125268, 63.324560795950255, | ||
76.81145747868608, 112.5344391730816, | ||
120.2331069215131, 109.1146186356347, | ||
90.50414355155237, 131.4610208388783, | ||
138.31124321616085, 124.21352583354198, | ||
]; | ||
utils.checkValue(y, expected); | ||
}); | ||
|
||
it('l2Pool2d autoPad same-upper nhwc', function() { | ||
const x = new Tensor([1, 5, 5, 1], [ | ||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, | ||
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, | ||
]); | ||
const windowDimensions = [5, 5]; | ||
const autoPad = 'same-upper'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, we're still using @huningxin, you opened webmachinelearning/webnn#326 where we talked about removing autoPad from conv2d, and @Honry removed it from the WebNN EP in microsoft/onnxruntime#18688, but for consistency, wouldn't we want to be explicit with pooling too? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, we should be consistent and only do explicit padding for pooling. |
||
const layout = 'nhwc'; | ||
const y = l2Pool2d(x, {windowDimensions, autoPad, layout}); | ||
|
||
utils.checkShape(y, [1, 5, 5, 1]); | ||
const expected = [ | ||
24.43358344574123, 29.832867780352597, | ||
35.21363372331802, 32.89376840679705, | ||
29.748949561287034, 38.28837943815329, | ||
46.04345773288535, 53.5723809439155, | ||
49.558046773455466, 44.384682042344295, | ||
54.037024344425184, 64.42049363362563, | ||
74.33034373659252, 68.33739825307956, | ||
60.8276253029822, 53.907327887774215, | ||
64.18722614352485, 73.95944834840239, | ||
67.94115100585212, 60.41522986797286, | ||
52.507142371300304, 62.369864518050704, | ||
71.69379331573968, 65.7419196555744, | ||
58.35237784358063, | ||
]; | ||
utils.checkValue(y, expected); | ||
}); | ||
|
||
it('l2Pool2d strides default', function() { | ||
const x = new Tensor([1, 1, 5, 5], [ | ||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, | ||
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, | ||
]); | ||
const windowDimensions = [2, 2]; | ||
const strides = [2, 2]; | ||
const y = l2Pool2d(x, {windowDimensions, strides}); | ||
utils.checkShape(y, [1, 1, 2, 2]); | ||
const expected = [ | ||
9.486832980505138, | ||
13.038404810405298, | ||
28.460498941515414, | ||
32.4037034920393, | ||
]; | ||
utils.checkValue(y, expected); | ||
}); | ||
|
||
it('l2Pool2d strides nhwc', function() { | ||
const x = new Tensor([1, 5, 5, 1], [ | ||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, | ||
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, | ||
]); | ||
const windowDimensions = [2, 2]; | ||
const strides = [2, 2]; | ||
const layout = 'nhwc'; | ||
const y = l2Pool2d(x, {windowDimensions, strides, layout}); | ||
utils.checkShape(y, [1, 2, 2, 1]); | ||
const expected = [ | ||
9.486832980505138, | ||
13.038404810405298, | ||
28.460498941515414, | ||
32.4037034920393, | ||
]; | ||
utils.checkValue(y, expected); | ||
}); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shall we add a single element test case here (e.g. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay,I have added a single element test case. |
||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nit]
if (currentIndex == 1) {