darknet源码中的cfg文件读取完之后还要解析,解析由函数parse_network_cfg(cfgfile)中的parse_xxx系列函数搞定。
主要流程为:
1.make_network:用于构建一个新网络
2.parse_net_options:解析[net]层的各个参数以及操作
3.parse_xxx(如:parse_convolutional):解析cfg中的各个 [xxx](如:[convolutional])的各个参数以及操作。
1.src/network.c/make_network(int n)
上述代码中的network就是darknet框架中的网络的数据结构,详细请参看darknet网络核心结构体。
/*
** 新建一个空网络,并为网络中部分指针参数动态分配内存
** 输入: n 神经网络层数
** 说明: 该函数只为网络的三个指针参数动态分配了内存,并没有为所有指针参数分配内存
*/
network make_network(int n)
{
network net = {0};
net.n = n;
// 为每一层分配内存
net.layers = calloc(net.n, sizeof(layer));
net.seen = calloc(1, sizeof(int));
net.cost = calloc(1, sizeof(float));
return net;
}
2.src/parser.c/parse_net_options(list *options, network *net)
void parse_net_options(list *options, network *net)
{
// 从.cfg网络参数配置文件中读入一些通用的网络配置参数,option_find_int()以及option_find_float()函数的第三个参数都是默认值(如果配置文件中没有设置该参数的值,就取默认值)
// 稍微提一下batch这个参数,首先读入的net->batch是真实batch值,即每个batch中包含的照片张数,而后又读入一个subdivisions参数,这个参数暂时还没搞懂有什么用,
// 总之最终的net->batch = net->batch / net->subdivisions
net->batch = option_find_int(options, "batch",1);
net->learning_rate = option_find_float(options, "learning_rate", .001);
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->notruth = option_find_int_quiet(options, "notruth",0);
net->batch /= subdivs;
net->batch *= net->time_steps;
net->subdivisions = subdivs;
net->adam = option_find_int_quiet(options, "adam", 0);
if(net->adam){
net->B1 = option_find_float(options, "B1", .9);
net->B2 = option_find_float(options, "B2", .999);
net->eps = option_find_float(options, "eps", .00000001);
}
net->h = option_find_int_quiet(options, "height",0);
net->w = option_find_int_quiet(options, "width",0);
net->c = option_find_int_quiet(options, "channels",0);
// 一张输入图片的元素个数,如果网络配置文件没有指定,则默认值为net->h * net->w * net->c
net->inputs = option_find_int_quiet(options, "inputs", net->h * net->w * net->c);
net->max_crop = option_find_int_quiet(options, "max_crop",net->w*2);
net->min_crop = option_find_int_quiet(options, "min_crop",net->w);
net->center = option_find_int_quiet(options, "center",0);
net->angle = option_find_float_quiet(options, "angle", 0);
net->aspect = option_find_float_quiet(options, "aspect", 1);
net->saturation = option_find_float_quiet(options, "saturation", 1);
net->exposure = option_find_float_quiet(options, "exposure", 1);
net->hue = option_find_float_quiet(options, "hue", 0);
if(!net->inputs && !(net->h && net->w && net->c)) error("No input parameters supplied");
char *policy_s = option_find_str(options, "policy", "constant");
net->policy = get_policy(policy_s);
net->burn_in = option_find_int_quiet(options, "burn_in", 0);
net->power = option_find_float_quiet(options, "power", 4);
if(net->policy == STEP){
net->step = option_find_int(options, "step", 1);
net->scale = option_find_float(options, "scale", 1);
} else if (net->policy == STEPS){
char *l = option_find(options, "steps");
char *p = option_find(options, "scales");
if(!l || !p) error("STEPS policy must have steps and scales in cfg file");
int len = strlen(l);
int n = 1;
int i;
for(i = 0; i < len; ++i){
if (l[i] == ',') ++n;
}
int *steps = calloc(n, sizeof(int));
float *scales = calloc(n, sizeof(float));
for(i = 0; i < n; ++i){
int step = atoi(l);
float scale = atof(p);
l = strchr(l, ',')+1;
p = strchr(p, ',')+1;
steps[i] = step;
scales[i] = scale;
}
net->scales = scales;
net->steps = steps;
net->num_steps = n;
} else if (net->policy == EXP){
net->gamma = option_find_float(options, "gamma", 1);
} else if (net->policy == SIG){
net->gamma = option_find_float(options, "gamma", 1);
net->step = option_find_int(options, "step", 1);
} else if (net->policy == POLY || net->policy == RANDOM){
}
net->max_batches = option_find_int(options, "max_batches", 0);
}
3.src/parser.c/parse_convolutional(list *options, size_params params)
convolutional_layer parse_convolutional(list *options, size_params params)
{
// 获取卷积核个数,若配置文件中没有指定,则设为1
int n = option_find_int(options, "filters",1);
// 获取卷积核尺寸,若配置文件中没有指定,则设为1
int size = option_find_int(options, "size",1);
// 获取跨度,若配置文件中没有指定,则设为1
int stride = option_find_int(options, "stride",1);
// 是否在输入图像四周补0,若需要补0,值为1;若配置文件中没有指定,则设为0,不补0
int pad = option_find_int_quiet(options, "pad",0);
// 四周补0的长读,下面这句代码多余,有if(pad)这句就够了
int padding = option_find_int_quiet(options, "padding",0);
if(pad) padding = size/2; // 如若需要补0,补0长度为卷积核一半长度(往下取整),这对应same补0策略
// 获取该层使用的激活函数类型,若配置文件中没有指定,则使用logistic激活函数
char *activation_s = option_find_str(options, "activation", "logistic");
ACTIVATION activation = get_activation(activation_s);
// h,w,c为上一层的输出的高度/宽度/通道数(第一层的则是输入的图片的尺寸与通道数,也即net.h,net.w,net.c),batch所有层都一样(不变),
// params.h,params.w,params.c及params.inputs在构建每一层之后都会更新为上一层相应的输出参数(参见parse_network_cfg())
int batch,h,w,c;
h = params.h;
w = params.w;
c = params.c;
batch=params.batch;
// 如果这三个数存在0值,那肯定有问题了,因为上一层(或者输入)必须不为0
if(!(h && w && c)) error("Layer before convolutional layer must output image.");
// 是否进行规范化,1表示进行规范化,若配置文件中没有指定,则设为0,即默认不进行规范化
int batch_normalize = option_find_int_quiet(options, "batch_normalize", 0);
// 是否对权重进行二值化,1表示进行二值化,若配置文件中没有指定,则设为0,即默认不进行二值化
int binary = option_find_int_quiet(options, "binary", 0);
// 是否对权重以及输入进行二值化,1表示是,若配置文件中没有指定,则设为0,即默认不进行二值化
int xnor = option_find_int_quiet(options, "xnor", 0);
//以上已经获取到了构建一层卷积层的所有参数,现在可以用这些参数构建卷积层了
convolutional_layer layer = make_convolutional_layer(batch,h,w,c,n,size,stride,padding,activation, batch_normalize, binary, xnor, params.net.adam);
layer.flipped = option_find_int_quiet(options, "flipped", 0);
layer.dot = option_find_float_quiet(options, "dot", 0);
if(params.net.adam){
layer.B1 = params.net.B1;
layer.B2 = params.net.B2;
layer.eps = params.net.eps;
}
return layer;
}