|
3 | 3 |
|
4 | 4 | import numbers
|
5 | 5 | import numpy as np
|
| 6 | +from onnx.numpy_helper import from_array |
6 | 7 | from ..common._apply_operation import (
|
7 | 8 | apply_cast,
|
8 | 9 | apply_concat,
|
@@ -124,7 +125,7 @@ def predict(
|
124 | 125 | [indices_name, dummy_proba_name],
|
125 | 126 | op_domain=op_domain,
|
126 | 127 | op_version=op_version,
|
127 |
| - **attrs |
| 128 | + **attrs, |
128 | 129 | )
|
129 | 130 | else:
|
130 | 131 | zero_name = scope.get_unique_variable_name("zero")
|
@@ -243,7 +244,7 @@ def _append_decision_output(
|
243 | 244 | dpath,
|
244 | 245 | op_domain=op_domain,
|
245 | 246 | op_version=op_version,
|
246 |
| - **attrs |
| 247 | + **attrs, |
247 | 248 | )
|
248 | 249 |
|
249 | 250 | if n_out is None:
|
@@ -306,6 +307,53 @@ def convert_sklearn_decision_tree_classifier(
|
306 | 307 | dtype = np.float32
|
307 | 308 | op = operator.raw_operator
|
308 | 309 | options = scope.get_options(op, dict(decision_path=False, decision_leaf=False))
|
| 310 | + if np.asarray(op.classes_).size == 1: |
| 311 | + # The model was trained with one label. |
| 312 | + # There is no need to build a tree. |
| 313 | + if op.n_outputs_ != 1: |
| 314 | + raise RuntimeError( |
| 315 | + f"One training class and multiple outputs is not " |
| 316 | + f"supported yet for class {op.__class__.__name__!r}." |
| 317 | + ) |
| 318 | + if options["decision_path"] or options["decision_leaf"]: |
| 319 | + raise RuntimeError( |
| 320 | + f"One training class, option 'decision_path' " |
| 321 | + f"or 'decision_leaf' are not supported for " |
| 322 | + f"class {op.__class__.__name__!r}." |
| 323 | + ) |
| 324 | + |
| 325 | + zero = scope.get_unique_variable_name("zero") |
| 326 | + one = scope.get_unique_variable_name("one") |
| 327 | + new_shape = scope.get_unique_variable_name("new_shape") |
| 328 | + container.add_initializer(zero, onnx_proto.TensorProto.INT64, [1], [0]) |
| 329 | + container.add_initializer(one, onnx_proto.TensorProto.INT64, [1], [1]) |
| 330 | + container.add_initializer(new_shape, onnx_proto.TensorProto.INT64, [2], [-1, 1]) |
| 331 | + shape = scope.get_unique_variable_name("shape") |
| 332 | + container.add_node("Shape", [operator.inputs[0].full_name], [shape]) |
| 333 | + shape_sliced = scope.get_unique_variable_name("shape_sliced") |
| 334 | + container.add_node("Slice", [shape, zero, one, zero], [shape_sliced]) |
| 335 | + |
| 336 | + # labels |
| 337 | + container.add_node( |
| 338 | + "ConstantOfShape", |
| 339 | + [shape_sliced], |
| 340 | + [operator.outputs[0].full_name], |
| 341 | + value=from_array(np.array([op.classes_[0]], dtype=np.int64)), |
| 342 | + ) |
| 343 | + |
| 344 | + # probabilities |
| 345 | + probas = scope.get_unique_variable_name("probas") |
| 346 | + container.add_node( |
| 347 | + "ConstantOfShape", |
| 348 | + [shape_sliced], |
| 349 | + [probas], |
| 350 | + value=from_array(np.array([1], dtype=dtype)), |
| 351 | + ) |
| 352 | + container.add_node( |
| 353 | + "Reshape", [probas, new_shape], [operator.outputs[1].full_name] |
| 354 | + ) |
| 355 | + return |
| 356 | + |
309 | 357 | if op.n_outputs_ == 1:
|
310 | 358 | attrs = get_default_tree_classifier_attribute_pairs()
|
311 | 359 | attrs["name"] = scope.get_unique_operator_name(op_type)
|
@@ -355,7 +403,7 @@ def convert_sklearn_decision_tree_classifier(
|
355 | 403 | [operator.outputs[0].full_name, operator.outputs[1].full_name],
|
356 | 404 | op_domain=op_domain,
|
357 | 405 | op_version=op_version,
|
358 |
| - **attrs |
| 406 | + **attrs, |
359 | 407 | )
|
360 | 408 |
|
361 | 409 | n_out = 2
|
@@ -510,7 +558,7 @@ def convert_sklearn_decision_tree_regressor(
|
510 | 558 | operator.outputs[0].full_name,
|
511 | 559 | op_domain=op_domain,
|
512 | 560 | op_version=op_version,
|
513 |
| - **attrs |
| 561 | + **attrs, |
514 | 562 | )
|
515 | 563 |
|
516 | 564 | options = scope.get_options(op, dict(decision_path=False, decision_leaf=False))
|
|
0 commit comments