基于FPGA的GoogleNet加速器-Local Response Normalization

版权声明:本文为博主原创文章,欢迎转载,但请注明出处 https://blog.csdn.net/lulugay/article/details/53492866
局部归一化据说是没啥用,不过既然GNet里面有那还是要写的。
该层需要参数有:
norm_region: 选择对相邻通道间归一化还是通道内空间区域归一化,默认为ACROSS_CHANNELS,即通道间归一化;
local_size:两种表示(1)通道间归一化时表示求和的通道数;(2)通道内归一化时表示求和区间的边长;默认值为5;
alpha:缩放因子(详细见后面),默认值为1;
beta:指数项(详细见后面), 默认值为5;
在通道间归一化模式中,局部区域范围在相邻通道间,但没有空间扩展(即尺寸为 local_size x 1 x 1);
在通道内归一化模式中,局部区域在空间上扩展,但只针对独立通道进行(即尺寸为 1 x local_size x local_size);
每个输入值都除以
caffe的lrn.cpp文件中有将近200行代码,但是实际上我们用到的只有CrossChannelForward_cpu()这一个函数而已。Forward
意思是向前传播,也就是说还有backward反向传播,是用来算梯度的,我们现在用不上。
在这里将caffe的代码贴出来,一步一步拆解
  1.   const vector<Blob<Dtype>*>& bottom, vector<Blob<Dtype>*>* top) {  
  2.   const Dtype* bottom_data = bottom[0]->cpu_data();  
  3.   Dtype* top_data = (*top)[0]->mutable_cpu_data();  
  4.   Dtype* scale_data = scale_.mutable_cpu_data();//用指针获取每个Blob对象的内存地址,便于后面操作  
  5.   // start with the constant value  
  6.   for (int i = 0; i < scale_.count(); ++i) {//初始化值为1.0  
  7.     scale_data[i] = 1.;  
  8.   } 
虽然没学过C++,但是我们已经知道了这层的功能,所以大概猜出来这段代码是干什么的并不难。bottom是输入,top是输出,scale用来存放中间数据
大小在这里找不出来,但是根据后面的代码可以算出来scale的大小就是整个输入的大小,初始化为1。下面紫色的立方体就代表输入,画出来的是长6宽6高5

图1
  1.   Blob<Dtype> padded_square(1, channels_ + size_ - 1, height_, width_);//补零后的Blob,第三维尺寸比bottom大了size_ - 1;  
  2.   Dtype* padded_square_data = padded_square.mutable_cpu_data();  
  3.   caffe_set(padded_square.count(), Dtype(0), padded_square_data);//先清零  
padded_square 用来存储平方后的数据,高为 channels_ + size_ - 1,跟卷积时候添加的pad类似要上下各延伸一部分, 下图那些透明的部分始终为0,存放数据时候直接跳过。 最前面的1是长宽高以外的第四维度,我并不知道他是做什么的,但是并不影响,我们继续看。
  1.  Dtype alpha_over_size = alpha_ / size_;//预先计算公式中的alpha/n  
  2. // go through the images  
  3.   for (int n = 0; n < num_; ++n) {//bottom的第四维尺寸num_,需要分解为单个来做归一化 
  4. // compute the padded square  
  5.     caffe_sqr(channels_ * height_ * width_,  
  6.         bottom_data + bottom[0]->offset(n),  
  7.         padded_square_data + padded_square.offset(0, pre_pad_));//计算bottom的平方,放入padded_square矩阵中,前pre_pad_个位置依旧0  
for循环不用看,因为在这里num=1。难点在于理解caffe_sqr:在caffe下面的math_function.cpp文件里我们可以找到caffe_sqr的定义
caffe_sqr(int len,float* a,float* b) ,意思就是b[i]=a[i]*a[i],执行len次。关于offset(n),可以回想在卷积层我们每次执行计算都要找到现在计算的点
是所有输入中的第几个,在这里offset就是起到这个作用。offset(int num,int channel,int hight,int width)输入当前所在位置就可以得到当前算的点是第几个,放进什么地方。C++中如果传入参数数目可以小于声明时所定义的数目时取默认值。
那么这一段的意思就很好理解了,输入的每一个数都平方放进padded_Square中对应位置,比如in[0]放进padded_square第pad层的第一个位置,因为上下各延伸了pad层。在这里就是图1算完放进图2紫色的部分。

  1. for (int c = 0; c < size_; ++c) {//对n个通道平方求和并乘以预先算好的(alpha/n),累加至scale_中(实现计算 1 + sum_under_i(x_i^2))  
  2.       caffe_axpy<Dtype>(height_ * width_, alpha_over_size,  
  3.           padded_square_data + padded_square.offset(0, c),  
  4.           scale_data + scale_.offset(n, 0));  
  5.     } 
找到caffe_axpy()的定义:caffe_axpy(int len,float alpha,float* x,float* y){for(int i=0;i<len;i++)y[i]=alpha*x+y;}
在这里,len= height * width,意思就是每调用一次caffe_axpy这个函数算一层。所以下面以层为单位讲述,scale(0)表示
当c=0时,这段函数执行的是将scale(0)=alpha_over_size*padded_square(0),for循环执行完scale(0)=1+ alpha_over_size*[padded_square(0)+ padded_square(1)+ padded_square(2)+ padded_square(3)+ padded_square(4)]如图所示。到这儿你可能就看明白为什么scale要全部赋值为1了
到这里,除了最后的指数项已经全部完成了。但是这只算完了scale(0),还剩下channel-1个没算呢

图3

  1.     for (int c = 1; c < channels_; ++c) {//这里使用了类似FIFO的形式计算其余scale_参数,每次向后移动一个单位,加头去尾,避免重复计算求和  
  2.       // copy previous scale  
  3.       caffe_copy<Dtype>(height_ * width_,  
  4.           scale_data + scale_.offset(n, c - 1),  
  5.           scale_data + scale_.offset(n, c));  
  6.       // add head  
  7.       caffe_axpy<Dtype>(height_ * width_, alpha_over_size,  
  8.           padded_square_data + padded_square.offset(0, c + size_ - 1),  
  9.           scale_data + scale_.offset(n, c));  
  10.       // subtract tail  
  11.       caffe_axpy<Dtype>(height_ * width_, -alpha_over_size,  
  12.           padded_square_data + padded_square.offset(0, c - 1),  
  13.           scale_data + scale_.offset(n, c));  
  14.     }  
  15.   }  
现在开始计算后面那些层:
首先scale(1)=scale(0)= scale(0)=1+ alpha_over_size*[padded_square(0)+ padded_square(1)+ padded_square(2)+ padded_square(3)+ padded_square(4)]
然后scale(1)=scale(1)+pad(5)= 1+ alpha_over_size*[pad(0)+ pad(1)+ pad(2)+ pad(3)+ pad(4)+pad(5)]
如图4所展示

图4
最后要把scale(1)多算的那一层pad(0)给去掉,scale(1)= 1+ alpha_over_size*[ pad(1)+ pad(2)+ pad(3)+ pad(4)+pad(5)]
------------------------------------------------------------------------------------------------------------------------------------------
  1. caffe_powx<Dtype>(scale_.count(), scale_data, -beta_, top_data);//计算求指数,将除法转换为乘法,故指数变负  
  2.   caffe_mul<Dtype>(scale_.count(), top_data, bottom_data, top_data);//bottom .* scale_-> top最后输出结果


猜你喜欢

转载自blog.csdn.net/lulugay/article/details/53492866