def test_proto_type(self): dtypes = [ (np.int32, Int32TensorType, onnx_proto.TensorProto.INT32), (np.int64, Int64TensorType, onnx_proto.TensorProto.INT64), (np.float32, FloatTensorType, onnx_proto.TensorProto.FLOAT), (np.float64, DoubleTensorType, onnx_proto.TensorProto.DOUBLE), (np.str_, StringTensorType, onnx_proto.TensorProto.STRING), (np.bool_, BooleanTensorType, onnx_proto.TensorProto.BOOL), (np.int8, Int8TensorType, onnx_proto.TensorProto.INT8), (np.uint8, UInt8TensorType, onnx_proto.TensorProto.UINT8) ] if Complex64TensorType is not None: dtypes.append((np.complex64, Complex64TensorType, onnx_proto.TensorProto.COMPLEX64)) if Complex128TensorType is not None: dtypes.append((np.complex128, Complex128TensorType, onnx_proto.TensorProto.COMPLEX128)) for dtype, exp, pt in dtypes: nt2 = guess_proto_type(exp([None, 1])) self.assertEqual(nt2, pt) nt1 = _guess_type_proto(pt, [None, 1]) self.assertEqual(nt1.__class__, exp)
def decorrelate_transformer_converter(scope, operator, container): op = operator.raw_operator 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. proto_dtype = guess_proto_type(X.type) mean_name = scope.get_unique_variable_name('mean') container.add_initializer(mean_name, proto_dtype, op.mean_.shape, list(op.mean_.ravel())) coef_name = scope.get_unique_variable_name('coef') container.add_initializer(coef_name, proto_dtype, op.coef_.shape, list(op.coef_.ravel())) op_name = scope.get_unique_operator_name('sub') sub_name = scope.get_unique_variable_name('sub') # This function is defined in package onnxconverter_common. # Most common operators can be added to the graph with # these functions. It handles the case when specifications # changed accross opsets (a parameter becomes an input # for example). apply_sub(scope, [X.full_name, mean_name], sub_name, container, operator_name=op_name) op_name = scope.get_unique_operator_name('matmul') container.add_node('MatMul', [sub_name, coef_name], out[0].full_name, name=op_name)
def converter_lightgbm_concat(scope, operator, container): """ Converter for operator *LightGBMConcat*. """ op = operator.raw_operator options = container.get_options(op, dict(cast=False)) proto_dtype = guess_proto_type(operator.inputs[0].type) if proto_dtype != onnx_proto.TensorProto.DOUBLE: # pylint: disable=E1101 proto_dtype = onnx_proto.TensorProto.FLOAT # pylint: disable=E1101 if options['cast']: concat_name = scope.get_unique_variable_name('cast_lgbm') apply_cast(scope, concat_name, operator.outputs[0].full_name, container, operator_name=scope.get_unique_operator_name('cast_lgmb'), to=proto_dtype) else: concat_name = operator.outputs[0].full_name apply_concat(scope, [_.full_name for _ in operator.inputs], concat_name, container, axis=1)
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)