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
449 lines
15 KiB
JavaScript
Executable File
449 lines
15 KiB
JavaScript
Executable File
|
|
// Experimental
|
|
|
|
const barracuda = {};
|
|
|
|
barracuda.ModelFactory = class {
|
|
|
|
async match(context) {
|
|
const stream = context.stream;
|
|
if (stream && stream.length > 12) {
|
|
const buffer = stream.peek(12);
|
|
if (buffer[0] <= 0x20 && buffer.subarray(1, 8).every((value) => value === 0x00)) {
|
|
return context.set('barracuda');
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
async open(context) {
|
|
const metadata = barracuda.Metadata.open();
|
|
const reader = await context.read('binary');
|
|
const model = new barracuda.NNModel(reader);
|
|
return new barracuda.Model(metadata, model);
|
|
}
|
|
};
|
|
|
|
barracuda.Model = class {
|
|
|
|
constructor(metadata, model) {
|
|
const version = model.version.toString();
|
|
this.format = `Barracuda v${version}`;
|
|
this.modules = [new barracuda.Graph(metadata, model)];
|
|
}
|
|
};
|
|
|
|
barracuda.Graph = class {
|
|
|
|
constructor(metadata, model) {
|
|
this.name = '';
|
|
this.inputs = [];
|
|
this.outputs = [];
|
|
this.nodes = [];
|
|
const values = new Map();
|
|
values.map = (name, type, tensor) => {
|
|
if (!values.has(name)) {
|
|
type = tensor ? tensor.type : type;
|
|
values.set(name, new barracuda.Value(name, type, tensor));
|
|
} else if (type || tensor) {
|
|
throw new barracuda.Error(`Duplicate value '${name}'.`);
|
|
}
|
|
return values.get(name);
|
|
};
|
|
const layers = [];
|
|
for (const layer of model.layers) {
|
|
if (layer.type !== 255 || layer.inputs.length > 0) {
|
|
layers.push(layer);
|
|
} else {
|
|
for (const tensor of layer.tensors) {
|
|
values.map(tensor.name, null, new barracuda.Tensor(tensor));
|
|
}
|
|
}
|
|
}
|
|
for (const input of model.inputs) {
|
|
const shape = new barracuda.TensorShape(input.shape);
|
|
const type = new barracuda.TensorType(4, shape);
|
|
const argument = new barracuda.Argument(input.name, [values.map(input.name, type)]);
|
|
this.inputs.push(argument);
|
|
}
|
|
for (const output of model.outputs) {
|
|
const argument = new barracuda.Argument(output, [values.map(output)]);
|
|
this.outputs.push(argument);
|
|
}
|
|
for (const layer of layers) {
|
|
const node = new barracuda.Node(metadata, layer, null, values);
|
|
this.nodes.push(node);
|
|
}
|
|
}
|
|
};
|
|
|
|
barracuda.Argument = class {
|
|
|
|
constructor(name, value, type = null) {
|
|
this.name = name;
|
|
this.value = value;
|
|
this.type = type;
|
|
}
|
|
};
|
|
|
|
barracuda.Value = class {
|
|
|
|
constructor(name, type = null, initializer = null) {
|
|
this.name = name;
|
|
this.type = type;
|
|
this.initializer = initializer;
|
|
}
|
|
};
|
|
|
|
barracuda.Node = class {
|
|
|
|
constructor(metadata, layer, type, values) {
|
|
this.name = layer.name || '';
|
|
this.type = type ? type : metadata.type(layer.type);
|
|
this.inputs = [];
|
|
this.outputs = [];
|
|
this.attributes = [];
|
|
const inputs = Array.prototype.slice.call(this.type.inputs || ['input']);
|
|
if (this.type.inputs && this.type.inputs.length === 1 && this.type.inputs[0].name === 'inputs') {
|
|
const argument = new barracuda.Argument('inputs', layer.inputs.map((input) => values.map(input)));
|
|
this.inputs.push(argument);
|
|
} else if (layer.inputs) {
|
|
for (let i = 0; i < layer.inputs.length; i++) {
|
|
const input = layer.inputs[i];
|
|
const name = inputs.length > 0 && inputs[0] ? inputs.shift().name : i.toString();
|
|
const argument = new barracuda.Argument(name, [values.map(input)]);
|
|
this.inputs.push(argument);
|
|
}
|
|
}
|
|
if (layer.tensors) {
|
|
for (let i = 0; i < layer.tensors.length; i++) {
|
|
const tensor = layer.tensors[i];
|
|
const initializer = new barracuda.Tensor(tensor);
|
|
const name = inputs.length > 0 && inputs[0] ? inputs.shift().name : i.toString();
|
|
const argument = new barracuda.Argument(name, [values.map(tensor.name, initializer.type, initializer)]);
|
|
this.inputs.push(argument);
|
|
}
|
|
}
|
|
if (layer.inputs !== undefined) {
|
|
const argument = new barracuda.Argument('output', [values.map(this.name)]);
|
|
this.outputs.push(argument);
|
|
}
|
|
if (layer.activation !== undefined && (layer.type === 50 || layer.activation !== 0)) {
|
|
const type = barracuda.Activation[layer.activation];
|
|
if (!type) {
|
|
throw new barracuda.Error(`Unsupported activation '${layer.activation}'.`);
|
|
}
|
|
const node = new barracuda.Node(metadata, {}, { name: type, category: 'Activation' }, values);
|
|
this.chain = [node];
|
|
}
|
|
const attributes = [
|
|
['strides', 'int32[]', []],
|
|
['pads', 'int32[]', (value) => Array.isArray(value) && (value.every((v) => v === 0) || value.every((v) => v === -1))],
|
|
['pool_size', 'int32[]', []],
|
|
['alpha', 'float32', 1],
|
|
['beta', 'float32', 0],
|
|
['axis', 'int32', -1]
|
|
];
|
|
for (const [name, type, defaultValue] of attributes) {
|
|
const value = layer[name];
|
|
if ((value === undefined) ||
|
|
(Array.isArray(defaultValue) && Array.isArray(value) && value.length === defaultValue.length && value.every((v, i) => v === defaultValue[i])) ||
|
|
(typeof defaultValue === 'function' && defaultValue(value)) ||
|
|
(defaultValue === value)) {
|
|
continue;
|
|
}
|
|
const attribute = new barracuda.Argument(name, value, type);
|
|
this.attributes.push(attribute);
|
|
}
|
|
}
|
|
};
|
|
|
|
barracuda.Tensor = class {
|
|
|
|
constructor(tensor) {
|
|
this.type = new barracuda.TensorType(tensor.itemsize, new barracuda.TensorShape(tensor.shape));
|
|
this.values = tensor.data;
|
|
}
|
|
};
|
|
|
|
barracuda.TensorType = class {
|
|
|
|
constructor(itemsize, shape) {
|
|
switch (itemsize) {
|
|
case 4: this.dataType = 'float32'; break;
|
|
default: throw new barracuda.Error(`Unsupported data type size '${itemsize}'.`);
|
|
}
|
|
this.shape = shape;
|
|
}
|
|
|
|
toString() {
|
|
return this.dataType + this.shape.toString();
|
|
}
|
|
};
|
|
|
|
barracuda.TensorShape = class {
|
|
|
|
constructor(dimensions) {
|
|
this.dimensions = dimensions;
|
|
}
|
|
|
|
toString() {
|
|
return this.dimensions ? (`[${this.dimensions.map((dimension) => dimension ? dimension.toString() : '?').join(',')}]`) : '';
|
|
}
|
|
};
|
|
|
|
barracuda.NNModel = class {
|
|
|
|
constructor(reader) {
|
|
// https://github.com/Unity-Technologies/barracuda-release/blob/release/1.3.2/Barracuda/Runtime/Core/Model.cs
|
|
reader = new barracuda.BinaryReader(reader);
|
|
this.version = reader.int32();
|
|
reader.int32();
|
|
this.inputs = new Array(reader.int32());
|
|
for (let i = 0; i < this.inputs.length; i++) {
|
|
this.inputs[i] = {
|
|
name: reader.string(),
|
|
shape: reader.shape()
|
|
};
|
|
}
|
|
this.outputs = reader.strings();
|
|
this.memories = new Array(reader.int32());
|
|
for (let i = 0; i < this.memories.length; i++) {
|
|
this.memories[i] = {
|
|
shape: reader.shape(),
|
|
in: reader.string(),
|
|
out: reader.string()
|
|
};
|
|
}
|
|
this.layers = new Array(reader.int32());
|
|
for (let i = 0; i < this.layers.length; i++) {
|
|
const layer = {};
|
|
layer.name = reader.string();
|
|
layer.type = reader.int32();
|
|
layer.activation = reader.int32();
|
|
reader.int32();
|
|
reader.int32();
|
|
layer.pads = reader.int32s();
|
|
layer.strides = reader.int32s();
|
|
layer.pool_size = reader.int32s();
|
|
layer.axis = reader.int32();
|
|
layer.alpha = reader.float32();
|
|
layer.beta = reader.float32();
|
|
reader.int32();
|
|
layer.inputs = reader.strings();
|
|
layer.tensors = [];
|
|
const tensorsLength = reader.int32();
|
|
for (let j = 0; j < tensorsLength; j++) {
|
|
layer.tensors.push({
|
|
name: reader.string(),
|
|
shape: reader.shape(),
|
|
offset: reader.int64().toNumber(),
|
|
itemsize: reader.int32(),
|
|
length: reader.int32()
|
|
});
|
|
}
|
|
this.layers[i] = layer;
|
|
}
|
|
const position = reader.position;
|
|
for (const layer of this.layers) {
|
|
for (const tensor of layer.tensors) {
|
|
const offset = tensor.offset;
|
|
reader.seek(position + (offset * tensor.itemsize));
|
|
tensor.data = reader.read(tensor.length * tensor.itemsize);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
barracuda.Activation = {
|
|
0: "Linear", 1: "Relu", 2: "Softmax", 3: "Tanh", 4: "Sigmoid", 5: "Elu", 6: "Relu6", 7: "LeakyRelu", 8: "Selu", 9: "Swish",
|
|
10: "LogSoftmax", 11: "Softplus", 12: "Softsign", 13: "PRelu",
|
|
20: "Hardmax", 21: "HardSigmoid",
|
|
100: "Abs", 101: "Neg", 102: "Ceil", 103: "Clip", 104: "Floor", 105: "Round",
|
|
110: "Reciprocal", 111: "Sqrt", 113: "Exp", 114: "Log",
|
|
200: "Acos", 201: "Acosh", 202: "Asin", 203: "Asinh", 204: "Atan", 205: "Atanh", 206: "Cos", 207: "Cosh", 208: "Sin", 209: "Sinh", 210: "Tan"
|
|
};
|
|
|
|
barracuda.BinaryReader = class {
|
|
|
|
constructor(reader) {
|
|
this._reader = reader;
|
|
}
|
|
|
|
get position() {
|
|
return this._reader.position;
|
|
}
|
|
|
|
seek(position) {
|
|
this._reader.seek(position);
|
|
}
|
|
|
|
skip(offset) {
|
|
this._reader.skip(offset);
|
|
}
|
|
|
|
read(length) {
|
|
return this._reader.read(length);
|
|
}
|
|
|
|
byte() {
|
|
return this._reader.byte();
|
|
}
|
|
|
|
int32() {
|
|
return this._reader.int32();
|
|
}
|
|
|
|
int32s() {
|
|
const values = new Array(this.int32());
|
|
for (let i = 0; i < values.length; i++) {
|
|
values[i] = this.int32();
|
|
}
|
|
return values;
|
|
}
|
|
|
|
int64() {
|
|
return this._reader.int64();
|
|
}
|
|
|
|
float32() {
|
|
return this._reader.float32();
|
|
}
|
|
|
|
string() {
|
|
let content = '';
|
|
const size = this.int32();
|
|
for (let i = 0; i < size; i++) {
|
|
const c = this.byte();
|
|
content += String.fromCharCode(c);
|
|
}
|
|
return content;
|
|
}
|
|
|
|
strings() {
|
|
const values = [];
|
|
const length = this.int32();
|
|
for (let i = 0; i < length; i++) {
|
|
values.push(this.string());
|
|
}
|
|
return values;
|
|
}
|
|
|
|
shape() {
|
|
return this.int32s();
|
|
}
|
|
};
|
|
|
|
barracuda.Metadata = class {
|
|
|
|
static open() {
|
|
barracuda.Metadata._metadata = barracuda.Metadata._metadata || new barracuda.Metadata();
|
|
return barracuda.Metadata._metadata;
|
|
}
|
|
|
|
constructor() {
|
|
this._types = new Map();
|
|
const register = (id, name, category, inputs) => {
|
|
this._types.set(id, { name, category, inputs: (inputs || []).map((input) => {
|
|
return { name: input };
|
|
}) });
|
|
};
|
|
register(0, 'Nop', '');
|
|
register(1, 'Dense', 'Layer', ['input', 'kernel', 'bias']);
|
|
register(2, 'MatMul', '', ['input', 'kernel', 'bias']);
|
|
register(20, 'Conv2D', 'Layer', ['input', 'kernel', 'bias']);
|
|
register(21, 'DepthwiseConv2D', 'Layer', ['input', 'kernel', 'bias']);
|
|
register(22, 'Conv2DTrans', 'Layer', ['input', 'kernel', 'bias']);
|
|
register(23, 'Upsample2D', 'Data');
|
|
register(25, 'MaxPool2D', 'Pool');
|
|
register(26, 'AvgPool2D', 'Pool');
|
|
register(27, 'GlobalMaxPool2D', 'Pool');
|
|
register(28, 'GlobalAvgPool2D', 'Pool');
|
|
register(29, 'Border2D', '');
|
|
register(30, 'Conv3D', 'Layer');
|
|
register(32, 'Conv3DTrans', 'Layer');
|
|
register(33, 'Upsample3D', 'Data');
|
|
register(35, 'MaxPool3D', 'Pool');
|
|
register(36, 'AvgPool3D', 'Pool');
|
|
register(37, 'GlobalMaxPool3D', 'Pool');
|
|
register(38, 'GlobalAvgPool3D', 'Pool');
|
|
register(39, 'Border3D', '');
|
|
register(50, 'Activation', '', ['input']);
|
|
register(51, 'ScaleBias', 'Normalization', ['input', 'scale', 'bias']);
|
|
register(52, 'Normalization', 'Normalization');
|
|
register(53, 'LRN', 'Normalization');
|
|
register(60, 'Dropout', 'Dropout');
|
|
register(64, 'RandomNormal', '');
|
|
register(65, 'RandomUniform', '');
|
|
register(66, 'Multinomial', '');
|
|
register(67, 'OneHot', '');
|
|
register(68, 'TopKIndices', '');
|
|
register(69, 'TopKValues', '');
|
|
register(100, 'Add', '', ['inputs']);
|
|
register(101, 'Sub', '', ['inputs']);
|
|
register(102, 'Mul', '', ['inputs']);
|
|
register(103, 'RealDiv', '', ['inputs']);
|
|
register(104, 'Pow', '', ['inputs']);
|
|
register(110, 'Minimum', '', ['inputs']);
|
|
register(111, 'Maximum', '', ['inputs']);
|
|
register(112, 'Mean', '', ['inputs']);
|
|
register(120, 'ReduceL1', '', ['inputs']);
|
|
register(121, 'ReduceL2', '', ['inputs']);
|
|
register(122, 'ReduceLogSum', '', ['inputs']);
|
|
register(123, 'ReduceLogSumExp', '', ['inputs']);
|
|
register(124, 'ReduceMax', '', ['inputs']);
|
|
register(125, 'ReduceMean', '', ['inputs']);
|
|
register(126, 'ReduceMin', '', ['inputs']);
|
|
register(127, 'ReduceProd', '', ['inputs']);
|
|
register(128, 'ReduceSum', '', ['inputs']);
|
|
register(129, 'ReduceSumSquare', '', ['inputs']);
|
|
register(140, 'Greater', '');
|
|
register(141, 'GreaterEqual', '');
|
|
register(142, 'Less', '');
|
|
register(143, 'LessEqual', '');
|
|
register(144, 'Equal', '');
|
|
register(145, 'LogicalOr', '');
|
|
register(146, 'LogicalAnd', '');
|
|
register(147, 'LogicalNot', '');
|
|
register(148, 'LogicalXor', '');
|
|
register(160, 'Pad2DReflect', '');
|
|
register(161, 'Pad2DSymmetric', '');
|
|
register(162, 'Pad2DEdge', '');
|
|
register(200, 'Flatten', 'Shape');
|
|
register(201, 'Reshape', 'Shape');
|
|
register(202, 'Transpose', '');
|
|
register(203, 'Squeeze', '');
|
|
register(204, 'Unsqueeze', '');
|
|
register(205, 'Gather', '');
|
|
register(206, 'DepthToSpace', '');
|
|
register(207, 'SpaceToDepth', '');
|
|
register(208, 'Expand', '');
|
|
register(209, 'Resample2D', '');
|
|
register(210, 'Concat', 'Tensor', ['inputs']);
|
|
register(211, 'StridedSlice', 'Shape');
|
|
register(212, 'Tile', '');
|
|
register(213, 'Shape', '');
|
|
register(214, 'NonMaxSuppression', '');
|
|
register(215, 'LSTM', '');
|
|
register(255, 'Load', '');
|
|
}
|
|
|
|
type(name) {
|
|
if (!this._types.has(name)) {
|
|
this._types.set(name, { name: name.toString() });
|
|
}
|
|
return this._types.get(name);
|
|
}
|
|
};
|
|
|
|
barracuda.Error = class extends Error {
|
|
|
|
constructor(message) {
|
|
super(message);
|
|
this.name = 'Error loading Barracuda model.';
|
|
}
|
|
};
|
|
|
|
export const ModelFactory = barracuda.ModelFactory;
|
|
|