Skip to content

Commit

Permalink
Merge pull request #2 from zpye/pybind11
Browse files Browse the repository at this point in the history
Pybind11
  • Loading branch information
zpye committed May 24, 2023
2 parents 5880e9b + 4799348 commit c332388
Show file tree
Hide file tree
Showing 19 changed files with 266 additions and 49 deletions.
9 changes: 9 additions & 0 deletions 3rdparty/xmake.lua
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,12 @@ target("highway")
set_kind("static")
add_includedirs("highway/", { public = true })
add_files("highway/hwy/*.cc|*_test.cc") -- no contrib

if has_config("build_python") then
target("pybind11")
set_kind("headeronly")
add_includedirs("pybind11/include/", "$(env PYTHON_ROOT)/include/", { public = true })
add_headerfiles("pybind11/include/**.h")
add_linkdirs("$(env PYTHON_ROOT)/libs/", { public = true })
add_links("python3")
end
49 changes: 43 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,50 @@ SimpleInfer uses [xmake](https://xmake.io/#/) to build library and tests.
```shell
git clone --recursive https://github.com/zpye/SimpleInfer.git
cd SimpleInfer
xmake config -m release
xmake
xmake config -a x64 -m release
xmake -w --all
```
## Run

After building successfully, run test-engin to check.
After building successfully, run test-yolo to check.

```shell
xmake run test-engine
xmake run test-yolo
```

## YOLO Result

Here are visualized results of YOLO detection.

![result_31.jpg](imgs/result_31.jpg)

![result_bus.jpg](imgs/result_bus.jpg)

![result_car.jpg](imgs/result_car.jpg)

![result_zidane.jpg](imgs/result_zidane.jpg)

## Working With Python

1. Set environment `PYTHON_ROOT` where `python` binary exists, pybind11 needs `${PYTHON_ROOT}/include` and `${PYTHON_ROOT}/libs` for compiling and linking.

2. Set `--build_python=true` after `xmake config` and build:

```shell
xmake config -a x64 -m release --builf_python=true
xmake -w --all
```

3. install package by pip:

```shell
pip install build/python/
```

4. run python test:

```shell
python test/test_python/test_model.py
```

## Reference
Expand All @@ -32,8 +67,10 @@ xmake run test-engine

[CGraph](https://github.com/ChunelFeng/CGraph) -> graph based pipeline

[highway](https://github.com/google/highway), [Simd](https://github.com/ermig1979/Simd.git) -> SIMD, GEMM, Winograd, parallel
[highway](https://github.com/google/highway), [Simd](https://github.com/ermig1979/Simd) -> SIMD, GEMM, Winograd, parallel

[benchmark](https://github.com/google/benchmark), [Catch2](https://github.com/catchorg/Catch2) -> benchmark and unit tests

[tmp](https://github.com/zjhellofss/tmp.git) -> models
[pybind11](https://github.com/pybind/pybind11) -> python bindings of c++

[tmp](https://github.com/zjhellofss/tmp) -> pnnx models
1 change: 1 addition & 0 deletions imgs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
yolo_result_*
Binary file added imgs/result_31.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added imgs/result_bus.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added imgs/result_car.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added imgs/result_zidane.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
68 changes: 68 additions & 0 deletions python/pybind11_main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#include <pybind11/eigen/tensor.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>

#include "engine.h"
#include "tensor.h"
#include "types.h"

using namespace SimpleInfer;

namespace py = pybind11;

PYBIND11_MODULE(simpleinfer, m) {
m.doc() = "pybind11 SimpleInfer";

m.def("InitializeContext", &InitializeContext);

py::enum_<DataType>(m, "DataType")
.value("None", DataType::kNone)
.value("Float32", DataType::kFloat32);

py::enum_<Status>(m, "Status")
.value("Success", Status::kSuccess)
.value("Fail", Status::kFail)
.value("Empty", Status::kEmpty)
.value("ErrorShape", Status::kErrorShape)
.value("ErrorContext", Status::kErrorContext)
.value("Unsupport", Status::kUnsupport);

py::class_<Tensor>(m, "Tensor")
.def(py::init<>())
.def(py::init<DataType, std::vector<int>>())
.def("GetDataType",
static_cast<const DataType (Tensor::*)() const>(
&Tensor::GetDataType))
.def("Shape",
static_cast<const std::vector<int>& (Tensor::*)() const>(
&Tensor::Shape))
.def("SetTensorDim4",
static_cast<Status (Tensor::*)(const EigenTensorMap<float, 4>&)>(
&Tensor::SetEigenTensor<float, 4>),
py::return_value_policy::reference)
.def("GetTensorDim4",
static_cast<EigenTensorMap<float, 4> (Tensor::*)() const>(
&Tensor::GetEigenTensor<float, 4>),
py::return_value_policy::reference);

py::class_<Engine>(m, "Engine")
.def(py::init<>())
.def("LoadModel",
static_cast<Status (Engine::*)(const std::string&,
const std::string&)>(
&Engine::LoadModel))
.def("Release", static_cast<Status (Engine::*)()>(&Engine::Release))
.def("InputNames",
static_cast<const std::vector<std::string> (Engine::*)()>(
&Engine::InputNames))
.def("OutputNames",
static_cast<const std::vector<std::string> (Engine::*)()>(
&Engine::OutputNames))
.def("Input",
static_cast<Status (Engine::*)(const std::string&, const Tensor&)>(
&Engine::Input))
.def("Forward", static_cast<Status (Engine::*)()>(&Engine::Forward))
.def("Extract",
static_cast<Status (Engine::*)(const std::string&, Tensor&)>(
&Engine::Extract));
}
13 changes: 13 additions & 0 deletions python/setup.py.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from setuptools import setup, find_packages

setup(name='SimpleInfer',
version='0.1',
description='SimpleInfer is a simple neural network inference framework.',
author='zpye',
author_email='[email protected]',
url='https://github.com/zpye/SimpleInfer',
packages=['simpleinfer'],
package_dir={'': '.'},
package_data={'simpleinfer': ['simpleinfer.pyd']},
install_requires=['numpy']
)
2 changes: 2 additions & 0 deletions python/simpleinfer/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# SimpleInfer
from .simpleinfer import *
3 changes: 2 additions & 1 deletion src/engine_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,8 @@ Status EngineImpl::AllocateTensorMemory() {

Status ret = tensor_node_iter.second->tensor.Allocate();
if (Status::kSuccess != ret) {
LOG(ERROR) << "allocate tensor memory fail";
LOG(ERROR) << "allocate tensor [" << tensor_node_iter.first
<< "] memory fail";
return ret;
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/pnnx/expand_expression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ static std::string expand_expression(Graph& graph, const Operator* op, int& pnnx
op_unary_out->producer = op_unary;

op_unary_out->shape = op_unary_in->shape;
op_unary_out->type = op_unary_in->type;

op_unary->inputs.push_back(op_unary_in);
op_unary->outputs.push_back(op_unary_out);
Expand Down Expand Up @@ -211,6 +212,7 @@ static std::string expand_expression(Graph& graph, const Operator* op, int& pnnx
op_binary_out->producer = op_binary;

op_binary_out->shape = op_binary_inb->shape;
op_binary_out->type = op_binary_inb->type;

op_binary->inputs.push_back(op_binary_inb);
op_binary->outputs.push_back(op_binary_out);
Expand All @@ -235,6 +237,7 @@ static std::string expand_expression(Graph& graph, const Operator* op, int& pnnx
op_binary_out->producer = op_binary;

op_binary_out->shape = op_binary_ina->shape;
op_binary_out->type = op_binary_ina->type;

op_binary->inputs.push_back(op_binary_ina);
op_binary->outputs.push_back(op_binary_out);
Expand Down Expand Up @@ -272,6 +275,8 @@ static std::string expand_expression(Graph& graph, const Operator* op, int& pnnx
}
op_binary_out->shape = out_shape;

op_binary_out->type = op_binary_ina->type;

op_binary->inputs.push_back(op_binary_ina);
op_binary->inputs.push_back(op_binary_inb);
op_binary->outputs.push_back(op_binary_out);
Expand Down
4 changes: 4 additions & 0 deletions src/tensor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include <cstdlib>

#include "logger.h"

namespace SimpleInfer {

Tensor::Tensor() {}
Expand Down Expand Up @@ -58,6 +60,8 @@ Status Tensor::Allocate() {
}
}

LOG(ERROR) << "Tensor Allocate Fail, size " << total_size;

return Status::kFail;
}

Expand Down
20 changes: 16 additions & 4 deletions test/test_classify/test_classify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,22 @@ int main() {
Tensor output;
engine.Extract("122", output);

EigenTensorMap<float, 4> result = output.GetEigenTensor<float, 4>();
EigenDSize<4> result_shape = result.dimensions();

// LOG(INFO) << result.shuffle(EigenDSize<4>(0, 3, 1, 2));
EigenTensorMap<float, 2> result = output.GetEigenTensor<float, 2>();
EigenDSize<2> result_shape = result.dimensions();

for (Eigen::DenseIndex b = 0; b < result_shape[0]; ++b) {
float score_max = -1.0f;
Eigen::DenseIndex index_max = -1;

for (Eigen::DenseIndex i = 0; i < result_shape[1]; ++i) {
if (result(b, i) > score_max) {
score_max = result(b, i);
index_max = i;
}
}

LOG(INFO) << absl::StrFormat("%d: (%d, %f)", b, index_max, score_max);
}

return 0;
}
24 changes: 12 additions & 12 deletions test/test_pnnx/test_pnnx_ir.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,24 +186,24 @@ void ShowGraph(const pnnx::Graph& graph) {

LOG(INFO) << ">>>>>>>>>> operand >>>>>>>>>>>>";

// i = 0;
// for (auto operand : graph.operands) {
// LOG(INFO) << ">>>>>>>>>>>>>>>>>>>>>>>>>";
// LOG(INFO) << absl::StrFormat("operand %d", i);
// ShowOperand(operand, "");

// i += 1;
// }
i = 0;
for (auto operand : graph.operands) {
LOG(INFO) << ">>>>>>>>>>>>>>>>>>>>>>>>>";
LOG(INFO) << absl::StrFormat("operand %d", i);
ShowOperand(operand, "");

i += 1;
}
}

int main() {
SimpleInfer::InitializeLogger();

pnnx::Graph graph;
int ret = graph.load(model_path + "/resnet/resnet18_batch1.param",
model_path + "/resnet/resnet18_batch1.pnnx.bin");
// int ret = graph.load(model_path + "/add/resnet_add3.pnnx.param",
// model_path + "/add/resnet_add3.pnnx.bin");
// int ret = graph.load(model_path + "/resnet/resnet18_batch1.param",
// model_path + "/resnet/resnet18_batch1.pnnx.bin");
int ret = graph.load(model_path + "/add/resnet_add3.pnnx.param",
model_path + "/add/resnet_add3.pnnx.bin");
// int ret = graph.load(model_path + "/yolo/demo/yolov5s_batch8.pnnx.param",
// model_path + "/yolo/demo/yolov5s_batch8.pnnx.bin");

Expand Down
41 changes: 41 additions & 0 deletions test/test_python/test_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import simpleinfer as infer
import numpy as np
import os

if __name__ == '__main__':
infer.InitializeContext()

engine = infer.Engine()

cur_file_path = os.path.dirname(os.path.realpath(__file__))
model_path = cur_file_path + '/../../3rdparty/tmp/yolo/demo/'
rc = engine.LoadModel(model_path + 'yolov5n_small.pnnx.param',
model_path + 'yolov5n_small.pnnx.bin')
print('LoadModel', rc)

input_names = engine.InputNames()
output_names = engine.OutputNames()
print('input_names: ', input_names)
print('output_names: ', output_names)

# input
input_shape = [4, 320, 320, 3] # NHWC
input_np = np.ones(input_shape, dtype = np.float32) * 42.0
input_tensor = infer.Tensor(infer.DataType.Float32, input_shape)
rc = input_tensor.SetTensorDim4(input_np)
print('SetTensorDim4', rc)

rc = engine.Input(input_names[0], input_tensor)
print('Input', rc)

rc = engine.Forward()
print('Forward', rc)

output_tensor = infer.Tensor()
rc = engine.Extract(output_names[0], output_tensor)
print('Extract', rc)

output_np = output_tensor.GetTensorDim4()
print(output_np.dtype, output_np.shape)

print(output_np[0, 0, 0, :])
Loading

0 comments on commit c332388

Please sign in to comment.