Max Pooling算子的设计实现

max pooling算子的定义
池化层在深度学习网络中的作用一般是用来缓解卷积层对位置的过度敏感性。池化层每次对输入数据的一个固定形状窗口(池化窗口的大小为pooling height, pooling width)中的元素计算输出,池化层直接计算池化窗口内元素的最大值或者平均值,因此该运算也分别叫做最大池化或平均池化。
在我们本节课要讲的二维最大池化中,池化窗口从输入数组的最左上方开始,按从左往右、从上往下的顺序,依次在输入数组上滑动(滑动的幅度被称为stride)。当池化窗口滑动到某一位置时,窗口中的输入子数组的最大值即输出数组中相应位置的元素。
图1展示了池化窗口形状为 2×2 的最大池化,阴影部分为第一个输出元素及其计算所使用的输入元素。
输出数组的高和宽分别为2,其中的4个元素由取最大值运算 max 得出。 如下公式所示,池化操作的步骤依次为从左到右,从上到下,每次向下移动的步长为stride height, 向右移动的步长为stride width. 进行池化操作元素的数量由pooling height和pooling width所组成的2×2的窗口所决定。
max pooling operator的实现
class maxpoolingop : public operator { public:  explicit maxpoolingop(uint32_t pooling_h, uint32_t pooling_w, uint32_t stride_h,                        uint32_t stride_w, uint32_t padding_h, uint32_t padding_w);  void set_pooling_h(uint32_t pooling_height);  void set_pooling_w(uint32_t pooling_width);  void set_stride_w(uint32_t stride_width);  void set_stride_h(uint32_t stride_height);  void set_padding_h(uint32_t padding_height);  void set_padding_w(uint32_t padding_width);  uint32_t padding_height() const;  uint32_t padding_width() const;  uint32_t stride_width() const;  uint32_t stride_height() const;  uint32_t pooling_height() const;  uint32_t pooling_width() const; private:  uint32_t pooling_h_; // 池化核高度大小  uint32_t pooling_w_; // 池化核宽度大小  uint32_t stride_h_;  // 高度上的步长  uint32_t stride_w_;  // 宽度上的步长  uint32_t padding_h_; // 高度上的填充  uint32_t padding_w_; // 宽度上的填充};  
可以看到如上的operator中,有6个类内属性,分别对应着我们第一节中讲过的步长(stride), 池化核(pooling)以及在池化前对边缘的扩充,以下我们在分别讲讲:
stride: 池化核每次移动的步长
pooling: 池化核的大小
padding: 对输入特征图的边缘扩充
如下图2是pad(padding值为1)后输入特征图的池化操作(池化核为2):
max pooling layer的实现
maxpoolinglayer::maxpoolinglayer(const std::shared_ptr &op) : layer(maxpooling) {  check(op->op_type_ == optype::koperatormaxpooling) << operator has a wrong type:   &inputs,                               std::vector &outputs) {  check(this->op_ != nullptr);  check(this->op_->op_type_ == optype::koperatormaxpooling);  check(!inputs.empty());  const uint32_t padding_h = this->op_->padding_height();  const uint32_t padding_w = this->op_->padding_width();  const uint32_t kernel_h = this->op_->pooling_height();  const uint32_t kernel_w = this->op_->pooling_width();  const uint32_t stride_h = this->op_->stride_height();  const uint32_t stride_w = this->op_->stride_width();  const uint32_t batch_size = inputs.size();  for (uint32_t i = 0; i < batch_size; ++i) {    const std::shared_ptr &input_data_ = inputs.at(i)->clone();    input_data_->padding({padding_h, padding_h, padding_w, padding_w}, std::numeric_limits::lowest());    const uint32_t input_h = input_data_->rows();    const uint32_t input_w = input_data_->cols();    const uint32_t input_c = input_data_->channels();    const uint32_t output_c = input_c;    const uint32_t output_h = uint32_t(std::floor((input_h - kernel_h) / stride_h + 1));    const uint32_t output_w = uint32_t(std::floor((input_w - kernel_w) / stride_w + 1));    check(output_w > 0 && output_h > 0);    std::shared_ptr output_data = std::make_shared(output_c, output_h, output_w);    for (uint32_t ic = 0; ic at(ic);      arma::fmat &output_channel = output_data->at(ic);      for (uint32_t r = 0; r < input_h - kernel_h + 1; r += stride_h) {        for (uint32_t c = 0; c op_type_ == optype::koperatormaxpooling);  std::shared_ptr max_layer = std::make_sh了ared(op);  return max_layer;}layerregistererwrapper kmaxpoolinglayer(optype::koperatormaxpooling, maxpoolinglayer::createinstance);void maxpoolinglayer::forwards(const std::vector &inputs,                               std::vector &outputs) {  check(this->op_ != nullptr);  check(this->op_->op_type_ == optype::koperatormaxpooling);  check(!inputs.empty());}  
我们重点来看forwards函数, 首先判断输入是否为空并获得池化操作相关的属性值(原本存放在op中).
计算池化后的输出特征图大小, 公式为:
for (uint32_t i = 0; i < batch_size; ++i) {    const std::shared_ptr &input_data_ = inputs.at(i)->clone();    input_data_->padding({padding_h, padding_h, padding_w, padding_w}, std::numeric_limits::lowest());  
如上的过程表示对输入的特征图四周进行填充,填充的大小由于padding_w和padding_h决定。这两个layer计算时候的属性由op中得到,也就是说padding_w和padding_h存放在this->op中, this->op_ = std::make_unique(*max_pooling_op);
for (uint32_t i = 0; i < batch_size; ++i) {    const std::shared_ptr &input_data_ = inputs.at(i)->clone();    input_data_->padding({padding_h, padding_h, padding_w, padding_w}, std::numeric_limits::lowest());    const uint32_t input_h = input_data_->rows();    const uint32_t input_w = input_data_->cols();    const uint32_t input_c = input_data_->channels();    const uint32_t output_c = input_c;    const uint32_t output_h = uint32_t(std::floor((input_h - kernel_h) / stride_h + 1));    const uint32_t output_w = uint32_t(std::floor((input_w - kernel_w) / stride_w + 1));    check(output_w > 0 && output_h > 0);  
如上的过程表示根据输入的特征图大小input_h和input_w来计算对应的输出特征值大小output_h和output_w. 计算的公式如上文所示。如果输入的特征数据input_data_有填充,则根据填充数据的输入大小来计算对应的输出大小。
for (uint32_t i = 0; i at(ic);      for (uint32_t r = 0; r < input_h - kernel_h + 1; r += stride_h) {        for (uint32_t c = 0; c < input_w - kernel_w + 1; c += stride_w) {          const arma::fmat ®ion = input_channel.submat(r, c, r + kernel_h - 1, c + kernel_w - 1);          output_channel.at(int(r / stride_h), int(c / stride_w)) = region.max();        }      }    }}  
for(uint32_t ic =0; ic op_type_ == optype::koperatormaxpooling) << operator has a wrong type:  op_中保存了stride,padding,pooling等计算时需要的属性信息。
单元测试
test(test_layer, forward_maxpooling1) {  using namespace kuiper_infer;  uint32_t stride_h = 1;  uint32_t stride_w = 1;  uint32_t padding_h = 0;  uint32_t padding_w = 0;  uint32_t pooling_h = 2;  uint32_t pooling_w = 2;  std::shared_ptr      max_op = std::make_shared(pooling_h, pooling_w, stride_h, stride_w, padding_h, padding_w);  std::shared_ptr max_layer = layerregisterer::createlayer(max_op);  check(max_layer != nullptr);  arma::fmat input_data = 0 1 2 ;                          3 4 5 ;                          6 7 8 ;;  std::shared_ptr input = std::make_shared(2, input_data.n_rows, input_data.n_cols);  input->at(0) = input_data;  input->at(1) = input_data;  std::vector inputs;  std::vector outputs;  inputs.push_back(input);  max_layer->forwards(inputs, outputs);  assert_eq(outputs.size(), 1);  const auto &output = outputs.at(0);  log(info) <<  rows(), 2);  assert_eq(output->cols(), 2);  assert_eq(output->at(0, 0, 0), 4);  assert_eq(output->at(0, 0, 1), 5);  assert_eq(output->at(0, 1, 0), 7);  assert_eq(output->at(0, 1, 1), 8);  assert_eq(output->at(1, 0, 0), 4);  assert_eq(output->at(1, 0, 1), 5);  assert_eq(output->at(1, 1, 0), 7);  assert_eq(output->at(1, 1, 1), 8);}  
可以看到, 我们的输入为  arma::fmat input_data =0 1 2 ; 3 4 5 ;6 7 8; , 池化核的大小为2, 每次移动的步长stride =1,所以根据我们在第一节中的计算, 最后的输出特征图大小应该是2乘2大小, 池化得到的值分别为4 5 7 8.


高级16位数字信号控制器dsPIC33F的主要特性及应用电路
我国多地加强人工智能产业发展 日本智能电网建设取得新进展
基于HLS之任务级并行编程
在隐形眼镜上能否放置一个微型显示屏
小米6最新消息:小米官方已默认小米6重启问题,希望用户等待米6的系统更新
Max Pooling算子的设计实现
RISC-V MCU gp全局指针说明
保护电路厂商:芯凯电子科技(上海)有限公司简介
vivo公布充电控制专利,通过调整充电芯片频率以提升效率
如何选购家庭臭氧机
生物识别技术的应用现状_生物识别发展趋势
电压驻波比测量
模型的带宽对信号眼图有什么影响?
重庆汽车电子再增新生力量
HDR显示器缺陷太多 暂时还不值得入手
电子油门,电子油门是什么意思
通用型粉尘检测仪JCF-5H的使用方法和注意事项
市场化是中国半导体业发展的必由之路
薄膜在线瑕疵检测仪的原理及技术指标的介绍
大力布局,特斯拉或计划在德国生产电芯