YOLOv2源码分析(三)

本文详细分析了YOLOv2中forward_convolutional_layer函数,包括输入参数、二值化操作及其相关函数。接着探讨了im2col_cpu和gemm函数如何将卷积转换为矩阵运算,简化计算过程。此外,还解析了forward_batchnorm_layer函数,解释了批量归一化层的实现细节,包括计算均值、方差和规范化处理。
摘要由CSDN通过智能技术生成

文章全部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_weightsbinarize_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_cpugemm。先看第一个

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
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值