| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448 |
- // 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;
|