darknet 源码阅读(零) - Entry Point

系列目录

darknet 源码阅读(零) - Entry Point
darknet 源码阅读(一) - 解析网络配置文件 cfg
darknet 源码阅读(二) - 加载训练样本数据
darknet 源码阅读(三) - 训练网络
darknet 源码阅读(番外篇一) - 卷积层

从第一次接触 darknet 到现在也有很长时间了, 最初的印象是代码中几乎没有使用任何第三方库, 相比于 Caffe 的编译速度, darknet 简直快的飞起. 当时就有一种阅读源码的想法, 最近正好项目需要且时间允许, 就顺便完成了这个计划吧.

源码阅读第一步: 找到入口 - main() 函数.

文件: darknet/examples/darknet.c,   
函数: main()

1. 关于分析主线的确定


darknet 实现了较多的深度学习应用工具, 例如: classifier, segmenter, detector 等. 基于目前的研究方向为目标检测, 以 detector 作为主线展开后续的分析.

int main(int argc, char **argv)
{
    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")){
    ...
    } else if (0 == strcmp(argv[1], "detector")){
        run_detector(argc, argv);
    } else if (0 == strcmp(argv[1], "classifier")){
        run_classifier(argc, argv);
    } else if (0 == strcmp(argv[1], "regressor")){
        run_regressor(argc, argv);
    } else if (0 == strcmp(argv[1], "segmenter")){
        run_segmenter(argc, argv);
    } 
    ...
    } else if (0 == strcmp(argv[1], "imtest")){
        test_resize(argv[2]);
    } else {
        fprintf(stderr, "Not an option: %s\n", argv[1]);
    }
    return 0;
}

2. 目标检测 - run_detector


对于任意一个深度学习应用场景, 必不可少的三步: 训练-验证-测试. 同样地, 对于 detector 也是如此, 它应该包含这些工具. run_detector() 函数中为我们提供了这些功能. 由于测试阶段中完成的工作几乎和训练阶段的前向运算完全一致, 因此, 我们重点分析 detector 的训练过程.

void run_detector(int argc, char **argv)
{
    float thresh = find_float_arg(argc, argv, "-thresh", .5);
    ...
    if(argc < 4){
        fprintf(stderr, "usage: %s %s [train/test/valid] [cfg] [weights (optional)]\n", argv[0], argv[1]);
        return;
    }
    ...
    char *datacfg = argv[3];
    char *cfg = argv[4];
    char *weights = (argc > 5) ? argv[5] : 0;
    char *filename = (argc > 6) ? argv[6]: 0;
    if(0==strcmp(argv[2], "test")) test_detector(datacfg, cfg, weights, filename, thresh, hier_thresh, outfile, fullscreen);
    else if(0==strcmp(argv[2], "train")) train_detector(datacfg, cfg, weights, gpus, ngpus, clear);
    else if(0==strcmp(argv[2], "valid")) validate_detector(datacfg, cfg, weights, outfile);
    else if(0==strcmp(argv[2], "valid2")) validate_detector_flip(datacfg, cfg, weights, outfile);
    else if(0==strcmp(argv[2], "recall")) validate_detector_recall(cfg, weights);
    else if(0==strcmp(argv[2], "demo")) {
        list *options = read_data_cfg(datacfg);
        int classes = option_find_int(options, "classes", 20);
        char *name_list = option_find_str(options, "names", "data/names.list");
        char **names = get_labels(name_list);
        demo(cfg, weights, thresh, cam_index, filename, names, classes, frame_skip, prefix, avg, hier_thresh, width, height, fps, fullscreen, savevideo);
    }
}

3. 训练检测器 - train_detector


这是一个很庞大的系统, 当然不可能用一篇博客的篇幅来理清其中的所有细节. 因此, 就有了接下来的系列文章. 这里只是先从整体上分析一下训练一个检测器的流程.

  • 解析网络配置文件;
  • 加载训练样本图像和 labels;
  • 开始训练;
  • 训练结束后保存模型权重;

因此, 接下来的博客也会按照这个顺序依次分析.

下面贴出 train_detector() 函数的框架代码.

void train_detector(char *datacfg, char *cfgfile, 
				     char *weightfile, int *gpus, int ngpus, int clear)
{
	// 解析网络配置文件
    list *options = read_data_cfg(datacfg);
    for(i = 0; i < ngpus; ++i){
    	...
        nets[i] = load_network(cfgfile, weightfile, clear);
        nets[i]->learning_rate *= ngpus;
    }
    
    ...

    pthread_t load_thread = load_data(args);
    while(get_current_batch(net) < net->max_batches){
        ...
        // 加载训练样本图像和 labels;
        pthread_join(load_thread, 0);
        train = buffer;  // 加载完成, 拿到加载后的数据  
        load_thread = load_data(args);   // 开启新的加载线程, 供下一次使用

        ...
        // 开始训练网络;
        float loss = 0;
#ifdef GPU
        if(ngpus == 1){
            loss = train_network(net, train);
        } else {
            loss = train_networks(nets, ngpus, train, 4);
        }
#else
        loss = train_network(net, train);
#endif
        ...
        free_data(train);
    }
    
    // 训练结束后保存模型权重
#ifdef GPU
    if(ngpus != 1) sync_nets(nets, ngpus, 0);
#endif
    char buff[256];
    sprintf(buff, "%s/%s_final.weights", backup_directory, base);
    save_weights(net, buff);
}

总结

万事开头难, 既然已经走到了这里, 说明距离入门 darkent 又近了一步. 正所谓: 道路是曲折的, 方向是前进的.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值