microsoft / onnxruntime

ONNX Runtime: cross-platform, high performance ML inferencing and training accelerator
https://onnxruntime.ai
MIT License
14.52k stars 2.91k forks source link

C++ Runtime does not recognize supposedly correct input. #16430

Open sbviana opened 1 year ago

sbviana commented 1 year ago

Describe the issue

I have been struggling with this issue for quite some time.

I have a Sk2learn model that uses a Random Forest Classifier, its input should be 28 floats, when calling it from a C++ code I got the following, very puzzling, error:

2023-06-20 11:40:34.8409599 [E:onnxruntime:, sequential_executor.cc:514 onnxruntime::ExecuteKernel] Non-zero status code returned while running TreeEnsembleClassifier node. Name:'TreeEnsembleClassifier' Status Message: C:\a_work\1\s\onnxruntime\core\providers\cpu\ml\tree_ensemble_common.h:418 onnxruntime::ml::detail::TreeEnsembleCommon<float,float,float>::ComputeAgg One path in the graph requests feature 28 but input tensor has 28 features.

Exception Non-zero status code returned while running TreeEnsembleClassifier node. Name:'TreeEnsembleClassifier' Status Message: C:\a_work\1\s\onnxruntime\core\providers\cpu\ml\tree_ensemble_common.h:418 onnxruntime::ml::detail::TreeEnsembleCommon<float,float,float>::ComputeAgg One path in the graph requests feature 28 but input tensor has 28 features.

So for me it is "clear" that the C++ code is passing the parameters correctly but when it gets to ONNX for some reason there is a mismatch,

I have exported the model from Python using:

from skl2onnx import convert_sklearn from skl2onnx.common.data_types import FloatTensorType initial_type = [('fxxx', FloatTensorType([None, 28]))] onx = to_onnx(model2, initial_types=initial_type, target_opset=17)

And in C++ I define the inputs as such:

const std::array<int64_t, 2> input_shape = {0, 28};

const char *const input_names = { "fxxx" };
std::vector<float>  input_data = { 4.11225e+03, 4.11300e+03, 4.11225e+03, 4.11275e+03, 9.30000e+01, 1.54000e+02,
    0.00000e+00, 0.00000e+00, 0.00000e+00, 0.00000e+00, 0.00000e+00, 0.00000e+00,
    0.00000e+00, 0.00000e+00, 0.00000e+00, 3.00000e+00, 0.00000e+00, 0.00000e+00,
    5.20000e+00, 0.00000e+00, 0.00000e+00, 0.00000e+00, 0.00000e+00, 0.00000e+00,
    0.00000e+00, 0.00000e+00, 0.00000e+00, 1.83000e+00 };

auto inputTensor = Ort::Value::CreateTensor(memory_info, input_data.data(), input_data.size(), input_shape.data(), input_shape.size());

For calling the run method I am using:

std::vector<const char*> output_names = { "output_probability", "output_label" };

auto out_s = session.Run(runOptions, inputNames.data(), &inputTensor, 1, output_names.data(), output_names.size());

At this point I am using a trial and error approach since I cannot find anything on the documentation on what could be leading to this error.

To reproduce

Simply Calling the runtime.

Urgency

No response

Platform

Windows

OS Version

Win 11

ONNX Runtime Installation

Released Package

ONNX Runtime Version or Commit ID

1.15.1

ONNX Runtime API

C++

Architecture

X64

Execution Provider

Default CPU

Execution Provider Library Version

Microsoft.ML.Runtime 1.15.1

yuslepukhin commented 1 year ago

You shape does not match the input size. According to your shape the input has exactly zero elements.

sbviana commented 1 year ago

Thanks for responding Dmitri, I had the 0 there since the model was exported from Python with initial_type = [('fxxx', FloatTensorType([None, 28]))], removing the None I could get it to work with an input_shape of {28}.

I still have one question I am researching for with no success so far, the model returns an array of ints, and a sequence of maps, I cannot find a way to get this sequence, can you help me with that, or point me to the documentation on where I can find it?

I could not find a method to convert one of the outputs into a seq(map(int64,tensor(float))).

Thanks.

yuslepukhin commented 1 year ago

Onnxruntime always takes OrtValues as inputs and returns the same as outputs. OrtValue are containers (discriminated unions) that may contain one of the following tensors, sparse_tensors, maps, and sequences. The latter two are composite and potentially recursive data structures.

You can use GetTypeInfo() to call GetONNXType() to find out what a particular value contains. You then can use other TypeInfo methods to retrieve type specific metadata.

See Onnx standard that Onnxruntime aspires to implement.

Sequences may contain any other data type including other sequences, maps and tensors. All of those are presented as OrtValues.

Maps are composed of two OrtValues. Keys are always represented by a tensor, so keys data types can either be primitive or strings.

The number of keys is the same as the number of values and they are in the same order. The OrtValue for map values can either contain a tensor with primitive data types or a sequence of composite types such as other sequences or maps.

GetValueCount() would always return 2 maps (1 for keys and 1 for values). GetValueCount() would return N for the number of ortvalues contained in the sequence.

GetValue() would enable you to retrieve by index ortvalues contained within composite types.

Any composite data type eventually reduces to an OrtValue that contains a tensor.