Ejemplo n.º 1
0
def infer():
    # Step1:初始化ONNXRuntime库并配置相应参数, 并进行预测
    # 加载ONNX模型
    sess = InferenceSession(FLAGS.onnx_file)

    # define transforms
    input_shape = sess.get_inputs()[0].shape[2:]
    eval_transforms = ClassificationPresetEval(
        crop_size=input_shape, resize_size=FLAGS.crop_size)
    # 准备输入
    with open(FLAGS.img_path, 'rb') as f:
        img = Image.open(f).convert('RGB')

    img = eval_transforms(img)
    img = np.expand_dims(img, axis=0)

    # 模型预测
    ort_outs = sess.run(output_names=None,
                        input_feed={sess.get_inputs()[0].name: img})

    output = ort_outs[0]
    class_id = output.argmax()
    prob = output[0][class_id]
    print("ONNXRuntime predict: ")
    print(f"class_id: {class_id}, prob: {prob}")
Ejemplo n.º 2
0
    def test_dict_vectorizer_rfr(self):
        this = os.path.abspath(os.path.dirname(__file__))
        data = os.path.join(this, "data", "pipeline_vectorize.onnx")
        sess = InferenceSession(data)
        input_name = sess.get_inputs()[0].name
        self.assertEqual(input_name, "float_input")
        input_type = str(sess.get_inputs()[0].type)
        self.assertEqual(input_type, "map(int64,tensor(float))")
        input_shape = sess.get_inputs()[0].shape
        self.assertEqual(input_shape, [])
        output_name = sess.get_outputs()[0].name
        self.assertEqual(output_name, "variable1")
        output_type = sess.get_outputs()[0].type
        self.assertEqual(output_type, "tensor(float)")
        output_shape = sess.get_outputs()[0].shape
        self.assertEqual(output_shape, [1, 1])

        x = {0: 25.0, 1: 5.13, 2: 0.0, 3: 0.453, 4: 5.966}
        res = sess.run([output_name], {input_name: x})

        model_onnx = onnx.load(data)
        oinf = OnnxInference(model_onnx, runtime='onnxruntime1')
        res2 = oinf.run({input_name: x})

        x = {k: numpy.float32(v) for k, v in x.items()}
        oinf = OnnxInference(model_onnx, runtime='python')
        res3 = oinf.run({input_name: [x]})  # , verbose=10, fLOG=print)

        self.assertEqualFloat(res[0][0, 0], res2["variable1"][0, 0])
        self.assertEqualFloat(res[0][0, 0], res3["variable1"][0])
Ejemplo n.º 3
0
def format_results(sess: rt.InferenceSession, data: list) -> dict:
    input_name = sess.get_inputs()[0].name
    input_shape = sess.get_inputs()[0].shape
    label_name = sess.get_outputs()[0].name

    return {
        'input_name': input_name,
        'input_shape': input_shape[1:],
        'output_name': label_name,
        'prediction': data,
        'createdAt': datetime.now(timezone.utc).astimezone().isoformat()
    }
class OnnxModelLoader:
    def __init__(self, onnx_path: str):
        """
        Class for loading ONNX models to inference on CPU.

        :param onnx_path: path to ONNX model file (*.onnx file).
        """
        self.onnx_path = onnx_path
        self.sess = InferenceSession(self.onnx_path,
                                     providers=['CPUExecutionProvider'])

        # In current case model always has exactly one input and one output.
        self.input_name = [x.name for x in self.sess.get_inputs()][0]
        self.output_name = [x.name for x in self.sess.get_outputs()][0]

    def inference(self, inputs: np.ndarray) -> np.ndarray:
        """
        Run inference.

        :param inputs: input numpy array.
        :return: output numpy array.
        """
        outputs = self.sess.run(
            [self.output_name],
            input_feed={self.input_name: np.float32(inputs)})
        return outputs[0]
Ejemplo n.º 5
0
def run_inference(sess: rt.InferenceSession,
                  TRUNCATION: float = 0.7,
                  seed=None) -> np.array:
    # return raw array from Generator

    # really badly set numpy seed from string if provided
    # note that `hash()` would not work here, as it is itself initialized from a random state
    # it would be possible to force hash() to be set from a static type, but would require an additional python file
    #   to set the environment seed... So equally awful
    random.seed(seed)  # this may be a string
    np.random.seed(
        random.randint(0, 2**32 - 1)
    )  # use python random state to generate numpy random state repeatably, lol

    # randomized generator inputs
    latents = np.random.randn(1, 512).astype(np.float32)
    truncation = np.array([TRUNCATION]).astype(np.float32)

    input_name_a = sess.get_inputs()[0].name
    input_name_b = sess.get_inputs()[1].name
    label_name = sess.get_outputs()[0].name
    pred = sess.run([label_name], {
        input_name_a: latents,
        input_name_b: truncation
    })[0]

    return pred
Ejemplo n.º 6
0
 def test_xgboost_classifier_i5450(self):
     iris = load_iris()
     X, y = iris.data, iris.target
     X_train, X_test, y_train, y_test = train_test_split(X,
                                                         y,
                                                         random_state=10)
     clr = XGBClassifier(objective="multi:softmax",
                         max_depth=1,
                         n_estimators=2)
     clr.fit(X_train,
             y_train,
             eval_set=[(X_test, y_test)],
             early_stopping_rounds=40)
     initial_type = [('float_input', FloatTensorType([None, 4]))]
     onx = convert_xgboost(clr, initial_types=initial_type)
     sess = InferenceSession(onx.SerializeToString())
     input_name = sess.get_inputs()[0].name
     label_name = sess.get_outputs()[1].name
     predict_list = [1., 20., 466., 0.]
     predict_array = np.array(predict_list).reshape(
         (1, -1)).astype(np.float32)
     pred_onx = sess.run([label_name], {input_name: predict_array})[0]
     pred_xgboost = sessresults = clr.predict_proba(predict_array)
     bst = clr.get_booster()
     bst.dump_model('dump.raw.txt')
     dump_data_and_model(
         X_test.astype(np.float32) + 1e-5,
         clr,
         onx,
         allow_failure=
         "StrictVersion(onnx.__version__) < StrictVersion('1.3.0')",
         basename="XGBClassifierIris")
Ejemplo n.º 7
0
 def _predict_with_onnx(model, X):
     session = InferenceSession(model.SerializeToString())
     output_names = [s_output.name for s_output in session.get_outputs()]
     input_names = [s_input.name for s_input in session.get_inputs()]
     if len(input_names) > 1:
         raise RuntimeError(
             "Test expects one input. Found multiple inputs: %r."
             "" % input_names)
     input_name = input_names[0]
     return session.run(output_names, {input_name: X})[0][:, 0]
Ejemplo n.º 8
0
    def get_io_numpy_type_map(
            ort_session: InferenceSession) -> Dict[str, numpy.dtype]:
        """Create a mapping from input/output name to numpy data type"""
        name_to_numpy_type = {}
        for input in ort_session.get_inputs():
            name_to_numpy_type[input.name] = TypeHelper.ort_type_to_numpy_type(
                input.type)

        for output in ort_session.get_outputs():
            name_to_numpy_type[
                output.name] = TypeHelper.ort_type_to_numpy_type(output.type)
        return name_to_numpy_type
Ejemplo n.º 9
0
def onnx_predict(sess: InferenceSession, x: np.ndarray):
    """
    use ONNX runtime session to predict result
    :param sess: ONNX runtime session
    :param x: input ndarray
    :return: predicted result
    """
    x = x.reshape((-1, 1, 28, 28))
    input_name = sess.get_inputs()[0].name
    label_name = sess.get_outputs()[0].name
    pred = sess.run([label_name], {input_name: x.astype("float32")})[0]
    return np.argmax(pred, axis=1)[0]
Ejemplo n.º 10
0
def predict(ort_session: ort.InferenceSession, im_array: np.ndarray) -> Image:
    sample = preprocess(im_array)
    inputs_test = np.expand_dims(sample["image"], 0).astype(np.float32)

    ort_inputs = {ort_session.get_inputs()[0].name: inputs_test}
    ort_outs = ort_session.run(None, ort_inputs)

    d1 = ort_outs[0]
    pred = d1[:, 0, :, :]
    predict = np.squeeze(norm_pred(pred))
    img = Image.fromarray(predict * 255).convert("RGB")

    return img
Ejemplo n.º 11
0
 def test_variable_names(self):
     pipeline = Pipeline([("passthrough", Passthrough())])
     initial_types = [("input", FloatTensorType([None, 2]))]
     model_onnx = convert_sklearn(pipeline,
                                  initial_types=initial_types,
                                  target_opset=TARGET_OPSET,
                                  verbose=0)
     self.assertIn('Identity', str(model_onnx))
     x = np.array([0, 1, 1, 0], dtype=np.float32).reshape((-1, 2))
     sess = InferenceSession(model_onnx.SerializeToString())
     name = sess.get_inputs()[0].name
     got = sess.run(None, {name: x})
     assert_almost_equal(x, got[0])
Ejemplo n.º 12
0
class SimplifiedOnnxInference:
    "Simple wrapper around InferenceSession which imitates OnnxInference."

    def __init__(self, ort):
        from onnxruntime import InferenceSession
        self.sess = InferenceSession(ort)

    @property
    def input_names(self):
        "Returns InferenceSession input names."
        return [_.name for _ in self.sess.get_inputs()]

    def run(self, input):
        "Calls InferenceSession.run."
        return self.sess.run(None, input)
Ejemplo n.º 13
0
 def test_pipeline_make_column_selector(self):
     X = pandas.DataFrame({
         'city': ['London', 'London', 'Paris', 'Sallisaw'],
         'rating': [5, 3, 4, 5]})
     X['rating'] = X['rating'].astype(numpy.float32)
     ct = make_column_transformer(
         (StandardScaler(), make_column_selector(
             dtype_include=numpy.number)),
         (OneHotEncoder(), make_column_selector(
             dtype_include=object)))
     expected = ct.fit_transform(X)
     onx = to_onnx(ct, X, target_opset=TARGET_OPSET)
     sess = InferenceSession(onx.SerializeToString())
     names = [i.name for i in sess.get_inputs()]
     got = sess.run(None, {names[0]: X[names[0]].values.reshape((-1, 1)),
                           names[1]: X[names[1]].values.reshape((-1, 1))})
     assert_almost_equal(expected, got[0])
Ejemplo n.º 14
0
    def verify_onnx(model: T5EncoderDecoderInit, ort_session: InferenceSession, device: torch.device, max_cases=4):
        """ Compare the result from PyTorch and OnnxRuntime to verify the ONNX model is good.
        """
        ort_inputs = ort_session.get_inputs()
        use_decoder_input_ids = len(ort_inputs) == 3

        test_cases = [(4, 11), (1, 2), (3, 1), (8, 5)]
        test_cases_max_diff = []
        for (batch_size, encode_sequence_length) in test_cases[:max_cases]:
            inputs = T5EncoderDecoderInitInputs.create_dummy(model.config,
                                                             batch_size,
                                                             encode_sequence_length,
                                                             use_decoder_input_ids=use_decoder_input_ids,
                                                             device=device)

            ort_outputs = T5EncoderDecoderInitHelper.onnxruntime_inference(ort_session, inputs)

            # Run inference of PyTorch model
            input_list = inputs.to_list()
            torch_outputs = model(*input_list)

            assert (torch_outputs[0].cpu().numpy().shape == ort_outputs[0].shape)
            max_diff = numpy.amax(numpy.abs(torch_outputs[0].cpu().numpy() - ort_outputs[0]))
            logger.debug(f"logits max_diff={max_diff}")
            max_diff_all = max_diff

            assert (torch_outputs[1].cpu().numpy().shape == ort_outputs[1].shape)
            max_diff = numpy.amax(numpy.abs(torch_outputs[1].cpu().numpy() - ort_outputs[1]))
            logger.debug(f"encoder_hidden_states max_diff={max_diff}")
            max_diff_all = max(max_diff_all, max_diff)

            for i in range(2 * model.config.num_layers):
                max_diff = numpy.amax(numpy.abs(torch_outputs[2][i].cpu().numpy() - ort_outputs[2 + i]))
                logger.debug(f"self attention past state {i} max_diff={max_diff}")

            for i in range(2 * model.config.num_layers):
                max_diff = numpy.amax(
                    numpy.abs(torch_outputs[3][i].cpu().numpy() - ort_outputs[2 + 2 * model.config.num_layers + i]))
                logger.debug(f"cross attention past state {i} max_diff={max_diff}")
                max_diff_all = max(max_diff_all, max_diff)

            test_cases_max_diff.append(max_diff_all)
            logger.info(
                f"batch_size={batch_size} encode_sequence_length={encode_sequence_length}, max_diff={max_diff_all}")

        return max(test_cases_max_diff)
    def _test_lgbm(self, X, model, extra_config={}):
        # Create ONNX-ML model
        onnx_ml_model = convert_model(
            model, 'lgbm-onnxml',
            [("input", FloatTensorType([X.shape[0], X.shape[1]]))])[0]

        # Create ONNX model
        onnx_model = convert_model(
            model,
            'lgbm-onnx',
            [("input", FloatTensorType([X.shape[0], X.shape[1]]))],
            without_onnx_ml=True)[0]

        try:
            from onnxruntime import InferenceSession
        except ImportError:
            # onnxruntime not installed (python 2.7)
            return

        # Get the predictions for the ONNX-ML model
        session = InferenceSession(onnx_ml_model.SerializeToString())
        output_names = [
            session.get_outputs()[i].name
            for i in range(len(session.get_outputs()))
        ]
        onnx_ml_pred = [[] for i in range(len(output_names))]
        inputs = {session.get_inputs()[0].name: X}
        pred = session.run(output_names, inputs)
        for i in range(len(output_names)):
            if output_names[i] == "label":
                onnx_ml_pred[1] = pred[i]
            else:
                onnx_ml_pred[0] = pred[i]

        # Get the predictions for the ONNX model
        session = InferenceSession(onnx_model.SerializeToString())
        onnx_pred = [[] for i in range(len(output_names))]
        pred = session.run(output_names, inputs)
        for i in range(len(output_names)):
            if output_names[i] == "label":
                onnx_pred[1] = pred[i]
            else:
                onnx_pred[0] = pred[i]

        return onnx_ml_pred, onnx_pred, output_names
Ejemplo n.º 16
0
class OnnxModelLoader:
    def __init__(self, onnx_path: str) -> None:
        """
        Class for loading ONNX models to inference on CPU. CPU inference is very effective using onnxruntime.

        :param onnx_path: path to ONNX model file (*.onnx file).
        """
        self.sess = InferenceSession(onnx_path,
                                     providers=['CPUExecutionProvider'])

        self.input_name = [x.name for x in self.sess.get_inputs()][0]
        self.output_names = [x.name for x in self.sess.get_outputs()]

    def inference(self, inputs: np.ndarray) -> List[np.ndarray]:
        """
        Run inference.

        :param inputs: list of arguments, order must match names in input_names.
        :return: list of outputs.
        """
        return self.sess.run(self.output_names,
                             input_feed={self.input_name: inputs})
Ejemplo n.º 17
0
def benchmark(name,
              onx,
              fct_numpy,
              *args,
              dims=(1, 10, 100, 200, 500, 1000, 2000, 10000)):
    sess = InferenceSession(onx.SerializeToString())
    device = C_OrtDevice(C_OrtDevice.cpu(), C_OrtDevice.default_memory(), 0)
    names = [i.name for i in sess.get_inputs()]
    out_names = [o.name for o in sess.get_outputs()]
    if len(names) != len(args):
        raise RuntimeError(f"Size mismatch {len(names)} != {len(args)}.")

    rows = []
    for dim in tqdm(dims):
        new_args = [reshape(a, dim) for a in args]
        ortvalues = [
            C_OrtValue.ortvalue_from_numpy(a, device) for a in new_args
        ]

        ms = measure_time(lambda: fct_numpy(*new_args), repeat=50, number=100)
        ms.update(dict(name=name, impl='numpy', dim=dim))
        rows.append(ms)

        inps = {n: a for n, a in zip(names, new_args)}
        ms = measure_time(lambda: sess.run(None, inps))
        ms.update(dict(name=name, impl='sess', dim=dim))
        rows.append(ms)

        bind = SessionIOBinding(sess._sess)
        ms = measure_time(lambda: bind_and_run(sess._sess, bind, names,
                                               ortvalues, out_names, device))
        ms.update(dict(name=name, impl='bind_run', dim=dim))
        rows.append(ms)

        ms = measure_time(lambda: nobind_just_run(sess._sess, bind))
        ms.update(dict(name=name, impl='run', dim=dim))
        rows.append(ms)

    return rows
Ejemplo n.º 18
0
def predict_with_onnxruntime(onx, X):
    sess = InferenceSession(onx.SerializeToString())
    input_name = sess.get_inputs()[0].name
    res = sess.run(None, {input_name: X.astype(np.float32)})
    return res[0]
    def forward_no_training(self, exc=None, verbose=False):
        if exc is None:
            exc = __name__ != '__main__'
        from onnxruntime.capi._pybind_state import (OrtValue as C_OrtValue,
                                                    OrtDevice as C_OrtDevice,
                                                    OrtMemType)
        from onnxruntime.capi._pybind_state import (OrtValueVector)
        from onnxcustom.training.ortgradient import OrtGradientForwardBackward

        X, y = make_regression(  # pylint: disable=W0632
            100, n_features=10, bias=2)
        X = X.astype(numpy.float32)
        y = y.astype(numpy.float32)
        X_train, X_test, y_train, _ = train_test_split(X, y)
        reg = LinearRegression()
        reg.fit(X_train, y_train)
        reg.coef_ = reg.coef_.reshape((1, -1))
        onx = to_onnx(reg,
                      X_train,
                      target_opset=opset,
                      black_op={'LinearRegressor'})

        # starts testing
        if verbose:
            print("[forward_no_training] start testing")
        if exc:
            if verbose:
                print("[forward_no_training] check exception")
            self.assertRaise(
                lambda: OrtGradientForwardBackward(
                    onx, debug=True, enable_logging=True, providers=['NONE']),
                ValueError)
        if verbose:
            print("[forward_no_training] instantiate")
        forback = OrtGradientForwardBackward(onx,
                                             debug=True,
                                             enable_logging=True)
        self.assertEqual(repr(forback), "OrtGradientForwardBackward(...)")
        self.assertTrue(hasattr(forback, 'cls_type_'))
        self.assertEqual(forback.cls_type_._onx_inp,
                         ['X', 'coef', 'intercept'])
        self.assertEqual(forback.cls_type_._onx_out,
                         ['X_grad', 'coef_grad', 'intercept_grad'])
        self.assertEqual(forback.cls_type_._weights_to_train,
                         ['coef', 'intercept'])
        self.assertEqual(forback.cls_type_._grad_input_names,
                         ['X', 'coef', 'intercept'])
        self.assertEqual(forback.cls_type_._input_names, ['X'])
        self.assertEqual(forback.cls_type_._bw_fetches_names,
                         ['X_grad', 'coef_grad', 'intercept_grad'])
        self.assertEqual(forback.cls_type_._output_names, ['variable'])

        if verbose:
            print("[forward_no_training] expected prediction")

        expected = reg.predict(X_test)
        coef = reg.coef_.astype(numpy.float32).reshape((-1, 1))
        intercept = numpy.array([reg.intercept_], dtype=numpy.float32)

        if verbose:
            print("[forward_no_training] InferenceSession")

        providers = device_to_providers('cpu')
        sess0 = InferenceSession(onx.SerializeToString(), providers=providers)
        inames = [i.name for i in sess0.get_inputs()]  # pylint: disable=E1101
        self.assertEqual(inames, ['X'])
        got = sess0.run(None, {'X': X_test})
        self.assertEqualArray(expected.ravel(), got[0].ravel(), decimal=4)

        if verbose:
            print("[forward_no_training] evaluation")

        sess_eval = forback.cls_type_._sess_eval  # pylint: disable=E1101
        inames = [i.name for i in sess_eval.get_inputs()]
        self.assertEqual(inames, ['X', 'coef', 'intercept'])
        got = sess_eval.run(None, {
            'X': X_test,
            'coef': coef,
            'intercept': intercept
        })
        self.assertEqualArray(expected.ravel(), got[0].ravel(), decimal=4)

        # OrtValue
        if verbose:
            print("[forward_no_training] OrtValue")
        inst = forback.new_instance()
        device = C_OrtDevice(C_OrtDevice.cpu(), OrtMemType.DEFAULT, 0)

        # list of OrtValues
        inputs = []
        for a in [X_test, coef, intercept]:
            inputs.append(C_OrtValue.ortvalue_from_numpy(a, device))
        got_ort = inst.forward(inputs)
        got = [v.numpy() for v in got_ort]
        self.assertEqual(len(got), 1)
        self.assertEqualArray(expected.ravel(), got[0].ravel(), decimal=4)

        # OrtValueVector
        if verbose:
            print("[forward_no_training] OrtValueVector")
        inputs = OrtValueVector()
        for a in [X_test, coef, intercept]:
            inputs.push_back(C_OrtValue.ortvalue_from_numpy(a, device))
        got = inst.forward(inputs)
        self.assertEqual(len(got), 1)
        self.assertEqualArray(expected.ravel(),
                              got[0].numpy().ravel(),
                              decimal=4)

        # numpy
        if verbose:
            print("[forward_no_training] numpy")
        inputs = [X_test, coef, intercept]
        got = inst.forward(inputs)
        self.assertEqual(len(got), 1)
        self.assertEqualArray(expected.ravel(),
                              got[0].numpy().ravel(),
                              decimal=4)
        if verbose:
            print("[forward_no_training] end")
    def test_forward_no_training_pickle(self):
        from onnxruntime.capi._pybind_state import (OrtValue as C_OrtValue,
                                                    OrtMemType, OrtDevice as
                                                    C_OrtDevice)
        from onnxruntime.capi._pybind_state import (OrtValueVector)
        from onnxcustom.training.ortgradient import OrtGradientForwardBackward
        X, y = make_regression(  # pylint: disable=W0632
            100, n_features=10, bias=2)
        X = X.astype(numpy.float32)
        y = y.astype(numpy.float32)
        X_train, X_test, y_train, _ = train_test_split(X, y)
        reg = LinearRegression()
        reg.fit(X_train, y_train)
        reg.coef_ = reg.coef_.reshape((1, -1))
        onx = to_onnx(reg,
                      X_train,
                      target_opset=opset,
                      black_op={'LinearRegressor'})
        forback0 = OrtGradientForwardBackward(onx, debug=True)
        st = io.BytesIO()
        pickle.dump(forback0, st)
        st2 = io.BytesIO(st.getvalue())
        forback = pickle.load(st2)

        self.assertTrue(hasattr(forback, 'cls_type_'))
        self.assertEqual(forback.cls_type_._onx_inp,
                         ['X', 'coef', 'intercept'])
        self.assertEqual(forback.cls_type_._onx_out,
                         ['X_grad', 'coef_grad', 'intercept_grad'])
        self.assertEqual(forback.cls_type_._weights_to_train,
                         ['coef', 'intercept'])
        self.assertEqual(forback.cls_type_._grad_input_names,
                         ['X', 'coef', 'intercept'])
        self.assertEqual(forback.cls_type_._input_names, ['X'])
        self.assertEqual(forback.cls_type_._bw_fetches_names,
                         ['X_grad', 'coef_grad', 'intercept_grad'])
        self.assertEqual(forback.cls_type_._output_names, ['variable'])

        expected = reg.predict(X_test)
        coef = reg.coef_.astype(numpy.float32).reshape((-1, 1))
        intercept = numpy.array([reg.intercept_], dtype=numpy.float32)

        providers = device_to_providers('cpu')
        sess0 = InferenceSession(onx.SerializeToString(), providers=providers)
        inames = [i.name for i in sess0.get_inputs()]
        self.assertEqual(inames, ['X'])
        got = sess0.run(None, {'X': X_test})
        self.assertEqualArray(expected.ravel(), got[0].ravel(), decimal=4)

        sess_eval = forback.cls_type_._sess_eval  # pylint: disable=W0212
        inames = [i.name for i in sess_eval.get_inputs()]
        self.assertEqual(inames, ['X', 'coef', 'intercept'])
        got = sess_eval.run(None, {
            'X': X_test,
            'coef': coef,
            'intercept': intercept
        })
        self.assertEqualArray(expected.ravel(), got[0].ravel(), decimal=4)

        # OrtValue
        inst = forback.new_instance()
        inputs = []
        device = C_OrtDevice(C_OrtDevice.cpu(), OrtMemType.DEFAULT, 0)
        for a in [X_test, coef, intercept]:
            inputs.append(C_OrtValue.ortvalue_from_numpy(a, device))
        got_ort = inst.forward(inputs)
        got = [v.numpy() for v in got_ort]
        self.assertEqual(len(got), 1)
        self.assertEqualArray(expected.ravel(), got[0].ravel(), decimal=4)

        # OrtValueVector
        inputs = OrtValueVector()
        for a in [X_test, coef, intercept]:
            inputs.push_back(C_OrtValue.ortvalue_from_numpy(a, device))
        got = inst.forward(inputs)
        self.assertEqual(len(got), 1)
        self.assertEqualArray(expected.ravel(),
                              got[0].numpy().ravel(),
                              decimal=4)

        # numpy
        inputs = [X_test, coef, intercept]
        got = inst.forward(inputs)
        self.assertEqual(len(got), 1)
        self.assertEqualArray(expected.ravel(),
                              got[0].numpy().ravel(),
                              decimal=4)
Ejemplo n.º 21
0
 def get_input_type(ort_session: InferenceSession, name: str) -> str:
     for i, input in enumerate(ort_session.get_inputs()):
         if input.name == name:
             return input.type
     raise ValueError(f"input name {name} not found")
Ejemplo n.º 22
0
    def _create_onnx_graphs(self):
        """
        Creates forward and backward ONNX graph.
        The new class has the following attributes:

        * `__doc__`: doc string
        * `__module__`: module name (this file)
        * `_run_options`: see :epkg:`RunOptions`
        * `_sess`: :epkg:`InferenceSession` with the original graph
        * `_sess_eval`: :epkg:`InferenceSession` on the graph
            with weights as inputs
        * `_training_agent`: :epkg:`TrainingAgent`
        * `_cache`: :epkg:`OrtValueCache`
        * `_logger`: logger
        * `_input_names`: input names
        * `_debug`: use debug mode
        * `_grad_input_names`: gradient input names
        * `_output_names`: output names
        * `_weights_to_train`: names of the weights to train

        Training attributes

        * `_bw_fetches_names`: bw_fetches_names,
        * `_fw_outputs_device_info`: fw_outputs_device_info,
        * `_bw_outputs_device_info`: bw_outputs_device_info,
        * `_fw_no_grad_output_device_info`: fw_no_grad_output_device_info,
        * `_graph_info`: graph_info}

        Additional attributes added if *keep_model* is True:

        * `_trained_onnx`: ONNX graph for the gradient
        * `_optimized_pre_grad_model`: evaluation ONNX graph taking
            weights as inputs
        * `_graph_builder`: :epkg:`OrtModuleGraphBuilder`
        """
        logger = self._logger
        if logger is not None:
            logger.info("[OrtGradientForwardBackward] create training onnx")
            logger.info("[OrtGradientForwardBackward] input_names=%r",
                        self.input_names)
            logger.info("[OrtGradientForwardBackward] output_names=%r",
                        self.output_names)
            logger.info("[OrtGradientForwardBackward] weights_to_train=%r",
                        self.weights_to_train)

        builder = OrtModuleGraphBuilder()

        if logger is not None:
            cf = self.graph_builder_config.graph_transformer_config
            cfp = cf.propagate_cast_ops_config
            logger.info("[OrtGradientForwardBackward] "
                        "OrtModuleGraphBuilder.initialize")
            logger.info(
                "[OrtGradientForwardBackward] graph_builder_config=%s",
                OrtGradientForwardBackward._repr_helper_(
                    self.graph_builder_config, indent=4))
            logger.info(
                "[OrtGradientForwardBackward] graph_builder_config."
                "graph_transformer_config=%s",
                OrtGradientForwardBackward._repr_helper_(cf, indent=4))
            logger.info(
                "[OrtGradientForwardBackward] graph_builder_config."
                "graph_transformer_config.propagate_cast_ops_config=%s",
                OrtGradientForwardBackward._repr_helper_(cfp, indent=4))

        builder.initialize(self.onnx_model.SerializeToString(),
                           self.graph_builder_config)

        if logger is not None:
            logger.info(
                "[OrtGradientForwardBackward] OrtModuleGraphBuilder.build")
        builder.build()

        if logger is not None:
            logger.info(
                "[OrtGradientForwardBackward] OrtModuleGraphBuilder.get_model")

        train_onnx_model_serialized = builder.get_model()

        optimized_pre_grad_model = builder.get_inference_optimized_model()
        graph_info = builder.get_graph_info()

        if logger is not None:
            logger.info(
                "[OrtGradientForwardBackward] graph_info=%s",
                OrtGradientForwardBackward._repr_helper_(graph_info, indent=4))
            logger.info("[OrtGradientForwardBackward] create TrainSession")
            logger.info(
                "[OrtGradientForwardBackward] sess_options=%s",
                OrtGradientForwardBackward._repr_helper_(self.sess_options,
                                                         indent=4))
            logger.info("[OrtGradientForwardBackward] providers=%r",
                        self.providers)

        sess = InferenceSession(train_onnx_model_serialized,
                                sess_options=self.sess_options,
                                provider_options=self.provider_options,
                                providers=self.providers)

        if logger is not None:
            logger.info("[OrtGradientForwardBackward] create InferenceSession")

        sess_eval = InferenceSession(optimized_pre_grad_model,
                                     sess_options=self.sess_options,
                                     provider_options=self.provider_options,
                                     providers=self.providers)

        if logger is not None:
            logger.info("[OrtGradientForwardBackward] create training agent")

        grad_input_names = [obj.name for obj in sess.get_inputs()]
        bw_fetches_names = [obj.name for obj in sess.get_outputs()]

        fw_outputs_device_info = [
            OrtDevice(
                OrtGradientForwardBackward._provider_name_to_device_type(i),
                OrtDevice.default_memory(), self.device_index)
            for i in self.providers
        ]
        bw_outputs_device_info = [
            OrtDevice(
                OrtGradientForwardBackward._provider_name_to_device_type(
                    self.providers[0]), OrtDevice.default_memory(),
                self.device_index) for i in bw_fetches_names
        ]
        fw_no_grad_output_device_info = [
            OrtDevice(
                OrtGradientForwardBackward._provider_name_to_device_type(
                    self.providers[0]), OrtDevice.default_memory(),
                self.device_index) for i in self.output_names
        ]

        try:
            # onnxruntime>=1.12
            training_agent = TrainingAgent(sess._sess, grad_input_names,
                                           fw_outputs_device_info,
                                           bw_fetches_names,
                                           bw_outputs_device_info, 0)
        except TypeError:
            # onnxruntime<=1.11
            training_agent = TrainingAgent(sess._sess, grad_input_names,
                                           fw_outputs_device_info,
                                           bw_fetches_names,
                                           bw_outputs_device_info)

        if logger is not None:
            logger.info(
                "[OrtGradientForwardBackward] instantiate dynamic class %r",
                self.class_name)
            logger.info("[OrtGradientForwardBackward] weights_to_train=%r",
                        self.weights_to_train)
            logger.info("[OrtGradientForwardBackward] grad_input_names=%r",
                        grad_input_names)
            logger.info("[OrtGradientForwardBackward] bw_fetches_names=%r",
                        bw_fetches_names)
            logger.info("[OrtGradientForwardBackward] device_index=%r",
                        self.device_index)
        devices = list(fw_outputs_device_info)
        while len(devices) < len(grad_input_names):
            devices.append(devices[-1])

        trained_onnx = onnx.load(BytesIO(train_onnx_model_serialized))
        onnx_loss = onnx.load(BytesIO(optimized_pre_grad_model))
        for i, node in enumerate(trained_onnx.graph.node):
            if node.name == '':
                node.name = "N%d" % i
        for i, node in enumerate(onnx_loss.graph.node):
            if node.name == '':
                node.name = "N%d" % i

        kwargs = {
            '_run_options': self.run_options,
            '_sess': sess,
            '_sess_eval': sess_eval,
            '_training_agent': training_agent,
            '_cache': OrtValueCache(),
            '_logger': logger,
            '_input_names': self.input_names,
            '_grad_input_names': grad_input_names,
            '_output_names': self.output_names,
            '_bw_fetches_names': bw_fetches_names,
            '_fw_outputs_device_info': fw_outputs_device_info,
            '_bw_outputs_device_info': bw_outputs_device_info,
            '_fw_no_grad_output_device_info': fw_no_grad_output_device_info,
            '_weights_to_train': list(sorted(self.weights_to_train)),
            '_graph_info': graph_info,
            #
            '_trained_onnx': trained_onnx,
            '_optimized_pre_grad_model': onnx_loss,
            '_graph_builder': builder,
            '_devices': devices,
            '_debug': self.debug
        }
        graph = kwargs['_trained_onnx'].graph
        kwargs.update({
            '_onx_inp': [o.name for o in graph.input],
            '_onx_out': [o.name for o in graph.output]
        })

        if len(kwargs['_onx_inp']) != len(kwargs['_onx_out']):
            raise RuntimeError(  # pragma: no cover
                "Gradient input and output are inconsistant: "
                "%r != %r" % (kwargs['_onx_inp'], kwargs['_onx_out']))
        return kwargs
Ejemplo n.º 23
0
class OpRunOnnxRuntime:
    """
    Unique operator which calls :epkg:`onnxruntime`
    to compute predictions for one operator.
    """
    def __init__(self,
                 onnx_node,
                 desc=None,
                 variables=None,
                 dtype=None,
                 **options):
        """
        @param      onnx_node               :epkg:`onnx` node
        @param      desc                    internal representation
        @param      variables               registered variables created by previous operators
        @param      dtype                   float computation type
        @param      options                 runtime options
        """
        self._provider = 'onnxruntime'
        self.onnx_node = onnx_node
        self.desc = desc
        self._schema = _schemas.get(onnx_node.op_type, None)
        if desc is not None:
            if 'atts' in desc:
                for a, b in desc['atts'].items():
                    if not isinstance(b, dict) or 'value' not in b:
                        raise ValueError(  # pragma: no cover
                            "Unexpected value {}.".format(b))
                    options[a] = b['value']

        self.options = options
        self.dtype = dtype
        self._init(variables)

    def _name_mapping(self, inputs):
        mapping = {}
        new_inputs = []
        for name in inputs:
            if name in mapping:
                i = 0
                new_name = "{}_{}".format(name, i)
                while new_name in mapping:
                    i += 1
                    new_name = "{}_{}".format(name, i)
                mapping[new_name] = name
                new_inputs.append(new_name)
            else:
                new_inputs.append(name)
                mapping[name] = name
        return mapping, new_inputs

    def _guess_proto_type(self, dtype):
        if dtype == numpy.float32:
            return TensorProto.FLOAT  # pylint: disable=E1101
        if dtype == numpy.float64:
            return TensorProto.DOUBLE  # pylint: disable=E1101
        if dtype == numpy.int64:
            return TensorProto.INT64  # pylint: disable=E1101
        raise RuntimeError("Unable to guess type for dtype={}.".format(
            dtype))  # pragma: no cover

    def _init(self, variables=None):
        """
        Initializes the node.

        @param      variables               registered variables created by previous operators

        The current implementation for operator *Scan*
        only works for matrices.
        """
        try:
            self.alg_class = getattr(alg2, 'Onnx' + self.onnx_node.op_type)
        except AttributeError:
            self.alg_class = getattr(alg, 'Onnx' + self.onnx_node.op_type)
        inputs = list(self.onnx_node.input)
        self.mapping, self.inputs = self._name_mapping(inputs)
        self.outputs = list(self.onnx_node.output)

        options = self.options.copy()
        target_opset = options.pop('target_opset', None)
        domain = options.pop('domain', None)
        disable_optimisation = options.pop('disable_optimisation', False)
        ir_version = options.pop('ir_version', None)

        if domain == '' and target_opset < 9:
            # target_opset should be >= 9 not {} for main domain.
            # We assume it was the case when the graph was created.
            pass

        if self.onnx_node.op_type == 'ConstantOfShape':
            for k in options:
                v = options[k]
                if isinstance(v, numpy.ndarray):
                    options[k] = make_tensor(k,
                                             self._guess_proto_type(v.dtype),
                                             v.shape, v.tolist())

            self.inst_ = self.alg_class(*self.inputs,
                                        output_names=self.outputs,
                                        op_version=target_opset,
                                        **options)
            inputs = get_defined_inputs(self.inputs,
                                        variables,
                                        dtype=self.dtype)
            try:
                self.onnx_ = self.inst_.to_onnx(inputs,
                                                target_opset=target_opset,
                                                domain=domain)
                if "dim_value: 0" in str(self.onnx_):
                    raise RuntimeError(  # pragma: no cover
                        "Probable issue as one dimension is null.\n--\n{}".
                        format(self.onnx_))
            except AttributeError as e:  # pragma: no cover
                # older version of skl2onnx
                self.onnx_ = self.inst_.to_onnx(inputs)
                if "dim_value: 0" in str(self.onnx_):
                    raise RuntimeError(
                        "Probable issue as one dimension is null.\n--\n{}".
                        format(self.onnx_)) from e
            forced = False
        elif self.onnx_node.op_type == 'Scan':
            self.inst_ = self.alg_class(*self.inputs,
                                        output_names=self.outputs,
                                        op_version=target_opset,
                                        **options)
            inputs = get_defined_inputs(self.inputs,
                                        variables,
                                        dtype=self.dtype)
            outputs = get_defined_outputs(self.outputs,
                                          self.onnx_node,
                                          inputs,
                                          variables,
                                          dtype=self.dtype)
            inputs = [(name, cl.__class__([None, None]))
                      for (name, cl) in inputs]
            outputs = [(name, cl.__class__([None, None]))
                       for (name, cl) in outputs]
            self.onnx_ = self.inst_.to_onnx(inputs,
                                            outputs=outputs,
                                            target_opset=target_opset,
                                            domain=domain)
            if "dim_value: 0" in str(self.onnx_):
                raise RuntimeError(  # pragma: no cover
                    "Probable issue as one dimension is null.\n--\n{}".format(
                        self.onnx_))
            forced = True
        else:
            self.inst_ = self.alg_class(*self.inputs,
                                        output_names=self.outputs,
                                        op_version=target_opset,
                                        domain=domain,
                                        **options)
            inputs = get_defined_inputs(self.inputs,
                                        variables,
                                        dtype=self.dtype)

            try:
                self.onnx_ = self.inst_.to_onnx(inputs,
                                                target_opset=target_opset,
                                                domain=domain)
                if "dim_value: 0" in str(self.onnx_):
                    raise RuntimeError(  # pragma: no cover
                        "Probable issue as one dimension is null.\n--\n{}\n---\n{}"
                        .format(self.onnx_, inputs))
                forced = False
            except (RuntimeError, ValueError):
                # Let's try again by forcing output types.
                forced = True
                outputs = get_defined_outputs(self.outputs,
                                              self.onnx_node,
                                              inputs,
                                              variables,
                                              dtype=self.dtype)
                self.onnx_ = self.inst_.to_onnx(inputs,
                                                outputs=outputs,
                                                target_opset=target_opset,
                                                domain=domain)
                if "dim_value: 0" in str(self.onnx_):
                    raise RuntimeError(  # pragma: no cover
                        "Probable issue as one dimension is null.\n--\n{}".
                        format(self.onnx_)) from e

        if len(self.onnx_.graph.output) != len(self.outputs):
            # Something is wrong, falls back to default plan.
            forced = True
            outputs = get_defined_outputs(self.outputs,
                                          self.onnx_node,
                                          inputs,
                                          variables,
                                          dtype=self.dtype)
            self.onnx_ = self.inst_.to_onnx(inputs,
                                            outputs=outputs,
                                            target_opset=target_opset,
                                            domain=domain)
            if "dim_value: 0" in str(self.onnx_):
                raise RuntimeError(
                    "Probable issue as one dimension is null.\n--\n{}".format(
                        self.onnx_))
        else:
            lo = list(self.onnx_.graph.output)
            outputs = proto2vars(lo)

        sess_options = SessionOptions()
        self.run_options = RunOptions()

        try:
            sess_options.session_log_severity_level = 3
            # sess_options.sessions_log_verbosity_level = 0
        except AttributeError:
            # onnxruntime not recent enough.
            pass
        try:
            self.run_options.run_log_severity_level = 3
            # self.run_options.run_log_verbosity_level = 0
        except AttributeError:
            # onnxruntime not recent enough.
            pass
        if ir_version is not None:
            self.onnx_.ir_version = ir_version
        if disable_optimisation:
            sess_options.graph_optimization_level = (
                GraphOptimizationLevel.ORT_DISABLE_ALL)
        try:
            self.sess_ = InferenceSession(self.onnx_.SerializeToString(),
                                          sess_options=sess_options)
        except (RuntimeError, OrtNotImplemented, OrtInvalidGraph,
                OrtFail) as e:
            raise RuntimeError(
                "Unable to load node '{}' (output type was {})\n{}".format(
                    self.onnx_node.op_type,
                    "guessed" if forced else "inferred", self.onnx_)) from e
        self.typed_outputs_ = outputs

    def run(self, *args, **kwargs):
        """
        Should be overwritten.
        """
        inputs = {name: val for name, val in zip(self.inputs, args)}

        try:
            res = self.sess_.run(None, inputs, self.run_options)
        except (RuntimeError, OrtInvalidArgument) as e:  # pragma: no cover
            dtypes = {k: v.dtype for k, v in inputs.items()}
            shapes = {k: v.shape for k, v in inputs.items()}
            exp = [_.name for _ in self.sess_.get_inputs()]
            exp_types = [_.type for _ in self.sess_.get_inputs()]
            raise RuntimeError(
                "Predictions failed. List of inputs: {}, class={}"
                "\ndtypes={}\nshapes={}\nexpected={}\nexpected={}\n"
                "exception={}\n--ONNX--\n{}".format(list(sorted(inputs)),
                                                    self.alg_class, dtypes,
                                                    shapes, exp, exp_types, e,
                                                    self.onnx_)) from e
        return tuple(res)
 def infer_from_onnx(model_onnx, input_list):            
     sess = InferenceSession(model_onnx.SerializeToString())
     input_name = sess.get_inputs()[0].name
     pred_onx = sess.run(None, {input_name: numpy.array(input_list, numpy.float32)})
     return pred_onx
Ejemplo n.º 25
0
from sklearn.linear_model import LogisticRegression
from skl2onnx.common.data_types import FloatTensorType, Int64TensorType
from skl2onnx import to_onnx

iris = load_iris()
X, y = iris.data, iris.target
X = X.astype(numpy.float32)
X_train, X_test, y_train, y_test = train_test_split(X, y)

clr = LogisticRegression(solver="liblinear")
clr.fit(X_train, y_train)

onx = to_onnx(clr, X, options={'zipmap': False})

sess = InferenceSession(onx.SerializeToString())
input_names = [i.name for i in sess.get_inputs()]
output_names = [o.name for o in sess.get_outputs()]
print("inputs=%r, outputs=%r" % (input_names, output_names))
print(sess.run(None, {input_names[0]: X_test[:2]}))

####################################
# Changes the input names
# +++++++++++++++++++++++
#
# It is possible to change the input name by using the
# parameter *initial_types*. However, the user must specify the input
# types as well.

onx = to_onnx(clr,
              X,
              options={'zipmap': False},
Ejemplo n.º 26
0
class OnnxTransformer(BaseEstimator, TransformerMixin, OnnxOperatorMixin):
    """
    Calls :epkg:`onnxruntime` inference following :epkg:`scikit-learn` API
    so that it can be included in a :epkg:`scikit-learn` pipeline.

    Parameters
    ----------

    onnx_bytes : bytes
    output_name: string
        requested output name or None to request all and
        have method *transform* to store all of them in a dataframe
    enforce_float32 : boolean
        :epkg:`onnxruntime` only supports *float32*,
        :epkg:`scikit-learn` usually uses double floats, this parameter
        ensures that every array of double floats is converted into
        single floats
    """
    def __init__(self, onnx_bytes, output_name=None, enforce_float32=True):
        BaseEstimator.__init__(self)
        TransformerMixin.__init__(self)
        self.onnx_bytes = onnx_bytes
        self.output_name = output_name
        self.enforce_float32 = enforce_float32
        if not isinstance(onnx_bytes, bytes):
            raise TypeError("onnx_bytes must be bytes to be pickled.")

    def __repr__(self):
        """
        usual
        """
        ob = self.onnx_bytes
        if len(ob) > 20:
            ob = ob[:10] + b"..." + ob[-10:]
        return "{0}(onnx_bytes=b'{1}', output_name={2}, enforce_float32={3})".format(
            self.__class__.__name__, ob, self.output_name, enforce_float32)

    def fit(self, X=None, y=None, **fit_params):
        """
        Loads the :epkg:`ONNX` model.

        Parameters
        ----------
        X : unused
        y : unused

        Returns
        -------
        self
        """
        self.onnxrt_ = InferenceSession(self.onnx_bytes)
        self.inputs_ = [_.name for _ in self.onnxrt_.get_inputs()]
        return self

    def _check_arrays(self, inputs):
        """
        Ensures that double floats are converted into single floats
        if *enforce_float32* is True or raises an exception.
        """
        for k in inputs:
            v = inputs[k]
            if isinstance(v, numpy.ndarray):
                if v.dtype == numpy.float64:
                    if self.enforce_float32:
                        inputs[k] = v.astype(numpy.float32)
                    else:
                        raise TypeError(
                            "onnxunruntime only supports floats. Input '{0}' "
                            "should be converted.".format(k))

    def transform(self, X, y=None, **inputs):
        """
        Runs the predictions. If *X* is a dataframe,
        the function assumes every columns is a separate input,
        otherwise, *X* is considered as a first input and *inputs*
        can be used to specify extra inputs.

        Parameters
        ----------
        X : iterable, data to process (or first input if several expected)
        y : unused
        inputs: :epkg:`ONNX` graph support multiple inputs,
            each column of a dataframe is converted into as many inputs if
            *X* is a dataframe, otherwise, *X* is considered as the first input
            and *inputs* can be used to specify the other ones

        Returns
        -------
        :epkg:`DataFrame`
        """
        if not hasattr(self, "onnxrt_"):
            raise AttributeError(
                "Transform OnnxTransformer must be fit first.")
        rt_inputs = {}
        if isinstance(X, pandas.DataFrame):
            for c in X.columns:
                rt_inputs[c] = X[c]
        elif isinstance(X, numpy.ndarray):
            rt_inputs[self.inputs_[0]] = X
        elif isinstance(X, dict) and len(inputs) == 0:
            for k, v in X.items():
                rt_inputs[k] = v
        elif isinstance(X, list):
            if len(self.inputs_) == 1:
                rt_inputs[self.inputs_[0]] = numpy.array(X)
            else:
                for i in range(len(self.inputs_)):
                    rt_inputs[self.inputs_[i]] = [row[i] for row in X]

        for k, v in inputs.items():
            rt_inputs[k] = v

        names = [self.output_name] if self.output_name else None
        self._check_arrays(rt_inputs)
        outputs = self.onnxrt_.run(names, rt_inputs)

        if self.output_name or len(outputs) == 1:
            if isinstance(outputs[0], list):
                return pandas.DataFrame(outputs[0])
            else:
                return outputs[0]
        else:
            names = self.output_name if self.output_name else [
                o.name for o in self.onnxrt_.get_outputs()
            ]
            return pandas.DataFrame({k: v for k, v in zip(names, outputs)})

    def fit_transform(self, X, y=None, **inputs):
        """
        Loads the *ONNX* model and runs the predictions.

        Parameters
        ----------
        X : iterable, data to process (or first input if several expected)
        y : unused
        inputs: :epkg:`ONNX` graph support multiple inputs,
            each column of a dataframe is converted into as many inputs if
            *X* is a dataframe, otherwise, *X* is considered as the first input
            and *inputs* can be used to specify the other ones

        Returns
        -------
        :epkg:`DataFrame`
        """
        return self.fit(X, y=y, **inputs).transform(X, y)

    @staticmethod
    def enumerate_create(onnx_bytes, output_names=None, enforce_float32=True):
        """
        Creates multiple *OnnxTransformer*,
        one for each requested intermediate node.

        onnx_bytes : bytes
        output_names: string
            requested output names or None to request all and
            have method *transform* to store all of them in a dataframe
        enforce_float32 : boolean
            :epkg:`onnxruntime` only supports *float32*,
            :epkg:`scikit-learn` usually uses double floats, this parameter
            ensures that every array of double floats is converted into
            single floats
        :return: iterator on OnnxTransformer *('output name', OnnxTransformer)*
        """
        selected = None if output_names is None else set(output_names)
        model = load_onnx_model(onnx_bytes)
        for out in enumerate_model_node_outputs(model):
            m = select_model_inputs_outputs(model, out)
            if selected is None or out in selected:
                tr = OnnxTransformer(m.SerializeToString(),
                                     enforce_float32=enforce_float32)
                yield out, tr

    def onnx_parser(self, inputs=None):
        """
        Returns a parser for this model.
        """
        if inputs:
            self.parsed_inputs_ = inputs

        def parser():
            return [o.name for o in self.onnxrt_.get_outputs()]

        return parser

    def onnx_shape_calculator(self):
        def shape_calculator(operator):
            cout = self.onnxrt_.get_outputs()
            if len(operator.outputs) != len(cout):
                raise RuntimeError("Mismatched number of outputs: {} != {}."
                                   "".format(len(operator.outputs), len(cout)))
            for out in operator.outputs:
                shape = out.type.shape
                typ = guess_type(out.type)
                out.type = typ(shape=shape)

        return shape_calculator

    def onnx_converter(self):
        """
        Returns a converter for this model.
        If not overloaded, it fetches the converter
        mapped to the first *scikit-learn* parent
        it can find.
        """
        inputs = getattr(self, "parsed_inputs_", None)

        if inputs is None:
            inputs = []
            for inp in self.onnxrt_.get_inputs():
                shape = inp.type.shape
                typ = guess_type(inp.type)
                inputs.append((inp.name, typ(shape)))
        if outputs is None:
            outputs = [out.name for out in self.onnxrt_.get_outputs()]

        def copy_inout(inout):
            shape = [s.dim_value for s in inout.type.tensor_type.shape.dim]
            value_info = helper.make_tensor_value_info(
                clean_name(inout.name), inout.type.tensor_type.elem_type,
                shape)
            return value_info

        def clean_variable_name(name, scope):
            return scope.get_unique_variable_name(naame)

        def clean_operator_name(name, scope):
            return scope.get_unique_operator_name(naame)

        def clean_initializer_name(name, scope):
            return scope.get_unique_variable_name(naame)

        def converter(scope, operator, container):

            graph = model_onnx.graph
            inputs = [copy_inout(o) for o in graph.input]
            outputs = [copy_inout(o) for o in graph.output]
            for node in graph.node:
                n = helper.make_node(
                    node.op_type, [clean_variable_name(o) for o in node.input],
                    [clean_variable_name(o) for o in node.output])
                n.attribute.extend(node.attribute)  # pylint: disable=E1101
                container.nodes.append(n)

            inits = []
            for o in graph.initializer:
                tensor = TensorProto()
                tensor.data_type = o.data_type
                tensor.name = clean_initializer_name(o.name)
                tensor.raw_data = o.raw_data
                tensor.dims.extend(o.dims)  # pylint: disable=E1101
                container.initializers.append(tensor)

        return converter
    print(list(sorted(data["data"])))
    print(data["data"]['skl'])

##################################
# The input data is the following:

if good:
    print(data['data']['data'])

########################################
# Let's compare predictions.

if good:
    model_skl = data["data"]['skl']
    model_onnx = InferenceSession(data["data"]['ort_onnx'].SerializeToString())
    input_name = model_onnx.get_inputs()[0].name


def ort_predict_proba(sess, input, input_name):
    res = model_onnx.run(None, {input_name: input.astype(numpy.float32)})[1]
    return pandas.DataFrame(res).values


if good:
    pred_skl = [
        model_skl.predict_proba(input[0]) for input in data['data']['data']
    ]
    pred_onnx = [
        ort_predict_proba(model_onnx, input[0], input_name)
        for input in data['data']['data']
    ]
Ejemplo n.º 28
0
    else:
        print("'%s' already downloaded" % name)


model_name = "squeezenet1.1-7.onnx"
url_name = ("https://github.com/onnx/models/raw/master/vision/"
            "classification/squeezenet/model")
url_name += "/" + model_name
download_file(url_name, model_name, 100000)

################################################
# Loading the ONNX file and use it on one image.

sess = InferenceSession(model_name)

for inp in sess.get_inputs():
    print(inp)

#####################################
# The model expects a series of images of size
# `[3, 224, 224]`.

##########################################
# Classifying an image
# ++++++++++++++++++++

url = ("https://upload.wikimedia.org/wikipedia/commons/d/d2/"
       "East_Coker_elm%2C_2.jpg")
img = "East_Coker_elm.jpg"
download_file(url, img, 100000)
class DatasetsOrtBenchPerfTest(BenchPerfTest):
    def __init__(self, model, dataset, norm):
        BenchPerfTest.__init__(self)
        self.model_name = model
        self.dataset_name = dataset
        self.datas = common_datasets[dataset]
        skl_model = get_model(model)
        if norm:
            if 'NB' in model:
                self.model = make_pipeline(MinMaxScaler(), skl_model)
            else:
                self.model = make_pipeline(StandardScaler(), skl_model)
        else:
            self.model = skl_model
        self.model.fit(self.datas[0], self.datas[2])
        self.data_test = self.datas[1]

        if '-cdist' in model:
            options = {id(skl_model): {'optim': 'cdist'}}
        else:
            options = None
        self.onx = to_onnx(self.model,
                           self.datas[0].astype(numpy.float32),
                           options=options,
                           target_opset=__max_supported_opset__)
        self.onx.ir_version = get_ir_version(__max_supported_opset__)
        logger = getLogger("skl2onnx")
        logger.propagate = False
        logger.disabled = True
        self.ort = InferenceSession(self.onx.SerializeToString())
        self.oinf = OnnxInference(self.onx, runtime='python')
        self.oinfc = OnnxInference(self.onx, runtime='python_compiled')
        self.output_name = self.oinf.output_names[-1]
        self.input_name = self.ort.get_inputs()[0].name
        self.model_info = analyze_model(self.model)

    def fcts(self, **kwargs):
        def predict_ort(X, model=self.ort, namei=self.input_name):
            try:
                return model.run(None, {namei: X})[1]
            except Exception as e:
                return None

        def predict_skl(X, model=self.model):
            return model.predict(X)

        def predict_pyrt(X,
                         model=self.oinf,
                         namei=self.input_name,
                         nameo=self.output_name):
            return model.run({namei: X})[nameo]

        def predict_pyrtc(X,
                          model=self.oinfc,
                          namei=self.input_name,
                          nameo=self.output_name):
            return model.run({namei: X})[nameo]

        return [{
            'lib': 'ort',
            'fct': predict_ort
        }, {
            'lib': 'skl',
            'fct': predict_skl
        }, {
            'lib': 'pyrt',
            'fct': predict_pyrt
        }, {
            'lib': 'pyrtc',
            'fct': predict_pyrtc
        }]

    def data(self, N=10, dim=-1, **kwargs):  # pylint: disable=W0221
        if dim != -1:
            raise ValueError("dim must be -1 as it is fixed.")

        nbs = numpy.random.randint(0, self.data_test.shape[0] - 1, N)
        res = self.data_test[nbs, :].astype(numpy.float32)
        return (res, )

    def validate(self, results, **kwargs):
        nb = 5
        if len(results) != nb * 4:  # skl, ort, pyrt, pyrtc
            raise RuntimeError("Expected only 3 results not {0}.".format(
                len(results)))
        res = {}
        for idt, fct, vals in results:
            if idt not in res:
                res[idt] = {}
            if isinstance(vals, list):
                vals = pandas.DataFrame(vals).values
            lib = fct['lib']
            res[idt][lib] = vals

        if len(res) != nb:
            raise RuntimeError("Expected only 2 results not {0}.".format(
                len(results)))
        final = {}
        for diff_name in ['ort', 'pyrt', 'pyrtc']:
            diffs = []
            for i in range(0, nb):
                r = res[i]
                if diff_name not in r or r[diff_name] is None:
                    continue
                bas = numpy.squeeze(r['skl'])
                onn = numpy.squeeze(r[diff_name].squeeze())
                if bas.shape != onn.shape:
                    raise AssertionError(
                        "Shape mismatch {} != {} params={}".format(
                            bas.shape, onn.shape, results[0][0]))
                diff = numpy.max(numpy.abs(onn - bas))
                diffs.append(diff)
            if len(diffs) > 0:
                final.update({
                    'diff_%s' % diff_name: sum(diffs) / nb,
                    'upper_diff_%s' % diff_name: max(diffs),
                    'lower_diff_%s' % diff_name: min(diffs)
                })
        for k, v in self.model_info.items():
            final['fit_' + k] = v
        return final
Ejemplo n.º 30
0
class InferenceSession:  # pylint: disable=E0102
    """
    Wrappers around InferenceSession from :epkg:`onnxruntime`.

    :param onnx_bytes: onnx bytes
    :param session_options: session options
    :param log_severity_level: change the logging level
    :param device: device, a string `cpu`, `cuda`, `cuda:0`...
    """
    def __init__(self,
                 onnx_bytes,
                 sess_options=None,
                 log_severity_level=4,
                 device=None):
        if InferenceSession is None:
            raise ImportError(  # pragma: no cover
                "onnxruntime is not available.")
        self.log_severity_level = log_severity_level
        if device is None:
            self.device = get_ort_device('cpu')
        else:
            self.device = get_ort_device(device)
        self.providers = device_to_providers(self.device)
        set_default_logger_severity(3)
        if sess_options is None:
            self.so = SessionOptions()
            self.so.log_severity_level = log_severity_level
            self.sess = OrtInferenceSession(onnx_bytes,
                                            sess_options=self.so,
                                            providers=self.providers)
        else:
            self.so = sess_options
            self.sess = OrtInferenceSession(onnx_bytes,
                                            sess_options=sess_options,
                                            providers=self.providers)
        self.ro = RunOptions()
        self.ro.log_severity_level = log_severity_level
        self.ro.log_verbosity_level = log_severity_level
        self.output_names = [o.name for o in self.get_outputs()]

    def run(self, output_names, input_feed, run_options=None):
        """
        Executes the ONNX graph.

        :param output_names: None for all, a name for a specific output
        :param input_feed: dictionary of inputs
        :param run_options: None or RunOptions
        :return: array
        """
        if any(map(lambda v: isinstance(v, C_OrtValue), input_feed.values())):
            return self.sess._sess.run_with_ort_values(input_feed,
                                                       self.output_names,
                                                       run_options or self.ro)
        return self.sess.run(output_names, input_feed, run_options or self.ro)

    def get_inputs(self):
        "Returns input types."
        return self.sess.get_inputs()

    def get_outputs(self):
        "Returns output types."
        return self.sess.get_outputs()

    def end_profiling(self):
        "Ends profiling."
        return self.sess.end_profiling()