darknet源码剖析(七)

至此load_data_detection的所有功能就都分析完了,总结一下就是获取16幅图片的地址,进行随机变换,并读取对应图片的标注。

再次回到load_threads函数中,load_threads共创建64个进程,每个进程加载16幅图像数据。

    for(i = 0; i < args.threads; ++i){
        pthread_join(threads[i], 0);
    }
    *out = concat_datas(buffers, args.threads);
    out->shallow = 0;
    for(i = 0; i < args.threads; ++i){
        buffers[i].shallow = 1;
        free_data(buffers[i]);
    }
    free(buffers);
    free(threads);
    return 0;

此处调用pthread_join等待数据加载完成。

concat_datas的作用是将加载的数据整合到一起。最后释放资源。

重新回到train_detector中。

    pthread_t load_thread = load_data(args);
    double time;
    int count = 0;
    while(get_current_batch(net) < net->max_batches){

首先是net->max_batches,根据yolov3_voc.cfg,max_batches = 50200。进入get_current_batch

size_t get_current_batch(network *net)
{
    size_t batch_num = (*net->seen)/(net->batch*net->subdivisions);
    return batch_num;
}

net->seen是多少没找到,应该是在load_network时设置的,这个函数之后在详细分析。此处只需要知道batch_num是一个数字,根据函数名称推测是当前的batch号,通过< net->max_batches也可以验证这一推测。

        if(l.random && count++%10 == 0){
            printf("Resizing\n");
            int dim = (rand() % 10 + 10) * 32;
            if (get_current_batch(net)+200 > net->max_batches) dim = 608;
            //int dim = (rand() % 4 + 16) * 32;
            printf("%d\n", dim);
            args.w = dim;
            args.h = dim;

            pthread_join(load_thread, 0);
            train = buffer;
            free_data(train);
            load_thread = load_data(args);

            #pragma omp parallel for
            for(i = 0; i < ngpus; ++i){
                resize_network(nets[i], dim, dim);
            }
            net = nets[0];
        }

若l.random为1,同时count为10的整倍数,则进入该代码块。根据yolov3_voc.cfg的配置,random为1,初次进入时count为0,因此该代码块进入。

若当前batch号加200大于net->max_batches,则dim设为608,否则使用随机生成的dim。上述if语句说明最后200个batch使用608的图像size,由于只有10的整倍数才进入该if语句,因此共有20个batch使用608的图像size。

pthread_join等待load_thread完成加载数据。

由于图片的size设置为dim,因此将buffer中已经加载的数据释放,并重新调用load_data加载数据。

同时resize_network配置。此处采用openmp编译注释的模式,并行执行resize_network。resize_network在network.c,resize_convolutional_layer在convolutional_layer.c文件中。

        time=what_time_is_it_now();
        pthread_join(load_thread, 0);
        train = buffer;
        load_thread = load_data(args);

若未进入if语句,则等待load_thread完成数据加载,再次load_data应该是为下一个batch准备数据。

        printf("Loaded: %lf seconds\n", what_time_is_it_now()-time);

        time=what_time_is_it_now();
        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

此处开始正式进入网络训练,由于我定义了GPU,同时GPU数量为1,因此进入train_network。train_network位于network.c文件中。

float train_network(network *net, data d)
{
    assert(d.X.rows % net->batch == 0);
    int batch = net->batch;
    int n = d.X.rows / batch;

    int i;
    float sum = 0;
    for(i = 0; i < n; ++i){
        get_next_batch(d, batch, i*batch, net->input, net->truth);
        float err = train_network_datum(net);
        sum += err;
    }
    return (float)sum/(n*batch);
}

batch根据yolov3_voc.cfg的配置为64,d.X.rows为64,因此n==1,同时data d中有64幅训练图片。首先进入get_next_batch,

void get_next_batch(data d, int n, int offset, float *X, float *y)
{
    int j;
    for(j = 0; j < n; ++j){
        int index = offset + j;
        memcpy(X+j*d.X.cols, d.X.vals[index], d.X.cols*sizeof(float));
        if(y) memcpy(y+j*d.y.cols, d.y.vals[index], d.y.cols*sizeof(float));
    }
}

上述代码的作用是将data d中的训练数据与标签拷贝至net->input,net->truth,至此网络与数据准备完成。调用train_network_datum开始训练。train_network_datum同样位于network.c文件中。

float train_network_datum(network *net)
{
    *net->seen += net->batch;
    net->train = 1;
    forward_network(net);
    backward_network(net);
    float error = *net->cost;
    if(((*net->seen)/net->batch)%net->subdivisions == 0) update_network(net);
    return error;
}

 

你可能感兴趣的:(darknet源码剖析)