| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531 |
- /* jshint esversion: 6 */
- /* eslint "indent": [ "error", 4, { "SwitchCase": 1 } ] */
- var acuity = acuity || {};
- var json = json || require('./json');
- acuity.ModelFactory = class {
- match(context) {
- const extension = context.identifier.split('.').pop().toLowerCase();
- if (extension === 'json') {
- const obj = context.open('json');
- if (obj && obj.MetaData && obj.Layers) {
- return true;
- }
- }
- return false;
- }
- open(context) {
- return acuity.Metadata.open(context).then((metadata) => {
- const extension = context.identifier.split('.').pop().toLowerCase();
- switch (extension) {
- case 'json': {
- const obj = context.open('json');
- if (obj && obj.MetaData && obj.Layers) {
- return new acuity.Model(metadata, obj);
- }
- }
- }
- });
- }
- };
- acuity.Model = class {
- constructor(metadata, model, data, quantization) {
- this._name = model.MetaData.Name;
- this._format = 'Acuity ' + 'v' + model.MetaData.AcuityVersion;
- this._runtime = model.MetaData.Platform;
- this._graphs = [ new acuity.Graph(metadata, model, data, quantization) ];
- }
- get format() {
- return this._format;
- }
- get name() {
- return this._name;
- }
- get runtime() {
- return this._runtime;
- }
- get graphs() {
- return this._graphs;
- }
- };
- acuity.Graph = class {
- constructor(metadata, model) {
- this._nodes = [];
- this._inputs = [];
- this._outputs = [];
- const args = new Map();
- const arg = (name) => {
- if (!args.has(name)) {
- args.set(name, { name: name, shape: null });
- }
- return args.get(name);
- };
- for (const layerName of Object.keys(model.Layers)) {
- const layer = model.Layers[layerName];
- layer.inputs = layer.inputs.map((input) => {
- return arg(input);
- });
- layer.outputs = layer.outputs.map((port) => {
- const argument = arg("@" + layerName + ":" + port);
- let shape = null;
- if (layer.op.toLowerCase() == 'input' ||
- layer.op.toLowerCase() == 'variable') {
- if (Object.prototype.hasOwnProperty.call(layer.parameters, 'shape') && layer.parameters.shape.length > 0) {
- shape = layer.parameters.shape;
- }
- else if (Object.prototype.hasOwnProperty.call(layer.parameters, 'size') && Object.prototype.hasOwnProperty.call(layer.parameters, 'channels')) {
- const sizes = layer.parameters.size.split(' ');
- shape = [0, parseInt(sizes[0]), parseInt(sizes[1]), layer.parameters.channels];
- }
- }
- argument.shape = shape;
- return argument;
- });
- }
- new acuity.Inference(model.Layers);
- for (const pair of args) {
- const type = new acuity.TensorType(null, new acuity.TensorShape(pair[1].shape));
- const arg = new acuity.Argument(pair[0], type, null, null);
- args.set(pair[0], arg);
- }
- for (const layerName of Object.keys(model.Layers)) {
- const layer = model.Layers[layerName];
- switch (layer.op.toLowerCase()) {
- case 'input': {
- this._inputs.push(new acuity.Parameter(layerName, true, [
- args.get(layer.outputs[0].name)
- ]));
- break;
- }
- case 'output': {
- this._outputs.push(new acuity.Parameter(layerName, true, [
- args.get(layer.inputs[0].name)
- ]));
- break;
- }
- default: {
- this._nodes.push(new acuity.Node(metadata, layerName, layer, args));
- break;
- }
- }
- }
- }
- get inputs() {
- return this._inputs;
- }
- get outputs() {
- return this._outputs;
- }
- get nodes() {
- return this._nodes;
- }
- };
- acuity.Node = class {
- constructor(metadata, name, layer, args) {
- this._name = name;
- this._type = metadata.type(layer.op) || { name: layer.op };
- this._inputs = [];
- this._outputs = [];
- this._attributes = [];
- this._layer = layer;
- if (this._type) {
- if (layer.parameters) {
- for (const key of Object.keys(layer.parameters)) {
- const attributeMetadata = metadata.attribute(this._type, key);
- this._attributes.push(new acuity.Attribute(attributeMetadata, key, layer.parameters[key]));
- }
- }
- }
- for (let i = 0; i < layer.inputs.length; i++) {
- const input = layer.inputs[i];
- const arg = args.get(input.name);
- const name = this._type && this._type.inputs && i < this._type.inputs.length ? this._type.inputs[i].name : 'input' + i.toString();
- this._inputs.push(new acuity.Parameter(name, true, [ arg ]));
- }
- if (this._type && this._type.constants) {
- for (const constant of this._type.constants) {
- // const name = "@" + this._name + ":" + constant.name;
- const type = new acuity.TensorType(null, new acuity.TensorShape(null));
- const argument = new acuity.Argument('', type, null, new acuity.Tensor(type));
- this._inputs.push(new acuity.Parameter(constant.name, true, [ argument ]));
- }
- }
- for (let i = 0; i < layer.outputs.length; i++) {
- const output = layer.outputs[i];
- const arg = args.get(output.name);
- const name = this._type && this._type.outputs && i < this._type.outputs.length ? this._type.outputs[i].name : 'output' + i.toString();
- this._outputs.push(new acuity.Parameter(name, true, [arg]));
- }
- }
- get type() {
- return this._type;
- }
- get name() {
- return this._name;
- }
- get inputs() {
- return this._inputs;
- }
- get outputs() {
- return this._outputs;
- }
- get attributes() {
- return this._attributes;
- }
- };
- acuity.Attribute = class {
- constructor(metadata, name, value) {
- this._type = null;
- this._name = name;
- this._value = value;
- if (metadata) {
- this._type = metadata.type || null;
- if (Object.prototype.hasOwnProperty.call(metadata, 'default')) {
- if (metadata.default === value) {
- this._visible = false;
- }
- }
- }
- }
- get name() {
- return this._name;
- }
- get type() {
- return this._type;
- }
- get value() {
- return this._value;
- }
- get visible() {
- return this._visible == false ? false : true;
- }
- };
- acuity.Parameter = class {
- constructor(name, visible, args) {
- this._name = name;
- this._visible = visible;
- this._arguments = args;
- if (this._arguments.some((arg) => !arg)) {
- throw "";
- }
- }
- get name() {
- return this._name;
- }
- get visible() {
- return this._visible;
- }
- get arguments() {
- return this._arguments;
- }
- };
- acuity.Argument = class {
- constructor(name, type, quantization, initializer) {
- if (typeof name !== 'string') {
- throw new acuity.Error("Invalid argument identifier '" + JSON.stringify(name) + "'.");
- }
- this._name = name;
- this._type = type || null;
- this._quantization = quantization || null;
- this._initializer = initializer || null;
- }
- get name() {
- return this._name;
- }
- get type() {
- return this._type;
- }
- get quantization() {
- return this._quantization;
- }
- set quantization(quantization) {
- this._quantization = quantization;
- }
- get initializer() {
- return this._initializer;
- }
- set initializer(initializer) {
- this._initializer = initializer;
- }
- };
- acuity.TensorType = class {
- constructor(dataType, shape) {
- this._dataType = dataType || '?';
- this._shape = shape;
- }
- get dataType() {
- return this._dataType;
- }
- set dataType(dataType) {
- this._dataType = dataType;
- }
- get shape() {
- return this._shape;
- }
- set shape(shape) {
- this._shape = shape;
- }
- toString() {
- return (this.dataType || '?') + this._shape.toString();
- }
- };
- acuity.TensorShape = class {
- constructor(dimensions) {
- this._dimensions = dimensions || null;
- }
- get dimensions() {
- return this._dimensions;
- }
- set dimensions(dimensions) {
- this._dimensions = dimensions;
- }
- toString() {
- if (!this._dimensions || this._dimensions.length == 0) {
- return '';
- }
- return '[' + this._dimensions.map((dimension) => dimension.toString()).join(',') + ']';
- }
- };
- acuity.Tensor = class {
- constructor(type) {
- this._type = type;
- }
- get kind() {
- return 'Constant';
- }
- get type() {
- return this._type;
- }
- get state() {
- return 'Not supported.';
- }
- toString() {
- return '';
- }
- };
- acuity.Metadata = class {
- static open(context) {
- if (acuity.Metadata._metadata) {
- return Promise.resolve(acuity.Metadata._metadata);
- }
- return context.request('acuity-metadata.json', 'utf-8', null).then((data) => {
- acuity.Metadata._metadata = new acuity.Metadata(data);
- return acuity.Metadata._metadata;
- }).catch(() => {
- acuity.Metadata._metadata = new acuity.Metadata(null);
- return acuity.Metadata._metadata;
- });
- }
- constructor(data) {
- this._map = new Map();
- if (data) {
- const metadata = JSON.parse(data);
- this._map = new Map(metadata.map((item) => [ item.name, item ]));
- }
- }
- type(name) {
- return this._map.get(name);
- }
- attribute(type, name) {
- const schema = this.type(type);
- if (schema) {
- let attributeMap = schema.attributeMap;
- if (!attributeMap) {
- attributeMap = {};
- if (schema.attributes) {
- for (const attribute of schema.attributes) {
- attributeMap[attribute.name] = attribute;
- }
- }
- schema.attributeMap = attributeMap;
- }
- const attributeSchema = attributeMap[name];
- if (attributeSchema) {
- return attributeSchema;
- }
- }
- return null;
- }
- };
- acuity.Inference = class {
- constructor(layers) {
- this._outputs = new Map();
- const outputLayers = [];
- for (const layerName of Object.keys(layers)) {
- const layer = layers[layerName];
- if (layer.op.toLowerCase() == 'output') {
- outputLayers.push(layer);
- }
- for (const output of layer.outputs) {
- this._outputs.set(output.name, layer);
- }
- }
- this._passthroughs = new Set([
- 'a_times_b_plus_c', 'abs', 'cast', 'clipbyvalue', 'dequantize', 'dtype_converter',
- 'elu', 'exp', 'floor', 'floor_div', 'hard_swish', 'leakyrelu', 'log', 'log_softmax',
- 'neg', 'pow', 'prelu', 'quantize', 'relu', 'relu_keras', 'relun', 'rsqrt', 'sigmoid',
- 'sin', 'softmax', 'softrelu', 'sqrt', 'square', 'tanh'
- ]);
- this._operators = new Map();
- this._operators.set('concat', (inputs, parameters) => {
- const outputShape = inputs[0].slice();
- outputShape[parameters.dim] = 0;
- for (const shape of inputs) {
- outputShape[parameters.dim] += shape[parameters.dim];
- }
- return [outputShape];
- });
- this._operators.set('convolution', (inputs, parameters) => {
- if (parameters.padding == 'VALID') {
- const out_h = ~~((inputs[0][1] + parameters.stride_h - parameters.ksize_h) / parameters.stride_h);
- const out_w = ~~((inputs[0][2] + parameters.stride_w - parameters.ksize_w) / parameters.stride_w);
- return [[inputs[0][0], out_h, out_w, parameters.weights]];
- }
- else if (parameters.padding == 'SAME') {
- const out_h = ~~((inputs[0][1] + parameters.stride_h - 1) / parameters.stride_h);
- const out_w = ~~((inputs[0][2] + parameters.stride_w - 1) / parameters.stride_w);
- return [[inputs[0][0], out_h, out_w, parameters.weights]];
- }
- });
- this._operators.set('fullconnect', (inputs, parameters) => {
- return [inputs[0].slice(0, parameters.axis).concat([parameters.weights])];
- });
- this._operators.set('pooling', (inputs, parameters) => {
- if (parameters.padding == 'VALID') {
- const out_h = ~~((inputs[0][1] + parameters.stride_h - parameters.ksize_h) / parameters.stride_h);
- const out_w = ~~((inputs[0][2] + parameters.stride_w - parameters.ksize_w) / parameters.stride_w);
- return [[inputs[0][0], out_h, out_w, inputs[0][3]]];
- }
- else if (parameters.padding == 'SAME') {
- const out_h = ~~((inputs[0][1] + parameters.stride_h - 1) / parameters.stride_h);
- const out_w = ~~((inputs[0][2] + parameters.stride_w - 1) / parameters.stride_w);
- return [[inputs[0][0], out_h, out_w, inputs[0][3]]];
- }
- });
- for (const layer of outputLayers) {
- for (const output of layer.outputs) {
- this._infer(output);
- }
- }
- }
- _infer(output) {
- if (this._outputs.has(output.name)) {
- let inputShapeReady = true;
- const layer = this._outputs.get(output.name);
- for (const input of layer.inputs) {
- if (input.shape === null) {
- this._infer(input);
- if (input.shape === null) {
- inputShapeReady = false;
- break;
- }
- }
- }
- if (inputShapeReady) {
- let callback = null;
- if (this._operators.has(layer.op)) {
- callback = this._operators.get(layer.op);
- }
- else if (this._passthroughs.has(layer.op)) {
- callback = (inputs) => [ inputs[0].slice() ];
- }
- else {
- callback = () => [];
- }
- const parameters = layer.parameters;
- const inputs = layer.inputs.map((input) => input.shape);
- const outputs = callback(inputs, parameters);
- for (let i = 0; i < outputs.length; i++) {
- if (i < layer.outputs.length) {
- layer.outputs[i].shape = outputs[i];
- }
- }
- }
- }
- }
- };
- acuity.Error = class extends Error {
- constructor(message) {
- super(message);
- this.name = 'Error loading Acuity model.';
- }
- };
- if (typeof module !== 'undefined' && typeof module.exports === 'object') {
- module.exports.ModelFactory = acuity.ModelFactory;
- }
|