Skip to content
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

Implement resample2d #33

Merged
merged 2 commits into from
Mar 13, 2023
Merged

Conversation

BruceDai
Copy link
Contributor

@BruceDai BruceDai commented Mar 7, 2023

@fdwr Thanks much for explanations of resample2d by nearest-neighbor and linear modes on #18 and webmachinelearning/webnn#358, this PR is to sample pixel centers to align with WebNN-Polyfill and Chromium WebNN implementation.

@fdwr @huningxin @lisa0314 PTAL, thanks.

* @return {Tensor}
*/
function nearestNeighbor(src, dst, dstHeight, dstWidth, scaleHeight, scaleWidth) {
const [srcBatches, srcHeight, srcWidth, srcChannels] = src.shape;
Copy link

@fdwr fdwr Mar 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curiously your for loop below is NHWC, but the shape is NCHW. It would be slightly more performant to change the shape to NCHW like the loop, so they have consistent memory access patterns, not that perf really matters in the test code, but it is nice to have the two coherent when reading it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Now I let them be the same of NCHW, thanks.

ih = 0;
} else if (ih > srcHeight - 1) {
ih = srcHeight - 1;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about just ih = Math.max(Math.min(ih, srcHeight - 1), 0);?

I'd say just use Math.clamp(), but it seems the Math.clamp proposal never entered Javascript .

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated, thanks.

scales = [1.0, 1.0],
sizes,
axes = [2, 3],
} = {}) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(minor) It's kinda hard to follow these } = {}) {. Line break somewhere in there to aid readability?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated, thanks.

} = {}) {
if (axes[0] === 0) {
// hwnc -> nhwc
input = transpose(input, {permutation: [2, 0, 1, 3]});
Copy link

@fdwr fdwr Mar 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be more robust/clearer to verify that axes[0] == 0 && axes[1] == 1, rather than just axes[0] == 0.

I'd also add an else for axes[0] == 1 && axes[1] == 2 for clarity, even if it is just a comment that says "NHWC -> NHWC Do nothing". That way it's apparent to readers that you didn't leave it out.

Copy link

@fdwr fdwr Mar 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw, I had this clever idea that you could just say a single line...

input = transpose(permutation: [(axes[0] + 2) & 3, (axes[1] + 2) & 3, axes[0], axes[1]]);

...instead of explicit switch case statements for each transpose, and it works nicely and generically for the first transpose (it assigns the last 2 dimensions H and W to read from whatever axes were given, and it assigns the batch and channel to the other two axes). All the standard axes work:

axes = [0,1] -> permute:[2, 3, 0, 1]
axes = [1,2] -> permute:[3, 0, 1, 2]
axes = [2,3] -> permute:[0, 1, 2, 3]

And it works for the mirrors of the standard ones:

axes = [1,0] -> permute:[3, 2, 1, 0]
axes = [2,1] -> permute:[0, 3, 2, 1]
axes = [3,2] -> permute:[1, 0, 3, 2]

And even works for HNCW (which isn't valid per the spec anyway):

axes = [0,3] -> permute:[2, 1, 0, 3]

But it's not clear how to untranspose that (the 2nd transpose). So, I'm not proposing that idea... 🤔🤷‍♂️

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

input = transpose(permutation: [(axes[0] + 2) & 3, (axes[1] + 2) & 3, axes[0], axes[1]]);'

Thanks, this code is fairly concise.
For more easily understand, I modified previous switch case statements following your first comment.

data: [
1, 1, 1, 2, 2, 2, 1, 1, 1, 2, 2, 2,
3, 3, 3, 4, 4, 4, 3, 3, 3, 4, 4, 4,
],
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The output would be clearer if wrapped consistently with the shape. e.g.

      data: [
        1, 1, 1, 2, 2, 2,
        1, 1, 1, 2, 2, 2,
        3, 3, 3, 4, 4, 4,
        3, 3, 3, 4, 4, 4,
      ],

Like you nicely did with the other cases above. e.g.

      shape: [1, 1, 4, 4],
      data: [
        1.0, 1.25, 1.75, 2.0,
        1.5, 1.75, 2.25, 2.5,
        2.5, 2.75, 3.25, 3.5,
        3.0, 3.25, 3.75, 4.0,
      ],

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated test data, thanks.

@BruceDai
Copy link
Contributor Author

BruceDai commented Mar 8, 2023

@fdwr Thanks for your reviewing! I updated commit to address your comments, please take another look, thanks.

Copy link

@fdwr fdwr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:)

@huningxin huningxin merged commit c8128a4 into webmachinelearning:main Mar 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants