Repository: onnx/sklearn-onnx

Issue:
NotImplementedError: There is no corresponding post_transform for 'HalfMultinomialLoss' when converting a RandomForest
Using the scikit-learn dev branch, an RF conversion yields:

```python-traceback
...
  File "/home/runner/work/sklearn_benchmarks/sklearn_benchmarks/./sklearn_benchmarks/benchmarking/core.py", line 389, in run_func
    onx = convert_sklearn(self.estimator, initial_types=initial_type)
  File "/usr/share/miniconda/envs/sklbench/lib/python3.8/site-packages/skl2onnx/convert.py", line 160, in convert_sklearn
    onnx_model = convert_topology(topology, name, doc_string, target_opset,
  File "/usr/share/miniconda/envs/sklbench/lib/python3.8/site-packages/skl2onnx/common/_topology.py", line 1087, in convert_topology
    conv(scope, operator, container)
  File "/usr/share/miniconda/envs/sklbench/lib/python3.8/site-packages/skl2onnx/common/_registration.py", line 29, in __call__
    return self._fct(*args)
  File "/usr/share/miniconda/envs/sklbench/lib/python3.8/site-packages/skl2onnx/operator_converters/random_forest.py", line 190, in convert_sklearn_random_forest_classifier
    raise NotImplementedError(
NotImplementedError: There is no corresponding post_transform for 'HalfMultinomialLoss'.
```

as observed in https://github.com/mbatoul/sklearn_benchmarks/issues/20.

I you cannot reproduce by running the `sklearn-onnx` test suite against the current scikit-learn `main`, I can try to work on a minimal reproducer instead.


Discussion:
It happens in `convert_sklearn_random_forest_classifier` but I am not 100% sure it's actually converting a random forest. Maybe it's an histogram gradient boosting model instead. It's not clear from the logs.

Resolving patch:
diff --git a/docs/tutorial/plot_jcustom_syntax.py b/docs/tutorial/plot_jcustom_syntax.py
--- a/docs/tutorial/plot_jcustom_syntax.py
+++ b/docs/tutorial/plot_jcustom_syntax.py
@@ -9,7 +9,8 @@
 There are two ways to write a converter. The first one
 is less verbose and easier to understand
 (see `k_means.py <https://github.com/onnx/sklearn-onnx/blob/
-master/skl2onnx/operator_converters/k_means.py>`_). The other is very verbose (see `ada_boost.py <https://github.com/onnx/
+master/skl2onnx/operator_converters/k_means.py>`_).
+The other is very verbose (see `ada_boost.py <https://github.com/onnx/
 sklearn-onnx/blob/master/skl2onnx/operator_converters/ada_boost.py>`_
 for an example).
 
diff --git a/skl2onnx/operator_converters/random_forest.py b/skl2onnx/operator_converters/random_forest.py
--- a/skl2onnx/operator_converters/random_forest.py
+++ b/skl2onnx/operator_converters/random_forest.py
@@ -180,9 +180,11 @@ def convert_sklearn_random_forest_classifier(
         if loss is not None:
             if use_raw_scores:
                 attr_pairs['post_transform'] = "NONE"
-            elif loss.__class__.__name__ == "BinaryCrossEntropy":
+            elif loss.__class__.__name__ in (
+                    "BinaryCrossEntropy", "HalfBinomialLoss"):
                 attr_pairs['post_transform'] = "LOGISTIC"
-            elif loss.__class__.__name__ == "CategoricalCrossEntropy":
+            elif loss.__class__.__name__ in (
+                    "CategoricalCrossEntropy", "HalfMultinomialLoss"):
                 attr_pairs['post_transform'] = "SOFTMAX"
             else:
                 raise NotImplementedError(
@@ -206,7 +208,8 @@ def convert_sklearn_random_forest_classifier(
                 if k in ('nodes_values', 'class_weights',
                          'target_weights', 'nodes_hitrates',
                          'base_values'):
-                    attr_pairs[k] = np.array(attr_pairs[k], dtype=dtype)
+                    attr_pairs[k] = np.array(
+                        attr_pairs[k], dtype=dtype).ravel()
 
         container.add_node(
             op_type, input_name,
@@ -246,7 +249,7 @@ def convert_sklearn_random_forest_classifier(
                     if k in ('nodes_values', 'class_weights',
                              'target_weights', 'nodes_hitrates',
                              'base_values'):
-                        attrs[k] = np.array(attrs[k], dtype=dtype)
+                        attrs[k] = np.array(attrs[k], dtype=dtype).ravel()
 
             if options['decision_path']:
                 # decision_path
@@ -389,7 +392,7 @@ def convert_sklearn_random_forest_regressor_converter(
             if k in ('nodes_values', 'class_weights',
                      'target_weights', 'nodes_hitrates',
                      'base_values'):
-                attrs[k] = np.array(attrs[k], dtype=dtype)
+                attrs[k] = np.array(attrs[k], dtype=dtype).ravel()
 
     container.add_node(
         op_type, input_name,
@@ -430,7 +433,7 @@ def convert_sklearn_random_forest_regressor_converter(
                 if k in ('nodes_values', 'class_weights',
                          'target_weights', 'nodes_hitrates',
                          'base_values'):
-                    attrs[k] = np.array(attrs[k], dtype=dtype)
+                    attrs[k] = np.array(attrs[k], dtype=dtype).ravel()
 
         if options.get('decision_path', False):
             # decision_path


Test patch: