2
0

barracuda.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  1. // Experimental
  2. var barracuda = barracuda || {};
  3. var base = base || require('./base');
  4. barracuda.ModelFactory = class {
  5. match(context) {
  6. const stream = context.stream;
  7. if (stream.length > 12) {
  8. const buffer = stream.peek(12);
  9. if (buffer[0] <= 0x20 && buffer.subarray(1, 8).every((value) => value == 0x00)) {
  10. return true;
  11. }
  12. }
  13. return false;
  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._data = tensor.data;
  217. }
  218. get kind() {
  219. return '';
  220. }
  221. get type() {
  222. return this._type;
  223. }
  224. get state() {
  225. return this._context().state || null;
  226. }
  227. get value() {
  228. const context = this._context();
  229. if (context.state) {
  230. return null;
  231. }
  232. context.limit = Number.MAX_SAFE_INTEGER;
  233. return this._decode(context, 0);
  234. }
  235. toString() {
  236. const context = this._context();
  237. if (context.state) {
  238. return '';
  239. }
  240. context.limit = 10000;
  241. const value = this._decode(context, 0);
  242. return JSON.stringify(value, null, 4);
  243. }
  244. _context() {
  245. const context = {};
  246. context.index = 0;
  247. context.count = 0;
  248. context.state = null;
  249. if (this._type.dataType == '?') {
  250. context.state = 'Tensor has unknown data type.';
  251. return context;
  252. }
  253. if (!this._type.shape || (this._type.shape.dimensions && this._type.shape.dimensions.length == 0)) {
  254. context.state = 'Tensor has no dimensions.';
  255. return context;
  256. }
  257. if (!this._data) {
  258. context.state = 'Tensor data is empty.';
  259. return context;
  260. }
  261. switch (this._type.dataType) {
  262. case 'float32':
  263. context.data = new DataView(this._data.buffer, this._data.byteOffset, this._data.byteLength);
  264. break;
  265. default:
  266. context.state = 'Tensor data type is not implemented.';
  267. break;
  268. }
  269. context.dataType = this._type.dataType;
  270. context.shape = this._type.shape.dimensions;
  271. return context;
  272. }
  273. _decode(context, dimension) {
  274. const shape = context.shape.length == 0 ? [ 1 ] : context.shape;
  275. const results = [];
  276. const size = shape[dimension];
  277. if (dimension == shape.length - 1) {
  278. for (let i = 0; i < size; i++) {
  279. if (context.count > context.limit) {
  280. results.push('...');
  281. return results;
  282. }
  283. switch (this._type.dataType) {
  284. case 'float32':
  285. results.push(context.data.getFloat32(context.index, true));
  286. context.index += 4;
  287. context.count++;
  288. break;
  289. default:
  290. throw new barracuda.Error("Unsupported tensor data type '" + this._type.dataType + "'.");
  291. }
  292. }
  293. }
  294. else {
  295. for (let j = 0; j < size; j++) {
  296. if (context.count > context.limit) {
  297. results.push('...');
  298. return results;
  299. }
  300. results.push(this._decode(context, dimension + 1));
  301. }
  302. }
  303. if (context.shape.length == 0) {
  304. return results[0];
  305. }
  306. return results;
  307. }
  308. };
  309. barracuda.TensorType = class {
  310. constructor(itemsize, shape) {
  311. switch (itemsize) {
  312. case 4: this._dataType = 'float32'; break;
  313. default: throw new barracuda.Error("Unsupported data type size '" + itemsize.toString() + "'.");
  314. }
  315. this._shape = shape;
  316. }
  317. get dataType() {
  318. return this._dataType;
  319. }
  320. get shape() {
  321. return this._shape;
  322. }
  323. toString() {
  324. return this._dataType + this._shape.toString();
  325. }
  326. };
  327. barracuda.TensorShape = class {
  328. constructor(dimensions) {
  329. this._dimensions = dimensions;
  330. }
  331. get dimensions() {
  332. return this._dimensions;
  333. }
  334. toString() {
  335. return this._dimensions ? ('[' + this._dimensions.map((dimension) => dimension ? dimension.toString() : '?').join(',') + ']') : '';
  336. }
  337. };
  338. barracuda.NNModel = class {
  339. constructor(buffer) {
  340. // https://github.com/Unity-Technologies/barracuda-release/blob/release/1.3.2/Barracuda/Runtime/Core/Model.cs
  341. const reader = new barracuda.BinaryReader(buffer);
  342. this._version = reader.int32();
  343. reader.int32();
  344. this._inputs = new Array(reader.int32());
  345. for (let i = 0; i < this._inputs.length; i++) {
  346. this._inputs[i] = {
  347. name: reader.string(),
  348. shape: reader.shape()
  349. };
  350. }
  351. this._outputs = reader.strings();
  352. this._memories = new Array(reader.int32());
  353. for (let i = 0; i < this._memories.length; i++) {
  354. this._memories[i] = {
  355. shape: reader.shape(),
  356. in: reader.string(),
  357. out: reader.string()
  358. };
  359. }
  360. this._layers = new Array(reader.int32());
  361. for (let i = 0; i < this._layers.length; i++) {
  362. const layer = {};
  363. layer.name = reader.string();
  364. layer.type = reader.int32();
  365. layer.activation = reader.int32();
  366. reader.int32();
  367. reader.int32();
  368. layer.pads = reader.int32s();
  369. layer.strides = reader.int32s();
  370. layer.pool_size = reader.int32s();
  371. layer.axis = reader.int32();
  372. layer.alpha = reader.float32();
  373. layer.beta = reader.float32();
  374. reader.int32();
  375. layer.inputs = reader.strings();
  376. layer.tensors = [];
  377. const tensorsLength = reader.int32();
  378. for (let j = 0; j < tensorsLength; j++) {
  379. layer.tensors.push({
  380. name: reader.string(),
  381. shape: reader.shape(),
  382. offset: reader.int64(),
  383. itemsize: reader.int32(),
  384. length: reader.int32()
  385. });
  386. }
  387. this._layers[i] = layer;
  388. }
  389. const position = reader.position;
  390. for (const layer of this._layers) {
  391. for (const tensor of layer.tensors) {
  392. reader.seek(position + (tensor.offset * tensor.itemsize));
  393. tensor.data = reader.read(tensor.length * tensor.itemsize);
  394. }
  395. }
  396. }
  397. get version() {
  398. return this._version;
  399. }
  400. get inputs() {
  401. return this._inputs;
  402. }
  403. get outputs() {
  404. return this._outputs;
  405. }
  406. get memories() {
  407. return this._memories;
  408. }
  409. get layers() {
  410. return this._layers;
  411. }
  412. };
  413. barracuda.Activation = {
  414. 0: "Linear", 1: "Relu", 2: "Softmax", 3: "Tanh", 4: "Sigmoid", 5: "Elu", 6: "Relu6", 7: "LeakyRelu", 8: "Selu", 9: "Swish",
  415. 10: "LogSoftmax", 11: "Softplus", 12: "Softsign", 13: "PRelu",
  416. 20: "Hardmax", 21: "HardSigmoid",
  417. 100: "Abs", 101: "Neg", 102: "Ceil", 103: "Clip", 104: "Floor", 105: "Round",
  418. 110: "Reciprocal", 111: "Sqrt", 113: "Exp", 114: "Log",
  419. 200: "Acos", 201: "Acosh", 202: "Asin", 203: "Asinh", 204: "Atan", 205: "Atanh", 206: "Cos", 207: "Cosh", 208: "Sin", 209: "Sinh", 210: "Tan"
  420. };
  421. barracuda.BinaryReader = class extends base.BinaryReader {
  422. int32s() {
  423. const values = new Array(this.int32());
  424. for (let i = 0; i < values.length; i++) {
  425. values[i] = this.int32();
  426. }
  427. return values;
  428. }
  429. string() {
  430. let content = '';
  431. const size = this.int32();
  432. let position = this._position;
  433. this.skip(size);
  434. for (let i = 0; i < size; i++) {
  435. content += String.fromCharCode(this._buffer[position++]);
  436. }
  437. return content;
  438. }
  439. strings() {
  440. const values = [];
  441. const length = this.int32();
  442. for (let i = 0; i < length; i++) {
  443. values.push(this.string());
  444. }
  445. return values;
  446. }
  447. shape() {
  448. return this.int32s();
  449. }
  450. };
  451. barracuda.Metadata = class {
  452. static open() {
  453. barracuda.Metadata._metadata = barracuda.Metadata._metadata || new barracuda.Metadata();
  454. return Promise.resolve(barracuda.Metadata._metadata);
  455. }
  456. constructor() {
  457. this._types = new Map();
  458. const register = (id, name, category, inputs) => {
  459. this._types.set(id, { name: name, category: category, inputs: (inputs || []).map((input) => { return { name: input }; }) });
  460. };
  461. register(0, 'Nop', '');
  462. register(1, 'Dense', 'Layer', [ 'input', 'kernel', 'bias' ]);
  463. register(2, 'MatMul', '', [ 'input', 'kernel', 'bias' ]);
  464. register(20, 'Conv2D', 'Layer', [ 'input', 'kernel', 'bias' ]);
  465. register(21, 'DepthwiseConv2D', 'Layer', [ 'input', 'kernel', 'bias' ]);
  466. register(22, 'Conv2DTrans', 'Layer', [ 'input', 'kernel', 'bias' ]);
  467. register(23, 'Upsample2D', 'Data');
  468. register(25, 'MaxPool2D', 'Pool');
  469. register(26, 'AvgPool2D', 'Pool');
  470. register(27, 'GlobalMaxPool2D', 'Pool');
  471. register(28, 'GlobalAvgPool2D', 'Pool');
  472. register(29, 'Border2D', '');
  473. register(30, 'Conv3D', 'Layer');
  474. register(32, 'Conv3DTrans', 'Layer');
  475. register(33, 'Upsample3D', 'Data');
  476. register(35, 'MaxPool3D', 'Pool');
  477. register(36, 'AvgPool3D', 'Pool');
  478. register(37, 'GlobalMaxPool3D', 'Pool');
  479. register(38, 'GlobalAvgPool3D', 'Pool');
  480. register(39, 'Border3D', '');
  481. register(50, 'Activation', '', [ 'input' ]);
  482. register(51, 'ScaleBias', 'Normalization', [ 'input', 'scale', 'bias' ]);
  483. register(52, 'Normalization', 'Normalization');
  484. register(53, 'LRN', 'Normalization');
  485. register(60, 'Dropout', 'Dropout');
  486. register(64, 'RandomNormal', '');
  487. register(65, 'RandomUniform', '');
  488. register(66, 'Multinomial', '');
  489. register(67, 'OneHot', '');
  490. register(68, 'TopKIndices', '');
  491. register(69, 'TopKValues', '');
  492. register(100, 'Add', '', [ 'inputs' ]);
  493. register(101, 'Sub', '', [ 'inputs' ]);
  494. register(102, 'Mul', '', [ 'inputs' ]);
  495. register(103, 'RealDiv', '', [ 'inputs' ]);
  496. register(104, 'Pow', '', [ 'inputs' ]);
  497. register(110, 'Minimum', '', [ 'inputs' ]);
  498. register(111, 'Maximum', '', [ 'inputs' ]);
  499. register(112, 'Mean', '', [ 'inputs' ]);
  500. register(120, 'ReduceL1', '', [ 'inputs' ]);
  501. register(121, 'ReduceL2', '', [ 'inputs' ]);
  502. register(122, 'ReduceLogSum', '', [ 'inputs' ]);
  503. register(123, 'ReduceLogSumExp', '', [ 'inputs' ]);
  504. register(124, 'ReduceMax', '', [ 'inputs' ]);
  505. register(125, 'ReduceMean', '', [ 'inputs' ]);
  506. register(126, 'ReduceMin', '', [ 'inputs' ]);
  507. register(127, 'ReduceProd', '', [ 'inputs' ]);
  508. register(128, 'ReduceSum', '', [ 'inputs' ]);
  509. register(129, 'ReduceSumSquare', '', [ 'inputs' ]);
  510. register(140, 'Greater', '');
  511. register(141, 'GreaterEqual', '');
  512. register(142, 'Less', '');
  513. register(143, 'LessEqual', '');
  514. register(144, 'Equal', '');
  515. register(145, 'LogicalOr', '');
  516. register(146, 'LogicalAnd', '');
  517. register(147, 'LogicalNot', '');
  518. register(148, 'LogicalXor', '');
  519. register(160, 'Pad2DReflect', '');
  520. register(161, 'Pad2DSymmetric', '');
  521. register(162, 'Pad2DEdge', '');
  522. register(200, 'Flatten', 'Shape');
  523. register(201, 'Reshape', 'Shape');
  524. register(202, 'Transpose', '');
  525. register(203, 'Squeeze', '');
  526. register(204, 'Unsqueeze', '');
  527. register(205, 'Gather', '');
  528. register(206, 'DepthToSpace', '');
  529. register(207, 'SpaceToDepth', '');
  530. register(208, 'Expand', '');
  531. register(209, 'Resample2D', '');
  532. register(210, 'Concat', 'Tensor', [ 'inputs' ]);
  533. register(211, 'StridedSlice', 'Shape');
  534. register(212, 'Tile', '');
  535. register(213, 'Shape', '');
  536. register(214, 'NonMaxSuppression', '');
  537. register(215, 'LSTM', '');
  538. register(255, 'Load', '');
  539. }
  540. type(name) {
  541. if (!this._types.has(name)) {
  542. this._types.set(name, { name: name.toString() });
  543. }
  544. return this._types.get(name);
  545. }
  546. };
  547. barracuda.Error = class extends Error {
  548. constructor(message) {
  549. super(message);
  550. this.name = 'Error loading Barracuda model.';
  551. }
  552. };
  553. if (typeof module !== 'undefined' && typeof module.exports === 'object') {
  554. module.exports.ModelFactory = barracuda.ModelFactory;
  555. }