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>