(l-onnx-docai-onnx-ml-TreeEnsemble)=
# ai.onnx.ml - TreeEnsemble
(l-onnx-opai-onnx-ml-treeensemble-5)=
## TreeEnsemble - 5 (ai.onnx.ml)
### Version
- **name**: [TreeEnsemble (GitHub)](https://github.com/onnx/onnx/blob/main/docs/Operators-ml.md#ai.onnx.ml.TreeEnsemble)
- **domain**: `ai.onnx.ml`
- **since_version**: `5`
- **function**: `False`
- **support_level**: `SupportType.COMMON`
- **shape inference**: `True`
This version of the operator has been available
**since version 5 of domain ai.onnx.ml**.
### Summary
Tree Ensemble operator. Returns the regressed values for each input in a batch.
Inputs have dimensions `[N, F]` where `N` is the input batch size and `F` is the number of input features.
Outputs have dimensions `[N, num_targets]` where `N` is the batch size and `num_targets` is the number of targets, which is a configurable attribute.
The encoding of this attribute is split along interior nodes and the leaves of the trees. Notably, attributes with the prefix `nodes_*` are associated with interior nodes, and attributes with the prefix `leaf_*` are associated with leaves.
The attributes `nodes_*` must all have the same length and encode a sequence of tuples, as defined by taking all the `nodes_*` fields at a given position.
All fields prefixed with `leaf_*` represent tree leaves, and similarly define tuples of leaves and must have identical length.
This operator can be used to implement both the previous `TreeEnsembleRegressor` and `TreeEnsembleClassifier` nodes.
The `TreeEnsembleRegressor` node maps directly to this node and requires changing how the nodes are represented.
The `TreeEnsembleClassifier` node can be implemented by adding a `ArgMax` node after this node to determine the top class.
To encode class labels, a `LabelEncoder` or `GatherND` operator may be used.
### Attributes
* **aggregate_function - INT** (default is `'1'`):
Defines how to aggregate leaf values within a target.
One of 'AVERAGE' (0) 'SUM' (1) 'MIN' (2) 'MAX (3) defaults to 'SUM' (1)
* **leaf_targetids - INTS** (required) :
The index of the target that this leaf contributes to (this must be in range `[0, n_targets)`).
* **leaf_weights - TENSOR** (required) :
The weight for each leaf.
* **membership_values - TENSOR** :
Members to test membership of for each set membership node. List all of the members to test again in the order that the 'BRANCH_MEMBER' mode appears in `node_modes`, delimited by `NaN`s. Will have the same number of sets of values as nodes with mode 'BRANCH_MEMBER'. This may be omitted if the node doesn't contain any 'BRANCH_MEMBER' nodes.
* **n_targets - INT** :
The total number of targets.
* **nodes_falseleafs - INTS** (required) :
1 if false branch is leaf for each node and 0 if an interior node. To represent a tree that is a leaf (only has one node), one can do so by having a single `nodes_*` entry with true and false branches referencing the same `leaf_*` entry
* **nodes_falsenodeids - INTS** (required) :
If `nodes_falseleafs` is false at an entry, this represents the position of the false branch node. This position can be used to index into a `nodes_*` entry. If `nodes_falseleafs` is false, it is an index into the leaf_* attributes.
* **nodes_featureids - INTS** (required) :
Feature id for each node.
* **nodes_hitrates - TENSOR** :
Popularity of each node, used for performance and may be omitted.
* **nodes_missing_value_tracks_true - INTS** :
For each node, define whether to follow the true branch (if attribute value is 1) or false branch (if attribute value is 0) in the presence of a NaN input feature. This attribute may be left undefined and the default value is false (0) for all nodes.
* **nodes_modes - TENSOR** (required) :
The comparison operation performed by the node. This is encoded as an enumeration of 0 ('BRANCH_LEQ'), 1 ('BRANCH_LT'), 2 ('BRANCH_GTE'), 3 ('BRANCH_GT'), 4 ('BRANCH_EQ'), 5 ('BRANCH_NEQ'), and 6 ('BRANCH_MEMBER'). Note this is a tensor of type uint8.
* **nodes_splits - TENSOR** (required) :
Thresholds to do the splitting on for each node with mode that is not 'BRANCH_MEMBER'.
* **nodes_trueleafs - INTS** (required) :
1 if true branch is leaf for each node and 0 an interior node. To represent a tree that is a leaf (only has one node), one can do so by having a single `nodes_*` entry with true and false branches referencing the same `leaf_*` entry
* **nodes_truenodeids - INTS** (required) :
If `nodes_trueleafs` is false at an entry, this represents the position of the true branch node. This position can be used to index into a `nodes_*` entry. If `nodes_trueleafs` is false, it is an index into the leaf_* attributes.
* **post_transform - INT** (default is `'0'`):
Indicates the transform to apply to the score.
One of 'NONE' (0), 'SOFTMAX' (1), 'LOGISTIC' (2), 'SOFTMAX_ZERO' (3) or 'PROBIT' (4), defaults to 'NONE' (0)
* **tree_roots - INTS** (required) :
Index into `nodes_*` for the root of each tree. The tree structure is derived from the branching of each node.
### Inputs
- **X** (heterogeneous) - **T**:
Input of shape [Batch Size, Number of Features]
### Outputs
- **Y** (heterogeneous) - **T**:
Output of shape [Batch Size, Number of targets]
### Type Constraints
* **T** in ( `tensor(double)`, `tensor(float)`, `tensor(float16)` ):
The input type must be a tensor of a numeric type.
### Examples
#### _tree_ensemble_single_tree
```python
import numpy as np
import onnx
node = onnx.helper.make_node(
"TreeEnsemble",
["X"],
["Y"],
domain="ai.onnx.ml",
n_targets=2,
membership_values=None,
nodes_missing_value_tracks_true=None,
nodes_hitrates=None,
aggregate_function=1,
post_transform=0,
tree_roots=[0],
nodes_modes=make_tensor(
"nodes_modes",
onnx.TensorProto.UINT8,
(3,),
np.array([0, 0, 0], dtype=np.uint8),
),
nodes_featureids=[0, 0, 0],
nodes_splits=make_tensor(
"nodes_splits",
onnx.TensorProto.DOUBLE,
(3,),
np.array([3.14, 1.2, 4.2], dtype=np.float64),
),
nodes_truenodeids=[1, 0, 1],
nodes_trueleafs=[0, 1, 1],
nodes_falsenodeids=[2, 2, 3],
nodes_falseleafs=[0, 1, 1],
leaf_targetids=[0, 1, 0, 1],
leaf_weights=make_tensor(
"leaf_weights",
onnx.TensorProto.DOUBLE,
(4,),
np.array([5.23, 12.12, -12.23, 7.21], dtype=np.float64),
),
)
x = np.array([1.2, 3.4, -0.12, 1.66, 4.14, 1.77], np.float64).reshape(3, 2)
y = np.array([[5.23, 0], [5.23, 0], [0, 12.12]], dtype=np.float64)
expect(
node,
inputs=[x],
outputs=[y],
name="test_ai_onnx_ml_tree_ensemble_single_tree",
)
```
#### _tree_ensemble_set_membership
```python
import numpy as np
import onnx
node = onnx.helper.make_node(
"TreeEnsemble",
["X"],
["Y"],
domain="ai.onnx.ml",
n_targets=4,
aggregate_function=1,
membership_values=make_tensor(
"membership_values",
onnx.TensorProto.FLOAT,
(8,),
[1.2, 3.7, 8, 9, np.nan, 12, 7, np.nan],
),
nodes_missing_value_tracks_true=None,
nodes_hitrates=None,
post_transform=0,
tree_roots=[0],
nodes_modes=make_tensor(
"nodes_modes",
onnx.TensorProto.UINT8,
(3,),
np.array([0, 6, 6], dtype=np.uint8),
),
nodes_featureids=[0, 0, 0],
nodes_splits=make_tensor(
"nodes_splits",
onnx.TensorProto.FLOAT,
(3,),
np.array([11, 232344.0, np.nan], dtype=np.float32),
),
nodes_trueleafs=[0, 1, 1],
nodes_truenodeids=[1, 0, 1],
nodes_falseleafs=[1, 0, 1],
nodes_falsenodeids=[2, 2, 3],
leaf_targetids=[0, 1, 2, 3],
leaf_weights=make_tensor(
"leaf_weights", onnx.TensorProto.FLOAT, (4,), [1, 10, 1000, 100]
),
)
x = np.array([1.2, 3.4, -0.12, np.nan, 12, 7], np.float32).reshape(-1, 1)
expected = np.array(
[
[1, 0, 0, 0],
[0, 0, 0, 100],
[0, 0, 0, 100],
[0, 0, 1000, 0],
[0, 0, 1000, 0],
[0, 10, 0, 0],
],
dtype=np.float32,
)
expect(
node,
inputs=[x],
outputs=[expected],
name="test_ai_onnx_ml_tree_ensemble_set_membership",
)
```