Skip to content

Commit

Permalink
Deploying to gh-pages from @ 555aeab 🚀
Browse files Browse the repository at this point in the history
  • Loading branch information
Honry committed May 6, 2024
1 parent c33e093 commit 3c00996
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 10 deletions.
70 changes: 66 additions & 4 deletions common/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,57 @@ export async function getBufferFromUrl(url) {
return arrayBuffer;
}

export async function buildConstantByNpy(builder, url) {
// ref: http://stackoverflow.com/questions/32633585/how-do-you-convert-to-half-floats-in-javascript
export const toHalf = (function() {
const floatView = new Float32Array(1);
const int32View = new Int32Array(floatView.buffer);

/* This method is faster than the OpenEXR implementation (very often
* used, eg. in Ogre), with the additional benefit of rounding, inspired
* by James Tursa?s half-precision code. */
return function toHalf(val) {
floatView[0] = val;
const x = int32View[0];

let bits = (x >> 16) & 0x8000; /* Get the sign */
let m = (x >> 12) & 0x07ff; /* Keep one extra bit for rounding */
const e = (x >> 23) & 0xff; /* Using int is faster here */

/* If zero, or denormal, or exponent underflows too much for a denormal
* half, return signed zero. */
if (e < 103) {
return bits;
}

/* If NaN, return NaN. If Inf or exponent overflow, return Inf. */
if (e > 142) {
bits |= 0x7c00;
/* If exponent was 0xff and one mantissa bit was set, it means NaN,
* not Inf, so make sure we set one mantissa bit too. */
bits |= ((e == 255) ? 0 : 1) && (x & 0x007fffff);
return bits;
}

/* If exponent underflows but not too much, return a denormal */
if (e < 113) {
m |= 0x0800;
/* Extra rounding may overflow and set mantissa to 0 and exponent
* to 1, which is OK. */
bits |= (m >> (114 - e)) + ((m >> (113 - e)) & 1);
return bits;
}

bits |= ((e - 112) << 10) | (m >> 1);
/* Extra rounding. An overflow will set mantissa to 0 and increment
* the exponent, which is OK. */
bits += m & 1;
return bits;
};
})();

// Convert npy data in original data type to `targetType`, only support
// 'float32' to 'float16' conversion currently.
export async function buildConstantByNpy(builder, url, targetType) {
const dataTypeMap = new Map([
['f2', {type: 'float16', array: Uint16Array}],
['f4', {type: 'float32', array: Float32Array}],
Expand All @@ -55,11 +105,22 @@ export async function buildConstantByNpy(builder, url) {
throw new Error(`Data type ${npArray.dataType} is not supported.`);
}
const dimensions = npArray.shape;
const type = dataTypeMap.get(npArray.dataType).type;
let type = dataTypeMap.get(npArray.dataType).type;
const TypedArrayConstructor = dataTypeMap.get(npArray.dataType).array;
const dataView = new Uint8Array(npArray.data.buffer);
const dataView2 = dataView.slice();
const typedArray = new TypedArrayConstructor(dataView2.buffer);
let typedArray = new TypedArrayConstructor(dataView2.buffer);
if (type === 'float32' && targetType === 'float16') {
const uint16Array = new Uint16Array(typedArray.length);
for (let i = 0; i < typedArray.length; ++i) {
uint16Array[i] = toHalf(typedArray[i]);
}
typedArray = uint16Array;
type = targetType;
} else if (type !== targetType) {
throw new Error(`Conversion from ${npArray.dataType} ` +
`to ${targetType} is not supported.`);
}
return builder.constant({dataType: type, type, dimensions}, typedArray);
}

Expand Down Expand Up @@ -494,7 +555,8 @@ export function getDefaultLayout(deviceType) {
// Windows or Mac platform.
if (deviceType.indexOf('cpu') != -1) {
return 'nhwc';
} else if (deviceType.indexOf('gpu') != -1) {
} else if (deviceType.indexOf('gpu') != -1 ||
deviceType.indexOf('npu') != -1) {
return 'nchw';
}
}
Expand Down
3 changes: 3 additions & 0 deletions object_detection/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@
<label class="btn btn-outline-info custom" name="webnn">
<input type="radio" name="backend" id="webnn_gpu" autocomplete="off">WebNN (GPU)
</label>
<label class="btn btn-outline-info custom" name="webnn">
<input type="radio" name="backend" id="webnn_npu" autocomplete="off">WebNN (NPU)
</label>
</div>
</div>
</div>
Expand Down
27 changes: 21 additions & 6 deletions object_detection/ssd_mobilenetv1_nchw.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export class SsdMobilenetV1Nchw {
constructor() {
this.context_ = null;
this.deviceType_ = null;
this.targetDataType_ = 'float32';
this.model_ = null;
this.builder_ = null;
this.graph_ = null;
Expand Down Expand Up @@ -57,9 +58,11 @@ ${nameArray[1]}_BatchNorm_batchnorm`;
}

const weightsName = this.weightsUrl_ + prefix + weightSuffix;
const weights = await buildConstantByNpy(this.builder_, weightsName);
const weights = await buildConstantByNpy(
this.builder_, weightsName, this.targetDataType_);
const biasName = this.biasUrl_ + prefix + biasSuffix;
const bias = await buildConstantByNpy(this.builder_, biasName);
const bias = await buildConstantByNpy(
this.builder_, biasName, this.targetDataType_);
options.padding = computePadding2DForAutoPad(
/* nchw */[input.shape()[2], input.shape()[3]],
/* oihw */[weights.shape()[2], weights.shape()[3]],
Expand All @@ -69,7 +72,7 @@ ${nameArray[1]}_BatchNorm_batchnorm`;
// TODO: Set clamp activation to options once it's supported in
// WebNN DML backend.
// Implement `clip` by `clamp` of WebNN API
if (this.deviceType_ == 'gpu') {
if (this.deviceType_ == 'gpu' || this.deviceType_ == 'npu') {
return this.builder_.clamp(
this.builder_.conv2d(input, weights, options),
{minValue: 0, maxValue: 6});
Expand All @@ -83,12 +86,17 @@ ${nameArray[1]}_BatchNorm_batchnorm`;
async load(contextOptions) {
this.context_ = await navigator.ml.createContext(contextOptions);
this.deviceType_ = contextOptions.deviceType;
if (this.deviceType_ == 'gpu' || this.deviceType_ == 'npu') {
this.targetDataType_ = 'float16';
}
this.builder_ = new MLGraphBuilder(this.context_);
const input = this.builder_.input('input', {
type: 'float32',
let input = this.builder_.input('input', {
dataType: 'float32',
dimensions: this.inputOptions.inputDimensions,
});
if (this.targetDataType_ === 'float16') {
input = this.builder_.cast(input, 'float16');
}
const strides = [2, 2];
const conv0 = await this.buildConv_(
input, ['', '0', '', '165__cf__168'],
Expand Down Expand Up @@ -249,7 +257,14 @@ ${nameArray[1]}_BatchNorm_batchnorm`;
const concat1 = this.builder_.concat(
[reshape6, reshape7, reshape8, reshape9, reshape10, reshape11], 1);

return {'boxes': concat0, 'scores': concat1};
let boxes = concat0;
let scores = concat1;

if (this.targetDataType_ === 'float16') {
boxes = this.builder_.cast(boxes, 'float32');
scores = this.builder_.cast(boxes, 'float32');
}
return {boxes, scores};
}

async build(outputOperand) {
Expand Down

0 comments on commit 3c00996

Please sign in to comment.