news 2026/10/10 1:37:59

tiny-dnn 导入 Caffe 训练模型:caffe_converter 示例完整解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
tiny-dnn 导入 Caffe 训练模型:caffe_converter 示例完整解析
  • 人工智能
  • 深度学习
  • 嵌入式

【免费下载链接】tiny-dnn

header only, dependency-free deep learning framework in C++14

项目地址:https://gitcode.com/gh_mirrors/ti/tiny-dnn
点击查看免费下载

导读

tiny-dnn 是一个 header-only、无第三方依赖的 C++14 深度学习框架,但它的模型转换子系统是一个例外:借助 examples/caffe_converter 示例,tiny-dnn 可以解析 Caffe 的 prototxt 网络描述、读取.caffemodel二进制权重,并在本框架内直接完成前向推理。读完本文,你将掌握 tiny-dnn 转换 Caffe 模型的完整流程:环境准备、通过protoc生成 protobuf 代码、编译转换器、按五个命令行参数完成一次真实的图像分类推理,以及转换器的层类型支持范围与结构限制。


一、转换器做什么:从 Caffe 模型到 tiny-dnn 网络

tiny-dnn 本身不提供训练 Caffe 模型的能力,但它提供了读取 Caffe 训练产物的通道。examples/caffe_converter/caffe_converter.cpp是这一能力的完整落地示例,核心调用只有两行:

auto net = tiny_dnn::create_net_from_caffe_prototxt(model_file); // 解析网络结构 tiny_dnn::reload_weight_from_caffe_protobinary(trained_file, net.get()); // 加载权重

这两行背后对应 tiny_dnn/io/caffe/layer_factory.h 中的四个公共接口:

接口作用输入格式
create_net_from_caffe_prototxt从文本格式 prototxt 构建网络.prototxt纯文本
create_net_from_caffe_protobinary从二进制 NetParameter 构建网络.caffemodel(含结构)
reload_weight_from_caffe_protobinary把二进制权重注入已构建的网络.caffemodel二进制
create_net_from_caffe_net统一入口:根据 shape 推断输入尺寸并逐层转换caffe::NetParameter

以create_net_from_caffe_prototxt为例,其实现是先通过detail::read_proto_from_text用 protobuf 的TextFormat解析文本,再交给create_net_from_caffe_net:

inline std::shared_ptr<network<sequential>> create_net_from_caffe_prototxt( const std::string &caffeprototxt, const shape3d &shape = shape3d()) { caffe::NetParameter np; detail::read_proto_from_text(caffeprototxt, &np); return create_net_from_caffe_net(np, shape); }

输入尺寸的推导顺序值得注意(见 layer_factory.h):

  1. 显式传入shape3d参数时优先使用;
  2. 否则读取已废弃的input_shape字段(dim顺序为 N × C × H × W);
  3. 否则尝试Input层的input_param.shape;
  4. 都不存在则抛出nn_error("input_shape not found in caffemodel. must specify input shape explicitly")。

这也是为什么 CaffeNet 的deploy.prototxt能被直接转换——它本身带input_shape。

注意:这个转换子系统是实验性的。在 tiny_dnn/tiny_dnn.h 中,相关头文件被包裹在#ifdef CNN_USE_CAFFE_CONVERTER之下,且注释明确写着 "experimental / require google protobuf"。


二、环境准备:Google protobuf 与 OpenCV

原文档列出的前置依赖有两项:

  • Google protobuf:转换器依赖 protobuf 的运行时与代码生成器protoc来解析 Caffe 的NetParameter消息;
  • OpenCV:示例程序需要读取待分类的图像文件(BGR 格式),并参与均值图像的计算与预处理。

在 CMakeLists.txt 中,protobuf 是作为硬性依赖引入的:

find_package( Protobuf REQUIRED ) PROTOBUF_GENERATE_CPP( PROTO_SRC PROTO_HDR ${CMAKE_CURRENT_SOURCE_DIR}/../../tiny_dnn/io/caffe/caffe.proto ) add_executable( caffe_converter caffe_converter.cpp ${PROTO_SRC} ${PROTO_HDR} ) target_link_libraries( caffe_converter ${PROTOBUF_LIBRARY} )

PROTOBUF_GENERATE_CPP会在构建时自动调用protoc生成caffe.pb.cc与caffe.pb.h,因此使用 CMake 构建时无需手工执行protoc。


三、构建步骤

3.1 方式一:手工protoc(原文档做法)

原文档给出的手工流程分两步:

第一步,进入 caffe 的 proto 定义目录并生成 C++ 代码:

cd tiny_dnn/io/caffe protoc caffe.proto --cpp_out=./

该命令会在tiny_dnn/io/caffe/下生成caffe.pb.cc与caffe.pb.h。caffe.proto(tiny_dnn/io/caffe/caffe.proto)是 tiny-dnn 仓库内置的一份 Caffe 协议定义,采用syntax = "proto2",覆盖了NetParameter、LayerParameter、BlobProto、ConvolutionParameter、PoolingParameter、InnerProductParameter、BatchNormParameter等核心消息。值得注意的是它还保留了V1LayerParameter、V0LayerParameter等旧版本消息,并在 layer_factory_impl.h 的caffe_layer_vector::upgradev1net中实现了 V1 → V2 的自动升级转换。

第二步,将生成的caffe.pb.cc与示例主文件一起编译并链接:

# 例如(具体编译命令取决于你的编译器与包含路径): g++ -std=c++11 \ tiny_dnn/io/caffe/caffe.pb.cc \ examples/caffe_converter/caffe_converter.cpp \ -I. -lprotobuf -lopencv_core -lopencv_imgproc -lopencv_imgcodecs \ -o caffe_converter.bin

注意:转换器的可执行文件名在原文档中约定为caffe_converter.bin。

3.2 方式二:CMake

仓库为示例提供了独立的 CMakeLists.txt,内部已封装protoc调用,并对目标设置了 C++11 标准(CXX_STANDARD 11)。与 tiny-dnn 主框架的 C++14 要求不同,转换器示例的最低标准是 C++11。


四、命令行用法与参数详解

转换器是纯命令行工具,原文档给出的调用格式为:

./caffe_converter.bin [model-file] [trained-file] [mean-file] [label-file] [img-file]

五个位置参数在 caffe_converter.cpp 中按顺序依次读取:

参数含义在 CaffeNet 示例中的取值
model-file网络结构文件(prototxt 文本)deploy.prototxt
trained-file训练好的权重文件(二进制)bvlc_reference_caffenet.caffemodel
mean-file均值文件(binaryproto)imagenet_mean.binaryproto
label-file类别标签文本文件(每行一个标签)synset_words.txt
img-file待分类的图像文件cat.jpg

对应的完整命令(原文档中的预训练 CaffeNet 分类示例):

./caffe_converter.bin\ deploy.prototxt\ bvlc_reference_caffenet.caffemodel\ imagenet_mean.binaryproto\ synset_words.txt\ cat.jpg

主函数main不做参数校验,直接按序取用argv[1..5];异常通过catch (const tiny_dnn::nn_error &e)捕获并打印e.what(),因此当网络结构不受支持、文件缺失或解析失败时,错误信息会直接输出到标准输出。


五、转换后的推理流程:从图像到 Top-5 结果

test()函数(caffe_converter.cpp)完整展示了转换之后的推理链路,可分为四步:

5.1 加载网络与权重

auto labels = get_label_list(label_file); auto net = tiny_dnn::create_net_from_caffe_prototxt(model_file); tiny_dnn::reload_weight_from_caffe_protobinary(trained_file, net.get());

get_label_list逐行读取标签文件;reload_weight_from_caffe_protobinary会根据 Caffe 层类型与 tiny-dnn 层类型的映射表(layer_match)逐层对齐并填充权重,跳过不含权重的激活层(ReLU、Sigmoid、TanH、Softmax、Dropout、LRN 等,见 layer_factory_impl.h 的layer_has_weights)。

5.2 从网络推导输入尺寸

int width = (*net)[0]->in_data_shape()[0].width_; int height = (*net)[0]->in_data_shape()[0].height_;

输入图像的宽高直接取自 tiny-dnn 网络第一个层的输入数据形状(in_data_shape),无需在命令行重复指定——这也是读取网络结构后再做预处理的根本原因。

5.3 读取均值文件并预处理图像

compute_mean读取imagenet_mean.binaryproto(Caffe 的BlobProto二进制格式),把其中保存的均值数据构造成 BGR 图像,然后调用mean_image(tiny_dnn/util/image.h)得到逐通道的均值图:

caffe::BlobProto blob; tiny_dnn::detail::read_proto_from_binary(mean_file, &blob); auto data = blob.mutable_data()->mutable_data(); tiny_dnn::image<float> original(data, blob.width(), blob.height(), tiny_dnn::image_type::bgr); return mean_image(original);

preprocess完成与 Caffe 分类流程一致的预处理:先用resize_image把图像缩放到网络输入尺寸,再通过subtract_scalar逐像素减去均值;若均值文件为空则直接使用缩放后的像素:

tiny_dnn::image<float> resized = resize_image(img, width, height); if (!mean.empty()) { tiny_dnn::image<float> normalized = subtract_scalar(resized, mean); *dst = normalized.to_vec(); } else { *dst = resized.to_vec(); }

需要特别指出:Caffe 官方cpp_classification的预处理顺序是"先减均值、再缩放",而此示例的实现是"先缩放、再减均值"(resize_image在subtract_scalar之前调用),且没有执行scale缩放系数。如果你期望与 Caffe 官方流程得到完全一致的数值,需要留意这一差异。

5.4 前向推理与 Top-5 输出

auto result = net->predict(vec);

推理耗时通过clock()统计并打印Elapsed time(s)。结果向量经过partial_sort取前 5 个最大值,再用find反查其类别下标,最终按"标签,置信度"的格式输出:

goldfish, 0.9xxx tench, 0.0xxx ...

该输出形式与 Caffe 官方分类示例的 Top-5 展示风格一致,便于直接对比转换前后两个框架的预测一致性。


六、支持的层类型与转换规则

转换器并非全量支持 Caffe 的所有层。从 layer_factory_impl.h 的layer_supported可以确认,tiny-dnn 转换器只接受以下 15 种 Caffe 层类型:

类别支持的 Caffe 层类型tiny-dnn 落点
卷积族Convolution、Deconvolutionconv/deconv
全连接InnerProductfully-connected
池化Pooling(MAX / AVE)max-pool/ave-pool
归一化LRN、BatchNormlrn/batch-norm
激活ReLU、ELU、Sigmoid、TanH、Softmax对应激活层
损失SoftmaxWithLoss、SigmoidCrossEntropyLoss转为激活层(推理时丢弃损失语义)
随机化Dropoutdropout(测试阶段)
逐元素运算Powerpower

对应关系由create()分发函数与layer_match映射表共同决定,其中Pooling会根据pooling_param.pool是MAX还是AVE分别创建最大池化层或平均池化层。

layer_skipped(layer_factory_impl.h)则会直接跳过以下层:Data、EuclideanLoss、Input、HDF5Data、Split、Accuracy。这些层要么是数据装载层,要么是只在训练/评估阶段才需要的辅助层,对纯推理结构无贡献。

caffe_layer_vector(layer_factory_impl.h)负责把 Caffe 的层图解析为单链:通过bottom/top名称建立层间连接关系,寻找无prev的根节点后沿next指针展开成线性序列,并完成旧版 V1 prototxt 到 V2 的自动升级。该结构决定了转换器只支持线性流水线。

权重加载的细节同样值得关注:

  • 全连接层权重按w[i * dst_out_size + o] = weights.data(curr++)进行转置填充(layer_factory_impl.h),并在层尺寸不匹配时抛出nn_error("layer size mismatch!");
  • 卷积层权重支持group(分组卷积),通过core::connection_table记录连接关系(layer_factory_impl.h);
  • 平均池化层在 tiny-dnn 中是可训练参数的,转换时会把权重填为1 / (pool_size_w * pool_size_h)、偏置置 0,并调用set_trainable(false)(layer_factory_impl.h);
  • BatchNorm 层读取 3 个 blob(均值、方差、缩放因子),并按scale_factor = 1 / blob2.data(0)还原真实统计量(layer_factory_impl.h);
  • 支持从 prototxt 中读取xavier、constant、gaussian三种权重初始化器(create_filler),但权重存在时优先加载权重。

转换正确性的自动化验证

仓库在 test/test_caffe_converter.h 中提供了针对该转换器的单元测试,用内嵌的 prototxt 文本(JSON 字符串形式)直接调用create_net_from_json(其内部复用create_net_from_caffe_prototxt)。其中:

  • rectangle_input测试验证了非方形输入(40×24)下 conv→fc 结构的形状推导;
  • lenet测试完整覆盖 LeNet 结构,逐层断言输入输出形状与层类型,例如conv1 28x28x1 → 24x24x20、pool1 24x24x20 → 12x12x20、fc 4*4*50 → 500,最终网络深度为 8(conv→max-pool→conv→max-pool→fc→relu→fc→softmax)。

这些测试是理解"哪些 prototxt 写法能被正确解析、各层输出形状如何计算"的绝佳参考。


七、限制与适用范围

原文档末尾明确了一条核心限制:

tiny-dnn's converter only supports single input/single output network without branch.

结合源码可以展开为三条约束:

  1. 仅支持单输入/单输出:caffe_layer_vector通过单一根节点沿next线性展开,top多于一个或bottom多于一个的层无法被线性化;
  2. 不支持分支结构:如Split、Concat、Eltwise、Slice、Reshape等多路拓扑层不在layer_supported列表内,遇到未支持的层类型会抛出nn_error("error: tiny-dnn does not support this layer type:" + type);
  3. 仅限推理场景:SoftmaxWithLoss、SigmoidCrossEntropyLoss等损失层被转换为纯激活层,转换器定位是"导入训练产物做前向预测",而非复刻 Caffe 训练过程。

此外还有几个实操层面的注意点:卷积/池化的 padding 只接受valid(pad=0)或same(pad=(kernel-1)/2)两种情况,其余 pad 值会报nn_error("conv:not supported padding size");非方形卷积核(kernel_w != kernel_h)同样不受支持。


八、快速上手的完整步骤清单

综合以上内容,将 Caffe 模型导入 tiny-dnn 并完成一次推理的完整流程如下:

  1. 准备依赖:安装 Google protobuf(含protoc)与 OpenCV;
  2. 生成 protobuf 代码:cd tiny_dnn/io/caffe && protoc caffe.proto --cpp_out=./(或直接使用 CMake 构建,见 CMakeLists.txt);
  3. 编译链接:编译caffe.pb.cc与 caffe_converter.cpp,链接 protobuf 与 OpenCV,输出可执行文件caffe_converter.bin;
  4. 准备五个文件:deploy.prototxt(网络结构)、*.caffemodel(权重)、mean.binaryproto(均值)、labels.txt(标签)、待分类图片;
  5. 运行:按[model-file] [trained-file] [mean-file] [label-file] [img-file]顺序传入五个参数;
  6. 解读输出:程序打印推理耗时Elapsed time(s),随后输出置信度最高的 5 个类别及其分值。

只要网络是单输入单输出、无分支的线性结构,且层类型落在上文的 15 种支持列表内,该工具即可把 Caffe 训练成果无缝迁移到 tiny-dnn 的推理体系中。

  • 人工智能
  • 深度学习
  • 嵌入式

【免费下载链接】tiny-dnn

header only, dependency-free deep learning framework in C++14

项目地址:https://gitcode.com/gh_mirrors/ti/tiny-dnn
点击查看免费下载

相关推荐

上一篇:3分钟上手DBeaver数据透视表:让查询结果自动高亮关键业务数据
下一篇:cli3/cli扩展兼容性测试:自动化验证不同Spotify版本支持

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/10 1:35:30

Docker CLI 的 legacy builder:`docker image build` 命令完整指南

CLI开发工具 【免费下载链接】cli The Docker CLI 项目地址&#xff1a; https://gitcode.com/gh_mirrors/cli5/cli 点击查看 免费下载 导读 本文以 docs/reference/commandline/image_build.md 为骨架&#xff0c;系统讲解 Docker CLI 中 docker image build&#xff08;即 …

作者头像 李华
网站建设 2026/10/10 1:34:35

基于 Go + Vue 的个人数字生活管理系统

Spring-_-Bear 的 CSDN 博客导航 文章目录SelfHub&#xff08;一隅&#xff09;✨ 核心特性&#x1f6e0;️ 技术栈&#x1f680; 快速开始后端服务部署前端应用部署默认登录账户&#x1f4f1; 功能模块&#x1f510; 登录页&#x1f4ca; 知行录统计看板任务列表完成情况&…

作者头像 李华