nncase Model Simulator API Manual#
Overview#
In addition to the model compilation API, nncase also provides APIs for inferring models, using Python on PC to infer kmodel generated by compiled models, to verify whether the inference results of nncase are consistent with the results generated by the runtime of the corresponding deep learning framework. The API provided in this document is used to verify the correctness of kmodel conversion on a local PC, and is not the code running on k230. For learning about nncase, please refer to: nncase github repo .
API Introduction#
MemoryRange#
【Description】
MemoryRange class, used to represent memory range.
【Definition】
py::class_<memory_range>(m, "MemoryRange")
.def_readwrite("location", &memory_range::memory_location)
.def_property(
"dtype", [](const memory_range &range) { return to_dtype(range.datatype); },
[](memory_range &range, py::object dtype) { range.datatype = from_dtype(py::dtype::from_args(dtype)); })
.def_readwrite("start", &memory_range::start)
.def_readwrite("size", &memory_range::size);
【Properties】
Name |
Type |
Description |
|---|---|---|
location |
int |
Memory location, 0 means input, 1 means output, 2 means rdata, 3 means data, 4 means shared_data |
dtype |
python data type |
Data type |
start |
int |
Memory start address |
Size |
int |
Memory size |
【Example】
mr = nncase.MemoryRange()
RuntimeTensor#
【Description】
RuntimeTensor class, used to represent runtime tensor.
【Definition】
py::class_<runtime_tensor>(m, "RuntimeTensor")
.def_static("from_numpy", [](py::array arr) {
auto src_buffer = arr.request();
auto datatype = from_dtype(arr.dtype());
auto tensor = host_runtime_tensor::create(
datatype,
to_rt_shape(src_buffer.shape),
to_rt_strides(src_buffer.itemsize, src_buffer.strides),
gsl::make_span(reinterpret_cast<gsl::byte *>(src_buffer.ptr), src_buffer.size * src_buffer.itemsize),
[=](gsl::byte *) { arr.dec_ref(); })
.unwrap_or_throw();
arr.inc_ref();
return tensor;
})
.def("copy_to", [](runtime_tensor &from, runtime_tensor &to) {
from.copy_to(to).unwrap_or_throw();
})
.def("to_numpy", [](runtime_tensor &tensor) {
auto host = tensor.as_host().unwrap_or_throw();
auto src_map = std::move(hrt::map(host, hrt::map_read).unwrap_or_throw());
auto src_buffer = src_map.buffer();
return py::array(
to_dtype(tensor.datatype()),
tensor.shape(),
to_py_strides(runtime::get_bytes(tensor.datatype()), tensor.strides()),
src_buffer.data());
})
.def_property_readonly("dtype", [](runtime_tensor &tensor) {
return to_dtype(tensor.datatype());
})
.def_property_readonly("shape", [](runtime_tensor &tensor) {
return to_py_shape(tensor.shape());
})
【Properties】
Name |
Type |
Description |
|---|---|---|
dtype |
python data type |
Tensor’s data type |
shape |
list |
Tensor’s shape |
from_numpy#
【Description】
Construct a RuntimeTensor object from numpy.ndarray.
【Definition】
from_numpy(py::array arr)
【Parameters】
Name |
Type |
Description |
|---|---|---|
Arr |
numpy.ndarray |
numpy.ndarray object |
【Return Value】
RuntimeTensor object.
【Example】
tensor = nncase.RuntimeTensor.from_numpy(self.inputs[i]['data'])
copy_to#
【Description】
Copy RuntimeTensor.
【Definition】
copy_to(RuntimeTensor to)
【Parameters】
Name |
Type |
Description |
|---|---|---|
to |
RuntimeTensor |
RuntimeTensor object |
【Return Value】
None.
【Example】
sim.get_output_tensor(i).copy_to(to)
to_numpy#
【Description】
Convert RuntimeTensor to numpy.ndarray object.
【Definition】
to_numpy()
【Parameters】
None.
【Return Value】
numpy.ndarray object.
【Example】
arr = sim.get_output_tensor(i).to_numpy()
Simulator#
【Description】
Simulator class, used to infer kmodel on PC.
【Definition】
py::class_<interpreter>(m, "Simulator")
.def(py::init())
.def("load_model", [](interpreter &interp, gsl::span<const gsl::byte> buffer) { interp.load_model(buffer).unwrap_or_throw(); })
.def_property_readonly("inputs_size", &interpreter::inputs_size)
.def_property_readonly("outputs_size", &interpreter::outputs_size)
.def("get_input_desc", &interpreter::input_desc)
.def("get_output_desc", &interpreter::output_desc)
.def("get_input_tensor", [](interpreter &interp, size_t index) { return interp.input_tensor(index).unwrap_or_throw(); })
.def("set_input_tensor", [](interpreter &interp, size_t index, runtime_tensor tensor) { return interp.input_tensor(index, tensor).unwrap_or_throw(); })
.def("get_output_tensor", [](interpreter &interp, size_t index) { return interp.output_tensor(index).unwrap_or_throw(); })
.def("set_output_tensor", [](interpreter &interp, size_t index, runtime_tensor tensor) { return interp.output_tensor(index, tensor).unwrap_or_throw(); })
.def("run", [](interpreter &interp) { interp.run().unwrap_or_throw(); })
【Properties】
Name |
Type |
Description |
|---|---|---|
inputs_size |
int |
Number of inputs |
outputs_size |
int |
Number of outputs |
【Example】
sim = nncase.Simulator()
load_model#
【Description】
Load kmodel.
【Definition】
load_model(model_content)
【Parameters】
Name |
Type |
Description |
|---|---|---|
model_content |
byte[] |
kmodel byte stream |
【Return Value】
None.
【Example】
sim.load_model(kmodel)
get_input_desc#
【Description】
Get the description information of the input at the specified index.
【Definition】
get_input_desc(index)
【Parameters】
Name |
Type |
Description |
|---|---|---|
index |
int |
Index of the input |
【Return Value】
MemoryRange
【Example】
input_desc_0 = sim.get_input_desc(0)
get_output_desc#
【Description】
Get the description information of the output at the specified index.
【Definition】
get_output_desc(index)
【Parameters】
Name |
Type |
Description |
|---|---|---|
index |
int |
Index of the output |
【Return Value】
MemoryRange
【Example】
output_desc_0 = sim.get_output_desc(0)
get_input_tensor#
【Description】
Get the RuntimeTensor of the input at the specified index.
【Definition】
get_input_tensor(index)
【Parameters】
Name |
Type |
Description |
|---|---|---|
index |
int |
Index of the input tensor |
【Return Value】
RuntimeTensor
【Example】
input_tensor_0 = sim.get_input_tensor(0)
set_input_tensor#
【Description】
Set the RuntimeTensor of the input at the specified index.
【Definition】
set_input_tensor(index, tensor)
【Parameters】
Name |
Type |
Description |
|---|---|---|
index |
int |
Index of the input tensor |
tensor |
RuntimeTensor |
Input tensor |
【Return Value】
None.
【Example】
sim.set_input_tensor(0, nncase.RuntimeTensor.from_numpy(self.inputs[0]['data']))
get_output_tensor#
【Description】
Get the RuntimeTensor of the output at the specified index.
【Definition】
get_output_tensor(index)
【Parameters】
Name |
Type |
Description |
|---|---|---|
index |
int |
Index of the output tensor |
【Return Value】
RuntimeTensor
【Example】
output_arr_0 = sim.get_output_tensor(0).to_numpy()
set_output_tensor#
【Description】
Set the RuntimeTensor of the output at the specified index.
【Definition】
set_output_tensor(index, tensor)
【Parameters】
Name |
Type |
Description |
|---|---|---|
index |
int |
Index of the output tensor |
tensor |
RuntimeTensor |
Output tensor |
【Return Value】
None.
【Example】
sim.set_output_tensor(0, tensor)
run#
【Description】
Run kmodel inference.
【Definition】
run()
【Parameters】
None.
【Return Value】
None.
【Example】
sim.run()
Example#
Prerequisites: The yolov5s.onnx model has been compiled by the yolov5s_onnx.py script.
yolov5s_onnx_simu.py is located in the src/rtsmart/libs/nncase/examples/scripts subdirectory, with the following content:
import os
import copy
import argparse
import numpy as np
import onnx
import onnxruntime as ort
import nncase
def read_model_file(model_file):
with open(model_file, 'rb') as f:
model_content = f.read()
return model_content
def cosine(gt, pred):
return (gt @ pred) / (np.linalg.norm(gt, 2) * np.linalg.norm(pred, 2))
def main():
parser = argparse.ArgumentParser(prog="nncase")
parser.add_argument("--model", type=str, help='original model file')
parser.add_argument("--model_input", type=str, help='input bin file for original model')
parser.add_argument("--kmodel", type=str, help='kmodel file')
parser.add_argument("--kmodel_input", type=str, help='input bin file for kmodel')
args = parser.parse_args()
# cpu inference
ort_session = ort.InferenceSession(args.model)
output_names = []
model_outputs = ort_session.get_outputs()
for i in range(len(model_outputs)):
output_names.append(model_outputs[i].name)
model_input = ort_session.get_inputs()[0]
model_input_name = model_input.name
model_input_type = np.float32
model_input_shape = model_input.shape
model_input_data = np.fromfile(args.model_input, model_input_type).reshape(model_input_shape)
cpu_results = []
cpu_results = ort_session.run(output_names, { model_input_name : model_input_data })
# create simulator
sim = nncase.Simulator()
# read kmodel
kmodel = read_model_file(args.kmodel)
# load kmodel
sim.load_model(kmodel)
# read input.bin
# input_tensor=sim.get_input_tensor(0).to_numpy()
dtype = sim.get_input_desc(0).dtype
input = np.fromfile(args.kmodel_input, dtype).reshape([1, 3, 320, 320])
# set input for simulator
sim.set_input_tensor(0, nncase.RuntimeTensor.from_numpy(input))
# simulator inference
nncase_results = []
sim.run()
for i in range(sim.outputs_size):
nncase_result = sim.get_output_tensor(i).to_numpy()
nncase_results.append(copy.deepcopy(nncase_result))
# compare
for i in range(sim.outputs_size):
cos = cosine(np.reshape(nncase_results[i], (-1)), np.reshape(cpu_results[i], (-1)))
print('output {0} cosine similarity : {1}'.format(i, cos))
if __name__ == '__main__':
main()
Execute the inference script
root@5f718e19f8a7:/mnt/# cd rtos_sdk/src/rtsmart/libs/nncase/examples
root@5f718e19f8a7:/mnt/rtos_sdk/src/rtsmart/libs/nncase/examples # export PATH=$PATH:/usr/local/lib/python3.8/dist-packages/
root@5f718e19f8a7:/mnt/rtos_sdk/src/rtsmart/libs/nncase/examples # python3 scripts/yolov5s_onnx_simu.py --model models/yolov5s.onnx --model_input object_detect/data/input_fp32.bin --kmodel tmp/yolov5s_onnx/test.kmodel --kmodel_input object_detect/data/input_uint8.bin
The comparison between the nncase simulator and CPU inference results is as follows:
output 0 cosine similarity : 0.9997244477272034
output 1 cosine similarity : 0.999757707118988
output 2 cosine similarity : 0.9997308850288391
