sklearn-onnx
sklearn-onnx copied to clipboard
Custom converter has inputs and outputs have variables in common.
I tried to get around the issue outlined in #710 with the following solution.
def rvc_converter(scope: Scope, operator: Operator,
container: ModelComponentContainer):
from .rvc import RVC
rvc: RVC = operator.raw_operator
input: Variable = operator.inputs[0]
op_outputs: List[Variable] = operator.outputs
op_version: Union[int, None] = container.target_opset
y_list: List[OnnxOperator] = [
OnnxReshape(OnnxSubEstimator(bsvc, input, op_version=op_version),
np.array([-1, 1], dtype=np.int64),
op_version=op_version) for bsvc in rvc.binary_rvc_list_
]
y_matrix: OnnxOperator = OnnxConcat(*y_list, axis=1, op_version=op_version)
# y_matrix: OnnxOperator = OnnxConcatFromSequence(*y_list,
# axis=1,
# new_axis=1,
# op_version=op_version)
probs: OnnxOperator = OnnxSoftmax(y_matrix,
axis=1,
op_version=op_version,
output_names=[op_outputs[1]])
probs.add_to(scope=scope, container=container)
labels: OnnxOperator = OnnxArgMax(probs,
axis=1,
keepdims=0,
op_version=op_version,
output_names=[op_outputs[0]])
labels.add_to(scope=scope, container=container)
However, I get the following error:
RuntimeError: inputs and outputs cannot have variables in common {'y'} in node 'Identity' with name 'SubOpId'.
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
~/Documents/research/sklearn_plugins/test/sklearn_plugins/rvm/test_rvc_export.py in
34 """
35 onx: ModelProto
----> 36 onx = to_onnx(rvc, X_train[:1, :].astype(np.float64), target_opset=13)
~/anaconda3/envs/sklearn_plugins/lib/python3.8/site-packages/skl2onnx/convert.py in to_onnx(model, X, name, initial_types, target_opset, options, white_op, black_op, final_types, dtype, verbose)
214 name = "ONNX(%s)" % model.__class__.__name__
215 initial_types = guess_initial_types(X, initial_types)
--> 216 return convert_sklearn(model, initial_types=initial_types,
217 target_opset=target_opset,
218 name=name, options=options,
~/anaconda3/envs/sklearn_plugins/lib/python3.8/site-packages/skl2onnx/convert.py in convert_sklearn(model, name, initial_types, doc_string, target_opset, custom_conversion_functions, custom_shape_calculators, custom_parsers, options, intermediate, white_op, black_op, final_types, dtype, verbose)
160 if verbose >= 1:
161 print("[convert_sklearn] convert_topology")
--> 162 onnx_model = convert_topology(topology, name, doc_string, target_opset,
163 options=options,
164 remove_identity=not intermediate,
~/anaconda3/envs/sklearn_plugins/lib/python3.8/site-packages/skl2onnx/common/_topology.py in convert_topology(topology, model_name, doc_string, target_opset, channel_first_inputs, options, remove_identity, verbose)
1200 type(getattr(operator, 'raw_model', None))))
1201 container.validate_options(operator)
-> 1202 conv(scope, operator, container)
1203
1204 container.ensure_topological_order()
~/anaconda3/envs/sklearn_plugins/lib/python3.8/site-packages/skl2onnx/common/_registration.py in __call__(self, *args)
24 if args[1].raw_operator is not None:
25 args[2]._get_allowed_options(args[1].raw_operator)
---> 26 return self._fct(*args)
27
28 def get_allowed_options(self):
~/Documents/research/sklearn_plugins/src/sklearn_plugins/rvm/_onnx_transfrom.py in rvc_converter(scope, operator, container)
125 op_version=op_version,
126 output_names=[op_outputs[1]])
--> 127 probs.add_to(scope=scope, container=container)
128 labels: OnnxOperator = OnnxArgMax(probs,
129 axis=1,
~/anaconda3/envs/sklearn_plugins/lib/python3.8/site-packages/skl2onnx/algebra/onnx_operator.py in add_to(self, scope, container, operator, run_converters)
547 output_range=self.output_range,
548 operator=operator, run_converters=run_converters, **kwargs)
--> 549 self.state.run()
550 self._verify_add_to_()
551
~/anaconda3/envs/sklearn_plugins/lib/python3.8/site-packages/skl2onnx/algebra/graph_state.py in run(self)
412 inputs = []
413 for i in self.inputs:
--> 414 v = self._get_var_name(i, False, index=None)
415 inputs.extend(v)
416
~/anaconda3/envs/sklearn_plugins/lib/python3.8/site-packages/skl2onnx/algebra/graph_state.py in _get_var_name(self, var, in_out, operator, index)
122 "input: True for output, False for input"
123 if hasattr(var, 'add_to'):
--> 124 var.add_to(self.scope, self.container, operator=operator,
125 run_converters=self.run_converters)
126 outputs = var.outputs
~/anaconda3/envs/sklearn_plugins/lib/python3.8/site-packages/skl2onnx/algebra/onnx_operator.py in add_to(self, scope, container, operator, run_converters)
547 output_range=self.output_range,
548 operator=operator, run_converters=run_converters, **kwargs)
--> 549 self.state.run()
550 self._verify_add_to_()
551
~/anaconda3/envs/sklearn_plugins/lib/python3.8/site-packages/skl2onnx/algebra/graph_state.py in run(self)
412 inputs = []
413 for i in self.inputs:
--> 414 v = self._get_var_name(i, False, index=None)
415 inputs.extend(v)
416
~/anaconda3/envs/sklearn_plugins/lib/python3.8/site-packages/skl2onnx/algebra/graph_state.py in _get_var_name(self, var, in_out, operator, index)
122 "input: True for output, False for input"
123 if hasattr(var, 'add_to'):
--> 124 var.add_to(self.scope, self.container, operator=operator,
125 run_converters=self.run_converters)
126 outputs = var.outputs
~/anaconda3/envs/sklearn_plugins/lib/python3.8/site-packages/skl2onnx/algebra/onnx_operator.py in add_to(self, scope, container, operator, run_converters)
547 output_range=self.output_range,
548 operator=operator, run_converters=run_converters, **kwargs)
--> 549 self.state.run()
550 self._verify_add_to_()
551
~/anaconda3/envs/sklearn_plugins/lib/python3.8/site-packages/skl2onnx/algebra/graph_state.py in run(self)
412 inputs = []
413 for i in self.inputs:
--> 414 v = self._get_var_name(i, False, index=None)
415 inputs.extend(v)
416
~/anaconda3/envs/sklearn_plugins/lib/python3.8/site-packages/skl2onnx/algebra/graph_state.py in _get_var_name(self, var, in_out, operator, index)
122 "input: True for output, False for input"
123 if hasattr(var, 'add_to'):
--> 124 var.add_to(self.scope, self.container, operator=operator,
125 run_converters=self.run_converters)
126 outputs = var.outputs
~/anaconda3/envs/sklearn_plugins/lib/python3.8/site-packages/skl2onnx/algebra/onnx_operator.py in add_to(self, scope, container, operator, run_converters)
949 op_domain=None, onnx_prefix_name=self.onnx_prefix,
950 options=self.options, run_converters=run_converters, **kwargs)
--> 951 self.state.run()
952
953
~/anaconda3/envs/sklearn_plugins/lib/python3.8/site-packages/skl2onnx/algebra/graph_state.py in run(self)
477 for i, out in enumerate(sub_op.outputs):
478 var = outputs[i]
--> 479 self.container.add_node(
480 'Identity', [out.onnx_name], [var[0]],
481 name=self.scope.get_unique_operator_name("SubOpId"))
~/anaconda3/envs/sklearn_plugins/lib/python3.8/site-packages/skl2onnx/common/_container.py in add_node(self, op_type, inputs, outputs, op_domain, op_version, name, **attrs)
512 "" % (inputs, outputs, op_type)) from e
513 if common:
--> 514 raise RuntimeError(
515 "inputs and outputs cannot have "
516 "variables in common {} in node '{}' "
RuntimeError: inputs and outputs cannot have variables in common {'y'} in node 'Identity' with name 'SubOpId'.
I believe it is the below 2 lines that cause the problem as each _BinaryRVC produces an output variable y
. It is necessary for me to combine multiple _BinaryRVC
instances to produce the output of RVC
classifier.
y_list: List[OnnxOperator] = [
OnnxReshape(OnnxSubEstimator(bsvc, input, op_version=op_version),
np.array([-1, 1], dtype=np.int64),
op_version=op_version) for bsvc in rvc.binary_rvc_list_
]
y_matrix: OnnxOperator = OnnxConcat(*y_list, axis=1, op_version=op_version)
I dig through the documentation, but I couldn't find a way to get around this issue. Could anyone help me with this?
This is a bug. I'll propose a fix soon.
I just merged the PR (#714) which should fix that issue. Would it be possible to check if it is working with development version?
I still encountered the same error even after using the 1.9.3.dev. The RVC uses multiple RVRs.
The following is the shape calculator for RVR. I specified the output of the RVR to have name "y"
on the last line of the function.
def rvr_shape_calculator(operator: Operator):
check_input_and_output_types(
operator,
good_input_types=[FloatTensorType, DoubleTensorType],
good_output_types=[FloatTensorType, DoubleTensorType])
op_inputs: List[Variable] = operator.inputs
if len(op_inputs) != 1:
raise RuntimeError("Only one input matrix is allowed for RVR.")
op_outputs: List[Variable] = operator.outputs
if len(op_outputs) != 1:
raise RuntimeError("Only one output is allowed for RVR.")
# retrieve rvr inputs dtype
input_var_type: DataType = op_inputs[0].type
# confirm rvr input and output shape
n_samples: int = input_var_type.shape[0]
op_outputs[0].type.shape = [n_samples]
op_outputs[0].set_onnx_name("y")
The code for the converter of RVC looks like the following.
def rvc_converter(scope: Scope, operator: Operator,
container: ModelComponentContainer):
from .rvc import RVC
rvc: RVC = operator.raw_operator
input: Variable = operator.inputs[0]
op_outputs: List[Variable] = operator.outputs
op_version: Union[int, None] = container.target_opset
y_list: List[OnnxOperator] = [
OnnxReshape(OnnxSubEstimator(bsvc, input, op_version=op_version),
np.array([-1, 1], dtype=np.int64),
op_version=op_version) for bsvc in rvc.binary_rvc_list_
]
y_matrix: OnnxOperator = OnnxConcat(*y_list, axis=1, op_version=op_version)
probs: OnnxOperator = OnnxSoftmax(y_matrix,
axis=1,
op_version=op_version,
output_names=[op_outputs[1]])
probs.add_to(scope=scope, container=container)
labels: OnnxOperator = OnnxArgMax(probs,
axis=1,
keepdims=0,
op_version=op_version,
output_names=[op_outputs[0]])
labels.add_to(scope=scope, container=container)
As it still complains about variable name "y"
is duplicating, I simply remove the op_outputs[0].set_onnx_name("y")
from rvr_shape_calculator
. However, I still want to let the final onnx model have more descriptive output names instead of its default output name or even unnamed.
Would you be able to suggest me the proper way of setting the variable name?
Sorry for the delay. You may try the latest version of the package, I fix a couple of bugs in the way the library is converting pipeline. You should use set_onnx_name_prefix
to more descriptive names.
There were two release since this issue was raised. Did it solve your issue?
Thanks for the followup.
The semester began late August, and since then I am just so busy on class work. I haven't had time to work on this particular project. But I might have time after the semester is over, which is around 2 weeks from now.