caffe lrn 反向传播框架中 LRN层有什么作用,改变各个参数会有怎么的效果,求

所有的层都具有的参数,如name, type, bottom, top和transform_param请参看我的前一篇文章:
本文只讲解视觉层(Vision Layers)的参数,视觉层包括Convolution, Pooling,&Local Response Normalization (LRN), im2col等层。
1、Convolution层:
就是卷积层,是卷积神经网络(CNN)的核心层。
层类型:Convolution
  lr_mult: 学习率的系数,最终的学习率是这个数乘以solver.prototxt配置文件中的base_lr。如果有两个lr_mult, 则第一个表示权值的学习率,第二个表示偏置项的学习率。一般偏置项的学习率是权值学习率的两倍。
在后面的convolution_param中,我们可以设定卷积层的特有参数。
必须设置的参数:
&   num_output: 卷积核(filter)的个数
&   kernel_size: 卷积核的大小。如果卷积核的长和宽不等,需要用kernel_h和kernel_w分别设定
其它参数:
&   &stride: 卷积核的步长,默认为1。也可以用stride_h和stride_w来设置。
&   &pad: 扩充边缘,默认为0,不扩充。 扩充的时候是左右、上下对称的,比如卷积核的大小为5*5,那么pad设置为2,则四个边缘都扩充2个像素,即宽度和高度都扩充了4个像素,这样卷积运算之后的特征图就不会变小。也可以通过pad_h和pad_w来分别设定。
&& &  weight_filler: 权值初始化。 默认为&constant",值全为0,很多时候我们用"xavier"算法来进行初始化,也可以设置为&gaussian"
& &   bias_filler: 偏置项的初始化。一般设置为"constant",值全为0。
& &   bias_term: 是否开启偏置项,默认为true, 开启
& &   group: 分组,默认为1组。如果大于1,我们限制卷积的连接操作在一个子集内。如果我们根据图像的通道来分组,那么第i个输出分组只能与第i个输入分组进行连接。
输入:n*c0*w0*h0
输出:n*c1*w1*h1
其中,c1就是参数中的num_output,生成的特征图个数
&w1=(w0+2*pad-kernel_size)/stride+1;
&h1=(h0+2*pad-kernel_size)/stride+1;
如果设置stride为1,前后两次卷积部分存在重叠。如果设置pad=(kernel_size-1)/2,则运算后,宽度和高度不变。
name: "conv1"
type: "Convolution"
bottom: "data"
top: "conv1"
lr_mult: 1
lr_mult: 2
convolution_param {
num_output: 20
kernel_size: 5
weight_filler {
type: "xavier"
bias_filler {
type: "constant"
& 2、Pooling层
也叫池化层,为了减少运算量和数据维度而设置的一种层。
层类型:Pooling
必须设置的参数:
&   &kernel_size: 池化的核大小。也可以用kernel_h和kernel_w分别设定。
其它参数:
&& pool: 池化方法,默认为MAX。目前可用的方法有MAX, AVE, 或STOCHASTIC
  pad: 和卷积层的pad的一样,进行边缘扩充。默认为0
  stride: 池化的步长,默认为1。一般我们设置为2,即不重叠。也可以用stride_h和stride_w来设置。
name: "pool1"
type: "Pooling"
bottom: "conv1"
top: "pool1"
pooling_param {
kernel_size: 3
pooling层的运算方法基本是和卷积层是一样的。
输入:n*c*w0*h0
输出:n*c*w1*h1
和卷积层的区别就是其中的c保持不变
&w1=(w0+2*pad-kernel_size)/stride+1;
&h1=(h0+2*pad-kernel_size)/stride+1;
如果设置stride为2,前后两次卷积部分不重叠。100*100的特征图池化后,变成50*50.
3、Local Response Normalization (LRN)层
此层是对一个输入的局部区域进行归一化,达到&侧抑制&的效果。可去搜索AlexNet或GoogLenet,里面就用到了这个功能
&层类型:LRN
参数:全部为可选,没有必须
  local_size: 默认为5。如果是跨通道LRN,则表示求和的通道数;如果是在通道内LRN,则表示求和的正方形区域长度。
  alpha: 默认为1,归一化公式中的参数。
  beta: 默认为5,归一化公式中的参数。
  norm_region: 默认为ACROSS_CHANNELS。有两个选择,ACROSS_CHANNELS表示在相邻的通道间求和归一化。WITHIN_CHANNEL表示在一个通道内部特定的区域内进行求和归一化。与前面的local_size参数对应。
归一化公式:对于每一个输入, 去除以,得到归一化后的输出
name: "norm1"
bottom: "pool1"
top: "norm1"
lrn_param {
local_size: 5
alpha: 0.0001
beta: 0.75
4、im2col层
如果对matlab比较熟悉的话,就应该知道im2col是什么意思。它先将一个大矩阵,重叠地划分为多个子矩阵,对每个子矩阵序列化成向量,最后得到另外一个矩阵。
看一看图就知道了:
在caffe中,卷积运算就是先对数据进行im2col操作,再进行内积运算(inner product)。这样做,比原始的卷积操作速度更快。
看看两种卷积操作的异同:
阅读(...) 评论()Caffe(56)
Caffe源码导读(7):LRN层的实现 - 卜居 - 博客频道 - CSDN.NET &
http://blog.csdn.net/kkk584520/article/details/
LRN全称为Local Response Normalization,即局部响应归一化层,具体实现在CAFFE_ROOT/src/caffe/layers/lrn_layer.cpp和同一目录下lrn_layer.cu中。
该层需要参数有:
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 版本较旧,新版 Caffe 已经增加参数 k,变为 (k + (alpha / n) ……),感谢 @云峰 同学指出】
其中n为局部尺寸大小local_size,
alpha和beta前面已经定义。
求和将在当前值处于中间位置的局部区域内进行(如果有必要则进行补零)。
下面我们看Caffe代码如何实现。打开CAFFE_ROOT/include/caffe/vision_layers.hpp,从第242行开始看起:
&&&&template&&typename&Dtype&&class&PoolingL&&template&&typename&Dtype&&class&SplitL&&&&&&&&&&&&template&&typename&Dtype&&&class&LRNLayer&:&public&Layer&Dtype&&{&&&public:&&&&explicit&LRNLayer(const&LayerParameter&&param)&&&&&&&&:&Layer&Dtype&(param)&{}&&&&virtual&void&LayerSetUp(const&vector&Blob&Dtype&*&&&bottom,&&&&&&&&vector&Blob&Dtype&*&*&top);&&&&virtual&void&Reshape(const&vector&Blob&Dtype&*&&&bottom,&&&&&&&&vector&Blob&Dtype&*&*&top);&&&&&&&&virtual&inline&LayerParameter_LayerType&type()&const&{&&&&&&return&LayerParameter_LayerType_LRN;&&&&}&&&&virtual&inline&int&ExactNumBottomBlobs()&const&{&return&1;&}&&&&virtual&inline&int&ExactNumTopBlobs()&const&{&return&1;&}&&&&&&&protected:&&&&virtual&void&Forward_cpu(const&vector&Blob&Dtype&*&&&bottom,&&&&&&&&vector&Blob&Dtype&*&*&top);&&&&virtual&void&Forward_gpu(const&vector&Blob&Dtype&*&&&bottom,&&&&&&&&vector&Blob&Dtype&*&*&top);&&&&virtual&void&Backward_cpu(const&vector&Blob&Dtype&*&&&top,&&&&&&&&const&vector&bool&&&propagate_down,&vector&Blob&Dtype&*&*&bottom);&&&&virtual&void&Backward_gpu(const&vector&Blob&Dtype&*&&&top,&&&&&&&&const&vector&bool&&&propagate_down,&vector&Blob&Dtype&*&*&bottom);&&&&&&virtual&void&CrossChannelForward_cpu(const&vector&Blob&Dtype&*&&&bottom,&&&&&&&&vector&Blob&Dtype&*&*&top);&&&&virtual&void&CrossChannelForward_gpu(const&vector&Blob&Dtype&*&&&bottom,&&&&&&&&vector&Blob&Dtype&*&*&top);&&&&virtual&void&WithinChannelForward(const&vector&Blob&Dtype&*&&&bottom,&&&&&&&&vector&Blob&Dtype&*&*&top);&&&&virtual&void&CrossChannelBackward_cpu(const&vector&Blob&Dtype&*&&&top,&&&&&&&&const&vector&bool&&&propagate_down,&vector&Blob&Dtype&*&*&bottom);&&&&virtual&void&CrossChannelBackward_gpu(const&vector&Blob&Dtype&*&&&top,&&&&&&&&const&vector&bool&&&propagate_down,&vector&Blob&Dtype&*&*&bottom);&&&&virtual&void&WithinChannelBackward(const&vector&Blob&Dtype&*&&&top,&&&&&&&&const&vector&bool&&&propagate_down,&vector&Blob&Dtype&*&*&bottom);&&&&&&int&size_;&&&&int&pre_pad_;&&&&Dtype&alpha_;&&&&Dtype&beta_;&&&&int&num_;&&&&int&channels_;&&&&int&height_;&&&&int&width_;&&&&&&&&&&&&&&Blob&Dtype&&scale_;&&&&&&&&&&shared_ptr&SplitLayer&Dtype&&&&split_layer_;&&&&vector&Blob&Dtype&*&&split_top_vec_;&&&&shared_ptr&PowerLayer&Dtype&&&&square_layer_;&&&&Blob&Dtype&&square_input_;&&&&Blob&Dtype&&square_output_;&&&&vector&Blob&Dtype&*&&square_bottom_vec_;&&&&vector&Blob&Dtype&*&&square_top_vec_;&&&&shared_ptr&PoolingLayer&Dtype&&&&pool_layer_;&&&&Blob&Dtype&&pool_output_;&&&&vector&Blob&Dtype&*&&pool_top_vec_;&&&&shared_ptr&PowerLayer&Dtype&&&&power_layer_;&&&&Blob&Dtype&&power_output_;&&&&vector&Blob&Dtype&*&&power_top_vec_;&&&&shared_ptr&EltwiseLayer&Dtype&&&&product_layer_;&&&&Blob&Dtype&&product_input_;&&&&vector&Blob&Dtype&*&&product_bottom_vec_;&&};&&
// Forward declare PoolingLayer and SplitLayer for use in LRNLayer.
template &typename Dtype& class PoolingL
template &typename Dtype& class SplitL
 * @brief Normalize the input in a local region across or within feature maps.
 * TODO(dox): thorough documentation for Forward, Backward, and proto params.
template &typename Dtype&
class LRNLayer : public Layer&Dtype& {
 public:
  explicit LRNLayer(const LayerParameter& param)
      : Layer&Dtype&(param) {}
  virtual void LayerSetUp(const vector&Blob&Dtype&*&& bottom,
      vector&Blob&Dtype&*&* top);
  virtual void Reshape(const vector&Blob&Dtype&*&& bottom,
      vector&Blob&Dtype&*&* top);
  virtual inline LayerParameter_LayerType type() const {
    return LayerParameter_LayerType_LRN;
  virtual inline int ExactNumBottomBlobs() const { return 1; }
  virtual inline int ExactNumTopBlobs() const { return 1; }
 protected:
  virtual void Forward_cpu(const vector&Blob&Dtype&*&& bottom,
      vector&Blob&Dtype&*&* top);
  virtual void Forward_gpu(const vector&Blob&Dtype&*&& bottom,
      vector&Blob&Dtype&*&* top);
  virtual void Backward_cpu(const vector&Blob&Dtype&*&& top,
      const vector&bool&& propagate_down, vector&Blob&Dtype&*&* bottom);
  virtual void Backward_gpu(const vector&Blob&Dtype&*&& top,
      const vector&bool&& propagate_down, vector&Blob&Dtype&*&* bottom);
virtual void CrossChannelForward_cpu(const vector&Blob&Dtype&*&& bottom,
vector&Blob&Dtype&*&* top);
virtual void CrossChannelForward_gpu(const vector&Blob&Dtype&*&& bottom,
vector&Blob&Dtype&*&* top);
virtual void WithinChannelForward(const vector&Blob&Dtype&*&& bottom,
vector&Blob&Dtype&*&* top);
virtual void CrossChannelBackward_cpu(const vector&Blob&Dtype&*&& top,
const vector&bool&& propagate_down, vector&Blob&Dtype&*&* bottom);
virtual void CrossChannelBackward_gpu(const vector&Blob&Dtype&*&& top,
const vector&bool&& propagate_down, vector&Blob&Dtype&*&* bottom);
virtual void WithinChannelBackward(const vector&Blob&Dtype&*&& top,
const vector&bool&& propagate_down, vector&Blob&Dtype&*&* bottom);
int size_;
int pre_pad_;
Dtype alpha_;
Dtype beta_;
int channels_;
int height_;
int width_;
// Fields used for normalization ACROSS_CHANNELS
// scale_ stores the intermediate summing results
Blob&Dtype& scale_;
// Fields used for normalization WITHIN_CHANNEL
shared_ptr&SplitLayer&Dtype& & split_layer_;
vector&Blob&Dtype&*& split_top_vec_;
shared_ptr&PowerLayer&Dtype& & square_layer_;
Blob&Dtype& square_input_;
Blob&Dtype& square_output_;
vector&Blob&Dtype&*& square_bottom_vec_;
vector&Blob&Dtype&*& square_top_vec_;
shared_ptr&PoolingLayer&Dtype& & pool_layer_;
Blob&Dtype& pool_output_;
vector&Blob&Dtype&*& pool_top_vec_;
shared_ptr&PowerLayer&Dtype& & power_layer_;
Blob&Dtype& power_output_;
vector&Blob&Dtype&*& power_top_vec_;
shared_ptr&EltwiseLayer&Dtype& & product_layer_;
Blob&Dtype& product_input_;
vector&Blob&Dtype&*& product_bottom_vec_;
内容较多,可能看一眼记不住所有的成员变量和函数,但记住一点,凡是Layer类型肯定都包含Forward()和Backward(),以及LayerSetUp()和Reshape(),这些在头文件中不必细看。关注的是以“_”结尾的成员变量,这些是和算法息息相关的。
很高兴看到了num_, height_, width_, channels_,这四个变量定义了该层输入图像的尺寸信息,是一个num_ x channels_ x height_ x width_的四维Blob矩阵(想不通?就当作视频流吧,前两维是宽高,第三维是颜色,第四维是时间)。
另外看到了alpha_, beta_, 这两个就是我们上面公式中的参数。
公式中的n(local_size)在类中用size_表示。
上面提到过需要补零,所以定义了pre_pad_变量。
在ACROSS_CHANNELS模式下,我们只需要用到scale_这个Blob矩阵,后面定义都可以忽略了~~好开森~~
读完了头文件中的声明,是不是觉得挺简单?我们接着看下实现细节,打开CAFFE_ROOT/src/caffe/layers/lrn_layer.cpp,从头看起,第一个实现函数为LayerSetUp(),代码如下:
template&&typename&Dtype&&&void&LRNLayer&Dtype&::LayerSetUp(const&vector&Blob&Dtype&*&&&bottom,&&&&&&&&vector&Blob&Dtype&*&*&top)&{&&&&size_&=&this-&layer_param_.lrn_param().local_size();&&&&CHECK_EQ(size_&%&2,&1)&&&&&LRN&only&supports&odd&values&for&local_size&;&&&&pre_pad_&=&(size_&-&1)&/&2;&&&&alpha_&=&this-&layer_param_.lrn_param().alpha();&&&&beta_&=&this-&layer_param_.lrn_param().beta();&&&&if&(this-&layer_param_.lrn_param().norm_region()&==&&&&&&&&LRNParameter_NormRegion_WITHIN_CHANNEL)&{&&&&&&&&&&&&split_top_vec_.clear();&&&&&&split_top_vec_.push_back(&product_input_);&&&&&&split_top_vec_.push_back(&square_input_);&&&&&&LayerParameter&split_&&&&&&split_layer_.reset(new&SplitLayer&Dtype&(split_param));&&&&&&split_layer_-&SetUp(bottom,&&split_top_vec_);&&&&&&&&&&&&square_bottom_vec_.clear();&&&&&&square_top_vec_.clear();&&&&&&square_bottom_vec_.push_back(&square_input_);&&&&&&square_top_vec_.push_back(&square_output_);&&&&&&LayerParameter&square_&&&&&&square_param.mutable_power_param()-&set_power(Dtype(2));&&&&&&square_layer_.reset(new&PowerLayer&Dtype&(square_param));&&&&&&square_layer_-&SetUp(square_bottom_vec_,&&square_top_vec_);&&&&&&&&&&&&pool_top_vec_.clear();&&&&&&pool_top_vec_.push_back(&pool_output_);&&&&&&LayerParameter&pool_&&&&&&pool_param.mutable_pooling_param()-&set_pool(&&&&&&&&&&PoolingParameter_PoolMethod_AVE);&&&&&&pool_param.mutable_pooling_param()-&set_pad(pre_pad_);&&&&&&pool_param.mutable_pooling_param()-&set_kernel_size(size_);&&&&&&pool_layer_.reset(new&PoolingLayer&Dtype&(pool_param));&&&&&&pool_layer_-&SetUp(square_top_vec_,&&pool_top_vec_);&&&&&&&&&&&&&&&&&&power_top_vec_.clear();&&&&&&power_top_vec_.push_back(&power_output_);&&&&&&LayerParameter&power_&&&&&&power_param.mutable_power_param()-&set_power(-beta_);&&&&&&power_param.mutable_power_param()-&set_scale(alpha_);&&&&&&power_param.mutable_power_param()-&set_shift(Dtype(1));&&&&&&power_layer_.reset(new&PowerLayer&Dtype&(power_param));&&&&&&power_layer_-&SetUp(pool_top_vec_,&&power_top_vec_);&&&&&&&&&&&&&&&&&&product_bottom_vec_.clear();&&&&&&product_bottom_vec_.push_back(&product_input_);&&&&&&product_bottom_vec_.push_back(&power_output_);&&&&&&LayerParameter&product_&&&&&&EltwiseParameter*&eltwise_param&=&product_param.mutable_eltwise_param();&&&&&&eltwise_param-&set_operation(EltwiseParameter_EltwiseOp_PROD);&&&&&&product_layer_.reset(new&EltwiseLayer&Dtype&(product_param));&&&&&&product_layer_-&SetUp(product_bottom_vec_,&top);&&&&}&&}&&
template &typename Dtype&
void LRNLayer&Dtype&::LayerSetUp(const vector&Blob&Dtype&*&& bottom,
vector&Blob&Dtype&*&* top) {
size_ = this-&layer_param_.lrn_param().local_size();
CHECK_EQ(size_ % 2, 1) && &LRN only supports odd values for local_size&;
pre_pad_ = (size_ - 1) / 2;
alpha_ = this-&layer_param_.lrn_param().alpha();
beta_ = this-&layer_param_.lrn_param().beta();
if (this-&layer_param_.lrn_param().norm_region() ==
LRNParameter_NormRegion_WITHIN_CHANNEL) {
// Set up split_layer_ to use inputs in the numerator and denominator.
split_top_vec_.clear();
split_top_vec_.push_back(&product_input_);
split_top_vec_.push_back(&square_input_);
LayerParameter split_
split_layer_.reset(new SplitLayer&Dtype&(split_param));
split_layer_-&SetUp(bottom, &split_top_vec_);
// Set up square_layer_ to square the inputs.
square_bottom_vec_.clear();
square_top_vec_.clear();
square_bottom_vec_.push_back(&square_input_);
square_top_vec_.push_back(&square_output_);
LayerParameter square_
square_param.mutable_power_param()-&set_power(Dtype(2));
square_layer_.reset(new PowerLayer&Dtype&(square_param));
square_layer_-&SetUp(square_bottom_vec_, &square_top_vec_);
// Set up pool_layer_ to sum over square neighborhoods of the input.
pool_top_vec_.clear();
pool_top_vec_.push_back(&pool_output_);
LayerParameter pool_
pool_param.mutable_pooling_param()-&set_pool(
PoolingParameter_PoolMethod_AVE);
pool_param.mutable_pooling_param()-&set_pad(pre_pad_);
pool_param.mutable_pooling_param()-&set_kernel_size(size_);
pool_layer_.reset(new PoolingLayer&Dtype&(pool_param));
pool_layer_-&SetUp(square_top_vec_, &pool_top_vec_);
// Set up power_layer_ to compute (1 + alpha_/N^2 s)^-beta_, where s is
// the sum of a squared neighborhood (the output of pool_layer_).
power_top_vec_.clear();
power_top_vec_.push_back(&power_output_);
LayerParameter power_
power_param.mutable_power_param()-&set_power(-beta_);
power_param.mutable_power_param()-&set_scale(alpha_);
power_param.mutable_power_param()-&set_shift(Dtype(1));
power_layer_.reset(new PowerLayer&Dtype&(power_param));
power_layer_-&SetUp(pool_top_vec_, &power_top_vec_);
// Set up a product_layer_ to compute outputs by multiplying inputs by the
// inverse demoninator computed by the power layer.
product_bottom_vec_.clear();
    product_bottom_vec_.push_back(&product_input_);
    product_bottom_vec_.push_back(&power_output_);
    LayerParameter product_
    EltwiseParameter* eltwise_param = product_param.mutable_eltwise_param();
    eltwise_param-&set_operation(EltwiseParameter_EltwiseOp_PROD);
    product_layer_.reset(new EltwiseLayer&Dtype&(product_param));
    product_layer_-&SetUp(product_bottom_vec_, top);
这个函数实现了参数的初始化过程。首先从layer_param_对象中提取出size_的值,并检查是否为奇数,如果不是则报错;之后用size_计算pre_pad_的值,在前后各补一半0。接着alpha_和beta_也被初始化。如果是WITHIN_CHANNEL模式,那么还需要初始化一系列中间子层,这里我们不关心,因为我们用ACROSS_CHANNELS模式。这么简单,还是好开森~~
接下来看Reshape()函数的实现:
template&&typename&Dtype&&&void&LRNLayer&Dtype&::Reshape(const&vector&Blob&Dtype&*&&&bottom,&&&&&&&&vector&Blob&Dtype&*&*&top)&{&&&&num_&=&bottom[0]-&num();&&&&channels_&=&bottom[0]-&channels();&&&&height_&=&bottom[0]-&height();&&&&width_&=&bottom[0]-&width();&&&&switch&(this-&layer_param_.lrn_param().norm_region())&{&&&&case&LRNParameter_NormRegion_ACROSS_CHANNELS:&&&&&&(*top)[0]-&Reshape(num_,&channels_,&height_,&width_);&&&&&&scale_.Reshape(num_,&channels_,&height_,&width_);&&&&&&break;&&&&case&LRNParameter_NormRegion_WITHIN_CHANNEL:&&&&&&split_layer_-&Reshape(bottom,&&split_top_vec_);&&&&&&square_layer_-&Reshape(square_bottom_vec_,&&square_top_vec_);&&&&&&pool_layer_-&Reshape(square_top_vec_,&&pool_top_vec_);&&&&&&power_layer_-&Reshape(pool_top_vec_,&&power_top_vec_);&&&&&&product_layer_-&Reshape(product_bottom_vec_,&top);&&&&&&break;&&&&}&&}&&
template &typename Dtype&
void LRNLayer&Dtype&::Reshape(const vector&Blob&Dtype&*&& bottom,
vector&Blob&Dtype&*&* top) {
num_ = bottom[0]-&num();
channels_ = bottom[0]-&channels();
height_ = bottom[0]-&height();
width_ = bottom[0]-&width();
switch (this-&layer_param_.lrn_param().norm_region()) {
case LRNParameter_NormRegion_ACROSS_CHANNELS:
(*top)[0]-&Reshape(num_, channels_, height_, width_);
scale_.Reshape(num_, channels_, height_, width_);
case LRNParameter_NormRegion_WITHIN_CHANNEL:
split_layer_-&Reshape(bottom, &split_top_vec_);
square_layer_-&Reshape(square_bottom_vec_, &square_top_vec_);
pool_layer_-&Reshape(square_top_vec_, &pool_top_vec_);
power_layer_-&Reshape(pool_top_vec_, &power_top_vec_);
product_layer_-&Reshape(product_bottom_vec_, top);
首先根据bottom的尺寸初始化了num_, channels_, height_, width_这四个尺寸参数,之后根据归一化模式进行不同设置。在ACROSS_CHANNELS模式中,将top尺寸设置为和bottom一样大(num_,
channels_, height_, width_),然后将scale_的尺寸也设置为一样大,这样我们在进行归一化时,只要逐点将scale_值乘以bottom值,就得到相应的top值。scale_值需要根据文章开头的计算公式得到,我们进一步考察怎么实现。
看下一个函数:
template&&typename&Dtype&&&void&LRNLayer&Dtype&::Forward_cpu(const&vector&Blob&Dtype&*&&&bottom,&&&&&&vector&Blob&Dtype&*&*&top)&{&&&&switch&(this-&layer_param_.lrn_param().norm_region())&{&&&&case&LRNParameter_NormRegion_ACROSS_CHANNELS:&&&&&&CrossChannelForward_cpu(bottom,&top);&&&&&&break;&&&&case&LRNParameter_NormRegion_WITHIN_CHANNEL:&&&&&&WithinChannelForward(bottom,&top);&&&&&&break;&&&&default:&&&&&&LOG(FATAL)&&&&&Unknown&normalization&region.&;&&&&}&&}&&
template &typename Dtype&
void LRNLayer&Dtype&::Forward_cpu(const vector&Blob&Dtype&*&& bottom,
vector&Blob&Dtype&*&* top) {
switch (this-&layer_param_.lrn_param().norm_region()) {
case LRNParameter_NormRegion_ACROSS_CHANNELS:
CrossChannelForward_cpu(bottom, top);
case LRNParameter_NormRegion_WITHIN_CHANNEL:
WithinChannelForward(bottom, top);
LOG(FATAL) && &Unknown normalization region.&;
很简单,根据归一化模式调用相应的Forward函数。我们这里看CrossChannelForward_cpu()这个函数,代码如下:
template&&typename&Dtype&&&void&LRNLayer&Dtype&::CrossChannelForward_cpu(&&&&&&const&vector&Blob&Dtype&*&&&bottom,&vector&Blob&Dtype&*&*&top)&{&&&&const&Dtype*&bottom_data&=&bottom[0]-&cpu_data();&&&&Dtype*&top_data&=&(*top)[0]-&mutable_cpu_data();&&&&Dtype*&scale_data&=&scale_.mutable_cpu_data();&&&&&&&&for&(int&i&=&0;&i&&&scale_.count();&++i)&{&&&&&&scale_data[i]&=&1.;&&&&}&&&&Blob&Dtype&&padded_square(1,&channels_&+&size_&-&1,&height_,&width_);&&&&Dtype*&padded_square_data&=&padded_square.mutable_cpu_data();&&&&caffe_set(padded_square.count(),&Dtype(0),&padded_square_data);&&&&Dtype&alpha_over_size&=&alpha_&/&size_;&&&&&&&&for&(int&n&=&0;&n&&&num_;&++n)&{&&&&&&&&&&&&caffe_sqr(channels_&*&height_&*&width_,&&&&&&&&&&bottom_data&+&bottom[0]-&offset(n),&&&&&&&&&&padded_square_data&+&padded_square.offset(0,&pre_pad_));&&&&&&&&&&&&for&(int&c&=&0;&c&&&size_;&++c)&{&&&&&&&&caffe_axpy&Dtype&(height_&*&width_,&alpha_over_size,&&&&&&&&&&&&padded_square_data&+&padded_square.offset(0,&c),&&&&&&&&&&&&scale_data&+&scale_.offset(n,&0));&&&&&&}&&&&&&for&(int&c&=&1;&c&&&channels_;&++c)&{&&&&&&&&&&&&&&&&caffe_copy&Dtype&(height_&*&width_,&&&&&&&&&&&&scale_data&+&scale_.offset(n,&c&-&1),&&&&&&&&&&&&scale_data&+&scale_.offset(n,&c));&&&&&&&&&&&&&&&&caffe_axpy&Dtype&(height_&*&width_,&alpha_over_size,&&&&&&&&&&&&padded_square_data&+&padded_square.offset(0,&c&+&size_&-&1),&&&&&&&&&&&&scale_data&+&scale_.offset(n,&c));&&&&&&&&&&&&&&&&caffe_axpy&Dtype&(height_&*&width_,&-alpha_over_size,&&&&&&&&&&&&padded_square_data&+&padded_square.offset(0,&c&-&1),&&&&&&&&&&&&scale_data&+&scale_.offset(n,&c));&&&&&&}&&&&}&&&&&&&&&&caffe_powx&Dtype&(scale_.count(),&scale_data,&-beta_,&top_data);&&&&caffe_mul&Dtype&(scale_.count(),&top_data,&bottom_data,&top_data);&&}&&
template &typename Dtype&
void LRNLayer&Dtype&::CrossChannelForward_cpu(
const vector&Blob&Dtype&*&& bottom, vector&Blob&Dtype&*&* top) {
const Dtype* bottom_data = bottom[0]-&cpu_data();
Dtype* top_data = (*top)[0]-&mutable_cpu_data();
Dtype* scale_data = scale_.mutable_cpu_data();//用指针获取每个Blob对象的内存地址,便于后面操作
// start with the constant value
for (int i = 0; i & scale_.count(); ++i) {//初始化值为1.0
scale_data[i] = 1.;
Blob&Dtype& padded_square(1, channels_ + size_ - 1, height_, width_);//补零后的Blob,第三维尺寸比bottom大了size_ - 1;
Dtype* padded_square_data = padded_square.mutable_cpu_data();
caffe_set(padded_square.count(), Dtype(0), padded_square_data);//先清零
Dtype alpha_over_size = alpha_ / size_;//预先计算公式中的alpha/n
// go through the images
for (int n = 0; n & num_; ++n) {//bottom的第四维尺寸num_,需要分解为单个来做归一化
// compute the padded square
caffe_sqr(channels_ * height_ * width_,
bottom_data + bottom[0]-&offset(n),
padded_square_data + padded_square.offset(0, pre_pad_));//计算bottom的平方,放入padded_square矩阵中,前pre_pad_个位置依旧0
// Create the first channel scale
for (int c = 0; c & size_; ++c) {//对n个通道平方求和并乘以预先算好的(alpha/n),累加至scale_中(实现计算 1 + sum_under_i(x_i^2))
caffe_axpy&Dtype&(height_ * width_, alpha_over_size,
padded_square_data + padded_square.offset(0, c),
scale_data + scale_.offset(n, 0));
for (int c = 1; c & channels_; ++c) {//这里使用了类似FIFO的形式计算其余scale_参数,每次向后移动一个单位,加头去尾,避免重复计算求和
// copy previous scale
caffe_copy&Dtype&(height_ * width_,
scale_data + scale_.offset(n, c - 1),
scale_data + scale_.offset(n, c));
// add head
caffe_axpy&Dtype&(height_ * width_, alpha_over_size,
padded_square_data + padded_square.offset(0, c + size_ - 1),
scale_data + scale_.offset(n, c));
// subtract tail
caffe_axpy&Dtype&(height_ * width_, -alpha_over_size,
padded_square_data + padded_square.offset(0, c - 1),
scale_data + scale_.offset(n, c));
// In the end, compute output
caffe_powx&Dtype&(scale_.count(), scale_data, -beta_, top_data);//计算求指数,由于将除法转换为乘法,故指数变负
caffe_mul&Dtype&(scale_.count(), top_data, bottom_data, top_data);//bottom .* scale_ -& top
可能你对caffe_axpy, caffe_sqr, caffe_powx, caffe_mul还不熟悉,其实都是很简单的数学计算,在CAFFE_ROOT/include/caffe/util/math_functions.hpp中有声明。
template&&typename&Dtype&&&void&caffe_axpy(const&int&N,&const&Dtype&alpha,&const&Dtype*&X,&&&&&&Dtype*&Y);&&
template &typename Dtype&
void caffe_axpy(const int N, const Dtype alpha, const Dtype* X,
Dtype* Y);
实现如下操作:Y = alpha * X + Y;其中X, Y为N个元素的向量。
template&&typename&Dtype&&&void&caffe_powx(const&int&n,&const&Dtype*&a,&const&Dtype&b,&Dtype*&y);&&
template &typename Dtype&
void caffe_powx(const int n, const Dtype* a, const Dtype b, Dtype* y);
实现如下操作:y = a^b, 其中a, y为n个元素的向量,b为标量。
其余请自己推导。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:292685次
积分:4356
积分:4356
排名:第5700名
原创:82篇
转载:320篇
译文:15篇
评论:36条
(5)(8)(2)(11)(4)(3)(12)(7)(33)(36)(3)(123)(19)(15)(22)(25)(48)(33)(9)}

我要回帖

更多关于 caffe lrn层 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信