Skip to content

Commit

Permalink
x[Draft] Experimental Python API
Browse files Browse the repository at this point in the history
ONE-DCO-1.0-Signed-off-by: ragmani <ragmani0216@gmail.com>
  • Loading branch information
ragmani committed Jan 3, 2025
1 parent 28a44e2 commit ae0f71a
Show file tree
Hide file tree
Showing 35 changed files with 1,751 additions and 83 deletions.
2 changes: 2 additions & 0 deletions infra/nnfw/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)

### CMAKE_BUILD_TYPE_LC: Build type lower case
string(TOLOWER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_LC)
# string(TOLOWER ${CMAKE_BUILD_TYPE} "debug")
set(CMAKE_CXX_FLAGS_DEBUG "-g")

set(NNAS_PROJECT_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../.." CACHE
INTERNAL "Where to find nnas top-level source directory"
Expand Down
27 changes: 19 additions & 8 deletions infra/nnfw/python/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,23 @@

# copy *py files to package_directory
PY_DIR = os.path.join(THIS_FILE_DIR, '../../../runtime/onert/api/python/package')
for py_file in os.listdir(PY_DIR):
if py_file.endswith(".py"):
src_path = os.path.join(PY_DIR, py_file)
dest_path = os.path.join(THIS_FILE_DIR, package_directory)
shutil.copy(src_path, dest_path)
print(f"Copied '{src_path}' to '{dest_path}'")
for root, dirs, files in os.walk(PY_DIR):
# Calculate the relative path from the source directory
rel_path = os.path.relpath(root, PY_DIR)
dest_dir = os.path.join(THIS_FILE_DIR, package_directory)
dest_sub_dir = os.path.join(dest_dir, rel_path)
print(f"dest_sub_dir '{dest_sub_dir}'")

# Ensure the corresponding destination subdirectory exists
os.makedirs(dest_sub_dir, exist_ok=True)

# Copy only .py files
for py_file in files:
if py_file.endswith(".py"):
src_path = os.path.join(root, py_file)
# dest_path = os.path.join(THIS_FILE_DIR, package_directory)
shutil.copy(src_path, dest_sub_dir)
print(f"Copied '{src_path}' to '{dest_sub_dir}'")

# remove architecture directory
if os.path.exists(package_directory):
Expand Down Expand Up @@ -136,12 +147,12 @@ def get_directories():
# copy .so files to architecture directories

setup(name=package_name,
version='0.1.0',
version='0.2.0',
description='onert API binding',
long_description='It provides onert Python api',
url='https://github.com/Samsung/ONE',
license='Apache-2.0, MIT, BSD-2-Clause, BSD-3-Clause, Mozilla Public License 2.0',
has_ext_modules=lambda: True,
packages=[package_directory],
packages=find_packages(),
package_data={package_directory: so_list},
install_requires=['numpy >= 1.19'])
76 changes: 76 additions & 0 deletions runtime/onert/api/python/include/nnfw_api_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,24 @@
* limitations under the License.
*/

#ifndef __ONERT_API_PYTHON_NNFW_API_WRAPPER_H__
#define __ONERT_API_PYTHON_NNFW_API_WRAPPER_H__

#include "nnfw.h"
#include "nnfw_experimental.h"

#include <pybind11/stl.h>
#include <pybind11/numpy.h>

namespace py = pybind11;

namespace onert
{
namespace api
{
namespace python
{

/**
* @brief tensor info describes the type and shape of tensors
*
Expand Down Expand Up @@ -120,6 +131,7 @@ class NNFW_SESSION

void close_session();
void set_input_tensorinfo(uint32_t index, const tensorinfo *tensor_info);
void prepare();
void run();
void run_async();
void wait();
Expand Down Expand Up @@ -159,4 +171,68 @@ class NNFW_SESSION
void set_output_layout(uint32_t index, const char *layout);
tensorinfo input_tensorinfo(uint32_t index);
tensorinfo output_tensorinfo(uint32_t index);

//////////////////////////////////////////////
// Experimental APIs for training
//////////////////////////////////////////////
nnfw_train_info train_get_traininfo();
void train_set_traininfo(const nnfw_train_info *info);

template <typename T> void train_set_input(uint32_t index, py::array_t<T> &buffer)
{
nnfw_tensorinfo tensor_info;
nnfw_input_tensorinfo(this->session, index, &tensor_info);

py::buffer_info buf_info = buffer.request();
const auto buf_shape = buf_info.shape;
assert(tensor_info.rank == static_cast<int32_t>(buf_shape.size()) && buf_shape.size() > 0);
tensor_info.dims[0] = static_cast<int32_t>(buf_shape.at(0));

ensure_status(nnfw_train_set_input(this->session, index, buffer.request().ptr, &tensor_info));
}
template <typename T> void train_set_expected(uint32_t index, py::array_t<T> &buffer)
{
nnfw_tensorinfo tensor_info;
nnfw_output_tensorinfo(this->session, index, &tensor_info);

py::buffer_info buf_info = buffer.request();
const auto buf_shape = buf_info.shape;
assert(tensor_info.rank == static_cast<int32_t>(buf_shape.size()) && buf_shape.size() > 0);
tensor_info.dims[0] = static_cast<int32_t>(buf_shape.at(0));

ensure_status(
nnfw_train_set_expected(this->session, index, buffer.request().ptr, &tensor_info));
}
template <typename T> void train_set_output(uint32_t index, py::array_t<T> &buffer)
{
nnfw_tensorinfo tensor_info;
nnfw_output_tensorinfo(this->session, index, &tensor_info);
NNFW_TYPE type = tensor_info.dtype;
uint32_t output_elements = num_elems(&tensor_info);
size_t length = sizeof(T) * output_elements;

ensure_status(nnfw_train_set_output(session, index, type, buffer.request().ptr, length));
}

void train_prepare();
void train(bool update_weights);
float train_get_loss(uint32_t index);

void train_export_circle(const py::str &path);
void train_import_checkpoint(const py::str &path);
void train_export_checkpoint(const py::str &path);

//////////////////////////////////////////////
// Optional APIs for training
//////////////////////////////////////////////
// nnfw_tensorinfo train_input_tensorinfo(uint32_t index);
// nnfw_tensorinfo train_expected_tensorinfo(uint32_t index);

// TODO Add other apis
};

} // namespace python
} // namespace api
} // namespace onert

#endif // __ONERT_API_PYTHON_NNFW_API_WRAPPER_H__
28 changes: 28 additions & 0 deletions runtime/onert/api/python/include/nnfw_session_bindings.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright (c) 2024 Samsung Electronics Co., Ltd. All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#ifndef __ONERT_API_PYTHON_NNFW_SESSION_BINDINGS_H__
#define __ONERT_API_PYTHON_NNFW_SESSION_BINDINGS_H__

#include <pybind11/pybind11.h>

// Declare binding common functions
void bind_nnfw_session(pybind11::module_ &m);

// Declare binding experimental functinos
void bind_experimental_nnfw_session(pybind11::module_ &m);

#endif // __ONERT_API_PYTHON_NNFW_SESSION_BINDINGS_H__
25 changes: 25 additions & 0 deletions runtime/onert/api/python/include/nnfw_tensorinfo_bindings.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright (c) 2024 Samsung Electronics Co., Ltd. All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#ifndef __ONERT_API_PYTHON_NNFW_TENSORINFO_BINDINGS_H__
#define __ONERT_API_PYTHON_NNFW_TENSORINFO_BINDINGS_H__

#include <pybind11/pybind11.h>

// Declare binding tensorinfo
void bind_tensorinfo(pybind11::module_ &m);

#endif // __ONERT_API_PYTHON_NNFW_TENSORINFO_BINDINGS_H__
34 changes: 34 additions & 0 deletions runtime/onert/api/python/include/nnfw_traininfo_bindings.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright (c) 2024 Samsung Electronics Co., Ltd. All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#ifndef __ONERT_API_PYTHON_NNFW_TRAININFO_BINDINGS_H__
#define __ONERT_API_PYTHON_NNFW_TRAININFO_BINDINGS_H__

#include <pybind11/pybind11.h>
#include <pybind11/stl.h>

namespace py = pybind11;

// Declare binding train enums
void bind_nnfw_train_enums(py::module_ &m);

// Declare binding loss info
void bind_nnfw_loss_info(py::module_ &m);

// Declare binding train info
void bind_nnfw_train_info(py::module_ &m);

#endif // __ONERT_API_PYTHON_NNFW_TRAININFO_BINDINGS_H__
17 changes: 15 additions & 2 deletions runtime/onert/api/python/package/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,15 @@
__all__ = ['infer']
from . import infer
# Define the public API of the onert package
# __all__ = ["infer", "train"]
__all__ = ["infer", "tensorinfo", "train"]
# __all__ = ["tensorinfo", "train"]

# Import and expose the infer module's functionalities
from . import infer as infer
# from . import session as infer, tensorinfo

# Import and expose tensorinfo
from .infer import tensorinfo as tensorinfo

# Import and expose the train module's functionalities
# from . import train
from . import train
3 changes: 3 additions & 0 deletions runtime/onert/api/python/package/common/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .basesession import BaseSession

__all__ = ["BaseSession"]
89 changes: 89 additions & 0 deletions runtime/onert/api/python/package/common/basesession.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import numpy as np


def num_elems(tensor_info):
"""Get the total number of elements in nnfw_tensorinfo.dims."""
n = 1
for x in range(tensor_info.rank):
n *= tensor_info.dims[x]
return n


class BaseSession:
"""
Base class providing common functionality for inference and training sessions.
"""
def __init__(self, backend_session):
"""
Initialize the BaseSession with a backend session.
Args:
backend_session: A backend-specific session object (e.g., nnfw_session).
"""
self.session = backend_session
self.inputs = []
self.outputs = []

def __getattr__(self, name):
"""
Delegate attribute access to the bound NNFW_SESSION instance.
Args:
name (str): The name of the attribute or method to access.
Returns:
The attribute or method from the bound NNFW_SESSION instance.
"""
return getattr(self.session, name)

# def num_elems(self, tensor_info):
# """
# Get the total number of elements in a tensor.

# Args:
# tensor_info: Tensor information object.

# Returns:
# int: Total number of elements in the tensor.
# """
# n = 1
# for x in range(tensor_info.rank):
# n *= tensor_info.dims[x]
# return n

def set_inputs(self, size, inputs_array=[]):
"""
Set the input tensors for the session.
Args:
size (int): Number of input tensors.
inputs_array (list): List of numpy arrays for the input data.
"""
for i in range(size):
input_tensorinfo = self.session.input_tensorinfo(i)

if len(inputs_array) > i:
input_array = np.array(inputs_array[i], dtype=input_tensorinfo.dtype)
else:
print(
f"Model's input size is {size}, but given inputs_array size is {len(inputs_array)}.\n{i}-th index input is replaced by an array filled with 0."
)
input_array = np.zeros((num_elems(input_tensorinfo)),
dtype=input_tensorinfo.dtype)

self.session.set_input(i, input_array)
self.inputs.append(input_array)

def set_outputs(self, size):
"""
Set the output tensors for the session.
Args:
size (int): Number of output tensors.
"""
for i in range(size):
output_tensorinfo = self.session.output_tensorinfo(i)
output_array = np.zeros((num_elems(output_tensorinfo)),
dtype=output_tensorinfo.dtype)
self.session.set_output(i, output_array)
self.outputs.append(output_array)
Loading

0 comments on commit ae0f71a

Please sign in to comment.