Skip to content

Commit d85affe

Browse files
committed
[onert/python] Support dynamic shapes(on-the-fly)
This commit support dynamic shapes and add dynamic_shape_inference sample. - Remove the old “replace -1 with 1” placeholder in the constructor - In `infer()`, on first call: - Inspect each `tensorinfo.dims` and for any `-1` replace it with the matching `inputs_array[i].shape[j]` - Validate that any non-`-1` dims match the actual input shape, raising on mismatch - Call `update_inputs_tensorinfo()`, then `prepare()` and `set_outputs()` once - Bundle the above into the “auto‑dynamic” path instead of hard‑coding 1’s - Add `dynamic_shape_inference.py` sample to show 10 runs with random shapes ONE-DCO-1.0-Signed-off-by: ragmani <ragmani0216@gmail.com>
1 parent 2f08ce4 commit d85affe

2 files changed

Lines changed: 72 additions & 19 deletions

File tree

runtime/onert/api/python/package/infer/session.py

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,6 @@ def __init__(self, path: str, backends: str = "cpu") -> None:
2020
super().__init__(infer.nnfw_session(path, backends))
2121
self._prepared: bool = False
2222

23-
# TODO: Revise this after discussion to properly support dynamic shapes
24-
# This is a temporary workaround to prevent prepare() errors when tensorinfo dims include -1
25-
original_infos: List[tensorinfo] = self.get_inputs_tensorinfo()
26-
fixed_infos: List[tensorinfo] = []
27-
for info in original_infos:
28-
dims = list(info.dims)
29-
# replace -1 with 1
30-
dims = [1 if d == -1 else d for d in dims]
31-
info.dims = dims # assume setter accepts a list
32-
fixed_infos.append(info)
33-
# update tensorinfo in session
34-
self.update_inputs_tensorinfo(fixed_infos)
35-
3623
def update_inputs_tensorinfo(self, new_infos: List[tensorinfo]) -> None:
3724
"""
3825
Update all input tensors' tensorinfo at once.
@@ -76,19 +63,45 @@ def infer(self, inputs_array: List[np.ndarray]) -> List[np.ndarray]:
7663
Returns:
7764
list[np.ndarray]: A list containing the output numpy arrays.
7865
"""
79-
# Check if the session is prepared. If not, call prepare() and set_outputs() once.
80-
if not self._prepared:
81-
self.session.prepare()
82-
self.set_outputs(self.session.output_size())
83-
self._prepared = True
84-
8566
# Verify that the number of provided inputs matches the session's expected input count.
8667
expected_input_size: int = self.session.input_size()
8768
if len(inputs_array) != expected_input_size:
8869
raise ValueError(
8970
f"Expected {expected_input_size} input(s), but received {len(inputs_array)}."
9071
)
9172

73+
# Check if the session is prepared. If not, call prepare() and set_outputs() once.
74+
if not self._prepared:
75+
# On first call, fix any -1 dims to real input shapes and validate
76+
original_infos = self.get_inputs_tensorinfo()
77+
fixed_infos = []
78+
for idx, info in enumerate(original_infos):
79+
input_shape = inputs_array[idx].shape
80+
new_dims = []
81+
# only the first `info.rank` entries matter
82+
for j, d in enumerate(info.dims[:info.rank]):
83+
if d == -1:
84+
# replace dynamic dim with actual incoming shape
85+
new_dims.append(input_shape[j])
86+
elif d == input_shape[j]:
87+
# static dim must match the provided array
88+
new_dims.append(d)
89+
else:
90+
raise ValueError(
91+
f"Input #{idx} dim {j} mismatch: "
92+
f"tensorinfo={d}, actual input shape={input_shape[j]}")
93+
# Preserve any trailing dims beyond rank
94+
# new_dims += list(info.dims[info.rank:])
95+
info.dims = new_dims
96+
fixed_infos.append(info)
97+
98+
# Update tensorinfo to optimize using it
99+
self.update_inputs_tensorinfo(fixed_infos)
100+
101+
self.session.prepare()
102+
self.set_outputs(self.session.output_size())
103+
self._prepared = True
104+
92105
# Configure input buffers using the current session's input size and provided data.
93106
self.set_inputs(expected_input_size, inputs_array)
94107
# Execute the inference.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import numpy as np
2+
import random
3+
import sys
4+
from onert import infer
5+
6+
7+
def main(nnpackage_path, backends="cpu"):
8+
# Create session and load nnpackage
9+
session = infer.session(nnpackage_path, backends)
10+
11+
# Prepare input. Here we just allocate dummy input arrays.
12+
input_infos = session.get_inputs_tensorinfo()
13+
14+
# Call infer() 10 times
15+
for i in range(10):
16+
dummy_inputs = []
17+
for info in input_infos:
18+
# Retrieve the dimensions list from tensorinfo property.
19+
dims = list(info.dims)
20+
# Replace -1 with a random value between 1 and 10
21+
dims = [random.randint(1, 10) if d == -1 else d for d in dims]
22+
# Build the shape tuple from tensorinfo dimensions.
23+
print(dims)
24+
shape = tuple(dims[:info.rank])
25+
# Create a dummy numpy array filled with uniform random values in [0,1).
26+
dummy_inputs.append(
27+
np.random.uniform(low=0.0, high=1.0, size=shape).astype(info.dtype))
28+
29+
print(dummy_inputs)
30+
outputs = session.infer(dummy_inputs)
31+
print(outputs)
32+
print(f"Inference run {i+1}/10 completed.")
33+
34+
print(f"nnpackage {nnpackage_path.split('/')[-1]} runs successfully.")
35+
return
36+
37+
38+
if __name__ == "__main__":
39+
argv = sys.argv[1:]
40+
main(*argv)

0 commit comments

Comments
 (0)