From b23f950bc9de10a202689c2ddece050b4d1d6dae Mon Sep 17 00:00:00 2001 From: Wanming Lin Date: Thu, 21 Sep 2023 15:54:13 +0800 Subject: [PATCH] Enable WebNN GPU backend --- common/component/component.js | 7 ------- face_recognition/main.js | 2 +- facial_landmark_detection/main.js | 2 +- .../ssd_mobilenetv2_face_nchw.js | 14 ++++++++++++-- .../ssd_mobilenetv2_face_nhwc.js | 14 ++++++++++++-- image_classification/main.js | 2 +- image_classification/mobilenet_nchw.js | 16 ++++++++++++---- image_classification/mobilenet_nhwc.js | 16 ++++++++++++---- lenet/main.js | 2 +- nsnet2/denoiser.js | 2 +- object_detection/main.js | 2 +- object_detection/ssd_mobilenetv1_nchw.js | 14 ++++++++++++-- object_detection/ssd_mobilenetv1_nhwc.js | 14 ++++++++++++-- rnnoise/main.js | 2 +- semantic_segmentation/deeplabv3_mnv2_nchw.js | 14 ++++++++++++-- semantic_segmentation/deeplabv3_mnv2_nhwc.js | 16 ++++++++++++---- semantic_segmentation/main.js | 2 +- style_transfer/main.js | 2 +- 18 files changed, 105 insertions(+), 38 deletions(-) diff --git a/common/component/component.js b/common/component/component.js index 717281bd..e8861ec3 100644 --- a/common/component/component.js +++ b/common/component/component.js @@ -590,13 +590,6 @@ $(document).ready(async () => { "title", "WebNN is supported, disable WebNN Polyfill." ); - $('label:contains("WebNN (GPU)")').addClass("disabled"); - $('label:contains("WebNN (GPU)")').addClass("btn-outline-secondary"); - $('label:contains("WebNN (GPU)")').removeClass("btn-outline-info"); - $('label:contains("WebNN (GPU)")').attr( - "title", - "WebNN GPU backend is not supported." - ); } } $("#webnnstatus").html("supported").addClass("webnn-status-true"); diff --git a/face_recognition/main.js b/face_recognition/main.js index f0a53ac3..04d497cc 100644 --- a/face_recognition/main.js +++ b/face_recognition/main.js @@ -310,7 +310,7 @@ async function main() { // UI shows model loading progress await ui.showProgressComponent('current', 'pending', 'pending'); console.log('- Loading weights... '); - const contextOptions = {deviceType}; + const contextOptions = {'devicePreference': deviceType}; if (powerPreference) { contextOptions['powerPreference'] = powerPreference; } diff --git a/facial_landmark_detection/main.js b/facial_landmark_detection/main.js index eb9e3268..3e801bf2 100644 --- a/facial_landmark_detection/main.js +++ b/facial_landmark_detection/main.js @@ -245,7 +245,7 @@ async function main() { // UI shows model loading progress await ui.showProgressComponent('current', 'pending', 'pending'); console.log('- Loading weights... '); - const contextOptions = {deviceType}; + const contextOptions = {'devicePreference': deviceType}; if (powerPreference) { contextOptions['powerPreference'] = powerPreference; } diff --git a/facial_landmark_detection/ssd_mobilenetv2_face_nchw.js b/facial_landmark_detection/ssd_mobilenetv2_face_nchw.js index a92db945..7b7ef186 100644 --- a/facial_landmark_detection/ssd_mobilenetv2_face_nchw.js +++ b/facial_landmark_detection/ssd_mobilenetv2_face_nchw.js @@ -6,6 +6,7 @@ import {buildConstantByNpy} from '../common/utils.js'; export class SsdMobilenetV2FaceNchw { constructor() { this.context_ = null; + this.devicePreference_ = null; this.builder_ = null; this.graph_ = null; this.weightsUrl_ = '../test-data/models/ssd_mobilenetv2_face_nchw/weights/'; @@ -74,8 +75,16 @@ ${nameArray[1]}`; } options.bias = bias; if (clip) { - // implement `clip` by `clamp` of WebNN API - options.activation = this.builder_.clamp({minValue: 0, maxValue: 6}); + // TODO: Set clamp activation to options once it's supported in + // WebNN DML backend. + // Implement `clip` by `clamp` of WebNN API + if (this.devicePreference_ == 'gpu') { + return this.builder_.clamp( + this.builder_.conv2d(input, weights, options), + {minValue: 0, maxValue: 6}); + } else { + options.activation = this.builder_.clamp({minValue: 0, maxValue: 6}); + } } return this.builder_.conv2d(input, weights, options); } @@ -110,6 +119,7 @@ ${nameArray[1]}`; async load(contextOptions) { this.context_ = await navigator.ml.createContext(contextOptions); + this.devicePreference_ = contextOptions.devicePreference; this.builder_ = new MLGraphBuilder(this.context_); const input = this.builder_.input('input', {type: 'float32', dimensions: this.inputOptions.inputDimensions}); diff --git a/facial_landmark_detection/ssd_mobilenetv2_face_nhwc.js b/facial_landmark_detection/ssd_mobilenetv2_face_nhwc.js index 8006eecd..3e9ffd61 100644 --- a/facial_landmark_detection/ssd_mobilenetv2_face_nhwc.js +++ b/facial_landmark_detection/ssd_mobilenetv2_face_nhwc.js @@ -6,6 +6,7 @@ import {buildConstantByNpy} from '../common/utils.js'; export class SsdMobilenetV2FaceNhwc { constructor() { this.context_ = null; + this.devicePreference_ = null; this.builder_ = null; this.graph_ = null; this.weightsUrl_ = '../test-data/models/ssd_mobilenetv2_face_nhwc/weights/'; @@ -81,8 +82,16 @@ ${nameArray[1]}`; } options.bias = bias; if (relu6) { - // implement `relu6` by `clamp` of WebNN API - options.activation = this.builder_.clamp({minValue: 0, maxValue: 6}); + // TODO: Set clamp activation to options once it's supported in + // WebNN DML backend. + // Implement `clip` by `clamp` of WebNN API + if (this.devicePreference_ == 'gpu') { + return this.builder_.clamp( + this.builder_.conv2d(input, weights, options), + {minValue: 0, maxValue: 6}); + } else { + options.activation = this.builder_.clamp({minValue: 0, maxValue: 6}); + } } return this.builder_.conv2d(input, weights, options); } @@ -117,6 +126,7 @@ ${nameArray[1]}`; async load(contextOptions) { this.context_ = await navigator.ml.createContext(contextOptions); + this.devicePreference_ = contextOptions.devicePreference; this.builder_ = new MLGraphBuilder(this.context_); const input = this.builder_.input('input', {type: 'float32', dimensions: this.inputOptions.inputDimensions}); diff --git a/image_classification/main.js b/image_classification/main.js index 93589c9a..2a65b3ec 100644 --- a/image_classification/main.js +++ b/image_classification/main.js @@ -231,7 +231,7 @@ async function main() { // UI shows model loading progress await ui.showProgressComponent('current', 'pending', 'pending'); console.log('- Loading weights... '); - const contextOptions = {deviceType}; + const contextOptions = {'devicePreference': deviceType}; if (powerPreference) { contextOptions['powerPreference'] = powerPreference; } diff --git a/image_classification/mobilenet_nchw.js b/image_classification/mobilenet_nchw.js index 530a1d76..19e682d5 100644 --- a/image_classification/mobilenet_nchw.js +++ b/image_classification/mobilenet_nchw.js @@ -6,6 +6,7 @@ import {buildConstantByNpy} from '../common/utils.js'; export class MobileNetV2Nchw { constructor() { this.context_ = null; + this.devicePreference_ = null; this.builder_ = null; this.graph_ = null; this.weightsUrl_ = '../test-data/models/mobilenetv2_nchw/weights/'; @@ -30,10 +31,16 @@ export class MobileNetV2Nchw { await buildConstantByNpy(this.builder_, biasName); options.bias = bias; if (relu6) { - // implement `relu6` by `clamp` of WebNN API - options.activation = this.builder_.clamp({minValue: 0, maxValue: 6}); - } else { - options.activation = undefined; + // TODO: Set clamp activation to options once it's supported in + // WebNN DML backend. + // Implement `clip` by `clamp` of WebNN API + if (this.devicePreference_ == 'gpu') { + return this.builder_.clamp( + this.builder_.conv2d(input, weights, options), + {minValue: 0, maxValue: 6}); + } else { + options.activation = this.builder_.clamp({minValue: 0, maxValue: 6}); + } } return this.builder_.conv2d(input, weights, options); } @@ -69,6 +76,7 @@ export class MobileNetV2Nchw { async load(contextOptions) { this.context_ = await navigator.ml.createContext(contextOptions); + this.devicePreference_ = contextOptions.devicePreference; this.builder_ = new MLGraphBuilder(this.context_); const data = this.builder_.input('input', {type: 'float32', dimensions: this.inputOptions.inputDimensions}); diff --git a/image_classification/mobilenet_nhwc.js b/image_classification/mobilenet_nhwc.js index de1dc4e6..24391bc8 100644 --- a/image_classification/mobilenet_nhwc.js +++ b/image_classification/mobilenet_nhwc.js @@ -8,6 +8,7 @@ import {buildConstantByNpy} from '../common/utils.js'; export class MobileNetV2Nhwc { constructor() { this.context_ = null; + this.devicePreference_ = null; this.builder_ = null; this.graph_ = null; this.weightsUrl_ = '../test-data/models/mobilenetv2_nhwc/weights/'; @@ -29,10 +30,16 @@ export class MobileNetV2Nhwc { options.inputLayout = 'nhwc'; options.bias = bias; if (relu6) { - // `relu6` in TFLite equals to `clamp` in WebNN API - options.activation = this.builder_.clamp({minValue: 0, maxValue: 6}); - } else { - options.activation = undefined; + // TODO: Set clamp activation to options once it's supported in + // WebNN DML backend. + // Implement `clip` by `clamp` of WebNN API + if (this.devicePreference_ == 'gpu') { + return this.builder_.clamp( + this.builder_.conv2d(input, weights, options), + {minValue: 0, maxValue: 6}); + } else { + options.activation = this.builder_.clamp({minValue: 0, maxValue: 6}); + } } return this.builder_.conv2d(input, weights, options); } @@ -60,6 +67,7 @@ export class MobileNetV2Nhwc { async load(contextOptions) { this.context_ = await navigator.ml.createContext(contextOptions); + this.devicePreference_ = contextOptions.devicePreference; this.builder_ = new MLGraphBuilder(this.context_); const strides = [2, 2]; const autoPad = 'same-upper'; diff --git a/lenet/main.js b/lenet/main.js index 8d9658de..4521a13d 100644 --- a/lenet/main.js +++ b/lenet/main.js @@ -68,7 +68,7 @@ async function main() { const lenet = new LeNet(weightUrl); const [numRuns, powerPreference, numThreads] = utils.getUrlParams(); try { - const contextOptions = {deviceType}; + const contextOptions = {'devicePreference': deviceType}; if (powerPreference) { contextOptions['powerPreference'] = powerPreference; } diff --git a/nsnet2/denoiser.js b/nsnet2/denoiser.js index 6d89650e..8729112b 100644 --- a/nsnet2/denoiser.js +++ b/nsnet2/denoiser.js @@ -31,7 +31,7 @@ export class Denoiser { const start = performance.now(); const weightsUrl = '../test-data/models/nsnet2/weights/'; const powerPreference = getUrlParams()[1]; - const contextOptions = {deviceType}; + const contextOptions = {'devicePreference': deviceType}; if (powerPreference) { contextOptions['powerPreference'] = powerPreference; } diff --git a/object_detection/main.js b/object_detection/main.js index 2b3a466a..460d9412 100644 --- a/object_detection/main.js +++ b/object_detection/main.js @@ -219,7 +219,7 @@ async function main() { // UI shows model loading progress await ui.showProgressComponent('current', 'pending', 'pending'); console.log('- Loading weights... '); - const contextOptions = {deviceType}; + const contextOptions = {'devicePreference': deviceType}; if (powerPreference) { contextOptions['powerPreference'] = powerPreference; } diff --git a/object_detection/ssd_mobilenetv1_nchw.js b/object_detection/ssd_mobilenetv1_nchw.js index 0f2adbbb..daae42da 100644 --- a/object_detection/ssd_mobilenetv1_nchw.js +++ b/object_detection/ssd_mobilenetv1_nchw.js @@ -6,6 +6,7 @@ import {buildConstantByNpy} from '../common/utils.js'; export class SsdMobilenetV1Nchw { constructor() { this.context_ = null; + this.devicePreference_ = null; this.model_ = null; this.builder_ = null; this.graph_ = null; @@ -60,14 +61,23 @@ ${nameArray[1]}_BatchNorm_batchnorm`; options.autoPad = 'same-upper'; options.bias = bias; if (relu6) { - // implement `relu6` by `clamp` of WebNN API - options.activation = this.builder_.clamp({minValue: 0, maxValue: 6}); + // TODO: Set clamp activation to options once it's supported in + // WebNN DML backend. + // Implement `clip` by `clamp` of WebNN API + if (this.devicePreference_ == 'gpu') { + return this.builder_.clamp( + this.builder_.conv2d(input, weights, options), + {minValue: 0, maxValue: 6}); + } else { + options.activation = this.builder_.clamp({minValue: 0, maxValue: 6}); + } } return this.builder_.conv2d(input, weights, options); } async load(contextOptions) { this.context_ = await navigator.ml.createContext(contextOptions); + this.devicePreference_ = contextOptions.devicePreference; this.builder_ = new MLGraphBuilder(this.context_); const input = this.builder_.input('input', {type: 'float32', dimensions: this.inputOptions.inputDimensions}); diff --git a/object_detection/ssd_mobilenetv1_nhwc.js b/object_detection/ssd_mobilenetv1_nhwc.js index 66b0a48e..3de7a0f8 100644 --- a/object_detection/ssd_mobilenetv1_nhwc.js +++ b/object_detection/ssd_mobilenetv1_nhwc.js @@ -6,6 +6,7 @@ import {buildConstantByNpy} from '../common/utils.js'; export class SsdMobilenetV1Nhwc { constructor() { this.context_ = null; + this.devicePreference_ = null; this.model_ = null; this.builder_ = null; this.graph_ = null; @@ -71,14 +72,23 @@ ${nameArray[1]}_BatchNorm_batchnorm`; } options.bias = bias; if (relu6) { - // implement `relu6` by `clamp` of WebNN API - options.activation = this.builder_.clamp({minValue: 0, maxValue: 6}); + // TODO: Set clamp activation to options once it's supported in + // WebNN DML backend. + // Implement `clip` by `clamp` of WebNN API + if (this.devicePreference_ == 'gpu') { + return this.builder_.clamp( + this.builder_.conv2d(input, weights, options), + {minValue: 0, maxValue: 6}); + } else { + options.activation = this.builder_.clamp({minValue: 0, maxValue: 6}); + } } return this.builder_.conv2d(input, weights, options); } async load(contextOptions) { this.context_ = await navigator.ml.createContext(contextOptions); + this.devicePreference_ = contextOptions.devicePreference; this.builder_ = new MLGraphBuilder(this.context_); const input = this.builder_.input('input', {type: 'float32', dimensions: this.inputOptions.inputDimensions}); diff --git a/rnnoise/main.js b/rnnoise/main.js index c805fe50..9b497ab1 100644 --- a/rnnoise/main.js +++ b/rnnoise/main.js @@ -220,7 +220,7 @@ export async function main() { `[${batchSize} (batch_size) x 100 (frames) x 42].`, true); await log(modelInfo, '- Loading model...'); const powerPreference = utils.getUrlParams()[1]; - const contextOptions = {deviceType}; + const contextOptions = {'devicePreference': deviceType}; if (powerPreference) { contextOptions['powerPreference'] = powerPreference; } diff --git a/semantic_segmentation/deeplabv3_mnv2_nchw.js b/semantic_segmentation/deeplabv3_mnv2_nchw.js index 571a5a6f..226bba37 100644 --- a/semantic_segmentation/deeplabv3_mnv2_nchw.js +++ b/semantic_segmentation/deeplabv3_mnv2_nchw.js @@ -8,6 +8,7 @@ import {buildConstantByNpy} from '../common/utils.js'; export class DeepLabV3MNV2Nchw { constructor() { this.context_ = null; + this.devicePreference_ = null; this.builder_ = null; this.graph_ = null; this.weightsUrl_ = '../test-data/models/deeplabv3_mnv2_nchw/weights/'; @@ -43,8 +44,16 @@ export class DeepLabV3MNV2Nchw { options.bias = bias; if (activation === 'relu6') { - // implement `relu6` by `clamp` of WebNN API - options.activation = this.builder_.clamp({minValue: 0, maxValue: 6}); + // TODO: Set clamp activation to options once it's supported in + // WebNN DML backend. + // Implement `clip` by `clamp` of WebNN API + if (this.devicePreference_ == 'gpu') { + return this.builder_.clamp( + this.builder_.conv2d(input, weights, options), + {minValue: 0, maxValue: 6}); + } else { + options.activation = this.builder_.clamp({minValue: 0, maxValue: 6}); + } } else if (activation === 'relu') { options.activation = this.builder_.relu(); } else { @@ -82,6 +91,7 @@ export class DeepLabV3MNV2Nchw { async load(contextOptions) { this.context_ = await navigator.ml.createContext(contextOptions); + this.devicePreference_ = contextOptions.devicePreference; this.builder_ = new MLGraphBuilder(this.context_); const strides = [2, 2]; diff --git a/semantic_segmentation/deeplabv3_mnv2_nhwc.js b/semantic_segmentation/deeplabv3_mnv2_nhwc.js index 2f587d52..d952051c 100644 --- a/semantic_segmentation/deeplabv3_mnv2_nhwc.js +++ b/semantic_segmentation/deeplabv3_mnv2_nhwc.js @@ -6,6 +6,7 @@ import {buildConstantByNpy} from '../common/utils.js'; export class DeepLabV3MNV2Nhwc { constructor() { this.context_ = null; + this.devicePreference_ = null; this.builder_ = null; this.graph_ = null; this.weightsUrl_ = '../test-data/models/deeplabv3_mnv2_nhwc/weights/'; @@ -43,10 +44,16 @@ export class DeepLabV3MNV2Nhwc { } options.bias = bias; if (relu6) { - // `relu6` in TFLite equals to `clamp` in WebNN API - options.activation = this.builder_.clamp({minValue: 0, maxValue: 6}); - } else { - options.activation = undefined; + // TODO: Set clamp activation to options once it's supported in + // WebNN DML backend. + // Implement `clip` by `clamp` of WebNN API + if (this.devicePreference_ == 'gpu') { + return this.builder_.clamp( + this.builder_.conv2d(input, weights, options), + {minValue: 0, maxValue: 6}); + } else { + options.activation = this.builder_.clamp({minValue: 0, maxValue: 6}); + } } return this.builder_.conv2d(input, weights, options); } @@ -73,6 +80,7 @@ export class DeepLabV3MNV2Nhwc { async load(contextOptions) { this.context_ = await navigator.ml.createContext(contextOptions); + this.devicePreference_ = contextOptions.devicePreference; this.builder_ = new MLGraphBuilder(this.context_); const strides = [2, 2]; const input = this.builder_.input('input', diff --git a/semantic_segmentation/main.js b/semantic_segmentation/main.js index 14338f9c..3bde23fb 100644 --- a/semantic_segmentation/main.js +++ b/semantic_segmentation/main.js @@ -324,7 +324,7 @@ export async function main() { // UI shows model loading progress await ui.showProgressComponent('current', 'pending', 'pending'); console.log('- Loading weights... '); - const contextOptions = {deviceType}; + const contextOptions = {'devicePreference': deviceType}; if (powerPreference) { contextOptions['powerPreference'] = powerPreference; } diff --git a/style_transfer/main.js b/style_transfer/main.js index 25dde460..a01a69a7 100644 --- a/style_transfer/main.js +++ b/style_transfer/main.js @@ -217,7 +217,7 @@ export async function main() { // UI shows model loading progress await ui.showProgressComponent('current', 'pending', 'pending'); console.log('- Loading weights... '); - const contextOptions = {deviceType}; + const contextOptions = {'devicePreference': deviceType}; if (powerPreference) { contextOptions['powerPreference'] = powerPreference; }