Browse Source

split load torchscript sources from pnnx, drop cxxabi hack, link torchscript with whole-archive (#5348)

tags/20240410
nihui GitHub 2 years ago
parent
commit
dcf94ae0cd
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
16 changed files with 1165 additions and 730 deletions
  1. +53
    -19
      tools/pnnx/CMakeLists.txt
  2. +79
    -41
      tools/pnnx/src/CMakeLists.txt
  3. +0
    -359
      tools/pnnx/src/ir.cpp
  4. +31
    -7
      tools/pnnx/src/ir.h
  5. +552
    -0
      tools/pnnx/src/load_torchscript.cpp
  6. +35
    -0
      tools/pnnx/src/load_torchscript.h
  7. +16
    -137
      tools/pnnx/src/main.cpp
  8. +371
    -40
      tools/pnnx/src/onnx.proto
  9. +1
    -1
      tools/pnnx/src/pass_level2/torch_permute.cpp
  10. +0
    -2
      tools/pnnx/src/pass_level2/torch_repeat_interleave.cpp
  11. +1
    -1
      tools/pnnx/src/pass_level5/fuse_layernorm.cpp
  12. +1
    -1
      tools/pnnx/src/pass_level5/fuse_scaled_dot_product_attention.cpp
  13. +14
    -28
      tools/pnnx/src/save_onnx.cpp
  14. +0
    -81
      tools/pnnx/src/save_onnx_cxxabi_bridge.cpp
  15. +0
    -11
      tools/pnnx/src/utils.cpp
  16. +11
    -2
      tools/pnnx/src/utils.h

+ 53
- 19
tools/pnnx/CMakeLists.txt View File

@@ -25,34 +25,21 @@ include(PNNXPyTorch)
# c++14 is required for using torch headers
set(CMAKE_CXX_STANDARD 14)

#set(CMAKE_BUILD_TYPE debug)
# set(CMAKE_BUILD_TYPE debug)
#set(CMAKE_BUILD_TYPE relwithdebinfo)
#set(CMAKE_BUILD_TYPE release)
# set(CMAKE_BUILD_TYPE release)

option(PNNX_COVERAGE "build for coverage" OFF)

#set(Torch_INSTALL_DIR "/home/nihui/.local/lib/python3.9/site-packages/torch" CACHE STRING "")
#set(Torch_INSTALL_DIR "/home/nihui/osd/pnnx/pytorch-v1.10.0/build/install" CACHE STRING "")
#set(Torch_INSTALL_DIR "/home/nihui/osd/pnnx/libtorch" CACHE STRING "")
# set(Torch_INSTALL_DIR "/home/nihui/osd/pnnx/install" CACHE STRING "")
set(TorchVision_INSTALL_DIR "/home/nihui/osd/vision/build/install" CACHE STRING "")

#set(Torch_DIR "${Torch_INSTALL_DIR}/share/cmake/Torch")
set(TorchVision_DIR "${TorchVision_INSTALL_DIR}/share/cmake/TorchVision")

find_package(protobuf CONFIG)

if(protobuf_FOUND)
set(PROTOBUF_FOUND ${protobuf_FOUND})
set(PROTOBUF_VERSION ${protobuf_VERSION})
else()
# fallback to system
find_package(Protobuf)
set(PROTOBUF_FOUND ${Protobuf_FOUND})
set(PROTOBUF_VERSION ${Protobuf_VERSION})
if(TARGET protobuf::protoc)
set_target_properties(protobuf::protoc PROPERTIES IMPORTED_LOCATION_RELEASE "${PROTOBUF_PROTOC_EXECUTABLE}")
endif()
endif()
# test if libtorch and protobuf has the same cxxabi version

find_package(Python3 COMPONENTS Interpreter Development)

@@ -75,15 +62,62 @@ if(Torch_VERSION VERSION_GREATER_EQUAL "2.1")
set(CMAKE_CXX_STANDARD 17)
endif()

if(TorchVision_FOUND)
# find torchvision library
find_library(TORCHVISION_LIBRARY torchvision PATHS "${TorchVision_INSTALL_DIR}/lib" "${TorchVision_INSTALL_DIR}/lib64")
if(TORCHVISION_LIBRARY)
message(STATUS "Found TorchVision: ${TORCHVISION_LIBRARY}")
if(APPLE)
list(APPEND TORCHVISION_LIBRARY "-Wl,-force_load,${TORCHVISION_LIBRARY}")
elseif(MSVC)
list(APPEND TORCHVISION_LIBRARY "-WHOLEARCHIVE:${TORCHVISION_LIBRARY}")
else()
list(APPEND TORCHVISION_LIBRARY "-Wl,--whole-archive ${TORCHVISION_LIBRARY} -Wl,--no-whole-archive")
endif()
set(TorchVision_FOUND TRUE)
message(STATUS "Building with TorchVision")
add_definitions(-DPNNX_TORCHVISION)
else()
message(WARNING "static library ${TORCHVISION_LIBRARY} not found.")
set(TorchVision_FOUND FALSE)
message(WARNING "Building without TorchVision")
endif()

include_directories(${TORCH_INCLUDE_DIRS})

if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
include(CheckCXXSourceCompiles)
set(CMAKE_REQUIRED_FLAGS "${TORCH_CXX_FLAGS}")
check_cxx_source_compiles("#include <cxxabi.h>\n#if _GLIBCXX_USE_CXX11_ABI\nint main() { return 0; }\n#endif" PNNX_TORCH_USE_CXX11_ABI)
unset(CMAKE_REQUIRED_FLAGS)
check_cxx_source_compiles("#include <cxxabi.h>\n#if _GLIBCXX_USE_CXX11_ABI\nint main() { return 0; }\n#endif" PNNX_COMPILER_USE_CXX11_ABI)
endif()

if((PNNX_TORCH_USE_CXX11_ABI AND PNNX_COMPILER_USE_CXX11_ABI) OR (NOT PNNX_TORCH_USE_CXX11_ABI AND NOT PNNX_COMPILER_USE_CXX11_ABI))
find_package(protobuf CONFIG)

if(protobuf_FOUND)
set(PROTOBUF_FOUND ${protobuf_FOUND})
set(PROTOBUF_VERSION ${protobuf_VERSION})
else()
# fallback to system
find_package(Protobuf)
set(PROTOBUF_FOUND ${Protobuf_FOUND})
set(PROTOBUF_VERSION ${Protobuf_VERSION})
if(TARGET protobuf::protoc)
set_target_properties(protobuf::protoc PROPERTIES IMPORTED_LOCATION_RELEASE "${PROTOBUF_PROTOC_EXECUTABLE}")
endif()
endif()
endif()

# set(onnxruntime_INSTALL_DIR "/home/nihui/osd/ncnn-nihui/tools/pnnx/build/src/test/onnxruntime-1.16.3/build/install" CACHE STRING "")
# set(onnxruntime_DIR "${onnxruntime_INSTALL_DIR}/lib64/cmake/onnxruntime")
#
# find_package(onnxruntime)
#
# message(STATUS "onnxruntime_VERSION = ${onnxruntime_VERSION}")
# message(STATUS "onnxruntime_VERSION_MAJOR = ${onnxruntime_VERSION_MAJOR}")
# message(STATUS "onnxruntime_VERSION_MINOR = ${onnxruntime_VERSION_MINOR}")
# message(STATUS "onnxruntime_VERSION_PATCH = ${onnxruntime_VERSION_PATCH}")

add_subdirectory(src)

enable_testing()


+ 79
- 41
tools/pnnx/src/CMakeLists.txt View File

@@ -572,52 +572,88 @@ if(PROTOBUF_FOUND)

if(Protobuf_FOUND OR protobuf_MODULE_COMPATIBLE)
protobuf_generate_cpp(ONNX_PROTO_SRCS ONNX_PROTO_HDRS onnx.proto)
add_library(onnxproto STATIC ${ONNX_PROTO_SRCS} ${ONNX_PROTO_HDRS})
target_include_directories(onnxproto PUBLIC ${PROTOBUF_INCLUDE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(onnxproto PUBLIC ${PROTOBUF_LIBRARIES})
else()
add_library(onnxproto STATIC onnx.proto)
target_include_directories(onnxproto PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
protobuf_generate(TARGET onnxproto)
target_link_libraries(onnxproto PUBLIC protobuf::libprotobuf)
endif()
endif()

add_library(pnnx2onnx STATIC
save_onnx.cpp
save_onnx_cxxabi_bridge.cpp
${ONNX_PROTO_SRCS} ${ONNX_PROTO_HDRS}
)
set(torch2pnnx_SRCS

pass_level0.cpp
pass_level1.cpp

target_include_directories(pnnx2onnx PRIVATE ${PROTOBUF_INCLUDE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(pnnx2onnx PRIVATE ${PROTOBUF_LIBRARIES})
${pnnx_pass_level0_SRCS}
${pnnx_pass_level1_SRCS}

# libtorch is usually compiled with old cxx11 abi
set_source_files_properties(save_onnx_cxxabi_bridge.cpp PROPERTIES COMPILE_FLAGS "${TORCH_CXX_FLAGS}")
load_torchscript.cpp
)

message(STATUS "Building with onnx-zero")
else()
add_library(torch2pnnx OBJECT ${torch2pnnx_SRCS})
target_compile_definitions(torch2pnnx PRIVATE BUILD_TORCH2PNNX)
target_compile_options(torch2pnnx PUBLIC "${TORCH_CXX_FLAGS}")

add_library(pnnx2onnx STATIC
save_onnx.cpp
save_onnx_cxxabi_bridge.cpp
onnx.proto
)
set_source_files_properties(save_onnx_cxxabi_bridge.cpp PROPERTIES COMPILE_FLAGS "${TORCH_CXX_FLAGS}")
target_include_directories(pnnx2onnx PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
protobuf_generate(TARGET pnnx2onnx)
target_link_libraries(pnnx2onnx PRIVATE protobuf::libprotobuf)
message(STATUS "Building with onnx-zero")
endif()
if(TorchVision_FOUND)
set_property(SOURCE load_torchscript.cpp APPEND PROPERTY COMPILE_DEFINITIONS PNNX_TORCHVISION)
endif()
if(PROTOBUF_FOUND)
add_library(pnnx2onnx STATIC
save_onnx.cpp
)
target_link_libraries(pnnx2onnx PRIVATE onnxproto)
message(STATUS "Building with onnx-zero")
else()
message(STATUS "Building without onnx-zero")
endif()

# if(onnxruntime_FOUND)
#
# set(pnnx_pass_onnx_SRCS
# pass_onnx/canonicalize.cpp
# pass_onnx/dead_code_elimination.cpp
# pass_onnx/fold_constants.cpp
# pass_onnx/inline_containers.cpp
# pass_onnx/model_stat.cpp
# pass_onnx/shape_inference.cpp
# )
#
# set(onnx2pnnx_SRCS
# ${pnnx_pass_onnx_SRCS}
# load_onnx.cpp
# )
#
# add_library(onnx2pnnx STATIC ${onnx2pnnx_SRCS})
# target_link_libraries(onnx2pnnx PRIVATE onnxproto onnxruntime::onnxruntime)
#
# target_compile_definitions(onnx2pnnx PRIVATE BUILD_ONNX2PNNX)
#
# message(STATUS "Building with dynamo-onnx")
# else()
# message(STATUS "Building without dynamo-onnx")
# endif()

if(NOT MSVC)
add_definitions(-Wall -Wextra)
endif()

set(pnnx_SRCS
main.cpp
ir.cpp
storezip.cpp
utils.cpp

pass_level0.cpp
pass_level1.cpp
pass_level2.cpp
pass_level3.cpp
pass_level4.cpp
pass_level5.cpp

${pnnx_pass_level0_SRCS}
${pnnx_pass_level1_SRCS}
${pnnx_pass_level2_SRCS}
${pnnx_pass_level3_SRCS}
${pnnx_pass_level4_SRCS}
@@ -628,36 +664,38 @@ set(pnnx_SRCS
${pnnx_pass_ncnn_SRCS}
)

if(NOT MSVC)
add_definitions(-Wall -Wextra)
endif()

add_executable(pnnx ${pnnx_SRCS})

target_compile_definitions(pnnx PRIVATE BUILD_PNNX)
set_property(SOURCE main.cpp APPEND PROPERTY COMPILE_DEFINITIONS BUILD_TORCH2PNNX)
target_link_libraries(pnnx PRIVATE torch2pnnx)

if(PNNX_COVERAGE)
target_compile_options(pnnx PUBLIC -coverage -fprofile-arcs -ftest-coverage)
target_link_libraries(pnnx PUBLIC -coverage -lgcov)
if(TorchVision_FOUND)
target_link_libraries(pnnx PRIVATE ${TORCHVISION_LIBRARY})
endif()

if(WIN32)
target_compile_definitions(pnnx PUBLIC NOMINMAX)
target_link_libraries(pnnx PRIVATE ${TORCH_LIBRARIES})
else()
target_link_libraries(pnnx PRIVATE ${TORCH_LIBRARIES} pthread dl)
endif()

if(PROTOBUF_FOUND)
target_compile_definitions(pnnx PRIVATE BUILD_PNNX2ONNX)
set_property(SOURCE main.cpp APPEND PROPERTY COMPILE_DEFINITIONS BUILD_PNNX2ONNX)
target_link_libraries(pnnx PRIVATE pnnx2onnx)
endif()

if(TorchVision_FOUND)
target_link_libraries(pnnx PRIVATE TorchVision::TorchVision)
# if(onnxruntime_FOUND)
# set_property(SOURCE main.cpp APPEND PROPERTY COMPILE_DEFINITIONS BUILD_ONNX2PNNX)
# target_link_libraries(pnnx PRIVATE onnx2pnnx)
# endif()

if(PNNX_COVERAGE)
target_compile_options(pnnx PUBLIC -coverage -fprofile-arcs -ftest-coverage)
target_link_libraries(pnnx PUBLIC -coverage -lgcov)
endif()

if(WIN32)
target_link_libraries(pnnx PRIVATE ${TORCH_LIBRARIES})
else()
target_link_libraries(pnnx PRIVATE ${TORCH_LIBRARIES} pthread dl)
target_compile_definitions(pnnx PUBLIC NOMINMAX)
endif()

# set_target_properties(pnnx PROPERTIES COMPILE_FLAGS -fsanitize=address)


+ 0
- 359
tools/pnnx/src/ir.cpp View File

@@ -23,11 +23,6 @@
#include <string>
#include <stack>

#if BUILD_PNNX
#include <torch/script.h>
#include <torch/csrc/api/include/torch/version.h>
#endif

#include "storezip.h"
#include "utils.h"

@@ -135,276 +130,6 @@ static int string_to_type(const char* s)
return 0; // null
}

#if BUILD_PNNX
int get_at_tensor_type(const at::ScalarType& st)
{
if (st == c10::ScalarType::Float) return 1;
if (st == c10::ScalarType::Double) return 2;
if (st == c10::ScalarType::Half) return 3;
if (st == c10::ScalarType::Int) return 4;
if (st == c10::ScalarType::QInt32) return 4;
if (st == c10::ScalarType::Long) return 5;
if (st == c10::ScalarType::Short) return 6;
if (st == c10::ScalarType::Char) return 7;
if (st == c10::ScalarType::QInt8) return 7;
if (st == c10::ScalarType::Byte) return 8;
if (st == c10::ScalarType::QUInt8) return 8;
if (st == c10::ScalarType::Bool) return 9;
if (st == c10::ScalarType::ComplexFloat) return 10;
if (st == c10::ScalarType::ComplexDouble) return 11;
if (st == c10::ScalarType::ComplexHalf) return 12;
return 0; // unknown type
}

Parameter::Parameter(const torch::jit::Node* value_node)
{
type = 0;

if (value_node->kind() == c10::prim::Constant)
{
if (value_node->output()->type()->kind() == c10::TypeKind::NoneType)
{
type = 0;
return;
}

if (!value_node->hasAttribute(torch::jit::attr::value))
{
fprintf(stderr, "no attribute value\n");
value_node->dump();
return;
}

switch (value_node->output()->type()->kind())
{
case c10::TypeKind::NoneType:
{
type = 0;
break;
}
case c10::TypeKind::BoolType:
{
type = 1;
b = value_node->i(torch::jit::attr::value);
break;
}
case c10::TypeKind::IntType:
{
type = 2;
int64_t i64 = value_node->i(torch::jit::attr::value);
if (i64 == std::numeric_limits<int64_t>::max()) i64 = INT_MAX;
if (i64 == std::numeric_limits<int64_t>::min()) i64 = INT_MIN;
i = (int)i64;
break;
}
case c10::TypeKind::FloatType:
{
type = 3;
f = (float)value_node->f(torch::jit::attr::value);
break;
}
case c10::TypeKind::StringType:
{
type = 4;
s = value_node->s(torch::jit::attr::value);
break;
}
case c10::TypeKind::DeviceObjType:
{
type = 4;
s = value_node->s(torch::jit::attr::value);
break;
}
#if TORCH_VERSION_MAJOR >= 2 || (TORCH_VERSION_MAJOR >= 1 && TORCH_VERSION_MINOR >= 9)
case c10::TypeKind::ComplexType:
{
type = 10;
c = std::complex<float>(value_node->c(torch::jit::attr::value));
break;
}
#endif
case c10::TypeKind::TensorType:
{
at::Tensor t = value_node->t(torch::jit::attr::value);

if (t.dim() == 0 && t.numel() == 1)
{
if (t.scalar_type() == c10::ScalarType::Long)
{
type = 2;
int64_t i64 = t.item<int64_t>();
if (i64 == std::numeric_limits<int64_t>::max()) i64 = INT_MAX;
if (i64 == std::numeric_limits<int64_t>::min()) i64 = INT_MIN;
i = (int)i64;
}
else if (t.scalar_type() == c10::ScalarType::Int)
{
type = 2;
i = t.item<int>();
}
else if (t.scalar_type() == c10::ScalarType::Double)
{
type = 3;
f = (float)t.item<double>();
}
else if (t.scalar_type() == c10::ScalarType::Float)
{
type = 3;
f = t.item<float>();
}
else if (t.scalar_type() == c10::ScalarType::ComplexDouble)
{
type = 10;
c = std::complex<float>(t.item<c10::complex<double> >());
}
else if (t.scalar_type() == c10::ScalarType::ComplexFloat)
{
type = 10;
c = std::complex<float>(t.item<c10::complex<float> >());
}
else
{
fprintf(stderr, "unknown Parameter value kind %s of TensorType, t.dim = 0\n", value_node->kind().toDisplayString());
}
}
else
{
// constant tensor will become pnnx attribute node later
type = 8;
}

break;
}
case c10::TypeKind::ListType:
{
switch (value_node->output()->type()->containedTypes()[0]->kind())
{
case c10::TypeKind::IntType:
{
type = 5;
std::vector<int64_t> i64s = value_node->ival(torch::jit::attr::value).toIntVector();
for (auto i64 : i64s)
{
if (i64 == std::numeric_limits<int64_t>::max()) i64 = INT_MAX;
if (i64 == std::numeric_limits<int64_t>::min()) i64 = INT_MIN;
ai.push_back(i64);
}
break;
}
case c10::TypeKind::FloatType:
{
type = 6;
std::vector<double> fs = value_node->ival(torch::jit::attr::value).toDoubleVector();
for (auto f : fs)
{
af.push_back((float)f);
}
break;
}
default:
{
fprintf(stderr, "unknown Parameter value list element kind %s\n", c10::typeKindToString(value_node->output()->type()->containedTypes()[0]->kind()));
break;
}
}
break;
}
default:
{
fprintf(stderr, "unknown Parameter value kind %s\n", c10::typeKindToString(value_node->output()->type()->kind()));
break;
}
}
}
else if (value_node->kind() == c10::prim::ListConstruct)
{
switch (value_node->output()->type()->cast<c10::ListType>()->getElementType()->kind())
{
case c10::TypeKind::IntType:
{
type = 5;
for (const auto& x : value_node->inputs())
{
if (!x->node()->hasAttribute(torch::jit::attr::value))
{
fprintf(stderr, "no attribute value in int list\n");
ai.push_back(0);
continue;
}

ai.push_back((int)x->node()->i(torch::jit::attr::value));
}
break;
}
case c10::TypeKind::FloatType:
{
type = 6;
for (const auto& x : value_node->inputs())
{
if (!x->node()->hasAttribute(torch::jit::attr::value))
{
fprintf(stderr, "no attribute value in float list\n");
af.push_back(0.f);
continue;
}

af.push_back((float)x->node()->f(torch::jit::attr::value));
}
break;
}
case c10::TypeKind::StringType:
{
type = 7;
for (const auto& x : value_node->inputs())
{
if (!x->node()->hasAttribute(torch::jit::attr::value))
{
fprintf(stderr, "no attribute value in string list\n");
as.push_back("");
continue;
}

as.push_back(x->node()->s(torch::jit::attr::value));
}
break;
}
#if TORCH_VERSION_MAJOR >= 2 || (TORCH_VERSION_MAJOR >= 1 && TORCH_VERSION_MINOR >= 9)
case c10::TypeKind::ComplexType:
{
type = 11;
for (const auto& x : value_node->inputs())
{
if (!x->node()->hasAttribute(torch::jit::attr::value))
{
fprintf(stderr, "no attribute value in complex list\n");
ac.push_back(std::complex<float>(0.f, 0.f));
continue;
}

ac.push_back(std::complex<float>(x->node()->c(torch::jit::attr::value)));
}
break;
}
#endif
default:
{
fprintf(stderr, "unknown Parameter value list element kind %s\n", c10::typeKindToString(value_node->output()->type()->cast<c10::ListType>()->getElementType()->kind()));
break;
}
}
}
else
{
fprintf(stderr, "unknown Parameter value_node kind %s\n", value_node->kind().toDisplayString());
}
}

Parameter::Parameter(const torch::jit::Value* value)
: Parameter(value->node())
{
}
#endif // BUILD_PNNX

bool operator==(const Parameter& lhs, const Parameter& rhs)
{
if (lhs.type != rhs.type)
@@ -443,59 +168,6 @@ bool operator==(const Parameter& lhs, const Parameter& rhs)
return false;
}

#if BUILD_PNNX
Attribute::Attribute(const at::Tensor& t)
{
type = get_at_tensor_type(t.scalar_type());

const int ndim = (int)t.dim();

if (ndim == 0)
{
shape = {1};

data.resize(type_to_elemsize(type));

if (t.scalar_type() == c10::ScalarType::Long)
{
int64_t i = t.item<int64_t>();
memcpy((void*)data.data(), (const void*)&i, data.size());
}
else if (t.scalar_type() == c10::ScalarType::Int)
{
int i = t.item<int>();
memcpy((void*)data.data(), (const void*)&i, data.size());
}
else if (t.scalar_type() == c10::ScalarType::Double)
{
double f = t.item<double>();
memcpy((void*)data.data(), (const void*)&f, data.size());
}
else if (t.scalar_type() == c10::ScalarType::Float)
{
float f = t.item<float>();
memcpy((void*)data.data(), (const void*)&f, data.size());
}
else
{
fprintf(stderr, "unknown Attribute tensor scalar type %d\n", type);
}

return;
}

shape.resize(ndim);
for (int i = 0; i < ndim; i++)
shape[i] = t.size(i);

if (shape.size() > 0)
{
data.resize(elemcount() * type_to_elemsize(type));
memcpy((void*)data.data(), (const void*)t.cpu().contiguous().data_ptr(), data.size());
}
}
#endif // BUILD_PNNX

Attribute::Attribute(const std::initializer_list<int>& _shape, const std::vector<float>& t)
{
type = 1;
@@ -3129,37 +2801,6 @@ Operator* Graph::new_operator_after(const std::string& type, const std::string&
return op;
}

#if BUILD_PNNX
Operand* Graph::new_operand(const torch::jit::Value* v)
{
Operand* r = new Operand;
r->name = v->debugName();

r->type = -1;

auto pt = v->type()->cast<c10::TensorType>();
if (pt)
{
if (pt->scalarType().has_value() && pt->dim().has_value())
{
r->type = get_at_tensor_type(pt->scalarType().value());
const int ndim = (int)pt->dim().value();
r->shape.resize(ndim);
for (int i = 0; i < ndim; i++)
{
if (pt->sizes()[i].has_value())
r->shape[i] = (int)pt->sizes()[i].value();
else
r->shape[i] = -1;
}
}
}

operands.push_back(r);
return r;
}
#endif // BUILD_PNNX

Operand* Graph::new_operand(const std::string& name)
{
Operand* r = new Operand;


+ 31
- 7
tools/pnnx/src/ir.h View File

@@ -24,7 +24,7 @@
#include <string>
#include <vector>

#if BUILD_PNNX
#if BUILD_TORCH2PNNX
namespace torch {
namespace jit {
struct Value;
@@ -34,7 +34,14 @@ struct Node;
namespace at {
class Tensor;
}
#endif // BUILD_PNNX
#endif // BUILD_TORCH2PNNX

#if BUILD_ONNX2PNNX
namespace onnx {
class TensorProto;
class ValueInfoProto;
} // namespace onnx
#endif // BUILD_ONNX2PNNX

namespace pnnx {

@@ -102,6 +109,17 @@ public:
: type(5), ai(_ai)
{
}
Parameter(const std::vector<int64_t>& _ai)
: type(5)
{
for (const auto& x : _ai)
{
int64_t _l = x;
if (_l == std::numeric_limits<int64_t>::max()) _l = INT_MAX;
if (_l == std::numeric_limits<int64_t>::min()) _l = INT_MIN;
ai.push_back((int)_l);
}
}
Parameter(const std::initializer_list<float>& _af)
: type(6), af(_af)
{
@@ -165,10 +183,10 @@ public:
ac.push_back(std::complex<float>(x));
}

#if BUILD_PNNX
#if BUILD_TORCH2PNNX
Parameter(const torch::jit::Node* value_node);
Parameter(const torch::jit::Value* value);
#endif // BUILD_PNNX
#endif // BUILD_TORCH2PNNX

static Parameter parse_from_string(const std::string& value);
static std::string encode_to_string(const Parameter& param);
@@ -200,9 +218,12 @@ public:
{
}

#if BUILD_PNNX
#if BUILD_TORCH2PNNX
Attribute(const at::Tensor& t);
#endif // BUILD_PNNX
#endif
#if BUILD_ONNX2PNNX
Attribute(const onnx::TensorProto& t);
#endif

Attribute(const std::initializer_list<int>& shape, const std::vector<float>& t);

@@ -299,9 +320,12 @@ public:

Operator* new_operator_after(const std::string& type, const std::string& name, const Operator* cur);

#if BUILD_PNNX
#if BUILD_TORCH2PNNX
Operand* new_operand(const torch::jit::Value* v);
#endif
#if BUILD_ONNX2PNNX
Operand* new_operand(const onnx::ValueInfoProto& value);
#endif

Operand* new_operand(const std::string& name);



+ 552
- 0
tools/pnnx/src/load_torchscript.cpp View File

@@ -0,0 +1,552 @@
// Tencent is pleased to support the open source community by making ncnn available.
//
// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved.
//
// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// https://opensource.org/licenses/BSD-3-Clause
//
// 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.

#include "load_torchscript.h"

#if _WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#endif

#include <torch/script.h>
#include <torch/csrc/api/include/torch/version.h>
#ifdef PNNX_TORCHVISION
namespace vision {
int64_t cuda_version();
} // namespace vision
#endif

#include "pass_level0.h"
#include "pass_level1.h"

namespace pnnx {

static int get_at_tensor_type(const at::ScalarType& st)
{
if (st == c10::ScalarType::Float) return 1;
if (st == c10::ScalarType::Double) return 2;
if (st == c10::ScalarType::Half) return 3;
if (st == c10::ScalarType::Int) return 4;
if (st == c10::ScalarType::QInt32) return 4;
if (st == c10::ScalarType::Long) return 5;
if (st == c10::ScalarType::Short) return 6;
if (st == c10::ScalarType::Char) return 7;
if (st == c10::ScalarType::QInt8) return 7;
if (st == c10::ScalarType::Byte) return 8;
if (st == c10::ScalarType::QUInt8) return 8;
if (st == c10::ScalarType::Bool) return 9;
if (st == c10::ScalarType::ComplexFloat) return 10;
if (st == c10::ScalarType::ComplexDouble) return 11;
if (st == c10::ScalarType::ComplexHalf) return 12;
return 0; // unknown type
}

static size_t type_to_elemsize(int type)
{
if (type == 1) return 4;
if (type == 2) return 8;
if (type == 3) return 2;
if (type == 4) return 4;
if (type == 5) return 8;
if (type == 6) return 2;
if (type == 7) return 1;
if (type == 8) return 1;
if (type == 9) return 1;
if (type == 10) return 8;
if (type == 11) return 16;
if (type == 12) return 4;
return 0; // null
}

Parameter::Parameter(const torch::jit::Node* value_node)
{
type = 0;

if (value_node->kind() == c10::prim::Constant)
{
if (value_node->output()->type()->kind() == c10::TypeKind::NoneType)
{
type = 0;
return;
}

if (!value_node->hasAttribute(torch::jit::attr::value))
{
fprintf(stderr, "no attribute value\n");
value_node->dump();
return;
}

switch (value_node->output()->type()->kind())
{
case c10::TypeKind::NoneType:
{
type = 0;
break;
}
case c10::TypeKind::BoolType:
{
type = 1;
b = value_node->i(torch::jit::attr::value);
break;
}
case c10::TypeKind::IntType:
{
type = 2;
int64_t i64 = value_node->i(torch::jit::attr::value);
if (i64 == std::numeric_limits<int64_t>::max()) i64 = INT_MAX;
if (i64 == std::numeric_limits<int64_t>::min()) i64 = INT_MIN;
i = (int)i64;
break;
}
case c10::TypeKind::FloatType:
{
type = 3;
f = (float)value_node->f(torch::jit::attr::value);
break;
}
case c10::TypeKind::StringType:
{
type = 4;
s = value_node->s(torch::jit::attr::value);
break;
}
case c10::TypeKind::DeviceObjType:
{
type = 4;
s = value_node->s(torch::jit::attr::value);
break;
}
#if TORCH_VERSION_MAJOR >= 2 || (TORCH_VERSION_MAJOR >= 1 && TORCH_VERSION_MINOR >= 9)
case c10::TypeKind::ComplexType:
{
type = 10;
c = std::complex<float>(value_node->c(torch::jit::attr::value));
break;
}
#endif
case c10::TypeKind::TensorType:
{
at::Tensor t = value_node->t(torch::jit::attr::value);

if (t.dim() == 0 && t.numel() == 1)
{
if (t.scalar_type() == c10::ScalarType::Long)
{
type = 2;
int64_t i64 = t.item<int64_t>();
if (i64 == std::numeric_limits<int64_t>::max()) i64 = INT_MAX;
if (i64 == std::numeric_limits<int64_t>::min()) i64 = INT_MIN;
i = (int)i64;
}
else if (t.scalar_type() == c10::ScalarType::Int)
{
type = 2;
i = t.item<int>();
}
else if (t.scalar_type() == c10::ScalarType::Double)
{
type = 3;
f = (float)t.item<double>();
}
else if (t.scalar_type() == c10::ScalarType::Float)
{
type = 3;
f = t.item<float>();
}
else if (t.scalar_type() == c10::ScalarType::ComplexDouble)
{
type = 10;
c = std::complex<float>(t.item<c10::complex<double> >());
}
else if (t.scalar_type() == c10::ScalarType::ComplexFloat)
{
type = 10;
c = std::complex<float>(t.item<c10::complex<float> >());
}
else
{
fprintf(stderr, "unknown Parameter value kind %s of TensorType, t.dim = 0\n", value_node->kind().toDisplayString());
}
}
else
{
// constant tensor will become pnnx attribute node later
type = 8;
}

break;
}
case c10::TypeKind::ListType:
{
switch (value_node->output()->type()->containedTypes()[0]->kind())
{
case c10::TypeKind::IntType:
{
type = 5;
std::vector<int64_t> i64s = value_node->ival(torch::jit::attr::value).toIntVector();
for (auto i64 : i64s)
{
if (i64 == std::numeric_limits<int64_t>::max()) i64 = INT_MAX;
if (i64 == std::numeric_limits<int64_t>::min()) i64 = INT_MIN;
ai.push_back(i64);
}
break;
}
case c10::TypeKind::FloatType:
{
type = 6;
std::vector<double> fs = value_node->ival(torch::jit::attr::value).toDoubleVector();
for (auto f : fs)
{
af.push_back((float)f);
}
break;
}
default:
{
fprintf(stderr, "unknown Parameter value list element kind %s\n", c10::typeKindToString(value_node->output()->type()->containedTypes()[0]->kind()));
break;
}
}
break;
}
default:
{
fprintf(stderr, "unknown Parameter value kind %s\n", c10::typeKindToString(value_node->output()->type()->kind()));
break;
}
}
}
else if (value_node->kind() == c10::prim::ListConstruct)
{
switch (value_node->output()->type()->cast<c10::ListType>()->getElementType()->kind())
{
case c10::TypeKind::IntType:
{
type = 5;
for (const auto& x : value_node->inputs())
{
if (!x->node()->hasAttribute(torch::jit::attr::value))
{
fprintf(stderr, "no attribute value in int list\n");
ai.push_back(0);
continue;
}

ai.push_back((int)x->node()->i(torch::jit::attr::value));
}
break;
}
case c10::TypeKind::FloatType:
{
type = 6;
for (const auto& x : value_node->inputs())
{
if (!x->node()->hasAttribute(torch::jit::attr::value))
{
fprintf(stderr, "no attribute value in float list\n");
af.push_back(0.f);
continue;
}

af.push_back((float)x->node()->f(torch::jit::attr::value));
}
break;
}
case c10::TypeKind::StringType:
{
type = 7;
for (const auto& x : value_node->inputs())
{
if (!x->node()->hasAttribute(torch::jit::attr::value))
{
fprintf(stderr, "no attribute value in string list\n");
as.push_back("");
continue;
}

as.push_back(x->node()->s(torch::jit::attr::value));
}
break;
}
#if TORCH_VERSION_MAJOR >= 2 || (TORCH_VERSION_MAJOR >= 1 && TORCH_VERSION_MINOR >= 9)
case c10::TypeKind::ComplexType:
{
type = 11;
for (const auto& x : value_node->inputs())
{
if (!x->node()->hasAttribute(torch::jit::attr::value))
{
fprintf(stderr, "no attribute value in complex list\n");
ac.push_back(std::complex<float>(0.f, 0.f));
continue;
}

ac.push_back(std::complex<float>(x->node()->c(torch::jit::attr::value)));
}
break;
}
#endif
default:
{
fprintf(stderr, "unknown Parameter value list element kind %s\n", c10::typeKindToString(value_node->output()->type()->cast<c10::ListType>()->getElementType()->kind()));
break;
}
}
}
else
{
fprintf(stderr, "unknown Parameter value_node kind %s\n", value_node->kind().toDisplayString());
}
}

Parameter::Parameter(const torch::jit::Value* value)
: Parameter(value->node())
{
}

Attribute::Attribute(const at::Tensor& t)
{
type = get_at_tensor_type(t.scalar_type());

const int ndim = (int)t.dim();

if (ndim == 0)
{
shape = {1};

data.resize(type_to_elemsize(type));

if (t.scalar_type() == c10::ScalarType::Long)
{
int64_t i = t.item<int64_t>();
memcpy((void*)data.data(), (const void*)&i, data.size());
}
else if (t.scalar_type() == c10::ScalarType::Int)
{
int i = t.item<int>();
memcpy((void*)data.data(), (const void*)&i, data.size());
}
else if (t.scalar_type() == c10::ScalarType::Double)
{
double f = t.item<double>();
memcpy((void*)data.data(), (const void*)&f, data.size());
}
else if (t.scalar_type() == c10::ScalarType::Float)
{
float f = t.item<float>();
memcpy((void*)data.data(), (const void*)&f, data.size());
}
else
{
fprintf(stderr, "unknown Attribute tensor scalar type %d\n", type);
}

return;
}

shape.resize(ndim);
for (int i = 0; i < ndim; i++)
shape[i] = t.size(i);

if (shape.size() > 0)
{
data.resize(elemcount() * type_to_elemsize(type));
memcpy((void*)data.data(), (const void*)t.cpu().contiguous().data_ptr(), data.size());
}
}

Operand* Graph::new_operand(const torch::jit::Value* v)
{
// Operand* r = new Operand;
// r->name = v->debugName();

Operand* r = new_operand(v->debugName());

r->type = -1;

auto pt = v->type()->cast<c10::TensorType>();
if (pt)
{
if (pt->scalarType().has_value() && pt->dim().has_value())
{
r->type = get_at_tensor_type(pt->scalarType().value());
const int ndim = (int)pt->dim().value();
r->shape.resize(ndim);
for (int i = 0; i < ndim; i++)
{
if (pt->sizes()[i].has_value())
r->shape[i] = (int)pt->sizes()[i].value();
else
r->shape[i] = -1;
}
}
}

// operands.push_back(r);
return r;
}

static c10::ScalarType input_type_to_c10_ScalarType(const std::string& t)
{
if (t == "c64") return torch::kComplexFloat;
if (t == "c32") return torch::kComplexHalf;
if (t == "c128") return torch::kComplexDouble;
if (t == "f32") return torch::kFloat32;
if (t == "f16") return torch::kFloat16;
if (t == "f64") return torch::kFloat64;
if (t == "i32") return torch::kInt32;
if (t == "i16") return torch::kInt16;
if (t == "i64") return torch::kInt64;
if (t == "i8") return torch::kInt8;
if (t == "u8") return torch::kUInt8;

fprintf(stderr, "unsupported type %s fallback to f32\n", t.c_str());
return torch::kFloat32;
}

const torch::jit::Node* find_node_by_kind(const std::shared_ptr<torch::jit::Graph>& graph, const std::string& kind)
{
for (const auto& n : graph->nodes())
{
if (n->kind().toDisplayString() == kind)
return n;
}

return 0;
}

int load_torchscript(const std::string& ptpath, Graph& pnnx_graph,
const std::string& device,
const std::vector<std::vector<int64_t> >& input_shapes,
const std::vector<std::string>& input_types,
const std::vector<std::vector<int64_t> >& input_shapes2,
const std::vector<std::string>& input_types2,
const std::vector<std::string>& customop_modules,
const std::vector<std::string>& module_operators,
const std::string& foldable_constants_zippath,
std::set<std::string>& foldable_constants)
{
#ifdef PNNX_TORCHVISION
// call some vision api to register vision ops :P
(void)vision::cuda_version();
#endif

for (auto m : customop_modules)
{
fprintf(stderr, "load custom module %s\n", m.c_str());
#if _WIN32
HMODULE handle = LoadLibraryExA(m.c_str(), NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
if (!handle)
{
fprintf(stderr, "LoadLibraryExA %s failed %d\n", m.c_str(), GetLastError());
}
#else
void* handle = dlopen(m.c_str(), RTLD_LAZY);
if (!handle)
{
fprintf(stderr, "dlopen %s failed %s\n", m.c_str(), dlerror());
}
#endif
}

std::vector<at::Tensor> input_tensors;
for (size_t i = 0; i < input_shapes.size(); i++)
{
const std::vector<int64_t>& shape = input_shapes[i];
const std::string& type = input_types[i];

at::Tensor t = torch::ones(shape, input_type_to_c10_ScalarType(type));
if (device == "gpu")
t = t.cuda();

input_tensors.push_back(t);
}

std::vector<at::Tensor> input_tensors2;
for (size_t i = 0; i < input_shapes2.size(); i++)
{
const std::vector<int64_t>& shape = input_shapes2[i];
const std::string& type = input_types2[i];

at::Tensor t = torch::ones(shape, input_type_to_c10_ScalarType(type));
if (device == "gpu")
t = t.cuda();

input_tensors2.push_back(t);
}

torch::jit::Module mod;

try
{
mod = torch::jit::load(ptpath, (device == "gpu") ? c10::kCUDA : c10::kCPU);
}
catch (const c10::Error& e)
{
fprintf(stderr, "Load torchscript failed: %s\n", e.what());

fprintf(stderr, "Please export model to torchscript as follows\n");
fprintf(stderr, "------------------------------------------\n");
fprintf(stderr, "import torch\n");
fprintf(stderr, "import torchvision.models as models\n\n");
fprintf(stderr, "net = models.resnet18(pretrained=True)\n");
fprintf(stderr, "net = net.eval()\n\n");
fprintf(stderr, "x = torch.rand(1, 3, 224, 224)\n");
fprintf(stderr, "mod = torch.jit.trace(net, x)\n");
fprintf(stderr, "mod.save(\"resnet18.pt\")\n");
fprintf(stderr, "------------------------------------------\n");

return -1;
}

mod.eval();

// mod.dump(true, false, false);
// mod.dump(true, true, true);

auto method = mod.find_method("forward");
if (!method)
{
auto methods = mod.get_methods();
if (methods.empty())
{
fprintf(stderr, "No method in torchscript\n");
return -1;
}

method = methods[0];
fprintf(stderr, "Use method %s as the entrypoint instead of forward\n", method->name().c_str());
}

auto g = method->graph();

// g->dump();

fprintf(stderr, "############# pass_level0\n");

pnnx::pass_level0(mod, g, input_tensors, input_tensors2, module_operators, ptpath, device, foldable_constants, foldable_constants_zippath);

// g->dump();

fprintf(stderr, "############# pass_level1\n");

pnnx::pass_level1(mod, g, module_operators, pnnx_graph);

return 0;
}

} // namespace pnnx

+ 35
- 0
tools/pnnx/src/load_torchscript.h View File

@@ -0,0 +1,35 @@
// Tencent is pleased to support the open source community by making ncnn available.
//
// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved.
//
// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// https://opensource.org/licenses/BSD-3-Clause
//
// 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 PNNX_LOAD_TORCHSCRIPT_H
#define PNNX_LOAD_TORCHSCRIPT_H

#include "ir.h"

namespace pnnx {

int load_torchscript(const std::string& ptpath, Graph& g,
const std::string& device,
const std::vector<std::vector<int64_t> >& input_shapes,
const std::vector<std::string>& input_types,
const std::vector<std::vector<int64_t> >& input_shapes2,
const std::vector<std::string>& input_types2,
const std::vector<std::string>& customop_modules,
const std::vector<std::string>& module_operators,
const std::string& foldable_constants_zippath,
std::set<std::string>& foldable_constants);

} // namespace pnnx

#endif // PNNX_LOAD_TORCHSCRIPT_H

+ 16
- 137
tools/pnnx/src/main.cpp View File

@@ -13,31 +13,25 @@
// specific language governing permissions and limitations under the License.

#include <stdio.h>
#include <string.h>

#if _WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#endif

#include <algorithm>
#include <string>
#include <vector>

#include <torch/script.h>

#ifdef PNNX_TORCHVISION
// register torchvision ops via including headers
#include <torchvision/vision.h>
#endif

#include "ir.h"
#include "pass_level0.h"
#include "pass_level1.h"
#include "pass_level2.h"
#include "pass_level3.h"
#include "pass_level4.h"
#include "pass_level5.h"

#if BUILD_TORCH2PNNX
#include "load_torchscript.h"
#endif
#if BUILD_ONNX2PNNX
#include "load_onnx.h"
#endif

#include "pass_ncnn.h"
#include "save_ncnn.h"

@@ -160,24 +154,6 @@ static void print_shape_list(const std::vector<std::vector<int64_t> >& shapes, c
}
}

static c10::ScalarType input_type_to_c10_ScalarType(const std::string& t)
{
if (t == "c64") return torch::kComplexFloat;
if (t == "c32") return torch::kComplexHalf;
if (t == "c128") return torch::kComplexDouble;
if (t == "f32") return torch::kFloat32;
if (t == "f16") return torch::kFloat16;
if (t == "f64") return torch::kFloat64;
if (t == "i32") return torch::kInt32;
if (t == "i16") return torch::kInt16;
if (t == "i64") return torch::kInt64;
if (t == "i8") return torch::kInt8;
if (t == "u8") return torch::kUInt8;

fprintf(stderr, "unsupported type %s fallback to f32\n", t.c_str());
return torch::kFloat32;
}

static void show_usage()
{
fprintf(stderr, "Usage: pnnx [model.pt] [(key=value)...]\n");
@@ -314,114 +290,17 @@ int main(int argc, char** argv)
fprintf(stderr, "\n");
}

#ifdef PNNX_TORCHVISION
// call some vision api to register vision ops :P
(void)vision::cuda_version();
#endif

for (auto m : customop_modules)
{
fprintf(stderr, "load custom module %s\n", m.c_str());
#if _WIN32
HMODULE handle = LoadLibraryExA(m.c_str(), NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
if (!handle)
{
fprintf(stderr, "LoadLibraryExA %s failed %d\n", m.c_str(), GetLastError());
}
#else
void* handle = dlopen(m.c_str(), RTLD_LAZY);
if (!handle)
{
fprintf(stderr, "dlopen %s failed %s\n", m.c_str(), dlerror());
}
#endif
}

std::vector<at::Tensor> input_tensors;
for (size_t i = 0; i < input_shapes.size(); i++)
{
const std::vector<int64_t>& shape = input_shapes[i];
const std::string& type = input_types[i];

at::Tensor t = torch::ones(shape, input_type_to_c10_ScalarType(type));
if (device == "gpu")
t = t.cuda();

input_tensors.push_back(t);
}

std::vector<at::Tensor> input_tensors2;
for (size_t i = 0; i < input_shapes2.size(); i++)
{
const std::vector<int64_t>& shape = input_shapes2[i];
const std::string& type = input_types2[i];

at::Tensor t = torch::ones(shape, input_type_to_c10_ScalarType(type));
if (device == "gpu")
t = t.cuda();

input_tensors2.push_back(t);
}

torch::jit::Module mod;

try
{
mod = torch::jit::load(ptpath, (device == "gpu") ? c10::kCUDA : c10::kCPU);
}
catch (const c10::Error& e)
{
fprintf(stderr, "Load torchscript failed: %s\n", e.what());

fprintf(stderr, "Please export model to torchscript as follows\n");
fprintf(stderr, "------------------------------------------\n");
fprintf(stderr, "import torch\n");
fprintf(stderr, "import torchvision.models as models\n\n");
fprintf(stderr, "net = models.resnet18(pretrained=True)\n");
fprintf(stderr, "net = net.eval()\n\n");
fprintf(stderr, "x = torch.rand(1, 3, 224, 224)\n");
fprintf(stderr, "mod = torch.jit.trace(net, x)\n");
fprintf(stderr, "mod.save(\"resnet18.pt\")\n");
fprintf(stderr, "------------------------------------------\n");

return -1;
}

mod.eval();

// mod.dump(true, false, false);
// mod.dump(true, true, true);

auto method = mod.find_method("forward");
if (!method)
{
auto methods = mod.get_methods();
if (methods.empty())
{
fprintf(stderr, "No method in torchscript\n");
return -1;
}

method = methods[0];
fprintf(stderr, "Use method %s as the entrypoint instead of forward\n", method->name().c_str());
}

auto g = method->graph();

// g->dump();

fprintf(stderr, "############# pass_level0\n");

std::set<std::string> foldable_constants;
std::string foldable_constants_zippath = ptbase + ".foldable_constants.zip";
pnnx::pass_level0(mod, g, input_tensors, input_tensors2, module_operators, ptpath, device, foldable_constants, foldable_constants_zippath);

// g->dump();

fprintf(stderr, "############# pass_level1\n");

pnnx::Graph pnnx_graph;
pnnx::pass_level1(mod, g, module_operators, pnnx_graph);
load_torchscript(ptpath, pnnx_graph,
device, input_shapes, input_types,
input_shapes2, input_types2,
customop_modules, module_operators,
foldable_constants_zippath, foldable_constants);

// load_onnx(ptpath.c_str(), pnnx_graph);

// g->dump();



+ 371
- 40
tools/pnnx/src/onnx.proto View File

@@ -3,8 +3,8 @@
//


// Copyright (c) ONNX Project Contributors.
// Licensed under the MIT license.
// SPDX-License-Identifier: Apache-2.0

syntax = "proto2";

@@ -20,23 +20,16 @@ package onnx;
//
// This document describes the syntax of models and their computation graphs,
// as well as the standard data types. Together, they are referred to as the ONNX
// Intermediate Representation, or 'IR' for short.
// Intermediate Representation, or 'IR' for short.
//
// The normative semantic specification of the ONNX IR is found in docs/IR.md.
// Definitions of the built-in neural network operators may be found in docs/Operators.md.

// Notes
//
// Release
//
// We are still in the very early stage of defining ONNX. The current
// version of ONNX is a starting point. While we are actively working
// towards a complete spec, we would like to get the community involved
// by sharing our working version of ONNX.
//
// Protobuf compatibility
//
// To simplify framework compatibility, ONNX is defined using the subset of protobuf
//
// To simplify framework compatibility, ONNX is defined using the subset of protobuf
// that is compatible with both protobuf v2 and v3. This means that we do not use any
// protobuf features that are only available in one of the two versions.
//
@@ -60,8 +53,8 @@ enum Version {
_START_VERSION = 0;
// The version field is always serialized and we will use it to store the
// version that the graph is generated from. This helps us set up version
// control.
// For the IR, we are using simple numbers starting with with 0x00000001,
// control.
// For the IR, we are using simple numbers starting with 0x00000001,
// which was the version we published on Oct 10, 2017.
IR_VERSION_2017_10_10 = 0x0000000000000001;

@@ -84,7 +77,36 @@ enum Version {
// IR VERSION 5 published on March 18, 2019
// - Add message TensorAnnotation.
// - Add quantization annotation in GraphProto to map tensor with its scale and zero point quantization parameters.
IR_VERSION = 0x0000000000000005;
IR_VERSION_2019_3_18 = 0x0000000000000005;

// IR VERSION 6 published on Sep 19, 2019
// - Add support for sparse tensor constants stored in model.
// - Add message SparseTensorProto
// - Add sparse initializers
IR_VERSION_2019_9_19 = 0x0000000000000006;

// IR VERSION 7 published on May 8, 2020
// - Add support to allow function body graph to rely on multiple external opreator sets.
// - Add a list to promote inference graph's initializers to global and
// mutable variables. Global variables are visible in all graphs of the
// stored models.
// - Add message TrainingInfoProto to store initialization
// method and training algorithm. The execution of TrainingInfoProto
// can modify the values of mutable variables.
// - Implicitly add inference graph into each TrainingInfoProto's algorithm.
IR_VERSION_2020_5_8 = 0x0000000000000007;

// IR VERSION 8 published on July 30, 2021
// Introduce TypeProto.SparseTensor
// Introduce TypeProto.Optional
// Added a list of FunctionProtos local to the model
// Deprecated since_version and operator status from FunctionProto
IR_VERSION_2021_7_30 = 0x0000000000000008;

// IR VERSION 9 published on May 5, 2023
// Added AttributeProto to FunctionProto so that default attribute values can be set.
// Added FLOAT8E4M3FN, FLOAT8E4M3FNUZ, FLOAT8E5M2, FLOAT8E5M2FNUZ.
IR_VERSION = 0x0000000000000009;
}

// Attributes
@@ -94,6 +116,8 @@ enum Version {
// An AttributeProto MUST contain the name field, and *only one* of the
// following content fields, effectively enforcing a C/C++ union equivalent.
message AttributeProto {
reserved 12, 16 to 19;
reserved "v";

// Note: this enum is structurally identical to the OpSchema::AttrType
// enum defined in schema.h. If you rev one, you likely need to rev the other.
@@ -104,17 +128,21 @@ message AttributeProto {
STRING = 3;
TENSOR = 4;
GRAPH = 5;
SPARSE_TENSOR = 11;
TYPE_PROTO = 13;

FLOATS = 6;
INTS = 7;
STRINGS = 8;
TENSORS = 9;
GRAPHS = 10;
SPARSE_TENSORS = 12;
TYPE_PROTOS = 14;
}

// The name field MUST be present for this version of the IR.
optional string name = 1; // namespace Attribute
// if ref_attr_name is not empty, ref_attr_name is the attribute name in parent function.
// In this case, this AttributeProto does not contain data, and it's a reference of attribute
// in parent scope.
@@ -126,7 +154,7 @@ message AttributeProto {

// The type field MUST be present for this version of the IR.
// For 0.0.1 versions of the IR, this field was not defined, and
// implementations needed to use has_field hueristics to determine
// implementations needed to use has_field heuristics to determine
// which value field was in use. For IR_VERSION 0.0.2 or later, this
// field MUST be set and match the f|i|s|t|... field in use. This
// change was made to accommodate proto3 implementations.
@@ -138,14 +166,18 @@ message AttributeProto {
optional bytes s = 4; // UTF-8 string
optional TensorProto t = 5; // tensor value
optional GraphProto g = 6; // graph
optional SparseTensorProto sparse_tensor = 22; // sparse tensor value
// Do not use field below, it's deprecated.
// optional ValueProto v = 12; // value - subsumes everything but graph
optional TypeProto tp = 14; // type proto

repeated float floats = 7; // list of floats
repeated int64 ints = 8; // list of ints
repeated bytes strings = 9; // list of UTF-8 strings
repeated TensorProto tensors = 10; // list of tensors
repeated GraphProto graphs = 11; // list of graph
repeated SparseTensorProto sparse_tensors = 23; // list of sparse tensors
repeated TypeProto type_protos = 15;// list of type protos
}

// Defines information on value, including the name, the type, and
@@ -153,7 +185,8 @@ message AttributeProto {
message ValueInfoProto {
// This field MUST be present in this version of the IR.
optional string name = 1; // namespace Value
// This field MUST be present in this version of the IR.
// This field MUST be present in this version of the IR for
// inputs and outputs of the top-level graph.
optional TypeProto type = 2;
// A human-readable documentation for this value. Markdown is allowed.
optional string doc_string = 3;
@@ -164,7 +197,7 @@ message ValueInfoProto {
// Computation graphs are made up of a DAG of nodes, which represent what is
// commonly called a "layer" or "pipeline stage" in machine learning frameworks.
//
// For example, it can be a node of type "Conv" that takes in an image, a filter
// For example, it can be a node of type "Conv" that takes in an image, a filter
// tensor and a bias tensor, and produces the convolved output.
message NodeProto {
repeated string input = 1; // namespace Value
@@ -186,12 +219,130 @@ message NodeProto {
optional string doc_string = 6;
}

// Training information
// TrainingInfoProto stores information for training a model.
// In particular, this defines two functionalities: an initialization-step
// and a training-algorithm-step. Initialization resets the model
// back to its original state as if no training has been performed.
// Training algorithm improves the model based on input data.
//
// The semantics of the initialization-step is that the initializers
// in ModelProto.graph and in TrainingInfoProto.algorithm are first
// initialized as specified by the initializers in the graph, and then
// updated by the "initialization_binding" in every instance in
// ModelProto.training_info.
//
// The field "algorithm" defines a computation graph which represents a
// training algorithm's step. After the execution of a
// TrainingInfoProto.algorithm, the initializers specified by "update_binding"
// may be immediately updated. If the targeted training algorithm contains
// consecutive update steps (such as block coordinate descent methods),
// the user needs to create a TrainingInfoProto for each step.
message TrainingInfoProto {
// This field describes a graph to compute the initial tensors
// upon starting the training process. Initialization graph has no input
// and can have multiple outputs. Usually, trainable tensors in neural
// networks are randomly initialized. To achieve that, for each tensor,
// the user can put a random number operator such as RandomNormal or
// RandomUniform in TrainingInfoProto.initialization.node and assign its
// random output to the specific tensor using "initialization_binding".
// This graph can also set the initializers in "algorithm" in the same
// TrainingInfoProto; a use case is resetting the number of training
// iteration to zero.
//
// By default, this field is an empty graph and its evaluation does not
// produce any output. Thus, no initializer would be changed by default.
optional GraphProto initialization = 1;

// This field represents a training algorithm step. Given required inputs,
// it computes outputs to update initializers in its own or inference graph's
// initializer lists. In general, this field contains loss node, gradient node,
// optimizer node, increment of iteration count.
//
// An execution of the training algorithm step is performed by executing the
// graph obtained by combining the inference graph (namely "ModelProto.graph")
// and the "algorithm" graph. That is, the actual
// input/initializer/output/node/value_info/sparse_initializer list of
// the training graph is the concatenation of
// "ModelProto.graph.input/initializer/output/node/value_info/sparse_initializer"
// and "algorithm.input/initializer/output/node/value_info/sparse_initializer"
// in that order. This combined graph must satisfy the normal ONNX conditions.
// Now, let's provide a visualization of graph combination for clarity.
// Let the inference graph (i.e., "ModelProto.graph") be
// tensor_a, tensor_b -> MatMul -> tensor_c -> Sigmoid -> tensor_d
// and the "algorithm" graph be
// tensor_d -> Add -> tensor_e
// The combination process results
// tensor_a, tensor_b -> MatMul -> tensor_c -> Sigmoid -> tensor_d -> Add -> tensor_e
//
// Notice that an input of a node in the "algorithm" graph may reference the
// output of a node in the inference graph (but not the other way round). Also, inference
// node cannot reference inputs of "algorithm". With these restrictions, inference graph
// can always be run independently without training information.
//
// By default, this field is an empty graph and its evaluation does not
// produce any output. Evaluating the default training step never
// update any initializers.
optional GraphProto algorithm = 2;

// This field specifies the bindings from the outputs of "initialization" to
// some initializers in "ModelProto.graph.initializer" and
// the "algorithm.initializer" in the same TrainingInfoProto.
// See "update_binding" below for details.
//
// By default, this field is empty and no initializer would be changed
// by the execution of "initialization".
repeated StringStringEntryProto initialization_binding = 3;

// Gradient-based training is usually an iterative procedure. In one gradient
// descent iteration, we apply
//
// x = x - r * g
//
// where "x" is the optimized tensor, "r" stands for learning rate, and "g" is
// gradient of "x" with respect to a chosen loss. To avoid adding assignments
// into the training graph, we split the update equation into
//
// y = x - r * g
// x = y
//
// The user needs to save "y = x - r * g" into TrainingInfoProto.algorithm. To
// tell that "y" should be assigned to "x", the field "update_binding" may
// contain a key-value pair of strings, "x" (key of StringStringEntryProto)
// and "y" (value of StringStringEntryProto).
// For a neural network with multiple trainable (mutable) tensors, there can
// be multiple key-value pairs in "update_binding".
//
// The initializers appears as keys in "update_binding" are considered
// mutable variables. This implies some behaviors
// as described below.
//
// 1. We have only unique keys in all "update_binding"s so that two
// variables may not have the same name. This ensures that one
// variable is assigned up to once.
// 2. The keys must appear in names of "ModelProto.graph.initializer" or
// "TrainingInfoProto.algorithm.initializer".
// 3. The values must be output names of "algorithm" or "ModelProto.graph.output".
// 4. Mutable variables are initialized to the value specified by the
// corresponding initializer, and then potentially updated by
// "initializer_binding"s and "update_binding"s in "TrainingInfoProto"s.
//
// This field usually contains names of trainable tensors
// (in ModelProto.graph), optimizer states such as momentums in advanced
// stochastic gradient methods (in TrainingInfoProto.graph),
// and number of training iterations (in TrainingInfoProto.graph).
//
// By default, this field is empty and no initializer would be changed
// by the execution of "algorithm".
repeated StringStringEntryProto update_binding = 4;
}

// Models
//
// ModelProto is a top-level file/container format for bundling a ML model and
// associating its computation graph with metadata.
//
// The semantics of the model are described by the associated GraphProto.
// The semantics of the model are described by the associated GraphProto's.
message ModelProto {
// The version of the IR this model targets. See Version enum above.
// This field MUST be present.
@@ -236,13 +387,42 @@ message ModelProto {

// Named metadata values; keys should be distinct.
repeated StringStringEntryProto metadata_props = 14;

// Training-specific information. Sequentially executing all stored
// `TrainingInfoProto.algorithm`s and assigning their outputs following
// the corresponding `TrainingInfoProto.update_binding`s is one training
// iteration. Similarly, to initialize the model
// (as if training hasn't happened), the user should sequentially execute
// all stored `TrainingInfoProto.initialization`s and assigns their outputs
// using `TrainingInfoProto.initialization_binding`s.
//
// If this field is empty, the training behavior of the model is undefined.
repeated TrainingInfoProto training_info = 20;

// A list of function protos local to the model.
//
// Name of the function "FunctionProto.name" should be unique within the domain "FunctionProto.domain".
// In case of any conflicts the behavior (whether the model local functions are given higher priority,
// or standard operator sets are given higher priotity or this is treated as error) is defined by
// the runtimes.
//
// The operator sets imported by FunctionProto should be compatible with the ones
// imported by ModelProto and other model local FunctionProtos.
// Example, if same operator set say 'A' is imported by a FunctionProto and ModelProto
// or by 2 FunctionProtos then versions for the operator set may be different but,
// the operator schema returned for op_type, domain, version combination
// for both the versions should be same for every node in the function body.
//
// One FunctionProto can reference other FunctionProto in the model, however, recursive reference
// is not allowed.
repeated FunctionProto functions = 25;
};

// StringStringEntryProto follows the pattern for cross-proto-version maps.
// See https://developers.google.com/protocol-buffers/docs/proto3#maps
message StringStringEntryProto {
optional string key = 1;
optional string value= 2;
optional string value = 2;
};

message TensorAnnotation {
@@ -258,7 +438,7 @@ message TensorAnnotation {

// Graphs
//
// A graph defines the computational logic of a model and is comprised of a parameterized
// A graph defines the computational logic of a model and is comprised of a parameterized
// list of nodes that form a directed acyclic graph based on their inputs and outputs.
// This is the equivalent of the "network" or "graph" in many deep learning
// frameworks.
@@ -270,10 +450,14 @@ message GraphProto {
optional string name = 2; // namespace Graph

// A list of named tensor values, used to specify constant inputs of the graph.
// Each TensorProto entry must have a distinct name (within the list) that
// MAY also appear in the input list.
// Each initializer (both TensorProto as well SparseTensorProto) MUST have a name.
// The name MUST be unique across both initializer and sparse_initializer,
// but the name MAY also appear in the input list.
repeated TensorProto initializer = 5;

// Initializers (see above) stored in sparse format.
repeated SparseTensorProto sparse_initializer = 15;

// A human-readable documentation for this graph. Markdown is allowed.
optional string doc_string = 10;

@@ -291,13 +475,8 @@ message GraphProto {
// which means, tensor 'a_scale' and tensor 'a_zero_point' are scale and zero point of tensor 'a' in the model.
repeated TensorAnnotation quantization_annotation = 14;

// DO NOT USE the following fields, they were deprecated from earlier versions.
// repeated string input = 3;
// repeated string output = 4;
// optional int64 ir_version = 6;
// optional int64 producer_version = 7;
// optional string producer_tag = 8;
// optional string domain = 9;
reserved 3, 4, 6 to 9;
reserved "ir_version", "producer_version", "producer_tag", "domain";
}

// Tensors
@@ -332,6 +511,17 @@ message TensorProto {
// This format has 1 sign bit, 8 exponent bits, and 7 mantissa bits.
BFLOAT16 = 16;

// Non-IEEE floating-point format based on papers
// FP8 Formats for Deep Learning, https://arxiv.org/abs/2209.05433,
// 8-bit Numerical Formats For Deep Neural Networks, https://arxiv.org/pdf/2206.02915.pdf.
// Operators supported FP8 are Cast, CastLike, QuantizeLinear, DequantizeLinear.
// The computation usually happens inside a block quantize / dequantize
// fused by the runtime.
FLOAT8E4M3FN = 17; // float 8, mostly used for coefficients, supports nan, not inf
FLOAT8E4M3FNUZ = 18; // float 8, mostly used for coefficients, supports nan, not inf, no negative zero
FLOAT8E5M2 = 19; // follows IEEE 754, supports nan, inf, mostly used for gradients
FLOAT8E5M2FNUZ = 20; // follows IEEE 754, supports nan, inf, mostly used for gradients, no negative zero

// Future extensions go here.
}

@@ -359,17 +549,17 @@ message TensorProto {
// For float and complex64 values
// Complex64 tensors are encoded as a single array of floats,
// with the real components appearing in odd numbered positions,
// and the corresponding imaginary component apparing in the
// and the corresponding imaginary component appearing in the
// subsequent even numbered position. (e.g., [1.0 + 2.0i, 3.0 + 4.0i]
// is encoded as [1.0, 2.0 ,3.0 ,4.0]
// When this field is present, the data_type field MUST be FLOAT or COMPLEX64.
repeated float float_data = 4 [packed = true];

// For int32, uint8, int8, uint16, int16, bool, and float16 values
// float16 values must be bit-wise converted to an uint16_t prior
// For int32, uint8, int8, uint16, int16, bool, float8, and float16 values
// float16 and float8 values must be bit-wise converted to an uint16_t prior
// to writing to the buffer.
// When this field is present, the data_type field MUST be
// INT32, INT16, INT8, UINT16, UINT8, BOOL, or FLOAT16
// INT32, INT16, INT8, UINT16, UINT8, BOOL, FLOAT16, BFLOAT16, FLOAT8E4M3FN, FLOAT8E4M3FNUZ, FLOAT8E5M2, FLOAT8E5M2FNUZ
repeated int32 int32_data = 5 [packed = true];

// For strings.
@@ -431,7 +621,7 @@ message TensorProto {
// For double
// Complex128 tensors are encoded as a single array of doubles,
// with the real components appearing in odd numbered positions,
// and the corresponding imaginary component apparing in the
// and the corresponding imaginary component appearing in the
// subsequent even numbered position. (e.g., [1.0 + 2.0i, 3.0 + 4.0i]
// is encoded as [1.0, 2.0 ,3.0 ,4.0]
// When this field is present, the data_type field MUST be DOUBLE or COMPLEX128
@@ -443,6 +633,30 @@ message TensorProto {
repeated uint64 uint64_data = 11 [packed = true];
}

// A serialized sparse-tensor value
message SparseTensorProto {
// The sequence of non-default values are encoded as a tensor of shape [NNZ].
// The default-value is zero for numeric tensors, and empty-string for string tensors.
// values must have a non-empty name present which serves as a name for SparseTensorProto
// when used in sparse_initializer list.
optional TensorProto values = 1;

// The indices of the non-default values, which may be stored in one of two formats.
// (a) Indices can be a tensor of shape [NNZ, rank] with the [i,j]-th value
// corresponding to the j-th index of the i-th value (in the values tensor).
// (b) Indices can be a tensor of shape [NNZ], in which case the i-th value
// must be the linearized-index of the i-th value (in the values tensor).
// The linearized-index can be converted into an index tuple (k_1,...,k_rank)
// using the shape provided below.
// The indices must appear in ascending order without duplication.
// In the first format, the ordering is lexicographic-ordering:
// e.g., index-value [1,4] must appear before [2,1]
optional TensorProto indices = 2;

// The shape of the underlying dense-tensor: [dim_1, dim_2, ... dim_rank]
repeated int64 dims = 3;
}

// Defines a tensor shape. A dimension can be either an integer value
// or a symbolic variable. A symbolic variable represents an unknown
// dimension.
@@ -455,7 +669,7 @@ message TensorShapeProto {
// Standard denotation can optionally be used to denote tensor
// dimensions with standard semantic descriptions to ensure
// that operations are applied to the correct axis of a tensor.
// Refer to https://github.com/onnx/onnx/blob/master/docs/DimensionDenotation.md#denotation-definition
// Refer to https://github.com/onnx/onnx/blob/main/docs/DimensionDenotation.md#denotation-definition
// for pre-defined dimension denotations.
optional string denotation = 3;
};
@@ -475,16 +689,68 @@ message TypeProto {
optional TensorShapeProto shape = 2;
}

// repeated T
message Sequence {
// The type and optional shape of each element of the sequence.
// This field MUST be present for this version of the IR.
optional TypeProto elem_type = 1;
};

// map<K,V>
message Map {
// This field MUST have a valid TensorProto.DataType value
// This field MUST be present for this version of the IR.
// This field MUST refer to an integral type ([U]INT{8|16|32|64}) or STRING
optional int32 key_type = 1;
// This field MUST be present for this version of the IR.
optional TypeProto value_type = 2;
};

// wrapper for Tensor, Sequence, or Map
message Optional {
// The type and optional shape of the element wrapped.
// This field MUST be present for this version of the IR.
// Possible values correspond to OptionalProto.DataType enum
optional TypeProto elem_type = 1;
};


message SparseTensor {
// This field MUST NOT have the value of UNDEFINED
// This field MUST have a valid TensorProto.DataType value
// This field MUST be present for this version of the IR.
optional int32 elem_type = 1;
optional TensorShapeProto shape = 2;
}


oneof value {
// The type of a tensor.
Tensor tensor_type = 1;

// NOTE: DNN-only implementations of ONNX MAY elect to not support non-tensor values
// as input and output to graphs and nodes. These types are needed to naturally
// support classical ML operators. DNN operators SHOULD restrict their input
// and output types to tensors.

// The type of a sequence.
Sequence sequence_type = 4;

// The type of a map.
Map map_type = 5;

// The type of an optional.
Optional optional_type = 9;


// Type of the sparse tensor
SparseTensor sparse_tensor_type = 8;

}

// An optional denotation can be used to denote the whole
// type with a standard semantic description as to what is
// stored inside. Refer to https://github.com/onnx/onnx/blob/master/docs/TypeDenotation.md#type-denotation-definition
// An optional denotation can be used to denote the whole
// type with a standard semantic description as to what is
// stored inside. Refer to https://github.com/onnx/onnx/blob/main/docs/TypeDenotation.md#type-denotation-definition
// for pre-defined type denotations.
optional string denotation = 6;
}
@@ -503,3 +769,68 @@ message OperatorSetIdProto {
// This field MUST be present in this version of the IR.
optional int64 version = 2;
}

// Operator/function status.
enum OperatorStatus {
EXPERIMENTAL = 0;
STABLE = 1;
}

message FunctionProto {
// The name of the function, similar usage of op_type in OperatorProto.
// Combined with FunctionProto.domain, this forms the unique identity of
// the FunctionProto.
optional string name = 1;

// Deprecated since IR Version 8
// optional int64 since_version = 2;
reserved 2;
reserved "since_version";

// Deprecated since IR Version 8
// optional OperatorStatus status = 3;
reserved 3;
reserved "status";

// The inputs and outputs of the function.
repeated string input = 4;
repeated string output = 5;

// The attribute parameters of the function.
// It is for function parameters without default values.
repeated string attribute = 6;

// The attribute protos of the function.
// It is for function attributes with default values.
// A function attribute shall be represented either as
// a string attribute or an AttributeProto, not both.
repeated AttributeProto attribute_proto = 11;

// The nodes in the function.
repeated NodeProto node = 7;
// A human-readable documentation for this function. Markdown is allowed.
optional string doc_string = 8;

// The OperatorSets this function body (graph) relies on.
//
// All nodes in the function body (graph) will bind against the operator
// with the same-domain/same-op_type operator with the HIGHEST version
// in the referenced operator sets. This means at most one version can be relied
// for one domain.
//
// The operator sets imported by FunctionProto should be compatible with the ones
// imported by ModelProto. Example, if same operator set say 'A' is imported by FunctionProto
// and ModelProto then versions for the operator set may be different but,
// the operator schema returned for op_type, domain, version combination
// for both the versions should be same.

repeated OperatorSetIdProto opset_import = 9;

// The domain which this function belongs to. Combined with FunctionProto.name, this forms the unique identity of
// the FunctionProto.
optional string domain = 10;
}

// For using protobuf-lite
option optimize_for = LITE_RUNTIME;


+ 1
- 1
tools/pnnx/src/pass_level2/torch_permute.cpp View File

@@ -14,7 +14,7 @@

#include "pass_level2.h"

#include <torch/csrc/api/include/torch/torch.h>
#include <torch/csrc/api/include/torch/version.h>

namespace pnnx {



+ 0
- 2
tools/pnnx/src/pass_level2/torch_repeat_interleave.cpp View File

@@ -14,8 +14,6 @@

#include "pass_level2.h"

#include <torch/csrc/api/include/torch/torch.h>

namespace pnnx {

class torch_repeat_interleave : public GraphRewriterPass


+ 1
- 1
tools/pnnx/src/pass_level5/fuse_layernorm.cpp View File

@@ -19,7 +19,7 @@
#include <math.h>
#include <string.h>

#include <torch/csrc/api/include/torch/torch.h>
#include <torch/csrc/api/include/torch/version.h>

namespace pnnx {



+ 1
- 1
tools/pnnx/src/pass_level5/fuse_scaled_dot_product_attention.cpp View File

@@ -19,7 +19,7 @@
#include <math.h>
#include <string.h>

#include <torch/csrc/api/include/torch/torch.h>
#include <torch/csrc/api/include/torch/version.h>

namespace pnnx {



+ 14
- 28
tools/pnnx/src/save_onnx.cpp View File

@@ -24,17 +24,6 @@

namespace pnnx {

// from cxxabi bridge
extern const char* get_operand_name(const Operand* x);
extern const char* get_operator_type(const Operator* op);
extern const char* get_operator_name(const Operator* op);
extern std::vector<const char*> get_operator_params_keys(const Operator* op);
extern std::vector<const char*> get_operator_attrs_keys(const Operator* op);
extern const Parameter& get_operator_param(const Operator* op, const char* key);
extern const Attribute& get_operator_attr(const Operator* op, const char* key);
extern const char* get_param_s(const Parameter& p);
extern std::vector<const char*> get_param_as(const Parameter& p);

int save_onnx(const Graph& g, const char* onnxpath, int fp16)
{
onnx::ModelProto model;
@@ -45,7 +34,7 @@ int save_onnx(const Graph& g, const char* onnxpath, int fp16)
{
onnx::ValueInfoProto* vip = gp->add_value_info();

vip->set_name(get_operand_name(x));
vip->set_name(x->name);

onnx::TypeProto* tp = vip->mutable_type();

@@ -108,27 +97,26 @@ int save_onnx(const Graph& g, const char* onnxpath, int fp16)
{
onnx::NodeProto* np = gp->add_node();

np->set_op_type(get_operator_type(op));
np->set_name(get_operator_name(op));
np->set_op_type(op->type);
np->set_name(op->name);

for (const Operand* oprand : op->inputs)
{
np->add_input(get_operand_name(oprand));
np->add_input(oprand->name);
}

for (const Operand* oprand : op->outputs)
{
np->add_output(get_operand_name(oprand));
np->add_output(oprand->name);
}

std::vector<const char*> params_keys = get_operator_params_keys(op);
for (const char* param_name : params_keys)
for (const auto& it : op->params)
{
const Parameter& param = get_operator_param(op, param_name);
const Parameter& param = it.second;

onnx::AttributeProto* ap = np->add_attribute();

ap->set_name(param_name);
ap->set_name(it.first);

if (param.type == 0)
{
@@ -156,7 +144,7 @@ int save_onnx(const Graph& g, const char* onnxpath, int fp16)
if (param.type == 4)
{
ap->set_type(onnx::AttributeProto::STRING);
ap->set_s(get_param_s(param));
ap->set_s(param.s);
}
if (param.type == 5)
{
@@ -177,24 +165,22 @@ int save_onnx(const Graph& g, const char* onnxpath, int fp16)
if (param.type == 7)
{
ap->set_type(onnx::AttributeProto::STRINGS);
std::vector<const char*> as = get_param_as(param);
for (auto s : as)
for (auto s : param.as)
{
ap->add_strings(s);
}
}
}

std::vector<const char*> attrs_keys = get_operator_attrs_keys(op);
for (const char* attr_name : attrs_keys)
for (const auto& it : op->attrs)
{
onnx::TensorProto* tp = gp->add_initializer();

tp->set_name(std::string(get_operator_name(op)) + "." + attr_name);
tp->set_name(op->name + "." + it.first);

np->add_input(std::string(get_operator_name(op)) + "." + attr_name);
np->add_input(op->name + "." + it.first);

const Attribute& attr = get_operator_attr(op, attr_name);
const Attribute& attr = it.second;
for (auto s : attr.shape)
{
tp->add_dims(s);


+ 0
- 81
tools/pnnx/src/save_onnx_cxxabi_bridge.cpp View File

@@ -1,81 +0,0 @@
// Tencent is pleased to support the open source community by making ncnn available.
//
// Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved.
//
// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// https://opensource.org/licenses/BSD-3-Clause
//
// 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.

#include "ir.h"

namespace pnnx {

const char* get_operand_name(const Operand* x)
{
return x->name.c_str();
}

const char* get_operator_type(const Operator* op)
{
return op->type.c_str();
}

const char* get_operator_name(const Operator* op)
{
return op->name.c_str();
}

std::vector<const char*> get_operator_params_keys(const Operator* op)
{
std::vector<const char*> keys;
for (const auto& it : op->params)
{
const std::string& key = it.first;
keys.push_back(key.c_str());
}
return keys;
}

std::vector<const char*> get_operator_attrs_keys(const Operator* op)
{
std::vector<const char*> keys;
for (const auto& it : op->attrs)
{
const std::string& key = it.first;
keys.push_back(key.c_str());
}
return keys;
}

const Parameter& get_operator_param(const Operator* op, const char* key)
{
return op->params.at(key);
}

const Attribute& get_operator_attr(const Operator* op, const char* key)
{
return op->attrs.at(key);
}

const char* get_param_s(const Parameter& p)
{
return p.s.c_str();
}

std::vector<const char*> get_param_as(const Parameter& p)
{
std::vector<const char*> as;
for (const auto& s : p.as)
{
as.push_back(s.c_str());
}
return as;
}

} // namespace pnnx

+ 0
- 11
tools/pnnx/src/utils.cpp View File

@@ -16,17 +16,6 @@

namespace pnnx {

const torch::jit::Node* find_node_by_kind(const std::shared_ptr<torch::jit::Graph>& graph, const std::string& kind)
{
for (const auto& n : graph->nodes())
{
if (n->kind().toDisplayString() == kind)
return n;
}

return 0;
}

unsigned short float32_to_float16(float value)
{
// 1 : 8 : 23


+ 11
- 2
tools/pnnx/src/utils.h View File

@@ -15,12 +15,21 @@
#ifndef PNNX_UTILS_H
#define PNNX_UTILS_H

#include <torch/script.h>
#include <torch/csrc/jit/api/module.h>
#if BUILD_TORCH2PNNX
#include <memory>
namespace torch {
namespace jit {
struct Graph;
struct Node;
} // namespace jit
} // namespace torch
#endif

namespace pnnx {

#if BUILD_TORCH2PNNX
const torch::jit::Node* find_node_by_kind(const std::shared_ptr<torch::jit::Graph>& graph, const std::string& kind);
#endif

unsigned short float32_to_float16(float value);



Loading…
Cancel
Save