def test_softmax(self): X = np.random.randn(100, 4).astype(np.float32) y = X.sum(axis=1) + np.random.randn(100) / 10 y = y.astype(np.float32) self.assertEqual(y.shape, (100, )) weight = np.random.randn(4, 1).astype(np.float32) intercept = np.random.randn(1).astype(np.float32) node = OnnxAdd(OnnxMatMul('X', weight, op_version=TARGET_OPSET), intercept, op_version=TARGET_OPSET) nn_onnx = node.to_onnx({'X': X}, target_opset=TARGET_OPSET) with open("debug_ort_add.onnx", "wb") as f: f.write(nn_onnx.SerializeToString()) self.assertEqual(len(nn_onnx.graph.output), 1) node = OnnxMatMul('X', weight, op_version=TARGET_OPSET) nn_onnx = node.to_onnx({'X': X}, target_opset=TARGET_OPSET) self.assertEqual(len(nn_onnx.graph.output), 1) node = OnnxSoftmax(OnnxAdd(OnnxMatMul('X', weight, op_version=TARGET_OPSET), intercept, op_version=TARGET_OPSET), op_version=TARGET_OPSET) nn_onnx = node.to_onnx({'X': X}, target_opset=TARGET_OPSET) self.assertEqual(len(nn_onnx.graph.output), 1)
def test_onnx_micro_runtime_matmul(self): opset = TestOnnxMicroRuntime.opset x = numpy.array([1, 2, 4, 5]).astype(numpy.float32).reshape((2, 2)) cop = OnnxMatMul('X', 'X', op_version=opset, output_names=['Y']) model_def = cop.to_onnx({'X': x}, target_opset=opset) rt = OnnxMicroRuntime(model_def) out = rt.run({'X': x}) self.assertEqual(numpy.matmul(x, x), out['Y'])
def decorrelate_transformer_converter(scope, operator, container): op = operator.raw_operator opv = container.target_opset out = operator.outputs X = operator.inputs[0] dtype = guess_numpy_type(X.type) options = container.get_options(op, dict(use_gemm=False)) use_gemm = options['use_gemm'] print('conversion: use_gemm=', use_gemm) if use_gemm: Y = OnnxGemm(X, op.coef_.astype(dtype), (-op.mean_ @ op.coef_).astype(dtype), op_version=opv, alpha=1., beta=1., output_names=out[:1]) else: Y = OnnxMatMul(OnnxSub(X, op.mean_.astype(dtype), op_version=opv), op.coef_.astype(dtype), op_version=opv, output_names=out[:1]) Y.add_to(scope, container)
def _onnx_linear_regression(target_opset=None, dtype=numpy.float32): """ Returns the ONNX graph for function :math:`Y = f(X, A, B) = A X + B`. .. gdot:: :script: DOT-SECTION from mlprodict.onnxrt import OnnxInference from onnxcustom.utils.onnx_function import function_onnx_graph model_onnx = function_onnx_graph('linear_regression') oinf = OnnxInference(model_onnx, inplace=False) print("DOT-SECTION", oinf.to_dot()) """ from skl2onnx.algebra.onnx_ops import (OnnxMatMul, OnnxAdd) res = OnnxAdd(OnnxMatMul('X', 'A', op_version=target_opset), 'B', op_version=target_opset, output_names=['Y']) var_type = dtype_to_var_type(dtype) varsx = [('X', var_type([None, None])), ('A', var_type([None, None])), ('B', var_type([None, None]))] onx = res.to_onnx(varsx, outputs=[('Y', var_type())], target_opset=target_opset, other_outputs=[res]) return onx
def test_onnx_rename_weights(self): N, D_in, D_out, H = 3, 3, 3, 3 var = [('X', FloatTensorType([N, D_in]))] w1 = numpy.random.randn(D_in, H).astype(numpy.float32) w2 = numpy.random.randn(H, D_out).astype(numpy.float32) opv = 14 onx_alg = OnnxMatMul( OnnxRelu(OnnxMatMul(*var, w1, op_version=opv), op_version=opv), w2, op_version=opv, output_names=['Y']) onx = onx_alg.to_onnx( var, target_opset=opv, outputs=[('Y', FloatTensorType())]) onx = onnx_rename_weights(onx) names = [init.name for init in onx.graph.initializer] self.assertEqual(['I0_Ma_MatMulcst', 'I1_Ma_MatMulcst1'], names) self.assertEqual(get_onnx_opset(onx), 14) self.assertRaise(lambda: get_onnx_opset(onx, "H"), ValueError)
def decorrelate_transformer_converter2(scope, operator, container): op = operator.raw_operator opv = container.target_opset out = operator.outputs X = operator.inputs[0] dtype = guess_numpy_type(X.type) m = OnnxMatMul(OnnxSub(X, op.pca_.mean_.astype(dtype), op_version=opv), op.pca_.components_.T.astype(dtype), op_version=opv) Y = OnnxIdentity(m, op_version=opv, output_names=out[:1]) Y.add_to(scope, container)
def custom_linear_regressor_converter(scope, operator, container): op = operator.raw_operator opv = container.target_opset out = operator.outputs X = operator.inputs[0] dtype = guess_numpy_type(X.type) m = OnnxAdd(OnnxMatMul(X, op.coef_.astype(dtype), op_version=opv), numpy.array([op.intercept_]), op_version=opv) Y = OnnxIdentity(m, op_version=opv, output_names=out[:1]) Y.add_to(scope, container)
def custom_cluster_converter(scope, operator, container): op = operator.raw_operator opv = container.target_opset out = operator.outputs X = operator.inputs[0] dtype = guess_numpy_type(X.type) dist = OnnxMatMul(X, op.clusters_.astype(dtype), op_version=opv) label = OnnxArgMax(dist, axis=1, op_version=opv) Yl = OnnxIdentity(label, op_version=opv, output_names=out[:1]) Yp = OnnxIdentity(dist, op_version=opv, output_names=out[1:]) Yl.add_to(scope, container) Yp.add_to(scope, container)
def decorrelate_transformer_converter(scope, operator, container): op = operator.raw_operator opv = container.target_opset out = operator.outputs # We retrieve the unique input. X = operator.inputs[0] # In most case, computation happen in floats. # But it might be with double. ONNX is very strict # about types, every constant should have the same # type as the input. dtype = guess_numpy_type(X.type) # We tell in ONNX language how to compute the unique output. # op_version=opv tells which opset is requested Y = OnnxMatMul(OnnxSub(X, op.mean_.astype(dtype), op_version=opv), op.coef_.astype(dtype), op_version=opv, output_names=out[:1]) Y.add_to(scope, container)
def decorrelate_transformer_converter(scope, operator, container): op = operator.raw_operator opv = container.target_opset out = operator.outputs X = operator.inputs[0] dtype = guess_numpy_type(X.type) Y1 = OnnxMatMul( OnnxSub(X, op.mean_.astype(dtype), op_version=opv), op.coef_.astype(dtype), op_version=opv, output_names=out[:1]) Y2 = OnnxGemm(X, op.coef_.astype(dtype), (- op.mean_ @ op.coef_).astype(dtype), op_version=opv, alpha=1., beta=1., output_names=out[1:2]) Y1.add_to(scope, container) Y2.add_to(scope, container)
def test_algebra_to_onnx(self): X = numpy.random.randn(5, 4) beta = numpy.array([1, 2, 3, 4]) / 10 beta32 = beta.astype(numpy.float32) onnxExpM = OnnxExp(OnnxMatMul('X', beta32)) cst = numpy.ones((1, 3), dtype=numpy.float32) onnxExpM1 = OnnxAdd(onnxExpM, cst) onnxPred = OnnxDiv(onnxExpM, onnxExpM1) inputs = {'X': X[:1].astype(numpy.float32)} model_onnx = onnxPred.to_onnx(inputs) s1 = str(model_onnx) model_onnx = onnxPred.to_onnx(inputs) s2 = str(model_onnx) assert s1 == s2
def test_algebra_converter(self): coef = numpy.array([[1, 2], [3, 4]], dtype=numpy.float64) intercept = 1 X_test = numpy.array([[1, -2], [3, -4]], dtype=numpy.float64) onnx_fct = OnnxSub(OnnxMatMul('X', coef), numpy.array([intercept], dtype=numpy.float64), output_names=['Y']) onnx_model = onnx_fct.to_onnx({'X': X_test}, dtype=numpy.float64) sess = InferenceSession(onnx_model.SerializeToString()) ort_pred = sess.run(None, {'X': X_test})[0] assert_almost_equal(ort_pred, numpy.array([[-6., -7.], [-10., -11.]]))
def custom_linear_classifier_converter(scope, operator, container): op = operator.raw_operator opv = container.target_opset out = operator.outputs X = operator.inputs[0] dtype = guess_numpy_type(X.type) raw = OnnxAdd(OnnxMatMul(X, op.coef_.astype(dtype), op_version=opv), op.intercept_.astype(dtype), op_version=opv) prob = OnnxSigmoid(raw, op_version=opv) label = OnnxArgMax(prob, axis=1, op_version=opv) Yl = OnnxIdentity(label, op_version=opv, output_names=out[:1]) Yp = OnnxIdentity(prob, op_version=opv, output_names=out[1:]) Yl.add_to(scope, container) Yp.add_to(scope, container)
def test_bug_add(self): coef = numpy.array([-8.43436238e-02, 5.47765517e-02, 6.77578341e-02, 1.56675273e+00, -1.45737317e+01, 3.78662574e+00 - 6.52943746e-03 - 1.39463522e+00, 2.89157796e-01 - 1.53753213e-02 - 9.88045749e-01, 1.00224585e-02, -4.96820220e-01], dtype=numpy.float64) intercept = 35.672858515632 X_test = (coef + 1.).reshape((1, coef.shape[0])) onnx_fct = OnnxAdd( OnnxMatMul('X', coef.astype(numpy.float64), op_version=TARGET_OPSET), numpy.array([intercept]), output_names=['Y'], op_version=TARGET_OPSET) onnx_model64 = onnx_fct.to_onnx({'X': X_test.astype(numpy.float64)}) oinf = OnnxInference(onnx_model64) ort_pred = oinf.run({'X': X_test.astype(numpy.float64)})['Y'] self.assertEqualArray(ort_pred, numpy.array([245.19907295849504]))
def test_algebra_to_onnx(self): X = numpy.random.randn(5, 4) beta = numpy.array([1, 2, 3, 4]) / 10 beta32 = beta.astype(numpy.float32) onnxExpM = OnnxExp(OnnxMatMul('X', beta32)) cst = numpy.ones((1, 3), dtype=numpy.float32) onnxExpM1 = OnnxAdd(onnxExpM, cst) onnxPred = OnnxDiv(onnxExpM, onnxExpM1) inputs = {'X': X[:1].astype(numpy.float32)} model_onnx = onnxPred.to_onnx(inputs) s1 = str(model_onnx) model_onnx = onnxPred.to_onnx(inputs) s2 = str(model_onnx) assert s1 == s2 nin = list(onnxExpM1.enumerate_initial_types()) nno = list(onnxExpM1.enumerate_nodes()) nva = list(onnxExpM1.enumerate_variables()) self.assertEqual(len(nin), 0) self.assertEqual(len(nno), 3) self.assertEqual(len(nva), 0)
def model_convert(onnx_filename): 'make the model' if "cifar" in onnx_filename: mean_list = [0.4914, 0.4822, 0.4465] sigma_list = [0.2023, 0.1994, 0.2010] else: assert 'mnist' in onnx_filename mean_list = [0.1307] sigma_list = [0.3081] onnx_model = onnx.load(onnx_filename + ".onnx") onnx_model = remove_unused_initializers(onnx_model) onnx_input = onnx_model.graph.input[0].name print(f"onnx input: {onnx_input}") inp = onnx_model.graph.input[0] image_shape = tuple( d.dim_value if d.dim_value != 0 else 1 for d in inp.type.tensor_type.shape.dim) # single input print(f"using input shape: {image_shape}") print( f"orig input shape: {tuple(d.dim_value for d in inp.type.tensor_type.shape.dim)}" ) image_cols = image_shape[-1] image_rows = image_shape[-2] b_mats = [] c_mats = [] for mean, sigma in zip(mean_list, sigma_list): b_mats.append(np.identity(image_cols, dtype=np.float32) / sigma) c_mats.append( np.ones((image_rows, image_cols), dtype=np.float32) * (-mean)) c_mats = np.array(c_mats) while len(c_mats.shape) != len(image_shape): c_mats = np.expand_dims(c_mats, axis=0) print(f"c_mats shape: {c_mats.shape}") b_mats = np.array(b_mats) while len(b_mats.shape) != len(image_shape): b_mats = np.expand_dims(b_mats, axis=0) print(f"b_mats shape: {b_mats.shape}") input_name = 'unscaled_input' add_node = OnnxAdd(input_name, c_mats) matmul_node = OnnxMatMul(add_node, b_mats, output_names=[onnx_input]) i = onnx.helper.make_tensor_value_info('i', TensorProto.FLOAT, image_shape) # test matmul model matmul_model = matmul_node.to_onnx({input_name: i}) onnx.checker.check_model(matmul_model) zero_in = np.zeros(image_shape, dtype=np.float32) o = predict_with_onnxruntime(matmul_model, zero_in) olist = [o[0, 0, 5, 7], o[0, 1, 5, 7], o[0, 2, 5, 7]] print(f"output mat_mul zeros: {olist}") zero_val_list = [(0 - mean) / sigma for mean, sigma in zip(mean_list, sigma_list)] print(f"expected zero vals = {zero_val_list}") assert np.allclose(olist, zero_val_list) one_in = np.ones(image_shape, dtype=np.float32) o = predict_with_onnxruntime(matmul_model, one_in) olist = [o[0, 0, 5, 7], o[0, 1, 5, 7], o[0, 2, 5, 7]] print(f"output mat_mul zeros: {olist}") one_val_list = [(1 - mean) / sigma for mean, sigma in zip(mean_list, sigma_list)] print(f"expected one vals = {one_val_list}") assert np.allclose(olist, one_val_list) print("zero and one vals matches with prefix network!") # only shapes are used model2_def = matmul_node.to_onnx({input_name: i}) #print('The model is:\n{}'.format(model2_def)) onnx.checker.check_model(model2_def) print('The models are checked!') combined = glue_models(model2_def, onnx_model) converted_filename = f"{onnx_filename}_noscale.onnx" onnx.save_model(combined, converted_filename) print(f"Saved unscaled model to: {converted_filename}\n")
def live_decorrelate_transformer_converter(scope, operator, container): # shortcuts op = operator.raw_operator opv = container.target_opset out = operator.outputs # We retrieve the unique input. X = operator.inputs[0] # We guess its type. If the operator ingests float (or double), # it outputs float (or double). proto_dtype = guess_proto_type(X.type) dtype = guess_numpy_type(X.type) # Lines in comment specify the numpy computation # the ONNX code implements. # mean_ = numpy.mean(X, axis=0, keepdims=True) mean = OnnxReduceMean(X, axes=[0], keepdims=1, op_version=opv) # This is trick I often use. The converter automatically # chooses a name for every output. In big graph, # it is difficult to know which operator is producing which output. # This line just tells every node must prefix its ouputs with this string. # It also applies to all inputs nodes unless this method # was called for one of these nodes. mean.set_onnx_name_prefix('mean') # X2 = X - mean_ X2 = OnnxSub(X, mean, op_version=opv) # V = X2.T @ X2 / X2.shape[0] N = OnnxGatherElements(OnnxShape(X, op_version=opv), numpy.array([0], dtype=numpy.int64), op_version=opv) Nf = OnnxCast(N, to=proto_dtype, op_version=opv) # Every output involved in N and Nf is prefixed by 'N'. Nf.set_onnx_name_prefix('N') V = OnnxDiv(OnnxMatMul(OnnxTranspose(X2, op_version=opv), X2, op_version=opv), Nf, op_version=opv) V.set_onnx_name_prefix('V1') # V += numpy.identity(V.shape[0]) * self.alpha V = OnnxAdd(V, op.alpha * numpy.identity(op.nf_, dtype=dtype), op_version=opv) V.set_onnx_name_prefix('V2') # L, P = numpy.linalg.eig(V) LP = OnnxEig(V, eigv=True, op_version=opv) LP.set_onnx_name_prefix('LP') # Linv = L ** (-0.5) # Notation LP[0] means OnnxPow is taking the first output # of operator OnnxEig, LP[1] would mean the second one # LP is not allowed as it is ambiguous Linv = OnnxPow(LP[0], numpy.array([-0.5], dtype=dtype), op_version=opv) Linv.set_onnx_name_prefix('Linv') # diag = numpy.diag(Linv) diag = OnnxMul(OnnxEyeLike(numpy.zeros((op.nf_, op.nf_), dtype=numpy.int64), k=0, op_version=opv), Linv, op_version=opv) diag.set_onnx_name_prefix('diag') # root = P @ diag @ P.transpose() trv = OnnxTranspose(LP[1], op_version=opv) coef_left = OnnxMatMul(LP[1], diag, op_version=opv) coef_left.set_onnx_name_prefix('coef_left') coef = OnnxMatMul(coef_left, trv, op_version=opv) coef.set_onnx_name_prefix('coef') # Same part as before. Y = OnnxMatMul(X2, coef, op_version=opv, output_names=out[:1]) Y.set_onnx_name_prefix('Y') # The last line specifies the final output. # Every node involved in the computation is added to the ONNX # graph at this stage. Y.add_to(scope, container)
def live_decorrelate_transformer_converter(scope, operator, container): op = operator.raw_operator opv = container.target_opset out = operator.outputs # We retrieve the unique input. X = operator.inputs[0] proto_dtype = guess_proto_type(X.type) dtype = guess_numpy_type(X.type) # new part # mean_ = numpy.mean(X, axis=0, keepdims=True) mean = OnnxReduceMean(X, axes=[0], keepdims=1, op_version=opv) mean.set_onnx_name_prefix('mean') # X2 = X - mean_ X2 = OnnxSub(X, mean, op_version=opv) # V = X2.T @ X2 / X2.shape[0] N = OnnxGatherElements(OnnxShape(X, op_version=opv), numpy.array([0], dtype=numpy.int64), op_version=opv) Nf = OnnxCast(N, to=proto_dtype, op_version=opv) Nf.set_onnx_name_prefix('N') V = OnnxDiv(OnnxMatMul(OnnxTranspose(X2, op_version=opv), X2, op_version=opv), Nf, op_version=opv) V.set_onnx_name_prefix('V1') # V += numpy.identity(V.shape[0]) * self.alpha V = OnnxAdd(V, op.alpha * numpy.identity(op.nf_, dtype=dtype), op_version=opv) V.set_onnx_name_prefix('V2') # L, P = numpy.linalg.eig(V) LP = OnnxEig(V, eigv=True, op_version=opv) LP.set_onnx_name_prefix('LP') # Linv = L ** (-0.5) Linv = OnnxPow(LP[0], numpy.array([-0.5], dtype=dtype), op_version=opv) Linv.set_onnx_name_prefix('Linv') # diag = numpy.diag(Linv) diag = OnnxMul(OnnxEyeLike(numpy.array([op.nf_, op.nf_], dtype=numpy.int64), k=0, op_version=opv), Linv, op_version=opv) diag.set_onnx_name_prefix('diag') # root = P @ diag @ P.transpose() trv = OnnxTranspose(LP[1], op_version=opv) coef_left = OnnxMatMul(LP[1], diag, op_version=opv) coef_left.set_onnx_name_prefix('coef_left') coef = OnnxMatMul(coef_left, trv, op_version=opv) coef.set_onnx_name_prefix('coef') # Same part as before. Y = OnnxMatMul(X2, coef, op_version=opv, output_names=out[:1]) Y.set_onnx_name_prefix('Y') Y.add_to(scope, container)