Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 33 additions & 20 deletions runtime/onert/api/python/package/infer/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,6 @@ def __init__(self, path: str, backends: str = "cpu") -> None:
super().__init__(infer.nnfw_session(path, backends))
self._prepared: bool = False

# TODO: Revise this after discussion to properly support dynamic shapes
# This is a temporary workaround to prevent prepare() errors when tensorinfo dims include -1
original_infos: List[tensorinfo] = self.get_inputs_tensorinfo()
fixed_infos: List[tensorinfo] = []
for info in original_infos:
dims = list(info.dims)
# replace -1 with 1
dims = [1 if d == -1 else d for d in dims]
info.dims = dims # assume setter accepts a list
fixed_infos.append(info)
# update tensorinfo in session
self.update_inputs_tensorinfo(fixed_infos)

def update_inputs_tensorinfo(self, new_infos: List[tensorinfo]) -> None:
"""
Update all input tensors' tensorinfo at once.
Expand Down Expand Up @@ -89,20 +76,46 @@ def infer(
"""
metrics: Dict[str, float] = {}

# Check if the session is prepared. If not, call prepare() and set_outputs() once.
if not self._prepared:
with self._time_block(metrics, 'prepare_time_ms', measure):
self.session.prepare()
self.set_outputs(self.session.output_size())
self._prepared = True

# Verify that the number of provided inputs matches the session's expected input count.
expected_input_size: int = self.session.input_size()
if len(inputs_array) != expected_input_size:
raise ValueError(
f"Expected {expected_input_size} input(s), but received {len(inputs_array)}."
)

# Check if the session is prepared. If not, call prepare() and set_outputs() once.
if not self._prepared:
with self._time_block(metrics, 'prepare_time_ms', measure):
# On first call, fix any -1 dims to real input shapes and validate
original_infos = self.get_inputs_tensorinfo()
fixed_infos = []
for idx, info in enumerate(original_infos):
input_shape = inputs_array[idx].shape
new_dims = []
# only the first `info.rank` entries matter
for j, d in enumerate(info.dims[:info.rank]):
if d == -1:
# replace dynamic dim with actual incoming shape
new_dims.append(input_shape[j])
elif d == input_shape[j]:
# static dim must match the provided array
new_dims.append(d)
else:
raise ValueError(
f"Input #{idx} dim {j} mismatch: "
f"tensorinfo={d}, actual input shape={input_shape[j]}")
Comment thread
ragmani marked this conversation as resolved.
Outdated
# Preserve any trailing dims beyond rank
# new_dims += list(info.dims[info.rank:])
info.dims = new_dims
fixed_infos.append(info)

# Update tensorinfo to optimize using it
self.update_inputs_tensorinfo(fixed_infos)

self.session.prepare()
self.set_outputs(self.session.output_size())
self._prepared = True

# Configure input buffers using the current session's input size and provided data.
with self._time_block(metrics, 'io_time_ms', measure):
self.set_inputs(expected_input_size, inputs_array)
Expand Down
37 changes: 37 additions & 0 deletions runtime/onert/sample/minimal-python/src/dynamic_shape_inference.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import numpy as np
import random
import sys
from onert import infer


def main(nnpackage_path, backends="cpu"):
# Create session and load nnpackage
session = infer.session(nnpackage_path, backends)

# Prepare input. Here we just allocate dummy input arrays.
input_infos = session.get_inputs_tensorinfo()

# Call infer() 10 times
for i in range(10):
dummy_inputs = []
for info in input_infos:
# Retrieve the dimensions list from tensorinfo property.
dims = list(info.dims)
# Replace -1 with a random value between 1 and 10
dims = [random.randint(1, 10) if d == -1 else d for d in dims]
# Build the shape tuple from tensorinfo dimensions.
shape = tuple(dims[:info.rank])
# Create a dummy numpy array filled with uniform random values in [0,1).
dummy_inputs.append(
np.random.uniform(low=0.0, high=1.0, size=shape).astype(info.dtype))

outputs = session.infer(dummy_inputs)
print(f"Inference run {i+1}/10 completed.")

print(f"nnpackage {nnpackage_path.split('/')[-1]} runs successfully.")
return


if __name__ == "__main__":
argv = sys.argv[1:]
main(*argv)
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,7 @@ def main(nnpackage_path, backends="cpu"):
# We copy the current info and modify the rank and dims.
# (Note: Depending on your model, you may want to modify additional dimensions.)
new_shape = [10] + list(info.dims[1:info.rank])
info.rank = len(new_shape)
for i, dim in enumerate(new_shape):
info.dims[i] = dim
# For any remaining dimensions up to NNFW_MAX_RANK, set them to a default (1).
for i in range(len(new_shape), len(info.dims)):
info.dims[i] = 1
info.dims = new_shape
new_input_infos.append(info)

# Update all input tensorinfos in the session at once.
Expand Down