mirror of
https://github.com/saymrwulf/onnxruntime.git
synced 2026-07-26 19:52:38 +00:00
Handle dynamic shapes and reshape the input according to the model (#2986)
Put type validation in separate method.
This commit is contained in:
parent
ec07fdc5b0
commit
4f4f4bcd92
1 changed files with 49 additions and 23 deletions
|
|
@ -45,42 +45,68 @@ class DataFrameTool():
|
|||
self._sess_options = sess_options
|
||||
self._sess = onnxrt.InferenceSession(self._model_path, self._sess_options)
|
||||
|
||||
def _reshape_input(self, input_array, expected_shape):
|
||||
"""
|
||||
:param - input_array numpy array. This one is obtained from DataFrame and expected to have
|
||||
: a rank if 1.
|
||||
:expected_shape - shape fetched from the model which may include dynamic elements.
|
||||
: expected_shape may at most have one -1, None or zero which will be computed from
|
||||
: the size of the input_array. We replace None and zeros to -1 and let np.ndarray.reshape deal with it.
|
||||
"""
|
||||
# expected_shape rank is one, we will let onnxruntime to deal with it
|
||||
if len(expected_shape) == 1:
|
||||
return input_array
|
||||
|
||||
inferred_shape = [dim if dim else -1 for dim in expected_shape]
|
||||
return input_array.reshape(inferred_shape)
|
||||
|
||||
def _validate_type(self, input_meta, col_type):
|
||||
"""
|
||||
: input_meta - meta info obtained from the model for the given input
|
||||
: col_type - dtype of the column
|
||||
: throws if conditions are not met
|
||||
|
||||
float16 and bool will always require exact match
|
||||
We attempt to convert any type to a string if it is required.
|
||||
With strings we always want to put this into a flat array, cast to np.object and then reshape as object
|
||||
Any other type to qualify for casting must match either integer or floating point types
|
||||
"""
|
||||
expected_type = types_dict[input_meta.type]
|
||||
if input_meta.type == 'tensor(string)':
|
||||
return
|
||||
elif expected_type == col_type:
|
||||
return
|
||||
elif expected_type in ort_float_set and str(col_type) in pd_float_set:
|
||||
return
|
||||
elif expected_type in ort_int_set and str(col_type) in pd_int_set:
|
||||
return
|
||||
|
||||
raise TypeError("Input {} requires type {} unable to cast column type {} ".format(
|
||||
input_meta.name, expected_type, col_type))
|
||||
|
||||
|
||||
def _process_input_list(self, df, input_metas, require):
|
||||
"""
|
||||
Return a dictionary of input_name : a typed and shaped np.array of values for a given input_meta
|
||||
The function does the heavy lifting for _get_input_feeds()
|
||||
|
||||
:param df: See :class:`pandas.DataFrame`.
|
||||
:param df: See :class:`pandas.DataFrame`.
|
||||
:param input_metas: a list of name/type pairs
|
||||
:require is a boolean. If True this helper throws on a missing input.
|
||||
|
||||
|
||||
"""
|
||||
feeds = {}
|
||||
# Process mandadory inputs. Raise an error if anything is not present
|
||||
for input_meta in input_metas:
|
||||
shape = [dim if dim else 1 for dim in input_meta.shape]
|
||||
# We fully expect all the types are in the above dictionary
|
||||
assert input_meta.type in types_dict, "Update types_dict for the new type"
|
||||
if input_meta.name in df.columns:
|
||||
expected_type = types_dict[input_meta.type]
|
||||
# float16 and bool will always require exact match
|
||||
# We attempt to convert any type to a string if it is required.
|
||||
# With strings we always want to put this into a flat array, cast to np.object and then reshape as object
|
||||
if input_meta.type == 'tensor(string)':
|
||||
#print('Col: {} processed as string type: {} '.format(input_meta.name, df[input_meta.name].dtype))
|
||||
feeds[input_meta.name] = np.array([df[input_meta.name][0]]).astype(expected_type).reshape(shape)
|
||||
elif expected_type == df[input_meta.name].dtype: # If there is an exact match we take as is
|
||||
#print('Col: {} processed exact match type: {} '.format(input_meta.name, df[input_meta.name].dtype))
|
||||
feeds[input_meta.name] = np.array([df[input_meta.name][0]]).astype(expected_type).reshape(shape)
|
||||
elif expected_type in ort_float_set and str(df[input_meta.name].dtype) in pd_float_set:
|
||||
#print('Col: {} processed as floating type: {} '.format(input_meta.name, df[input_meta.name].dtype))
|
||||
feeds[input_meta.name] = np.array([df[input_meta.name][0]]).astype(expected_type).reshape(shape)
|
||||
elif expected_type in ort_int_set and str(df[input_meta.name].dtype) in pd_int_set:
|
||||
#print('Col: {} processed as integer type: {} '.format(input_meta.name, df[input_meta.name].dtype))
|
||||
feeds[input_meta.name] = np.array([df[input_meta.name][0]]).astype(expected_type).reshape(shape)
|
||||
else:
|
||||
raise TypeError("Input {} expected to be of type: {} got {} ".format(
|
||||
input_meta.name, expected_type, df[input_meta.name].dtype))
|
||||
self._validate_type(input_meta, df[input_meta.name].dtype)
|
||||
# With strings we must cast first to np.object then then reshape
|
||||
# so we do it for everything
|
||||
input_array = np.array(df[input_meta.name]).astype(types_dict[input_meta.type])
|
||||
feeds[input_meta.name] = self._reshape_input(input_array, input_meta.shape)
|
||||
|
||||
elif require:
|
||||
raise RuntimeError("This model requires input {} of type {} but it is not found in the DataFrame".format(
|
||||
input_meta.name, types_dict[input_meta.type]))
|
||||
|
|
@ -102,7 +128,7 @@ class DataFrameTool():
|
|||
and feeds the data to the appropriate model inputs.
|
||||
|
||||
:param sess: See :class:`onnxruntime.InferenceSession`.
|
||||
|
||||
|
||||
::
|
||||
For example: pd.DataFrame([[0], [4],[20]],index=[0], columns=['A', 'B', 'C'])
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue