欢迎关注我的公众号 [极智视界],回复001获取Google编程规范

O_o>_<o_OO_o~_~o_O

  本文分析下 darknet load_weights 接口,这个接口主要做模型权重的加载。

文章目录

  • 1、darknet 数据加载流程
  • 2、load_weights 接口

1、darknet 数据加载流程

  之前的文章已经介绍了一下 darknet 目标检测的数据加载流程,并介绍了.data、.names 和 .cfg 的加载实现。

  接下来这里 load_weights 接口主要做 .weights 模型权重的加载。

2、load_weights 接口

  先来看一下接口调用:

load_weights(&net, weightfile);

  其中 net 为 network 结构体的实例,weightfile 为权重的文件路径,看一下 load_weights 的实现:

/// parser.c
void load_weights(network *net, char *filename)
{load_weights_upto(net, filename, net->n);
}

  主要调用了 load_weights_upto 函数:

/// parser.c
void load_weights_upto(network *net, char *filename, int cutoff)
{#ifdef GPUif(net->gpu_index >= 0){cuda_set_device(net->gpu_index);                         // 设置 gpu_index}
#endiffprintf(stderr, "Loading weights from %s...", filename);fflush(stdout);                                             // 强制马上输出FILE *fp = fopen(filename, "rb");if(!fp) file_error(filename);int major;int minor;int revision;fread(&major, sizeof(int), 1, fp);                          // 一些标志位的加载fread(&minor, sizeof(int), 1, fp);fread(&revision, sizeof(int), 1, fp);if ((major * 10 + minor) >= 2) {printf("\n seen 64");uint64_t iseen = 0;fread(&iseen, sizeof(uint64_t), 1, fp);*net->seen = iseen;}else {printf("\n seen 32");uint32_t iseen = 0;fread(&iseen, sizeof(uint32_t), 1, fp);*net->seen = iseen;}*net->cur_iteration = get_current_batch(*net);printf(", trained: %.0f K-images (%.0f Kilo-batches_64) \n", (float)(*net->seen / 1000), (float)(*net->seen / 64000));int transpose = (major > 1000) || (minor > 1000);int i;for(i = 0; i < net->n && i < cutoff; ++i){                     // 识别不同算子进行权重加载layer l = net->layers[i];if (l.dontload) continue;if(l.type == CONVOLUTIONAL && l.share_layer == NULL){load_convolutional_weights(l, fp);}if (l.type == SHORTCUT && l.nweights > 0) {load_shortcut_weights(l, fp);}if (l.type == IMPLICIT) {load_implicit_weights(l, fp);}if(l.type == CONNECTED){load_connected_weights(l, fp, transpose);}if(l.type == BATCHNORM){load_batchnorm_weights(l, fp);}if(l.type == CRNN){load_convolutional_weights(*(l.input_layer), fp);load_convolutional_weights(*(l.self_layer), fp);load_convolutional_weights(*(l.output_layer), fp);}if(l.type == RNN){load_connected_weights(*(l.input_layer), fp, transpose);load_connected_weights(*(l.self_layer), fp, transpose);load_connected_weights(*(l.output_layer), fp, transpose);}if(l.type == GRU){load_connected_weights(*(l.input_z_layer), fp, transpose);load_connected_weights(*(l.input_r_layer), fp, transpose);load_connected_weights(*(l.input_h_layer), fp, transpose);load_connected_weights(*(l.state_z_layer), fp, transpose);load_connected_weights(*(l.state_r_layer), fp, transpose);load_connected_weights(*(l.state_h_layer), fp, transpose);}if(l.type == LSTM){load_connected_weights(*(l.wf), fp, transpose);load_connected_weights(*(l.wi), fp, transpose);load_connected_weights(*(l.wg), fp, transpose);load_connected_weights(*(l.wo), fp, transpose);load_connected_weights(*(l.uf), fp, transpose);load_connected_weights(*(l.ui), fp, transpose);load_connected_weights(*(l.ug), fp, transpose);load_connected_weights(*(l.uo), fp, transpose);}if (l.type == CONV_LSTM) {if (l.peephole) {load_convolutional_weights(*(l.vf), fp);load_convolutional_weights(*(l.vi), fp);load_convolutional_weights(*(l.vo), fp);}load_convolutional_weights(*(l.wf), fp);if (!l.bottleneck) {load_convolutional_weights(*(l.wi), fp);load_convolutional_weights(*(l.wg), fp);load_convolutional_weights(*(l.wo), fp);}load_convolutional_weights(*(l.uf), fp);load_convolutional_weights(*(l.ui), fp);load_convolutional_weights(*(l.ug), fp);load_convolutional_weights(*(l.uo), fp);}if(l.type == LOCAL){int locations = l.out_w*l.out_h;int size = l.size*l.size*l.c*l.n*locations;fread(l.biases, sizeof(float), l.outputs, fp);fread(l.weights, sizeof(float), size, fp);
#ifdef GPUif(gpu_index >= 0){push_local_layer(l);}
#endif}if (feof(fp)) break;}fprintf(stderr, "Done! Loaded %d layers from weights-file \n", i);fclose(fp);
}

  以上有几个点不容易看懂,如以下这段:

int major;
int minor;
int revision;
fread(&major, sizeof(int), 1, fp);
fread(&minor, sizeof(int), 1, fp);
fread(&revision, sizeof(int), 1, fp);

  这个最好结合保存权重的接口一起来看,load_weights 是 save_weights 的解码过程,来看一下 save_weights_upto 的前面部分:

void save_weights_upto(network net, char *filename, int cutoff, int save_ema)
{#ifdef GPUif(net.gpu_index >= 0){cuda_set_device(net.gpu_index);}
#endiffprintf(stderr, "Saving weights to %s\n", filename);FILE *fp = fopen(filename, "wb");if(!fp) file_error(filename);int major = MAJOR_VERSION;int minor = MINOR_VERSION;int revision = PATCH_VERSION;fwrite(&major, sizeof(int), 1, fp);              // 先打上 majorfwrite(&minor, sizeof(int), 1, fp);              // 再打上 minorfwrite(&revision, sizeof(int), 1, fp);           // 再打上 revision(*net.seen) = get_current_iteration(net) * net.batch * net.subdivisions; // remove this line, when you will save to weights-file both: seen & cur_iterationfwrite(net.seen, sizeof(uint64_t), 1, fp);       // 最后打上 net.seen
......
}

  从上面的 save_weights 接口可以看出 darknet 的权重在前面会先打上几个标志:major、minor、revision、net.seen,然后再连续存储各层的权重数据,这样就不难理解 load_weights 的时候做这个解码了,以下是这几个参数的宏定义:

/// version.h
#define MAJOR_VERSION 0
#define MINOR_VERSION 2
#define PATCH_VERSION 5

  再回到 load_weights,在加载这些标志后是加载各层的权重,以卷积权重加载来说,里面的逻辑分两个:

  (1) 单 conv,使用 fread 根据特定大小依次加载 biases 和 weights;

  (2) conv + bn 融合,使用 fread 根据特定大小依次加载 biases、scales、rolling_mean、rolling_variance、weights。

  来看实现:

/// parser.c
void load_convolutional_weights(layer l, FILE *fp)
{if(l.binary){//load_convolutional_weights_binary(l, fp);//return;}int num = l.nweights;int read_bytes;read_bytes = fread(l.biases, sizeof(float), l.n, fp);               // load biasesif (read_bytes > 0 && read_bytes < l.n) printf("\n Warning: Unexpected end of wights-file! l.biases - l.index = %d \n", l.index);//fread(l.weights, sizeof(float), num, fp); // as in connected layerif (l.batch_normalize && (!l.dontloadscales)){read_bytes = fread(l.scales, sizeof(float), l.n, fp);          // load scalesif (read_bytes > 0 && read_bytes < l.n) printf("\n Warning: Unexpected end of wights-file! l.scales - l.index = %d \n", l.index);read_bytes = fread(l.rolling_mean, sizeof(float), l.n, fp);    // load rolling_meanif (read_bytes > 0 && read_bytes < l.n) printf("\n Warning: Unexpected end of wights-file! l.rolling_mean - l.index = %d \n", l.index);read_bytes = fread(l.rolling_variance, sizeof(float), l.n, fp);   // load rolling_varianceif (read_bytes > 0 && read_bytes < l.n) printf("\n Warning: Unexpected end of wights-file! l.rolling_variance - l.index = %d \n", l.index);if(0){int i;for(i = 0; i < l.n; ++i){printf("%g, ", l.rolling_mean[i]);}printf("\n");for(i = 0; i < l.n; ++i){printf("%g, ", l.rolling_variance[i]);}printf("\n");}if(0){fill_cpu(l.n, 0, l.rolling_mean, 1);fill_cpu(l.n, 0, l.rolling_variance, 1);}}read_bytes = fread(l.weights, sizeof(float), num, fp);          // load weightsif (read_bytes > 0 && read_bytes < l.n) printf("\n Warning: Unexpected end of wights-file! l.weights - l.index = %d \n", l.index);//if(l.adam){//    fread(l.m, sizeof(float), num, fp);//    fread(l.v, sizeof(float), num, fp);//}//if(l.c == 3) scal_cpu(num, 1./256, l.weights, 1);if (l.flipped) {transpose_matrix(l.weights, (l.c/l.groups)*l.size*l.size, l.n);}//if (l.binary) binarize_weights(l.weights, l.n, (l.c/l.groups)*l.size*l.size, l.weights);
#ifdef GPUif(gpu_index >= 0){push_convolutional_layer(l);}
#endif
}

  再说一下 fread,这个函数在框架源码中数据读取方面会用的比较多,来看一下这个 C 语言的函数:

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)

  参数说明:

  • ptr:指向带有最小尺寸 size * nmemb 字节的内存块的指针;
  • size:要读取的每个元素的大小,以字节为单位;
  • nmemb:元素的个数,每个元素的大小为 size 字节;
  • stream:指向 FILE 对象的指针,指定了一个输入流;

  返回值:成功读取的元素个数以 size_t 对象返回,返回值与 nmenb 参数一样,若不一样,则可能发生了读错误或达到了文件尾。

  好了,以上分析了 darknet 的 load_weights 接口及 weights 数据结构,再结合之前的文章就已经集齐了 darkent 目标检测数据加载部分的解读,希望我的分享对你的学习能有一点帮助。

 【公众号传送】

《【编程艺术】剖析 darknet load_weights 接口》

扫描下方二维码即可关注我的微信公众号【极智视界】,获取更多AI经验分享,让我们用极致+极客的心态来迎接AI !

极智Coding | 剖析 darknet load_weights 接口相关推荐

  1. 极智Coding | C 和 C++ 读存 bin 文件方法

      欢迎关注我的公众号 [极智视界],获取我的更多笔记分享   O_o   >_<   o_O   O_o   ~_~   o_O   本文介绍一下 C 和 C++ 读取和保存 bin 文 ...

  2. 极智AI | pytorch 与 darknet 计算卷积输出 shape 方式对比

      欢迎关注我的公众号 [极智视界],回复001获取Google编程规范   O_o   >_<   o_O   O_o   ~_~   o_O   本文记录了 pytorch 与 dar ...

  3. 极智AI | 量化实现分享五:详解格灵深瞳 EQ 量化算法实现

    欢迎关注我的公众号 [极智视界],回复001获取Google编程规范   O_o   >_<   o_O   O_o   ~_~   o_O   大家好,我是极智视界,本文剖析一下格灵深瞳 ...

  4. 极智AI | 昇腾 CANN ATC 模型转换

      欢迎关注我的公众号 [极智视界],获取我的更多笔记分享   大家好,我是极智视界,本文介绍一下 昇腾 CANN ATC 模型转换.   昇腾 CANN 的全称是 Compute Architect ...

  5. 极智读书 | 《树莓派开始,玩转Linux》读书分享

        感谢杭州地铁六号线,这里有我大把的美好读书时间.     这次要分享的书是<树莓派开始,玩转Linux>. 书简介     先上封面:     树莓派是我三年前刚接触硬件入手的一个 ...

  6. 极智Paper | YOLOv7 更高 更快 更强

      欢迎关注我的公众号 [极智视界],获取我的更多笔记分享   大家好,我是极智视界,本文解读一下 更高.更快.更强的 YOLOv7:Trainable bag-of-freebies sets ne ...

  7. 极智开发 | 昇腾atlas300 docker开发环境搭建

    ​  遵循 驱动 -> 固件的安装顺序. 容器内npu驱动安装   有两种方式: 1.宿主机内已经安装好了驱动,容器内调用宿主机的驱动: 2.管你宿主机有没有装驱动,我在容器里自己装: 1.宿主 ...

  8. 极智AI | 量化实现分享二:详解 KL 对称量化算法实现

      欢迎关注我的公众号 [极智视界],回复001获取Google编程规范   O_o   >_<   o_O   O_o   ~_~   o_O   大家好,我是极智视界,本文剖析一下 K ...

  9. 全球首款搭载鸿蒙操作系统的设备是什么,全球首款搭载鸿蒙操作系统:舒华展示极智2代豪华商用跑步机...

    随着人们生活节奏的加快,亚健康似乎成为了大家需要解决的头等大事,而健身房也就成为平时工作之余最常去的地方.在健身房中,最受关注的应该就是跑步机了,通过在跑步机上挥汗如雨,从而实现减脂,给自己一个轻松的 ...

最新文章

  1. d3.js 简易柱形图,入门demo
  2. keras从入门到放弃(十一)电影评价预测
  3. Unity3d之AssetBundle打包与读取
  4. (转)Spring Boot(十二):Spring Boot 如何测试打包部署
  5. Roundcube Webmail跨站脚本漏洞(CVE-2015-5381 )
  6. 能做出这样的数据可视化报告,不信老板不给你加薪
  7. C语言关键字能用大写字母,C语言关键字及其解释
  8. Android如何显示音标
  9. Pycharm工具下的数据可视化(图形绘制)
  10. 【现代密码学】作业一
  11. Jerry Wang的英语学习笔记
  12. Linux常用命令指南
  13. 一文搞掂十大经典排序算法
  14. Ping 1000个包看丢跑率
  15. 前端面试不用怕!一分钟带你了解es6的解构赋值
  16. [java] JNLP文件安装
  17. 热分析技术清单:导热材料热扩散系数闪光法测量中的样品厚度选择
  18. 页面之间数据调转传输
  19. win10下docker搭建ES7集群
  20. R树:处理空间存储问题

热门文章

  1. 网页链接如何做成html,html网页的超链接怎么添加?
  2. centos是arm还是amd_amd系列cpu安装linux
  3. 无人驾驶真体验!老百姓都能打得到的“共享无人车”来了
  4. WPS怎么在线转换成Word,WPS转换成Word的操作步骤
  5. 【图解版】B2C电商平台解决方案
  6. 以下python扩展库_以下对Python常用扩展库的描述错误的是哪一个选项?
  7. 视频存储网站服务器配置,视频存储服务器配置
  8. 【转租】【房东直租】【次渠东里一区、高层正规两居室、次卧】
  9. mysql 5.6 插入表情符
  10. Ubuntu18.04 + RTX2080Ti + CUDA +cudnn 环境配置