Skip to content

Commit

Permalink
Refactor some models to fetch in parallel
Browse files Browse the repository at this point in the history
  • Loading branch information
ibelem committed Mar 22, 2024
1 parent d64624c commit 5604eb5
Show file tree
Hide file tree
Showing 7 changed files with 260 additions and 227 deletions.
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

0 comments on commit 5604eb5

Please sign in to comment.