在本文正式开始之前,笔者要先和各位读者朋友们道个歉。因为身为研一小白的笔者实在身不由己,除了各种任务之外,还要应付繁忙的课程,忙于各种考试像一只咸鱼。因此耽误了博文的撰写,对不起各位读者朋友,笔者在忙完6月进入研二之后一定再接再厉。下面开始干货~

本篇还是一个插播的博客,旨在向大家分享caffemodel里面记录的信息。在我们使用caffe框架训练网络时,最终总会生成一个caffemodel,我们大概知道里面记录了模型的参数。而作为初学者的笔者,总是不分青红皂白地就直接在模型的验证阶段就调用了。从来没有去看过caffemodel里面到底讲了什么,那么,本篇中笔者就解析一下caffemodel。

有读者朋友未免会问,笔者是出于什么目的想解析caffemodel的呢?这说来话长,是笔者最近做的项目需要将model拆开并进行改动,因此需要去解析caffemodel。那么笔者是怎么摸索的呢?各位读者朋友们是否还记得,笔者前两期的博客里面提及的caffe官方提供的classification.cpp文件,里面在执行Classifier类的构造函数的时候,有一个net_指针,并且执行了一个操作:

[cpp] view plaincopy
  1. net_->CopyTrainedLayersFrom(trained_file);

这个CopyTrainedLayersFrom函数就是从caffemodel里面载入我们需要的参数了。然后笔者就从这个函数开始挖,进入net.cpp文件:

[cpp] view plaincopy
  1. template <typename Dtype>
  2. void Net<Dtype>::CopyTrainedLayersFrom(const string trained_filename) {
  3. if (H5Fis_hdf5(trained_filename.c_str())) {
  4. CopyTrainedLayersFromHDF5(trained_filename);
  5. } else {
  6. CopyTrainedLayersFromBinaryProto(trained_filename);
  7. }
  8. }

在这里很明显执行了else下面的语句,从二进制文件中去读取了参数,然后,笔者又找到了CopyTrainedLayersFromBinaryProto函数,很巧就在CopyTrainedLayersFrom函数的下方:

[cpp] view plaincopy
  1. template <typename Dtype>
  2. void Net<Dtype>::CopyTrainedLayersFromBinaryProto(
  3. const string trained_filename) {
  4. NetParameter param;
  5. ReadNetParamsFromBinaryFileOrDie(trained_filename, &parm);
  6. CopyTrainedLayersFrom(param);
  7. }

在里面首先执行了一个ReadNetParamsFromBinaryFileOrDie函数,把二进制文件中的参数读到parm里面,parm是一个NetParameter类型的,NetParameter继承了实际是一个Message,Message是proto类型的,详细的笔者后话解析。然后笔者去找了一下ReadNetParamsFromBinaryFileOrDie函数,这个函数在upgrade_proto.cpp里面:

[cpp] view plaincopy
  1. void ReadNetParamsFromBinaryFileOrDie(const string& param_file,
  2. NetParameter* param) {
  3. CHECK(ReadProtoFromBinaryFile(param_file, param))
  4. << "Failed to parse NetParameter file: " << param_file;
  5. UpgradeNetAsNeeded(param_file, param);
  6. }

这个函数完成的功能是首先进行ReadProtoFromBinaryFile,然后执行了一个更新的操作。然后笔者就去找这个ReadProtoFromBinaryFile函数,这个函数在io.cpp里面:

[cpp] view plaincopy
  1. bool ReadProtoFromBinaryFile(const char* filename, Message* proto) {
  2. int fd = open(filename, O_RDONLY);
  3. CHECK_NE(fd, -1) << "File not found: " << filename;
  4. ZeroCopyInputStream* raw_input = new FileInputStream(fd);
  5. CodedInputStream* coded_input = new CodedInputStream(raw_input);
  6. coded_input->SetTotalBytesLimit(kProtoReadBytesLimit, 536870912);
  7. bool success = proto->ParseFromCodedStream(coded_input);
  8. delete coded_input;
  9. delete raw_input;
  10. close(fd);
  11. return success;
  12. }

这个函数就比较底层了,读者朋友们可以看到,这个函数里面就使用了open函数,和一些底层的google::protobuf的数据流。在这里我们其实就明白,是先把二进制文件(caffemodel)转化成文件流,再放入proto里面。

那么,笔者大胆猜想,能读就能写。

其实io.cpp里面已经定义了这种接口:

[cpp] view plaincopy
  1. void WriteProtoToTextFile(const Message& proto, const char* filename) {
  2. int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
  3. FileOutputStream* output = new FileOutputStream(fd);
  4. CHECK(google::protobuf::TextFormat::Print(proto, output));
  5. delete output;
  6. close(fd);
  7. }

也就是说:先用ReadProtoFromBinaryFile函数将二进制文件读入proto里面,再将proto文件读入txt文件就行了。

笔者迫不及待地拿lenet网络训练生成的caffemodel做了个测试:

[cpp] view plaincopy
  1. #include <caffe/caffe.hpp>
  2. #include <google/protobuf/io/coded_stream.h>
  3. #include <google/protobuf/io/zero_copy_stream_impl.h>
  4. #include <google/protobuf/text_format.h>
  5. #include <algorithm>
  6. #include <iosfwd>
  7. #include <memory>
  8. #include <string>
  9. #include <utility>
  10. #include <vector>
  11. #include <iostream>
  12. #include "caffe/common.hpp"
  13. #include "caffe/proto/caffe.pb.h"
  14. #include "caffe/util/io.hpp"
  15. using namespace caffe;
  16. using namespace std;
  17. using google::protobuf::io::FileInputStream;
  18. using google::protobuf::io::FileOutputStream;
  19. using google::protobuf::io::ZeroCopyInputStream;
  20. using google::protobuf::io::CodedInputStream;
  21. using google::protobuf::io::ZeroCopyOutputStream;
  22. using google::protobuf::io::CodedOutputStream;
  23. using google::protobuf::Message;
  24. int main()
  25. {
  26. NetParameter proto;
  27. ReadProtoFromBinaryFile("/home/cvlab/files/caffe-master/data/mnist/lenet_iter_10000.caffemodel", &proto);
  28. WriteProtoToTextFile(proto, "/home/cvlab/files/caffe-master/data/mnist/test.txt");
  29. return 0;
  30. }

笔者训练了一个lenet训练生成的二进制文件,并将其写入了一个名为test.txt的文件中。

其对应的CMakeLists.txt文件为:

[cpp] view plaincopy
  1. cmake_minimum_required (VERSION 2.8)
  2. project (pt_test)
  3. add_executable(pt_test pt.cpp)
  4. include_directories ( /home/cvlab/files/caffe-master/include
  5. /usr/local/include
  6. /usr/local/cuda/include
  7. /usr/include )
  8. target_link_libraries(pt_test
  9. /home/cvlab/files/caffe-master/build/lib/libcaffe.so
  10. /usr/lib/x86_64-linux-gnu/libglog.so
  11. /usr/lib/x86_64-linux-gnu/libboost_system.so
  12. )

编译执行:

然后我们打开test.txt文件可以见到:

一共43万多行,记录了lenet-5的网络参数。因为lenet-5使用了全连接层,因此参数规模是庞大的(全连接层的参数约占了总体参数规模的90%)。

同时也可以看到,读出的二进制文件中参数规格是按照caffe.proto中协定的格式来的。

到此,我们就能清晰地看到caffemodel中记载的数据和格式了。

欢迎阅读笔者后续博客,各位读者朋友的支持与鼓励是我最大的动力!

written by jiong

故上兵伐谋,其次伐交,其次伐兵,其下攻城;攻城之法为不得已。

tensorflow2caffe(1) : caffemodel解析,caffemodel里面到底记录了什么?相关推荐

  1. JavaScript预解析、作用域题目记录

    JavaScript预解析.作用域题目记录 写出以下题目执行的结果及过程分析 function fun ( n ) {console.log( n );var n = 456;console.log( ...

  2. 我记录网站综合系统 -- 技术原理解析[3:我记录框架处理流程]

    要分析我记录的整个处理流程,先从wojilu的初始化入口,看看wojilu在初始化的时候到底做了些什么事情. 相关代码:wojilu.Web\Global.asax 1 void Applicatio ...

  3. Python 实现动态解析阿里云DNS记录

    一.背景 最近有一个需求,公司内网的IP地址会发生变化,导致阿里云域名不能解析到新的IP地址,此时我们需要对阿里云的域名进行更新 二.实现 2.1 获取本地出口的公网IP 2.1.1 通过命令或网页 ...

  4. DNS解析中的A记录、AAAA记录、CNAME记录、MX记录、NS记录、TXT记录、SRV记录、URL转发等

    A A记录: 将域名指向一个IPv4地址(例如:100.100.100.100),需要增加A记录 NS NS记录: 域名解析服务器记录,如果要将子域名指定某个域名服务器来解析,需要设置NS记录 SOA ...

  5. 阿里云解析配置多 A 记录实现接入层DNS 负载均衡

    在负载均衡领域,通过设置dns域名的负载,以使得后端负载均衡服务器的水平扩展,比如通过设置某域名www.vrtellyou.com 解析多个负载均衡服务器的IP或多个站点层的方式,达到请求分散的目的, ...

  6. emf文件解析处理EMR_RESERVED_108格式记录

    参照链接:http://www.flounder.com/metafileexplorer.htm 在参照链接下载项目基础上进行格式解析,主要是分析里面的文本化的数据, 对于EMR_RESERVED_ ...

  7. Nginx 配置实现web解析php代码 过程记录

    [Nginx配置] Nginx本身不支持对php的解析,需要将php代码转发到php-fpm 进程管理器来交给php解析器解析代码. 重要的配置注意注释位置: user www www; # 用户 用 ...

  8. 联通研究院霍龙社博士深度解析“AI项目到底适不适合开源”

    近年来,AI开源逐渐成为众多开发者谈论的核心,但也出现了不少"AI不适合开源"的声音,那么,AI项目到底适不适合开源呢? 为了解答这个专业问题,9月19日,OpenI启智社区特邀联 ...

  9. windows10 CAD解析库libredwg安装记录

    因为项目需要解析CAD,网上搜了下C++解析库中目前libredwg还在更新,且支持最新2018版本的CAD图纸,所以安装试试. 一.安装环境配置 在windows10上安装libredwg前需要安装 ...

最新文章

  1. git 提交代码命令_Git命令可视化展示,代码管理再也不愁了,建议收藏!
  2. 数组中没有给初始值_Array中的reduce()、filter()、map()几张图搞懂
  3. CentOS 7 安装方式汇总
  4. Cocos2d 3.0继承自Sprite的类在addChild后出现故障
  5. QuartZ.net 常用配置说明
  6. 设置导航栏的相关属性
  7. 行业报告:医疗数据泄露愈发严重,谁之过错?
  8. sourcetree windows下中文乱码
  9. 使用Sigar采系统信息
  10. 国内ERP技术公司综合
  11. ASP.net校友录毕业设计(源代码+论文+开题报告+答辩PPT)ASP.NET小型证券术语解释及翻译系统的设计与开发(源代码+论文)
  12. 4r照片尺寸是多大_数码照片4D、4R、6RW是什么意思?4R照片尺寸是多大图片?
  13. ArcGIS基础:合并表格(追加、合并工具)
  14. nvidia卸载程序失败_英伟达显卡驱动安装失败怎么办?
  15. 一个月裂变50多万人?商城引流模式玩法——分享购
  16. 京东和区块链的那些事儿
  17. java.lang.UnsupportedClassVersionError: com/mysql/jdbc/Driver : Unsupported major.minor version 52.0
  18. Android人生整理第二章:Java编程第一节-对象导论
  19. STM32L152RE实现串口发送及接收数据
  20. uni-app小程序到微信发布踩的坑

热门文章

  1. boost::statechart模块实现无效结果复制测试
  2. boost::insert相关的测试程序
  3. boost::multiprecision模块Eigen相关的测试程序
  4. boost::hana::template_用法的测试程序
  5. Boost.Flyweight 序列化示例
  6. boost::coroutine模块实现不对称链的测试程序
  7. Boost:同步化的测试程序
  8. VTK:可视化之QuadricVisualization
  9. VTK:Rendering之Mace
  10. VTK:PolyData之PointInsideObject