nncase Model Simulator API Manual#
Overview#
In addition to the model compilation API, nncase also provides an API for inferring models. It uses Python to infer the kmodel generated by compiling the model on a PC, which is used 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 code that runs on k230. For learning about nncase, please refer to: nncase github repo.
API Introduction#
MemoryRange#
【Description】
The MemoryRange class, used to represent a 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 indicates input, 1 indicates output, 2 indicates rdata, 3 indicates data, 4 indicates shared_data |
dtype |
python data type |
Data type |
start |
int |
Memory start address |
size |
int |
Memory size |
【Example】
mr = nncase.MemoryRange()
RuntimeTensor#
【Description】
The RuntimeTensor class, used to represent a 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】
Constructs a RuntimeTensor object from a numpy.ndarray.
【Definition】
from_numpy(py::array arr)
【Parameters】
Name |
Type |
Description |
|---|---|---|
arr |
numpy.ndarray |
numpy.ndarray object |
【Return Value】
A RuntimeTensor object.
【Example】
tensor = nncase.RuntimeTensor.from_numpy(self.inputs[i]['data'])
copy_to#
【Description】
Copies the 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】
Converts the RuntimeTensor to a numpy.ndarray object.
【Definition】
to_numpy()
【Parameters】
None.
【Return Value】
A numpy.ndarray object.
【Example】
arr = sim.get_output_tensor(i).to_numpy()
Simulator#
【Description】
The Simulator class, used to infer a kmodel on a 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】
Loads a 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】
Gets 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】
Gets 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】
Gets 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】
Sets 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】
Gets 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】
Sets 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】
Runs kmodel inference.
【Definition】
run()
【Parameters】
None.
【Return Value】
None.
【Example】
sim.run()
Example#
Assuming that a certain onnx model has been compiled into a kmodel model, the simulation verification script is as follows:
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()
Using the input data from the saved bin file, calculate the cosine similarity between the inference results of onnx and kmodel, and you can verify the correctness of the model conversion.
