diff --git a/.ci/pnnx.yml b/.ci/pnnx.yml index 3b09c9c1a..5c44354aa 100644 --- a/.ci/pnnx.yml +++ b/.ci/pnnx.yml @@ -75,7 +75,7 @@ jobs: python3 -m pip install --upgrade pip apt-get remove -y python3-setuptools pip3 install -U setuptools==69.5.1 - pip3 install -U pytest wheel twine requests einops + pip3 install -U pytest wheel twine requests einops numpy==1.26.4 cd ${{ci.workspace}} wget -q https://github.com/Kitware/CMake/releases/download/v3.29.3/cmake-3.29.3-linux-x86_64.tar.gz tar -xf cmake-3.29.3-linux-x86_64.tar.gz diff --git a/tools/pnnx/src/CMakeLists.txt b/tools/pnnx/src/CMakeLists.txt index 3bf9c7af7..bf45bccda 100644 --- a/tools/pnnx/src/CMakeLists.txt +++ b/tools/pnnx/src/CMakeLists.txt @@ -124,7 +124,9 @@ set(pnnx_pass_level2_SRCS pass_level2/F_conv2d.cpp pass_level2/F_conv3d.cpp pass_level2/F_conv_transpose123d.cpp + pass_level2/F_conv_transpose1d.cpp pass_level2/F_conv_transpose2d.cpp + pass_level2/F_conv_transpose3d.cpp pass_level2/F_dropout.cpp pass_level2/F_dropout23d.cpp pass_level2/F_elu.cpp @@ -650,6 +652,7 @@ if(onnxruntime_FOUND) pass_onnx/inline_containers.cpp pass_onnx/model_stat.cpp pass_onnx/shape_inference.cpp + pass_onnx/fuse_constant_as_attribute.cpp pass_onnx/nn_AdaptiveAvgPool2d.cpp pass_onnx/nn_AdaptiveAvgPool3d.cpp diff --git a/tools/pnnx/src/load_onnx.cpp b/tools/pnnx/src/load_onnx.cpp index 1c9f3ea9b..876485fa5 100644 --- a/tools/pnnx/src/load_onnx.cpp +++ b/tools/pnnx/src/load_onnx.cpp @@ -34,6 +34,7 @@ #include "pass_onnx/inline_containers.h" #include "pass_onnx/model_stat.h" #include "pass_onnx/shape_inference.h" +#include "pass_onnx/fuse_constant_as_attribute.h" #include "pass_onnx.h" @@ -652,6 +653,26 @@ int load_onnx(const std::string& onnxpath, Graph& pnnx_graph, } } + fprintf(stderr, "%-34s", "fuse_constant_as_attribute ... "); + + t0 = get_current_time(); + + onnx2pnnx::fuse_constant_as_attribute(model); + + t1 = get_current_time(); + + fprintf(stderr, "%8.2fms\n", t1 - t0); + + fprintf(stderr, "%-34s", "dead_code_elimination ... "); + + t0 = get_current_time(); + + onnx2pnnx::dead_code_elimination(model); + + t1 = get_current_time(); + + fprintf(stderr, "%8.2fms\n", t1 - t0); + onnx2pnnx::ModelStat newstat = onnx2pnnx::get_model_stat(model); onnx2pnnx::print_model_stat(oldstat, newstat); diff --git a/tools/pnnx/src/pass_level2/F_conv1d.cpp b/tools/pnnx/src/pass_level2/F_conv1d.cpp index af263b701..38455e6c6 100644 --- a/tools/pnnx/src/pass_level2/F_conv1d.cpp +++ b/tools/pnnx/src/pass_level2/F_conv1d.cpp @@ -77,4 +77,158 @@ pnnx.Output output 1 0 out REGISTER_GLOBAL_PNNX_GRAPH_REWRITER_PASS(F_convmode, 10) +class F_conv1d_onnx : public GraphRewriterPass +{ +public: + const char* match_pattern_graph() const + { + return R"PNNXIR(7767517 +5 4 +pnnx.Input input_0 0 1 input +pnnx.Input input_1 0 1 weight +pnnx.Input input_2 0 1 bias +Conv op_0 3 1 input weight bias out kernel_shape=%kernel_shape strides=%strides pads=%pads dilations=%dilations group=%group +pnnx.Output output 1 0 out +)PNNXIR"; + } + + const char* type_str() const + { + return "F.conv1d"; + } + + bool match(const std::map& captured_params) const + { + if (captured_params.at("kernel_shape").type != 5) + return false; + + if (captured_params.at("kernel_shape").ai.size() != 1) + return false; + + if (captured_params.at("strides").type != 5) + return false; + + if (captured_params.at("strides").ai.size() != 1) + return false; + + if (captured_params.at("dilations").type != 5) + return false; + + if (captured_params.at("dilations").ai.size() != 1) + return false; + + if (captured_params.at("group").type != 2) + return false; + + if (captured_params.at("pads").type != 5) + return false; + + const std::vector& pads = captured_params.at("pads").ai; + if (pads.size() != 2 || pads[0] != pads[1]) + return false; + + return true; + } + + void write(Operator* op, const std::map& captured_params) const + { + const std::vector& pads = captured_params.at("pads").ai; + + op->params["stride"] = captured_params.at("strides"); + op->params["dilation"] = captured_params.at("dilations"); + op->params["groups"] = captured_params.at("group"); + op->params["padding"] = {pads[0]}; + } +}; + +REGISTER_GLOBAL_PNNX_GRAPH_REWRITER_PASS(F_conv1d_onnx, 10) + +class F_conv1d_onnx_0 : public F_conv1d_onnx +{ +public: + const char* match_pattern_graph() const + { + return R"PNNXIR(7767517 +4 3 +pnnx.Input input_0 0 1 input +pnnx.Input input_1 0 1 weight +Conv op_0 2 1 input weight out kernel_shape=%kernel_shape strides=%strides pads=%pads dilations=%dilations group=%group +pnnx.Output output 1 0 out +)PNNXIR"; + } + + const char* replace_pattern_graph() const + { + return R"PNNXIR(7767517 +4 3 +pnnx.Input input_0 0 1 input +pnnx.Input input_1 0 1 weight +F.conv1d conv 2 1 input weight out bias=None +pnnx.Output output 1 0 out +)PNNXIR"; + } +}; + +REGISTER_GLOBAL_PNNX_GRAPH_REWRITER_PASS(F_conv1d_onnx_0, 10) + +class F_conv1d_onnx_1 : public GraphRewriterPass +{ +public: + const char* match_pattern_graph() const + { + return R"PNNXIR(7767517 +5 4 +pnnx.Input input_0 0 1 input +pnnx.Input input_1 0 1 weight +pnnx.Input input_2 0 1 bias +Conv op_0 3 1 input weight bias out strides=%strides pads=%pads dilations=%dilations group=%group auto_pad=NOTSET +pnnx.Output output 1 0 out +)PNNXIR"; + } + + const char* type_str() const + { + return "F.conv1d"; + } + + bool match(const std::map& captured_params) const + { + if (captured_params.at("strides").type != 5) + return false; + + if (captured_params.at("strides").ai.size() != 1) + return false; + + if (captured_params.at("dilations").type != 5) + return false; + + if (captured_params.at("dilations").ai.size() != 1) + return false; + + if (captured_params.at("group").type != 2) + return false; + + if (captured_params.at("pads").type != 5) + return false; + + const std::vector& pads = captured_params.at("pads").ai; + if (pads.size() != 2 || pads[0] != pads[1]) + return false; + + return true; + } + + void write(Operator* op, const std::map& captured_params) const + { + const std::vector& pads = captured_params.at("pads").ai; + + op->params["stride"] = captured_params.at("strides"); + op->params["dilation"] = captured_params.at("dilations"); + op->params["groups"] = captured_params.at("group"); + op->params["padding"] = {pads[0]}; + } +}; + +REGISTER_GLOBAL_PNNX_GRAPH_REWRITER_PASS(F_conv1d_onnx_1, 10) + } // namespace pnnx diff --git a/tools/pnnx/src/pass_level2/F_conv3d.cpp b/tools/pnnx/src/pass_level2/F_conv3d.cpp index 742417081..70d4daf9d 100644 --- a/tools/pnnx/src/pass_level2/F_conv3d.cpp +++ b/tools/pnnx/src/pass_level2/F_conv3d.cpp @@ -98,4 +98,158 @@ pnnx.Output output 1 0 out REGISTER_GLOBAL_PNNX_GRAPH_REWRITER_PASS(F_conv3d_0, 10) +class F_conv3d_onnx : public GraphRewriterPass +{ +public: + const char* match_pattern_graph() const + { + return R"PNNXIR(7767517 +5 4 +pnnx.Input input_0 0 1 input +pnnx.Input input_1 0 1 weight +pnnx.Input input_2 0 1 bias +Conv op_0 3 1 input weight bias out kernel_shape=%kernel_shape strides=%strides pads=%pads dilations=%dilations group=%group +pnnx.Output output 1 0 out +)PNNXIR"; + } + + const char* type_str() const + { + return "F.conv3d"; + } + + bool match(const std::map& captured_params) const + { + if (captured_params.at("kernel_shape").type != 5) + return false; + + if (captured_params.at("kernel_shape").ai.size() != 3) + return false; + + if (captured_params.at("strides").type != 5) + return false; + + if (captured_params.at("strides").ai.size() != 3) + return false; + + if (captured_params.at("dilations").type != 5) + return false; + + if (captured_params.at("dilations").ai.size() != 3) + return false; + + if (captured_params.at("group").type != 2) + return false; + + if (captured_params.at("pads").type != 5) + return false; + + const std::vector& pads = captured_params.at("pads").ai; + if (pads.size() != 6 || pads[0] != pads[3] || pads[1] != pads[4] || pads[2] != pads[5]) + return false; + + return true; + } + + void write(Operator* op, const std::map& captured_params) const + { + const std::vector& pads = captured_params.at("pads").ai; + + op->params["stride"] = captured_params.at("strides"); + op->params["dilation"] = captured_params.at("dilations"); + op->params["groups"] = captured_params.at("group"); + op->params["padding"] = {pads[0], pads[1], pads[2]}; + } +}; + +REGISTER_GLOBAL_PNNX_GRAPH_REWRITER_PASS(F_conv3d_onnx, 10) + +class F_conv3d_onnx_0 : public F_conv3d_onnx +{ +public: + const char* match_pattern_graph() const + { + return R"PNNXIR(7767517 +4 3 +pnnx.Input input_0 0 1 input +pnnx.Input input_1 0 1 weight +Conv op_0 2 1 input weight out kernel_shape=%kernel_shape strides=%strides pads=%pads dilations=%dilations group=%group +pnnx.Output output 1 0 out +)PNNXIR"; + } + + const char* replace_pattern_graph() const + { + return R"PNNXIR(7767517 +4 3 +pnnx.Input input_0 0 1 input +pnnx.Input input_1 0 1 weight +F.conv3d conv 2 1 input weight out bias=None +pnnx.Output output 1 0 out +)PNNXIR"; + } +}; + +REGISTER_GLOBAL_PNNX_GRAPH_REWRITER_PASS(F_conv3d_onnx_0, 10) + +class F_conv3d_onnx_1 : public GraphRewriterPass +{ +public: + const char* match_pattern_graph() const + { + return R"PNNXIR(7767517 +5 4 +pnnx.Input input_0 0 1 input +pnnx.Input input_1 0 1 weight +pnnx.Input input_2 0 1 bias +Conv op_0 3 1 input weight bias out strides=%strides pads=%pads dilations=%dilations group=%group auto_pad=NOTSET +pnnx.Output output 1 0 out +)PNNXIR"; + } + + const char* type_str() const + { + return "F.conv3d"; + } + + bool match(const std::map& captured_params) const + { + if (captured_params.at("strides").type != 5) + return false; + + if (captured_params.at("strides").ai.size() != 3) + return false; + + if (captured_params.at("dilations").type != 5) + return false; + + if (captured_params.at("dilations").ai.size() != 3) + return false; + + if (captured_params.at("group").type != 2) + return false; + + if (captured_params.at("pads").type != 5) + return false; + + const std::vector& pads = captured_params.at("pads").ai; + if (pads.size() != 6 || pads[0] != pads[3] || pads[1] != pads[4] || pads[2] != pads[5]) + return false; + + return true; + } + + void write(Operator* op, const std::map& captured_params) const + { + const std::vector& pads = captured_params.at("pads").ai; + + op->params["stride"] = captured_params.at("strides"); + op->params["dilation"] = captured_params.at("dilations"); + op->params["groups"] = captured_params.at("group"); + op->params["padding"] = {pads[0], pads[1], pads[2]}; + } +}; + +REGISTER_GLOBAL_PNNX_GRAPH_REWRITER_PASS(F_conv3d_onnx_1, 10) + } // namespace pnnx diff --git a/tools/pnnx/src/pass_level2/F_conv_transpose1d.cpp b/tools/pnnx/src/pass_level2/F_conv_transpose1d.cpp new file mode 100644 index 000000000..7ee146bcd --- /dev/null +++ b/tools/pnnx/src/pass_level2/F_conv_transpose1d.cpp @@ -0,0 +1,276 @@ +// 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 "pass_level2.h" + +namespace pnnx { + +class F_conv_transpose1d_onnx : public GraphRewriterPass +{ +public: + const char* match_pattern_graph() const + { + return R"PNNXIR(7767517 +5 4 +pnnx.Input input_0 0 1 input +pnnx.Input input_1 0 1 weight +pnnx.Input input_2 0 1 bias +ConvTranspose op_0 3 1 input weight bias out kernel_shape=%kernel_shape strides=%strides pads=%pads dilations=%dilations group=%group +pnnx.Output output 1 0 out +)PNNXIR"; + } + + const char* type_str() const + { + return "F.conv_transpose1d"; + } + + bool match(const std::map& captured_params) const + { + if (captured_params.at("kernel_shape").type != 5) + return false; + + if (captured_params.at("kernel_shape").ai.size() != 1) + return false; + + if (captured_params.at("strides").type != 5) + return false; + + if (captured_params.at("strides").ai.size() != 1) + return false; + + if (captured_params.at("dilations").type != 5) + return false; + + if (captured_params.at("dilations").ai.size() != 1) + return false; + + if (captured_params.at("group").type != 2) + return false; + + if (captured_params.at("pads").type != 5) + return false; + + const std::vector& pads = captured_params.at("pads").ai; + if (pads.size() != 2 || pads[0] != pads[1]) + return false; + + return true; + } + + void write(Operator* op, const std::map& captured_params) const + { + const std::vector& pads = captured_params.at("pads").ai; + + op->params["stride"] = captured_params.at("strides"); + op->params["dilation"] = captured_params.at("dilations"); + op->params["groups"] = captured_params.at("group"); + op->params["padding"] = {pads[0]}; + op->params["output_padding"] = {0}; + } +}; + +REGISTER_GLOBAL_PNNX_GRAPH_REWRITER_PASS(F_conv_transpose1d_onnx, 10) + +class F_conv_transpose1d_onnx_0 : public F_conv_transpose1d_onnx +{ +public: + const char* match_pattern_graph() const + { + return R"PNNXIR(7767517 +4 3 +pnnx.Input input_0 0 1 input +pnnx.Input input_1 0 1 weight +ConvTranspose op_0 2 1 input weight out kernel_shape=%kernel_shape strides=%strides pads=%pads dilations=%dilations group=%group +pnnx.Output output 1 0 out +)PNNXIR"; + } + + const char* replace_pattern_graph() const + { + return R"PNNXIR(7767517 +4 3 +pnnx.Input input_0 0 1 input +pnnx.Input input_1 0 1 weight +F.conv_transpose1d conv 2 1 input weight out bias=None +pnnx.Output output 1 0 out +)PNNXIR"; + } +}; + +REGISTER_GLOBAL_PNNX_GRAPH_REWRITER_PASS(F_conv_transpose1d_onnx_0, 10) + +class F_conv_transpose1d_onnx_1 : public GraphRewriterPass +{ +public: + const char* match_pattern_graph() const + { + return R"PNNXIR(7767517 +5 4 +pnnx.Input input_0 0 1 input +pnnx.Input input_1 0 1 weight +pnnx.Input input_2 0 1 bias +ConvTranspose op_0 3 1 input weight bias out strides=%strides pads=%pads dilations=%dilations group=%group auto_pad=NOTSET +pnnx.Output output 1 0 out +)PNNXIR"; + } + + const char* type_str() const + { + return "F.conv_transpose1d"; + } + + bool match(const std::map& captured_params) const + { + if (captured_params.at("strides").type != 5) + return false; + + if (captured_params.at("strides").ai.size() != 1) + return false; + + if (captured_params.at("dilations").type != 5) + return false; + + if (captured_params.at("dilations").ai.size() != 1) + return false; + + if (captured_params.at("group").type != 2) + return false; + + if (captured_params.at("pads").type != 5) + return false; + + const std::vector& pads = captured_params.at("pads").ai; + if (pads.size() != 2 || pads[0] != pads[1]) + return false; + + return true; + } + + void write(Operator* op, const std::map& captured_params) const + { + const std::vector& pads = captured_params.at("pads").ai; + + op->params["stride"] = captured_params.at("strides"); + op->params["dilation"] = captured_params.at("dilations"); + op->params["groups"] = captured_params.at("group"); + op->params["padding"] = {pads[0]}; + op->params["output_padding"] = {0}; + } +}; + +REGISTER_GLOBAL_PNNX_GRAPH_REWRITER_PASS(F_conv_transpose1d_onnx_1, 10) + +class F_conv_transpose1d_onnx_2 : public GraphRewriterPass +{ +public: + const char* match_pattern_graph() const + { + return R"PNNXIR(7767517 +5 4 +pnnx.Input input_0 0 1 input +pnnx.Input input_1 0 1 weight +pnnx.Input input_2 0 1 bias +ConvTranspose op_0 3 1 input weight bias out kernel_shape=%kernel_shape strides=%strides pads=%pads dilations=%dilations group=%group output_padding=%output_padding +pnnx.Output output 1 0 out +)PNNXIR"; + } + + const char* type_str() const + { + return "F.conv_transpose1d"; + } + + bool match(const std::map& captured_params) const + { + if (captured_params.at("kernel_shape").type != 5) + return false; + + if (captured_params.at("kernel_shape").ai.size() != 1) + return false; + + if (captured_params.at("strides").type != 5) + return false; + + if (captured_params.at("strides").ai.size() != 1) + return false; + + if (captured_params.at("dilations").type != 5) + return false; + + if (captured_params.at("dilations").ai.size() != 1) + return false; + + if (captured_params.at("output_padding").type != 5) + return false; + + if (captured_params.at("output_padding").ai.size() != 1) + return false; + + if (captured_params.at("group").type != 2) + return false; + + if (captured_params.at("pads").type != 5) + return false; + + const std::vector& pads = captured_params.at("pads").ai; + if (pads.size() != 2 || pads[0] != pads[1]) + return false; + + return true; + } + + void write(Operator* op, const std::map& captured_params) const + { + const std::vector& pads = captured_params.at("pads").ai; + + op->params["stride"] = captured_params.at("strides"); + op->params["dilation"] = captured_params.at("dilations"); + op->params["groups"] = captured_params.at("group"); + op->params["padding"] = {pads[0]}; + op->params["output_padding"] = captured_params.at("output_padding"); + } +}; + +REGISTER_GLOBAL_PNNX_GRAPH_REWRITER_PASS(F_conv_transpose1d_onnx_2, 10) + +class F_conv_transpose1d_onnx_3 : public F_conv_transpose1d_onnx_2 +{ +public: + const char* match_pattern_graph() const + { + return R"PNNXIR(7767517 +4 3 +pnnx.Input input_0 0 1 input +pnnx.Input input_1 0 1 weight +ConvTranspose op_0 2 1 input weight out kernel_shape=%kernel_shape strides=%strides pads=%pads dilations=%dilations group=%group output_padding=%output_padding +pnnx.Output output 1 0 out +)PNNXIR"; + } + + const char* replace_pattern_graph() const + { + return R"PNNXIR(7767517 +4 3 +pnnx.Input input_0 0 1 input +pnnx.Input input_1 0 1 weight +F.conv_transpose1d conv 2 1 input weight out bias=None +pnnx.Output output 1 0 out +)PNNXIR"; + } +}; + +REGISTER_GLOBAL_PNNX_GRAPH_REWRITER_PASS(F_conv_transpose1d_onnx_3, 10) + +} // namespace pnnx diff --git a/tools/pnnx/src/pass_level2/F_conv_transpose2d.cpp b/tools/pnnx/src/pass_level2/F_conv_transpose2d.cpp index 9d51d11ea..f30e3a009 100644 --- a/tools/pnnx/src/pass_level2/F_conv_transpose2d.cpp +++ b/tools/pnnx/src/pass_level2/F_conv_transpose2d.cpp @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making ncnn available. // -// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +// 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 @@ -172,4 +172,105 @@ pnnx.Output output 1 0 out REGISTER_GLOBAL_PNNX_GRAPH_REWRITER_PASS(F_conv_transpose2d_onnx_1, 10) +class F_conv_transpose2d_onnx_2 : public GraphRewriterPass +{ +public: + const char* match_pattern_graph() const + { + return R"PNNXIR(7767517 +5 4 +pnnx.Input input_0 0 1 input +pnnx.Input input_1 0 1 weight +pnnx.Input input_2 0 1 bias +ConvTranspose op_0 3 1 input weight bias out kernel_shape=%kernel_shape strides=%strides pads=%pads dilations=%dilations group=%group output_padding=%output_padding +pnnx.Output output 1 0 out +)PNNXIR"; + } + + const char* type_str() const + { + return "F.conv_transpose2d"; + } + + bool match(const std::map& captured_params) const + { + if (captured_params.at("kernel_shape").type != 5) + return false; + + if (captured_params.at("kernel_shape").ai.size() != 2) + return false; + + if (captured_params.at("strides").type != 5) + return false; + + if (captured_params.at("strides").ai.size() != 2) + return false; + + if (captured_params.at("dilations").type != 5) + return false; + + if (captured_params.at("dilations").ai.size() != 2) + return false; + + if (captured_params.at("output_padding").type != 5) + return false; + + if (captured_params.at("output_padding").ai.size() != 2) + return false; + + if (captured_params.at("group").type != 2) + return false; + + if (captured_params.at("pads").type != 5) + return false; + + const std::vector& pads = captured_params.at("pads").ai; + if (pads.size() != 4 || pads[0] != pads[2] || pads[1] != pads[3]) + return false; + + return true; + } + + void write(Operator* op, const std::map& captured_params) const + { + const std::vector& pads = captured_params.at("pads").ai; + + op->params["stride"] = captured_params.at("strides"); + op->params["dilation"] = captured_params.at("dilations"); + op->params["groups"] = captured_params.at("group"); + op->params["padding"] = {pads[0], pads[1]}; + op->params["output_padding"] = captured_params.at("output_padding"); + } +}; + +REGISTER_GLOBAL_PNNX_GRAPH_REWRITER_PASS(F_conv_transpose2d_onnx_2, 10) + +class F_conv_transpose2d_onnx_3 : public F_conv_transpose2d_onnx_2 +{ +public: + const char* match_pattern_graph() const + { + return R"PNNXIR(7767517 +4 3 +pnnx.Input input_0 0 1 input +pnnx.Input input_1 0 1 weight +ConvTranspose op_0 2 1 input weight out kernel_shape=%kernel_shape strides=%strides pads=%pads dilations=%dilations group=%group output_padding=%output_padding +pnnx.Output output 1 0 out +)PNNXIR"; + } + + const char* replace_pattern_graph() const + { + return R"PNNXIR(7767517 +4 3 +pnnx.Input input_0 0 1 input +pnnx.Input input_1 0 1 weight +F.conv_transpose2d conv 2 1 input weight out bias=None +pnnx.Output output 1 0 out +)PNNXIR"; + } +}; + +REGISTER_GLOBAL_PNNX_GRAPH_REWRITER_PASS(F_conv_transpose2d_onnx_3, 10) + } // namespace pnnx diff --git a/tools/pnnx/src/pass_level2/F_conv_transpose3d.cpp b/tools/pnnx/src/pass_level2/F_conv_transpose3d.cpp new file mode 100644 index 000000000..50dbb7539 --- /dev/null +++ b/tools/pnnx/src/pass_level2/F_conv_transpose3d.cpp @@ -0,0 +1,276 @@ +// 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 "pass_level2.h" + +namespace pnnx { + +class F_conv_transpose3d_onnx : public GraphRewriterPass +{ +public: + const char* match_pattern_graph() const + { + return R"PNNXIR(7767517 +5 4 +pnnx.Input input_0 0 1 input +pnnx.Input input_1 0 1 weight +pnnx.Input input_2 0 1 bias +ConvTranspose op_0 3 1 input weight bias out kernel_shape=%kernel_shape strides=%strides pads=%pads dilations=%dilations group=%group +pnnx.Output output 1 0 out +)PNNXIR"; + } + + const char* type_str() const + { + return "F.conv_transpose3d"; + } + + bool match(const std::map& captured_params) const + { + if (captured_params.at("kernel_shape").type != 5) + return false; + + if (captured_params.at("kernel_shape").ai.size() != 3) + return false; + + if (captured_params.at("strides").type != 5) + return false; + + if (captured_params.at("strides").ai.size() != 3) + return false; + + if (captured_params.at("dilations").type != 5) + return false; + + if (captured_params.at("dilations").ai.size() != 3) + return false; + + if (captured_params.at("group").type != 2) + return false; + + if (captured_params.at("pads").type != 5) + return false; + + const std::vector& pads = captured_params.at("pads").ai; + if (pads.size() != 6 || pads[0] != pads[3] || pads[1] != pads[4] || pads[2] != pads[5]) + return false; + + return true; + } + + void write(Operator* op, const std::map& captured_params) const + { + const std::vector& pads = captured_params.at("pads").ai; + + op->params["stride"] = captured_params.at("strides"); + op->params["dilation"] = captured_params.at("dilations"); + op->params["groups"] = captured_params.at("group"); + op->params["padding"] = {pads[0], pads[1], pads[2]}; + op->params["output_padding"] = {0, 0, 0}; + } +}; + +REGISTER_GLOBAL_PNNX_GRAPH_REWRITER_PASS(F_conv_transpose3d_onnx, 10) + +class F_conv_transpose3d_onnx_0 : public F_conv_transpose3d_onnx +{ +public: + const char* match_pattern_graph() const + { + return R"PNNXIR(7767517 +4 3 +pnnx.Input input_0 0 1 input +pnnx.Input input_1 0 1 weight +ConvTranspose op_0 2 1 input weight out kernel_shape=%kernel_shape strides=%strides pads=%pads dilations=%dilations group=%group +pnnx.Output output 1 0 out +)PNNXIR"; + } + + const char* replace_pattern_graph() const + { + return R"PNNXIR(7767517 +4 3 +pnnx.Input input_0 0 1 input +pnnx.Input input_1 0 1 weight +F.conv_transpose3d conv 2 1 input weight out bias=None +pnnx.Output output 1 0 out +)PNNXIR"; + } +}; + +REGISTER_GLOBAL_PNNX_GRAPH_REWRITER_PASS(F_conv_transpose3d_onnx_0, 10) + +class F_conv_transpose3d_onnx_1 : public GraphRewriterPass +{ +public: + const char* match_pattern_graph() const + { + return R"PNNXIR(7767517 +5 4 +pnnx.Input input_0 0 1 input +pnnx.Input input_1 0 1 weight +pnnx.Input input_2 0 1 bias +ConvTranspose op_0 3 1 input weight bias out strides=%strides pads=%pads dilations=%dilations group=%group auto_pad=NOTSET +pnnx.Output output 1 0 out +)PNNXIR"; + } + + const char* type_str() const + { + return "F.conv_transpose3d"; + } + + bool match(const std::map& captured_params) const + { + if (captured_params.at("strides").type != 5) + return false; + + if (captured_params.at("strides").ai.size() != 3) + return false; + + if (captured_params.at("dilations").type != 5) + return false; + + if (captured_params.at("dilations").ai.size() != 3) + return false; + + if (captured_params.at("group").type != 2) + return false; + + if (captured_params.at("pads").type != 5) + return false; + + const std::vector& pads = captured_params.at("pads").ai; + if (pads.size() != 6 || pads[0] != pads[3] || pads[1] != pads[4] || pads[2] != pads[5]) + return false; + + return true; + } + + void write(Operator* op, const std::map& captured_params) const + { + const std::vector& pads = captured_params.at("pads").ai; + + op->params["stride"] = captured_params.at("strides"); + op->params["dilation"] = captured_params.at("dilations"); + op->params["groups"] = captured_params.at("group"); + op->params["padding"] = {pads[0], pads[1], pads[2]}; + op->params["output_padding"] = {0, 0, 0}; + } +}; + +REGISTER_GLOBAL_PNNX_GRAPH_REWRITER_PASS(F_conv_transpose3d_onnx_1, 10) + +class F_conv_transpose3d_onnx_2 : public GraphRewriterPass +{ +public: + const char* match_pattern_graph() const + { + return R"PNNXIR(7767517 +5 4 +pnnx.Input input_0 0 1 input +pnnx.Input input_1 0 1 weight +pnnx.Input input_2 0 1 bias +ConvTranspose op_0 3 1 input weight bias out kernel_shape=%kernel_shape strides=%strides pads=%pads dilations=%dilations group=%group output_padding=%output_padding +pnnx.Output output 1 0 out +)PNNXIR"; + } + + const char* type_str() const + { + return "F.conv_transpose3d"; + } + + bool match(const std::map& captured_params) const + { + if (captured_params.at("kernel_shape").type != 5) + return false; + + if (captured_params.at("kernel_shape").ai.size() != 3) + return false; + + if (captured_params.at("strides").type != 5) + return false; + + if (captured_params.at("strides").ai.size() != 3) + return false; + + if (captured_params.at("dilations").type != 5) + return false; + + if (captured_params.at("dilations").ai.size() != 3) + return false; + + if (captured_params.at("output_padding").type != 5) + return false; + + if (captured_params.at("output_padding").ai.size() != 3) + return false; + + if (captured_params.at("group").type != 2) + return false; + + if (captured_params.at("pads").type != 5) + return false; + + const std::vector& pads = captured_params.at("pads").ai; + if (pads.size() != 6 || pads[0] != pads[3] || pads[1] != pads[4] || pads[2] != pads[5]) + return false; + + return true; + } + + void write(Operator* op, const std::map& captured_params) const + { + const std::vector& pads = captured_params.at("pads").ai; + + op->params["stride"] = captured_params.at("strides"); + op->params["dilation"] = captured_params.at("dilations"); + op->params["groups"] = captured_params.at("group"); + op->params["padding"] = {pads[0], pads[1], pads[2]}; + op->params["output_padding"] = captured_params.at("output_padding"); + } +}; + +REGISTER_GLOBAL_PNNX_GRAPH_REWRITER_PASS(F_conv_transpose3d_onnx_2, 10) + +class F_conv_transpose3d_onnx_3 : public F_conv_transpose3d_onnx_2 +{ +public: + const char* match_pattern_graph() const + { + return R"PNNXIR(7767517 +4 3 +pnnx.Input input_0 0 1 input +pnnx.Input input_1 0 1 weight +ConvTranspose op_0 2 1 input weight out kernel_shape=%kernel_shape strides=%strides pads=%pads dilations=%dilations group=%group output_padding=%output_padding +pnnx.Output output 1 0 out +)PNNXIR"; + } + + const char* replace_pattern_graph() const + { + return R"PNNXIR(7767517 +4 3 +pnnx.Input input_0 0 1 input +pnnx.Input input_1 0 1 weight +F.conv_transpose3d conv 2 1 input weight out bias=None +pnnx.Output output 1 0 out +)PNNXIR"; + } +}; + +REGISTER_GLOBAL_PNNX_GRAPH_REWRITER_PASS(F_conv_transpose3d_onnx_3, 10) + +} // namespace pnnx diff --git a/tools/pnnx/src/pass_level2/F_pad.cpp b/tools/pnnx/src/pass_level2/F_pad.cpp index b30ea045a..b28183335 100644 --- a/tools/pnnx/src/pass_level2/F_pad.cpp +++ b/tools/pnnx/src/pass_level2/F_pad.cpp @@ -265,10 +265,9 @@ public: const char* match_pattern_graph() const { return R"PNNXIR(7767517 -4 3 -pnnx.Input input_0 0 1 input -pnnx.Input input_1 0 1 pad -Pad op_0 2 1 input pad out mode=constant +3 2 +pnnx.Input input 0 1 input +Pad op_0 1 1 input out mode=%mode pads=%pads pnnx.Output output 1 0 out )PNNXIR"; } @@ -277,8 +276,83 @@ pnnx.Output output 1 0 out { return "F.pad"; } + + void write(Operator* op, const std::map& captured_params) const + { + const std::string& mode = captured_params.at("mode").s; + if (mode == "constant") op->params["mode"] = "constant"; + if (mode == "reflect") op->params["mode"] = "reflect"; + if (mode == "edge") op->params["mode"] = "replicate"; + if (mode == "wrap") op->params["mode"] = "circular"; + + const std::vector& pads = captured_params.at("pads").ai; + + if (pads.size() == 2) + op->params["pad"] = pads; + + if (pads.size() == 4) + { + if (pads[0] == 0 && pads[2] == 0) + op->params["pad"] = std::vector{pads[1], pads[3]}; + else + op->params["pad"] = std::vector{pads[1], pads[3], pads[0], pads[2]}; + } + + if (pads.size() == 6) + { + if (pads[1] == 0 && pads[4] == 0 && pads[0] == 0 && pads[3] == 0) + op->params["pad"] = std::vector{pads[2], pads[5]}; + else if (pads[0] == 0 && pads[3] == 0) + op->params["pad"] = std::vector{pads[2], pads[5], pads[1], pads[4]}; + else + op->params["pad"] = std::vector{pads[2], pads[5], pads[1], pads[4], pads[0], pads[3]}; + } + + if (pads.size() == 8) + { + if (pads[1] == 0 && pads[5] == 0 && pads[0] == 0 && pads[4] == 0) + op->params["pad"] = std::vector{pads[3], pads[7], pads[2], pads[6]}; + else if (pads[0] == 0 && pads[4] == 0) + op->params["pad"] = std::vector{pads[3], pads[7], pads[2], pads[6], pads[1], pads[5]}; + else + op->params["pad"] = std::vector{pads[3], pads[7], pads[2], pads[6], pads[1], pads[5], pads[0], pads[4]}; + } + + if (pads.size() == 10) + { + if (pads[1] == 0 && pads[6] == 0 && pads[0] == 0 && pads[5] == 0) + op->params["pad"] = std::vector{pads[4], pads[9], pads[3], pads[8], pads[2], pads[7]}; + else if (pads[0] == 0 && pads[5] == 0) + op->params["pad"] = std::vector{pads[4], pads[9], pads[3], pads[8], pads[2], pads[7], pads[1], pads[6]}; + else + op->params["pad"] = std::vector{pads[4], pads[9], pads[3], pads[8], pads[2], pads[7], pads[1], pads[6], pads[0], pads[5]}; + } + } }; REGISTER_GLOBAL_PNNX_GRAPH_REWRITER_PASS(F_pad_onnx, 10) +class F_pad_onnx_1 : public F_pad_onnx +{ +public: + const char* match_pattern_graph() const + { + return R"PNNXIR(7767517 +3 2 +pnnx.Input input 0 1 input +Pad op_0 1 1 input out mode=%mode pads=%pads value=%value +pnnx.Output output 1 0 out +)PNNXIR"; + } + + void write(Operator* op, const std::map& captured_params) const + { + F_pad_onnx::write(op, captured_params); + + op->params["value"] = captured_params.at("value"); + } +}; + +REGISTER_GLOBAL_PNNX_GRAPH_REWRITER_PASS(F_pad_onnx_1, 10) + } // namespace pnnx diff --git a/tools/pnnx/src/pass_level2/Tensor_reshape.cpp b/tools/pnnx/src/pass_level2/Tensor_reshape.cpp index e71349e65..1c578a8d6 100644 --- a/tools/pnnx/src/pass_level2/Tensor_reshape.cpp +++ b/tools/pnnx/src/pass_level2/Tensor_reshape.cpp @@ -113,4 +113,41 @@ pnnx.Output output 1 0 out REGISTER_GLOBAL_PNNX_GRAPH_REWRITER_PASS(Tensor_reshape_onnx_3, 20) +class Tensor_reshape_onnx_4 : public GraphRewriterPass +{ +public: + const char* match_pattern_graph() const + { + return R"PNNXIR(7767517 +3 2 +pnnx.Input input 0 1 input +Reshape op_1 1 1 input out shape=%shape allowzero=* +pnnx.Output output 1 0 out +)PNNXIR"; + } + + const char* type_str() const + { + return "Tensor.reshape"; + } +}; + +REGISTER_GLOBAL_PNNX_GRAPH_REWRITER_PASS(Tensor_reshape_onnx_4, 20) + +class Tensor_reshape_onnx_5 : public Tensor_reshape_onnx_4 +{ +public: + const char* match_pattern_graph() const + { + return R"PNNXIR(7767517 +3 2 +pnnx.Input input 0 1 input +Reshape op_1 1 1 input out shape=%shape +pnnx.Output output 1 0 out +)PNNXIR"; + } +}; + +REGISTER_GLOBAL_PNNX_GRAPH_REWRITER_PASS(Tensor_reshape_onnx_5, 20) + } // namespace pnnx diff --git a/tools/pnnx/src/pass_level2/torch_mean.cpp b/tools/pnnx/src/pass_level2/torch_mean.cpp index 48701c899..18944fac3 100644 --- a/tools/pnnx/src/pass_level2/torch_mean.cpp +++ b/tools/pnnx/src/pass_level2/torch_mean.cpp @@ -180,4 +180,31 @@ pnnx.Output output 1 0 out REGISTER_GLOBAL_PNNX_GRAPH_REWRITER_PASS(torch_mean_onnx_2, 20) +class torch_mean_onnx_3 : public GraphRewriterPass +{ +public: + const char* match_pattern_graph() const + { + return R"PNNXIR(7767517 +3 2 +pnnx.Input input 0 1 input +ReduceMean op_0 1 1 input out axes=%axes keepdims=%keepdims noop_with_empty_axes=0 +pnnx.Output output 1 0 out +)PNNXIR"; + } + + const char* type_str() const + { + return "torch.mean"; + } + + void write(Operator* op, const std::map& captured_params) const + { + op->params["dim"] = captured_params.at("axes"); + op->params["keepdim"] = captured_params.at("keepdims").i ? true : false; + } +}; + +REGISTER_GLOBAL_PNNX_GRAPH_REWRITER_PASS(torch_mean_onnx_3, 20) + } // namespace pnnx diff --git a/tools/pnnx/src/pass_level5/fuse_pad_conv2d.cpp b/tools/pnnx/src/pass_level5/fuse_pad_conv2d.cpp index 0823168ce..df86d5a2b 100644 --- a/tools/pnnx/src/pass_level5/fuse_pad_conv2d.cpp +++ b/tools/pnnx/src/pass_level5/fuse_pad_conv2d.cpp @@ -42,7 +42,7 @@ pnnx.Output output 1 0 out const char* name_str() const { - return "conv2d"; + return "padconv2d"; } bool match(const std::map& captured_params) const @@ -131,7 +131,7 @@ pnnx.Output output 1 0 out const char* name_str() const { - return "conv2d"; + return "padconv2d"; } bool match(const std::map& captured_params) const @@ -215,7 +215,7 @@ pnnx.Output output 1 0 out const char* name_str() const { - return "conv2d"; + return "padconv2d"; } bool match(const std::map& captured_params) const @@ -293,7 +293,7 @@ pnnx.Output output 1 0 out const char* name_str() const { - return "conv2d"; + return "padconv2d"; } bool match(const std::map& captured_params) const @@ -362,7 +362,7 @@ pnnx.Output output 1 0 out const char* name_str() const { - return "conv2d"; + return "padconv2d"; } bool match(const std::map& captured_params) const @@ -431,7 +431,7 @@ pnnx.Output output 1 0 out const char* name_str() const { - return "conv2d"; + return "padconv2d"; } bool match(const std::map& captured_params) const diff --git a/tools/pnnx/src/pass_onnx.cpp b/tools/pnnx/src/pass_onnx.cpp index 3bd4c64fe..8cb28a5f1 100644 --- a/tools/pnnx/src/pass_onnx.cpp +++ b/tools/pnnx/src/pass_onnx.cpp @@ -797,15 +797,6 @@ void pass_onnx(const onnx::ModelProto& model, Graph& pnnx_graph) { if (is_aten_op) is_attr_list = true; - - if (sim_op_type == "Reshape" && j == 1) - is_attr_list = true; - - if (sim_op_type == "Pad" && j == 1) - is_attr_list = true; - - if (sim_op_type == "ReduceMean" && j == 1) - is_attr_list = true; } bool is_attr_weight = false; diff --git a/tools/pnnx/src/pass_onnx/fuse_constant_as_attribute.cpp b/tools/pnnx/src/pass_onnx/fuse_constant_as_attribute.cpp new file mode 100644 index 000000000..27df7b60e --- /dev/null +++ b/tools/pnnx/src/pass_onnx/fuse_constant_as_attribute.cpp @@ -0,0 +1,271 @@ +// 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 "fuse_constant_as_attribute.h" + +#include +#include +#include + +#include + +namespace pnnx { + +namespace onnx2pnnx { + +struct constant_as_attribute +{ + const char* op_type; + int input_index; + const char* attribute; +}; + +static constant_as_attribute caas[] = { + {"Pad", 1, "pads"}, + {"Pad", 2, "value"}, + {"ReduceMean", 1, "axes"}, + {"Reshape", 1, "shape"}, +}; + +static const char* get_constant_as_attribute(const std::string& op_type, int input_index) +{ + const int caas_count = sizeof(caas) / sizeof(caas[0]); + for (int i = 0; i < caas_count; i++) + { + if (op_type == caas[i].op_type && input_index == caas[i].input_index) + return caas[i].attribute; + } + + return NULL; +} + +void fuse_constant_as_attribute(onnx::ModelProto& model) +{ + // collect initializers + std::unordered_map initializers; + { + const onnx::GraphProto& graph = model.graph(); + for (int i = 0; i < graph.initializer_size(); i++) + { + initializers.insert(std::make_pair(graph.initializer(i).name(), i)); + } + } + + onnx::GraphProto* graph = model.mutable_graph(); + + for (int i = 0; i < graph->node_size(); i++) + { + onnx::NodeProto* node = graph->mutable_node(i); + if (!node->domain().empty()) + { + // native onnx op + continue; + } + + const std::string& op_type = node->op_type(); + + std::vector fused_input_indexes; + for (int j = 0; j < node->input_size(); j++) + { + const std::string& input = node->input(j); + if (input.empty()) + continue; + + if (initializers.find(input) == initializers.end()) + continue; + + const char* attr_name = get_constant_as_attribute(op_type, j); + if (!attr_name) + continue; + + // fprintf(stderr, "fuse_constant_as_attribute %s %d -> %s\n", op_type.c_str(), j, attr_name); + + const onnx::TensorProto& tensor = graph->initializer(initializers.at(input)); + + int64_t numel = 1; + for (int k = 0; k < tensor.dims_size(); k++) + { + numel *= tensor.dims(k); + } + + if (numel == 1) + { + // int or float scalar + if (tensor.data_type() == onnx::TensorProto::INT32) + { + int i32; + if (tensor.has_raw_data()) + { + // assert tensor.raw_data().size() == 4 + i32 = ((int*)tensor.raw_data().data())[0]; + } + else + { + // assert tensor.int32_data().size() == 1 + i32 = tensor.int32_data().at(0); + } + + onnx::AttributeProto* attr = node->add_attribute(); + attr->set_name(std::string(attr_name)); + attr->set_type(onnx::AttributeProto::INT); + attr->set_i(i32); + } + else if (tensor.data_type() == onnx::TensorProto::INT64) + { + int64_t i64; + if (tensor.has_raw_data()) + { + // assert tensor.raw_data().size() == 8 + i64 = ((int64_t*)tensor.raw_data().data())[0]; + } + else + { + // assert tensor.int64_data().size() == 1 + i64 = tensor.int64_data().at(0); + } + + if (i64 == std::numeric_limits::max()) i64 = INT_MAX; + if (i64 == std::numeric_limits::min()) i64 = INT_MIN; + + onnx::AttributeProto* attr = node->add_attribute(); + attr->set_name(std::string(attr_name)); + attr->set_type(onnx::AttributeProto::INT); + attr->set_i((int)i64); + } + else if (tensor.data_type() == onnx::TensorProto::FLOAT) + { + float f32; + if (tensor.has_raw_data()) + { + // assert tensor.raw_data().size() == 4 + f32 = ((float*)tensor.raw_data().data())[0]; + } + else + { + // assert tensor.float_data().size() == 1 + f32 = tensor.float_data().at(0); + } + + onnx::AttributeProto* attr = node->add_attribute(); + attr->set_name(std::string(attr_name)); + attr->set_type(onnx::AttributeProto::FLOAT); + attr->set_f(f32); + } + else if (tensor.data_type() == onnx::TensorProto::BOOL) + { + bool bb; + if (tensor.has_raw_data()) + { + // assert tensor.raw_data().size() == 2 + bb = ((uint16_t*)tensor.raw_data().data())[0] ? true : false; + } + else + { + // assert tensor.int32_data().size() == 1 + bb = tensor.int32_data().at(0) ? true : false; + } + + onnx::AttributeProto* attr = node->add_attribute(); + attr->set_name(std::string(attr_name)); + attr->set_type(onnx::AttributeProto::INT); + attr->set_i(bb ? 1 : 0); + } + else + { + fprintf(stderr, "unknown constant scalar type %d\n", (int)tensor.data_type()); + continue; + } + } + else if (tensor.dims_size() == 1) + { + // int list + const int list_size = tensor.dims(0); + if (tensor.data_type() == onnx::TensorProto::INT32) + { + std::vector ai(list_size); + if (tensor.has_raw_data()) + { + // assert tensor.raw_data().size() == 4 * list_size + memcpy((void*)ai.data(), (int*)tensor.raw_data().data(), sizeof(int) * list_size); + } + else + { + // assert tensor.int32_data().size() == list_size + memcpy((void*)ai.data(), tensor.int32_data().data(), sizeof(int) * list_size); + } + + onnx::AttributeProto* attr = node->add_attribute(); + attr->set_name(std::string(attr_name)); + attr->set_type(onnx::AttributeProto::INTS); + for (auto i32 : ai) + { + attr->add_ints(i32); + } + } + else if (tensor.data_type() == onnx::TensorProto::INT64) + { + std::vector ai(list_size); + if (tensor.has_raw_data()) + { + // assert tensor.raw_data().size() == 8 * list_size + memcpy((void*)ai.data(), (int64_t*)tensor.raw_data().data(), sizeof(int64_t) * list_size); + } + else + { + // assert tensor.int64_data().size() == list_size + memcpy((void*)ai.data(), tensor.int64_data().data(), sizeof(int64_t) * list_size); + } + + onnx::AttributeProto* attr = node->add_attribute(); + attr->set_name(std::string(attr_name)); + attr->set_type(onnx::AttributeProto::INTS); + for (auto i64 : ai) + { + if (i64 == std::numeric_limits::max()) i64 = INT_MAX; + if (i64 == std::numeric_limits::min()) i64 = INT_MIN; + + attr->add_ints((int)i64); + } + } + else + { + fprintf(stderr, "unknown constant list type %d\n", (int)tensor.data_type()); + continue; + } + } + + fused_input_indexes.push_back(j); + } + + // drop inputs + for (size_t j = 0; j < fused_input_indexes.size(); j++) + { + const int fused_input_index = fused_input_indexes[j]; + + // ..... fii ....... + const int node_input_size = node->input_size(); + for (int k = fused_input_index; k < node_input_size - 1; k++) + { + node->mutable_input()->SwapElements(k, k + 1); + } + + // ..... ....... fii + node->mutable_input()->RemoveLast(); + } + } +} + +} // namespace onnx2pnnx + +} // namespace pnnx diff --git a/tools/pnnx/src/pass_onnx/fuse_constant_as_attribute.h b/tools/pnnx/src/pass_onnx/fuse_constant_as_attribute.h new file mode 100644 index 000000000..ad6cf8000 --- /dev/null +++ b/tools/pnnx/src/pass_onnx/fuse_constant_as_attribute.h @@ -0,0 +1,25 @@ +// 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 "onnx.pb.h" + +namespace pnnx { + +namespace onnx2pnnx { + +void fuse_constant_as_attribute(onnx::ModelProto& model); + +} // namespace onnx2pnnx + +} // namespace pnnx diff --git a/tools/pnnx/tests/onnx/CMakeLists.txt b/tools/pnnx/tests/onnx/CMakeLists.txt index 274fb4793..7c01f7ab6 100644 --- a/tools/pnnx/tests/onnx/CMakeLists.txt +++ b/tools/pnnx/tests/onnx/CMakeLists.txt @@ -5,8 +5,23 @@ macro(pnnx_onnx_add_test name) add_test(NAME test_onnx_${name} COMMAND ${CMAKE_COMMAND} -DPYTHON_EXECUTABLE=${Python3_EXECUTABLE} -DPYTHON_SCRIPT=${CMAKE_CURRENT_SOURCE_DIR}/test_${name}.py -P ${CMAKE_CURRENT_SOURCE_DIR}/../run_test.cmake) endmacro() +pnnx_onnx_add_test(F_conv_transpose1d) +pnnx_onnx_add_test(F_conv_transpose2d) +pnnx_onnx_add_test(F_conv_transpose3d) +pnnx_onnx_add_test(F_conv1d) +pnnx_onnx_add_test(F_conv2d) +pnnx_onnx_add_test(F_conv3d) +pnnx_onnx_add_test(F_pad) pnnx_onnx_add_test(F_relu) +pnnx_onnx_add_test(nn_Conv1d) +pnnx_onnx_add_test(nn_Conv2d) +pnnx_onnx_add_test(nn_Conv3d) +pnnx_onnx_add_test(nn_ConvTranspose1d) +pnnx_onnx_add_test(nn_ConvTranspose2d) +pnnx_onnx_add_test(nn_ConvTranspose3d) +# pnnx_onnx_add_test(nn_ReLU) + pnnx_onnx_add_test(convnext_tiny) pnnx_onnx_add_test(mobilenet_v2) pnnx_onnx_add_test(mobilenet_v3_small) diff --git a/tools/pnnx/tests/onnx/test_F_conv1d.py b/tools/pnnx/tests/onnx/test_F_conv1d.py new file mode 100644 index 000000000..ad8ced142 --- /dev/null +++ b/tools/pnnx/tests/onnx/test_F_conv1d.py @@ -0,0 +1,66 @@ +# 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. + +import torch +import torch.nn as nn +import torch.nn.functional as F +from packaging import version + +class Model(nn.Module): + def __init__(self): + super(Model, self).__init__() + + self.w2 = nn.Parameter(torch.rand(12, 6, 4)) + self.b2 = nn.Parameter(torch.rand(12)) + self.w3 = nn.Parameter(torch.rand(6, 4, 3)) + + def forward(self, x, w0, w1, b1, y): + x = F.conv1d(x, w0, None, stride=2, padding=1) + x = F.conv1d(x, w1, b1, stride=1, padding=1, dilation=2, groups=2) + # x = F.conv1d(x, w1, b1, stride=1, padding='same', dilation=2, groups=2) + y = F.conv1d(y, self.w2, self.b2, stride=2, padding=2) + y = F.conv1d(y, self.w3, None, stride=2, padding=1, groups=3) + return x, y + +def test(): + net = Model() + net.eval() + + torch.manual_seed(0) + x = torch.rand(1, 12, 52) + w0 = torch.rand(16, 12, 3) + w1 = torch.rand(16, 8, 5) + b1 = torch.rand(16) + y = torch.rand(1, 6, 25) + + a0, a1 = net(x, w0, w1, b1, y) + + # export onnx + torch.onnx.export(net, (x, w0, w1, b1, y), "test_F_conv1d.onnx") + + # onnx to pnnx + import os + os.system("../../src/pnnx test_F_conv1d.onnx inputshape=[1,12,52],[16,12,3],[16,8,5],[16],[1,6,25]") + + # pnnx inference + import test_F_conv1d_pnnx + b0, b1 = test_F_conv1d_pnnx.test_inference() + + return torch.equal(a0, b0) and torch.equal(a1, b1) + +if __name__ == "__main__": + if test(): + exit(0) + else: + exit(1) diff --git a/tools/pnnx/tests/onnx/test_F_conv2d.py b/tools/pnnx/tests/onnx/test_F_conv2d.py new file mode 100644 index 000000000..830bf9e27 --- /dev/null +++ b/tools/pnnx/tests/onnx/test_F_conv2d.py @@ -0,0 +1,66 @@ +# 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. + +import torch +import torch.nn as nn +import torch.nn.functional as F +from packaging import version + +class Model(nn.Module): + def __init__(self): + super(Model, self).__init__() + + self.w2 = nn.Parameter(torch.rand(12, 6, 4, 4)) + self.b2 = nn.Parameter(torch.rand(12)) + self.w3 = nn.Parameter(torch.rand(6, 4, 3, 3)) + + def forward(self, x, w0, w1, b1, y): + x = F.conv2d(x, w0, None, stride=(2,2), padding=(1,1)) + x = F.conv2d(x, w1, b1, stride=(1,1), padding=(1,1), dilation=(2,1), groups=2) + # x = F.conv2d(x, w1, b1, stride=(1,1), padding='same', dilation=(2,1), groups=2) + y = F.conv2d(y, self.w2, self.b2, stride=(2,2), padding=(2,2)) + y = F.conv2d(y, self.w3, None, stride=(2,2), padding=(1,1), groups=3) + return x, y + +def test(): + net = Model() + net.eval() + + torch.manual_seed(0) + x = torch.rand(1, 12, 52, 64) + w0 = torch.rand(16, 12, 3, 3) + w1 = torch.rand(16, 8, 5, 5) + b1 = torch.rand(16) + y = torch.rand(1, 6, 32, 25) + + a0, a1 = net(x, w0, w1, b1, y) + + # export onnx + torch.onnx.export(net, (x, w0, w1, b1, y), "test_F_conv2d.onnx") + + # onnx to pnnx + import os + os.system("../../src/pnnx test_F_conv2d.onnx inputshape=[1,12,52,64],[16,12,3,3],[16,8,5,5],[16],[1,6,32,25]") + + # pnnx inference + import test_F_conv2d_pnnx + b0, b1 = test_F_conv2d_pnnx.test_inference() + + return torch.equal(a0, b0) and torch.equal(a1, b1) + +if __name__ == "__main__": + if test(): + exit(0) + else: + exit(1) diff --git a/tools/pnnx/tests/onnx/test_F_conv3d.py b/tools/pnnx/tests/onnx/test_F_conv3d.py new file mode 100644 index 000000000..323c477ac --- /dev/null +++ b/tools/pnnx/tests/onnx/test_F_conv3d.py @@ -0,0 +1,66 @@ +# 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. + +import torch +import torch.nn as nn +import torch.nn.functional as F +from packaging import version + +class Model(nn.Module): + def __init__(self): + super(Model, self).__init__() + + self.w2 = nn.Parameter(torch.rand(12, 6, 4, 4, 4)) + self.b2 = nn.Parameter(torch.rand(12)) + self.w3 = nn.Parameter(torch.rand(6, 4, 3, 3, 3)) + + def forward(self, x, w0, w1, b1, y): + x = F.conv3d(x, w0, None, stride=(2,2,2), padding=(1,0,1)) + x = F.conv3d(x, w1, b1, stride=(1,1,1), padding=(1,1,1), dilation=(2,2,1), groups=2) + # x = F.conv3d(x, w1, b1, stride=(1,1,1), padding='same', dilation=(2,2,1), groups=2) + y = F.conv3d(y, self.w2, self.b2, stride=(2,2,2), padding=(2,2,2)) + y = F.conv3d(y, self.w3, None, stride=(2,2,2), padding=(1,1,1), groups=3) + return x, y + +def test(): + net = Model() + net.eval() + + torch.manual_seed(0) + x = torch.rand(1, 12, 20, 32, 40) + w0 = torch.rand(16, 12, 3, 2, 3) + w1 = torch.rand(16, 8, 5, 4, 5) + b1 = torch.rand(16) + y = torch.rand(1, 6, 12, 11, 10) + + a0, a1 = net(x, w0, w1, b1, y) + + # export onnx + torch.onnx.export(net, (x, w0, w1, b1, y), "test_F_conv3d.onnx") + + # onnx to pnnx + import os + os.system("../../src/pnnx test_F_conv3d.onnx inputshape=[1,12,20,32,40],[16,12,3,2,3],[16,8,5,4,5],[16],[1,6,12,11,10]") + + # pnnx inference + import test_F_conv3d_pnnx + b0, b1 = test_F_conv3d_pnnx.test_inference() + + return torch.equal(a0, b0) and torch.equal(a1, b1) + +if __name__ == "__main__": + if test(): + exit(0) + else: + exit(1) diff --git a/tools/pnnx/tests/onnx/test_F_conv_transpose1d.py b/tools/pnnx/tests/onnx/test_F_conv_transpose1d.py new file mode 100644 index 000000000..e8c7a48dc --- /dev/null +++ b/tools/pnnx/tests/onnx/test_F_conv_transpose1d.py @@ -0,0 +1,65 @@ +# 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. + +import torch +import torch.nn as nn +import torch.nn.functional as F + +class Model(nn.Module): + def __init__(self): + super(Model, self).__init__() + + self.w2 = nn.Parameter(torch.rand(6, 12, 4)) + self.b2 = nn.Parameter(torch.rand(12)) + self.w3 = nn.Parameter(torch.rand(12, 2, 3)) + + def forward(self, x, w0, w1, b1, y): + x = F.conv_transpose1d(x, w0, None, stride=2, padding=1, output_padding=1) + x = F.conv_transpose1d(x, w1, b1, stride=1, padding=2, dilation=2, groups=2) + + y = F.conv_transpose1d(y, self.w2, self.b2, stride=2, padding=1, output_padding=1) + y = F.conv_transpose1d(y, self.w3, None, stride=1, padding=2, dilation=2, groups=3) + return x, y + +def test(): + net = Model() + net.eval() + + torch.manual_seed(0) + x = torch.rand(1, 12, 22) + w0 = torch.rand(12, 16, 3) + w1 = torch.rand(16, 8, 5) + b1 = torch.rand(16) + y = torch.rand(1, 6, 5) + + a0, a1 = net(x, w0, w1, b1, y) + + # export onnx + torch.onnx.export(net, (x, w0, w1, b1, y), "test_F_conv_transpose1d.onnx") + + # onnx to pnnx + import os + os.system("../../src/pnnx test_F_conv_transpose1d.onnx inputshape=[1,12,22],[12,16,3],[16,8,5],[16],[1,6,5]") + + # pnnx inference + import test_F_conv_transpose1d_pnnx + b0, b1 = test_F_conv_transpose1d_pnnx.test_inference() + + return torch.equal(a0, b0) and torch.equal(a1, b1) + +if __name__ == "__main__": + if test(): + exit(0) + else: + exit(1) diff --git a/tools/pnnx/tests/onnx/test_F_conv_transpose2d.py b/tools/pnnx/tests/onnx/test_F_conv_transpose2d.py new file mode 100644 index 000000000..c4f3b35b0 --- /dev/null +++ b/tools/pnnx/tests/onnx/test_F_conv_transpose2d.py @@ -0,0 +1,65 @@ +# 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. + +import torch +import torch.nn as nn +import torch.nn.functional as F + +class Model(nn.Module): + def __init__(self): + super(Model, self).__init__() + + self.w2 = nn.Parameter(torch.rand(6, 12, 4, 4)) + self.b2 = nn.Parameter(torch.rand(12)) + self.w3 = nn.Parameter(torch.rand(12, 2, 3, 3)) + + def forward(self, x, w0, w1, b1, y): + x = F.conv_transpose2d(x, w0, None, stride=(2,2), padding=(1,1), output_padding=(1,1)) + x = F.conv_transpose2d(x, w1, b1, stride=(1,2), padding=(2,1), dilation=(2,1), groups=2) + + y = F.conv_transpose2d(y, self.w2, self.b2, stride=(2,2), padding=(1,1), output_padding=(1,1)) + y = F.conv_transpose2d(y, self.w3, None, stride=(1,2), padding=(2,1), dilation=(2,1), groups=3) + return x, y + +def test(): + net = Model() + net.eval() + + torch.manual_seed(0) + x = torch.rand(1, 12, 22, 32) + w0 = torch.rand(12, 16, 3, 3) + w1 = torch.rand(16, 8, 5, 5) + b1 = torch.rand(16) + y = torch.rand(1, 6, 5, 6) + + a0, a1 = net(x, w0, w1, b1, y) + + # export onnx + torch.onnx.export(net, (x, w0, w1, b1, y), "test_F_conv_transpose2d.onnx") + + # onnx to pnnx + import os + os.system("../../src/pnnx test_F_conv_transpose2d.onnx inputshape=[1,12,22,32],[12,16,3,3],[16,8,5,5],[16],[1,6,5,6]") + + # pnnx inference + import test_F_conv_transpose2d_pnnx + b0, b1 = test_F_conv_transpose2d_pnnx.test_inference() + + return torch.equal(a0, b0) and torch.equal(a1, b1) + +if __name__ == "__main__": + if test(): + exit(0) + else: + exit(1) diff --git a/tools/pnnx/tests/onnx/test_F_conv_transpose3d.py b/tools/pnnx/tests/onnx/test_F_conv_transpose3d.py new file mode 100644 index 000000000..837ab45b3 --- /dev/null +++ b/tools/pnnx/tests/onnx/test_F_conv_transpose3d.py @@ -0,0 +1,65 @@ +# 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. + +import torch +import torch.nn as nn +import torch.nn.functional as F + +class Model(nn.Module): + def __init__(self): + super(Model, self).__init__() + + self.w2 = nn.Parameter(torch.rand(6, 12, 4, 4, 4)) + self.b2 = nn.Parameter(torch.rand(12)) + self.w3 = nn.Parameter(torch.rand(12, 2, 3, 3, 3)) + + def forward(self, x, w0, w1, b1, y): + x = F.conv_transpose3d(x, w0, None, stride=(2,2,2), padding=(1,0,1), output_padding=(1,1,0)) + x = F.conv_transpose3d(x, w1, b1, stride=(1,1,2), padding=(2,2,1), dilation=(2,2,1), groups=2) + + y = F.conv_transpose3d(y, self.w2, self.b2, stride=(2,2,2), padding=(1,0,1), output_padding=(1,1,0)) + y = F.conv_transpose3d(y, self.w3, None, stride=(1,1,2), padding=(2,2,1), dilation=(2,2,1), groups=3) + return x, y + +def test(): + net = Model() + net.eval() + + torch.manual_seed(0) + x = torch.rand(1, 12, 10, 12, 14) + w0 = torch.rand(12, 16, 3, 2, 3) + w1 = torch.rand(16, 8, 5, 4, 5) + b1 = torch.rand(16) + y = torch.rand(1, 6, 4, 5, 6) + + a0, a1 = net(x, w0, w1, b1, y) + + # export onnx + torch.onnx.export(net, (x, w0, w1, b1, y), "test_F_conv_transpose3d.onnx") + + # onnx to pnnx + import os + os.system("../../src/pnnx test_F_conv_transpose3d.onnx inputshape=[1,12,10,12,14],[12,16,3,2,3],[16,8,5,4,5],[16],[1,6,4,5,6]") + + # pnnx inference + import test_F_conv_transpose3d_pnnx + b0, b1 = test_F_conv_transpose3d_pnnx.test_inference() + + return torch.equal(a0, b0) and torch.equal(a1, b1) + +if __name__ == "__main__": + if test(): + exit(0) + else: + exit(1) diff --git a/tools/pnnx/tests/onnx/test_F_pad.py b/tools/pnnx/tests/onnx/test_F_pad.py new file mode 100644 index 000000000..758c529d8 --- /dev/null +++ b/tools/pnnx/tests/onnx/test_F_pad.py @@ -0,0 +1,73 @@ +# 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. + +import torch +import torch.nn as nn +import torch.nn.functional as F + +class Model(nn.Module): + def __init__(self): + super(Model, self).__init__() + + def forward(self, x, y, z, w): + x = F.pad(x, (3,4), mode='constant', value=1.3) + x = F.pad(x, (2,2)) + + y = F.pad(y, (5,6), mode='reflect') + y = F.pad(y, (2,1), mode='replicate') + y = F.pad(y, (3,4), mode='constant', value=1.3) + y = F.pad(y, (1,1)) + + z = F.pad(z, (3,4,3,4), mode='reflect') + z = F.pad(z, (2,1,2,0), mode='replicate') + z = F.pad(z, (1,0,2,0), mode='constant', value=1.3) + z = F.pad(z, (3,3,3,3)) + + #w = F.pad(w, (1,2,3,4,5,6), mode='reflect') + w = F.pad(w, (5,0,1,2,0,2), mode='replicate') + w = F.pad(w, (0,2,2,1,3,4), mode='constant', value=1.3) + w = F.pad(w, (2,2,2,2,2,2)) + + return x, y, z, w + +def test(): + net = Model() + net.eval() + + torch.manual_seed(0) + x = torch.rand(1, 16) + y = torch.rand(12, 2, 16) + z = torch.rand(1, 3, 12, 16) + w = torch.rand(1, 5, 7, 9, 11) + + a0, a1, a2, a3 = net(x, y, z, w) + + # export onnx + torch.onnx.export(net, (x, y, z, w), "test_F_pad.onnx") + + # onnx to pnnx + import os + os.system("../../src/pnnx test_F_pad.onnx inputshape=[1,16],[12,2,16],[1,3,12,16],[1,5,7,9,11]") + + # pnnx inference + import test_F_pad_pnnx + b0, b1, b2, b3 = test_F_pad_pnnx.test_inference() + + return torch.equal(a0, b0) and torch.equal(a1, b1) and torch.equal(a2, b2) and torch.equal(a3, b3) + +if __name__ == "__main__": + if test(): + exit(0) + else: + exit(1) diff --git a/tools/pnnx/tests/onnx/test_nn_Conv1d.py b/tools/pnnx/tests/onnx/test_nn_Conv1d.py new file mode 100644 index 000000000..2791a291f --- /dev/null +++ b/tools/pnnx/tests/onnx/test_nn_Conv1d.py @@ -0,0 +1,73 @@ +# 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. + +import torch +import torch.nn as nn +import torch.nn.functional as F +from packaging import version + +class Model(nn.Module): + def __init__(self): + super(Model, self).__init__() + + self.conv_0 = nn.Conv1d(in_channels=12, out_channels=16, kernel_size=3) + self.conv_1 = nn.Conv1d(in_channels=16, out_channels=20, kernel_size=2, stride=2, padding=2, dilation=1) + self.conv_2 = nn.Conv1d(in_channels=20, out_channels=24, kernel_size=3, stride=1, padding=(4), dilation=1, groups=1, bias=False) + self.conv_3 = nn.Conv1d(in_channels=24, out_channels=28, kernel_size=5, stride=1, padding=0, dilation=1, groups=4, bias=True) + self.conv_4 = nn.Conv1d(in_channels=28, out_channels=32, kernel_size=3, stride=1, padding=1, dilation=2, groups=2, bias=False, padding_mode='zeros') + # self.conv_3 = nn.Conv1d(in_channels=24, out_channels=28, kernel_size=5, stride=1, padding='valid', dilation=1, groups=4, bias=True) + # self.conv_4 = nn.Conv1d(in_channels=28, out_channels=32, kernel_size=3, stride=1, padding='same', dilation=2, groups=2, bias=False, padding_mode='zeros') + self.conv_5 = nn.Conv1d(in_channels=32, out_channels=32, kernel_size=2, stride=2, padding=3, dilation=1, groups=32, bias=True, padding_mode='reflect') + self.conv_6 = nn.Conv1d(in_channels=32, out_channels=28, kernel_size=2, stride=1, padding=2, dilation=1, groups=1, bias=False, padding_mode='replicate') + #self.conv_7 = nn.Conv1d(in_channels=28, out_channels=24, kernel_size=3, stride=2, padding=(5,6), dilation=2, groups=1, bias=True, padding_mode='circular') + + def forward(self, x): + x = self.conv_0(x) + x = self.conv_1(x) + x = self.conv_2(x) + x = self.conv_3(x) + x = self.conv_4(x) + x = self.conv_5(x) + x = self.conv_6(x) + #x = self.conv_7(x) + + return x + +def test(): + net = Model() + net.eval() + + torch.manual_seed(0) + x = torch.rand(1, 12, 64) + + a = net(x) + + # export onnx + torch.onnx.export(net, (x, ), "test_nn_Conv1d.onnx") + + # onnx to pnnx + import os + os.system("../../src/pnnx test_nn_Conv1d.onnx inputshape=[1,12,64]") + + # pnnx inference + import test_nn_Conv1d_pnnx + b = test_nn_Conv1d_pnnx.test_inference() + + return torch.equal(a, b) + +if __name__ == "__main__": + if test(): + exit(0) + else: + exit(1) diff --git a/tools/pnnx/tests/onnx/test_nn_Conv2d.py b/tools/pnnx/tests/onnx/test_nn_Conv2d.py new file mode 100644 index 000000000..48d642336 --- /dev/null +++ b/tools/pnnx/tests/onnx/test_nn_Conv2d.py @@ -0,0 +1,73 @@ +# 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. + +import torch +import torch.nn as nn +import torch.nn.functional as F +from packaging import version + +class Model(nn.Module): + def __init__(self): + super(Model, self).__init__() + + self.conv_0 = nn.Conv2d(in_channels=12, out_channels=16, kernel_size=3) + self.conv_1 = nn.Conv2d(in_channels=16, out_channels=20, kernel_size=(2,4), stride=(2,1), padding=2, dilation=1) + self.conv_2 = nn.Conv2d(in_channels=20, out_channels=24, kernel_size=(1,3), stride=1, padding=(2,4), dilation=1, groups=1, bias=False) + self.conv_3 = nn.Conv2d(in_channels=24, out_channels=28, kernel_size=(5,4), stride=1, padding=0, dilation=1, groups=4, bias=True) + self.conv_4 = nn.Conv2d(in_channels=28, out_channels=32, kernel_size=3, stride=1, padding=1, dilation=(1,2), groups=2, bias=False, padding_mode='zeros') + # self.conv_3 = nn.Conv2d(in_channels=24, out_channels=28, kernel_size=(5,4), stride=1, padding='valid', dilation=1, groups=4, bias=True) + # self.conv_4 = nn.Conv2d(in_channels=28, out_channels=32, kernel_size=3, stride=1, padding='same', dilation=(1,2), groups=2, bias=False, padding_mode='zeros') + self.conv_5 = nn.Conv2d(in_channels=32, out_channels=32, kernel_size=2, stride=2, padding=3, dilation=1, groups=32, bias=True, padding_mode='reflect') + self.conv_6 = nn.Conv2d(in_channels=32, out_channels=28, kernel_size=2, stride=1, padding=2, dilation=1, groups=1, bias=False, padding_mode='replicate') + #self.conv_7 = nn.Conv2d(in_channels=28, out_channels=24, kernel_size=3, stride=2, padding=(5,6), dilation=2, groups=1, bias=True, padding_mode='circular') + + def forward(self, x): + x = self.conv_0(x) + x = self.conv_1(x) + x = self.conv_2(x) + x = self.conv_3(x) + x = self.conv_4(x) + x = self.conv_5(x) + x = self.conv_6(x) + #x = self.conv_7(x) + + return x + +def test(): + net = Model() + net.eval() + + torch.manual_seed(0) + x = torch.rand(1, 12, 64, 64) + + a = net(x) + + # export onnx + torch.onnx.export(net, (x, ), "test_nn_Conv2d.onnx") + + # onnx to pnnx + import os + os.system("../../src/pnnx test_nn_Conv2d.onnx inputshape=[1,12,64,64]") + + # pnnx inference + import test_nn_Conv2d_pnnx + b = test_nn_Conv2d_pnnx.test_inference() + + return torch.equal(a, b) + +if __name__ == "__main__": + if test(): + exit(0) + else: + exit(1) diff --git a/tools/pnnx/tests/onnx/test_nn_Conv3d.py b/tools/pnnx/tests/onnx/test_nn_Conv3d.py new file mode 100644 index 000000000..9334af3ec --- /dev/null +++ b/tools/pnnx/tests/onnx/test_nn_Conv3d.py @@ -0,0 +1,77 @@ +# 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. + +import torch +import torch.nn as nn +import torch.nn.functional as F +from packaging import version + +class Model(nn.Module): + def __init__(self): + super(Model, self).__init__() + + self.conv_0 = nn.Conv3d(in_channels=12, out_channels=16, kernel_size=3) + self.conv_1 = nn.Conv3d(in_channels=16, out_channels=20, kernel_size=(2,3,4), stride=(2,2,1), padding=2, dilation=1) + self.conv_2 = nn.Conv3d(in_channels=20, out_channels=24, kernel_size=(1,2,3), stride=1, padding=(2,4,1), dilation=1, groups=1, bias=False) + self.conv_3 = nn.Conv3d(in_channels=24, out_channels=28, kernel_size=(5,4,3), stride=1, padding=0, dilation=1, groups=4, bias=True) + self.conv_4 = nn.Conv3d(in_channels=28, out_channels=32, kernel_size=3, stride=1, padding=1, dilation=(1,2,2), groups=2, bias=False, padding_mode='zeros') + # self.conv_3 = nn.Conv3d(in_channels=24, out_channels=28, kernel_size=(5,4,3), stride=1, padding='valid', dilation=1, groups=4, bias=True) + # self.conv_4 = nn.Conv3d(in_channels=28, out_channels=32, kernel_size=3, stride=1, padding='same', dilation=(1,2,2), groups=2, bias=False, padding_mode='zeros') + if version.parse(torch.__version__) >= version.parse('1.10'): + self.conv_5 = nn.Conv3d(in_channels=32, out_channels=32, kernel_size=2, stride=2, padding=3, dilation=1, groups=32, bias=True, padding_mode='reflect') + self.conv_6 = nn.Conv3d(in_channels=32, out_channels=28, kernel_size=2, stride=1, padding=2, dilation=1, groups=1, bias=False, padding_mode='replicate') + # self.conv_7 = nn.Conv3d(in_channels=28, out_channels=24, kernel_size=3, stride=2, padding=(5,6), dilation=2, groups=1, bias=True, padding_mode='circular') + + def forward(self, x): + x = self.conv_0(x) + x = self.conv_1(x) + x = self.conv_2(x) + x = self.conv_3(x) + x = self.conv_4(x) + if version.parse(torch.__version__) < version.parse('1.10'): + return x + + x = self.conv_5(x) + x = self.conv_6(x) + #x = self.conv_7(x) + + return x + +def test(): + net = Model() + net.eval() + + torch.manual_seed(0) + x = torch.rand(1, 12, 48, 48, 64) + + a = net(x) + + # export onnx + torch.onnx.export(net, (x, ), "test_nn_Conv3d.onnx") + + # onnx to pnnx + import os + os.system("../../src/pnnx test_nn_Conv3d.onnx inputshape=[1,12,48,48,64]") + + # pnnx inference + import test_nn_Conv3d_pnnx + b = test_nn_Conv3d_pnnx.test_inference() + + return torch.equal(a, b) + +if __name__ == "__main__": + if test(): + exit(0) + else: + exit(1) diff --git a/tools/pnnx/tests/onnx/test_nn_ConvTranspose1d.py b/tools/pnnx/tests/onnx/test_nn_ConvTranspose1d.py new file mode 100644 index 000000000..07f268af3 --- /dev/null +++ b/tools/pnnx/tests/onnx/test_nn_ConvTranspose1d.py @@ -0,0 +1,76 @@ +# 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. + +import torch +import torch.nn as nn +import torch.nn.functional as F + +class Model(nn.Module): + def __init__(self): + super(Model, self).__init__() + + self.deconv_0 = nn.ConvTranspose1d(in_channels=12, out_channels=16, kernel_size=3) + self.deconv_1 = nn.ConvTranspose1d(in_channels=16, out_channels=20, kernel_size=2, stride=2, padding=2, output_padding=0) + self.deconv_2 = nn.ConvTranspose1d(in_channels=20, out_channels=24, kernel_size=3, stride=1, padding=(2), output_padding=(0), dilation=1, groups=1, bias=False) + self.deconv_3 = nn.ConvTranspose1d(in_channels=24, out_channels=28, kernel_size=5, stride=2, padding=0, output_padding=(1), dilation=1, groups=4, bias=True) + self.deconv_4 = nn.ConvTranspose1d(in_channels=28, out_channels=32, kernel_size=3, stride=1, padding=1, output_padding=0, dilation=2, groups=2, bias=False) + self.deconv_5 = nn.ConvTranspose1d(in_channels=32, out_channels=32, kernel_size=2, stride=2, padding=3, output_padding=1, dilation=1, groups=32, bias=True) + self.deconv_6 = nn.ConvTranspose1d(in_channels=32, out_channels=28, kernel_size=2, stride=1, padding=2, output_padding=0, dilation=1, groups=1, bias=False) + self.deconv_7 = nn.ConvTranspose1d(in_channels=28, out_channels=24, kernel_size=3, stride=2, padding=(6), output_padding=(1), dilation=2, groups=1, bias=True) + + self.downsample = nn.Conv1d(24, 16, 3, stride=2, padding=1) + self.upsample = nn.ConvTranspose1d(16, 24, 3, stride=2, padding=1) + + def forward(self, x): + x = self.deconv_0(x) + x = self.deconv_1(x) + x = self.deconv_2(x) + x = self.deconv_3(x) + x = self.deconv_4(x) + x = self.deconv_5(x) + x = self.deconv_6(x) + x = self.deconv_7(x) + + y = self.downsample(x) + x = self.upsample(y, output_size=x.size()) + + return x + +def test(): + net = Model() + net.eval() + + torch.manual_seed(0) + x = torch.rand(1, 12, 10) + + a = net(x) + + # export onnx + torch.onnx.export(net, (x, ), "test_nn_ConvTranspose1d.onnx") + + # onnx to pnnx + import os + os.system("../../src/pnnx test_nn_ConvTranspose1d.onnx inputshape=[1,12,10]") + + # pnnx inference + import test_nn_ConvTranspose1d_pnnx + b = test_nn_ConvTranspose1d_pnnx.test_inference() + + return torch.equal(a, b) + +if __name__ == "__main__": + if test(): + exit(0) + else: + exit(1) diff --git a/tools/pnnx/tests/onnx/test_nn_ConvTranspose2d.py b/tools/pnnx/tests/onnx/test_nn_ConvTranspose2d.py new file mode 100644 index 000000000..755e821eb --- /dev/null +++ b/tools/pnnx/tests/onnx/test_nn_ConvTranspose2d.py @@ -0,0 +1,76 @@ +# 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. + +import torch +import torch.nn as nn +import torch.nn.functional as F + +class Model(nn.Module): + def __init__(self): + super(Model, self).__init__() + + self.deconv_0 = nn.ConvTranspose2d(in_channels=12, out_channels=16, kernel_size=3) + self.deconv_1 = nn.ConvTranspose2d(in_channels=16, out_channels=20, kernel_size=(2,4), stride=(2,1), padding=2, output_padding=0) + self.deconv_2 = nn.ConvTranspose2d(in_channels=20, out_channels=24, kernel_size=(1,3), stride=1, padding=(2,4), output_padding=(0,0), dilation=1, groups=1, bias=False) + self.deconv_3 = nn.ConvTranspose2d(in_channels=24, out_channels=28, kernel_size=(5,4), stride=2, padding=0, output_padding=(0,1), dilation=1, groups=4, bias=True) + self.deconv_4 = nn.ConvTranspose2d(in_channels=28, out_channels=32, kernel_size=3, stride=1, padding=1, output_padding=0, dilation=(1,2), groups=2, bias=False) + self.deconv_5 = nn.ConvTranspose2d(in_channels=32, out_channels=32, kernel_size=2, stride=2, padding=3, output_padding=1, dilation=1, groups=32, bias=True) + self.deconv_6 = nn.ConvTranspose2d(in_channels=32, out_channels=28, kernel_size=2, stride=1, padding=2, output_padding=0, dilation=1, groups=1, bias=False) + self.deconv_7 = nn.ConvTranspose2d(in_channels=28, out_channels=24, kernel_size=3, stride=2, padding=(5,6), output_padding=(1,0), dilation=2, groups=1, bias=True) + + self.downsample = nn.Conv2d(24, 16, 3, stride=2, padding=1) + self.upsample = nn.ConvTranspose2d(16, 24, 3, stride=2, padding=1) + + def forward(self, x): + x = self.deconv_0(x) + x = self.deconv_1(x) + x = self.deconv_2(x) + x = self.deconv_3(x) + x = self.deconv_4(x) + x = self.deconv_5(x) + x = self.deconv_6(x) + x = self.deconv_7(x) + + y = self.downsample(x) + x = self.upsample(y, output_size=x.size()) + + return x + +def test(): + net = Model() + net.eval() + + torch.manual_seed(0) + x = torch.rand(1, 12, 10, 10) + + a = net(x) + + # export onnx + torch.onnx.export(net, (x, ), "test_nn_ConvTranspose2d.onnx") + + # onnx to pnnx + import os + os.system("../../src/pnnx test_nn_ConvTranspose2d.onnx inputshape=[1,12,10,10]") + + # pnnx inference + import test_nn_ConvTranspose2d_pnnx + b = test_nn_ConvTranspose2d_pnnx.test_inference() + + return torch.equal(a, b) + +if __name__ == "__main__": + if test(): + exit(0) + else: + exit(1) diff --git a/tools/pnnx/tests/onnx/test_nn_ConvTranspose3d.py b/tools/pnnx/tests/onnx/test_nn_ConvTranspose3d.py new file mode 100644 index 000000000..06bd5398c --- /dev/null +++ b/tools/pnnx/tests/onnx/test_nn_ConvTranspose3d.py @@ -0,0 +1,76 @@ +# 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. + +import torch +import torch.nn as nn +import torch.nn.functional as F + +class Model(nn.Module): + def __init__(self): + super(Model, self).__init__() + + self.deconv_0 = nn.ConvTranspose3d(in_channels=12, out_channels=16, kernel_size=3) + self.deconv_1 = nn.ConvTranspose3d(in_channels=16, out_channels=20, kernel_size=(2,3,4), stride=(2,2,1), padding=2, output_padding=0) + self.deconv_2 = nn.ConvTranspose3d(in_channels=20, out_channels=24, kernel_size=(1,2,3), stride=1, padding=(2,3,4), output_padding=(0,0,0), dilation=1, groups=1, bias=False) + self.deconv_3 = nn.ConvTranspose3d(in_channels=24, out_channels=28, kernel_size=(5,4,3), stride=2, padding=0, output_padding=(0,1,1), dilation=1, groups=4, bias=True) + self.deconv_4 = nn.ConvTranspose3d(in_channels=28, out_channels=32, kernel_size=3, stride=1, padding=1, output_padding=0, dilation=(1,2,2), groups=2, bias=False) + self.deconv_5 = nn.ConvTranspose3d(in_channels=32, out_channels=32, kernel_size=2, stride=2, padding=3, output_padding=1, dilation=1, groups=32, bias=True) + self.deconv_6 = nn.ConvTranspose3d(in_channels=32, out_channels=28, kernel_size=2, stride=1, padding=2, output_padding=0, dilation=1, groups=1, bias=False) + self.deconv_7 = nn.ConvTranspose3d(in_channels=28, out_channels=24, kernel_size=3, stride=2, padding=(5,6,7), output_padding=(1,0,1), dilation=2, groups=1, bias=True) + + self.downsample = nn.Conv3d(24, 16, 3, stride=2, padding=1) + self.upsample = nn.ConvTranspose3d(16, 24, 3, stride=2, padding=1) + + def forward(self, x): + x = self.deconv_0(x) + x = self.deconv_1(x) + x = self.deconv_2(x) + x = self.deconv_3(x) + x = self.deconv_4(x) + x = self.deconv_5(x) + x = self.deconv_6(x) + x = self.deconv_7(x) + + y = self.downsample(x) + x = self.upsample(y, output_size=x.size()) + + return x + +def test(): + net = Model() + net.eval() + + torch.manual_seed(0) + x = torch.rand(1, 12, 7, 7, 10) + + a = net(x) + + # export onnx + torch.onnx.export(net, (x, ), "test_nn_ConvTranspose3d.onnx") + + # onnx to pnnx + import os + os.system("../../src/pnnx test_nn_ConvTranspose3d.onnx inputshape=[1,12,7,7,10]") + + # pnnx inference + import test_nn_ConvTranspose3d_pnnx + b = test_nn_ConvTranspose3d_pnnx.test_inference() + + return torch.equal(a, b) + +if __name__ == "__main__": + if test(): + exit(0) + else: + exit(1)