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

Refactor some models to fetch in parallel #202

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions face_recognition/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -325,16 +325,21 @@ async function main() {
contextOptions['numThreads'] = numThreads;
}
start = performance.now();
const fdOutputOperand = await fdInstance.load(contextOptions);
const frOutputOperand = await frInstance.load(contextOptions);
const [fdOutputOperand, frOutputOperand] = await Promise.all([
fdInstance.load(contextOptions),
frInstance.load(contextOptions),
]);

loadTime = (performance.now() - start).toFixed(2);
console.log(` done in ${loadTime} ms.`);
// UI shows model building progress
await ui.showProgressComponent('done', 'current', 'pending');
console.log('- Building... ');
start = performance.now();
await fdInstance.build(fdOutputOperand);
await frInstance.build(frOutputOperand);
await Promise.all([
fdInstance.build(fdOutputOperand),
frInstance.build(frOutputOperand),
]);
buildTime = (performance.now() - start).toFixed(2);
console.log(` done in ${buildTime} ms.`);
}
Expand Down
96 changes: 55 additions & 41 deletions image_classification/mobilenet_nhwc.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ export class MobileNetV2Nhwc {
const weightsName = this.weightsUrl_ + 'Const_' + weightsSubName + '.npy';
const weights = await buildConstantByNpy(this.builder_, weightsName);
const biasName = this.weightsUrl_ + 'MobilenetV2_' + biasSubName + '_bias.npy';
const bias = await buildConstantByNpy(this.builder_, biasName);
const bias = buildConstantByNpy(this.builder_, biasName);
options.inputLayout = 'nhwc';
options.bias = bias;
options.bias = await bias;
// WebNN spec drops autoPad support, compute the explicit padding instead.
if (options.autoPad == 'same-upper') {
options.padding =
computePadding2DForAutoPad(
/* nwhc */[input.shape()[1], input.shape()[2]],
/* nwhc */[await input.shape()[1], await input.shape()[2]],
/* ohwi or ihwo */[weights.shape()[1], weights.shape()[2]],
options.strides, options.dilations, options.autoPad);
}
Expand All @@ -44,7 +44,7 @@ export class MobileNetV2Nhwc {
// Implement `clip` by `clamp` of WebNN API
if (this.deviceType_ == 'gpu') {
return this.builder_.clamp(
this.builder_.conv2d(input, weights, options),
this.builder_.conv2d(await input, weights, options),
{minValue: 0, maxValue: 6});
} else {
options.activation = this.builder_.clamp({minValue: 0, maxValue: 6});
Expand All @@ -60,17 +60,32 @@ export class MobileNetV2Nhwc {
dwiseOptions.autoPad = autoPad;
dwiseOptions.filterLayout = 'ihwo';

const conv1x1Relu6 = await this.buildConv_(
input, weightsNameArray[0], `${biasPrefix}_expand_Conv2D`, true, {autoPad, filterLayout: 'ohwi'});
const dwise3x3Relu6 = await this.buildConv_(
conv1x1Relu6, weightsNameArray[1], `${biasPrefix}_depthwise_depthwise`, true, dwiseOptions);
const conv1x1Linear = await this.buildConv_(
dwise3x3Relu6, weightsNameArray[2], `${biasPrefix}_project_Conv2D`, false, {autoPad, filterLayout: 'ohwi'});
const conv1x1Relu6 = this.buildConv_(
await input,
weightsNameArray[0],
`${biasPrefix}_expand_Conv2D`,
true,
{autoPad, filterLayout: 'ohwi'},
);
const dwise3x3Relu6 = this.buildConv_(
await conv1x1Relu6,
weightsNameArray[1],
`${biasPrefix}_depthwise_depthwise`,
true,
dwiseOptions,
);
const conv1x1Linear = this.buildConv_(
await dwise3x3Relu6,
weightsNameArray[2],
`${biasPrefix}_project_Conv2D`,
false,
{autoPad, filterLayout: 'ohwi'},
);

if (shortcut) {
return this.builder_.add(input, conv1x1Linear);
return this.builder_.add(await input, await conv1x1Linear);
}
return conv1x1Linear;
return await conv1x1Linear;
}

async load(contextOptions) {
Expand All @@ -85,53 +100,52 @@ export class MobileNetV2Nhwc {
dataType: 'float32',
dimensions: this.inputOptions.inputDimensions,
});
const conv0 = await this.buildConv_(
const conv0 = this.buildConv_(
input, '90', 'Conv_Conv2D', true, {strides, autoPad, filterLayout});
const conv1 = await this.buildConv_(
conv0, '238', 'expanded_conv_depthwise_depthwise', true, {autoPad, groups: 32, filterLayout: 'ihwo'});
const conv2 = await this.buildConv_(
conv1, '167', 'expanded_conv_project_Conv2D', false, {autoPad, filterLayout});
const bottleneck0 = await this.buildLinearBottleneck_(
conv2, ['165', '99', '73'], '1', {strides, groups: 96}, false);
const bottleneck1 = await this.buildLinearBottleneck_(
const conv1 = this.buildConv_(
await conv0, '238', 'expanded_conv_depthwise_depthwise', true, {autoPad, groups: 32, filterLayout: 'ihwo'});
const conv2 = this.buildConv_(
await conv1, '167', 'expanded_conv_project_Conv2D', false, {autoPad, filterLayout});
const bottleneck0 = this.buildLinearBottleneck_(
await conv2, ['165', '99', '73'], '1', {strides, groups: 96}, false);
const bottleneck1 = this.buildLinearBottleneck_(
bottleneck0, ['3', '119', '115'], '2', {groups: 144});
const bottleneck2 = await this.buildLinearBottleneck_(
const bottleneck2 = this.buildLinearBottleneck_(
bottleneck1, ['255', '216', '157'], '3', {strides, groups: 144}, false);
const bottleneck3 = await this.buildLinearBottleneck_(
const bottleneck3 = this.buildLinearBottleneck_(
bottleneck2, ['227', '221', '193'], '4', {groups: 192});
const bottleneck4 = await this.buildLinearBottleneck_(
const bottleneck4 = this.buildLinearBottleneck_(
bottleneck3, ['243', '102', '215'], '5', {groups: 192});
const bottleneck5 = await this.buildLinearBottleneck_(
const bottleneck5 = this.buildLinearBottleneck_(
bottleneck4, ['226', '163', '229'], '6', {strides, groups: 192}, false);
const bottleneck6 = await this.buildLinearBottleneck_(
const bottleneck6 = this.buildLinearBottleneck_(
bottleneck5, ['104', '254', '143'], '7', {groups: 384});
const bottleneck7 = await this.buildLinearBottleneck_(
const bottleneck7 = this.buildLinearBottleneck_(
bottleneck6, ['25', '142', '202'], '8', {groups: 384});
const bottleneck8 = await this.buildLinearBottleneck_(
const bottleneck8 = this.buildLinearBottleneck_(
bottleneck7, ['225', '129', '98'], '9', {groups: 384});
const bottleneck9 = await this.buildLinearBottleneck_(
const bottleneck9 = this.buildLinearBottleneck_(
bottleneck8, ['169', '2', '246'], '10', {groups: 384}, false);
const bottleneck10 = await this.buildLinearBottleneck_(
const bottleneck10 = this.buildLinearBottleneck_(
bottleneck9, ['162', '87', '106'], '11', {groups: 576});
const bottleneck11 = await this.buildLinearBottleneck_(
const bottleneck11 = this.buildLinearBottleneck_(
bottleneck10, ['52', '22', '40'], '12', {groups: 576});
const bottleneck12 = await this.buildLinearBottleneck_(
const bottleneck12 = this.buildLinearBottleneck_(
bottleneck11, ['114', '65', '242'], '13', {strides, groups: 576}, false);
const bottleneck13 = await this.buildLinearBottleneck_(
const bottleneck13 = this.buildLinearBottleneck_(
bottleneck12, ['203', '250', '92'], '14', {groups: 960});
const bottleneck14 = await this.buildLinearBottleneck_(
const bottleneck14 = this.buildLinearBottleneck_(
bottleneck13, ['133', '130', '258'], '15', {groups: 960});
const bottleneck15 = await this.buildLinearBottleneck_(
const bottleneck15 = this.buildLinearBottleneck_(
bottleneck14, ['60', '248', '100'], '16', {groups: 960}, false);
const conv3 = await this.buildConv_(
bottleneck15, '71', 'Conv_1_Conv2D', true, {autoPad, filterLayout});
const conv3 = this.buildConv_(
await bottleneck15, '71', 'Conv_1_Conv2D', true, {autoPad, filterLayout});

const averagePool2d = this.builder_.averagePool2d(
conv3, {windowDimensions: [7, 7], layout: 'nhwc'});
const conv4 = await this.buildConv_(
const averagePool2d = this.builder_.averagePool2d(await conv3, {windowDimensions: [7, 7], layout: 'nhwc'});
const conv4 = this.buildConv_(
averagePool2d, '222', 'Logits_Conv2d_1c_1x1_Conv2D', false, {autoPad, filterLayout});
const reshape = this.builder_.reshape(conv4, [1, 1001]);
return this.builder_.softmax(reshape);
const reshape = this.builder_.reshape(await conv4, [1, 1001]);
return await this.builder_.softmax(reshape);
}

async build(outputOperand) {
Expand Down
110 changes: 56 additions & 54 deletions image_classification/resnet50v2_nchw.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ export class ResNet50V2Nchw {
prefix = this.weightsUrl_ + 'resnetv24_conv' + name;
}
const weightName = prefix + '_weight.npy';
const weight = await buildConstantByNpy(this.builder_, weightName);
return this.builder_.conv2d(input, weight, options);
const weight = buildConstantByNpy(this.builder_, weightName);
return this.builder_.conv2d(await input, await weight, options);
}

async buildBatchNorm_(input, name, stageName, relu = true) {
Expand All @@ -46,26 +46,31 @@ export class ResNet50V2Nchw {
const biasName = prefix + '_beta.npy';
const meanName = prefix + '_running_mean.npy';
const varName = prefix + '_running_var.npy';
const scale = await buildConstantByNpy(this.builder_, scaleName);
const bias = await buildConstantByNpy(this.builder_, biasName);
const mean = await buildConstantByNpy(this.builder_, meanName);
const variance = await buildConstantByNpy(this.builder_, varName);
const options = {scale: scale, bias: bias};
const scale = buildConstantByNpy(this.builder_, scaleName);
const bias = buildConstantByNpy(this.builder_, biasName);
const mean = buildConstantByNpy(this.builder_, meanName);
const variance = buildConstantByNpy(this.builder_, varName);
const options = {scale: await scale, bias: await bias};
if (relu) {
options.activation = this.builder_.relu();
}
return this.builder_.batchNormalization(input, mean, variance, options);
return this.builder_.batchNormalization(
await input,
await mean,
await variance,
options,
);
}

async buildGemm_(input, name) {
const prefix = this.weightsUrl_ + 'resnetv24_dense' + name;
const weightName = prefix + '_weight.npy';
const weight = await buildConstantByNpy(this.builder_, weightName);
const weight = buildConstantByNpy(this.builder_, weightName);
const biasName = prefix + '_bias.npy';
const bias = await buildConstantByNpy(this.builder_, biasName);
const bias = buildConstantByNpy(this.builder_, biasName);
const options =
{c: this.builder_.reshape(bias, [1, 1000]), bTranspose: true};
return this.builder_.gemm(input, weight, options);
{c: this.builder_.reshape(await bias, [1, 1000]), bTranspose: true};
return this.builder_.gemm(await input, await weight, options);
}

async buildBottlenectV2_(
Expand All @@ -76,20 +81,20 @@ export class ResNet50V2Nchw {
if (downsample) {
strides = [stride, stride];
}
const bn1 = await this.buildBatchNorm_(input, nameIndices[0], stageName);
const conv1 = await this.buildConv_(bn1, nameIndices[1], stageName);
const bn2 = await this.buildBatchNorm_(
const bn1 = this.buildBatchNorm_(input, nameIndices[0], stageName);
const conv1 = this.buildConv_(bn1, nameIndices[1], stageName);
const bn2 = this.buildBatchNorm_(
conv1, parseInt(nameIndices[0]) + 1, stageName);
const conv2 = await this.buildConv_(
const conv2 = this.buildConv_(
bn2, nameIndices[2], stageName, {padding: [1, 1, 1, 1], strides});
const bn3 = await this.buildBatchNorm_(
const bn3 = this.buildBatchNorm_(
conv2, parseInt(nameIndices[0]) + 2, stageName);
const conv3 = await this.buildConv_(bn3, nameIndices[3], stageName);
const conv3 = this.buildConv_(bn3, nameIndices[3], stageName);
if (downsample) {
residual = await this.buildConv_(
residual = this.buildConv_(
bn1, parseInt(nameIndices[0]) + 3, stageName, {strides});
}
return this.builder_.add(conv3, residual);
return this.builder_.add(await conv3, await residual);
}

async load(contextOptions) {
Expand All @@ -100,76 +105,73 @@ export class ResNet50V2Nchw {
dataType: 'float32',
dimensions: this.inputOptions.inputDimensions,
});
const bn1 = await this.buildBatchNorm_(data, '0', '', false);
const conv0 = await this.buildConv_(
const bn1 = this.buildBatchNorm_(data, '0', '', false);
const conv0 = this.buildConv_(
bn1, '0', '', {padding: [3, 3, 3, 3], strides: [2, 2]});
const bn2 = await this.buildBatchNorm_(conv0, '1', '');
const pool1 = await this.builder_.maxPool2d(bn2,
const bn2 = this.buildBatchNorm_(conv0, '1', '');
const pool1 = this.builder_.maxPool2d(await bn2,
{windowDimensions: [3, 3], padding: [1, 1, 1, 1], strides: [2, 2]});

// Stage 1
const bottleneck1 = await this.buildBottlenectV2_(
const bottleneck1 = this.buildBottlenectV2_(
pool1, '1', ['0', '0', '1', '2'], true);
const bottleneck2 = await this.buildBottlenectV2_(
const bottleneck2 = this.buildBottlenectV2_(
bottleneck1, '1', ['3', '4', '5', '6']);
const bottleneck3 = await this.buildBottlenectV2_(
const bottleneck3 = this.buildBottlenectV2_(
bottleneck2, '1', ['6', '7', '8', '9']);

// Stage 2
const bottleneck4 = await this.buildBottlenectV2_(
const bottleneck4 = this.buildBottlenectV2_(
bottleneck3, '2', ['0', '0', '1', '2'], true, 2);
const bottleneck5 = await this.buildBottlenectV2_(
const bottleneck5 = this.buildBottlenectV2_(
bottleneck4, '2', ['3', '4', '5', '6']);
const bottleneck6 = await this.buildBottlenectV2_(
const bottleneck6 = this.buildBottlenectV2_(
bottleneck5, '2', ['6', '7', '8', '9']);
const bottleneck7 = await this.buildBottlenectV2_(
const bottleneck7 = this.buildBottlenectV2_(
bottleneck6, '2', ['9', '10', '11', '12']);

// Stage 3
const bottleneck8 = await this.buildBottlenectV2_(
const bottleneck8 = this.buildBottlenectV2_(
bottleneck7, '3', ['0', '0', '1', '2'], true, 2);
const bottleneck9 = await this.buildBottlenectV2_(
const bottleneck9 = this.buildBottlenectV2_(
bottleneck8, '3', ['3', '4', '5', '6']);
const bottleneck10 = await this.buildBottlenectV2_(
const bottleneck10 = this.buildBottlenectV2_(
bottleneck9, '3', ['6', '7', '8', '9']);
const bottleneck11 = await this.buildBottlenectV2_(
const bottleneck11 = this.buildBottlenectV2_(
bottleneck10, '3', ['9', '10', '11', '12']);
const bottleneck12 = await this.buildBottlenectV2_(
const bottleneck12 = this.buildBottlenectV2_(
bottleneck11, '3', ['12', '13', '14', '15']);
const bottleneck13 = await this.buildBottlenectV2_(
const bottleneck13 = this.buildBottlenectV2_(
bottleneck12, '3', ['15', '16', '17', '18']);

// Stage 4
const bottleneck14 = await this.buildBottlenectV2_(
const bottleneck14 = this.buildBottlenectV2_(
bottleneck13, '4', ['0', '0', '1', '2'], true, 2);
const bottleneck15 = await this.buildBottlenectV2_(
const bottleneck15 = this.buildBottlenectV2_(
bottleneck14, '4', ['3', '4', '5', '6']);
const bottleneck16 = await this.buildBottlenectV2_(
const bottleneck16 = this.buildBottlenectV2_(
bottleneck15, '4', ['6', '7', '8', '9']);

const bn3 = await this.buildBatchNorm_(bottleneck16, '2', '');
const pool2 = await this.builder_.averagePool2d(bn3);
const reshape = this.builder_.reshape(pool2, [1, 2048]);
const gemm = await this.buildGemm_(reshape, '0');
return this.builder_.softmax(gemm);
const bn3 = this.buildBatchNorm_(bottleneck16, '2', '');
const pool2 = this.builder_.averagePool2d(await bn3);
const reshape = this.builder_.reshape(await pool2, [1, 2048]);
const gemm = this.buildGemm_(await reshape, '0');
return this.builder_.softmax(await gemm);
}

async build(outputOperand) {
this.graph_ = await this.builder_.build({'output': outputOperand});
this.graph_ = this.builder_.build({'output': outputOperand});
}

// Release the constant tensors of a model
dispose() {
// dispose() is only available in webnn-polyfill
if (this.graph_ !== null && 'dispose' in this.graph_) {
this.graph_.dispose();
}
}

async compute(inputBuffer, outputBuffer) {
const inputs = {'input': inputBuffer};
const outputs = {'output': outputBuffer};
const results = await this.context_.compute(this.graph_, inputs, outputs);
const results = await this.context_.compute(
await this.graph_,
inputs,
outputs,
);
return results;
}
}
Loading