当前位置: 首页> 教育> 幼教 > 云南设计网_免费网页模板下载大全_专业关键词优化平台_新媒体营销

云南设计网_免费网页模板下载大全_专业关键词优化平台_新媒体营销

时间:2025/7/12 20:47:34来源:https://blog.csdn.net/qq_38342510/article/details/142400275 浏览次数:0次
云南设计网_免费网页模板下载大全_专业关键词优化平台_新媒体营销

0 NCNN 介绍

ncnn(Nebula Convolutional Neural Network)是一个高效、轻量级的深度学习框架,支持各种神经网络模型,如pytorch、tensorflow、onnx等,以及多种硬件后端,如x86、arm、riscv、mips、vulkan等。由 Tencent 开发,具有以下特性:

  • 支持卷积神经网络,支持多输入和多分支结构,可计算部分分支
    ncnn 支持卷积神经网络结构,以及多分支多输入的复杂网络结构,如主流的 VGG、GoogLeNet、ResNet、SqueezeNet 等。

  • 无任何第三方库依赖,不依赖 BLAS/NNPACK 等计算框架
    ncnn 不依赖任何第三方库,完全独立实现所有计算过程

  • 纯 C++ 实现,跨平台,支持 Android、 iOS 等
    ncnn 代码全部使用 C/C++ 实现,跨平台的 cmake 编译系统,可在已知的绝大多数平台编译运行,如 Linux、Windows、Mac OS、Android、iOS 等。采用 C++ 03 标准实现,只用到了 std::vector 和 std::string 两个 STL 模板

  • ARM NEON Instrinsic 优化,计算速度快
    ncnn 为手机端 CPU 运行做了深度细致的优化,使用 ARM NEON 指令集实现卷积层、全连接层、池化层等大部分 CNN 关键层。对于寄存器压力较大的 armv7 架构,手工编写 neon 汇编,内存预对齐,cache 预缓存,排列流水线,充分利用一切硬件资源,防止编译器意外负优化。
    这个就很奈斯了,大有可用,本来之前还想着自己实现的,找到了这个就可以直接用了。

  • 精细的内存管理和数据结构设计,内存占用低
    ,在卷积层、全连接层等计算量较大的层实现中,没有采用通常框架中的 im2col + 矩阵乘法,因为这种方式会构造出非常大的矩阵,消耗大量内存。因此,ncnn 采用原始的滑动窗口卷积实现,并在此基础上进行优化,大幅节省了内存。在前向网络计算过程中,ncnn 可自动释放中间结果所占用的内存,进一步减少内存占用。

  • 支持多核并行计算加速,ARM big.LITTLE CPU 调度优化
    ncnn 提供了基于 OpenMP 的多核心并行计算加速,在多核心 CPU 上启用后能够获得很高的加速收益。ncnn 提供线程数控制接口,可以针对每个运行实例分别调控,满足不同场景的需求。

  • 可扩展的模型设计,支持 8bit 量化和半精度浮点存储

  • 支持直接内存零拷贝引用加载网络模型

  • 可注册自定义层实现并扩展
    ncnn 提供了注册自定义层实现的扩展方式,可以将自己实现的特殊层内嵌到 ncnn 的前向计算过程中

1 NCNN 编译与安装

最直接的编译方式就是使用 cmake:

# 在 ncnn 项目目录下
mkdir build && cd build
cmake ..
make -j 

如果在编译的时候需要设置一些 cmake 变量等操作来控制编译过程,可以使用 toolchains 目录下面的 cmake 脚本,自己编写编译脚本,来对多种不同的处理器和编译器提供支持:
build_custom.sh

set -e
set -x
if [ "${1}" = "x86" ]; thenTOOLCHAIN_FILE=./toolchains/host.gcc.toolchain.cmake
elif [ "${1}" = "arm" ]; thenTOOLCHAIN_FILE=./toolchains/aarch64-linux-gnu.toolchain.cmake
elif [ "${1}" = "cross" ]; thenTOOLCHAIN_FILE=./toolchains/aarch64-linux-gnu.toolchain.cmake
elif [ "${1}" = "pi" ]; thenTOOLCHAIN_FILE=./toolchains/arm-linux-gnueabihf.toolchain.cmake
elseecho "Usage: ./compile.sh <x86|arm|cross|pi>"exit 1
fiOS_TYPE=`uname -s`
if [ "${OS_TYPE}" == "Linux" ]; thenCPU_NUM=`cat /proc/cpuinfo | grep "processor" | wc -l`
elif [ "${OS_TYPE}" = "Darwin" ]; thenCPU_NUM=`sysctl -n machdep.cpu.core_count`
elseecho "Unknown OS type: ${OS_TYPE}, use cpu number 4 in default"CPU_NUM=2
fi
SOURCE_DIR=./
BUILD_DIR=${SOURCE_DIR}/build/
INSTALL_DIR=${BUILD_DIR}/install/cmake \-S ${SOURCE_DIR} \-B ${BUILD_DIR} \-G "Unix Makefiles" \-DNCNN_SHARED_LIB=ON \-DENABLE_RTTI=ON \-DNCNN_DISABLE_RTTI=OFF \-DNCNN_BUILD_BENCHMARK=OFF \-DNCNN_BUILD_EXAMPLES=OFF \-DNCNN_BUILD_TOOLS=ON \-DNCNN_BUILD_TESTS=OFF \-DCMAKE_BUILD_TYPE=Debug \-DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} \-DCMAKE_EXPORT_COMPILE_COMMANDS=1 \-DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_FILE}cmake --build ${BUILD_DIR} -j${CPU_NUM}

上面的编译脚本提供了这样的功能:

  • 通过脚本参数的不同,而选择使用 toolchains 中提供的不同的编译脚本、
  • 通过获取 cpu number,作为最后编译时候使用的最大 job 数,来尽可能的优化编译时间
  • 通过使用 cmake 的 -S-B-G选项,来支持指定代码目录、编译目录以及指定生成器
  • 通过使用-D来选择编译过程中打开、关闭哪些开关,ncnn 支持的开关位于项目根目录的CMakeLists.txt中,例如可以控制是否生成动态库、是否编译 tests、是否编译 tools、指定安装目录等

上面的编译脚本使用:

  • 给脚本添加执行权限
chmod +x ./build_custom.sh
  • 根据不同平台执行编译
# x86
./build_custom.sh x86# arm
./build_custom.sh arm
  • 安装,编译完成后,在编译目录执行 make install 就可以完成到指定目录的安装
cd build
make install

2 算子注册

2.1 DEFINE_LAYER_CREATOR

这个宏创建了一个 layer 的对象,与之对应的还有一个DEFINE_LAYER_DESTROYER宏。这个的实现位于 https://github.com/Tencent/ncnn/blob/20220701/src/layer.h#L201:

#define DEFINE_LAYER_CREATOR(name)                          \::ncnn::Layer* name##_layer_creator(void* /*userdata*/) \{                                                       \return new name;                                    \}#define DEFINE_LAYER_DESTROYER(name)                                      \void name##_layer_destroyer(::ncnn::Layer* layer, void* /*userdata*/) \{                                                                     \delete layer;                                                     \}   
2.2 layer_registry_entry

算子的注册过程中,注册的是用于创建指定 layer 的函数对象,需要一个统一的数据结构用来存储这个函数对象,这就要用到layer_registry_entry,定义于 https://github.com/Tencent/ncnn/blob/20220701/src/layer.h#L170,除了这个数据结构,ncnn 还支持用户自定义算子注册和释放,需要用到custom_layer_registry_entry,这两个数据结构如下:

// layer factory function
typedef Layer* (*layer_creator_func)(void*);
typedef void (*layer_destroyer_func)(Layer*, void*);struct layer_registry_entry
{
#if NCNN_STRING// layer type nameconst char* name;
#endif // NCNN_STRING// layer factory entrylayer_creator_func creator;
};struct custom_layer_registry_entry
{
#if NCNN_STRING// layer type nameconst char* name;
#endif // NCNN_STRING// layer factory entrylayer_creator_func creator;layer_destroyer_func destroyer;void* userdata;
};
2.3 ncnn_add_layer

这是一个 cmake 的宏,定义于 https://github.com/Tencent/ncnn/blob/20220701/cmake/ncnn_add_layer.cmake#L79,总体来讲,所有 layer 的注册过程就是通过这个 cmake 宏完成的:

macro(ncnn_add_layer class)string(TOLOWER ${class} name)# WITH_LAYER_xxx optionif(${ARGC} EQUAL 2)option(WITH_LAYER_${name} "build with layer ${name}" ${ARGV1})else()option(WITH_LAYER_${name} "build with layer ${name}" ON)endif()if(NCNN_CMAKE_VERBOSE)message(STATUS "WITH_LAYER_${name} = ${WITH_LAYER_${name}}")endif()if(WITH_LAYER_${name})list(APPEND ncnn_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/layer/${name}.cpp)# look for arch specific implementation and append source# optimized implementation for armv7, aarch64 or x86set(LAYER_ARCH_SRC ${CMAKE_CURRENT_SOURCE_DIR}/layer/${NCNN_TARGET_ARCH}/${name}_${NCNN_TARGET_ARCH}.cpp)if(EXISTS ${LAYER_ARCH_SRC})set(WITH_LAYER_${name}_${NCNN_TARGET_ARCH} 1)list(APPEND ncnn_SRCS ${LAYER_ARCH_SRC})endif()set(LAYER_VULKAN_SRC ${CMAKE_CURRENT_SOURCE_DIR}/layer/vulkan/${name}_vulkan.cpp)if(NCNN_VULKAN AND EXISTS ${LAYER_VULKAN_SRC})set(WITH_LAYER_${name}_vulkan 1)list(APPEND ncnn_SRCS ${LAYER_VULKAN_SRC})endif()endif()

下面是几个调用这个宏来注册 layer 的代码片段,位于 src/CMakeLists.txt:

...
ncnn_add_layer(Convolution1D)
ncnn_add_layer(Pooling1D)
ncnn_add_layer(ConvolutionDepthWise1D)
ncnn_add_layer(Pooling3D)
ncnn_add_layer(MatMul)
ncnn_add_layer(Deconvolution1D)
ncnn_add_layer(DeconvolutionDepthWise1D)
ncnn_add_layer(Einsum)...configure_file(layer_declaration.h.in ${CMAKE_CURRENT_BINARY_DIR}/layer_declaration.h)
configure_file(layer_registry.h.in ${CMAKE_CURRENT_BINARY_DIR}/layer_registry.h)
configure_file(layer_type_enum.h.in ${CMAKE_CURRENT_BINARY_DIR}/layer_type_enum.h)

这里的 configure_file 很重要,它把 cmake 代码和 c++ 代码连接起来,它可以把 cmake 中定义的变量的内容,替换到指定的目标文件中,比如上面的layer_declaration.h.in,它的内容如下:

@layer_declaration@

这个文件只有一行内容,layer_declaration是 cmake 中的变量,编译过后,就会生成build/src//layer_declaration.h这个文件,这个文件的内容就是layer_declaration变量的值,在 ncnn 中,它长下面这个样子:

#include "layer/absval.h"
namespace ncnn {
class AbsVal_final : virtual public AbsVal
{
public:virtual int create_pipeline(const Option& opt) {{ int ret = AbsVal::create_pipeline(opt); if (ret) return ret; }return 0;}virtual int destroy_pipeline(const Option& opt) {{ int ret = AbsVal::destroy_pipeline(opt); if (ret) return ret; }return 0;}
};
DEFINE_LAYER_CREATOR(AbsVal_final)
} // namespace ncnn
...

这里调用了前面提到的DEFINE_LAYER_CREATOR这个宏,也就是定义了AbsVal这个 layer 的创建函数,这里只列了AbsVal这一个 layer,实际上这个自动生成的文件内容包含后面有所有的 layer 的创建函数。这个 layer 的创建函数的注册涉及到layer_registry.h.in这个文件,它的内容如下:

static const layer_registry_entry layer_registry[] = {
@layer_registry@
};
...

这里面的layer_registry,也是一个 cmake 变量,在编译之后这个文件会被自动生成·build/src/layer_registry.h·这个文件,内容如下:

static const layer_registry_entry layer_registry[] = {
#if NCNN_STRING
{"AbsVal", AbsVal_final_layer_creator},
#else
{AbsVal_final_layer_creator},
#endif
...
...
};

这就是 layer 注册的过程,layer 创建函数被维护到layer_registry这个数组中,数组的元素类型就是前面提到的layer_registry_entry

前面提到 cmake 会给上面的这些变量赋值,具体细节就在前面给出来的ncnn_add_layer这个宏里面,实际上就是写入hard code:

if(WITH_LAYER_${name})set(layer_declaration "${layer_declaration}namespace ncnn {\n${layer_declaration_class}\n{\n")set(layer_declaration "${layer_declaration}public:\n")set(layer_declaration "${layer_declaration}    virtual int create_pipeline(const Option& opt) {\n${create_pipeline_content}        return 0;\n    }\n")set(layer_declaration "${layer_declaration}    virtual int destroy_pipeline(const Option& opt) {\n${destroy_pipeline_content}        return 0;\n    }\n")set(layer_declaration "${layer_declaration}};\n")set(layer_declaration "${layer_declaration}DEFINE_LAYER_CREATOR(${class}_final)\n} // namespace ncnn\n\n")endif()

layer_registry的赋值也是一样的:

if(WITH_LAYER_${name})set(layer_registry "${layer_registry}#if NCNN_STRING\n{\"${class}\", ${class}_final_layer_creator},\n#else\n{${class}_final_layer_creator},\n#endif\n")else()set(layer_registry "${layer_registry}#if NCNN_STRING\n{\"${class}\", 0},\n#else\n{0},\n#endif\n")endif()
2.4 create_layer

layer 的注册过程明了之后,layer 的创建就很清晰了:

Layer* create_layer(int index)
{if (index < 0 || index >= layer_registry_entry_count)return 0;// clang-format off// *INDENT-OFF*layer_creator_func layer_creator = 0;
#if NCNN_RUNTIME_CPU && NCNN_AVX512if (ncnn::cpu_support_x86_avx512()){layer_creator = layer_registry_avx512[index].creator;}else
#endif// NCNN_RUNTIME_CPU && NCNN_AVX512
#if NCNN_RUNTIME_CPU && NCNN_FMAif (ncnn::cpu_support_x86_fma()){layer_creator = layer_registry_fma[index].creator;}else
#endif// NCNN_RUNTIME_CPU && NCNN_FMA
#if NCNN_RUNTIME_CPU && NCNN_AVXif (ncnn::cpu_support_x86_avx()){layer_creator = layer_registry_avx[index].creator;}else
#endif // NCNN_RUNTIME_CPU && NCNN_AVX
#if NCNN_RUNTIME_CPU && NCNN_MSAif (ncnn::cpu_support_mips_msa()){layer_creator = layer_registry_msa[index].creator;}else
#endif // NCNN_RUNTIME_CPU && NCNN_MSA
#if NCNN_RUNTIME_CPU && NCNN_RVVif (ncnn::cpu_support_riscv_v()){layer_creator = layer_registry_rvv[index].creator;}else
#endif // NCNN_RUNTIME_CPU && NCNN_RVV{layer_creator = layer_registry[index].creator;}// *INDENT-ON*// clang-format onif (!layer_creator)return 0;Layer* layer = layer_creator(0);layer->typeindex = index;return layer;
}

这里的 layer 创建是有优先级的,特定后端的加速实现如果存在,就创建特定实现的 layer,如果没有特定实现,就创建最 naive 的 layer 实现。这里特定后端的加速实现指的是例如在 x86 机器上基于 avx512/avx2/avx/fma 等矢量加速指令的实现,在 arm 机器上基于 ARM Neon 指令的加速实现等。

关键字:云南设计网_免费网页模板下载大全_专业关键词优化平台_新媒体营销

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: