caffe源码分析--poolinger_layer.cpp

对于采样层,cafffe里实现了最大采样和平均采样的算法。

最大采样,给定一个扫描窗口,找最大值,

平均采样,扫描窗口内所有值的平均值。

其实对于caffe的实现一直有个疑问,

就是每一层貌似没有绑定一个激活函数?

看ufldl教程,感觉激活函数是必要存在的。

这怎么解释呢?

看到源码中,看到一些激活函数,比如sigmoid_layer.cpp和sigmoid_layer.cu。

也就是说,激活函数作为layer层面来实现了。当然,还有tanh_layer和relu_layer。

那,这个意思是说,让我们建立网络的时候更加随意,可自由搭配激活函数吗?

但是,我看了caffe自带的那些例子,貌似很少见到用了激活函数layer的,顶多看到用了relu_layer,其他的没见过。

这意思是说,激活函数不重要吗?真是费解啊。

// Copyright 2013 Yangqing Jia

#include <algorithm>
#include <cfloat>
#include <vector>

#include "caffe/layer.hpp"
#include "caffe/vision_layers.hpp"
#include "caffe/util/math_functions.hpp"

using std::max;
using std::min;

namespace caffe {

template <typename Dtype>
void PoolingLayer<Dtype>::SetUp(const vector<Blob<Dtype>*>& bottom,
      vector<Blob<Dtype>*>* top) {
  CHECK_EQ(bottom.size(), 1) << "PoolingLayer takes a single blob as input.";
  CHECK_EQ(top->size(), 1) << "PoolingLayer takes a single blob as output.";
  KSIZE_ = this->layer_param_.kernelsize();//核大小
  STRIDE_ = this->layer_param_.stride();//步长
  CHANNELS_ = bottom[0]->channels();//通道
  HEIGHT_ = bottom[0]->height();//高
  WIDTH_ = bottom[0]->width();//宽
  POOLED_HEIGHT_ = static_cast<int>(
      ceil(static_cast<float>(HEIGHT_ - KSIZE_) / STRIDE_)) + 1;//计算采样之后的高
  POOLED_WIDTH_ = static_cast<int>(
      ceil(static_cast<float>(WIDTH_ - KSIZE_) / STRIDE_)) + 1;//计算采样之后的宽
  (*top)[0]->Reshape(bottom[0]->num(), CHANNELS_, POOLED_HEIGHT_,//采样之后大小
      POOLED_WIDTH_);
  // If stochastic pooling, we will initialize the random index part.
  if (this->layer_param_.pool() == LayerParameter_PoolMethod_STOCHASTIC) {
    rand_idx_.Reshape(bottom[0]->num(), CHANNELS_, POOLED_HEIGHT_,
      POOLED_WIDTH_);
  }
}

// TODO(Yangqing): Is there a faster way to do pooling in the channel-first
// case?
template <typename Dtype>
void PoolingLayer<Dtype>::Forward_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();//采样层输出
  // Different pooling methods. We explicitly do the switch outside the for
  // loop to save time, although this results in more codes.
  int top_count = (*top)[0]->count();
  switch (this->layer_param_.pool()) {
  case LayerParameter_PoolMethod_MAX://最大采样方法
    // Initialize
    for (int i = 0; i < top_count; ++i) {
      top_data[i] = -FLT_MAX;
    }
    // The main loop
    for (int n = 0; n < bottom[0]->num(); ++n) {
      for (int c = 0; c < CHANNELS_; ++c) {
        for (int ph = 0; ph < POOLED_HEIGHT_; ++ph) {
          for (int pw = 0; pw < POOLED_WIDTH_; ++pw) {
            int hstart = ph * STRIDE_;
            int wstart = pw * STRIDE_;
            int hend = min(hstart + KSIZE_, HEIGHT_);
            int wend = min(wstart + KSIZE_, WIDTH_);
            for (int h = hstart; h < hend; ++h) {//找出核范围内最大
              for (int w = wstart; w < wend; ++w) {
                top_data[ph * POOLED_WIDTH_ + pw] =
                  max(top_data[ph * POOLED_WIDTH_ + pw],
                      bottom_data[h * WIDTH_ + w]);
              }
            }
          }
        }
        // compute offset 指针移动到下一个channel。注意代码这里的位置。采样是针对每个channel的。
        bottom_data += bottom[0]->offset(0, 1);
        top_data += (*top)[0]->offset(0, 1);
      }
    }
    break;
  case LayerParameter_PoolMethod_AVE:
    for (int i = 0; i < top_count; ++i) {
      top_data[i] = 0;
    }
    // The main loop
    for (int n = 0; n < bottom[0]->num(); ++n) {
      for (int c = 0; c < CHANNELS_; ++c) {
        for (int ph = 0; ph < POOLED_HEIGHT_; ++ph) {
          for (int pw = 0; pw < POOLED_WIDTH_; ++pw) {
            int hstart = ph * STRIDE_;
            int wstart = pw * STRIDE_;
            int hend = min(hstart + KSIZE_, HEIGHT_);
            int wend = min(wstart + KSIZE_, WIDTH_);
            for (int h = hstart; h < hend; ++h) {//核范围内算平均
              for (int w = wstart; w < wend; ++w) {
                top_data[ph * POOLED_WIDTH_ + pw] +=
                    bottom_data[h * WIDTH_ + w];
              }
            }
            top_data[ph * POOLED_WIDTH_ + pw] /=
                (hend - hstart) * (wend - wstart);
          }
        }
        // compute offset
        bottom_data += bottom[0]->offset(0, 1);
        top_data += (*top)[0]->offset(0, 1);
      }
    }
    break;
  case LayerParameter_PoolMethod_STOCHASTIC://这种算法这里未实现
    NOT_IMPLEMENTED;
    break;
  default:
    LOG(FATAL) << "Unknown pooling method.";
  }
}

template <typename Dtype>
Dtype PoolingLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
      const bool propagate_down, vector<Blob<Dtype>*>* bottom) {
  if (!propagate_down) {
    return Dtype(0.);
  }
  const Dtype* top_diff = top[0]->cpu_diff();
  const Dtype* top_data = top[0]->cpu_data();
  const Dtype* bottom_data = (*bottom)[0]->cpu_data();
  Dtype* bottom_diff = (*bottom)[0]->mutable_cpu_diff();
  // Different pooling methods. We explicitly do the switch outside the for
  // loop to save time, although this results in more codes.
  memset(bottom_diff, 0, (*bottom)[0]->count() * sizeof(Dtype));
  switch (this->layer_param_.pool()) {
  case LayerParameter_PoolMethod_MAX:
    // The main loop
    for (int n = 0; n < top[0]->num(); ++n) {
      for (int c = 0; c < CHANNELS_; ++c) {
        for (int ph = 0; ph < POOLED_HEIGHT_; ++ph) {
          for (int pw = 0; pw < POOLED_WIDTH_; ++pw) {
            int hstart = ph * STRIDE_;
            int wstart = pw * STRIDE_;
            int hend = min(hstart + KSIZE_, HEIGHT_);
            int wend = min(wstart + KSIZE_, WIDTH_);
            for (int h = hstart; h < hend; ++h) {
              for (int w = wstart; w < wend; ++w) {
                bottom_diff[h * WIDTH_ + w] +=//采样层输出的残传播给输入。由于是最大采样方法,输出存的都是输入范围内最大的值,所以残差传播的时候也只有范围内最大的值受影响
                    top_diff[ph * POOLED_WIDTH_ + pw] *
                    (bottom_data[h * WIDTH_ + w] ==
                        top_data[ph * POOLED_WIDTH_ + pw]);
              }
            }
          }
        }
        // offset  移动到下一个channel
        bottom_data += (*bottom)[0]->offset(0, 1);
        top_data += top[0]->offset(0, 1);
        bottom_diff += (*bottom)[0]->offset(0, 1);
        top_diff += top[0]->offset(0, 1);
      }
    }
    break;
  case LayerParameter_PoolMethod_AVE:
    // The main loop
    for (int n = 0; n < top[0]->num(); ++n) {
      for (int c = 0; c < CHANNELS_; ++c) {
        for (int ph = 0; ph < POOLED_HEIGHT_; ++ph) {
          for (int pw = 0; pw < POOLED_WIDTH_; ++pw) {
            int hstart = ph * STRIDE_;
            int wstart = pw * STRIDE_;
            int hend = min(hstart + KSIZE_, HEIGHT_);
            int wend = min(wstart + KSIZE_, WIDTH_);
            int poolsize = (hend - hstart) * (wend - wstart);
            for (int h = hstart; h < hend; ++h) {
              for (int w = wstart; w < wend; ++w) {
                bottom_diff[h * WIDTH_ + w] +=//采样层输出的残差传播给输入,由于是平均采样,所以权重都是1 / poolsize。
                  top_diff[ph * POOLED_WIDTH_ + pw] / poolsize;
              }
            }
          }
        }
        // offset
        bottom_data += (*bottom)[0]->offset(0, 1);
        top_data += top[0]->offset(0, 1);
        bottom_diff += (*bottom)[0]->offset(0, 1);
        top_diff += top[0]->offset(0, 1);
      }
    }
    break;
  case LayerParameter_PoolMethod_STOCHASTIC:
    NOT_IMPLEMENTED;
    break;
  default:
    LOG(FATAL) << "Unknown pooling method.";
  }
  return Dtype(0.);
}

INSTANTIATE_CLASS(PoolingLayer);

}  // namespace caffe

本文作者:linger

本文链接:http://blog.csdn.net/lingerlanlan/article/details/38294169

caffe源码分析--poolinger_layer.cpp

时间: 2024-10-13 12:01:22

caffe源码分析--poolinger_layer.cpp的相关文章

caffe源码分析--softmax_layer.cpp

caffe源码分析--softmax_layer.cpp 文件位置为caffe-master/src/caffe/layers/softmax_layer.cpp 这个是一个以前版本的程序,现在的代码有些不同了,不过可以参考 [cpp] view plaincopy // Copyright 2013 Yangqing Jia // #include <algorithm> #include <vector> #include "caffe/layer.hpp"

Caffe学习系列(17): caffe源码分析 vector&lt;Blob&lt;Dtype&gt;*&gt;&amp; bottom(转)

转自:http://blog.csdn.net/qq_14975217/article/details/51524042 Blob:4个维度 n x c x h x w: bottom[0] .bottom[1]代表该层有几个输入. bottom[0]->count(): 输入中,元素的总维数(个数) bottom[0]->nums(): 输入中,块(block)的个数,该参数还对应batch_size,即同时输入了几张图片 c:是卷积核(filter)的个数,每个卷积核产生一个通道的输出:在

神经网络caffe框架源码解析--softmax_layer.cpp类代码研究

// Copyright 2013 Yangqing Jia // #include <algorithm> #include <vector> #include "caffe/layer.hpp" #include "caffe/vision_layers.hpp" #include "caffe/util/math_functions.hpp" using std::max; namespace caffe { /**

神经网络caffe框架源码解析--data_layer.cpp类代码研究

dataLayer作为整个网络的输入层, 数据从leveldb中取.leveldb的数据是通过图片转换过来的. 网络建立的时候, datalayer主要是负责设置一些参数,比如batchsize,channels,height,width等. 这次会通过读leveldb一个数据块来获取这些信息. 然后启动一个线程来预先从leveldb拉取一批数据,这些数据是图像数据和图像标签. 正向传播的时候, datalayer就把预先拉取好数据拷贝到指定的cpu或者gpu的内存. 然后启动新线程再预先拉取数

caffe中HingeLossLayer层原理以及源码分析

输入: bottom[0]: NxKx1x1维,N为样本个数,K为类别数.是预测值. bottom[1]: Nx1x1x1维, N为样本个数,类别为K时,每个元素的取值范围为[0,1,2,-,K-1].是groundTruth. 输出: top[0]: 1x1x1x1维, 求得是hingeLoss. 关于HingeLoss: p: 范数,默认是L1范数,可以在配置中设置为L1或者L2范数. :指示函数,如果第n个样本的真实label为k,则为,否则为-1. tnk: bottom[0]中第n个样

分析caffe源码以及相应的Google c++ style

本人项目需分析caffe源码,并做一些相应的修改 1.caffe源码工程目录 src目录为整个工程的核心,它主要实现了神经网络的基本模型的组件:Blob.Layer.Net.Solver等核心类. include目录存放整个工程所有头文件. tools目录中存放了caffe的入口函数,用于参数解析,神经网络配置等. data目录和examples目录用于提供一些基本例子和相应数据. matlab和python目录存放用于两者的接口,只要不使用两者,目录下的内容没有实质作用. 其他一些目录和文件是

Caffe源码-im2col操作

目录 im2col简介 im2col.cpp源码 小结 参考 @(Caffe源码-im2col操作) im2col简介 caffe的卷积操作中使用im2col来加速,im2col将卷积核中的每个点在图像上的对应点全都提取出来按行排列,得到一个矩阵,这样就将卷积操作转化为矩阵进行操作. 如上图所示的,假设输入图像的形状为channels=1, height=width=5,并且pad_w=pad_h=1, kernel_h=kernel_w=3, stride_h=stride_w=2, dila

TeamTalk源码分析之login_server

login_server是TeamTalk的登录服务器,负责分配一个负载较小的MsgServer给客户端使用,按照新版TeamTalk完整部署教程来配置的话,login_server的服务端口就是8080,客户端登录服务器地址配置如下(这里是win版本客户端): 1.login_server启动流程 login_server的启动是从login_server.cpp中的main函数开始的,login_server.cpp所在工程路径为server\src\login_server.下表是logi

zeromq源码分析笔记之线程间收发命令(2)

在zeromq源码分析笔记之架构说到了zmq的整体架构,可以看到线程间通信包括两类,一类是用于收发命令,告知对象该调用什么方法去做什么事情,命令的结构由command_t结构体确定:另一类是socket_base_t实例与session的消息通信,消息的结构由msg_t确定.命令的发送与存储是通过mailbox_t实现的,消息的发送和存储是通过pipe_t实现的,这两个结构都会详细说到,今天先说一下线程间的收发命令. zeromq的线程可分为两类,一类是io线程,像reaper_t.io_thr