Skip to content

Commit

Permalink
Use MLTensor for Code and Image Classification samples
Browse files Browse the repository at this point in the history
Fixed partial of webmachinelearning#275
  • Loading branch information
Honry committed Sep 19, 2024
1 parent 2bb8f9a commit b81f43b
Show file tree
Hide file tree
Showing 14 changed files with 190 additions and 93 deletions.
1 change: 1 addition & 0 deletions code/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module.exports = {
'CodeMirror': 'readonly',
'executeCodeSnippet': 'readonly',
'sizeOfShape': 'readonly',
'MLTensorUsage': 'readonly',
},
ignorePatterns: ['libs/'],
};
10 changes: 5 additions & 5 deletions code/samples/matmul.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@ const graph = await builder.build({c});
// Step 3: Bind input and output buffers to the graph and execute.
const bufferA = new Float32Array(3*4).fill(1.0);
const bufferB = new Float32Array(4*3).fill(0.8);
const bufferC = new Float32Array(3*3);
descA.usage = MLTensorUsage.WRITE_TO;
descB.usage = MLTensorUsage.WRITE_TO;
descA.usage = MLTensorUsage.WRITE;
descB.usage = MLTensorUsage.WRITE;
const tensorA = await context.createTensor(descA);
const tensorB = await context.createTensor(descB);
context.writeTensor(tensorA, bufferA);
context.writeTensor(tensorB, bufferB);
const tensorC = await context.createTensor({
dataType: "float32",
dataType: 'float32',
dimensions: [3, 3],
usage: MLTensorUsage.READ_FROM,
shape: [3, 3],
usage: MLTensorUsage.READ,
});
context.dispatch(graph, {a: tensorA, b: tensorB}, {c: tensorC});
const results = await context.readTensor(tensorC);
Expand Down
8 changes: 3 additions & 5 deletions code/samples/mul_add.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,14 @@ const graph = await builder.build({'C': C});
// 3. Bind inputs to the graph and execute for the result.
const bufferA = new Float32Array(4).fill(1.0);
const bufferB = new Float32Array(4).fill(0.8);
const bufferC = new Float32Array(4);
desc.usage = MLTensorUsage.WRITE_TO;
desc.usage = MLTensorUsage.WRITE;
const tensorA = await context.createTensor(desc);
const tensorB = await context.createTensor(desc);
context.writeTensor(tensorA, bufferA);
context.writeTensor(tensorB, bufferB);
const tensorC = await context.createTensor({
dataType: 'float32',
dimensions: [2, 2],
usage: MLTensorUsage.READ_FROM,
...desc,
usage: MLTensorUsage.READ,
});
const inputs = {'A': tensorA, 'B': tensorB};
const outputs = {'C': tensorC};
Expand Down
23 changes: 17 additions & 6 deletions code/samples/simple_graph.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,26 @@ const graph = await builder.build({'output': output});
// Setup the input buffers with value 1.
const inputBuffer1 = new Float32Array(TENSOR_SIZE).fill(1);
const inputBuffer2 = new Float32Array(TENSOR_SIZE).fill(1);
const outputBuffer = new Float32Array(TENSOR_SIZE);

desc.usage = MLTensorUsage.WRITE;
const inputTensor1 = await context.createTensor(desc);
const inputTensor2 = await context.createTensor(desc);
context.writeTensor(inputTensor1, inputBuffer1);
context.writeTensor(inputTensor2, inputBuffer2);

const outputTensor = await context.createTensor({
...desc,
usage: MLTensorUsage.READ,
});

// Execute the compiled graph with the specified inputs.
const inputs = {
'input1': inputBuffer1,
'input2': inputBuffer2,
'input1': inputTensor1,
'input2': inputTensor2,
};
const outputs = {'output': outputBuffer};
const results = await context.compute(graph, inputs, outputs);
const outputs = {'output': outputTensor};
context.dispatch(graph, inputs, outputs);

console.log('Output value: ' + results.outputs.output);
const results = await context.readTensor(outputTensor);
console.log('Output value: ' + new Float32Array(results));
// Output value: 2.25,2.25,2.25,2.25,2.25,2.25,2.25,2.25
1 change: 1 addition & 0 deletions image_classification/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module.exports = {
globals: {
'MLGraphBuilder': 'readonly',
'MLTensorUsage': 'readonly',
},
};
27 changes: 20 additions & 7 deletions image_classification/efficientnet_fp16_nchw.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export class EfficientNetFP16Nchw {
this.context_ = null;
this.builder_ = null;
this.graph_ = null;
this.inputTensor_ = null;
this.outputTensor_ = null;
this.weightsUrl_ = weightsOrigin() +
'/test-data/models/efficientnet_fp16_nchw_optimized/weights/';
this.inputOptions = {
Expand All @@ -18,7 +20,7 @@ export class EfficientNetFP16Nchw {
labelUrl: './labels/labels1000.txt',
inputShape: [1, 3, 224, 224],
};
this.outputShape = [1, 1000];
this.outputShape_ = [1, 1000];
}

async buildConv_(input, name, blockName, clip = false, options = {}) {
Expand Down Expand Up @@ -75,10 +77,19 @@ export class EfficientNetFP16Nchw {
async load(contextOptions) {
this.context_ = await navigator.ml.createContext(contextOptions);
this.builder_ = new MLGraphBuilder(this.context_);
let data = this.builder_.input('input', {
const inputDesc = {
dataType: 'float32',
dimensions: this.inputOptions.inputShape,
shape: this.inputOptions.inputShape,
};
let data = this.builder_.input('input', inputDesc);
inputDesc.usage = MLTensorUsage.WRITE;
this.inputTensor_ = await this.context_.createTensor(inputDesc);
this.outputTensor_ = await this.context_.createTensor({
dataType: 'float32',
dimensions: this.outputShape_,
shape: this.outputShape_,
usage: MLTensorUsage.READ,
});
data = this.builder_.cast(data, 'float16');
// Block 0
Expand Down Expand Up @@ -165,10 +176,12 @@ export class EfficientNetFP16Nchw {
}
}

async compute(inputBuffer, outputBuffer) {
const inputs = {'input': inputBuffer};
const outputs = {'output': outputBuffer};
const results = await this.context_.compute(this.graph_, inputs, outputs);
return results;
async compute(inputBuffer) {
this.context_.writeTensor(this.inputTensor_, inputBuffer);
const inputs = {'input': this.inputTensor_};
const outputs = {'output': this.outputTensor_};
this.context_.dispatch(this.graph_, inputs, outputs);
const results = await this.context_.readTensor(this.outputTensor_);
return new Float32Array(results);
}
}
12 changes: 3 additions & 9 deletions image_classification/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ let loadTime = 0;
let buildTime = 0;
let computeTime = 0;
let inputOptions;
let outputBuffer;
let deviceType = '';
let lastdeviceType = '';
let backend = '';
Expand Down Expand Up @@ -215,9 +214,8 @@ async function renderCamStream() {
const inputCanvas = utils.getVideoFrame(camElement);
console.log('- Computing... ');
const start = performance.now();
const results = await netInstance.compute(inputBuffer, outputBuffer);
const outputBuffer = await netInstance.compute(inputBuffer);
computeTime = (performance.now() - start).toFixed(2);
outputBuffer = results.outputs.output;
console.log(` done in ${computeTime} ms.`);
drawInput(inputCanvas, 'camInCanvas');
showPerfResult();
Expand Down Expand Up @@ -334,8 +332,6 @@ async function main() {
netInstance = constructNetObject(instanceType);
inputOptions = netInstance.inputOptions;
labels = await fetchLabels(inputOptions.labelUrl);
outputBuffer =
new Float32Array(utils.sizeOfShape(netInstance.outputShape));
isFirstTimeLoad = false;
console.log(`- Model name: ${modelName}, Model layout: ${layout} -`);
// UI shows model loading progress
Expand Down Expand Up @@ -369,12 +365,11 @@ async function main() {
let medianComputeTime;

// Do warm up
let results = await netInstance.compute(inputBuffer, outputBuffer);
let outputBuffer = await netInstance.compute(inputBuffer);

for (let i = 0; i < numRuns; i++) {
start = performance.now();
results = await netInstance.compute(
results.inputs.input, results.outputs.output);
outputBuffer = await netInstance.compute(inputBuffer);
computeTime = (performance.now() - start).toFixed(2);
console.log(` compute time ${i+1}: ${computeTime} ms`);
computeTimeArray.push(Number(computeTime));
Expand All @@ -384,7 +379,6 @@ async function main() {
medianComputeTime = medianComputeTime.toFixed(2);
console.log(` median compute time: ${medianComputeTime} ms`);
}
outputBuffer = results.outputs.output;
console.log('outputBuffer: ', outputBuffer);
await ui.showProgressComponent('done', 'done', 'done');
ui.readyShowResultComponents();
Expand Down
27 changes: 20 additions & 7 deletions image_classification/mobilenet_nchw.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ export class MobileNetV2Nchw {
this.deviceType_ = null;
this.builder_ = null;
this.graph_ = null;
this.inputTensor_ = null;
this.outputTensor_ = null;
this.dataType_ = dataType;
this.weightsUrl_ = weightsOrigin();
if (this.dataType_ === 'float32') {
Expand All @@ -27,7 +29,7 @@ export class MobileNetV2Nchw {
labelUrl: './labels/labels1000.txt',
inputShape: [1, 3, 224, 224],
};
this.outputShape = [1, 1000];
this.outputShape_ = [1, 1000];
}

async buildConv_(input, name, relu6 = true, options = {}) {
Expand Down Expand Up @@ -89,10 +91,19 @@ export class MobileNetV2Nchw {
this.context_ = await navigator.ml.createContext(contextOptions);
this.deviceType_ = contextOptions.deviceType;
this.builder_ = new MLGraphBuilder(this.context_);
let data = this.builder_.input('input', {
const inputDesc = {
dataType: 'float32',
dimensions: this.inputOptions.inputShape,
shape: this.inputOptions.inputShape,
};
let data = this.builder_.input('input', inputDesc);
inputDesc.usage = MLTensorUsage.WRITE;
this.inputTensor_ = await this.context_.createTensor(inputDesc);
this.outputTensor_ = await this.context_.createTensor({
dataType: 'float32',
dimensions: this.outputShape_,
shape: this.outputShape_,
usage: MLTensorUsage.READ,
});
if (this.dataType_ === 'float16') {
data = this.builder_.cast(data, 'float16');
Expand Down Expand Up @@ -163,10 +174,12 @@ export class MobileNetV2Nchw {
}
}

async compute(inputBuffer, outputBuffer) {
const inputs = {'input': inputBuffer};
const outputs = {'output': outputBuffer};
const results = await this.context_.compute(this.graph_, inputs, outputs);
return results;
async compute(inputBuffer) {
this.context_.writeTensor(this.inputTensor_, inputBuffer);
const inputs = {'input': this.inputTensor_};
const outputs = {'output': this.outputTensor_};
this.context_.dispatch(this.graph_, inputs, outputs);
const results = await this.context_.readTensor(this.outputTensor_);
return new Float32Array(results);
}
}
27 changes: 20 additions & 7 deletions image_classification/mobilenet_nhwc.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export class MobileNetV2Nhwc {
this.deviceType_ = null;
this.builder_ = null;
this.graph_ = null;
this.inputTensor_ = null;
this.outputTensor_ = null;
this.weightsUrl_ = weightsOrigin() +
'/test-data/models/mobilenetv2_nhwc/weights/';
this.inputOptions = {
Expand All @@ -20,7 +22,7 @@ export class MobileNetV2Nhwc {
labelUrl: './labels/labels1001.txt',
inputShape: [1, 224, 224, 3],
};
this.outputShape = [1, 1001];
this.outputShape_ = [1, 1001];
}

async buildConv_(input, weightsSubName, biasSubName, relu6, options) {
Expand Down Expand Up @@ -87,10 +89,19 @@ export class MobileNetV2Nhwc {
const strides = [2, 2];
const autoPad = 'same-upper';
const filterLayout = 'ohwi';
const input = this.builder_.input('input', {
const inputDesc = {
dataType: 'float32',
dimensions: this.inputOptions.inputShape,
shape: this.inputOptions.inputShape,
};
const input = this.builder_.input('input', inputDesc);
inputDesc.usage = MLTensorUsage.WRITE;
this.inputTensor_ = await this.context_.createTensor(inputDesc);
this.outputTensor_ = await this.context_.createTensor({
dataType: 'float32',
dimensions: this.outputShape_,
shape: this.outputShape_,
usage: MLTensorUsage.READ,
});
const conv0 = this.buildConv_(
input, '90', 'Conv_Conv2D', true, {strides, autoPad, filterLayout});
Expand Down Expand Up @@ -152,10 +163,12 @@ export class MobileNetV2Nhwc {
}
}

async compute(inputBuffer, outputBuffer) {
const inputs = {'input': inputBuffer};
const outputs = {'output': outputBuffer};
const results = await this.context_.compute(this.graph_, inputs, outputs);
return results;
async compute(inputBuffer) {
this.context_.writeTensor(this.inputTensor_, inputBuffer);
const inputs = {'input': this.inputTensor_};
const outputs = {'output': this.outputTensor_};
this.context_.dispatch(this.graph_, inputs, outputs);
const results = await this.context_.readTensor(this.outputTensor_);
return new Float32Array(results);
}
}
31 changes: 20 additions & 11 deletions image_classification/resnet50v1_fp16_nchw.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export class ResNet50V1FP16Nchw {
this.context_ = null;
this.builder_ = null;
this.graph_ = null;
this.inputTensor_ = null;
this.outputTensor_ = null;
this.weightsUrl_ = weightsOrigin() +
'/test-data/models/resnet50v1_fp16_nchw_optimized/weights/';
this.inputOptions = {
Expand All @@ -18,7 +20,7 @@ export class ResNet50V1FP16Nchw {
labelUrl: './labels/labels1000.txt',
inputShape: [1, 3, 224, 224],
};
this.outputShape = [1, 1000];
this.outputShape_ = [1, 1000];
}

async buildConv_(input, name, stageName, relu, options = undefined) {
Expand Down Expand Up @@ -76,10 +78,19 @@ export class ResNet50V1FP16Nchw {
async load(contextOptions) {
this.context_ = await navigator.ml.createContext(contextOptions);
this.builder_ = new MLGraphBuilder(this.context_);
let data = this.builder_.input('input', {
const inputDesc = {
dataType: 'float32',
dimensions: this.inputOptions.inputShape,
shape: this.inputOptions.inputShape,
};
let data = this.builder_.input('input', inputDesc);
inputDesc.usage = MLTensorUsage.WRITE;
this.inputTensor_ = await this.context_.createTensor(inputDesc);
this.outputTensor_ = await this.context_.createTensor({
dataType: 'float32',
dimensions: this.outputShape_,
shape: this.outputShape_,
usage: MLTensorUsage.READ,
});
data = this.builder_.cast(data, 'float16');
const conv1 = await this.buildConv_(
Expand Down Expand Up @@ -134,14 +145,12 @@ export class ResNet50V1FP16Nchw {
}

// Release the constant tensors of a model
async compute(inputBuffer, outputBuffer) {
const inputs = {'input': inputBuffer};
const outputs = {'output': outputBuffer};
const results = await this.context_.compute(
await this.graph_,
inputs,
outputs,
);
return results;
async compute(inputBuffer) {
this.context_.writeTensor(this.inputTensor_, inputBuffer);
const inputs = {'input': this.inputTensor_};
const outputs = {'output': this.outputTensor_};
this.context_.dispatch(this.graph_, inputs, outputs);
const results = await this.context_.readTensor(this.outputTensor_);
return new Float32Array(results);
}
}
Loading

0 comments on commit b81f43b

Please sign in to comment.