@tensorflow/tfjs-backend-wasm
Version:
This package adds a WebAssembly backend to TensorFlow.js. It currently supports the following models from our [models](https://github.com/tensorflow/tfjs-models) repo: - BlazeFace - BodyPix - CocoSSD - Face landmarks detection - HandPose - KNN classifier
90 lines • 15 kB
JavaScript
/**
* @license
* Copyright 2019 Google LLC. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =============================================================================
*/
import { backend_util, Concat, util } from '@tensorflow/tfjs-core';
import { concatImplCPU } from '../kernel_utils/shared';
import { identity } from './Identity';
import { reshape } from './Reshape';
export function concat(args) {
const { inputs, backend } = args;
const axis = util.parseAxisParam(args.attrs.axis, inputs[0].shape)[0];
const shapes = inputs.map(t => t.shape);
backend_util.assertParamsConsistent(shapes, axis);
let outShape = backend_util.computeOutShape(inputs.map(t => t.shape), axis);
// Keep only non-empty tensors (ignore tensors with 0 in their shape).
const $inputs = inputs.filter(t => util.sizeFromShape(t.shape) > 0);
if ($inputs.length === 1) {
return identity({ inputs: { x: $inputs[0] }, backend });
}
const out = backend.makeOutput(outShape, inputs[0].dtype);
if (util.sizeFromShape(outShape) === 0) {
return out;
}
if ($inputs[0].dtype === 'string') {
// Any concat of n-dimensional tensors across any axis can be reduced to
// a concatenation of two-dimensional tensors across the axis 1 by first
// partitioning the axes of the original tensors into those less than the
// axis to be concatenated and the rest. Then reshape the tensors
// into a two-dimensional tensor by collapsing these two sets of axes and
// concatenate the resulting matrices across the axis 1, finally reshaping
// the result to have the proper shape.
const inputs2D = $inputs.map(t => {
const innerSize = util.sizeFromShape(t.shape.slice(axis));
const shape = [-1, innerSize];
return reshape({ inputs: { x: t }, backend, attrs: { shape } });
});
const inputsValShapes = inputs2D.map(t => {
return { vals: backend.readSync(t.dataId), shape: t.shape };
});
// Concats 2d tensors along axis=1.
outShape =
backend_util.computeOutShape(inputs2D.map(t => t.shape), 1 /* axis */);
const simplyConcat = inputs2D[0].shape[0] === 1;
const outVals = concatImplCPU(inputsValShapes, outShape, inputs[0].dtype, simplyConcat);
const finalOutShape = backend_util.computeOutShape($inputs.map(t => t.shape), axis);
out.shape = finalOutShape;
const outData = backend.dataIdMap.get(out.dataId);
outData.stringBytes = backend_util.fromStringArrayToUint8(outVals);
inputs2D.forEach(t => backend.disposeData(t.dataId));
return out;
}
const batchDim = util.sizeFromShape($inputs[0].shape.slice(0, axis));
let sumInnerDims = 0;
const innerDims = $inputs.map(input => {
const innerDim = util.sizeFromShape(input.shape.slice(axis));
sumInnerDims += innerDim;
return innerDim;
});
const inVals = $inputs.map(input => backend.typedArrayFromHeap(input));
const outVals = backend.typedArrayFromHeap(out);
for (let b = 0; b < batchDim; b++) {
let outOffset = b * sumInnerDims;
for (let i = 0; i < inVals.length; i++) {
const innerDim = innerDims[i];
const inOffset = b * innerDim;
const vals = inVals[i].subarray(inOffset, inOffset + innerDim);
outVals.set(vals, outOffset);
outOffset += innerDim;
}
}
return out;
}
export const concatConfig = {
kernelName: Concat,
backendName: 'wasm',
kernelFunc: concat,
};
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQ29uY2F0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vdGZqcy1iYWNrZW5kLXdhc20vc3JjL2tlcm5lbHMvQ29uY2F0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7R0FlRztBQUVILE9BQU8sRUFBQyxZQUFZLEVBQUUsTUFBTSxFQUF1RCxJQUFJLEVBQUMsTUFBTSx1QkFBdUIsQ0FBQztBQUd0SCxPQUFPLEVBQUMsYUFBYSxFQUFDLE1BQU0sd0JBQXdCLENBQUM7QUFDckQsT0FBTyxFQUFDLFFBQVEsRUFBQyxNQUFNLFlBQVksQ0FBQztBQUNwQyxPQUFPLEVBQUMsT0FBTyxFQUFDLE1BQU0sV0FBVyxDQUFDO0FBRWxDLE1BQU0sVUFBVSxNQUFNLENBQ2xCLElBQXNFO0lBQ3hFLE1BQU0sRUFBQyxNQUFNLEVBQUUsT0FBTyxFQUFDLEdBQUcsSUFBSSxDQUFDO0lBRS9CLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxjQUFjLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBRXRFLE1BQU0sTUFBTSxHQUFHLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDeEMsWUFBWSxDQUFDLHNCQUFzQixDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsQ0FBQztJQUVsRCxJQUFJLFFBQVEsR0FBRyxZQUFZLENBQUMsZUFBZSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLEVBQUUsSUFBSSxDQUFDLENBQUM7SUFFNUUsc0VBQXNFO0lBQ3RFLE1BQU0sT0FBTyxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxJ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QUFDLElBQUksRUFBRSxTQUFTLENBQUMsQ0FBQztZQUM3QixTQUFTLElBQUksUUFBUSxDQUFDO1NBQ3ZCO0tBQ0Y7SUFDRCxPQUFPLEdBQUcsQ0FBQztBQUNiLENBQUM7QUFFRCxNQUFNLENBQUMsTUFBTSxZQUFZLEdBQWlCO0lBQ3hDLFVBQVUsRUFBRSxNQUFNO0lBQ2xCLFdBQVcsRUFBRSxNQUFNO0lBQ25CLFVBQVUsRUFBRSxNQUErQjtDQUM1QyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBAbGljZW5zZVxuICogQ29weXJpZ2h0IDIwMTkgR29vZ2xlIExMQy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG4gKiB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG4gKiBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiAqXG4gKiBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiAqXG4gKiBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4gKiBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4gKiBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiAqIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiAqIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuICogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqL1xuXG5pbXBvcnQge2JhY2tlbmRfdXRpbCwgQ29uY2F0LCBDb25jYXRBdHRycywgQ29uY2F0SW5wdXRzLCBLZXJuZWxDb25maWcsIEtlcm5lbEZ1bmMsIHV0aWx9IGZyb20gJ0B0ZW5zb3JmbG93L3RmanMtY29yZSc7XG5cbmltcG9ydCB7QmFja2VuZFdhc219IGZyb20gJy4uL2JhY2tlbmRfd2FzbSc7XG5pbXBvcnQge2NvbmNhdEltcGxDUFV9IGZyb20gJy4uL2tlcm5lbF91dGlscy9zaGFyZWQnO1xuaW1wb3J0IHtpZGVudGl0eX0gZnJvbSAnLi9JZGVudGl0eSc7XG5pbXBvcnQge3Jlc2hhcGV9IGZyb20gJy4vUmVzaGFwZSc7XG5cbmV4cG9ydCBmdW5jdGlvbiBjb25jYXQoXG4gICAgYXJnczoge2lucHV0czogQ29uY2F0SW5wdXRzLCBiYWNrZW5kOiBCYWNrZW5kV2FzbSwgYXR0cnM6IENvbmNhdEF0dHJzfSkge1xuICBjb25zdCB7aW5wdXRzLCBiYWNrZW5kfSA9IGFyZ3M7XG5cbiAgY29uc3QgYXhpcyA9IHV0aWwucGFyc2VBeGlzUGFyYW0oYXJncy5hdHRycy5heGlzLCBpbnB1dHNbMF0uc2hhcGUpWzBdO1xuXG4gIGNvbnN0IHNoYXBlcyA9IGlucHV0cy5tYXAodCA9PiB0LnNoYXBlKTtcbiAgYmFja2VuZF91dGlsLmFzc2VydFBhcmFtc0NvbnNpc3RlbnQoc2hhcGVzLCBheGlzKTtcblxuICBsZXQgb3V0U2hhcGUgPSBiYWNrZW5kX3V0aWwuY29tcHV0ZU91dFNoYXBlKGlucHV0cy5tYXAodCA9PiB0LnNoYXBlKSwgYXhpcyk7XG5cbiAgLy8gS2VlcCBvbmx5IG5vbi1lbXB0eSB0ZW5zb3JzIChpZ25vcmUgdGVuc29ycyB3aXRoIDAgaW4gdGhlaXIgc2hhcGUpLlxuICBjb25zdCAkaW5wdXRzID0gaW5wdXRzLmZpbHRlcih0ID0+IHV0aWwuc2l6ZUZyb21TaGFwZSh0LnNoYXBlKSA+IDApO1xuICBpZiAoJGlucHV0cy5sZW5ndGggPT09IDEpIHtcbiAgICByZXR1cm4gaWRlbnRpdHkoe2lucHV0czoge3g6ICRpbnB1dHNbMF19LCBiYWNrZW5kfSk7XG4gIH1cblxuICBjb25zdCBvdXQgPSBiYWNrZW5kLm1ha2VPdXRwdXQob3V0U2hhcGUsIGlucHV0c1swXS5kdHlwZSk7XG5cbiAgaWYgKHV0aWwuc2l6ZUZyb21TaGFwZShvdXRTaGFwZSkgPT09IDApIHtcbiAgICByZXR1cm4gb3V0O1xuICB9XG5cbiAgaWYgKCRpbnB1dHNbMF0uZHR5cGUgPT09ICdzdHJpbmcnKSB7XG4gICAgLy8gQW55IGNvbmNhdCBvZiBuLWRpbWVuc2lvbmFsIHRlbnNvcnMgYWNyb3NzIGFueSBheGlzIGNhbiBiZSByZWR1Y2VkIHRvXG4gICAgLy8gYSBjb25jYXRlbmF0aW9uIG9mIHR3by1kaW1lbnNpb25hbCB0ZW5zb3JzIGFjcm9zcyB0aGUgYXhpcyAxIGJ5IGZpcnN0XG4gICAgLy8gcGFydGl0aW9uaW5nIHRoZSBheGVzIG9mIHRoZSBvcmlnaW5hbCB0ZW5zb3JzIGludG8gdGhvc2UgbGVzcyB0aGFuIHRoZVxuICAgIC8vIGF4aXMgdG8gYmUgY29uY2F0ZW5hdGVkIGFuZCB0aGUgcmVzdC4gVGhlbiByZXNoYXBlIHRoZSB0ZW5zb3JzXG4gICAgLy8gaW50byBhIHR3by1kaW1lbnNpb25hbCB0ZW5zb3IgYnkgY29sbGFwc2luZyB0aGVzZSB0d28gc2V0cyBvZiBheGVzIGFuZFxuICAgIC8vIGNvbmNhdGVuYXRlIHRoZSByZXN1bHRpbmcgbWF0cmljZXMgYWNyb3NzIHRoZSBheGlzIDEsIGZpbmFsbHkgcmVzaGFwaW5nXG4gICAgLy8gdGhlIHJlc3VsdCB0byBoYXZlIHRoZSBwcm9wZXIgc2hhcGUuXG4gICAgY29uc3QgaW5wdXRzMkQgPSAkaW5wdXRzLm1hcCh0ID0+IHtcbiAgICAgIGNvbnN0IGlubmVyU2l6ZSA9IHV0aWwuc2l6ZUZyb21TaGFwZSh0LnNoYXBlLnNsaWNlKGF4aXMpKTtcbiAgICAgIGNvbnN0IHNoYXBlID0gWy0xLCBpbm5lclNpemVdO1xuICAgICAgcmV0dXJuIHJlc2hhcGUoe2lucHV0czoge3g6IHR9LCBiYWNrZW5kLCBhdHRyczoge3NoYXBlfX0pO1xuICAgIH0pO1xuXG4gICAgY29uc3QgaW5wdXRzVmFsU2hhcGVzID0gaW5wdXRzMkQubWFwKHQgPT4ge1xuICAgICAgcmV0dXJuIHt2YWxzOiBiYWNrZW5kLnJlYWRTeW5jKHQuZGF0YUlkKSwgc2hhcGU6IHQuc2hhcGV9O1xuICAgIH0pO1xuXG4gICAgLy8gQ29uY2F0cyAyZCB0ZW5zb3JzIGFsb25nIGF4aXM9MS5cbiAgICBvdXRTaGFwZSA9XG4gICAgICAgIGJhY2tlbmRfdXRpbC5jb21wdXRlT3V0U2hhcGUoaW5wdXRzMkQubWFwKHQgPT4gdC5zaGFwZSksIDEgLyogYXhpcyAqLyk7XG4gICAgY29uc3Qgc2ltcGx5Q29uY2F0ID0gaW5wdXRzMkRbMF0uc2hhcGVbMF0gPT09IDE7XG4gICAgY29uc3Qgb3V0VmFscyA9IGNvbmNhdEltcGxDUFUoXG4gICAgICAgICAgICAgICAgICAgICAgICBpbnB1dHNWYWxTaGFwZXMsIG91dFNoYXBlLCBpbnB1dHNbMF0uZHR5cGUsXG4gICAgICAgICAgICAgICAgICAgICAgICBzaW1wbHlDb25jYXQpIGFzIHN0cmluZ1tdO1xuXG4gICAgY29uc3QgZmluYWxPdXRTaGFwZSA9XG4gICAgICAgIGJhY2tlbmRfdXRpbC5jb21wdXRlT3V0U2hhcGUoJGlucHV0cy5tYXAodCA9PiB0LnNoYXBlKSwgYXhpcyk7XG5cbiAgICBvdXQuc2hhcGUgPSBmaW5hbE91dFNoYXBlO1xuICAgIGNvbnN0IG91dERhdGEgPSBiYWNrZW5kLmRhdGFJZE1hcC5nZXQob3V0LmRhdGFJZCk7XG4gICAgb3V0RGF0YS5zdHJpbmdCeXRlcyA9IGJhY2tlbmRfdXRpbC5mcm9tU3RyaW5nQXJyYXlUb1VpbnQ4KG91dFZhbHMpO1xuXG4gICAgaW5wdXRzMkQuZm9yRWFjaCh0ID0+IGJhY2tlbmQuZGlzcG9zZURhdGEodC5kYXRhSWQpKTtcblxuICAgIHJldHVybiBvdXQ7XG4gIH1cblxuICBjb25zdCBiYXRjaERpbSA9IHV0aWwuc2l6ZUZyb21TaGFwZSgkaW5wdXRzWzBdLnNoYXBlLnNsaWNlKDAsIGF4aXMpKTtcbiAgbGV0IHN1bUlubmVyRGltcyA9IDA7XG4gIGNvbnN0IGlubmVyRGltcyA9ICRpbnB1dHMubWFwKGlucHV0ID0+IHtcbiAgICBjb25zdCBpbm5lckRpbSA9IHV0aWwuc2l6ZUZyb21TaGFwZShpbnB1dC5zaGFwZS5zbGljZShheGlzKSk7XG4gICAgc3VtSW5uZXJEaW1zICs9IGlubmVyRGltO1xuICAgIHJldHVybiBpbm5lckRpbTtcbiAgfSk7XG4gIGNvbnN0IGluVmFscyA9ICRpbnB1dHMubWFwKGlucHV0ID0+IGJhY2tlbmQudHlwZWRBcnJheUZyb21IZWFwKGlucHV0KSk7XG4gIGNvbnN0IG91dFZhbHMgPSBiYWNrZW5kLnR5cGVkQXJyYXlGcm9tSGVhcChvdXQpO1xuICBmb3IgKGxldCBiID0gMDsgYiA8IGJhdGNoRGltOyBiKyspIHtcbiAgICBsZXQgb3V0T2Zmc2V0ID0gYiAqIHN1bUlubmVyRGltcztcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGluVmFscy5sZW5ndGg7IGkrKykge1xuICAgICAgY29uc3QgaW5uZXJEaW0gPSBpbm5lckRpbXNbaV07XG4gICAgICBjb25zdCBpbk9mZnNldCA9IGIgKiBpbm5lckRpbTtcbiAgICAgIGNvbnN0IHZhbHMgPSBpblZhbHNbaV0uc3ViYXJyYXkoaW5PZmZzZXQsIGluT2Zmc2V0ICsgaW5uZXJEaW0pO1xuICAgICAgb3V0VmFscy5zZXQodmFscywgb3V0T2Zmc2V0KTtcbiAgICAgIG91dE9mZnNldCArPSBpbm5lckRpbTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIG91dDtcbn1cblxuZXhwb3J0IGNvbnN0IGNvbmNhdENvbmZpZzogS2VybmVsQ29uZmlnID0ge1xuICBrZXJuZWxOYW1lOiBDb25jYXQsXG4gIGJhY2tlbmROYW1lOiAnd2FzbScsXG4gIGtlcm5lbEZ1bmM6IGNvbmNhdCBhcyB1bmtub3duIGFzIEtlcm5lbEZ1bmMsXG59O1xuIl19