至此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;
}