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
199 lines
7.1 KiB
JavaScript
199 lines
7.1 KiB
JavaScript
|
|
const kann = {};
|
|
|
|
kann.ModelFactory = class {
|
|
|
|
async match(context) {
|
|
const reader = await context.peek('flatbuffers.binary');
|
|
if (reader && reader.identifier === 'KaNN') {
|
|
return context.set('kann.flatbuffers', reader);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
async open(context) {
|
|
kann.schema = await context.require('./kann-schema');
|
|
kann.schema = kann.schema.kann;
|
|
let model = null;
|
|
switch (context.type) {
|
|
case 'kann.flatbuffers': {
|
|
try {
|
|
const reader = context.value;
|
|
model = kann.schema.Model.create(reader);
|
|
} catch (error) {
|
|
const message = error && error.message ? error.message : error.toString();
|
|
throw new kann.Error(`File format is not kann.Model (${message.replace(/\.$/, '')}).`);
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
throw new kann.Error(`Unsupported KaNN format '${context.type}'.`);
|
|
}
|
|
}
|
|
const metadata = await context.metadata('kann-metadata.json');
|
|
return new kann.Model(metadata, model, context.identifier);
|
|
}
|
|
};
|
|
|
|
kann.Model = class {
|
|
|
|
constructor(metadata, model, identifier) {
|
|
this.format = 'KaNN';
|
|
this.name = identifier;
|
|
this.modules = model.graph.map((graph) => new kann.Graph(metadata, graph));
|
|
}
|
|
};
|
|
|
|
kann.Graph = class {
|
|
|
|
constructor(metadata, graph) {
|
|
const arcs = new Map();
|
|
for (const arc of graph.arcs) {
|
|
arcs.set(arc.name, new kann.Value(arc.name, arc.type, null));
|
|
}
|
|
this.nodes = graph.nodes.map((node) => new kann.Node(metadata, node, arcs));
|
|
this.inputs = graph.inputs.map((input) => new kann.Argument(input, [arcs.get(input)]));
|
|
this.outputs = graph.outputs.map((output) => new kann.Argument(output, [arcs.get(output)]));
|
|
}
|
|
};
|
|
|
|
kann.Node = class {
|
|
|
|
constructor(metadata, node, arcs) {
|
|
this.type = metadata.type(node.type);
|
|
this.name = node.name;
|
|
this.inputs = [];
|
|
this.outputs = [];
|
|
this.attributes = [];
|
|
const extractData = (value) => {
|
|
switch (value.type) {
|
|
case 'int': case 'int8': case 'int16': case 'int32': case 'int64': return value.value_int;
|
|
case 'uint': case 'uint8': case 'uint16': case 'uint32': case 'uint64': return value.value_uint;
|
|
case 'float': case 'float16': case 'float32': case 'float64': return value.value_float;
|
|
case 'string': return value.value_string;
|
|
case 'int[]': case 'int8[]': case 'int16[]': case 'int32[]': case 'int64[]': return Array.from(value.list_int);
|
|
case 'uint[]': case 'uint8[]': case 'uint16[]': case 'uint32[]': case 'uint64[]': return Array.from(value.list_uint);
|
|
case 'float[]': case 'float16[]': case 'float32[]': case 'float64[]': return Array.from(value.list_float);
|
|
case 'string[]': return Array.from(value.list_string);
|
|
default: throw new kann.Error(`Unsupported data type '${value.type}'.`);
|
|
}
|
|
};
|
|
const getAttributeValue = (attribute) => {
|
|
if (attribute.type === 'attributes') {
|
|
const obj = {};
|
|
for (const attr of attribute.attributes) {
|
|
obj[attr.name] = getAttributeValue(attr);
|
|
}
|
|
return obj;
|
|
}
|
|
if (attribute.value !== null) {
|
|
return extractData(attribute.value);
|
|
}
|
|
throw new kann.Error(`${attribute.name} doesn't have a value.`);
|
|
};
|
|
if (Array.isArray(node.attributes) && node.attributes.length > 0) {
|
|
for (const attr of node.attributes) {
|
|
let value = attr.type ? getAttributeValue(attr) : attr;
|
|
value = Array.isArray(value) ? value : [value];
|
|
const type = value.type === 'attributes' ? null : attr.type || null;
|
|
const attribute = new kann.Argument(attr.name, value, type);
|
|
this.attributes.push(attribute);
|
|
}
|
|
}
|
|
if (Array.isArray(node.inputs) && node.inputs.length > 0) {
|
|
const name = node.inputs.length > 1 ? 'inputs' : 'input';
|
|
const argument = new kann.Argument(name, node.inputs.map((input) => arcs.get(input)));
|
|
this.inputs.push(argument);
|
|
}
|
|
if (Array.isArray(node.outputs) && node.outputs.length > 0) {
|
|
const name = node.outputs.length > 1 ? 'outputs' : 'output';
|
|
const argument = new kann.Argument(name, node.outputs.map((output) => arcs.get(output)));
|
|
this.outputs.push(argument);
|
|
}
|
|
if (Array.isArray(node.params) && node.params.length > 0) {
|
|
for (const param of node.params) {
|
|
const type = new kann.TensorType(param.type, param.shape);
|
|
const data = param.value ? extractData(param.value) : null;
|
|
const quantization = param.scale && param.zero_point ? {
|
|
type: 'linear',
|
|
scale: extractData(param.scale),
|
|
offset: extractData(param.zero_point)
|
|
} : null;
|
|
const tensor = new kann.Tensor(param.name, type, data, quantization);
|
|
const value = new kann.Value('', type, tensor);
|
|
const argument = new kann.Argument(param.name, [value]);
|
|
this.inputs.push(argument);
|
|
}
|
|
}
|
|
if (node.relu) {
|
|
const relu = { type: 'ReLU', name: `${node.name}/relu`, params: [] };
|
|
this.chain = [new kann.Node(metadata, relu, arcs)];
|
|
}
|
|
}
|
|
};
|
|
|
|
kann.Argument = class {
|
|
|
|
constructor(name, value, type = null) {
|
|
this.name = name;
|
|
this.value = value;
|
|
this.type = type;
|
|
}
|
|
};
|
|
|
|
kann.Value = class {
|
|
|
|
constructor(name, type, initializer) {
|
|
this.name = name;
|
|
this.type = type;
|
|
this.initializer = initializer;
|
|
this.quantization = initializer && initializer.quantization ? initializer.quantization : null;
|
|
}
|
|
};
|
|
|
|
kann.Tensor = class {
|
|
|
|
constructor(name, type, values, quantization) {
|
|
this.name = name;
|
|
this.type = type;
|
|
this.encoding = Array.isArray(values) ? '|' : '<';
|
|
this.values = values;
|
|
this.quantization = quantization ? quantization : null;
|
|
}
|
|
};
|
|
|
|
kann.TensorType = class {
|
|
|
|
constructor(dataType, shape) {
|
|
this.dataType = dataType || '?';
|
|
this.shape = new kann.TensorShape(shape);
|
|
}
|
|
|
|
toString() {
|
|
return this.dataType + this.shape.toString();
|
|
}
|
|
};
|
|
|
|
kann.TensorShape = class {
|
|
|
|
constructor(dimensions) {
|
|
this.dimensions = Array.from(dimensions);
|
|
}
|
|
|
|
toString() {
|
|
if (Array.isArray(this.dimensions) && this.dimensions.length > 0) {
|
|
return `[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]`;
|
|
}
|
|
return '';
|
|
}
|
|
};
|
|
|
|
kann.Error = class extends Error {
|
|
|
|
constructor(message) {
|
|
super(message);
|
|
this.name = 'Error loading KaNN model.';
|
|
}
|
|
};
|
|
|
|
export const ModelFactory = kann.ModelFactory; |