diff --git a/tools/pnnx/CMakeLists.txt b/tools/pnnx/CMakeLists.txt index 0c8326fc9..377beb910 100644 --- a/tools/pnnx/CMakeLists.txt +++ b/tools/pnnx/CMakeLists.txt @@ -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 \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 \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() diff --git a/tools/pnnx/src/CMakeLists.txt b/tools/pnnx/src/CMakeLists.txt index 1ca95b6c3..aa64f71f4 100644 --- a/tools/pnnx/src/CMakeLists.txt +++ b/tools/pnnx/src/CMakeLists.txt @@ -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) diff --git a/tools/pnnx/src/ir.cpp b/tools/pnnx/src/ir.cpp index be86027ae..47bf293a4 100644 --- a/tools/pnnx/src/ir.cpp +++ b/tools/pnnx/src/ir.cpp @@ -23,11 +23,6 @@ #include #include -#if BUILD_PNNX -#include -#include -#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::max()) i64 = INT_MAX; - if (i64 == std::numeric_limits::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(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(); - if (i64 == std::numeric_limits::max()) i64 = INT_MAX; - if (i64 == std::numeric_limits::min()) i64 = INT_MIN; - i = (int)i64; - } - else if (t.scalar_type() == c10::ScalarType::Int) - { - type = 2; - i = t.item(); - } - else if (t.scalar_type() == c10::ScalarType::Double) - { - type = 3; - f = (float)t.item(); - } - else if (t.scalar_type() == c10::ScalarType::Float) - { - type = 3; - f = t.item(); - } - else if (t.scalar_type() == c10::ScalarType::ComplexDouble) - { - type = 10; - c = std::complex(t.item >()); - } - else if (t.scalar_type() == c10::ScalarType::ComplexFloat) - { - type = 10; - c = std::complex(t.item >()); - } - 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 i64s = value_node->ival(torch::jit::attr::value).toIntVector(); - for (auto i64 : i64s) - { - if (i64 == std::numeric_limits::max()) i64 = INT_MAX; - if (i64 == std::numeric_limits::min()) i64 = INT_MIN; - ai.push_back(i64); - } - break; - } - case c10::TypeKind::FloatType: - { - type = 6; - std::vector 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()->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(0.f, 0.f)); - continue; - } - - ac.push_back(std::complex(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()->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(); - memcpy((void*)data.data(), (const void*)&i, data.size()); - } - else if (t.scalar_type() == c10::ScalarType::Int) - { - int i = t.item(); - memcpy((void*)data.data(), (const void*)&i, data.size()); - } - else if (t.scalar_type() == c10::ScalarType::Double) - { - double f = t.item(); - memcpy((void*)data.data(), (const void*)&f, data.size()); - } - else if (t.scalar_type() == c10::ScalarType::Float) - { - float f = t.item(); - 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& _shape, const std::vector& 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(); - 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; diff --git a/tools/pnnx/src/ir.h b/tools/pnnx/src/ir.h index 8c3d89232..84bb46e76 100644 --- a/tools/pnnx/src/ir.h +++ b/tools/pnnx/src/ir.h @@ -24,7 +24,7 @@ #include #include -#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& _ai) + : type(5) + { + for (const auto& x : _ai) + { + int64_t _l = x; + if (_l == std::numeric_limits::max()) _l = INT_MAX; + if (_l == std::numeric_limits::min()) _l = INT_MIN; + ai.push_back((int)_l); + } + } Parameter(const std::initializer_list& _af) : type(6), af(_af) { @@ -165,10 +183,10 @@ public: ac.push_back(std::complex(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& shape, const std::vector& 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); diff --git a/tools/pnnx/src/load_torchscript.cpp b/tools/pnnx/src/load_torchscript.cpp new file mode 100644 index 000000000..12cc4129f --- /dev/null +++ b/tools/pnnx/src/load_torchscript.cpp @@ -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 +#else +#include +#endif + +#include +#include +#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::max()) i64 = INT_MAX; + if (i64 == std::numeric_limits::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(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(); + if (i64 == std::numeric_limits::max()) i64 = INT_MAX; + if (i64 == std::numeric_limits::min()) i64 = INT_MIN; + i = (int)i64; + } + else if (t.scalar_type() == c10::ScalarType::Int) + { + type = 2; + i = t.item(); + } + else if (t.scalar_type() == c10::ScalarType::Double) + { + type = 3; + f = (float)t.item(); + } + else if (t.scalar_type() == c10::ScalarType::Float) + { + type = 3; + f = t.item(); + } + else if (t.scalar_type() == c10::ScalarType::ComplexDouble) + { + type = 10; + c = std::complex(t.item >()); + } + else if (t.scalar_type() == c10::ScalarType::ComplexFloat) + { + type = 10; + c = std::complex(t.item >()); + } + 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 i64s = value_node->ival(torch::jit::attr::value).toIntVector(); + for (auto i64 : i64s) + { + if (i64 == std::numeric_limits::max()) i64 = INT_MAX; + if (i64 == std::numeric_limits::min()) i64 = INT_MIN; + ai.push_back(i64); + } + break; + } + case c10::TypeKind::FloatType: + { + type = 6; + std::vector 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()->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(0.f, 0.f)); + continue; + } + + ac.push_back(std::complex(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()->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(); + memcpy((void*)data.data(), (const void*)&i, data.size()); + } + else if (t.scalar_type() == c10::ScalarType::Int) + { + int i = t.item(); + memcpy((void*)data.data(), (const void*)&i, data.size()); + } + else if (t.scalar_type() == c10::ScalarType::Double) + { + double f = t.item(); + memcpy((void*)data.data(), (const void*)&f, data.size()); + } + else if (t.scalar_type() == c10::ScalarType::Float) + { + float f = t.item(); + 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(); + 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& 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 >& input_shapes, + const std::vector& input_types, + const std::vector >& input_shapes2, + const std::vector& input_types2, + const std::vector& customop_modules, + const std::vector& module_operators, + const std::string& foldable_constants_zippath, + std::set& 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 input_tensors; + for (size_t i = 0; i < input_shapes.size(); i++) + { + const std::vector& 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 input_tensors2; + for (size_t i = 0; i < input_shapes2.size(); i++) + { + const std::vector& 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 diff --git a/tools/pnnx/src/load_torchscript.h b/tools/pnnx/src/load_torchscript.h new file mode 100644 index 000000000..31a8a4217 --- /dev/null +++ b/tools/pnnx/src/load_torchscript.h @@ -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 >& input_shapes, + const std::vector& input_types, + const std::vector >& input_shapes2, + const std::vector& input_types2, + const std::vector& customop_modules, + const std::vector& module_operators, + const std::string& foldable_constants_zippath, + std::set& foldable_constants); + +} // namespace pnnx + +#endif // PNNX_LOAD_TORCHSCRIPT_H diff --git a/tools/pnnx/src/main.cpp b/tools/pnnx/src/main.cpp index 17b5d3fae..345107cb4 100644 --- a/tools/pnnx/src/main.cpp +++ b/tools/pnnx/src/main.cpp @@ -13,31 +13,25 @@ // specific language governing permissions and limitations under the License. #include +#include -#if _WIN32 -#include -#else -#include -#endif - +#include #include #include -#include - -#ifdef PNNX_TORCHVISION -// register torchvision ops via including headers -#include -#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 >& 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 input_tensors; - for (size_t i = 0; i < input_shapes.size(); i++) - { - const std::vector& 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 input_tensors2; - for (size_t i = 0; i < input_shapes2.size(); i++) - { - const std::vector& 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 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(); diff --git a/tools/pnnx/src/onnx.proto b/tools/pnnx/src/onnx.proto index 461bd0b78..15012ce65 100644 --- a/tools/pnnx/src/onnx.proto +++ b/tools/pnnx/src/onnx.proto @@ -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 + 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; + diff --git a/tools/pnnx/src/pass_level2/torch_permute.cpp b/tools/pnnx/src/pass_level2/torch_permute.cpp index fcf536bf3..cb17d7591 100644 --- a/tools/pnnx/src/pass_level2/torch_permute.cpp +++ b/tools/pnnx/src/pass_level2/torch_permute.cpp @@ -14,7 +14,7 @@ #include "pass_level2.h" -#include +#include namespace pnnx { diff --git a/tools/pnnx/src/pass_level2/torch_repeat_interleave.cpp b/tools/pnnx/src/pass_level2/torch_repeat_interleave.cpp index 0552e19a8..0f5442252 100644 --- a/tools/pnnx/src/pass_level2/torch_repeat_interleave.cpp +++ b/tools/pnnx/src/pass_level2/torch_repeat_interleave.cpp @@ -14,8 +14,6 @@ #include "pass_level2.h" -#include - namespace pnnx { class torch_repeat_interleave : public GraphRewriterPass diff --git a/tools/pnnx/src/pass_level5/fuse_layernorm.cpp b/tools/pnnx/src/pass_level5/fuse_layernorm.cpp index a723b4417..c52201f89 100644 --- a/tools/pnnx/src/pass_level5/fuse_layernorm.cpp +++ b/tools/pnnx/src/pass_level5/fuse_layernorm.cpp @@ -19,7 +19,7 @@ #include #include -#include +#include namespace pnnx { diff --git a/tools/pnnx/src/pass_level5/fuse_scaled_dot_product_attention.cpp b/tools/pnnx/src/pass_level5/fuse_scaled_dot_product_attention.cpp index e6f1489a9..38f137544 100644 --- a/tools/pnnx/src/pass_level5/fuse_scaled_dot_product_attention.cpp +++ b/tools/pnnx/src/pass_level5/fuse_scaled_dot_product_attention.cpp @@ -19,7 +19,7 @@ #include #include -#include +#include namespace pnnx { diff --git a/tools/pnnx/src/save_onnx.cpp b/tools/pnnx/src/save_onnx.cpp index 129959802..3406c730b 100644 --- a/tools/pnnx/src/save_onnx.cpp +++ b/tools/pnnx/src/save_onnx.cpp @@ -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 get_operator_params_keys(const Operator* op); -extern std::vector 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 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 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 as = get_param_as(param); - for (auto s : as) + for (auto s : param.as) { ap->add_strings(s); } } } - std::vector 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); diff --git a/tools/pnnx/src/save_onnx_cxxabi_bridge.cpp b/tools/pnnx/src/save_onnx_cxxabi_bridge.cpp deleted file mode 100644 index b74f2ab7a..000000000 --- a/tools/pnnx/src/save_onnx_cxxabi_bridge.cpp +++ /dev/null @@ -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 get_operator_params_keys(const Operator* op) -{ - std::vector keys; - for (const auto& it : op->params) - { - const std::string& key = it.first; - keys.push_back(key.c_str()); - } - return keys; -} - -std::vector get_operator_attrs_keys(const Operator* op) -{ - std::vector 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 get_param_as(const Parameter& p) -{ - std::vector as; - for (const auto& s : p.as) - { - as.push_back(s.c_str()); - } - return as; -} - -} // namespace pnnx diff --git a/tools/pnnx/src/utils.cpp b/tools/pnnx/src/utils.cpp index bfc8919c0..f7e52eebe 100644 --- a/tools/pnnx/src/utils.cpp +++ b/tools/pnnx/src/utils.cpp @@ -16,17 +16,6 @@ namespace pnnx { -const torch::jit::Node* find_node_by_kind(const std::shared_ptr& 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 diff --git a/tools/pnnx/src/utils.h b/tools/pnnx/src/utils.h index 1892d26a9..323d5ab48 100644 --- a/tools/pnnx/src/utils.h +++ b/tools/pnnx/src/utils.h @@ -15,12 +15,21 @@ #ifndef PNNX_UTILS_H #define PNNX_UTILS_H -#include -#include +#if BUILD_TORCH2PNNX +#include +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& graph, const std::string& kind); +#endif unsigned short float32_to_float16(float value);