예제 #1
0
    def _onnx_node_to_caffe2_op(cls, init_model, pred_model, node_def,
                                opset_version):
        cbackend = C.Caffe2Backend(cls._dummy_name)
        if cbackend.support_onnx_import(node_def.op_type):
            op_strs = cbackend.convert_node(node_def.SerializeToString(),
                                            opset_version)
            init_ops = []
            for s in op_strs[0]:
                op = caffe2_pb2.OperatorDef()
                op.ParseFromString(s)
                init_ops.append(op)
            ops = []
            for s in op_strs[1]:
                op = caffe2_pb2.OperatorDef()
                op.ParseFromString(s)
                ops.append(op)
            return Caffe2Ops(ops, init_ops, [])

        if node_def.op_type in cls._special_operators:
            translator = getattr(cls, cls._special_operators[node_def.op_type])
        else:
            translator = cls._common_onnx_node_to_caffe2_op
        ops = translator(init_model, pred_model, OnnxNode(node_def),
                         opset_version)
        if isinstance(ops, Caffe2Ops):
            return ops
        if not isinstance(ops, collections.Iterable):
            ops = [ops]
        return Caffe2Ops(ops, [], [])
예제 #2
0
 def test_tensor_filling_ops_c_backend(self):
     for dtype in [
             onnx.TensorProto.FLOAT,
             onnx.TensorProto.DOUBLE,
             onnx.TensorProto.BOOL,
             onnx.TensorProto.INT8,
             onnx.TensorProto.INT16,
             onnx.TensorProto.INT32,
             onnx.TensorProto.INT64,
             onnx.TensorProto.UINT8,
             onnx.TensorProto.UINT16,
             onnx.TensorProto.UINT32,
     ]:
         shape = (1, 2, 3)
         vals = np.random.randn(*shape)
         if dtype != onnx.TensorProto.BOOL:
             vals *= 5
         vals = vals.astype(mapping.TENSOR_TYPE_TO_NP_TYPE[dtype])
         tensor = make_tensor(
             name='test-tensor-{}'.format(dtype),
             data_type=dtype,
             dims=[1, 2, 3],
             vals=vals.flatten().tolist(),
         )
         b = C.Caffe2Backend()
         op = caffe2_pb2.OperatorDef()
         op.ParseFromString(
             b._build_tensor_filling_op(tensor.SerializeToString(), ''))
         self.assertEqual(len(op.input), 0)
         self.assertEqual(op.output, [tensor.name])
         ws, output = c2_native_run_op(op, inputs=[])
         self.assertEqual(len(output), 1)
         np.testing.assert_almost_equal(output[0], vals)
         np.testing.assert_almost_equal(ws.FetchBlob(op.output[0]), vals)
예제 #3
0
파일: backend.py 프로젝트: yilifzf/pytorch
    def run_node(cls, node, inputs, device='CPU', opset_version=_known_opset_version, outputs_info=None):
        super(Caffe2Backend, cls).run_node(node, inputs, device=device, outputs_info=outputs_info)

        device_option = get_device_option(Device(device))
        ws = Workspace()
        with core.DeviceScope(device_option):  # temporary!
            if isinstance(inputs, dict):
                for key, value in inputs.items():
                    ws.FeedBlob(key, value)
            else:
                assert len(node.input) == len(inputs), "{}: expected {} but got {}".format(
                    node.op_type, len(node.input), len(inputs))
                for key, value in zip(node.input, inputs):
                    ws.FeedBlob(key, value)

            ops = []
            cbackend = C.Caffe2Backend(cls._dummy_name)
            ops_str = cbackend.convert_node(node.SerializeToString(), opset_version)
            for s in ops_str[0] + ops_str[1]:
                op = caffe2_pb2.OperatorDef()
                op.ParseFromString(s)
                op.device_option.CopyFrom(device_option)
                ops.append(op)
            # For testing
            if "ONNX_CAFFE2_DEBUG" in os.environ:
                init_ops, ops2, _ = cls._onnx_node_to_caffe2_op(
                    None, None, node, opset_version or cls._known_opset_version)
                ops2 = init_ops + ops2
                for op in ops2:
                    op.device_option.CopyFrom(device_option)
                print("\nC++:\n{}\nPython:\n{}".format(ops, ops2))
            ws.RunOperatorsOnce(ops)
            output_values = [ws.FetchBlob(name) for name in node.output]
            return namedtupledict('Outputs', node.output)(*output_values)
예제 #4
0
    def test_check_arguments(self):
        b2 = C.Caffe2Backend()

        node_def = make_node("Add", inputs=["X", "Y"], outputs=["Z"])
        b2.convert_node(node_def.SerializeToString())

        bad_node_def = make_node("Add", inputs=["X", "Y"], outputs=["Z"], foo=42, bar=56)
        with self.assertRaisesRegexp(
                RuntimeError,
                "Don't know how to map unexpected argument (foo|bar)"):
            b2.convert_node(bad_node_def.SerializeToString())
예제 #5
0
    def run_node(cls,
                 node,
                 inputs,
                 device='CPU',
                 opset_version=_known_opset_version,
                 outputs_info=None):
        super(Caffe2Backend, cls).run_node(node,
                                           inputs,
                                           device=device,
                                           outputs_info=outputs_info,
                                           opset_version=opset_version)

        value_infos = []
        device_option = get_device_option(Device(device))
        ws = Workspace()
        with core.DeviceScope(device_option):  # temporary!
            if isinstance(inputs, dict):
                for key, value in inputs.items():
                    ws.FeedBlob(key, value)
                    value_infos.append(
                        onnx.helper.make_tensor_value_info(
                            name=key,
                            elem_type=onnx.mapping.NP_TYPE_TO_TENSOR_TYPE[
                                value.dtype],
                            shape=value.shape).SerializeToString())
            else:
                assert len(node.input) == len(
                    inputs), "{}: expected {} but got {}".format(
                        node.op_type, len(node.input), len(inputs))
                for key, value in zip(node.input, inputs):
                    ws.FeedBlob(key, value)
                    value_infos.append(
                        onnx.helper.make_tensor_value_info(
                            name=key,
                            elem_type=onnx.mapping.NP_TYPE_TO_TENSOR_TYPE[
                                value.dtype],
                            shape=value.shape).SerializeToString())

            ops = []
            cbackend = C.Caffe2Backend(cls._dummy_name)
            ops_str = cbackend.convert_node(node.SerializeToString(),
                                            value_infos, opset_version)
            for s in ops_str[0] + ops_str[1]:
                op = caffe2_pb2.OperatorDef()
                op.ParseFromString(s)
                op.device_option.CopyFrom(device_option)
                ops.append(op)
            ws.RunOperatorsOnce(ops)
            output_values = [ws.FetchBlob(name) for name in node.output]
            return namedtupledict('Outputs', node.output)(*output_values)
    def test_check_arguments(self):
        X = np.random.randn(3, 2).astype(np.float32)
        Y = np.random.randn(3, 2).astype(np.float32)
        Z = np.zeros((3, 2)).astype(np.float32)

        b2 = C.Caffe2Backend()

        node_def = make_node("Add", inputs = ["X", "Y"], outputs = ["Z"], broadcast = 0)
        output = b2.convert_node(node_def.SerializeToString(), 6)

        bad_node_def = make_node("Add", inputs = ["X", "Y"], outputs = ["Z"], foo = 42, bar = 56)
        with self.assertRaisesRegexp(
            RuntimeError,
            ".*?Don't know how to map unexpected argument (foo|bar) \(from operator .*?\).*$"):
            output = b2.convert_node(bad_node_def.SerializeToString(), 6)
예제 #7
0
    def test_gemm_conversion(self):
        node_def = make_node('Gemm', ['A', 'B', 'C'], ["Y"],
                             alpha=2.,
                             beta=3.,
                             transB=True)

        backend = C.Caffe2Backend()

        # without broadcast and without shape info, gemm will be
        # converted to matmul + add
        _, op_strs = backend.convert_node(node_def.SerializeToString())
        op_names = []
        for s in op_strs:
            op = caffe2_pb2.OperatorDef()
            op.ParseFromString(s)
            op_names.append(op.type)
        self.assertEqual(op_names, ['Scale', 'Scale', 'MatMul', 'Add'])

        # with shape info (that indicates C is 1D), gemm will be
        # converted to FC
        _, op_strs = backend.convert_node(node_def.SerializeToString(), [
            make_tensor_value_info("C", onnx.TensorProto.FLOAT,
                                   (1, )).SerializeToString()
        ])
        op_names = []
        for s in op_strs:
            op = caffe2_pb2.OperatorDef()
            op.ParseFromString(s)
            op_names.append(op.type)
        self.assertEqual(op_names, ['Scale', 'Scale', 'FC'])

        # or with broadcast, gemm will be converted to fc
        node_def = make_node('Gemm', ['A', 'B', 'C'], ["Y"],
                             transB=True,
                             broadcast=1)

        _, op_strs = backend.convert_node(node_def.SerializeToString())
        op_names = []
        for s in op_strs:
            op = caffe2_pb2.OperatorDef()
            op.ParseFromString(s)
            op_names.append(op.type)
        self.assertEqual(op_names, ['FC'])
예제 #8
0
    def _onnx_node_to_caffe2_op(cls, init_model, pred_model, node_def,
                                opset_version):
        cbackend = C.Caffe2Backend(cls._dummy_name)
        if cbackend.support_onnx_import(node_def.op_type):

            # extract value infos from pred model (value infos of
            # node's inputs that are in init model should be all
            # available in pred model)
            value_infos = []
            for name in node_def.input:
                if pred_model is not None:
                    for vi in itertools.chain(pred_model.graph.input,
                                              pred_model.graph.output,
                                              pred_model.graph.value_info):
                        if vi.name == name:
                            value_infos.append(vi.SerializeToString())

            op_strs = cbackend.convert_node(node_def.SerializeToString(),
                                            value_infos, opset_version)
            init_ops = []
            for s in op_strs[0]:
                op = caffe2_pb2.OperatorDef()
                op.ParseFromString(s)
                init_ops.append(op)
            ops = []
            for s in op_strs[1]:
                op = caffe2_pb2.OperatorDef()
                op.ParseFromString(s)
                ops.append(op)
            return Caffe2Ops(ops, init_ops, [])

        if node_def.op_type in cls._special_operators:
            translator = getattr(cls, cls._special_operators[node_def.op_type])
        else:
            translator = cls._common_onnx_node_to_caffe2_op
        ops = translator(init_model, pred_model, OnnxNode(node_def),
                         opset_version)
        if isinstance(ops, Caffe2Ops):
            return ops
        if not isinstance(ops, collections.abc.Iterable):
            ops = [ops]
        return Caffe2Ops(ops, [], [])
예제 #9
0
    def prepare(cls, model, device='CPU', raw_values_dict=None, **kwargs):
        '''
        For Onnx Caffe2Backend, we require that init_graph don't initialize the actual input of the predict_graph,

        for example, if "img" is the input blob for the predict_net, we require that in init_graph and in
        initializer of the predict_graph, "img" is not initalized. We don't have a check for this, since
        there is no way we can know which blob is the input of the predict_graph.
        '''
        if not kwargs.pop('no_check_UNSAFE', False):
            super(Caffe2Backend, cls).prepare(model, device, **kwargs)
        opset_version = None
        for imp in model.opset_import:
            if not imp.HasField("domain") or imp.domain == "":
                opset_version = imp.version
                if imp.version > cls._known_opset_version:
                    warnings.warn(
                        "This version of onnx-caffe2 targets ONNX operator set version {}, but the model we are trying to import uses version {}.  We will try to import it anyway, but if the model uses operators which had BC-breaking changes in the intervening versions, import will fail."
                        .format(cls._known_opset_version, imp.version))
            else:
                warnings.warn("Unrecognized operator set {}".format(
                    imp.domain))
        if opset_version is None:
            if model.ir_version >= 0x00000003:
                raise RuntimeError(
                    "Model with IR version >= 3 did not specify ONNX operator set version (onnx-caffe2 requires it)"
                )
            else:
                opset_version = 1

        model = onnx.shape_inference.infer_shapes(model)

        # Check whether we have RNN related ops
        pred_model = cls.optimize_onnx(model, predict=True)
        rnn_nodes = []
        for node in pred_model.graph.node:
            if node.op_type in {'LSTM', 'GRU', 'RNN'}:
                rnn_nodes.append(node)

        # Build the C++ backend
        # TODO: build a predictor that supports GPU
        #       And for RNN nets, we need to avoid adding init_net
        use_cpp_backend = device == 'CPU' and not rnn_nodes
        # use python backend for now
        use_cpp_backend = False
        if use_cpp_backend:
            c2_rnn_ops = []
            if rnn_nodes:
                init_model = cls.optimize_onnx(model, init=True)
                for node in rnn_nodes:
                    c2ops = cls._onnx_node_to_caffe2_op(
                        init_model, pred_model, node, opset_version)
                    init_ops = [x.SerializeToString() for x in c2ops.init_ops]
                    ops = [x.SerializeToString() for x in c2ops.ops]
                    external_inputs = c2ops.interface_blobs
                    c2_rnn_ops.append(
                        C.Caffe2Ops(init_ops, ops, external_inputs))
                del init_model

            cbackend = C.Caffe2Backend(cls._dummy_name)
            if raw_values_dict:
                cls._external_value_resolution_pass(model, raw_values_dict)
            rep = cbackend.prepare(model.SerializeToString(), device,
                                   c2_rnn_ops)
            # For testing
            # Dump the net descriptions to file for comparison with the Python ones
            if "ONNX_CAFFE2_DEBUG" in os.environ:
                pred_net_str = rep.pred_net()
                pn = caffe2_pb2.NetDef()
                pn.ParseFromString(pred_net_str)
                init_net_str = rep.init_net()
                inn = caffe2_pb2.NetDef()
                inn.ParseFromString(init_net_str)
                with open("cpp.txt", "w") as f:
                    f.write("pred_net: \n{}".format(pn))

            rep_wrapper = Caffe2CppRep(rep)
            return rep_wrapper
        else:
            ws = Workspace()
            device_option = get_device_option(Device(device))

            init_net, predict_net = cls._onnx_model_to_caffe2_net(
                model, device, opset_version, False)

            if raw_values_dict:
                cls._external_value_resolution_pass(model, raw_values_dict)

            # Directly load initializer data into blobs in workspace
            cls._direct_initialize_parameters(
                model.graph.initializer,
                ws,
                device_option,
            )

            initialized = {init.name for init in model.graph.initializer}

            cls._direct_initialize_inputs(
                model.graph.input,
                initialized,
                ws,
                device_option,
            )

            uninitialized = [
                value_info.name for value_info in model.graph.input
                if value_info.name not in initialized
            ]

            if "ONNX_CAFFE2_DEBUG" in os.environ:
                with open("python.txt", "w") as f:
                    f.write("pred_net: \n{}".format(predict_net))
            retval = Caffe2Rep(init_net, predict_net, ws, uninitialized)
            return retval
예제 #10
0
    def test_gemm_conversion(self):
        node_def = make_node('Gemm', ['A', 'B', 'C'], ["Y"], alpha=2., beta=3.)
        node_def_broadcast = make_node('Gemm', ['A', 'B', 'C'], ["Y"],
                                       alpha=2.,
                                       beta=3.,
                                       broadcast=1)
        node_def_transpose_b = make_node('Gemm', ['A', 'B', 'C'], ["Y"],
                                         alpha=2.,
                                         beta=3.,
                                         transB=1)

        node_def_transpose_b_broadcast = make_node('Gemm', ['A', 'B', 'C'],
                                                   ["Y"],
                                                   alpha=2.,
                                                   beta=3.,
                                                   transB=1,
                                                   broadcast=1)

        backend = C.Caffe2Backend()

        # without broadcast and without shape info, gemm will be
        # converted to matmul + add
        _, op_strs = backend.convert_node(node_def.SerializeToString())
        op_names = []
        for s in op_strs:
            op = caffe2_pb2.OperatorDef()
            op.ParseFromString(s)
            op_names.append(op.type)
        self.assertEqual(op_names, ['Scale', 'Scale', 'MatMul', 'Add'])

        # opset7
        # If C is a 1d tensor, gemm will be converted to FC/FCTransposed
        _, op_strs = backend.convert_node(
            node_def_transpose_b.SerializeToString(), [
                make_tensor_value_info("C", onnx.TensorProto.FLOAT,
                                       (3, )).SerializeToString()
            ], 7)
        op_names = []
        for s in op_strs:
            op = caffe2_pb2.OperatorDef()
            op.ParseFromString(s)
            op_names.append(op.type)
        self.assertEqual(op_names, ['Scale', 'Scale', 'FC'])

        _, op_strs = backend.convert_node(node_def.SerializeToString(), [
            make_tensor_value_info("C", onnx.TensorProto.FLOAT,
                                   (3, )).SerializeToString()
        ], 7)
        op_names = []
        for s in op_strs:
            op = caffe2_pb2.OperatorDef()
            op.ParseFromString(s)
            op_names.append(op.type)
        self.assertEqual(op_names, ['Scale', 'Scale', 'FCTransposed'])

        # opset6 without broadcast(C should match A*B's dim)
        # The gemm will be converted to matmul + add, since the FC requires c
        # to be 1d tensor.
        _, op_strs = backend.convert_node(node_def.SerializeToString(), [
            make_tensor_value_info("A", onnx.TensorProto.FLOAT,
                                   (3, 2)).SerializeToString(),
            make_tensor_value_info("B", onnx.TensorProto.FLOAT,
                                   (2, 3)).SerializeToString(),
            make_tensor_value_info("C", onnx.TensorProto.FLOAT,
                                   (3, 3)).SerializeToString()
        ], 6)
        op_names = []
        for s in op_strs:
            op = caffe2_pb2.OperatorDef()
            op.ParseFromString(s)
            op_names.append(op.type)
        self.assertEqual(op_names, ['Scale', 'Scale', 'MatMul', 'Add'])

        # opset6 with broadcast
        # If C is a 1d tensor, gemm will be converted to FC/FCTransposed
        _, op_strs = backend.convert_node(
            node_def_transpose_b_broadcast.SerializeToString(), [
                make_tensor_value_info("C", onnx.TensorProto.FLOAT,
                                       (3, )).SerializeToString()
            ], 6)
        op_names = []
        for s in op_strs:
            op = caffe2_pb2.OperatorDef()
            op.ParseFromString(s)
            op_names.append(op.type)
        self.assertEqual(op_names, ['Scale', 'Scale', 'FC'])

        _, op_strs = backend.convert_node(
            node_def_broadcast.SerializeToString(), [
                make_tensor_value_info("C", onnx.TensorProto.FLOAT,
                                       (3, )).SerializeToString()
            ], 6)
        op_names = []
        for s in op_strs:
            op = caffe2_pb2.OperatorDef()
            op.ParseFromString(s)
            op_names.append(op.type)
        self.assertEqual(op_names, ['Scale', 'Scale', 'FCTransposed'])

        # opset7
        # If C is a scalar and B's last dim is 1, gemm will be converted to FC/FCTransposed
        _, op_strs = backend.convert_node(
            node_def_transpose_b.SerializeToString(), [
                make_tensor_value_info("B", onnx.TensorProto.FLOAT,
                                       (1, 2)).SerializeToString(),
                make_tensor_value_info("C", onnx.TensorProto.FLOAT,
                                       (1, )).SerializeToString()
            ], 7)
        op_names = []
        for s in op_strs:
            op = caffe2_pb2.OperatorDef()
            op.ParseFromString(s)
            op_names.append(op.type)
        self.assertEqual(op_names, ['Scale', 'Scale', 'FC'])

        _, op_strs = backend.convert_node(node_def.SerializeToString(), [
            make_tensor_value_info("B", onnx.TensorProto.FLOAT,
                                   (2, 1)).SerializeToString(),
            make_tensor_value_info("C", onnx.TensorProto.FLOAT,
                                   (1, )).SerializeToString()
        ], 7)
        op_names = []
        for s in op_strs:
            op = caffe2_pb2.OperatorDef()
            op.ParseFromString(s)
            op_names.append(op.type)
        self.assertEqual(op_names, ['Scale', 'Scale', 'FCTransposed'])
        # If C is a scalar and B's last dim is not 1, gemm will be converted
        # to matmul + add.
        _, op_strs = backend.convert_node(
            node_def_transpose_b.SerializeToString(), [
                make_tensor_value_info("B", onnx.TensorProto.FLOAT,
                                       (2, 2)).SerializeToString(),
                make_tensor_value_info("C", onnx.TensorProto.FLOAT,
                                       (1, )).SerializeToString()
            ], 7)
        op_names = []
        for s in op_strs:
            op = caffe2_pb2.OperatorDef()
            op.ParseFromString(s)
            op_names.append(op.type)
        self.assertEqual(op_names, ['Scale', 'Scale', 'MatMul', 'Add'])
        # If C is a scalar and B's shape info is not available,
        # gemm will be converted to matmul + add.
        _, op_strs = backend.convert_node(
            node_def_transpose_b.SerializeToString(), [
                make_tensor_value_info("C", onnx.TensorProto.FLOAT,
                                       (1, )).SerializeToString()
            ], 7)
        op_names = []
        for s in op_strs:
            op = caffe2_pb2.OperatorDef()
            op.ParseFromString(s)
            op_names.append(op.type)
        self.assertEqual(op_names, ['Scale', 'Scale', 'MatMul', 'Add'])