7254f7b4d1
Build / Build (macos-latest) (push) Has been cancelled
Build / Build (ubuntu-latest) (push) Has been cancelled
Build / Build (windows-latest) (push) Has been cancelled
Build / Analyze (javascript) (push) Has been cancelled
Build / Analyze (python) (push) Has been cancelled
295 lines
11 KiB
JavaScript
295 lines
11 KiB
JavaScript
|
|
const armnn = {};
|
|
|
|
armnn.ModelFactory = class {
|
|
|
|
async match(context) {
|
|
const identifier = context.identifier;
|
|
const extension = identifier.lastIndexOf('.') > 0 ? identifier.split('.').pop().toLowerCase() : '';
|
|
if (extension === 'armnn') {
|
|
const reader = await context.peek('flatbuffers.binary');
|
|
if (reader) {
|
|
return context.set('armnn.flatbuffers', reader);
|
|
}
|
|
}
|
|
if (extension === 'json') {
|
|
const obj = await context.peek('json');
|
|
if (obj && obj.layers && obj.inputIds && obj.outputIds) {
|
|
return context.set('armnn.flatbuffers.json', obj);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
async open(context) {
|
|
armnn.schema = await context.require('./armnn-schema');
|
|
armnn.schema = armnn.schema.armnnSerializer;
|
|
let model = null;
|
|
switch (context.type) {
|
|
case 'armnn.flatbuffers': {
|
|
try {
|
|
const reader = await context.read('flatbuffers.binary');
|
|
model = armnn.schema.SerializedGraph.create(reader);
|
|
} catch (error) {
|
|
const message = error && error.message ? error.message : error.toString();
|
|
throw new armnn.Error(`File format is not armnn.SerializedGraph (${message.replace(/\.$/, '')}).`);
|
|
}
|
|
break;
|
|
}
|
|
case 'armnn.flatbuffers.json': {
|
|
try {
|
|
const reader = await context.read('flatbuffers.text');
|
|
model = armnn.schema.SerializedGraph.createText(reader);
|
|
} catch (error) {
|
|
const message = error && error.message ? error.message : error.toString();
|
|
throw new armnn.Error(`File text format is not armnn.SerializedGraph (${message.replace(/\.$/, '')}).`);
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
throw new armnn.Error(`Unsupported Arm NN format '${context.type}'.`);
|
|
}
|
|
}
|
|
const metadata = await context.metadata('armnn-metadata.json');
|
|
return new armnn.Model(metadata, model);
|
|
}
|
|
};
|
|
|
|
armnn.Model = class {
|
|
|
|
constructor(metadata, model) {
|
|
this.format = 'Arm NN';
|
|
this.modules = [new armnn.Graph(metadata, model)];
|
|
}
|
|
};
|
|
|
|
armnn.Graph = class {
|
|
|
|
constructor(metadata, graph) {
|
|
this.name = '';
|
|
this.nodes = [];
|
|
this.inputs = [];
|
|
this.outputs = [];
|
|
const counts = new Map();
|
|
for (const layer of graph.layers) {
|
|
const base = armnn.Node.getBase(layer);
|
|
for (const slot of base.inputSlots) {
|
|
const name = `${slot.connection.sourceLayerIndex}:${slot.connection.outputSlotIndex}`;
|
|
counts.set(name, counts.has(name) ? counts.get(name) + 1 : 1);
|
|
}
|
|
}
|
|
const values = new Map();
|
|
const value = (layerIndex, slotIndex, tensor) => {
|
|
const name = `${layerIndex}:${slotIndex}`;
|
|
if (!values.has(name)) {
|
|
const layer = graph.layers[layerIndex];
|
|
const base = layerIndex < graph.layers.length ? armnn.Node.getBase(layer) : null;
|
|
const tensorInfo = base && slotIndex < base.outputSlots.length ? base.outputSlots[slotIndex].tensorInfo : null;
|
|
values.set(name, new armnn.Value(name, tensorInfo, tensor));
|
|
}
|
|
return values.get(name);
|
|
};
|
|
const layers = graph.layers.filter((layer) => {
|
|
const base = armnn.Node.getBase(layer);
|
|
if (base.layerType === armnn.schema.LayerType.Constant && base.outputSlots.length === 1 && layer.layer.input) {
|
|
const [slot] = base.outputSlots;
|
|
const name = `${base.index}:${slot.index}`;
|
|
if (counts.get(name) === 1) {
|
|
const tensor = new armnn.Tensor(layer.layer.input, 'Constant');
|
|
value(base.index, slot.index, tensor);
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
});
|
|
for (const layer of layers) {
|
|
const base = armnn.Node.getBase(layer);
|
|
for (const slot of base.inputSlots) {
|
|
value(slot.connection.sourceLayerIndex, slot.connection.outputSlotIndex);
|
|
}
|
|
}
|
|
for (const layer of layers) {
|
|
const base = armnn.Node.getBase(layer);
|
|
switch (base.layerType) {
|
|
case armnn.schema.LayerType.Input: {
|
|
const name = base ? base.layerName : '';
|
|
for (const slot of base.outputSlots) {
|
|
const argument = new armnn.Argument(name, [value(base.index, slot.index)]);
|
|
this.inputs.push(argument);
|
|
}
|
|
break;
|
|
}
|
|
case armnn.schema.LayerType.Output: {
|
|
const base = armnn.Node.getBase(layer);
|
|
const name = base ? base.layerName : '';
|
|
for (const slot of base.inputSlots) {
|
|
const argument = new armnn.Argument(name, [value(slot.connection.sourceLayerIndex, slot.connection.outputSlotIndex)]);
|
|
this.outputs.push(argument);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
this.nodes.push(new armnn.Node(metadata, layer, value));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
armnn.Node = class {
|
|
|
|
constructor(metadata, layer, value) {
|
|
const name = layer.layer.constructor.name;
|
|
const type = metadata.type(name);
|
|
this.type = type ? { ...type } : { name };
|
|
this.type.name = this.type.name.replace(/Layer$/, '');
|
|
this.name = '';
|
|
this.outputs = [];
|
|
this.inputs = [];
|
|
this.attributes = [];
|
|
const inputSchemas = (this.type && this.type.inputs) ? [...this.type.inputs] : [{ name: 'input' }];
|
|
const outputSchemas = (this.type && this.type.outputs) ? [...this.type.outputs] : [{ name: 'output' }];
|
|
const base = armnn.Node.getBase(layer);
|
|
if (base) {
|
|
this.name = base.layerName;
|
|
const inputs = [...base.inputSlots];
|
|
while (inputs.length > 0) {
|
|
const schema = inputSchemas.length > 0 ? inputSchemas.shift() : { name: '?' };
|
|
const count = schema.list ? inputs.length : 1;
|
|
const argument = new armnn.Argument(schema.name, inputs.splice(0, count).map((inputSlot) => {
|
|
return value(inputSlot.connection.sourceLayerIndex, inputSlot.connection.outputSlotIndex);
|
|
}));
|
|
this.inputs.push(argument);
|
|
}
|
|
const outputs = [...base.outputSlots];
|
|
while (outputs.length > 0) {
|
|
const schema = outputSchemas.length > 0 ? outputSchemas.shift() : { name: '?' };
|
|
const count = schema.list ? outputs.length : 1;
|
|
this.outputs.push(new armnn.Argument(schema.name, outputs.splice(0, count).map((outputSlot) => {
|
|
return value(base.index, outputSlot.index);
|
|
})));
|
|
}
|
|
}
|
|
if (layer.layer) {
|
|
if (layer.layer.descriptor && this.type.attributes) {
|
|
for (const [key, obj] of Object.entries(layer.layer.descriptor)) {
|
|
const schema = metadata.attribute(name, key);
|
|
const type = schema ? schema.type : null;
|
|
let value = ArrayBuffer.isView(obj) ? Array.from(obj) : obj;
|
|
const enumType = armnn.schema[type];
|
|
if (enumType) {
|
|
value = enumType[value] || value;
|
|
}
|
|
const attribute = new armnn.Argument(key, value, type);
|
|
this.attributes.push(attribute);
|
|
}
|
|
}
|
|
for (const [name, tensor] of Object.entries(layer.layer).filter(([, value]) => value instanceof armnn.schema.ConstTensor)) {
|
|
const value = new armnn.Value('', tensor.info, new armnn.Tensor(tensor));
|
|
const argument = new armnn.Argument(name, [value]);
|
|
this.inputs.push(argument);
|
|
}
|
|
}
|
|
}
|
|
|
|
static getBase(layer) {
|
|
return layer.layer.base.base ? layer.layer.base.base : layer.layer.base;
|
|
}
|
|
|
|
static makeKey(layer_id, index) {
|
|
return `${layer_id}_${index}`;
|
|
}
|
|
};
|
|
|
|
armnn.Argument = class {
|
|
|
|
constructor(name, value, type = null) {
|
|
this.name = name;
|
|
this.value = value;
|
|
this.type = type;
|
|
}
|
|
};
|
|
|
|
armnn.Value = class {
|
|
|
|
constructor(name, tensorInfo, initializer) {
|
|
if (typeof name !== 'string') {
|
|
throw new armnn.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
|
|
}
|
|
this.name = name;
|
|
this.type = new armnn.TensorType(tensorInfo);
|
|
this.initializer = initializer;
|
|
if (tensorInfo.quantizationScale !== 0 ||
|
|
tensorInfo.quantizationOffset !== 0 ||
|
|
tensorInfo.quantizationScales.length > 0 ||
|
|
tensorInfo.quantizationDim !== 0) {
|
|
this.quantization = {
|
|
type: 'linear',
|
|
dimension: tensorInfo.quantizationDim,
|
|
scale: [tensorInfo.quantizationScale],
|
|
offset: [tensorInfo.quantizationOffset]
|
|
};
|
|
}
|
|
}
|
|
};
|
|
|
|
armnn.Tensor = class {
|
|
|
|
constructor(tensor, category = '') {
|
|
this.type = new armnn.TensorType(tensor.info);
|
|
this.category = category;
|
|
const data = tensor.data.data.slice(0);
|
|
this.values = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
}
|
|
};
|
|
|
|
armnn.TensorType = class {
|
|
|
|
constructor(tensorInfo) {
|
|
const dataType = tensorInfo.dataType;
|
|
switch (dataType) {
|
|
case 0: this.dataType = 'float16'; break;
|
|
case 1: this.dataType = 'float32'; break;
|
|
case 2: this.dataType = 'quint8'; break; // QuantisedAsymm8
|
|
case 3: this.dataType = 'int32'; break;
|
|
case 4: this.dataType = 'boolean'; break;
|
|
case 5: this.dataType = 'qint16'; break; // QuantisedSymm16
|
|
case 6: this.dataType = 'quint8'; break; // QAsymmU8
|
|
case 7: this.dataType = 'qint16'; break; // QSymmS16
|
|
case 8: this.dataType = 'qint8'; break; // QAsymmS8
|
|
case 9: this.dataType = 'qint8'; break; // QSymmS8
|
|
default:
|
|
throw new armnn.Error(`Unsupported data type '${JSON.stringify(dataType)}'.`);
|
|
}
|
|
this.shape = new armnn.TensorShape(tensorInfo.dimensions);
|
|
}
|
|
|
|
toString() {
|
|
return this.dataType + this.shape.toString();
|
|
}
|
|
};
|
|
|
|
armnn.TensorShape = class {
|
|
|
|
constructor(dimensions) {
|
|
this.dimensions = Array.from(dimensions);
|
|
}
|
|
|
|
toString() {
|
|
if (!this.dimensions || this.dimensions.length === 0) {
|
|
return '';
|
|
}
|
|
return `[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]`;
|
|
}
|
|
};
|
|
|
|
armnn.Error = class extends Error {
|
|
|
|
constructor(message) {
|
|
super(message);
|
|
this.name = 'Error loading Arm NN model.';
|
|
}
|
|
};
|
|
|
|
export const ModelFactory = armnn.ModelFactory;
|