barracuda.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  1. // Experimental
  2. var barracuda = {};
  3. var base = require('./base');
  4. barracuda.ModelFactory = class {
  5. match(context) {
  6. const stream = context.stream;
  7. if (stream && stream.length > 12) {
  8. const buffer = stream.peek(12);
  9. if (buffer[0] <= 0x20 && buffer.subarray(1, 8).every((value) => value == 0x00)) {
  10. return 'barracuda';
  11. }
  12. }
  13. return null;
  14. }
  15. open(context) {
  16. return barracuda.Metadata.open().then((metadata) => {
  17. const nn = new barracuda.NNModel(context.stream.peek());
  18. return new barracuda.Model(metadata, nn);
  19. });
  20. }
  21. };
  22. barracuda.Model = class {
  23. constructor(metadata, nn) {
  24. this._version = nn.version.toString();
  25. this._graphs = [ new barracuda.Graph(metadata, nn) ];
  26. }
  27. get format() {
  28. return "Barracuda v" + this._version;
  29. }
  30. get graphs() {
  31. return this._graphs;
  32. }
  33. };
  34. barracuda.Graph = class {
  35. constructor(metadata, nn) {
  36. this._inputs = [];
  37. this._outputs = [];
  38. this._nodes = [];
  39. for (const input of nn.inputs) {
  40. this._inputs.push(new barracuda.Parameter(input.name, [
  41. new barracuda.Argument(input.name, new barracuda.TensorType(4, new barracuda.TensorShape(input.shape)))
  42. ]));
  43. }
  44. for (const output of nn.outputs) {
  45. this._outputs.push(new barracuda.Parameter(output, [
  46. new barracuda.Argument(output)
  47. ]));
  48. }
  49. const layers = [];
  50. const initializers = new Map();
  51. for (const layer of nn.layers) {
  52. if (layer.type !== 255 || layer.inputs.length > 0) {
  53. layers.push(layer);
  54. }
  55. else {
  56. for (const tensor of layer.tensors) {
  57. initializers.set(tensor.name, new barracuda.Tensor(tensor));
  58. }
  59. }
  60. }
  61. for (const layer of layers) {
  62. this._nodes.push(new barracuda.Node(metadata, layer, null, initializers));
  63. }
  64. }
  65. get name() {
  66. return '';
  67. }
  68. get inputs() {
  69. return this._inputs;
  70. }
  71. get outputs() {
  72. return this._outputs;
  73. }
  74. get nodes() {
  75. return this._nodes;
  76. }
  77. };
  78. barracuda.Parameter = class {
  79. constructor(name, args) {
  80. this._name = name;
  81. this._arguments = args;
  82. }
  83. get name() {
  84. return this._name;
  85. }
  86. get visible() {
  87. return true;
  88. }
  89. get arguments() {
  90. return this._arguments;
  91. }
  92. };
  93. barracuda.Argument = class {
  94. constructor(name, type, initializer) {
  95. this._name = name;
  96. this._type = type || null;
  97. this._initializer = initializer || null;
  98. }
  99. get name() {
  100. return this._name;
  101. }
  102. get type() {
  103. return this._type;
  104. }
  105. get initializer() {
  106. return this._initializer;
  107. }
  108. };
  109. barracuda.Node = class {
  110. constructor(metadata, layer, type, initializers) {
  111. this._name = layer.name || '';
  112. this._type = type ? type : metadata.type(layer.type);
  113. this._inputs = [];
  114. this._outputs = [];
  115. this._attributes = [];
  116. const inputs = Array.prototype.slice.call(this._type.inputs || [ 'input' ]);
  117. if (this._type.inputs && this._type.inputs.length === 1 && this._type.inputs[0].name === 'inputs') {
  118. this._inputs.push(new barracuda.Parameter('inputs', layer.inputs.map((input) => {
  119. const initializer = initializers.has(input) ? initializers.get(input) : null;
  120. return new barracuda.Argument(input, initializer ? initializer.type : null, initializer);
  121. })));
  122. }
  123. else if (layer.inputs) {
  124. for (let i = 0; i < layer.inputs.length; i++) {
  125. const input = layer.inputs[i];
  126. const initializer = initializers.has(input) ? initializers.get(input) : null;
  127. this._inputs.push(new barracuda.Parameter(inputs.length > 0 ? inputs.shift().name : i.toString(), [
  128. new barracuda.Argument(input, initializer ? initializer.type : null, initializer)
  129. ]));
  130. }
  131. }
  132. if (layer.tensors) {
  133. for (let i = 0; i < layer.tensors.length; i++) {
  134. const tensor = layer.tensors[i];
  135. const initializer = new barracuda.Tensor(tensor);
  136. this._inputs.push(new barracuda.Parameter(inputs.length > 0 ? inputs.shift().name : i.toString(), [
  137. new barracuda.Argument(tensor.name, initializer.type, initializer)
  138. ]));
  139. }
  140. }
  141. if (layer.inputs !== undefined) {
  142. this._outputs.push(new barracuda.Parameter('output', [
  143. new barracuda.Argument(this._name)
  144. ]));
  145. }
  146. if (layer.activation !== undefined && (layer.type === 50 || layer.activation !== 0)) {
  147. const type = barracuda.Activation[layer.activation];
  148. if (!type) {
  149. throw new barracuda.Error("Unsupported activation '" + layer.activation + "'.");
  150. }
  151. this._chain = [ new barracuda.Node(metadata, {}, { name: type, category: 'Activation' }, initializers) ];
  152. }
  153. const attribute = (name, type, value, defaultValue) => {
  154. if (value === undefined) {
  155. return;
  156. }
  157. if (Array.isArray(defaultValue) && Array.isArray(value) && value.length == defaultValue.length && value.every((v, i) => v === defaultValue[i])) {
  158. return;
  159. }
  160. if (typeof defaultValue == 'function' && defaultValue(value)) {
  161. return;
  162. }
  163. if (defaultValue === value) {
  164. return;
  165. }
  166. this._attributes.push(new barracuda.Attribute(name, type, value));
  167. };
  168. attribute('strides', 'int32[]', layer.strides, []);
  169. attribute('pads', 'int32[]', layer.pads, (value) => Array.isArray(value) && (value.every((v) => v === 0) || value.every((v) => v === -1)));
  170. attribute('size', 'int32[]', layer.pool_size, []);
  171. attribute('alpha', 'float32', layer.alpha, 1);
  172. attribute('beta', 'float32', layer.beta, 0);
  173. attribute('axis', 'int32', layer.axis, -1);
  174. }
  175. get type() {
  176. return this._type;
  177. }
  178. get name() {
  179. return this._name;
  180. }
  181. get attributes() {
  182. return this._attributes;
  183. }
  184. get inputs() {
  185. return this._inputs;
  186. }
  187. get outputs() {
  188. return this._outputs;
  189. }
  190. get chain() {
  191. return this._chain;
  192. }
  193. };
  194. barracuda.Attribute = class {
  195. constructor(name, type, value) {
  196. this._name = name;
  197. this._type = type;
  198. this._value = value;
  199. }
  200. get type() {
  201. return this._type;
  202. }
  203. get name() {
  204. return this._name;
  205. }
  206. get value() {
  207. return this._value;
  208. }
  209. get visible() {
  210. return true;
  211. }
  212. };
  213. barracuda.Tensor = class {
  214. constructor(tensor) {
  215. this._type = new barracuda.TensorType(tensor.itemsize, new barracuda.TensorShape(tensor.shape));
  216. this._values = tensor.data;
  217. }
  218. get type() {
  219. return this._type;
  220. }
  221. get values() {
  222. return this._values;
  223. }
  224. };
  225. barracuda.TensorType = class {
  226. constructor(itemsize, shape) {
  227. switch (itemsize) {
  228. case 4: this._dataType = 'float32'; break;
  229. default: throw new barracuda.Error("Unsupported data type size '" + itemsize.toString() + "'.");
  230. }
  231. this._shape = shape;
  232. }
  233. get dataType() {
  234. return this._dataType;
  235. }
  236. get shape() {
  237. return this._shape;
  238. }
  239. toString() {
  240. return this._dataType + this._shape.toString();
  241. }
  242. };
  243. barracuda.TensorShape = class {
  244. constructor(dimensions) {
  245. this._dimensions = dimensions;
  246. }
  247. get dimensions() {
  248. return this._dimensions;
  249. }
  250. toString() {
  251. return this._dimensions ? ('[' + this._dimensions.map((dimension) => dimension ? dimension.toString() : '?').join(',') + ']') : '';
  252. }
  253. };
  254. barracuda.NNModel = class {
  255. constructor(buffer) {
  256. // https://github.com/Unity-Technologies/barracuda-release/blob/release/1.3.2/Barracuda/Runtime/Core/Model.cs
  257. const reader = new barracuda.BinaryReader(buffer);
  258. this._version = reader.int32();
  259. reader.int32();
  260. this._inputs = new Array(reader.int32());
  261. for (let i = 0; i < this._inputs.length; i++) {
  262. this._inputs[i] = {
  263. name: reader.string(),
  264. shape: reader.shape()
  265. };
  266. }
  267. this._outputs = reader.strings();
  268. this._memories = new Array(reader.int32());
  269. for (let i = 0; i < this._memories.length; i++) {
  270. this._memories[i] = {
  271. shape: reader.shape(),
  272. in: reader.string(),
  273. out: reader.string()
  274. };
  275. }
  276. this._layers = new Array(reader.int32());
  277. for (let i = 0; i < this._layers.length; i++) {
  278. const layer = {};
  279. layer.name = reader.string();
  280. layer.type = reader.int32();
  281. layer.activation = reader.int32();
  282. reader.int32();
  283. reader.int32();
  284. layer.pads = reader.int32s();
  285. layer.strides = reader.int32s();
  286. layer.pool_size = reader.int32s();
  287. layer.axis = reader.int32();
  288. layer.alpha = reader.float32();
  289. layer.beta = reader.float32();
  290. reader.int32();
  291. layer.inputs = reader.strings();
  292. layer.tensors = [];
  293. const tensorsLength = reader.int32();
  294. for (let j = 0; j < tensorsLength; j++) {
  295. layer.tensors.push({
  296. name: reader.string(),
  297. shape: reader.shape(),
  298. offset: reader.int64(),
  299. itemsize: reader.int32(),
  300. length: reader.int32()
  301. });
  302. }
  303. this._layers[i] = layer;
  304. }
  305. const position = reader.position;
  306. for (const layer of this._layers) {
  307. for (const tensor of layer.tensors) {
  308. reader.seek(position + (tensor.offset * tensor.itemsize));
  309. tensor.data = reader.read(tensor.length * tensor.itemsize);
  310. }
  311. }
  312. }
  313. get version() {
  314. return this._version;
  315. }
  316. get inputs() {
  317. return this._inputs;
  318. }
  319. get outputs() {
  320. return this._outputs;
  321. }
  322. get memories() {
  323. return this._memories;
  324. }
  325. get layers() {
  326. return this._layers;
  327. }
  328. };
  329. barracuda.Activation = {
  330. 0: "Linear", 1: "Relu", 2: "Softmax", 3: "Tanh", 4: "Sigmoid", 5: "Elu", 6: "Relu6", 7: "LeakyRelu", 8: "Selu", 9: "Swish",
  331. 10: "LogSoftmax", 11: "Softplus", 12: "Softsign", 13: "PRelu",
  332. 20: "Hardmax", 21: "HardSigmoid",
  333. 100: "Abs", 101: "Neg", 102: "Ceil", 103: "Clip", 104: "Floor", 105: "Round",
  334. 110: "Reciprocal", 111: "Sqrt", 113: "Exp", 114: "Log",
  335. 200: "Acos", 201: "Acosh", 202: "Asin", 203: "Asinh", 204: "Atan", 205: "Atanh", 206: "Cos", 207: "Cosh", 208: "Sin", 209: "Sinh", 210: "Tan"
  336. };
  337. barracuda.BinaryReader = class extends base.BinaryReader {
  338. int32s() {
  339. const values = new Array(this.int32());
  340. for (let i = 0; i < values.length; i++) {
  341. values[i] = this.int32();
  342. }
  343. return values;
  344. }
  345. string() {
  346. let content = '';
  347. const size = this.int32();
  348. let position = this._position;
  349. this.skip(size);
  350. for (let i = 0; i < size; i++) {
  351. content += String.fromCharCode(this._buffer[position++]);
  352. }
  353. return content;
  354. }
  355. strings() {
  356. const values = [];
  357. const length = this.int32();
  358. for (let i = 0; i < length; i++) {
  359. values.push(this.string());
  360. }
  361. return values;
  362. }
  363. shape() {
  364. return this.int32s();
  365. }
  366. };
  367. barracuda.Metadata = class {
  368. static open() {
  369. barracuda.Metadata._metadata = barracuda.Metadata._metadata || new barracuda.Metadata();
  370. return Promise.resolve(barracuda.Metadata._metadata);
  371. }
  372. constructor() {
  373. this._types = new Map();
  374. const register = (id, name, category, inputs) => {
  375. this._types.set(id, { name: name, category: category, inputs: (inputs || []).map((input) => {
  376. return { name: input };
  377. }) });
  378. };
  379. register(0, 'Nop', '');
  380. register(1, 'Dense', 'Layer', [ 'input', 'kernel', 'bias' ]);
  381. register(2, 'MatMul', '', [ 'input', 'kernel', 'bias' ]);
  382. register(20, 'Conv2D', 'Layer', [ 'input', 'kernel', 'bias' ]);
  383. register(21, 'DepthwiseConv2D', 'Layer', [ 'input', 'kernel', 'bias' ]);
  384. register(22, 'Conv2DTrans', 'Layer', [ 'input', 'kernel', 'bias' ]);
  385. register(23, 'Upsample2D', 'Data');
  386. register(25, 'MaxPool2D', 'Pool');
  387. register(26, 'AvgPool2D', 'Pool');
  388. register(27, 'GlobalMaxPool2D', 'Pool');
  389. register(28, 'GlobalAvgPool2D', 'Pool');
  390. register(29, 'Border2D', '');
  391. register(30, 'Conv3D', 'Layer');
  392. register(32, 'Conv3DTrans', 'Layer');
  393. register(33, 'Upsample3D', 'Data');
  394. register(35, 'MaxPool3D', 'Pool');
  395. register(36, 'AvgPool3D', 'Pool');
  396. register(37, 'GlobalMaxPool3D', 'Pool');
  397. register(38, 'GlobalAvgPool3D', 'Pool');
  398. register(39, 'Border3D', '');
  399. register(50, 'Activation', '', [ 'input' ]);
  400. register(51, 'ScaleBias', 'Normalization', [ 'input', 'scale', 'bias' ]);
  401. register(52, 'Normalization', 'Normalization');
  402. register(53, 'LRN', 'Normalization');
  403. register(60, 'Dropout', 'Dropout');
  404. register(64, 'RandomNormal', '');
  405. register(65, 'RandomUniform', '');
  406. register(66, 'Multinomial', '');
  407. register(67, 'OneHot', '');
  408. register(68, 'TopKIndices', '');
  409. register(69, 'TopKValues', '');
  410. register(100, 'Add', '', [ 'inputs' ]);
  411. register(101, 'Sub', '', [ 'inputs' ]);
  412. register(102, 'Mul', '', [ 'inputs' ]);
  413. register(103, 'RealDiv', '', [ 'inputs' ]);
  414. register(104, 'Pow', '', [ 'inputs' ]);
  415. register(110, 'Minimum', '', [ 'inputs' ]);
  416. register(111, 'Maximum', '', [ 'inputs' ]);
  417. register(112, 'Mean', '', [ 'inputs' ]);
  418. register(120, 'ReduceL1', '', [ 'inputs' ]);
  419. register(121, 'ReduceL2', '', [ 'inputs' ]);
  420. register(122, 'ReduceLogSum', '', [ 'inputs' ]);
  421. register(123, 'ReduceLogSumExp', '', [ 'inputs' ]);
  422. register(124, 'ReduceMax', '', [ 'inputs' ]);
  423. register(125, 'ReduceMean', '', [ 'inputs' ]);
  424. register(126, 'ReduceMin', '', [ 'inputs' ]);
  425. register(127, 'ReduceProd', '', [ 'inputs' ]);
  426. register(128, 'ReduceSum', '', [ 'inputs' ]);
  427. register(129, 'ReduceSumSquare', '', [ 'inputs' ]);
  428. register(140, 'Greater', '');
  429. register(141, 'GreaterEqual', '');
  430. register(142, 'Less', '');
  431. register(143, 'LessEqual', '');
  432. register(144, 'Equal', '');
  433. register(145, 'LogicalOr', '');
  434. register(146, 'LogicalAnd', '');
  435. register(147, 'LogicalNot', '');
  436. register(148, 'LogicalXor', '');
  437. register(160, 'Pad2DReflect', '');
  438. register(161, 'Pad2DSymmetric', '');
  439. register(162, 'Pad2DEdge', '');
  440. register(200, 'Flatten', 'Shape');
  441. register(201, 'Reshape', 'Shape');
  442. register(202, 'Transpose', '');
  443. register(203, 'Squeeze', '');
  444. register(204, 'Unsqueeze', '');
  445. register(205, 'Gather', '');
  446. register(206, 'DepthToSpace', '');
  447. register(207, 'SpaceToDepth', '');
  448. register(208, 'Expand', '');
  449. register(209, 'Resample2D', '');
  450. register(210, 'Concat', 'Tensor', [ 'inputs' ]);
  451. register(211, 'StridedSlice', 'Shape');
  452. register(212, 'Tile', '');
  453. register(213, 'Shape', '');
  454. register(214, 'NonMaxSuppression', '');
  455. register(215, 'LSTM', '');
  456. register(255, 'Load', '');
  457. }
  458. type(name) {
  459. if (!this._types.has(name)) {
  460. this._types.set(name, { name: name.toString() });
  461. }
  462. return this._types.get(name);
  463. }
  464. };
  465. barracuda.Error = class extends Error {
  466. constructor(message) {
  467. super(message);
  468. this.name = 'Error loading Barracuda model.';
  469. }
  470. };
  471. if (typeof module !== 'undefined' && typeof module.exports === 'object') {
  472. module.exports.ModelFactory = barracuda.ModelFactory;
  473. }