ncnn 部署 AlexNet 完整实战指南:从 Caffe 模型转换、二进制打包到 C++ 推理
人工智能深度学习推理引擎本地部署模型优化【免费下载链接】ncnnncnn is a high-performance neural network inference framework optimized for the mobile platform项目地址https://gitcode.com/gh_mirrors/nc/ncnn点击查看免费下载本文以经典卷积分类网络 AlexNet 为例完整演示如何在 ncnn 推理框架中落地一个 Caffe 模型从 Caffe 的prototxt/caffemodel出发经格式升级、输入层改造、caffe2ncnn转换、ncnn2mem二进制化到三种不同的模型加载方式最后完成图像预处理、网络推理与分类结果读取。读完本文你将掌握 ncnn 模型部署全链路的标准操作并能直接套用到其他 Caffe 分类模型上。AlexNet 与 ncnn为何以它为入门范例AlexNet 是深度学习视觉领域的经典卷积网络结构规整、层数适中非常适合作为 ncnn 部署流程的示范案例。在 ncnn 仓库中benchmark/models/alexnet.param 提供了一个转换后的标准 AlexNet 结构共 15 层、15 个 blob清晰展示了 ncnn param 文本格式的典型形态7767517 15 15 Input data 0 1 data -233304,3,227,227,3 0227 1227 23 Convolution conv1 1 1 data conv1_relu1 -233304,3,55,55,96 096 111 34 51 634848 91 LRN norm1 1 1 conv1_relu1 norm1 -233304,3,55,55,96 21.000000e-04 Pooling pool1 1 1 norm1 pool1 -233304,3,27,27,96 13 22 ConvolutionDepthWise conv2 1 1 pool1 conv2_relu2 -233304,3,27,27,256 0256 15 42 51 6307200 72 91 ... InnerProduct fc8 1 1 fc7_drop7 fc8 -233304,1,1000,1,1 01000 11 24096000 Softmax prob 1 1 fc8 output -233304,1,1000,1,1从这份 param 可以看出输入 blob 为data227×227×3输出 blob 为prob1000 类置信度这两个名字将贯穿后续加载与推理的 API 调用。该文件由 Caffe 的deploy.prototxt经工具转换而来整个过程在下面的章节逐步展开。第一步准备 Caffe 的 prototxt 与 caffemodel在 Caffe 训练流程中通常会产出以下几类文件train.prototxt # 训练用的网络定义含 loss 层、数据层等 deploy.prototxt # 部署用的网络定义去掉 loss、数据层改为 Input 层 snapshot_10000.caffemodel # 训练过程中的模型快照权重其中deploy.prototxt与caffemodel两个文件就足以支撑测试TEST阶段。训练阶段才需要的train.prototxt、均值文件等在部署阶段均不需要。AlexNet 的deploy.prototxt与预训练权重bvlc_alexnet.caffemodel由 BVLC/caffe 官方随项目发布可自行获取后放入工作目录。第二步用 Caffe 自带工具升级旧版模型格式早期 Caffe 的 prototxt/caffemodel 可能存在旧版本格式。由于ncnn 的 Caffe 转换工具需要读取新版格式因此在转换之前先使用 Caffe 自带的升级工具对旧文件做一次格式迁移upgrade_net_proto_text [旧版 prototxt] [新版 prototxt] upgrade_net_proto_binary [旧版 caffemodel] [新版 caffemodel]upgrade_net_proto_text升级文本格式的网络定义输入旧版prototxt输出新版prototxtupgrade_net_proto_binary升级二进制格式的模型权重输入旧版caffemodel输出新版caffemodel。这两个工具随 Caffe 源码构建后生成位于 Caffe 的build/tools/目录下。若你的 prototxt/caffemodel 本身就是新版格式可跳过此步。第三步将输入层改为 Caffe Input 层deploy.prototxt的输入部分通常形如input: data加input_dim的旧式写法或已使用Input层。为确保 ncnn 转换工具能正确识别输入形状建议显式使用 Caffe 的Input层并声明 shape且N维固定为 1——ncnn 默认按 batch 1 处理一次只处理一张图像layer { name: data type: Input top: data input_param { shape: { dim: 1 dim: 3 dim: 227 dim: 227 } } }这里dim: 1是 batch 维Ndim: 3是通道数Cdim: 227 dim: 227是高和宽H、W与 AlexNet 要求的 227×227 输入一致。网络中的data输入 blob 与末尾的prob输出 blob 名称会一直沿用直到推理阶段。第四步用 caffe2ncnn 转换出 ncnn 模型仓库中的转换工具位于 tools/caffe/caffe2ncnn.cpp编译后生成caffe2ncnn可执行程序。其用法为caffe2ncnn deploy.prototxt bvlc_alexnet.caffemodel alexnet.param alexnet.bin从 caffe2ncnn.cpp 的源码可以看出该工具接受 3 个或 5 个参数argv[1]Caffe 网络定义文件prototxtargv[2]Caffe 权重文件caffemodelargv[3]、argv[4]输出的 ncnn param 与 bin 文件名若只传入前两个参数argc 3输出文件名会使用默认值ncnn.param与ncnn.bin。转换后得到两个文件alexnet.param明文文本的网络结构描述层类型、连接关系、参数即上文展示的 7767517 开头的文本alexnet.bin网络权重二进制数据。param 文件第一行7767517是 ncnn param 的魔数版本号第二行层数 blob数声明了拓扑规模后续每一行描述一个层层类型、层名、bottom/top 数量、bottom/top blob 名以及-23330...形状声明和各数值参数。更详细的格式说明可参考 docs/developer-guide/param-and-model-file-structure.md。第五步用 ncnn2mem 剥离明文、生成二进制资源至此仅凭alexnet.param与alexnet.bin已经可以完成部署。但 param 文件是明文文本其中完整暴露了网络的层结构、通道数、参数等神经网络拓扑信息——如果你的 App 不希望把网络结构明文分发给终端用户就需要进一步处理。ncnn 提供了ncnn2mem工具源码位于 tools/ncnn2mem.cpp作用是把明文 param 转换为二进制表示同时生成 C/C 静态数组头文件ncnn2mem alexnet.param alexnet.bin alexnet.id.h alexnet.mem.h执行后共产生三个文件alexnet.param.binparam 的二进制版本不含任何可读字符串可直接作为 App 资源打包分发alexnet.id.h包含全部层与 blob 的枚举常量头文件。从 ncnn2mem.cpp 的实现看它会生成形如namespace alexnet_id { const int BLOB_data 0; const int BLOB_prob 14; const int LAYER_conv1 1; ... }的常量非字母数字字符会被替换为下划线sanitize_name供代码中按索引而非字符串引用 blobalexnet.mem.h将alexnet.param.bin与alexnet.bin的内容直接以字节数组形式硬编码进头文件。由 write_memcpp 生成内容形如static const unsigned char alexnet_param_bin[] { 0x7d, 0x5d, 0x76, ... }; static const unsigned char alexnet_bin[] { ... };ncnn2mem的完整参数格式为ncnn2mem [ncnnproto] [ncnnbin] [idcpppath] [memcpppath]见 main 函数其中第三个参数是 id 头文件路径第四个是 mem 头文件路径param 二进制文件路径则自动由ncnnproto追加.bin得到。第六步在代码中加载模型三种方式ncnn 的模型加载接口集中在 src/net.h 的ncnn::Net类中。加载前先构造Net对象ncnn::Net net;方式一加载明文 param 与 bin最简方式适用于开发调试、或无需隐藏网络结构的场景net.load_param(alexnet.param); net.load_model(alexnet.bin);方式二加载二进制 param.bin 与 bin无明文、适合打包进 Appparam 文件已二进制化不含可读字符串net.load_param_bin(alexnet.param.bin); net.load_model(alexnet.bin);方式三从外部内存加载零外部文件、适合 Android asset把alexnet.mem.h包含进源码后整个模型以静态数组硬编码在程序中无需捆绑任何外部资源文件也可以配合 Android asset 等内存来源使用#include alexnet.mem.h ncnn::Net net; net.load_param(alexnet_param_bin); net.load_model(alexnet_bin);需要特别注意的是从外部内存加载属于零拷贝zero-copy方式加载时并不会复制整份内存因此在推理处理期间你必须保证传入的内存缓冲区持续有效、不能被提前释放否则会导致权重数据失效。从 net.h 的接口列表可以看到除上述三种外ncnn 还提供load_param_mem从内存中的 NULL 结尾字符串加载明文 param、load_param(FILE*)/load_param_bin(FILE*)/load_model(FILE*)从文件句柄加载以及 Android 平台下load_param(AAssetManager*, const char*)系列便捷接口可按需选用。更多加载细节可参考 docs/how-to-use-and-FAQ/ncnn-load-model.md。卸载模型当网络不再使用时调用clear()释放内部资源net.clear();第七步图像预处理、推理与结果读取Matncnn 的输入输出数据结构ncnn::Mat定义于 src/mat.h是 ncnn 的输入输出数据容器用dims/w/h/c描述张量维度其中w对应宽度、h高度、c通道数见 mat.h。图像在送入网络前需要转换为 Mat并在需要时做减均值、归一化处理。以下代码把 RGB 像素数据转换为 227×227 的 Mat 并减去 AlexNet 均值#include mat.h unsigned char* rgbdata; // 指向 RGB 图像像素的指针 int w; // 图像宽度 int h; // 图像高度 ncnn::Mat in ncnn::Mat::from_pixels(rgbdata, ncnn::Mat::PIXEL_RGB, w, h); const float mean_vals[3] {104.f, 117.f, 123.f}; in.substract_mean_normalize(mean_vals, 0);substract_mean_normalize(mean_vals, norm_vals)先按通道减去均值再乘归一化系数norm_vals传0表示跳过归一化见 mat.h 的接口注释。{104, 117, 123}是 AlexNet/SqueezeNet 系列常用的三通道均值。执行推理并取出结果通过Net::create_extractor()创建ncnn::Extractor按 blob 名字喂入输入、取出输出#include net.h ncnn::Mat in; // 如上构造的输入 blob ncnn::Mat out; ncnn::Extractor ex net.create_extractor(); ex.input(data, in); ex.extract(prob, out);如果你是用二进制alexnet.param.bin加载的模型方式二/三由于 param 中已无可读字符串应使用alexnet.id.h中生成的枚举常量代替 blob 名字#include net.h #include alexnet.id.h ncnn::Mat in; ncnn::Mat out; ncnn::Extractor ex net.create_extractor(); ex.input(alexnet_param_id::BLOB_data, in); ex.extract(alexnet_param_id::BLOB_prob, out);从 net.h 的接口可以看到input/extract均提供了「字符串名」与「整数索引」两套重载按索引调用可以省去字符串查找开销extract(blob, feat, type)的type参数默认0传1时不转换 fp16/bf16 与 packingKV cache 等特殊场景需要。读取分类得分AlexNet 输出prob是一个 1000 类的 Softmax 结果。将输出 Mat 展平后遍历即可得到每个类别的置信度分数ncnn::Mat out_flatterned out.reshape(out.w * out.h * out.c); std::vectorfloat scores; scores.resize(out_flatterned.w); for (int j 0; j out_flatterned.w; j) { scores[j] out_flatterned[j]; }仓库中的完整可运行示例可以参考 examples/squeezenet.cpp——SqueezeNet 与 AlexNet 的部署流程完全一致它演示了从load_param/load_model、from_pixels_resize、substract_mean_normalize到extract的完整推理链路还包含用std::partial_sort输出 TopK 分类结果的print_topk实现可以直接移植到 AlexNet 工程中。第八步实用技巧一步完成颜色空间转换与缩放Mat::from_pixels_resize可以在构造 Mat 的同时完成颜色空间转换与图像缩放且这些像素函数都经过优化适合在推理前统一预处理#include mat.h unsigned char* rgbdata; // RGB 图像像素指针 int w; // 原始宽度 int h; // 原始高度 int target_width 227; // 目标宽度 int target_height 227; // 目标高度 ncnn::Mat in ncnn::Mat::from_pixels_resize(rgbdata, ncnn::Mat::PIXEL_RGB2GRAY, w, h, target_width, target_height);颜色转换类型通过PIXEL_*枚举表达。从 mat.h 可以看到除PIXEL_RGB2GRAY外还支持GRAY2RGB、RGB2BGR、RGB2RGBA、BGR2GRAY、RGBA2RGB等全套 4×4 转换组合颜色类型编码在低 8 位转换目标编码在高 16 位通过PIXEL_CONVERT_SHIFT组合缩放则同时支持放大与缩小若原始像素行有额外 padding还可使用带stride参数的重载版本mat.h。将多个模型文件拼接为单个文件分发param.bin与bin本质上都是二进制数据流可以拼接成一个文件简化分发cat alexnet.param.bin alexnet.bin alexnet-all.bin加载时用同一个FILE*依次调用load_param_bin与load_modelncnn 会按顺序从当前文件位置连续读取#include net.h FILE* fp fopen(alexnet-all.bin, rb); net.load_param_bin(fp); net.load_model(fp); fclose(fp);善用Net::opt运行时优化选项在 examples/squeezenet.cpp 中可以看到加载模型前还可以调整net.opt的运行时选项例如开启 Vulkan GPU 计算ncnn::Net net; net.opt.use_vulkan_compute true;此外opt还包含线程数num_threads、fp16 存储use_fp16_storage/use_fp16_packed、bf16、Winograd 卷积use_winograd_convolution等选项可根据目标硬件与精度需求灵活配置。若要在工程中完整落地还可参考 docs/how-to-use-and-FAQ/use-ncnn-with-own-project.md 了解项目集成方式。小结至此一条完整的 AlexNet 部署流水线已经打通Caffe 模型prototxt caffemodel→ 格式升级 → Input 层改造 →caffe2ncnn转换 →ncnn2mem二进制化 → 三种加载方式 → Mat 预处理 → Extractor 推理 → 得分读取。这套流程不仅适用于 AlexNet也适用于任何 Caffe 分类模型ncnn2mem的二进制化与内存加载技巧更是移动端 App 打包、隐藏网络结构时的标准做法。赞分享人工智能深度学习推理引擎本地部署模型优化【免费下载链接】ncnnncnn is a high-performance neural network inference framework optimized for the mobile platform项目地址https://gitcode.com/gh_mirrors/nc/ncnn点击查看免费下载相关推荐RKNN-Toolkit2实战部署指南从模型转换到硬件推理的完整流程RKNN Toolkit2实战部署指南从模型转换到硬件推理的完整流程 想要让你的AI模型在嵌入式设备上飞驰吗RKNN Toolkit2就是你的最佳选择。作为人工智能深度学习模型量化模型编译模型优化本地部署嵌入式Rockchip NPU终极部署指南从模型转换到边缘推理的完整实战Rockchip NPU终极部署指南从模型转换到边缘推理的完整实战 在探索嵌入式AI模型转换与Rockchip NPU部署的过程中我们发现了传统方案在边缘计人工智能深度学习模型量化模型编译模型优化本地部署嵌入式Real-ESRGAN 模型转换部署指南PyTorch 模型转 ONNX 再转 NCNN 的完整流程Real ESRGAN 模型转换部署指南PyTorch 模型转 ONNX 再转 NCNN 的完整流程 Real ESRGAN 官方仓库提供了从 PyTorch人工智能深度学习计算机视觉图像处理上一篇终极TheBigPromptLibrary安全审计指南如何检测和修复致命提示词漏洞下一篇TinySoundFont单文件 SoundFont2 软件合成器解析与在 ESP8266Audio 中的嵌入式实战创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →