欢迎关注我的公众号 [极智视界],回复001获取Google编程规范
O_o
>_<
o_O
O_o
~_~
o_O
В этой статье анализируется и вводится интерфейс даркнета parse_network_cfg, который является относительно сложным, в основном для загрузки структуры модели и реализации оператора.
1. Процедура загрузки данных даркнета
Предыдущая статья《[Искусство программирования] Анализ интерфейса read_data_cfg даркнета" представил процесс загрузки данных для обнаружения целей даркнета и представил реализацию загрузки .data и .names.
Затем интерфейс parse_network_cfg в основном используется для загрузки структуры модели .cfg, что включает в себя немного больше.
2. интерфейс parse_network_cfg
Давайте посмотрим на реализацию parse_network_cfg:
network parse_network_cfg(char *filename)
{
return parse_network_cfg_custom(filename, 0, 0);
}
Вы можете видеть, что внутри вызывается функция parse_network_cfg_custom, которая является основной функцией реализации функции, и это также ключевой момент для анализа здесь. Реализация этой функции занимает 451 строку, поэтому я не буду выкладывать ее здесь напрямую, а выделю несколько ключевых моментов и расскажу о них.
Сначала прочитайте cfg:
list *sections = read_cfg(filename);
Чтение cfg использует интерфейс read_cfg.Во-первых, нам нужно поговорить о структуре данных сетевой структуры в даркнете: вся сеть хранится в связанном списке.. Эти структуры очень понятны, если взглянуть на определение раздела, где type — это категория блока записи, то есть такие строки, как [net], [convolution] или [yolo].
typedef struct{
char *type;
list *options;
}section;
Взгляните на 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){ // 逐行读
++ nu;
strip(line);
switch(line[0]){ // 取每行的第一个字符
case '[': // 若是'[', 说明是一个块
current = (section*)xmalloc(sizeof(section)); // 用current section来存储这个块
list_insert(sections, current); // 将块插入网络结构链表
current->options = make_list(); // 块里面的链表存储块内结构
current->type = line; // 块类型即为[net]、[convolution]...
break; // 块存储完及新赋值type后即跳出
case '\0':
case '#':
case ';':
free(line);
break;
default:
if(!read_option(line, current->options)){ // 读块内结构,存储到块内链表
fprintf(stderr, "Config file error line %d, could parse: %s\n", nu, line);
free(line);
}
break;
}
}
fclose(file);
return sections; // 返回网络结构链表
}
Вышеприведенное завершает чтение сетевой структуры в хранилище связанного списка. Следующее, что нужно сделать, это преобразовать сетевую структуру в связанном списке в сетевую структуру данных. Сеть представляет собой структуру:
// 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;
...; // 有很多很多参
}
Постройте сеть:
network net = make_network(sections->size - 1);
На самом деле это просто разработка и инициализация памяти:
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;
}
Далее необходимо получить параметры конфигурации сети:
node *n = sections->front; // 指向网络链表头节点
section *s = (section *)n->val; // 获取头节点的值域setion
list *options = s->options; // 获取头节点的指针域
parse_net_options(options, &net); // 给net设置网络配置参数,[net]里的参数
Взгляните на parse_net_options:
/// 太多了,截取了部分
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
...;
}
На самом деле вы получаете следующее:
Далее будет загружена структура сети.Сначала сместим головной узел на один узел, то есть на операторный узел:
n = n->next;
Тогда более критично:
/// 这里省略了很多层实现,不然篇幅太长
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); // 将层type提取出来,转换为枚举类型的TYPE
if(lt == CONVOLUTIONAL){ // 开始搭积木了
l = parse_convolutional(options, params); // 添加卷积层
}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;}
...;
}
Реализация в даркнете каждого оператора в приведенных выше строительных блоках является сутью make_layer_xxx, которая не будет здесь описываться из-за нехватки места.В будущем будут соответствующие вводные статьи.
Путешествие в тысячу миль начинается с одного шага, а чтение исходного кода — хорошая привычка.
【Передача по общему номеру】 "[Искусство программирования] Анализ интерфейса даркнета parse_network_cfg》