浏览代码

Update openvino.js

Lutz Roeder 3 年之前
父节点
当前提交
238d8742ae
共有 3 个文件被更改,包括 89 次插入44 次删除
  1. 22 2
      source/openvino-metadata.json
  2. 42 11
      source/openvino.js
  3. 25 31
      test/models.js

+ 22 - 2
source/openvino-metadata.json

@@ -601,11 +601,31 @@
   },
   {
     "name": "GRUCell",
-    "category": "Layer"
+    "category": "Layer",
+    "description": "GRUCell represents a single GRU Cell that computes the output using the formula described in the [paper](https://arxiv.org/abs/1406.1078).",
+    "attributes": [
+      { "name": "hidden_size", "type": "int64", "description": "pecifies hidden state size." },
+      { "name": "linear_before_reset", "type": "boolean", "optional": true, "default": false, "description": "denotes if the layer behaves according to the modification of GRUCell described in the formula in the [ONNX documentation](https://github.com/onnx/onnx/blob/master/docs/Operators.md#GRU)." }
+    ],
+    "inputs": [
+      { "name": "X", "description": "2D tensor of type T `[batch_size, input_size]`, input data. Required." },
+      { "name": "initial_hidden_state", "description": "2D tensor of type T `[batch_size, hidden_size]`. Required." },
+      { "name": "W", "description": "2D tensor of type T `[3 * hidden_size, input_size]`, the weights for matrix multiplication, gate order: zrh. Required." },
+      { "name": "R", "description": "2D tensor of type T `[3 * hidden_size, hidden_size]`, the recurrence weights for matrix multiplication, gate order: zrh. Required." },
+      { "name": "B", "description": "1D tensor of type T. If linear_before_reset is set to 1, then the shape is `[4 * hidden_size]` - the sum of biases for z and r gates (weights and recurrence weights), the biases for h gate are placed separately. Otherwise the shape is `[3 * hidden_size]`, the sum of biases (weights and recurrence weights). Optional." }
+    ]
   },
   {
     "name": "LSTMCell",
-    "category": "Layer"
+    "category": "Layer",
+    "inputs": [
+      { "name": "X" },
+      { "name": "initial_hidden_state" },
+      { "name": "initial_cell_state" },
+      { "name": "W" },
+      { "name": "R" },
+      { "name": "B" }
+    ]
   },
   {
     "name": "MaxPool",

+ 42 - 11
source/openvino.js

@@ -147,7 +147,7 @@ openvino.Graph = class {
                     break;
                 }
                 default: {
-                    this._nodes.push(new openvino.Node(this, metadata, bin, layer, inputs, outputs));
+                    this._nodes.push(new openvino.Node(metadata, bin, layer, inputs, outputs));
                     break;
                 }
             }
@@ -232,7 +232,7 @@ openvino.Graph = class {
             for (const nestedLayer of this._const(iteratorLayers, iteratorAllEdges, iteratorBackEdgesMap)) {
                 const inputs = nestedLayer.inputs.map((input) => this._argument(nestedLayer.id, nestedLayer.precision, input, iteratorAllEdges));
                 const outputs = nestedLayer.outputs.map((output) => this._argument(nestedLayer.id, nestedLayer.precision || output.precision, output, null));
-                const nestedNode = new openvino.Node(this, metadata, bin, nestedLayer, inputs, outputs);
+                const nestedNode = new openvino.Node(metadata, bin, nestedLayer, inputs, outputs);
                 nestedNode._id = singleTensorIteratorNodeId + '_' + nestedLayer.id;
                 for (const input of nestedNode._inputs) {
                     for (const input_argument of input.arguments) {
@@ -474,7 +474,7 @@ openvino.Graph = class {
 
 openvino.Node = class {
 
-    constructor(graph, metadata, bin, layer, inputs, outputs) {
+    constructor(metadata, bin, layer, inputs, outputs) {
         this._name = layer.name || '';
         this._id = layer.id;
         this._inputs = [];
@@ -507,7 +507,7 @@ openvino.Node = class {
             const name = blob.name;
             const offset = blob.offset;
             const size = blob.size;
-            const data = (bin && (offset + size) <= bin.length) ? bin.slice(offset, offset + size) : null;
+            let data = (bin && (offset + size) <= bin.length) ? bin.slice(offset, offset + size) : null;
             let dimensions = blob.shape || null;
             const kind = blob.kind || 'Blob';
             const id = blob.id || '';
@@ -518,6 +518,17 @@ openvino.Node = class {
                 'U8': 1, 'U16': 2, 'U32': 4, 'U64': 8
             };
             const itemSize = precisionMap[dataType];
+            const weight = (data, name, dimensions) => {
+                const shape = dimensions ? new openvino.TensorShape(dimensions) : null;
+                this._inputs.push(new openvino.Parameter(name, [
+                    new openvino.Argument(id, null, new openvino.Tensor(dataType, shape, data, kind))
+                ]));
+                const size = dimensions.reduce((a, b) => a * b, 1) * itemSize;
+                if (data && data.length !== size) {
+                    return data.slice(size, data.length);
+                }
+                return null;
+            };
             if (itemSize) {
                 switch (type + ':' + name) {
                     case 'FullyConnected:weights': {
@@ -541,15 +552,35 @@ openvino.Node = class {
                         break;
                     }
                     case 'LSTMCell:weights': {
+                        const input_size = inputs[0].type.shape.dimensions[1];
+                        const hidden_size = parseInt(attributes['hidden_size'], 10);
+                        data = weight(data, 'W', [ 4 * hidden_size, input_size ]);
+                        data = weight(data, 'R', [ 4 * hidden_size, hidden_size ]);
+                        break;
+                    }
+                    case 'LSTMCell:biases': {
+                        const hidden_size = parseInt(attributes['hidden_size'], 10);
+                        data = weight(data, 'B', [ 4 * hidden_size ]);
+                        break;
+                    }
+                    case 'GRUCell:weights': {
+                        const input_size = inputs[0].type.shape.dimensions[1];
+                        const hidden_size = parseInt(attributes['hidden_size'], 10);
+                        data = weight(data, 'W', [ 3 * hidden_size, input_size ]);
+                        data = weight(data, 'R', [ 3 * hidden_size, hidden_size ]);
+                        break;
+                    }
+                    case 'GRUCell:biases': {
+                        const linear_before_reset = parseInt(attributes['linear_before_reset'], 10);
                         const hidden_size = parseInt(attributes['hidden_size'], 10);
-                        dimensions = [ Math.floor(size / (itemSize * hidden_size)) , hidden_size ];
+                        dimensions = linear_before_reset ? [ 4 * hidden_size ] : [ 3 * hidden_size ];
+                        data = weight(data, 'B', dimensions);
                         break;
                     }
                     case 'ScaleShift:weights':
                     case 'ScaleShift:biases':
                     case 'Convolution:biases':
                     case 'Normalize:weights':
-                    case 'LSTMCell:biases':
                     case 'PReLU:weights': {
                         dimensions = [ Math.floor(size / itemSize) ];
                         break;
@@ -568,10 +599,9 @@ openvino.Node = class {
                         break;
                 }
             }
-            const shape = dimensions ? new openvino.TensorShape(dimensions) : null;
-            this._inputs.push(new openvino.Parameter(name, [
-                new openvino.Argument(id, null, new openvino.Tensor(dataType, shape, data, kind))
-            ]));
+            if (data) {
+                weight(data, name, dimensions);
+            }
         }
     }
 
@@ -679,7 +709,8 @@ openvino.Attribute = class {
                                 throw new openvino.Error("Unsupported attribute boolean value '" + value + "'.");
                         }
                         break;
-                    case 'int32': {
+                    case 'int32':
+                    case 'int64': {
                         const intValue = Number.parseInt(this._value, 10);
                         this._value = Number.isNaN(this._value - intValue) ? value : intValue;
                         break;

+ 25 - 31
test/models.js

@@ -658,43 +658,37 @@ const loadModel = (target, item) => {
                             argument.type.toString();
                         }
                         if (argument.initializer) {
-                            // console.log(argument.name);
                             argument.initializer.type.toString();
-                            const log = (/* message */) => {
-                                // console.log('  ' + message);
-                            };
                             const tensor = new sidebar.Tensor(argument.initializer);
                             if (tensor.layout !== '<' && tensor.layout !== '>' && tensor.layout !== '|' && tensor.layout !== 'sparse' && tensor.layout !== 'sparse.coo') {
-                                log("Tensor layout '" + tensor.layout + "' is not implemented.");
+                                throw new Error("Tensor layout '" + tensor.layout + "' is not implemented.");
                             }
-                            else if (tensor.empty) {
-                                log('Tensor data is empty.');
-                            }
-                            else if (tensor.type && tensor.type.dataType === '?') {
-                                log('Tensor data type is not defined.');
-                            }
-                            else if (tensor.type && !tensor.type.shape) {
-                                log('Tensor shape is not defined.');
-                            }
-                            else {
-                                tensor.toString();
-                                // tensor.value;
-                            }
-                            /*
-                            const python = require('../source/python');
-                            const tensor = argument.initializer;
-                            if (tensor.type && tensor.type.dataType !== '?') {
-                                let data_type = tensor.type.dataType;
-                                switch (data_type) {
-                                    case 'boolean': data_type = 'bool'; break;
+                            if (!tensor.empty) {
+                                if (tensor.type && tensor.type.dataType === '?') {
+                                    throw new Error('Tensor data type is not defined.');
+                                }
+                                else if (tensor.type && !tensor.type.shape) {
+                                    throw new Error('Tensor shape is not defined.');
+                                }
+                                else {
+                                    tensor.toString();
+                                    /*
+                                    const python = require('../source/python');
+                                    const tensor = argument.initializer;
+                                    if (tensor.type && tensor.type.dataType !== '?') {
+                                        let data_type = tensor.type.dataType;
+                                        switch (data_type) {
+                                            case 'boolean': data_type = 'bool'; break;
+                                        }
+                                        const execution = new python.Execution();
+                                        const bytes = execution.invoke('io.BytesIO', []);
+                                        const dtype = execution.invoke('numpy.dtype', [ data_type ]);
+                                        const array = execution.invoke('numpy.asarray', [ tensor.value, dtype ]);
+                                        execution.invoke('numpy.save', [ bytes, array ]);
+                                    }
+                                    */
                                 }
-                                const execution = new python.Execution();
-                                const bytes = execution.invoke('io.BytesIO', []);
-                                const dtype = execution.invoke('numpy.dtype', [ data_type ]);
-                                const array = execution.invoke('numpy.asarray', [ tensor.value, dtype ]);
-                                execution.invoke('numpy.save', [ bytes, array ]);
                             }
-                            */
                         }
                     }
                 }