yolov4 darknet 源码学习笔记(二) 多线程数据加载

 

多线程数据加载(以训练过程为例)

 darknet的数据加载机制过程:

step1: 在darknet源码detector.c-->train_detector()-->load_data(args)首先创建并启动一个线程。

pthread_t load_thread = load_data(args);  //读数据

load_data()函数,首先启动一个线程,调用load_threads,load_threads是实际加载数据的线程,再load_threads加载完成前,主线程会等待。

//args:网络参数
pthread_t load_data(load_args args)
{
    pthread_t thread;
    struct load_args* ptr = (load_args*)xcalloc(1, sizeof(struct load_args));
    *ptr = args; //现在ptr是args的拷贝,二者使用不同的内存空间,但由于ptr是args的拷贝,因此ptr.d同样也指向buffer。 这个buffer用来不断获取训练数据信息并且传回train_detector中。


    //load_data中首先启动一个线程调用load_threads,
    //load_threads是实际加载数据的线程,再load_threads加载完成前,主线程会等待。
    if(pthread_create(&thread, 0, load_threads, ptr)) error("Thread creation failed");
    return thread;
}


step2: 在load_threads中通过循环方式, 创建args.threads=64个线程, 每个线程分别都与函数run_thread_loop绑定.

void *load_threads(void *ptr)
{
    //srand(time(0));
    int i;
    load_args args = *(load_args *)ptr; //此时args是ptr的拷贝,args.d指向buffer
    if (args.threads == 0) args.threads = 1;
    data *out = args.d; // 令out同样指向buffe
    int total = args.n; // batchsize
    free(ptr); //释放在load_data函数申请的ptr空间

    data* buffers = (data*)xcalloc(args.threads, sizeof(data)); // 此处申请的buffers用来存放训练数据
    
    if (!threads) {
        threads = (pthread_t*)xcalloc(args.threads, sizeof(pthread_t));
        run_load_data = (volatile int *)xcalloc(args.threads, sizeof(int));
        args_swap = (load_args *)xcalloc(args.threads, sizeof(load_args));
        fprintf(stderr, " Create %d permanent cpu-threads \n", args.threads);

        //共申请了64个data类型空间与64个pthread_t类型空间,
        //在循环中分别调用run_thread_loop,
        for (i = 0; i < args.threads; ++i) {
            int* ptr = (int*)xcalloc(1, sizeof(int));
            *ptr = i;
            if (pthread_create(&threads[i], 0, run_thread_loop, ptr)) error("Thread creation failed");
        }
    }

    for (i = 0; i < args.threads; ++i) {
        args.d = buffers + i;
        //此处每次args.d的地址向后移动一个data类型空间,args.n的值为batchsize/64。
        args.n = (i + 1) * total / args.threads - i * total / args.threads;

        pthread_mutex_lock(&mtx_load_data);
        args_swap[i] = args;
        pthread_mutex_unlock(&mtx_load_data);

        custom_atomic_store_int(&run_load_data[i], 1);  // run thread
    }
    for (i = 0; i < args.threads; ++i) {
        while (custom_atomic_load_int(&run_load_data[i])) this_thread_sleep_for(thread_wait_ms); //   join
    }

    //buffers中的数据都被concat在一起,同时复制到out执行的内存空间.
    *out = concat_datas(buffers, args.threads);
    out->shallow = 0;

    //释放在load_threads中分配的内存空间
    for(i = 0; i < args.threads; ++i){
        buffers[i].shallow = 1;
        free_data(buffers[i]);
    }
    free(buffers);
    //free(threads);
    return 0;
}


step3: 每个线程的run_thread_loop中调用load_thread()函数进行数据读取。

//run_thread_loop函数中启动线程,并调用load_thread函数,主线程返回load_threads中等待。
void *run_thread_loop(void *ptr)
{
    const int i = *(int *)ptr;

    while (!custom_atomic_load_int(&flag_exit)) {
        while (!custom_atomic_load_int(&run_load_data[i])) {
            if (custom_atomic_load_int(&flag_exit)) {
                free(ptr);
                return 0;
            }
            this_thread_sleep_for(thread_wait_ms);
        }

        pthread_mutex_lock(&mtx_load_data);
        load_args *args_local = (load_args *)xcalloc(1, sizeof(load_args));
        *args_local = args_swap[i];
        pthread_mutex_unlock(&mtx_load_data);

        load_thread(args_local);

        custom_atomic_store_int(&run_load_data[i], 0);
    }
    free(ptr);
    return 0;
}

step4: load_thread函数调用load_data_detection函数加载数据。

void *load_thread(void *ptr)
{
    //srand(time(0));
    //printf("Loading data: %d\n", random_gen());
    load_args a = *(struct load_args*)ptr;
    if(a.exposure == 0) a.exposure = 1;
    if(a.saturation == 0) a.saturation = 1;
    if(a.aspect == 0) a.aspect = 1;

    if (a.type == OLD_CLASSIFICATION_DATA){
        *a.d = load_data_old(a.paths, a.n, a.m, a.labels, a.classes, a.w, a.h);
    } else if (a.type == CLASSIFICATION_DATA){
        *a.d = load_data_augment(a.paths, a.n, a.m, a.labels, a.classes, a.hierarchy, a.flip, a.min, a.max, a.w, a.h, a.angle, a.aspect, a.hue, a.saturation, a.exposure, a.mixup, a.blur, a.show_imgs, a.label_smooth_eps, a.dontuse_opencv, a.contrastive);
    } else if (a.type == SUPER_DATA){
        *a.d = load_data_super(a.paths, a.n, a.m, a.w, a.h, a.scale);
    } else if (a.type == WRITING_DATA){
        *a.d = load_data_writing(a.paths, a.n, a.m, a.w, a.h, a.out_w, a.out_h);
    } else if (a.type == REGION_DATA){
        *a.d = load_data_region(a.n, a.paths, a.m, a.w, a.h, a.num_boxes, a.classes, a.jitter, a.hue, a.saturation, a.exposure);
    } else if (a.type == DETECTION_DATA){
        //检测调用
        //此处a是ptr的拷贝,a.d指向的是buffers的地址空间,
        //所以不需要再额外调用calloc函数,就可以直接使用*a.d。
        //free ptr后,在load_data_in_thread中申请的内存空间也就被释放了。
        //同时load_thread函数返回后,a变量占用的栈空间也会被释放。
        *a.d = load_data_detection(a.n, a.paths, a.m, a.w, a.h, a.c, a.num_boxes, a.truth_size, a.classes, a.flip, a.gaussian_noise, a.blur, a.mixup, a.jitter, a.resize,
            a.hue, a.saturation, a.exposure, a.mini_batch, a.track, a.augment_speed, a.letter_box, a.mosaic_bound, a.contrastive, a.contrastive_jit_flip, a.show_imgs);
    } else if (a.type == SWAG_DATA){
        *a.d = load_data_swag(a.paths, a.n, a.classes, a.jitter);
    } else if (a.type == COMPARE_DATA){
        *a.d = load_data_compare(a.n, a.paths, a.m, a.classes, a.w, a.h);
    } else if (a.type == IMAGE_DATA){
        *(a.im) = load_image(a.path, 0, 0, a.c);
        *(a.resized) = resize_image(*(a.im), a.w, a.h);
    }else if (a.type == LETTERBOX_DATA) {
        *(a.im) = load_image(a.path, 0, 0, a.c);
        *(a.resized) = letterbox_image(*(a.im), a.w, a.h);
    } else if (a.type == TAG_DATA){
        *a.d = load_data_tag(a.paths, a.n, a.m, a.classes, a.flip, a.min, a.max, a.w, a.h, a.angle, a.aspect, a.hue, a.saturation, a.exposure);
    }
    free(ptr);
    return 0;
}

step5: load_data_detection函数从所有训练图片中,随机读取n张,并对这n张图片进行数据增强,同时矫正增强后的数据标签信息。这个函数在下一节介绍

 

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
需要学习Windows系统YOLOv4的同学请前往《Windows版YOLOv4目标检测实战:原理与码解析》,课程链接 https://edu.csdn.net/course/detail/29865【为什么要学习这门课】 Linux创始人Linus Torvalds有一句名言:Talk is cheap. Show me the code. 冗谈不够,放码过来!  代码阅读是从基础到提高的必由之路。尤其对深度学习,许多框架隐藏了神经网络底层的实现,只能在上层调包使用,对其内部原理很难认识清晰,不利于进一步优化和创新。YOLOv4是最近推出的基于深度学习的端到端实时目标检测方法。YOLOv4的实现darknet是使用C语言开发的轻型开深度学习框架,依赖少,可移植性好,可以作为很好的代码阅读案例,让我们深入探究其实现原理。【课程内容与收获】 本课程将解析YOLOv4的实现原理和码,具体内容包括:- YOLOv4目标检测原理- 神经网络及darknet的C语言实现,尤其是反向传播的梯度求解和误差计算- 代码阅读工具及方法- 深度学习计算的利器:BLAS和GEMM- GPU的CUDA编程方法及在darknet的应用- YOLOv4的程序流程- YOLOv4各层及关键技术的码解析本课程将提供注释后的darknet码程序文件。【相关课程】 除本课程《YOLOv4目标检测:原理与码解析》外,本人推出了有关YOLOv4目标检测的系列课程,包括:《YOLOv4目标检测实战:训练自己的数据集》《YOLOv4-tiny目标检测实战:训练自己的数据集》《YOLOv4目标检测实战:人脸口罩佩戴检测》《YOLOv4目标检测实战:中国交通标志识别》建议先学习一门YOLOv4实战课程,对YOLOv4的使用方法了解以后再学习本课程。【YOLOv4网络模型架构图】 下图由白勇老师绘制  
### 回答1: MATLAB是一种功能强大的数学计算软件,支持深度学习和计算机视觉开发。而YOLOv4是一种在计算机视觉领域被广泛使用的目标检测算法,并且是当前性能最好的目标检测算法之一。而Darknet则是一个由YOLOv4作者开发的开深度学习框架,它支持YOLOv4等多种神经网络模型。 在MATLAB中,通过使用深度学习工具箱,可以轻松地训练和测试YOLOv4神经网络模型。此外,MATLAB还提供了很多辅助工具,如数据分析、图像预处理、图像增强等,这些工具可以帮助用户更好地处理图像数据,提高模型的精度和效率。此外,MATLAB还支持可视化工具,以便用户更直观地了解模型的性能和结果。 使用Darknet可以轻松地搭建YOLOv4神经网络模型,并进行训练和部署。Darknet提供了可视化工具来检查神经网络结构和各种层的输出,这有助于用户理解模型和以更好的方式进行训练和优化。此外,Darknet还提供了广泛的文档和示例代码,使用户可以逐步了解如何使用这个框架来进行深度学习开发。 总之,MATLAB和Darknet都是非常强大的工具,可以帮助用户轻松地开发和优化YOLOv4模型。通过结合这两个工具,可以实现更高效、准确和稳定的目标检测功能。 ### 回答2: Matlab YOLOv4 Darknet是基于深度学习的目标检测算法和工具,可用于图像分析和计算机视觉应用。该工具基于循环神经网络实现的YOLOv4模型和开神经网络框架Darknet,可以检测和识别多个物体类型,包括人、车、动物、建筑物等。与传统的目标检测算法相比,YOLOv4具有更高的准确性和速度,对于大规模数据和复杂场景的处理效果更优秀。 Matlab YOLOv4 Darknet不仅提供了基于预训练模型的目标检测功能,还支持用户自定义数据集和模型训练。用户可以通过提供图像数据集和标注信息,进行训练模型并优化模型参数,以满足特定应用场景的要求。此外,Matlab YOLOv4 Darknet还具备可视化功能,可以直观地展示神经网络的结构、模型训练和测试结果等。 总之,Matlab YOLOv4 Darknet是一种功能强大的目标检测工具,可以满足研究、开发和生产等不同领域的需求。它的应用范围包括智能交通、安防监控、医学影像等多个行业,对于提高数据处理和视觉分析的可靠性和效率有着重要作用。 ### 回答3: Yolov4是一种基于深度学习技术的目标检测算法,它能够快速且准确地识别图像中的不同物体,并对它们进行分类。同时,利用深度卷积神经网络的优点,Yolov4能够处理复杂的图像场景,完成精准的目标检测任务。 在实现Yolov4算法时,Matlab和Darknet是两个常用的工具。Matlab是一种面向科学计算的高级编程语言,拥有强大的图像处理和深度学习库,可以快速开发复杂的算法。Darknet则是一个轻量级的深度学习框架,适用于处理大量的图像数据,具有优秀的性能和高效的计算速度。 使用Matlab和Darknet来实现Yolov4算法能够带来多方面的好处。这两个工具都拥有许多可用的函数和库,可以快速构建和训练深度卷积神经网络。Matlab还可以通过可视化的方式来展示算法的结果和性能,帮助用户更好地理解和分析数据。而Darknet则具有高效的并行计算能力,可以处理大量的图像数据,适用于需要高效处理数据的场景。 总之,使用Matlab和Darknet来实现Yolov4算法是一种高效且准确的方式,可以支持大量的图像处理和深度学习任务,并能够在不同的场景中获得优秀的性能表现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值