文章全部YOLOv2源码分析
接着上一讲没有讲完的make_convolutional_layer
函数
#0x01 make_convolutional_layer
//make_convolutional_layer
l.forward = forward_convolutional_layer;
l.backward = backward_convolutional_layer;
l.update = update_convolutional_layer;
上来就是三坐大山_,我们先从第一个forward_convolutional_layer
开始。
0x0101 forward_convolutional_layer
void forward_convolutional_layer(convolutional_layer l, network net)
{
//传入卷积层参数和网络的总参数
int i, j;
fill_cpu(l.outputs*l.batch, 0, l.output, 1);
看这个fill_cpu
函数
void fill_cpu(int N, float ALPHA, float *X, int INCX)
{
int i;
for(i = 0; i < N; ++i) X[i*INCX] = ALPHA;
}
输入的参数N
表示一个batch
中所有的图像元素个数,x
指向n
对应大小分配的内存空间。整个函数来看就是对输出图像元素的一个初始化操作。
接着看后面
//forward_convolutional_layer
if(l.xnor){
binarize_weights(l.weights, l.n, l.c/l.groups*l.size*l.size, l.binary_weights);
swap_binary(&l);
binarize_cpu(net.input, l.c*l.h*l.w*l.batch, l.binary_input);
net.input = l.binary_input;
}
判断是否二值化操作,如果是的话,其中有两个关键的函数binarize_weights
和binarize_cpu
void binarize_weights(float *weights, int n, int size, float *binary)
{
int i, f;
for(f = 0; f < n; ++f){
float mean = 0;
for(i = 0; i < size; ++i){
mean += fabs(weights[f*size + i]);
}
mean = mean / size;
for(i = 0; i < size; ++i){
binary[f*size + i] = (weights[f*size + i] > 0) ? mean : -mean;
}
}
}
第一个参数就是指向分配给weight
内存空间 的指针,第二参数是卷积核个数,第三个参数是一个卷积核weight
的个数(这里应该使用l.nweights
/l.n
),第四个参数是指向分配给二值化weight
内存空间 的指针。举个例子
假设有两个2x2卷积核
n=2 size=4
权重值总共8个 1 2 3 4 5 6 7 8
第一次循环 f=0
mean = 1+2+3+4 = 10
mean/4 = 2.5
binary[0]=2.5 binary[1]=2.5 binary[2]=2.5 binary[3]=2.5
第二次循环 f=1
mean = 5+6+7+8 = 26
mean/4 = 6.5
binary[0]=6.5 binary[1]=6.5 binary[2]=6.5 binary[3]=6.5
接着看后面的swap_binary
函数
void swap_binary(convolutional_layer *l)
{
float *swap = l->weights;
l->weights = l->binary_weights;
l->binary_weights = swap;
#ifdef GPU
swap = l->weights_gpu;
l->weights_gpu = l->binary_weights_gpu;
l->binary_weights_gpu = swap;
#endif
}
函数的作用很明显了,就要把以前的权重值替换二值化后的
接着binarize_cpu
函数
void binarize_cpu(float *input, int n, float *binary)
{
int i;
for(i = 0; i < n; ++i){
binary[i] = (input[i] > 0) ? 1 : -1;
}
}
函数的第一个参数指向输入图像内存空间的指针,函数第二个参数表示一个batch的图像元素个数,函数第三个参数指向分配给二值化input
内存空间 的指针。
函数很简单,总体来看函数的作用就是出入图像的二值化。
最后将得到的二值化输入图像赋值给原来的输入图像。
我们接着回到forward_convolutional_layer
函数
//forward_convolutional_layer
int m = l.n/l.groups;//一个group的卷积核个数
int k = l.size*l.size*l.c/l.groups;//一个group的卷积核元素个数
int n = l.out_w*l.out_h;//一个输出图像的元素个数
for(i = 0; i < l.batch; ++i){
for(j = 0; j < l.groups; ++j){
float *a = l.weights + j*l.nweights/l.groups;
float *b = net.workspace;
float *c = l.output + (i*l.groups + j)*n*m;
im2col_cpu(net.input + (i*l.groups + j)*l.c/l.groups*l.h*l.w,
l.c/l.groups, l.h, l.w, l.size, l.stride, l.pad, b);
gemm(0,0,m,n,k,1,a,k,b,n,1,c,n);
}
}
这里有两个非常重要的函数im2col_cpu
和gemm
。先看第一个
0x0102 im2col_cpu && gemm
float im2col_get_pixel(float *im, int height, int width, int channels,
int row, int col, int channel, int pad)
{
row -= pad;
col -= pad;
if (row < 0 || col < 0 ||
row >= height || col >= width) return 0;
return im[col + width*(row + height*channel)];
}
//From Berkeley Vision's Caffe!
//https://github.com/BVLC/caffe/blob/master/LICENSE
void im2col_cpu(float* data_im