[programming art] analysis of darknet parse_network_cfg interface

Keywords: C Algorithm linked list Deep Learning source code analysis

Welcome to my official account, reply to 001 Google programming specification.

  O_o   >_<   o_O   O_o   ~_~   o_O

  this paper analyzes and introduces darknet parse_network_cfg interface, which is a hard core, is mainly used to load the model structure and implement the operator.

1. darknet data loading routine

    Previous articles< [programming art] analysis of darknet read_data_cfg interface >The data loading process of darknet target detection has been introduced, and the loading implementation of. Data and. names has been introduced.

    Next, here's parse_ network_ The cfg interface is mainly used to load the. cfg model structure, which involves a little more.

2,parse_network_cfg interface

     Take a look at parse_network_cfg implementation:

network parse_network_cfg(char *filename)
{
    return parse_network_cfg_custom(filename, 0, 0);
}

    You can see that parse is called inside_ network_ cfg_ Custom function, which is the main function implementation function, is also the focus to be analyzed here. The implementation of this function has 451 lines. I won't post it directly here. I'll pick some key places and say them.

  first read cfg:

list *sections = read_cfg(filename);

    Read cfg_ cfg interface. First, we need to talk about the data structure of the network structure stored by linked list in darknet: the whole network is stored by linked list. The value range of the linked list is section block, and the section block stores [net], [revolution], [yolo]... These structures. It is clear to see the definition of section, in which type is the category of record block, i.e. [net] or [revolution] or [yolo] Wait for a string.

typedef struct{
    char *type;
    list *options;
}section;

  take a look at read_cfg:

list *read_cfg(char *filename)
{
    FILE *file = fopen(filename, "r");
    if(file == 0) file_error(filename);
    char *line;
    int nu = 0;
    list *sections = make_list();
    section *current = 0;
    while((line=fgetl(file)) != 0){                             // Read line by line
        ++ nu;
        strip(line);
        switch(line[0]){                                        // Take the first character of each line
            case '[':                                           // If '[', it means a block
                current = (section*)xmalloc(sizeof(section));   // Use the current section to store this block
                list_insert(sections, current);                 // Insert block into network structure linked list
                current->options = make_list();                 // The linked list in the block stores the structure in the block
                current->type = line;                           // Block types are [net], [revolution]
                break;                                          // After the block is stored and the new type is assigned, it will jump out
            case '\0':
            case '#':
            case ';':
                free(line);
                break;
            default:                
                if(!read_option(line, current->options)){        // Read the intra block structure and store it in the intra block linked list
                    fprintf(stderr, "Config file error line %d, could parse: %s\n", nu, line);
                    free(line);
                }
                break;
        }
    }
    fclose(file);
    return sections;                                            // Return network structure linked list
}

  after reading the network structure into the linked list storage, the next thing to do is to convert the network structure in the linked list into a network data structure. Network is a structure:

// network.h
typedef struct network {
    int n;
    int batch;
    uint64_t *seen;
    float *badlabels_reject_threshold;
    float *delta_rolling_max;
    float *delta_rolling_avg;
    float *delta_rolling_std;
    int weights_reject_freq;
    int equidistant_point;
    ...;                       // There are many, many participants
}

  build the following network:

network net = make_network(sections->size - 1);

  in fact, we only did some memory development and initialization:

network make_network(int n)
{
    network net = {0};
    net.n = n;
    net.layers = (layer*)xcalloc(net.n, sizeof(layer));
    net.seen = (uint64_t*)xcalloc(1, sizeof(uint64_t));
    net.cuda_graph_ready = (int*)xcalloc(1, sizeof(int));
    net.badlabels_reject_threshold = (float*)xcalloc(1, sizeof(float));
    net.delta_rolling_max = (float*)xcalloc(1, sizeof(float));
    net.delta_rolling_avg = (float*)xcalloc(1, sizeof(float));
    net.delta_rolling_std = (float*)xcalloc(1, sizeof(float));
    net.cur_iteration = (int*)xcalloc(1, sizeof(int));
    net.total_bbox = (int*)xcalloc(1, sizeof(int));
    net.rewritten_bbox = (int*)xcalloc(1, sizeof(int));
    *net.rewritten_bbox = *net.total_bbox = 0;
#ifdef GPU
    net.input_gpu = (float**)xcalloc(1, sizeof(float*));
    net.truth_gpu = (float**)xcalloc(1, sizeof(float*));

    net.input16_gpu = (float**)xcalloc(1, sizeof(float*));
    net.output16_gpu = (float**)xcalloc(1, sizeof(float*));
    net.max_input16_size = (size_t*)xcalloc(1, sizeof(size_t));
    net.max_output16_size = (size_t*)xcalloc(1, sizeof(size_t));
#endif
    return net;
}

     Next, get the network configuration parameters:

node *n = sections->front;                   // Point to network chain header node
section *s = (section *)n->val;              // Gets the value field setion of the header node
list *options = s->options;                  // Gets the pointer field of the header node

parse_net_options(options, &net);            // Set the network configuration parameters for [net]

  take a look at parse_net_options:

///Too many, intercepted part
void parse_net_options(list *options, network *net)
{
    net->max_batches = option_find_int(options, "max_batches", 0);
    net->batch = option_find_int(options, "batch",1);
    net->learning_rate = option_find_float(options, "learning_rate", .001);
    net->learning_rate_min = option_find_float_quiet(options, "learning_rate_min", .00001);
    net->batches_per_cycle = option_find_int_quiet(options, "sgdr_cycle", net->max_batches);
    net->batches_cycle_mult = option_find_int_quiet(options, "sgdr_mult", 2);
    net->momentum = option_find_float(options, "momentum", .9);
    net->decay = option_find_float(options, "decay", .0001);
    int subdivs = option_find_int(options, "subdivisions",1);
    net->time_steps = option_find_int_quiet(options, "time_steps",1);
    net->track = option_find_int_quiet(options, "track", 0);
    net->augment_speed = option_find_int_quiet(options, "augment_speed", 2);
    net->init_sequential_subdivisions = net->sequential_subdivisions = option_find_int_quiet(options, "sequential_subdivisions", subdivs);
    if (net->sequential_subdivisions > subdivs) net->init_sequential_subdivisions = net->sequential_subdivisions = subdivs;
    net->try_fix_nan = option_find_int_quiet(options, "try_fix_nan", 0);
    net->batch /= subdivs;          // mini_batch
    const int mini_batch = net->batch;
    net->batch *= net->time_steps;  // mini_batch * time_steps
    net->subdivisions = subdivs;    // number of mini_batches
    ...;
}

     In fact, the following things are obtained:

    Next, the network structure will be loaded. First, offset the head node back by one node, that is, to the operator node:

n = n->next;

    Then there are the key:

///Many layers of implementation are omitted here, otherwise the length is too long
while(n){
    params.train = old_params_train;
    if (count < last_stop_backward) params.train = 0;

    params.index = count;
    fprintf(stderr, "%4d ", count);
    s = (section *)n->val;                                  
    options = s->options;
    layer l = { (LAYER_TYPE)0 };
    LAYER_TYPE lt = string_to_layer_type(s->type);          // Extract the layer type and convert it to the type of enumeration type
    if(lt == CONVOLUTIONAL){                                // Start building blocks
        l = parse_convolutional(options, params);           // Add volume layer
    }else if(lt == LOCAL){
        l = parse_local(options, params);
    }else if(lt == ACTIVE){
        l = parse_activation(options, params);
    }else if(lt == RNN){
        l = parse_rnn(options, params);
    }else if(lt == GRU){
        l = parse_gru(options, params);
    }else if(lt == LSTM){
        l = parse_lstm(options, params);
    }else if (lt == CONV_LSTM) {
        l = parse_conv_lstm(options, params);
    }else if (lt == HISTORY) {
        l = parse_history(options, params);
    }else if(lt == CRNN){
        l = parse_crnn(options, params);
    }else if(lt == CONNECTED){
        l = parse_connected(options, params);
    }else if(lt == CROP){
        l = parse_crop(options, params);
    }else if(lt == COST){
        l = parse_cost(options, params);
        l.keep_delta_gpu = 1;
    }else if(lt == REGION){
        l = parse_region(options, params);
        l.keep_delta_gpu = 1;
    }else if (lt == YOLO) {
        l = parse_yolo(options, params);
        l.keep_delta_gpu = 1;}
    ...;
}

    The make_layer_xxx of each operator corresponding to the darknet in each building block is the essence. Here is limited to the length of space.

  a journey of a thousand miles begins with a single step. Reading the source code is a good habit.

[official account transmission]
<[programming art] analysis of darknet parse_network_cfg interface>

Posted by l0ve2hat3 on Wed, 03 Nov 2021 00:08:42 -0700