Darknet 框架学习笔记 ---- 1

我的学习计划

了解工程文件结构

重新下载编译,并上传至github

解析Makefile

源码分析


了解工程文件结构

  • cfg/  :  目录下面有很多.cfg文件,类似caffe的prototxt文件,包含网络结构,学习参数等
  • data/  :  存放训练数据和标注文件,.name文件存放类别名。labels/下有ASCII码32-127的8种尺寸的图片,显示标签用。
  • examples/  : 例程代码
  •  include/  :   darknet.h
  • obj/  :  编译中间文件
  • python/  :  karknet.py  ,  darknet.pyc  ,  proverbot.py
  • scipts/  :  有关训练集索引文件的脚本
  • src/  :  源码
  • backup/ : 目录下保存训练产生的权重文件,初始为空文件夹

重新下载编译,并上传至github

cd darknet
git reset
rm -rf .git* # 删除已有的git规则
git init

git add *  
git status  
gedit .gitignore 
git add .gitignore # 我的.gitingore似乎没用了

git commit -m "first commit"

git remote add origin https://github.com/Z-Jeff/darknet.git
git push -u origin master


# 在另一设备上,拷贝下项目:
git clone https://github.com/Z-Jeff/darknet.git

修改Makefile:

# Makefile 修改如下:
GPU=1
CUDNN=1
OPENCV=1
...
NVCC=/usr/local/cuda-10.0/bin/nvcc  # 可由 which nvcc查询 

 编译:

make 

下载权重文件并测试运行:

wget https://pjreddie.com/media/files/yolov3.weights
./darknet detect cfg/yolov3.cfg yolov3.weights data/dog.jpg // 测试单张图片

./darknet detector demo cfg/coco.data cfg/yolov3.cfg yolov3.weights // 摄像头实时检测

遇到问题:
CUDA Error: out of memory
darknet: ./src/cuda.c:36: check_error: Assertion ‘0’ failed.

解决:更改cfg目录下的yolov3.cfg,把subdivisions=16
改为subdivisions=64


解析Makefile

GPUCUDNNOPENCVOPENMPDEBUGARCHSLIBALIBEXECOBJDIROPTSCOMMON等都是自动变量

ARCH:

  • ARCH-gencode保证用户GPU可以动态选择最适合的GPU框架,-arch=compute_30,-code=sm_30表示计算能力3.0及以上的GPU都可以运行编译的程序。
  • NVIDIA GPU算力可由查看https://developer.nvidia.com/cuda-gpus
  • 我的显卡为GTX1050,算力是6.1,于是我将其改为
ARCH= -gencode arch=compute_61,code=compute_61
  •  我尝试了下改成 ARCH= -gencode arch=compute_62,code=compute_62 ,发现编译没出错,运行时出错:CUDA Error: no kernel image is available for execution on the device,以后如果报这种错误,说明你ARCH设定的算力比机器真实算力高。

SLIB指明动态链接库文件,ALIB指明静态链接库文件,EXEC指明可行文件名称,OBJDIR指明中间文件存放目录

OPTS指明编译选项,OPTS=-Ofast中-Ofast 表示忽视严格的标准,使用所有-O3优化。-Ofast详见 http://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

VPATHCCCCPNVCCARLDFLAGSCFLAGS等是内置变量

VPATH指定依赖文件的搜索路径,当有多个路径时用分号 : 隔开

AR命令可以用来创建、修改库,也可以从库中提出单个模块,参考博客https://www.cnblogs.com/LiuYanYGZ/p/5535982.html

ARFLAGS是库文件维护程序的选项

LDFLAGS是GCC链接选项参数,LDFLAGS= -lm -pthread,-lm代表链接数学函数库,-pthread代表链接多线程编译库

CFLAGS是C编译选项

addprefix函数是添加前缀

wildcard是扩展通配符,$(wildcard src/*.h)代表src/下的头文件

这是Makefile的执行主题,分号前面是“目标”,分号后面是“依赖”,下一行是“命令”

Makefile有三个非常有用的变量。分别是$@,$^,$<代表的意义分别是:

$@--目标文件,$^--所有的依赖文件,$<--第一个依赖文件。

$(OBJDIR)%.o 代表文件夹下所有的.o文件

mkdir的-p选项允许你一次性创建多层次的目录

当Makefile文件所在目录有文件名为clean的文件,命令行“.PHONY: clean”又没添加的话,执行make clean是无效的

所以“.PHONY: clean”就是保证即使目录下有文件名为clean的文件,也能正常执行make clean

-DGPU相当于添加了GPU宏定义,用来条件编译和预处理。

源码分析

在examples/darknet.c中有main()函数

  • find_int_arg(),作者自己写的函数,gpu_index = find_int_arg(argc, argv, "-i", 0)的目的是找到-i后的数字参数并赋予gpu_index, 并把-i 和其后的一个参数从argv[]中删除,这说明了-i后面接GPU参数。gpu_index仅仅在cuda_set_device(gpu_index)用到,说明gpu_index应该是有多张显卡的情况下,指明选择哪张显卡
  • average(),看名字,猜是计算数据集平均值用的,用法是./daeknet  average [cfgfile] [outfile] [weightfile],遇到了一个跟网络配置有关的函数parse_network_cfg()
    • parse_network_cfg()
      • 其中的 list 是作者自己定义的双向链表结构体,在darknet.h中查到其定义,链表的相关操作在list.c,有创建链表make_list(),取出链表中的一个节点list_pop(), 插入节点到链表list_insert(),释放链表中指针free_list(),释放链表内容free_list_contents(),链表转数组list_to_array()等操作
      • read_cfg(),顾名思义,是用来读.cfg文件用的,其中有section结构体,就是加了个type字符标志的链表,声明了section结构体变量options;再下面有个fgetl(),也是作者自己写的,用来读取文件的一行;strip(line)把每一行的空格,制表符和回车符号去掉;然后判断每一行的首字符,如果是 [ ,则将那一行保存到current -> type中去,如果是 \0  #  ;  则跳过,其他情况下,将一行保存到 current -> options 中。
        有些复杂,我用下图便于理解。每一个红色框是一个section,也是链表options的一个节点,每个section由type和options组成。list *sections = read_cfg(filename)的作用一目了然。section可以看做是一个“层”的表示。
      • make_network(),初始化网络,首先要了解network结构体,其包含了网络的参数,意义如下:
        network结构体:
          n batch *seen *t epoch subdivisions *layers *output policy
        含义

        层数

        int

        批大小

        int

        计数用途。

        比如用30批大小迭代一次,seen为30。

        运行时加参数-clear可使seen为0.

        size_t

         

        int

        迭代次数

        float

        batch分割次数

        为解决内存不足

        int

        结构体

        见下表

        输出结果

        float

        学习策略

        enum

        learning_rate momentum decay gamma scale power time_steps step max_batches *scales *steps num_steps burn_in
                                 

        layers结构体:

          type activation cost_type

        (*forward)

        (*backward)

        (*updata)
        含义

        层的类型。

        枚举变量,有卷积,反卷积,连接,池化等类型。

        激活函数。

        枚举变量,有LOGISTIC,RELU,RELIE等。

        损失类型。

        枚举变量。有

        SSE,MASKED,L1SEG等

        前向,后向操作。

        函数指针。

        形参是layers和network

        更新操作。

        函数指针。

        形参是layers和update_args。

        update_args详见darknet.h第97行

        onlyforward stopbackward dontload dontsave dontloadscales numload
           

        是否载入该层权重。

        int

            载入的卷积个数
      • parse_net_options(),作用将.cfg文件中读到的信息保存到network结构体变量net中,其源码大部分是赋值语句,特别注意下batch变量,batch先除于subdivisions,再乘上time_steps,这个结果才是正真的batch,我很好奇作者这样做的意图,难道batch大小要随时间变化?
      • load_weights(),作用是载入权重,它的主体是函数load_weights_upto()。load_weights_upto()有四个形参:network结构体变量,权重文件名,开始的层数,截止的层数。
        定位到它的源码,fread(&major, sizeof(int), 1, fp)代表读一个int变量到major中,权重文件开头是三个int变量:major,minor,revision。major和minor组成的数字控制network->seen的内容,这里我有点疑惑。
        接下来的循环体,以层为单位进行遍历,新建了layer结构体变量l并赋值,首先判断 l.dontload 是否加载权重,然后根据 l.type 做不同操作。虽然type有很多种类型,比如CONVOLUTIONAL,CONNECTED,BATCHNORM,RNN等,但其载入权重的方式只有三种:load_convolutional_weights(),load_connected_weights(),load_batchnorm_weights()
        • load_convolutional_weights():
          先载入l.n个float类型的偏置,说明layer.n是卷积层中卷积的个数。如果有BN操作并且l.dontloadscales不为1时,读BN算法相关的参数:scales,rolling_mean,rolling_variance(这里均值和方差是滚动的)。每个卷积核都有独立的BN算法参数,所以要载入n组参数。
          有关BN算法参考博客https://blog.csdn.net/yujianmin1990/article/details/78764597

          如果scales对应γ,似乎少了个β参数,我又疑惑了
          然后载入卷积的权重参数到l.weights,参数个数计算方法为int num = l.c/l.groups*l.n*l.size*l.size; ( 通道数/分组数×卷积个数×卷积尺寸×卷积尺寸 ),l.flipped控制权重参数是否转置。
        • load_connected_weights():
          跟load_convolutional_weights()类似,就是biases和weights数量不一样。
        • load_batchnorm_weights():只载入scales,rolling_mean,rolling_variance这三组参数。
      • 整个average()看下来,其作用就是计算每层biases,weights,scales,rolling_mean,rolling_variance的平均值。
  • test_detector():这是运行./darknet  detect  cfg/yolov3.cfg  yolov3.weights  data/dog.jpg时调用的函数,可以看到有七个参数:datacfg,cfgfile,weightfile,filename,thresh,hier_thresh,outfile,fullscreen。
    • datacfg:有关数据集信息的文件。以cfg/coco.data为例

      如果只是推理,只会用到classes(类别个数),names(类别名称)
    • cfgfile:网络配置文件。以cfg/yolov3.cfg为例,它包含yolov3网络配置的参数,比如学习率,批大小。以及每层的卷积核大小,pad,stride等信息
    • weightfile:权重文件。以yolov3.weights为例,它包含yolov3网络各层的权重。
    • filename:测试的图片文件名。
    • thresh:用于目标检测的阈值。
    • hier_thresh:
    • outfile:输出图片文件名。输出图片为检测的结果
    • fullscreen:

test_detector()的输入参数作用了解后,开始读此函数的源码:

list *options = read_data_cfg(datacfg);   读datacfg文件

char *name_list = option_find_str(options, "names", "data/names.list");    然后找datacfg中“name”的那一项,就是标签名称文件。如果没找到,就默认为data/names.list。

char **names = get_labels(name_list);     获取标签的名称

image **alphabet = load_alphabet();    从data/labels/下加载ASCII码32-127的8种尺寸的图片,后边显示标签用。

network *net = load_network(cfgfile, weightfile, 0);    用cfgfile载入网络配置,用weightfile载入网络权重,第三个参数控制set->seen

set_batch_network(net, 1);    配置网络批大小为1

srand(2222222);    随机数发生器的初始化函数

image im = load_image_color(input,0,0);    载入图片。如果编译时选定了opencv,会用opencv的函数载入图片,否则用stb_image.h中的函数载入图片。后两个的参数是w,h(宽,高),如果这两个参数不为0时,会resize成对应的尺寸,如果为0时,则按原尺寸载入

image sized = letterbox_image(im, net->w, net->h);    将图片缩放适合输入网络的尺寸,其具体操作见下图:

layer l = net->layers[net->n-1];    这是网络最后一层

network_predict(net, X);    推理预测,函数内部调用forward_network(),

detection *dets = get_network_boxes(net, im.w, im.h, thresh, hier_thresh, 0, 1, &nboxes);    获取网络目标检测输出的box,这里面有关目标检测的细节有点多,暂时跳过

if (nms) do_nms_sort(dets, nboxes, l.classes, nms);    对检测结果做非极大值抑制(NMS)

draw_detections(im, dets, nboxes, thresh, names, alphabet, l.classes);    画出检测结果

看了average(),和test_detector()两个函数,下一步,想要了解darknet是如何训练的。

猜你喜欢

转载自blog.csdn.net/Jeff_zjf/article/details/101837477
今日推荐