예제 #1
0
파일: helper.py 프로젝트: harshit98/onnx
def make_model(graph, **kwargs):  # type: (GraphProto, **Any) -> ModelProto
    model = ModelProto()
    # Touch model.ir_version so it is stored as the version from which it is
    # generated.
    model.ir_version = IR_VERSION
    model.graph.CopyFrom(graph)

    opset_imports = None  # type: Optional[Sequence[OperatorSetIdProto]]
    opset_imports = kwargs.pop('opset_imports', None)  # type: ignore
    if opset_imports is not None:
        model.opset_import.extend(opset_imports)
    else:
        # Default import
        imp = model.opset_import.add()
        imp.version = defs.onnx_opset_version()

    for k, v in kwargs.items():
        # TODO: Does this work with repeated fields?
        setattr(model, k, v)
    return model
예제 #2
0
    def tensorflow_graph_to_onnx_graph(cls,
                                       graph_def,
                                       output,
                                       opset=(("", 0), ),
                                       name="graph"):
        """Converts a Tensorflow Graph Proto to an ONNX graph

    This function converts a Tensorflow Graph proto to an equivalent
    representation of ONNX graph.

    :param graph_def: Tensorflow Graph Proto object.
    :param output: A Tensorflow NodeDef object specifying which node
      to be taken as output of the ONNX graph.
    :param opset: Opset, which should be ((str domain: int version number),).
    :param name: The name of the output ONNX Graph.

    :returns: The equivalent ONNX Graph Proto object.
    """

        # This list holds the protobuf objects of type ValueInfoProto
        # representing the input to the converted ONNX graph.
        inputs_proto = []

        # This list holds the protobuf objects of type NodeProto
        # representing the ops in the converted ONNX graph.
        ops_proto = []

        # This dictionary contains a map from the name of the constant
        # op to the array of values it holds. This is useful because
        # tensorflow is less eager to know about input values at
        # graph construction time than ONNX. That is to say, some ONNX
        # attributes are input tensors in TF. This dictionary extracts
        # those values of constant tensors that are known at graph
        # construction time.
        consts = {}

        # Sometimes the constants are used as inputs to ops. This list
        # holds initializers that creates global constant tensors available
        # to be accessed by ops as inputs (as oppose to attributes which
        # is supplied by the `consts` map above).
        consts_proto = []

        node_tup = [(node.name, TensorflowNode(node))
                    for node in graph_def.node]

        for name, node in node_tup:

            if node.op == "Placeholder":
                # Tensorflow requires dtype to be known.
                # TODO: currently `dtype` is translated to `to`.
                onnx_type = node.attr["dtype"]
                shape = node.attr["shape"]
                input_proto = make_tensor_value_info(name, onnx_type, shape)
                inputs_proto.append(input_proto)
            elif node.op == "Const":
                const_dim = len(node.attr["value"].shape)

                consts[name] = node.attr["value"]
                raw_values = ([node.attr["value"].tolist()] if const_dim == 0
                              else node.attr["value"].flatten().tolist())
                if const_dim == 0:
                    values = [node.attr["value"]]
                else:
                    values = node.attr["value"]
                shape = np.array(values).shape
                consts_proto.append(
                    make_tensor(name=name,
                                data_type=node.attr["dtype"],
                                dims=shape,
                                vals=raw_values))
                input_proto = make_tensor_value_info(name, node.attr["dtype"],
                                                     shape)
                inputs_proto.append(input_proto)
            else:
                splitted_op_name = node.op.split(".")
                op_domain = "" if len(splitted_op_name) == 1 else ".".join(
                    splitted_op_name[:-1])
                op_name = splitted_op_name[-1]

                handler_name = "handle_" + op_name_to_lower(op_name)

                # TODO per domain frontend_tf_opset_version?
                versions = frontend_tf_opset_version[op_name_to_lower(op_name)]

                opset_dict = {}
                onnx_domain = defs.ONNX_DOMAIN
                for domain, version in opset:
                    if domain == "ai.onnx":
                        domain = ""
                    opset_dict[domain] = version
                    defs.ONNX_DOMAIN = domain
                    assert isinstance(
                        version, int
                    ) and (version <= defs.onnx_opset_version()) and (
                        version >= 0
                    ), "Opset should be an int less than or equal to {}, but {}: {}".format(
                        defs.onnx_opset_version(), type(version), version)
                    defs.ONNX_DOMAIN = onnx_domain

                opset_ver = opset_dict[op_domain]
                if opset_ver == 0:
                    version = max(versions)
                else:
                    versions = sorted(versions + [opset_ver])
                    version = versions[max(
                        [i
                         for i, v in enumerate(versions) if v == opset_ver]) -
                                       1]

                camel_domain = "".join(w.title() for w in op_domain.split("."))
                frontend_ver = "frontend_v{}".format(version)
                frontend_class_name = "{}TensorflowFrontend".format(
                    camel_domain)
                frontend_module = cls.frontend_version_cache.setdefault(
                    frontend_ver,
                    importlib.import_module("onnx_tf.frontends." +
                                            frontend_ver))
                if hasattr(frontend_module, frontend_class_name):
                    frontend = getattr(frontend_module, frontend_class_name)
                else:
                    assert NotImplementedError, \
                      "{} for domain {} is not implemented".format(frontend_ver, op_domain)

                # Check if specialized handler exists.
                if hasattr(frontend, handler_name):
                    method_to_call = getattr(frontend, handler_name)
                    node = method_to_call(node,
                                          consts=consts,
                                          node_dict=dict(node_tup))
                    if isinstance(node, list):
                        ops_proto.extend(node)
                    else:
                        ops_proto.append(node)
                elif node.op in TF_OP_STR_TO_ONNX_OP.keys():
                    # Remove tensorflow-specific attrs that are not
                    # needed/allowed in ONNX.
                    attr = cls.DEFAULT_TF_ATTR_PER_OP.get(node.op, {})
                    filtered_attr = dict(
                        filter(lambda pair: pair[0] not in TF_ATTR_TO_REMOVE,
                               node.attr.items()))
                    node_output = name
                    ops_proto.append(
                        make_node(TF_OP_STR_TO_ONNX_OP[node.op],
                                  node.inputs, [node_output],
                                  name=name,
                                  **filtered_attr))
                else:
                    raise NotImplementedError(
                        "{} op is not implemented.".format(node.op))

        output = TensorflowNode(output)
        # making output proto
        # TODO: deal with multi-output case.
        # TODO: default to BOOL, cf.
        # https://github.com/tensorflow/tensorflow/issues/14769
        output_onnx_type = output.attr.get("T", TensorProto.BOOL)
        output_proto = []
        for i in range(len(output.attr["_output_shapes"])):
            output_name = output.name + ":{}".format(
                i) if i > 0 else output.name
            output_proto.append(
                make_tensor_value_info(output_name, output_onnx_type,
                                       output.attr["_output_shapes"][i]))

        inputs = list(
            chain.from_iterable(map(lambda p: list(p.input), ops_proto)))

        # Remove proto in inputs_proto and consts_proto if proto is not used as input in ONNX
        inputs_proto = list(filter(lambda x: x.name in inputs, inputs_proto))
        consts_proto = list(filter(lambda x: x.name in inputs, consts_proto))

        return make_graph(ops_proto, name, inputs_proto, output_proto,
                          consts_proto)
예제 #3
0
                             onnx_node,
                             desc=desc,
                             expected_attributes=TopK_11.atts,
                             **options)
        if self.sorted not in (True, 1):
            raise RuntimeError(
                "TopK does not implement anything for sorted=0.")

    def _run(self, data, ink):  # pylint: disable=W0221
        """
        Runtime for operator *TopK*.
        The implementation is not the most efficient
        as it sorts everything then extracts the top *k*
        values.

        .. warning::
            ONNX specifications may be imprecise in case of negative value
            for axis. The implementation follows what :epkg:`onnxruntime`
            does in `top_k.cc
            <https://github.com/Microsoft/onnxruntime/blob/master/onnxruntime/core/providers/cpu/math/top_k.cc#L63>`_.
        """
        return _CommonTopK._common_run(self, data, ink, self.largest)


if onnx_opset_version() >= 11:
    TopK = TopK_11
elif onnx_opset_version() >= 10:
    TopK = TopK_10
else:
    TopK = TopK_1
예제 #4
0
def get_max_opset():
    """
    Returns the highest available onnx opset version.
    """
    from onnx.defs import onnx_opset_version
    return min(onnx_opset_version(), __max_supported_opset__)
예제 #5
0
  def tensorflow_graph_to_onnx_model(cls,
                                     graph_def,
                                     output,
                                     opset=0,
                                     producer_name="onnx-tensorflow",
                                     graph_name="graph",
                                     ignore_unimplemented=False,
                                     optimizer_passes=None):
    """Converts a Tensorflow Graph Proto to an ONNX model

    This function converts a Tensorflow Graph proto to an equivalent
    representation of ONNX model.

    :param graph_def: Tensorflow Graph Proto object.
    :param output: List of string or a string specifying the name
      of the output graph node.
    :param opset: Opset version number, list or tuple.
      Default is 0 means using latest version with domain ''.
      List or tuple items should be (str domain, int version number).
    :param producer_name: The name of the producer.
    :param graph_name: The name of the output ONNX Graph.
    :param ignore_unimplemented: Convert to ONNX model and ignore all the operators
      that are not currently supported by onnx-tensorflow.
      This is an experimental feature. By enabling this feature,
      the model would not be guaranteed to match the ONNX specifications.
    :param optimizer_passes: List of optimization names c.f.
      https://github.com/onnx/onnx/blob/master/onnx/optimizer.py for available
      optimization passes.

    :returns: The equivalent ONNX Model Proto object.
    """

    def get_node_by_name(nodes, name):
      for node in nodes:
        if node.name == name:
          return node
      raise ValueError(
          "Node {} is not found in the graph provided".format(name))

    if not isinstance(opset, (int, long, list, tuple)):
      raise TypeError("opset is expected to int, list or tuple, but {}.".format(
          type(opset)))
    if isinstance(opset, (int, long)):
      opset = [(defs.ONNX_DOMAIN, opset or defs.onnx_opset_version())]
    opset_imports = [make_opsetid(item[0], item[1]) for item in opset]

    if not isinstance(output, (list, tuple)):
      output = [output]

    output_nodes = [get_node_by_name(graph_def.node, o) for o in output]

    if "_output_shapes" not in output_nodes[0].attr:
      # Add infer_shapes to GraphDef
      graph_def = cls._add_infer_shapes(graph_def)
      output_nodes = [get_node_by_name(graph_def.node, o) for o in output]

    onnx_graph = cls.tensorflow_graph_to_onnx_graph(
        graph_def, output_nodes, opset, graph_name, ignore_unimplemented)
    onnx_model = make_model(
        onnx_graph, producer_name=producer_name, opset_imports=opset_imports)

    if isinstance(optimizer_passes, (list, tuple)) and optimizer_passes:
      onnx_model = optimize(onnx_model, optimizer_passes)

    return onnx_model
class TestNearestNeighbourConverter(unittest.TestCase):
    def _fit_model_binary_classification(self, model):
        iris = datasets.load_iris()
        X = iris.data[:, :3]
        y = iris.target
        y[y == 2] = 1
        model.fit(X, y)
        return model, X

    def _fit_model_multiclass_classification(self, model, use_string=False):
        iris = datasets.load_iris()
        X = iris.data[:, :3]
        y = iris.target
        if use_string:
            y = numpy.array(["cl%d" % _ for _ in y])
        model.fit(X, y)
        return model, X

    def _fit_model(self, model, n_targets=1, label_int=False):
        X, y = datasets.make_regression(n_features=4,
                                        random_state=0,
                                        n_targets=n_targets)
        if label_int:
            y = y.astype(numpy.int64)
        model.fit(X, y)
        return model, X

    @unittest.skipIf(
        StrictVersion(onnxruntime.__version__) < StrictVersion("0.5.0"),
        reason="not available")
    def test_model_knn_regressor(self):
        model, X = self._fit_model(KNeighborsRegressor(n_neighbors=2))
        model_onnx = convert_sklearn(model,
                                     "KNN regressor",
                                     [("input", FloatTensorType([None, 4]))],
                                     target_opset=TARGET_OPSET)
        self.assertIsNotNone(model_onnx)
        dump_data_and_model(X.astype(numpy.float32)[:7],
                            model,
                            model_onnx,
                            basename="SklearnKNeighborsRegressor")

    @unittest.skipIf(
        StrictVersion(onnxruntime.__version__) < StrictVersion("0.5.0"),
        reason="not available")
    @unittest.skipIf(StrictVersion(onnx.__version__) < StrictVersion("1.6.0"),
                     reason="not available")
    def test_model_knn_regressor_double(self):
        model, X = self._fit_model(KNeighborsRegressor(n_neighbors=2))
        model_onnx = convert_sklearn(model,
                                     "KNN regressor",
                                     [("input", DoubleTensorType([None, 4]))],
                                     target_opset=TARGET_OPSET,
                                     options={id(model): {
                                                  'optim': 'cdist'
                                              }},
                                     dtype=numpy.float64)
        self.assertIsNotNone(model_onnx)
        try:
            InferenceSession(model_onnx.SerializeToString())
        except OrtImpl as e:
            if ("Could not find an implementation for the node "
                    "To_TopK:TopK(11)") in str(e):
                # onnxruntime does not declare TopK(11) for double
                return
            raise e
        dump_data_and_model(X.astype(numpy.float64)[:7],
                            model,
                            model_onnx,
                            basename="SklearnKNeighborsRegressor64")

    @unittest.skipIf(
        StrictVersion(onnxruntime.__version__) < StrictVersion("0.5.0"),
        reason="not available")
    def test_model_knn_regressor_yint(self):
        model, X = self._fit_model(KNeighborsRegressor(n_neighbors=2),
                                   label_int=True)
        model_onnx = convert_sklearn(model,
                                     "KNN regressor",
                                     [("input", FloatTensorType([None, 4]))],
                                     target_opset=TARGET_OPSET)
        self.assertIsNotNone(model_onnx)
        dump_data_and_model(X.astype(numpy.float32)[:7],
                            model,
                            model_onnx,
                            basename="SklearnKNeighborsRegressorYInt")

    @unittest.skipIf(
        StrictVersion(onnxruntime.__version__) < StrictVersion("0.5.0"),
        reason="not available")
    def test_model_knn_regressor2_1(self):
        model, X = self._fit_model(KNeighborsRegressor(n_neighbors=1),
                                   n_targets=2)
        model_onnx = convert_sklearn(model,
                                     "KNN regressor",
                                     [("input", FloatTensorType([None, 4]))],
                                     target_opset=TARGET_OPSET)
        self.assertIsNotNone(model_onnx)
        dump_data_and_model(X.astype(numpy.float32)[:3],
                            model,
                            model_onnx,
                            basename="SklearnKNeighborsRegressor2")

    @unittest.skipIf(
        StrictVersion(onnxruntime.__version__) < StrictVersion("0.5.0"),
        reason="not available")
    @unittest.skipIf(StrictVersion(onnx.__version__) < StrictVersion("1.4.0"),
                     reason="not available")
    def test_model_knn_regressor2_1_opset(self):
        model, X = self._fit_model(KNeighborsRegressor(n_neighbors=1),
                                   n_targets=2)
        for op in [12, 11, 10, 9]:
            if op > TARGET_OPSET:
                continue
            with self.subTest(opset=op):
                model_onnx = convert_sklearn(
                    model,
                    "KNN regressor", [("input", FloatTensorType([None, 4]))],
                    target_opset=op)
                self.assertIsNotNone(model_onnx)
                dump_data_and_model(X.astype(numpy.float32)[:3],
                                    model,
                                    model_onnx,
                                    basename="SklearnKNeighborsRegressor2%d" %
                                    op)

    @unittest.skipIf(
        StrictVersion(onnxruntime.__version__) < StrictVersion("0.5.0"),
        reason="not available")
    def test_model_knn_regressor2_2(self):
        model, X = self._fit_model(KNeighborsRegressor(n_neighbors=2),
                                   n_targets=2)
        model_onnx = convert_sklearn(model,
                                     "KNN regressor",
                                     [("input", FloatTensorType([None, 4]))],
                                     target_opset=TARGET_OPSET)
        self.assertIsNotNone(model_onnx)
        dump_data_and_model(X.astype(numpy.float32)[:2],
                            model,
                            model_onnx,
                            basename="SklearnKNeighborsRegressor2")

    @unittest.skipIf(
        StrictVersion(onnxruntime.__version__) < StrictVersion("0.5.0"),
        reason="not available")
    @unittest.skipIf(TARGET_OPSET < 9, reason="needs higher target_opset")
    def test_model_knn_regressor_weights_distance_11(self):
        model, X = self._fit_model(
            KNeighborsRegressor(weights="distance",
                                algorithm="brute",
                                n_neighbors=1))
        for op in sorted(set([9, 10, 11, 12, TARGET_OPSET])):
            if op > TARGET_OPSET:
                continue
            with self.subTest(opset=op):
                model_onnx = convert_sklearn(
                    model,
                    "KNN regressor", [("input", FloatTensorType([None, 4]))],
                    target_opset=op)
                if op < 12 and model_onnx.ir_version > 6:
                    raise AssertionError(
                        "ir_version ({}, op={}) must be <= 6.".format(
                            model_onnx.ir_version, op))
                if op < 11 and model_onnx.ir_version > 5:
                    raise AssertionError(
                        "ir_version ({}, op={}) must be <= 5.".format(
                            model_onnx.ir_version, op))
                if op < 10 and model_onnx.ir_version > 4:
                    raise AssertionError(
                        "ir_version ({}, op={}) must be <= 4.".format(
                            model_onnx.ir_version, op))
                self.assertIsNotNone(model_onnx)
                dump_data_and_model(
                    X.astype(numpy.float32)[:3],
                    model,
                    model_onnx,
                    basename="SklearnKNeighborsRegressorWDist%d-Dec3" % op)

    @unittest.skipIf(
        StrictVersion(onnxruntime.__version__) < StrictVersion("0.5.0"),
        reason="not available")
    def test_model_knn_regressor_metric_cityblock(self):
        model, X = self._fit_model(KNeighborsRegressor(metric="cityblock"))
        model_onnx = convert_sklearn(model,
                                     "KNN regressor",
                                     [("input", FloatTensorType([None, 4]))],
                                     target_opset=TARGET_OPSET)
        self.assertIsNotNone(model_onnx)
        dump_data_and_model(
            X.astype(numpy.float32)[:7],
            model,
            model_onnx,
            basename="SklearnKNeighborsRegressorMetricCityblock")

    @unittest.skipIf(not onnx_built_with_ml(),
                     reason="Requires ONNX-ML extension.")
    @unittest.skipIf(
        StrictVersion(onnxruntime.__version__) < StrictVersion("0.5.0"),
        reason="not available")
    @unittest.skipIf(onnx_opset_version() < TARGET_OPSET,
                     reason="needs higher target_opset")
    def test_model_knn_classifier_binary_class(self):
        model, X = self._fit_model_binary_classification(
            KNeighborsClassifier())
        model_onnx = convert_sklearn(
            model,
            "KNN classifier binary",
            [("input", FloatTensorType([None, X.shape[1]]))],
            target_opset=TARGET_OPSET)
        self.assertIsNotNone(model_onnx)
        dump_data_and_model(X.astype(numpy.float32),
                            model,
                            model_onnx,
                            basename="SklearnKNeighborsClassifierBinary")

    @unittest.skipIf(not onnx_built_with_ml(),
                     reason="Requires ONNX-ML extension.")
    @unittest.skipIf(
        StrictVersion(onnxruntime.__version__) < StrictVersion("0.5.0"),
        reason="not available")
    def test_model_knn_classifier_multi_class(self):
        model, X = self._fit_model_multiclass_classification(
            KNeighborsClassifier())
        model_onnx = convert_sklearn(
            model,
            "KNN classifier multi-class",
            [("input", FloatTensorType([None, X.shape[1]]))],
            target_opset=TARGET_OPSET)
        self.assertIsNotNone(model_onnx)
        dump_data_and_model(X.astype(numpy.float32),
                            model,
                            model_onnx,
                            basename="SklearnKNeighborsClassifierMulti")

    @unittest.skipIf(not onnx_built_with_ml(),
                     reason="Requires ONNX-ML extension.")
    @unittest.skipIf(
        StrictVersion(onnxruntime.__version__) < StrictVersion("0.5.0"),
        reason="not available")
    def test_model_knn_classifier_multi_class_string(self):
        model, X = self._fit_model_multiclass_classification(
            KNeighborsClassifier(), use_string=True)
        model_onnx = convert_sklearn(model,
                                     "KNN classifier multi-class",
                                     [("input", FloatTensorType([None, 3]))],
                                     target_opset=TARGET_OPSET)
        self.assertIsNotNone(model_onnx)
        dump_data_and_model(X.astype(numpy.float32),
                            model,
                            model_onnx,
                            basename="SklearnKNeighborsClassifierMulti")

    @unittest.skipIf(
        StrictVersion(onnxruntime.__version__) < StrictVersion("0.5.0"),
        reason="not available")
    def test_model_knn_classifier_weights_distance(self):
        model, X = self._fit_model_multiclass_classification(
            KNeighborsClassifier(weights='distance'))
        model_onnx = convert_sklearn(model,
                                     'KNN classifier',
                                     [('input', FloatTensorType([None, 3]))],
                                     target_opset=TARGET_OPSET)
        self.assertIsNotNone(model_onnx)
        dump_data_and_model(
            X.astype(numpy.float32)[:7],
            model,
            model_onnx,
            basename="SklearnKNeighborsClassifierWeightsDistance")

    @unittest.skipIf(
        StrictVersion(onnxruntime.__version__) < StrictVersion("0.5.0"),
        reason="not available")
    def test_model_knn_classifier_metric_cityblock(self):
        model, X = self._fit_model_multiclass_classification(
            KNeighborsClassifier(metric='cityblock'))
        model_onnx = convert_sklearn(model,
                                     'KNN classifier',
                                     [('input', FloatTensorType([None, 3]))],
                                     target_opset=TARGET_OPSET)
        self.assertIsNotNone(model_onnx)
        dump_data_and_model(
            X.astype(numpy.float32)[:7],
            model,
            model_onnx,
            basename="SklearnKNeighborsClassifierMetricCityblock")

    @unittest.skipIf(
        StrictVersion(onnxruntime.__version__) < StrictVersion("0.5.0"),
        reason="not available")
    def test_model_knn_classifier_multilabel(self):
        model, X_test = fit_multilabel_classification_model(
            KNeighborsClassifier(),
            n_classes=7,
            n_labels=3,
            n_samples=100,
            n_features=10)
        options = {id(model): {'zipmap': False}}
        model_onnx = convert_sklearn(
            model,
            "scikit-learn KNN Classifier",
            [("input", FloatTensorType([None, X_test.shape[1]]))],
            options=options,
            target_opset=TARGET_OPSET)
        self.assertTrue(model_onnx is not None)
        assert 'zipmap' not in str(model_onnx).lower()
        dump_data_and_model(
            X_test,
            model,
            model_onnx,
            basename="SklearnKNNClassifierMultiLabel-Out0",
            allow_failure="StrictVersion("
            "onnxruntime.__version__) <= StrictVersion('0.2.1')",
        )

    @unittest.skipIf(
        StrictVersion(onnxruntime.__version__) < StrictVersion("0.5.0"),
        reason="not available")
    def test_model_knn_regressor_int(self):
        model, X = self._fit_model(KNeighborsRegressor())
        X = X.astype(numpy.int64)
        model_onnx = convert_sklearn(
            model,
            "KNN regressor", [("input", Int64TensorType([None, X.shape[1]]))],
            target_opset=TARGET_OPSET)
        self.assertIsNotNone(model_onnx)
        dump_data_and_model(X,
                            model,
                            model_onnx,
                            basename="SklearnKNNRegressorInt-Dec4")

    @unittest.skipIf(
        StrictVersion(onnxruntime.__version__) < StrictVersion("0.5.0"),
        reason="not available")
    def test_model_knn_regressor_equal(self):
        X, y = datasets.make_regression(n_samples=1000,
                                        n_features=100,
                                        random_state=42)
        X = X.astype(numpy.int64)
        X_train, X_test, y_train, y_test = train_test_split(X,
                                                            y,
                                                            test_size=0.5,
                                                            random_state=42)
        model = KNeighborsRegressor(algorithm='brute',
                                    metric='manhattan').fit(X_train, y_train)
        model_onnx = convert_sklearn(
            model,
            'knn', [('input', Int64TensorType([None, X_test.shape[1]]))],
            target_opset=TARGET_OPSET)
        exp = model.predict(X_test)

        sess = InferenceSession(model_onnx.SerializeToString())
        res = sess.run(None, {'input': numpy.array(X_test)})[0].ravel()

        # The conversion has discrepencies when
        # neighbours are at the exact same distance.
        maxd = 1000
        accb = numpy.abs(exp - res) > maxd
        ind = [i for i, a in enumerate(accb) if a == 1]
        assert len(ind) == 0

        accp = numpy.abs(exp - res) < maxd
        acc = numpy.sum(accp)
        ratio = acc * 1.0 / res.shape[0]
        assert ratio >= 0.7
        # assert_almost_equal(exp, res)

    @unittest.skipIf(
        StrictVersion(onnxruntime.__version__) < StrictVersion("0.5.0"),
        reason="not available")
    def test_model_knn_multi_class_nocl(self):
        model, X = fit_classification_model(KNeighborsClassifier(),
                                            2,
                                            label_string=True)
        model_onnx = convert_sklearn(
            model,
            "KNN multi-class nocl",
            [("input", FloatTensorType([None, X.shape[1]]))],
            options={id(model): {
                         'nocl': True
                     }},
            target_opset=TARGET_OPSET)
        self.assertIsNotNone(model_onnx)
        sonx = str(model_onnx)
        assert 'classlabels_strings' not in sonx
        assert 'cl0' not in sonx
        dump_data_and_model(X,
                            model,
                            model_onnx,
                            classes=model.classes_,
                            basename="SklearnKNNMultiNoCl",
                            verbose=False,
                            allow_failure="StrictVersion(onnx.__version__)"
                            " < StrictVersion('1.2') or "
                            "StrictVersion(onnxruntime.__version__)"
                            " <= StrictVersion('0.2.1')")

    @unittest.skipIf(
        StrictVersion(onnxruntime.__version__) < StrictVersion("0.5.0"),
        reason="not available")
    def test_model_knn_regressor2_2_pipee(self):
        pipe = make_pipeline(StandardScaler(), KNeighborsClassifier())
        model, X = self._fit_model_binary_classification(pipe)
        model_onnx = convert_sklearn(
            model,
            "KNN pipe", [("input", FloatTensorType([None, X.shape[1]]))],
            target_opset=TARGET_OPSET)
        self.assertIsNotNone(model_onnx)
        dump_data_and_model(X.astype(numpy.float32)[:2],
                            model,
                            model_onnx,
                            basename="SklearnKNeighborsRegressorPipe2")

    @unittest.skipIf(
        StrictVersion(onnxruntime.__version__) < StrictVersion("0.5.0"),
        reason="not available")
    def test_onnx_test_knn_transform(self):
        iris = datasets.load_iris()
        X, _ = iris.data, iris.target

        X_train, X_test = train_test_split(X, random_state=11)
        clr = NearestNeighbors(n_neighbors=3)
        clr.fit(X_train)

        for to in (9, 10, 11):
            if to > onnx_opset_version():
                break
            model_def = to_onnx(clr,
                                X_train.astype(numpy.float32),
                                target_opset=to)
            oinf = InferenceSession(model_def.SerializeToString())

            X_test = X_test[:3]
            y = oinf.run(None, {'X': X_test.astype(numpy.float32)})
            dist, ind = clr.kneighbors(X_test)

            assert_almost_equal(dist, DataFrame(y[1]).values, decimal=5)
            assert_almost_equal(ind, y[0])

    @unittest.skipIf(NeighborhoodComponentsAnalysis is None,
                     reason="new in 0.22")
    def test_sklearn_nca_default(self):
        model, X_test = fit_classification_model(
            NeighborhoodComponentsAnalysis(random_state=42), 3)
        model_onnx = convert_sklearn(
            model,
            "NCA",
            [("input", FloatTensorType((None, X_test.shape[1])))],
        )
        self.assertIsNotNone(model_onnx)
        dump_data_and_model(
            X_test,
            model,
            model_onnx,
            basename="SklearnNCADefault",
        )

    @unittest.skipIf(NeighborhoodComponentsAnalysis is None,
                     reason="new in 0.22")
    def test_sklearn_nca_identity(self):
        model, X_test = fit_classification_model(
            NeighborhoodComponentsAnalysis(init='identity',
                                           max_iter=4,
                                           random_state=42), 3)
        model_onnx = convert_sklearn(
            model,
            "NCA",
            [("input", FloatTensorType((None, X_test.shape[1])))],
        )
        self.assertIsNotNone(model_onnx)
        dump_data_and_model(
            X_test,
            model,
            model_onnx,
            basename="SklearnNCAIdentity",
        )

    @unittest.skipIf(NeighborhoodComponentsAnalysis is None,
                     reason="new in 0.22")
    def test_sklearn_nca_double(self):
        model, X_test = fit_classification_model(
            NeighborhoodComponentsAnalysis(n_components=2,
                                           max_iter=4,
                                           random_state=42), 3)
        X_test = X_test.astype(numpy.float64)
        model_onnx = convert_sklearn(
            model,
            "NCA",
            [("input", DoubleTensorType((None, X_test.shape[1])))],
        )
        self.assertIsNotNone(model_onnx)
        dump_data_and_model(
            X_test,
            model,
            model_onnx,
            basename="SklearnNCADouble",
        )

    @unittest.skipIf(NeighborhoodComponentsAnalysis is None,
                     reason="new in 0.22")
    def test_sklearn_nca_int(self):
        model, X_test = fit_classification_model(
            NeighborhoodComponentsAnalysis(init='pca',
                                           max_iter=4,
                                           random_state=42),
            3,
            is_int=True)
        model_onnx = convert_sklearn(
            model,
            "NCA",
            [("input", Int64TensorType((None, X_test.shape[1])))],
        )
        self.assertIsNotNone(model_onnx)
        dump_data_and_model(
            X_test,
            model,
            model_onnx,
            basename="SklearnNCAInt",
        )

    @unittest.skipIf(KNeighborsTransformer is None, reason="new in 0.22")
    def test_sklearn_k_neighbours_transformer_distance(self):
        model, X_test = fit_classification_model(
            KNeighborsTransformer(n_neighbors=4, mode='distance'), 2)
        model_onnx = convert_sklearn(
            model,
            "KNN transformer",
            [("input", FloatTensorType((None, X_test.shape[1])))],
        )
        self.assertIsNotNone(model_onnx)
        dump_data_and_model(
            X_test,
            model,
            model_onnx,
            basename="SklearnKNNTransformerDistance",
        )

    @unittest.skipIf(KNeighborsTransformer is None, reason="new in 0.22")
    def test_sklearn_k_neighbours_transformer_connectivity(self):
        model, X_test = fit_classification_model(
            KNeighborsTransformer(n_neighbors=3, mode='connectivity'), 3)
        model_onnx = convert_sklearn(
            model,
            "KNN transformer",
            [("input", FloatTensorType((None, X_test.shape[1])))],
        )
        self.assertIsNotNone(model_onnx)
        dump_data_and_model(
            X_test,
            model,
            model_onnx,
            basename="SklearnKNNTransformerConnectivity",
        )

    @unittest.skipIf(KNNImputer is None, reason="new in 0.22")
    @unittest.skipIf(
        (StrictVersion(onnx.__version__) < StrictVersion("1.4.1")),
        reason="ConstantOfShape op not available")
    def test_sklearn_knn_imputer(self):
        x_train = numpy.array([[1, 2, numpy.nan, 12], [3, numpy.nan, 3, 13],
                               [1, 4, numpy.nan, 1], [numpy.nan, 4, 3, 12]],
                              dtype=numpy.float32)
        x_test = numpy.array(
            [[1.3, 2.4, numpy.nan, 1], [-1.3, numpy.nan, 3.1, numpy.nan]],
            dtype=numpy.float32)
        model = KNNImputer(n_neighbors=3, metric='nan_euclidean').fit(x_train)
        for opset in [9, 10, 11, 12]:
            if opset > TARGET_OPSET:
                continue
            model_onnx = convert_sklearn(
                model,
                "KNN imputer",
                [("input", FloatTensorType((None, x_test.shape[1])))],
                target_opset=opset)
            self.assertIsNotNone(model_onnx)
            dump_data_and_model(x_test,
                                model,
                                model_onnx,
                                basename="SklearnKNNImputer%d" % opset)

    @unittest.skipIf(KNNImputer is None, reason="new in 0.22")
    @unittest.skipIf(
        (StrictVersion(onnx.__version__) < StrictVersion("1.4.1")),
        reason="ConstantOfShape op not available")
    def test_sklearn_knn_imputer_cdist(self):
        x_train = numpy.array([[1, 2, numpy.nan, 12], [3, numpy.nan, 3, 13],
                               [1, 4, numpy.nan, 1], [numpy.nan, 4, 3, 12]],
                              dtype=numpy.float32)
        x_test = numpy.array(
            [[1.3, 2.4, numpy.nan, 1], [-1.3, numpy.nan, 3.1, numpy.nan]],
            dtype=numpy.float32)
        model = KNNImputer(n_neighbors=3, metric='nan_euclidean').fit(x_train)

        with self.assertRaises(NameError):
            convert_sklearn(model,
                            "KNN imputer",
                            [("input", FloatTensorType(
                                (None, x_test.shape[1])))],
                            target_opset=TARGET_OPSET,
                            options={id(model): {
                                         'optim2': 'cdist'
                                     }})

        for opset in [12, 11, 10, 9]:
            if opset > TARGET_OPSET:
                continue
            model_onnx = convert_sklearn(
                model,
                "KNN imputer",
                [("input", FloatTensorType((None, x_test.shape[1])))],
                target_opset=opset,
                options={id(model): {
                             'optim': 'cdist'
                         }})
            self.assertIsNotNone(model_onnx)
            self.assertIn('op_type: "cdist"', str(model_onnx).lower())
            self.assertNotIn('scan', str(model_onnx).lower())
            dump_data_and_model(x_test,
                                model,
                                model_onnx,
                                basename="SklearnKNNImputer%dcdist" % opset)

    @unittest.skipIf(not onnx_built_with_ml(),
                     reason="Requires ONNX-ML extension.")
    @unittest.skipIf(
        StrictVersion(onnxruntime.__version__) < StrictVersion("0.5.0"),
        reason="not available")
    @unittest.skipIf(onnx_opset_version() < 11,
                     reason="needs higher target_opset")
    def test_model_knn_iris_regressor_multi_reg(self):
        iris = datasets.load_iris()
        X = iris.data.astype(numpy.float32)
        y = iris.target.astype(numpy.float32)
        y = numpy.vstack([y, 1 - y, y + 10]).T
        model = KNeighborsRegressor(algorithm='brute',
                                    weights='distance',
                                    n_neighbors=7)
        model.fit(X[:13], y[:13])
        onx = to_onnx(model,
                      X[:1],
                      options={id(model): {
                                   'optim': 'cdist'
                               }},
                      target_opset=TARGET_OPSET)
        dump_data_and_model(X.astype(numpy.float32)[:7],
                            model,
                            onx,
                            basename="SklearnKNeighborsRegressorMReg")

    @unittest.skipIf(not onnx_built_with_ml(),
                     reason="Requires ONNX-ML extension.")
    @unittest.skipIf(
        StrictVersion(onnxruntime.__version__) < StrictVersion("0.5.0"),
        reason="not available")
    @unittest.skipIf(onnx_opset_version() < 11,
                     reason="needs higher target_opset")
    def test_model_knn_iris_classifier_multi_reg2_weight(self):
        iris = datasets.load_iris()
        X = iris.data.astype(numpy.float32)
        y = iris.target.astype(numpy.int64)
        y = numpy.vstack([(y + 1) % 2, y % 2]).T
        model = KNeighborsClassifier(algorithm='brute',
                                     weights='distance',
                                     n_neighbors=7)
        model.fit(X[:13], y[:13])
        onx = to_onnx(model,
                      X[:1],
                      options={id(model): {
                                   'optim': 'cdist',
                                   'zipmap': False
                               }},
                      target_opset=TARGET_OPSET)
        dump_data_and_model(X.astype(numpy.float32)[:11],
                            model,
                            onx,
                            basename="SklearnKNeighborsClassifierMReg2-Out0")

    @unittest.skipIf(not onnx_built_with_ml(),
                     reason="Requires ONNX-ML extension.")
    @unittest.skipIf(
        StrictVersion(onnxruntime.__version__) < StrictVersion("0.5.0"),
        reason="not available")
    @unittest.skipIf(onnx_opset_version() < 11,
                     reason="needs higher target_opset")
    def test_model_knn_iris_classifier_multi_reg3_weight(self):
        iris = datasets.load_iris()
        X = iris.data.astype(numpy.float32)
        y = iris.target.astype(numpy.int64)
        y = numpy.vstack([y % 2, y % 2, (y + 1) % 2]).T
        model = KNeighborsClassifier(algorithm='brute',
                                     weights='distance',
                                     n_neighbors=7)
        model.fit(X[:13], y[:13])
        onx = to_onnx(model,
                      X[:1],
                      options={id(model): {
                                   'optim': 'cdist',
                                   'zipmap': False
                               }},
                      target_opset=TARGET_OPSET)
        dump_data_and_model(X.astype(numpy.float32)[:11],
                            model,
                            onx,
                            basename="SklearnKNeighborsClassifierMReg3-Out0")
예제 #7
0
    def create_onnx_graph_proto(self, sym, params, in_shape, in_type, verbose=False, opset_version=None):
        """Convert MXNet graph to ONNX graph

        Parameters
        ----------
        sym : :class:`~mxnet.symbol.Symbol`
            MXNet symbol object
        params : dict of ``str`` to :class:`~mxnet.ndarray.NDArray`
            Dict of converted parameters stored in ``mxnet.ndarray.NDArray`` format
        in_shape : List of tuple
            Input shape of the model e.g [(1,3,224,224)]
        in_type : data type
            Input data type e.g. np.float32
        verbose : Boolean
            If true will print logs of the model conversion
        opset_version : Int
            ONNX opset version to use for export, defaults to latest supported by onnx package

        Returns
        -------
        graph : GraphProto
            ONNX graph
        """
        try:
            from onnx import (checker, helper, NodeProto, ValueInfoProto, TensorProto)
            from onnx.helper import make_tensor_value_info
            from onnx.defs import onnx_opset_version
        except ImportError:
            raise ImportError("Onnx and protobuf need to be installed. "
                              + "Instructions to install - https://github.com/onnx/onnx")

        if opset_version is None:
            opset_version = onnx_opset_version()

        # When MXNet model is saved to json file , MXNet adds a node for label.
        # The name of this node is, name of the last node + "_label" ( i.e if last node
        # name is "Softmax", this node will have a name "Softmax_label". Also, the new node
        # will always be second last node in the json graph.
        # Deriving the output_label name.
        output_label = sym.get_internals()[len(sym.get_internals()) - 1].name + "_label"

        weights = MXNetGraph.convert_weights_to_numpy(params)

        mx_graph = json.loads(sym.tojson())["nodes"]

        initializer = []
        all_processed_nodes = []
        onnx_processed_nodes = []
        onnx_processed_inputs = []
        onnx_processed_outputs = []
        index_lookup = []

        # Determine output and internal shapes
        graph_outputs = MXNetGraph.get_outputs(sym, params, in_shape, output_label)
        graph_shapes = MXNetGraph.get_outputs(sym.get_internals(), params, in_shape, output_label, verbose=False)

        graph_input_idx = 0
        for idx, node in enumerate(mx_graph):
            op = node["op"]
            name = node["name"]
            if verbose:
                logging.info("Converting idx: %d, op: %s, name: %s", idx, op, name)

            # A node is an input node if its op_name is "null" and is not
            # in params dict
            if op == "null" and name not in params:
                # Handling graph input

                # Skipping output_label node, as this node is not part of graph
                # Refer "output_label" assignment above for more details.
                if name == output_label:
                    continue
                converted = MXNetGraph.convert_layer(
                    node,
                    is_input=True,
                    mx_graph=mx_graph,
                    weights=weights,
                    in_shape=in_shape[graph_input_idx],
                    in_type=in_type,
                    proc_nodes=all_processed_nodes,
                    graph_shapes=graph_shapes,
                    initializer=initializer,
                    index_lookup=index_lookup)
                graph_input_idx += 1

            else:
                # Handling graph layers
                converted = MXNetGraph.convert_layer(
                    node,
                    is_input=False,
                    mx_graph=mx_graph,
                    weights=weights,
                    in_shape=in_shape,
                    in_type=in_type,
                    proc_nodes=all_processed_nodes,
                    graph_shapes=graph_shapes,
                    initializer=initializer,
                    index_lookup=index_lookup,
                    idx=idx,
                    opset_version=opset_version
                )

            if isinstance(converted, list):
                # Iterate for all converted nodes
                for converted_node in converted:
                    # If converted node is ValueInfoProto, add it in inputs
                    if isinstance(converted_node, ValueInfoProto):
                        onnx_processed_inputs.append(converted_node)
                    # If converted node is NodeProto, add it in processed nodes list
                    elif isinstance(converted_node, NodeProto):
                        onnx_processed_nodes.append(converted_node)
                        # some operators have multiple outputs,
                        # therefore, check all output node names
                        node_names = list(converted_node.output)
                        for nodename in node_names:
                            if nodename in graph_outputs:
                                onnx_processed_outputs.append(
                                    make_tensor_value_info(
                                        name=nodename,
                                        elem_type=in_type,
                                        shape=graph_outputs[nodename]
                                    )
                                )
                                if verbose:
                                    logging.info("Output node is: %s", nodename)
                    elif isinstance(converted_node, TensorProto):
                        raise ValueError("Did not expect TensorProto")
                    else:
                        raise ValueError("node is of an unrecognized type: %s" % type(node))

                    all_processed_nodes.append(converted_node)

                if idx > 0:
                    # Handling extra node added to the graph if the MXNet model was
                    # saved to json file,
                    # refer "output_label" initialization above for more details.
                    # if extra node was added then prev_index to the last node is adjusted.
                    if idx == (len(mx_graph) - 1) and \
                            mx_graph[len(mx_graph)-2]["name"] == output_label:
                        prev_index = index_lookup[idx - 2]
                    else:
                        prev_index = index_lookup[idx - 1]

                    index_lookup.append(prev_index+len(converted))
                else:
                    index_lookup.append(len(converted) - 1)
            else:
                logging.info("Operator converter function should always return a list")

        graph = helper.make_graph(
            onnx_processed_nodes,
            "mxnet_converted_model",
            onnx_processed_inputs,
            onnx_processed_outputs
        )

        graph.initializer.extend(initializer)

        checker.check_graph(graph)
        return graph
예제 #8
0
        elif hasattr(self, 'value_floats') and self.value_floats is not None:
            self.cst = self.value_floats
        elif hasattr(self, 'value_int') and self.value_int is not None:
            self.cst = self.value_int
        elif hasattr(self, 'value_ints') and self.value_ints is not None:
            self.cst = self.value_ints
        elif hasattr(self, 'value_string') and self.value_string is not None:
            self.cst = self.value_string
        elif hasattr(self, 'value_strings') and self.value_strings is not None:
            self.cst = self.value_strings
        elif hasattr(self, 'value') and self.value is not None:
            self.cst = self.value
        else:
            raise AttributeError(
                "No constant is defined for operator 'Constant'.")

    def _run(self):  # pylint: disable=W0221
        return (self.cst, )

    def _infer_shapes(self):  # pylint: disable=W0221
        # pref = str(hex(id(self))[2:])
        return (ShapeObject(self.cst.shape, self.cst.dtype), )


if onnx_opset_version() >= 12:
    Constant = Constant_12
elif onnx_opset_version() >= 11:  # pragma: no cover
    Constant = Constant_11
else:  # pragma: no cover
    Constant = Constant_9
예제 #9
0
    def create_onnx_graph_proto(self,
                                sym,
                                params,
                                in_shapes,
                                in_types,
                                verbose=False,
                                opset_version=None,
                                dynamic=True,
                                dynamic_input_shapes=None):
        """Convert MXNet graph to ONNX graph

        Parameters
        ----------
        sym : :class:`~mxnet.symbol.Symbol`
            MXNet symbol object
        params : dict of ``str`` to :class:`~mxnet.ndarray.NDArray`
            Dict of converted parameters stored in ``mxnet.ndarray.NDArray`` format
        in_shapes : List of tuple
            Input shape of the model e.g [(1,3,224,224)]
        in_types : List of Int
            Input ONNX data types
        verbose : Boolean
            If true will print logs of the model conversion
        opset_version : Int
            ONNX opset version to use for export, defaults to latest supported by onnx package
        dynamic: Boolean
            If True will allow for dynamic input shapes to the model
        dynamic_input_shapes: list of tuple
            Specifies the dynamic input_shapes. If None then all dimensions are set to None

        Returns
        -------
        graph : GraphProto
            ONNX graph
        """
        try:
            from onnx import (helper, NodeProto, ValueInfoProto, TensorProto)
            from onnx.helper import make_tensor_value_info
            from onnx.defs import onnx_opset_version
        except ImportError:
            raise ImportError(
                "Onnx and protobuf need to be installed. " +
                "Instructions to install - https://github.com/onnx/onnx")

        if opset_version is None:
            opset_version = onnx_opset_version()

        # When MXNet model is saved to json file , MXNet adds a node for label.
        # The name of this node is, name of the last node + "_label" ( i.e if last node
        # name is "Softmax", this node will have a name "Softmax_label". Also, the new node
        # will always be second last node in the json graph.
        # Deriving the output_label name.
        output_label = sym.get_internals()[len(sym.get_internals()) -
                                           1].name + "_label"

        weights = MXNetGraph.convert_weights_to_numpy(params)

        mx_graph = json.loads(sym.tojson())["nodes"]

        class NodeOutput:
            def __init__(self, name, dtype):
                self.name = name
                self.dtype = np.dtype(dtype)

        initializer = []
        all_processed_nodes = []
        onnx_processed_nodes = []
        onnx_processed_inputs = []
        onnx_processed_outputs = []
        outputs_lookup = []

        # Determine graph output names, shapes, and dtypes. Also update in_shapes
        in_shapes, graph_outputs = MXNetGraph.get_outputs(
            sym, params, in_shapes, output_label, in_types, dynamic,
            dynamic_input_shapes)
        appeared_names = set()
        graph_input_idx = 0
        for idx, node in enumerate(mx_graph):
            op = node["op"]
            # check if the current node has the same name as nodes before
            if node["name"] in appeared_names:
                node["name"] = 'idx_' + str(idx) + '_' + node["name"]
            else:
                appeared_names.add(node["name"])
            name = node["name"]
            if verbose:
                logging.info("Converting idx: %d, op: %s, name: %s", idx, op,
                             name)

            # A node is an input node if its op_name is "null" and is not
            # in params dict
            if op == "null" and name not in params:
                # Handle graph input

                # Skip output_label node, as this node is not part of graph
                # Refer to "output_label" assignment above for more details.
                if name == output_label:
                    continue

                converted, dtypes = MXNetGraph.convert_layer(
                    node,
                    is_input=True,
                    mx_graph=mx_graph,
                    weights=weights,
                    in_shape=in_shapes[graph_input_idx],
                    in_type=in_types[graph_input_idx],
                    proc_nodes=all_processed_nodes,
                    initializer=initializer,
                    outputs_lookup=outputs_lookup)
                graph_input_idx += 1
            else:
                # Handle graph layers
                converted, dtypes = MXNetGraph.convert_layer(
                    node,
                    is_input=False,
                    mx_graph=mx_graph,
                    weights=weights,
                    proc_nodes=all_processed_nodes,
                    initializer=initializer,
                    outputs_lookup=outputs_lookup,
                    idx=idx,
                    opset_version=opset_version)
            if isinstance(converted, list):
                # Collect all the node's output names
                node_possible_names = [name
                                       ] + [name + str(i) for i in range(100)]
                node_output_names = []
                # Collect all the graph's output names
                graph_output_names = []
                # Iterate for all converted nodes
                for converted_node in converted:
                    # If converted node is ValueInfoProto, add it in inputs
                    if isinstance(converted_node, ValueInfoProto):
                        onnx_processed_inputs.append(converted_node)
                    # If converted node is NodeProto, add it in processed nodes list
                    elif isinstance(converted_node, NodeProto):
                        onnx_processed_nodes.append(converted_node)
                        # some operators have multiple outputs,
                        # therefore, check all output node names
                        node_names = list(converted_node.output)
                        for nodename in node_names:
                            if nodename in node_possible_names:
                                node_output_names.append(nodename)
                            if nodename in graph_outputs:
                                graph_output_names.append(nodename)
                                if verbose:
                                    logging.info("Output node is: %s",
                                                 nodename)
                    elif isinstance(converted_node, TensorProto):
                        raise ValueError("Did not expect TensorProto")
                    else:
                        raise ValueError(
                            "node is of an unrecognized type: %s" % type(node))

                    all_processed_nodes.append(converted_node)

                # if node_output_names is empty then we use the last returned node as output
                if not node_output_names:
                    node_output_names = [converted[-1].name]
                # process node outputs (sort by output index)
                def str2int(s, l):
                    if len(s) == l:
                        return -1
                    else:
                        return int(s[l:])

                node_output_names = sorted(node_output_names,
                                           key=lambda x: str2int(x, len(name)))

                # match the output names to output dtypes
                if dtypes is not None:
                    assert len(node_output_names) == len(dtypes)
                    node_outputs = [
                        NodeOutput(node_output_names[i], dtypes[i])
                        for i in range(len(dtypes))
                    ]
                else:
                    # in case dtypes is None, we just default to the dtype of the first input
                    assert len(node["inputs"]) > 0
                    first_input = node["inputs"][0]
                    first_input_dtype = outputs_lookup[first_input[0]][
                        first_input[1]].dtype
                    node_outputs = [
                        NodeOutput(n, first_input_dtype)
                        for n in node_output_names
                    ]
                outputs_lookup.append(node_outputs)

                # process graph outputs (sort by alphabetical order)
                graph_output_names.sort()
                for nodename in graph_output_names:
                    onnx_processed_outputs.append(
                        make_tensor_value_info(
                            name=nodename,
                            elem_type=graph_outputs[nodename]['dtype'],
                            shape=graph_outputs[nodename]['shape']))

            else:
                logging.info(
                    "Operator converter function should always return a list")

        # sometimes the graph output can also be in the intializer
        for i in initializer:
            if i.name in graph_outputs:
                onnx_processed_outputs.append(
                    make_tensor_value_info(
                        name=i.name,
                        elem_type=graph_outputs[i.name]['dtype'],
                        shape=graph_outputs[i.name]['shape']))

        graph = helper.make_graph(onnx_processed_nodes,
                                  "mxnet_converted_model",
                                  onnx_processed_inputs,
                                  onnx_processed_outputs)

        graph.initializer.extend(initializer)

        return graph
예제 #10
0
    def tensorflow_graph_to_onnx_graph(cls,
                                       tf_graph,
                                       opset=((defs.ONNX_DOMAIN,
                                               defs.onnx_opset_version()), ),
                                       ignore_unimplemented=False):
        """Converts a TensorflowGraph to an ONNX graph

    This function converts a TensorflowGraph to an equivalent
    representation of ONNX graph.

    :param tf_graph: TensorflowGraph object.
    :param opset: Opset, which should be ((str domain: int version number),).
    :param ignore_unimplemented: Convert to ONNX model and ignore all the operators
      that are not currently supported by onnx-tensorflow.
      This is an experimental feature. By enabling this feature,
      the graph would not be guaranteed to match the ONNX specifications.

    :returns: The equivalent ONNX Graph Proto object.
    """
        onnx_graph = OnnxGraph(tf_graph.graph_name)
        exception.IGNORE_UNIMPLEMENTED = ignore_unimplemented
        training_ops_to_remove = ["RandomShuffleQueueV2"]

        opset_dict = {}
        for domain, version in opset:
            if domain == "ai.onnx":
                domain = defs.ONNX_DOMAIN
            opset_dict[domain] = version

        handlers = get_all_frontend_handlers(opset_dict)

        node_tup = [(n.name, n) for n in tf_graph.nodes]
        for name, node in node_tup:
            if node.op_type == "Placeholder":
                onnx_graph.add_input_proto(node)
            elif node.op_type == "Const":
                onnx_graph.add_const(node)
                onnx_graph.add_const_proto(node)
                onnx_graph.add_input_proto(node)
            elif node.op_type in training_ops_to_remove:
                logger.info(
                    "A training op with name {} type {} has been removed.".
                    format(node.name, node.op_type))
            elif node.op_type == "QueueDequeueManyV2":
                num_output = len(node.attr["_output_shapes"])
                for index, shape, onnx_type in zip(
                        range(num_output), node.attr["_output_shapes"],
                        node.attr["component_types"]):
                    onnx_graph.add_input_proto_explicit(node.name + ":" +
                                                        str(index),
                                                        shape,
                                                        onnx_dtype=onnx_type)
            else:
                onnx_graph.add_value_info_proto(node)
                handler = handlers.get(node.domain, {}).get(node.op_type, None)
                node_proto = None
                if handler:
                    node_proto = handler.handle(
                        node,
                        consts=onnx_graph.consts,
                        node_dict=dict(node_tup),
                        data_type_cast_map=onnx_graph.data_type_cast_map)
                else:
                    exception.OP_UNIMPLEMENTED_EXCEPT(
                        node.op_type,
                        domain=None
                        if node.domain in handlers else node.domain)

                if node_proto is None:
                    node_proto = FrontendHandler.make_node_from_tf_node(
                        node, op_type=node.op_type, should_check=False)
                onnx_graph.add_node_proto(node_proto)

        for o in tf_graph.outputs:
            output_node = tf_graph.get_node_by_name(o)
            onnx_graph.add_output_proto(output_node)

        return onnx_graph.make_graph_proto()
예제 #11
0
파일: graph.py 프로젝트: zjhzxy/kaldi-onnx
 def find_opset(opset):
     if opset is None or opset == 0:
         opset = defs.onnx_opset_version()
         if opset > PREFERRED_OPSET:
             opset = PREFERRED_OPSET
     return opset
예제 #12
0
def get_maximum_opset_supported():
    return min(max(OPSET_TO_IR_VERSION.keys()), defs.onnx_opset_version())
예제 #13
0
    def test_onnx_ml(self):
        def generate_onnx_graph(opv):
            node = OnnxAdd(('X1', FloatTensorType()),
                           np.array([0.1], dtype=np.float32),
                           op_version=opv)
            out = OnnxLinearRegressor(node,
                                      coefficients=[0.3, 0.3, 0.4, 0.5, 0.6],
                                      intercepts=[-50.],
                                      op_version=1)
            last = OnnxIdentity(out, output_names=['Y'], op_version=opv)
            onx = last.to_onnx([('X1', FloatTensorType((None, 5)))],
                               outputs=[('Y', FloatTensorType())],
                               target_opset=opv)
            return onx, (node, out, last)

        for opv in [{'': 10}] + list(range(9, TARGET_OPSET + 1)):
            with self.subTest(opv=opv):
                if isinstance(opv, dict):
                    if opv[''] > get_latest_tested_opset_version():
                        continue
                elif (opv is not None
                      and opv > get_latest_tested_opset_version()):
                    continue
                for i, nbnode in enumerate((1, 2, 3, 100)):
                    onx, nodes = generate_onnx_graph(opv=opv)
                    if opv == {'': 10}:
                        for im in onx.opset_import:
                            if im.version > 10:
                                raise AssertionError(
                                    "Wrong final opset\nopv={}\n{}".format(
                                        opv, onx))
                    else:
                        for im in onx.opset_import:
                            if im.version > opv:
                                raise AssertionError(
                                    "Wrong final opset\nopv={}\n{}".format(
                                        opv, onx))
                    as_string = onx.SerializeToString()
                    try:
                        ort = InferenceSession(as_string)
                    except (InvalidGraph, InvalidArgument) as e:
                        if (isinstance(opv, dict)
                                and opv[''] >= onnx_opset_version()):
                            continue
                        if (isinstance(opv, int)
                                and opv >= onnx_opset_version()):
                            continue
                        raise AssertionError(
                            "Unable to load opv={}\n---\n{}\n---".format(
                                opv, onx)) from e
                    X = (np.ones((1, 5)) * nbnode).astype(np.float32)
                    res_out = ort.run(None, {'X1': X})
                    assert len(res_out) == 1
                    res = res_out[0]
                    self.assertEqual(res.shape, (1, 1))
                    inputs = None
                    expected = [[('Ad_C0', FloatTensorType(shape=[]))],
                                [('Li_Y0', FloatTensorType(shape=[]))],
                                [('Y', FloatTensorType(shape=[]))]]
                    for i, node in enumerate(nodes):
                        shape = node.get_output_type_inference(inputs)
                        self.assertEqual(len(shape), 1)
                        if isinstance(shape[0], tuple):
                            self.assertEqual(str(expected[i]), str(shape))
                        else:
                            self.assertEqual(
                                str(expected[i]),
                                str([(shape[0].onnx_name, shape[0].type)]))
                        inputs = shape
예제 #14
0
    def common_test_sub_graph(self,
                              first_input,
                              model,
                              options=None,
                              cls_type=FloatTensorType,
                              start=9):
        def generate_onnx_graph(opv):
            dtype = np.float32 if cls_type == FloatTensorType else np.float64
            node = OnnxAdd(first_input,
                           np.array([0.1], dtype=dtype),
                           op_version=opv)
            lr = model()
            lr.fit(np.ones([10, 5]), np.arange(0, 10) % 3)
            out = OnnxSubEstimator(lr, node, op_version=1, options=options)
            if model == LogisticRegression:
                last = OnnxIdentity(out[1], output_names=['Y'], op_version=opv)
            else:
                last = OnnxIdentity(out, output_names=['Y'], op_version=opv)
            onx = last.to_onnx([('X1', cls_type((None, 5)))],
                               outputs=[('Y', cls_type())],
                               target_opset=opv)
            return onx

        dtype = np.float32 if cls_type == FloatTensorType else np.float64

        opsets = list(range(start, TARGET_OPSET + 1))
        for opv in [{'': TARGET_OPSET}] + opsets:
            with self.subTest(opv=opv):
                if isinstance(opv, dict):
                    if opv[''] > get_latest_tested_opset_version():
                        continue
                elif (opv is not None
                      and opv > get_latest_tested_opset_version()):
                    continue
                for i, nbnode in enumerate((1, 2, 3, 100)):
                    onx = generate_onnx_graph(opv=opv)
                    if opv == {'': TARGET_OPSET}:
                        for im in onx.opset_import:
                            if im.version > TARGET_OPSET:
                                raise AssertionError(
                                    "Wrong final opset\nopv={}\n{}".format(
                                        opv, onx))
                    else:
                        for im in onx.opset_import:
                            if im.version > opv:
                                raise AssertionError(
                                    "Wrong final opset\nopv={}\n{}".format(
                                        opv, onx))
                    self.assertNotIn('zipmap', str(onx).lower())
                    as_string = onx.SerializeToString()
                    try:
                        ort = InferenceSession(as_string)
                    except (InvalidGraph, InvalidArgument, Fail,
                            NotImplemented) as e:
                        if (isinstance(opv, dict)
                                and opv[''] >= onnx_opset_version()):
                            continue
                        if (isinstance(opv, int)
                                and opv >= onnx_opset_version()):
                            continue
                        raise AssertionError(
                            "Unable to load opv={}\n---\n{}\n---".format(
                                opv, onx)) from e
                    X = (np.ones((1, 5)) * nbnode).astype(dtype)
                    res_out = ort.run(None, {'X1': X})
                    assert len(res_out) == 1
                    res = res_out[0]
                    if model == LogisticRegression:
                        self.assertEqual(res.shape, (1, 3))
                    else:
                        self.assertEqual(res.shape, (1, 1))
예제 #15
0
########################################
# ONNX and opset
# ++++++++++++++
#
# The converter can convert a model to an older opset
# than the default one, from 1 to the last available one.


def get_domain_opset(onx):
    domains = onx.opset_import
    res = [{'domain': dom.domain, 'version': dom.version} for dom in domains]
    return {d['domain']: d['version'] for d in res}


for opset in range(6, onnx_opset_version() + 1):
    try:
        onx = to_onnx(model,
                      X[:1].astype(numpy.float32),
                      target_opset={
                          '': opset,
                          'ai.onnx.ml': 2
                      })
    except RuntimeError as e:
        print('target: %r error: %r' % (opset, e))
        continue
    nodes = len(onx.graph.node)
    print('target: %r --> %s %d' % (opset, get_domain_opset(onx), nodes))

########################################
# It shows that the model cannot be converted for opset
예제 #16
0
def export_model(sym,
                 params,
                 input_shape,
                 input_type=np.float32,
                 onnx_file_path='model.onnx',
                 verbose=False,
                 opset_version=None):
    """Exports the MXNet model file, passed as a parameter, into ONNX model.
    Accepts both symbol,parameter objects as well as json and params filepaths as input.
    Operator support and coverage -
    https://cwiki.apache.org/confluence/display/MXNET/ONNX+Operator+Coverage

    Parameters
    ----------
    sym : str or symbol object
        Path to the json file or Symbol object
    params : str or symbol object
        Path to the params file or params dictionary. (Including both arg_params and aux_params)
    input_shape : List of tuple
        Input shape of the model e.g [(1,3,224,224)]
    input_type : data type
        Input data type e.g. np.float32
    onnx_file_path : str
        Path where to save the generated onnx file
    verbose : Boolean
        If true will print logs of the model conversion

    Returns
    -------
    onnx_file_path : str
        Onnx file path

    Notes
    -----
    This method is available when you ``import mxnet.contrib.onnx``

    """

    try:
        from onnx import helper, mapping
        from onnx.defs import onnx_opset_version
    except ImportError:
        raise ImportError(
            "Onnx and protobuf need to be installed. " +
            "Instructions to install - https://github.com/onnx/onnx")

    converter = MXNetGraph()
    if opset_version is None:
        # default is to use latest opset version the onnx package supports
        opset_version = onnx_opset_version()

    data_format = np.dtype(input_type)
    # if input parameters are strings(file paths), load files and create symbol parameter objects
    if isinstance(sym, string_types) and isinstance(params, string_types):
        logging.info("Converting json and weight file to sym and params")
        sym_obj, params_obj = load_module(sym, params)
        onnx_graph = converter.create_onnx_graph_proto(
            sym_obj,
            params_obj,
            input_shape,
            mapping.NP_TYPE_TO_TENSOR_TYPE[data_format],
            verbose=verbose,
            opset_version=opset_version)
    elif isinstance(sym, symbol.Symbol) and isinstance(params, dict):
        onnx_graph = converter.create_onnx_graph_proto(
            sym,
            params,
            input_shape,
            mapping.NP_TYPE_TO_TENSOR_TYPE[data_format],
            verbose=verbose,
            opset_version=opset_version)
    else:
        raise ValueError(
            "Input sym and params should either be files or objects")

    # Create the model (ModelProto)
    onnx_model = helper.make_model(onnx_graph)

    # Save model on disk
    with open(onnx_file_path, "wb") as file_handle:
        serialized = onnx_model.SerializeToString()
        file_handle.write(serialized)
        logging.info("Input shape of the model %s ", input_shape)
        logging.info("Exported ONNX file %s saved to disk", onnx_file_path)

    return onnx_file_path
예제 #17
0
def onnx_builtin_opset_version():
    return onnx_defs.onnx_opset_version()
    def test_bind_input_types(self):

        opset = onnx_opset_version()
        devices = [(
            C_OrtDevice(C_OrtDevice.cpu(), C_OrtDevice.default_memory(), 0),
            ["CPUExecutionProvider"],
        )]
        if "CUDAExecutionProvider" in onnxrt.get_all_providers():
            devices.append((
                C_OrtDevice(C_OrtDevice.cuda(), C_OrtDevice.default_memory(),
                            0),
                ["CUDAExecutionProvider"],
            ))

        for device, provider in devices:
            for dtype in [
                    np.float32,
                    np.float64,
                    np.int32,
                    np.uint32,
                    np.int64,
                    np.uint64,
                    np.int16,
                    np.uint16,
                    np.int8,
                    np.uint8,
                    np.float16,
                    np.bool_,
            ]:
                with self.subTest(dtype=dtype, device=str(device)):

                    x = np.arange(8).reshape((-1, 2)).astype(dtype)
                    proto_dtype = NP_TYPE_TO_TENSOR_TYPE[x.dtype]

                    X = helper.make_tensor_value_info("X", proto_dtype,
                                                      [None, x.shape[1]])
                    Y = helper.make_tensor_value_info("Y", proto_dtype,
                                                      [None, x.shape[1]])

                    # inference
                    node_add = helper.make_node("Identity", ["X"], ["Y"])

                    # graph
                    graph_def = helper.make_graph([node_add], "lr", [X], [Y],
                                                  [])
                    model_def = helper.make_model(
                        graph_def,
                        producer_name="dummy",
                        ir_version=7,
                        producer_version="0",
                        opset_imports=[helper.make_operatorsetid("", opset)],
                    )

                    sess = onnxrt.InferenceSession(
                        model_def.SerializeToString(), providers=provider)

                    bind = SessionIOBinding(sess._sess)
                    ort_value = C_OrtValue.ortvalue_from_numpy(x, device)
                    bind.bind_ortvalue_input("X", ort_value)
                    bind.bind_output("Y", device)
                    sess._sess.run_with_iobinding(bind, None)
                    ortvalue = bind.get_outputs()[0]
                    y = ortvalue.numpy()
                    assert_almost_equal(x, y)

                    bind = SessionIOBinding(sess._sess)
                    bind.bind_input("X", device, dtype, x.shape,
                                    ort_value.data_ptr())
                    bind.bind_output("Y", device)
                    sess._sess.run_with_iobinding(bind, None)
                    ortvalue = bind.get_outputs()[0]
                    y = ortvalue.numpy()
                    assert_almost_equal(x, y)
예제 #19
0
class TestOp10(unittest.TestCase):
    def check_domain(self, model, domain="", target_opset=10):
        for op in model.opset_import:
            if op.domain == domain:
                if op.version > target_opset:
                    raise RuntimeError("Wrong opset {} > {} expected".format(
                        op.domain, target_opset))

    @unittest.skipIf(not onnx_built_with_ml(), reason="onnx-ml")
    @unittest.skipIf(onnx_opset_version() < 10, reason="out of scope")
    def test_logistic_regression(self):
        model, X = fit_classification_model(linear_model.LogisticRegression(),
                                            3)
        target_opset = 10
        model_onnx = convert_sklearn(model,
                                     "op10",
                                     [("input", FloatTensorType([None, 3]))],
                                     target_opset=target_opset)
        self.check_domain(model_onnx, target_opset=target_opset)

    @unittest.skipIf(not onnx_built_with_ml(), reason="onnx-ml")
    @unittest.skipIf(onnx_opset_version() < 10, reason="out of scope")
    def test_kmeans(self):
        model, X = fit_classification_model(KMeans(), 3)
        target_opset = 10
        model_onnx = convert_sklearn(model,
                                     "op10",
                                     [("input", FloatTensorType([None, 3]))],
                                     target_opset=target_opset)
        self.check_domain(model_onnx, target_opset=target_opset)

    @unittest.skipIf(not onnx_built_with_ml(), reason="onnx-ml")
    @unittest.skipIf(onnx_opset_version() < 10, reason="out of scope")
    def test_gaussian_mixture(self):
        model, X = fit_classification_model(GaussianMixture(), 3)
        target_opset = 10
        model_onnx = convert_sklearn(
            model,
            "op10", [("input", FloatTensorType([None, X.shape[1]]))],
            target_opset=target_opset)
        self.check_domain(model_onnx, target_opset=target_opset)

    @unittest.skipIf(not onnx_built_with_ml(), reason="onnx-ml")
    @unittest.skipIf(onnx_opset_version() < 10, reason="out of scope")
    def test_gaussian_process_regressor(self):
        model, X = fit_classification_model(GaussianProcessRegressor(), 3)
        target_opset = 10
        model_onnx = convert_sklearn(model,
                                     "op10",
                                     [("input", FloatTensorType([None, 3]))],
                                     target_opset=target_opset)
        self.check_domain(model_onnx, target_opset=target_opset)

    @unittest.skipIf(not onnx_built_with_ml(), reason="onnx-ml")
    @unittest.skipIf(onnx_opset_version() < 10, reason="out of scope")
    def test_voting_classifier(self):
        model = VotingClassifier(
            voting="hard",
            flatten_transform=False,
            estimators=[
                ("lr", LogisticRegression()),
                ("lr2", LogisticRegression(fit_intercept=False)),
            ],
        )
        model, X = fit_classification_model(model, 3)
        target_opset = 10
        model_onnx = convert_sklearn(model,
                                     "op10",
                                     [("input", FloatTensorType([None, 3]))],
                                     target_opset=target_opset)
        self.check_domain(model_onnx, target_opset=target_opset)
예제 #20
0
 def _get_handlers(self, opset):
     opset = opset or [
         make_opsetid(defs.ONNX_DOMAIN, defs.onnx_opset_version())
     ]
     opset_dict = dict([(o.domain, o.version) for o in opset])
     return get_all_backend_handlers(opset_dict)
예제 #21
0
    def test_cascade_add(self):
        def generate_onnx_graph(dim, nbnode, input_name='X1', opv=None):
            i1 = input_name
            for i in range(nbnode - 1):
                i2 = (np.ones((1, dim)) * nbnode * 10).astype(np.float32)
                node = OnnxAdd(i1, i2, op_version=opv)
                i1 = node
            i2 = (np.ones((1, dim)) * nbnode * 10).astype(np.float32)
            node = OnnxAdd(i1, i2, output_names=['Y'], op_version=opv)
            onx = node.to_onnx([(input_name, FloatTensorType((None, dim)))],
                               outputs=[('Y', FloatTensorType())],
                               target_opset=opv)
            return onx

        exp = [
            np.array([[11., 11., 11., 11., 11.]]),
            np.array([[42., 42., 42., 42., 42.]]),
            np.array([[93., 93., 93., 93., 93.]]),
            np.array([[100100., 100100., 100100., 100100., 100100.]])
        ]
        for opv in ({'': 10}, 9, 10, 11, 12, onnx_opset_version()):
            if isinstance(opv, dict):
                if opv[''] > get_latest_tested_opset_version():
                    continue
            elif opv is not None and opv > get_latest_tested_opset_version():
                continue
            for i, nbnode in enumerate((1, 2, 3, 100)):
                with self.subTest(n_nodes=nbnode):
                    onx = generate_onnx_graph(5, nbnode, opv=opv)
                    if opv == {'': 10}:
                        for im in onx.opset_import:
                            if im.version > 10:
                                raise AssertionError(
                                    "Wrong final opset\nopv={}\n{}".format(
                                        opv, onx))
                    else:
                        for im in onx.opset_import:
                            if im.version > opv:
                                raise AssertionError(
                                    "Wrong final opset\nopv={}\n{}".format(
                                        opv, onx))
                    as_string = onx.SerializeToString()
                    try:
                        ort = InferenceSession(as_string)
                    except (InvalidGraph, InvalidArgument) as e:
                        if (isinstance(opv, dict)
                                and opv[''] >= onnx_opset_version()):
                            continue
                        if (isinstance(opv, int)
                                and opv >= onnx_opset_version()):
                            continue
                        raise AssertionError(
                            "Unable to load opv={}\n---\n{}\n---".format(
                                opv, onx)) from e
                    X = (np.ones((1, 5)) * nbnode).astype(np.float32)
                    res_out = ort.run(None, {'X1': X})
                    assert len(res_out) == 1
                    res = res_out[0]
                    assert_almost_equal(exp[i], res)

        with self.subTest(n_nodes=300):
            dim = 10
            onx = generate_onnx_graph(dim, 300, opv=11)
            as_string = onx.SerializeToString()
            ort = InferenceSession(as_string)
            X = (np.ones((1, dim)) * nbnode).astype(np.float32)
            res_out = ort.run(None, {'X1': X})
            assert len(res_out) == 1
            res = res_out[0]
            assert res.shape[1] == dim
예제 #22
0
def FindOpset(opset):
    if opset is None or opset == 0:
        opset = defs.onnx_opset_version()
    return opset
예제 #23
0
  def tensorflow_graph_to_onnx_graph(cls,
                                     graph_def,
                                     output,
                                     opset=((defs.ONNX_DOMAIN,
                                             defs.onnx_opset_version()),),
                                     name="graph",
                                     ignore_unimplemented=False):
    """Converts a Tensorflow Graph Proto to an ONNX graph

    This function converts a Tensorflow Graph proto to an equivalent
    representation of ONNX graph.

    :param graph_def: Tensorflow Graph Proto object.
    :param output: List of Tensorflow NodeDef object specifying which nodes
      to be taken as outputs of the ONNX graph.
    :param opset: Opset, which should be ((str domain: int version number),).
    :param name: The name of the output ONNX Graph.
    :param ignore_unimplemented: Convert to ONNX model and ignore all the operators
      that are not currently supported by onnx-tensorflow.
      This is an experimental feature. By enabling this feature,
      the graph would not be guaranteed to match the ONNX specifications.

    :returns: The equivalent ONNX Graph Proto object.
    """
    onnx_graph = OnnxGraph(name)
    exception.IGNORE_UNIMPLEMENTED = ignore_unimplemented

    opset_dict = {}
    for domain, version in opset:
      if domain == "ai.onnx":
        domain = defs.ONNX_DOMAIN
      opset_dict[domain] = version

    handlers = get_all_frontend_handlers(opset_dict)

    node_tup = [(node.name, TensorflowNode(node)) for node in graph_def.node]
    for name, node in node_tup:

      if node.op_type == "Placeholder":
        onnx_graph.add_input_proto(node)
      elif node.op_type == "Const":
        onnx_graph.add_const(node)
        onnx_graph.add_const_proto(node)
        onnx_graph.add_input_proto(node)
      else:
        onnx_graph.add_value_info_proto(node)
        handler = handlers.get(node.domain, {}).get(node.op_type, None)
        node_proto = None
        if handler:
          node_proto = handler.handle(
              node,
              consts=onnx_graph.consts,
              node_dict=dict(node_tup),
              data_type_cast_map=onnx_graph.data_type_cast_map)
        else:
          exception.OP_UNIMPLEMENTED_EXCEPT(
              node.op_type,
              domain=None if node.domain in handlers else node.domain)

        if node_proto is None:
          node_proto = FrontendHandler.make_node_from_tf_node(
              node, op_type=node.op_type, should_check=False)
        onnx_graph.add_node_proto(node_proto)

    for o in output:
      output_node = TensorflowNode(o)
      onnx_graph.add_output_proto(output_node)

    return onnx_graph.make_graph_proto()
예제 #24
0
def get_opset_number_from_onnx():
    """
    Returns the latest opset version supported
    by the *onnx* package.
    """
    return defs.onnx_opset_version()
예제 #25
0
def gen_support_status(docs_dir, onnx_version, onnx_path,
                       onnx_tf_release_build):

    # set filename
    if onnx_tf_release_build:
        # get onnx-tf version from VERSION_NUMBER file
        version_dir = os.path.dirname(
            os.path.dirname(os.path.realpath('VERSION_NUMBER')))
        version_file = os.path.join(version_dir, 'VERSION_NUMBER')
        onnx_tf_version = subprocess.check_output('cat ' + version_file,
                                                  shell=True)
        onnx_tf_version = 'v' + onnx_tf_version.decode().strip('\n')
        filename = 'support_status_' + onnx_tf_version.replace('.',
                                                               '_') + '.md'
    else:  # onnx-tf = master
        # get onnx-tf commit id
        onnx_tf_commit_id = subprocess.check_output('git rev-parse HEAD',
                                                    shell=True)
        onnx_tf_commit_id = onnx_tf_commit_id.decode().strip('\n')
        onnx_tf_version = 'Master ( commit id: {} )'.format(onnx_tf_commit_id)
        filename = 'support_status.md'

    with open(os.path.join(docs_dir, filename), 'w') as status_file:
        status_file.write('# ONNX-Tensorflow Support Status\n')
        status_file.write('|||\n')
        status_file.write('|-:|:-|\n')
        status_file.write(
            '|ONNX-Tensorflow Version|{}|\n'.format(onnx_tf_version))

        # get onnx commit id
        if onnx_version == 'master':
            onnx_commit_id = subprocess.check_output('cd ' + onnx_path +
                                                     '; git rev-parse HEAD',
                                                     shell=True)
            onnx_commit_id = onnx_commit_id.decode().strip('\n')
            status_file.write(
                '|ONNX Version|Master ( commit id: {} )|\n'.format(
                    onnx_commit_id))
        else:
            status_file.write('|ONNX Version|{}|\n'.format(onnx_version))

        # get tf_version
        status_file.write('|Tensorflow Version|v{}|\n\n'.format(
            tf.__version__))

        # display the table legend
        status_file.write('Notes:\n')
        status_file.write('* Values that are new or updated from a ')
        status_file.write('previous opset version are in bold.\n')
        status_file.write('* -: not defined in corresponding ONNX ')
        status_file.write('opset version\n')
        status_file.write('* \*: the operator is deprecated\n')
        status_file.write('* :small_red_triangle:: not supported yet\n')
        status_file.write('* :small_orange_diamond:: partially supported\n')
        status_file.write('* the rest are all supported\n\n')

        # get oll onnx ops
        onnx_ops = {}
        for schema in defs.get_all_schemas():
            if schema.domain == '':  # only get onnx ops
                onnx_ops[schema.name] = {
                    'versions': [],
                    'deprecated':
                    schema.since_version if schema.deprecated else -1
                }
        for schema in defs.get_all_schemas_with_history():
            if schema.domain == '':  # only get onnx ops
                op = onnx_ops[schema.name]
                versions = op['versions']
                versions.append(schema.since_version)

        # get all onnx-tf supported ops
        onnx_tf_ops = opset_version.backend_opset_version
        onnx_tf_ops_ps = opset_version.backend_partial_support

        # get the cureent opset version
        current_opset = defs.onnx_opset_version()

        # setup table header
        status_file.write('||')
        for i in range(current_opset):
            status_file.write('|')
        status_file.write('\n|:-:|')
        for i in range(current_opset):
            status_file.write(':-:|')
        status_file.write('\n|**ONNX Operator**|')
        for opset in range(1, current_opset + 1):
            status_file.write('**Opset {}**|'.format(opset))

        ops_count = len(onnx_ops)
        # fill in data for the table
        for key, val in sorted(onnx_ops.items()):
            try:
                status_file.write('\n|{}|'.format(key))
                i = 0
                vers = val['versions']
                deprecated = val['deprecated']
                for opset in range(1, current_opset + 1):
                    if i <= len(vers) - 1:
                        lb = vers[i]
                        ub = vers[i + 1] if i < len(vers) - 1 else vers[i]
                        if opset < lb:
                            if i == 0:
                                status_file.write('-')
                        elif opset == lb:
                            status_file.write('**{}**'.format(lb))
                            if lb == deprecated:
                                status_file.write('\*')
                            elif lb not in onnx_tf_ops[key]:
                                status_file.write(':small_red_triangle:')
                                if opset == current_opset:
                                    ops_count -= 1
                            elif key in onnx_tf_ops_ps:
                                status_file.write(':small_orange_diamond:')
                        else:  # opset > lb
                            if opset < ub:
                                status_file.write('{}'.format(lb))
                                if lb == deprecated:
                                    status_file.write('\*')
                                elif lb not in onnx_tf_ops[key]:
                                    status_file.write(':small_red_triangle:')
                                    if opset == current_opset:
                                        ops_count -= 1
                                elif key in onnx_tf_ops_ps:
                                    status_file.write(':small_orange_diamond:')
                            elif opset == ub:
                                status_file.write('**{}**'.format(ub))
                                if ub == deprecated:
                                    status_file.write('\*')
                                elif ub not in onnx_tf_ops[key]:
                                    status_file.write(':small_red_triangle:')
                                    if opset == current_opset:
                                        ops_count -= 1
                                elif key in onnx_tf_ops_ps:
                                    status_file.write(':small_orange_diamond:')
                                i += 1
                            else:  #opset > ub
                                status_file.write('{}'.format(ub))
                                if ub == deprecated:
                                    status_file.write('\*')
                                elif ub not in onnx_tf_ops[key]:
                                    status_file.write(':small_red_triangle:')
                                    if opset == current_opset:
                                        ops_count -= 1
                                elif key in onnx_tf_ops_ps:
                                    status_file.write(':small_orange_diamond:')
                        status_file.write('|')
            except:
                # ops defined in onnx but not in opset_version.backend_opset_versionn
                status_file.write(':small_red_triangle:|')

        status_file.write(
            '\n\nONNX-TF Supported Operators / ONNX Operators: {} / {}'.format(
                ops_count, len(onnx_ops)))

        # display partial support footnote
        status_file.write('\n\nNotes:\n')
        index = 1
        for key in onnx_tf_ops_ps:
            status_file.write(
                str(index) + '. ' + key + ': ' + onnx_tf_ops_ps[key] + '\n')
            index += 1
예제 #26
0
def export_model(sym, params, in_shapes=None, in_types=np.float32,
                 onnx_file_path='model.onnx', verbose=False, dynamic=False,
                 dynamic_input_shapes=None, run_shape_inference=False, input_type=None,
                 input_shape=None, large_model=False):
    """Exports the MXNet model file, passed as a parameter, into ONNX model.
    Accepts both symbol,parameter objects as well as json and params filepaths as input.
    Operator support and coverage -
    https://github.com/apache/incubator-mxnet/tree/v1.x/python/mxnet/onnx#operator-support-matrix

    Parameters
    ----------
    sym : str or symbol object
        Path to the json file or Symbol object
    params : str or dict or list of dict
        str - Path to the params file
        dict - params dictionary (Including both arg_params and aux_params)
        list - list of length 2 that contains arg_params and aux_params
    in_shapes : List of tuple
        Input shape of the model e.g [(1,3,224,224)]
    in_types : data type or list of data types
        Input data type e.g. np.float32, or [np.float32, np.int32]
    onnx_file_path : str
        Path where to save the generated onnx file
    verbose : Boolean
        If True will print logs of the model conversion
    dynamic: Boolean
        If True will allow for dynamic input shapes to the model
    dynamic_input_shapes: list of tuple
        Specifies the dynamic input_shapes. If None then all dimensions are set to None
    run_shape_inference : Boolean
        If True will run shape inference on the model
    input_type : data type or list of data types
        This is the old name of in_types. We keep this parameter name for backward compatibility
    input_shape : List of tuple
        This is the old name of in_shapes. We keep this parameter name for backward compatibility
    large_model : Boolean
        Whether to export a model that is larger than 2 GB. If true will save param tensors in separate
        files along with .onnx model file. This feature is supported since onnx 1.8.0

    Returns
    -------
    onnx_file_path : str
        Onnx file path

    Notes
    -----
    This method is available when you ``import mxnet.onnx``

    """

    try:
        import onnx
        from onnx import helper, mapping, shape_inference
        from onnx.defs import onnx_opset_version
    except ImportError:
        raise ImportError("Onnx and protobuf need to be installed. "
                          + "Instructions to install - https://github.com/onnx/onnx")

    if input_type is not None:
        in_types = input_type

    if input_shape is not None:
        in_shapes = input_shape

    converter = MXNetGraph()
    opset_version = onnx_opset_version()

    if not isinstance(in_types, list):
        in_types = [in_types for _ in range(len(in_shapes))]
    in_types_t = [mapping.NP_TYPE_TO_TENSOR_TYPE[np.dtype(i_t)] for i_t in in_types]
    assert len(in_types) == len(in_shapes), "The lengths of in_types and in_shapes must equal"
    # if input parameters are strings(file paths), load files and create symbol parameter objects
    if isinstance(sym, string_types) and isinstance(params, string_types):
        logging.info("Converting json and weight file to sym and params")
        sym_obj, params_obj = load_module(sym, params)
        onnx_graph = converter.create_onnx_graph_proto(sym_obj, params_obj, in_shapes,
                                                       in_types_t,
                                                       verbose=verbose, opset_version=opset_version,
                                                       dynamic=dynamic, dynamic_input_shapes=dynamic_input_shapes)
    elif isinstance(sym, symbol.Symbol) and isinstance(params, dict):
        onnx_graph = converter.create_onnx_graph_proto(sym, params, in_shapes,
                                                       in_types_t,
                                                       verbose=verbose, opset_version=opset_version,
                                                       dynamic=dynamic, dynamic_input_shapes=dynamic_input_shapes)
    elif isinstance(sym, symbol.Symbol) and isinstance(params, list) and len(params) == 2:
        # when params contains arg_params and aux_params
        p = {}
        p.update(params[0])
        p.update(params[1])
        onnx_graph = converter.create_onnx_graph_proto(sym, p, in_shapes,
                                                       in_types_t,
                                                       verbose=verbose, opset_version=opset_version,
                                                       dynamic=dynamic, dynamic_input_shapes=dynamic_input_shapes)
    else:
        raise ValueError("Input sym and params should either be files or objects")

    # Create the model (ModelProto)
    onnx_model = helper.make_model(onnx_graph)

    # Run shape inference on the model. Due to ONNX bug/incompatibility this may or may not crash
    if run_shape_inference:
        try:
            onnx_model = shape_inference.infer_shapes(onnx_model)
        except: # pylint: disable=bare-except
            logging.info("Shape inference failed, original export is kept.")

    if large_model:
        from onnx.external_data_helper import convert_model_to_external_data
        convert_model_to_external_data(onnx_model, all_tensors_to_one_file=False, location=onnx_file_path+'.data')

    onnx.save_model(onnx_model, onnx_file_path)
    onnx.checker.check_model(onnx_file_path)
    return onnx_file_path
예제 #27
0
    def _run(self, X):  # pylint: disable=W0221
        return self._private_run(X, self.ratio)


class Dropout_12(DropoutBase):

    atts = {'seed': 0}

    def __init__(self, onnx_node, desc=None, **options):
        DropoutBase.__init__(self,
                             onnx_node,
                             desc=desc,
                             expected_attributes=Dropout_12.atts,
                             **options)

    def _run(self, *inputs):  # pylint: disable=W0221
        X = inputs[0]
        ratio = 0.5 if len(inputs) <= 1 else inputs[1]
        training_mode = False if len(inputs) <= 2 else inputs[2]
        return self._private_run(X,
                                 seed=self.seed,
                                 ratio=ratio,
                                 training_mode=training_mode)


if onnx_opset_version() >= 12:
    Dropout = Dropout_12
else:
    Dropout = Dropout_7  # pragma: no cover
    def test_matmul_integer(self):
        if legacy_opset_pre_ver(10):
            raise unittest.SkipTest(
                "ONNX version {} doesn't support MatMulInteger.".format(
                    defs.onnx_opset_version()))

        node_def = helper.make_node("MatMulInteger",
                                    ["A", "B", "a_zero_point", "b_zero_point"],
                                    ["Z"])
        # A & B are 3-D tensor and a_zero_point & b_zero_point are scalar
        A = self._get_rnd_int(-20, 20, shape=(2, 3, 4), dtype=np.int8)
        B = self._get_rnd_int(-20, 20, shape=(2, 4, 6), dtype=np.int8)
        a_zero_point = self._get_rnd_int(-20, 20, dtype=np.int8)
        b_zero_point = self._get_rnd_int(-20, 20, dtype=np.int8)
        A_minus_zero_point = np.subtract(A.astype(np.int32),
                                         a_zero_point.astype(np.int32))
        B_minus_zero_point = np.subtract(B.astype(np.int32),
                                         b_zero_point.astype(np.int32))
        z = np.matmul(A_minus_zero_point, B_minus_zero_point)
        graph_def = helper.make_graph(
            [node_def],
            name="test_unknown_shape",
            inputs=[
                helper.make_tensor_value_info("A", TensorProto.INT8,
                                              [None, None, None]),
                helper.make_tensor_value_info("B", TensorProto.INT8,
                                              [None, None, None]),
                helper.make_tensor_value_info("a_zero_point", TensorProto.INT8,
                                              []),
                helper.make_tensor_value_info("b_zero_point", TensorProto.INT8,
                                              [])
            ],
            outputs=[
                helper.make_tensor_value_info("Z", TensorProto.INT32,
                                              [None, None, None])
            ])
        tf_rep = onnx_graph_to_tensorflow_rep(graph_def)
        output = tf_rep.run({
            "A": A,
            "B": B,
            "a_zero_point": a_zero_point,
            "b_zero_point": b_zero_point
        })
        np.testing.assert_almost_equal(output["Z"], z)
        # A & B are 4-D tensor and a_zero_point & b_zero_point are 1-D tensor
        A = self._get_rnd_int(-20, 20, shape=(2, 5, 3, 4), dtype=np.int8)
        B = self._get_rnd_int(-20, 20, shape=(2, 1, 4, 6), dtype=np.int8)
        a_zero_point = self._get_rnd_int(-20,
                                         20,
                                         shape=(A.shape[-2]),
                                         dtype=np.int8)
        b_zero_point = self._get_rnd_int(-20,
                                         20,
                                         shape=(B.shape[-1]),
                                         dtype=np.int8)
        a_zero_point_with_reshape = np.reshape(a_zero_point, [A.shape[-2], 1])
        A_minus_zero_point = np.subtract(
            A.astype(np.int32), a_zero_point_with_reshape.astype(np.int32))
        B_minus_zero_point = np.subtract(B.astype(np.int32),
                                         b_zero_point.astype(np.int32))
        z = np.matmul(A_minus_zero_point, B_minus_zero_point)
        graph_def = helper.make_graph(
            [node_def],
            name="test_unknown_shape",
            inputs=[
                helper.make_tensor_value_info("A", TensorProto.INT8,
                                              [None, None, None, None]),
                helper.make_tensor_value_info("B", TensorProto.INT8,
                                              [None, None, None, None]),
                helper.make_tensor_value_info("a_zero_point", TensorProto.INT8,
                                              [None]),
                helper.make_tensor_value_info("b_zero_point", TensorProto.INT8,
                                              [None])
            ],
            outputs=[
                helper.make_tensor_value_info("Z", TensorProto.INT32,
                                              [None, None, None, None])
            ])
        tf_rep = onnx_graph_to_tensorflow_rep(graph_def)
        output = tf_rep.run({
            "A": A,
            "B": B,
            "a_zero_point": a_zero_point,
            "b_zero_point": b_zero_point
        })
        np.testing.assert_almost_equal(output["Z"], z)