darknet是使用C和CUDA编写的开源的神经网络框架,它快速且使用简单,之前在海康做caffe方面的工作,本想研究caffe的源代码,但是被导师推荐阅读darknet源代码加深对深度学习的理解而且还能巩固C语言,由此记录一下我的darknet源码阅读之路。
一、darknet安装
本记录主要是阅读源码,所以opencv gpu我就先不弄了,darknet安装非常简单,直接源码安装,在github上下载源代码:
https://github.com/pjreddie/darknet
下载过后解压得到darknet-master文件夹,里面主要包含几个重要的文件夹和makefile和几个license,cfg文件夹存放的是各种常见网络结构的配置文件如yolo、rcnn等,如果想要定义自己的网络结构需要编写自己的cfg文件,这个感觉类似caffe的prototxt。data文件夹下是各种数据集如接下来演示会用到的dog图片。examples文件夹中存放的各种检测算法的例子,如detector.c就是检测的代码,根据你输入run_detector函数的参数是train还是test转到其内部的train_detector或者test_detector,此文件夹中最重要的文件是darknet.c,具体细节之后会讲到。include文件夹中只有一个文件darknet.h是darknet的头文件里面定义了很多要用到的结构,对于熟悉C语言结构体很有帮助。scripts文件夹中是几个shell脚本看几个文件的名字应该是用来获取数据集的。src文件夹中是CNN库的很多细节实现,这个文件夹中的代码应该会花好长时间来研究,里面有BN层的实现、卷积层的实现、正则化等等阅读这部分代码我觉得对于深度学习各个基础知识的理解还是非常有帮助的。
下载好了源代码是第一步,之后还要进行编译,这部分很好做,只要cd进入darknet-master之后执行make,这样就编译好了,这是你会发现文件夹里多出了obj、backup、results三个文件夹和libdarknet.a静态库、libdarknet.so动态库。动态链接的基本思想简单来说就是不对那些组成程序的目标文件进行链接,而是当程序运行时才进行链接,从而解决了静态链接空间浪费的问题。obj文件夹下就是和所有目标文件。这样darknet就非常简单的安装好了。
二、简单的使用
darknet的使用也是很简单,我们先在darknet官网上下载好已经训练出的yolov3的权重,之后在终端运行
./darknet detect cfg/yolov3.cfg yolov3.weights data/dog.jpg
这个命令结合代码非常好理解,首先可以看darknet.c的主函数中
int main(int argc, char **argv)
{
//test_resize("data/bad.jpg");
//test_box();
//test_convolutional_layer();
if(argc < 2){
fprintf(stderr, "usage: %s <function>\n", argv[0]);
return 0;
}
gpu_index = find_int_arg(argc, argv, "-i", 0);
if(find_arg(argc, argv, "-nogpu")) {
gpu_index = -1;
}
#ifndef GPU
gpu_index = -1;
#else
if(gpu_index >= 0){
cuda_set_device(gpu_index);
}
#endif
if (0 == strcmp(argv[1], "average")){
average(argc, argv);
} else if (0 == strcmp(argv[1], "yolo")){
run_yolo(argc, argv);
} else if (0 == strcmp(argv[1], "super")){
run_super(argc, argv);
} else if (0 == strcmp(argv[1], "lsd")){
run_lsd(argc, argv);
} else if (0 == strcmp(argv[1], "detector")){
run_detector(argc, argv);
} else if (0 == strcmp(argv[1], "detect")){
float thresh = find_float_arg(argc, argv, "-thresh", .5);
char *filename = (argc > 4) ? argv[4]: 0;
char *outfile = find_char_arg(argc, argv, "-out", 0);
int fullscreen = find_arg(argc, argv, "-fullscreen");
test_detector("cfg/coco.data", argv[2], argv[3], filename, thresh, .5, outfile, fullscreen);
可以看出该程序是根据给定的argv来决定程序的走向,如本例argv[1]为detect,也就是说程序会转到detector.c中的test_dector函数中,也就是测试检测,那么args[2]、args[3]、args[4]都分明指代什么呢,我们来看test_detector函数的形参
void test_detector(char *datacfg, char *cfgfile, char *weightfile, char *filename, float thresh, float hier_thresh, char *outfile, int fullscreen)
对比来看args[2]是所用检测网络的配置文件,定义了网络的结构,args[3]指的是测试要用到的网络权重的配置文件,args[4]也就是filename这个指的是要测试的图片,从main中的if分支可以看出filename可以不用制定,但即使你不指定程序会提醒你“Enter Image Path:”,还有一点“cfg/coco.data”是做什么的?通过分析test_detector函数我认为这是为了得到coco数据集中label的列表,从而显示filename检测出来的物体的名称,而这个名称是根据label列表索引到的,当然这只是我目前没有对源码深入理解的猜想,如有不正确指出欢迎指正。