记录:编写卷积层和池化层,比较需要注意的细节就是边界问题,还有另外一个就是重叠池化的情况,这两个小细节比较重要,边界问题pad在反向求导的时候,由于tensorflow是没有计算的,另外一个比较烦人的是Eigen::Tensor的rowmajor、和colmajor问题,也很烦人。为了跟tensorflow做比较,一些边界处理上的细节,需要特别注意。

一、c++、maxpooling、avgpooling

#pragma once
#include "config.h"
#include <vector>
enum PoolingMethod
{max,avg};class CPoolingLayer
{
public:CPoolingLayer(std::vector<int>pooling_shape,PoolingMethod pooling_method, int padding = 0) {m_hksize = pooling_shape[0];m_wksize = pooling_shape[1];m_hstride = pooling_shape[2];m_wstride = pooling_shape[3];m_padding = padding;m_pooling_method = pooling_method;};~CPoolingLayer() {};//返回(bottom[0],bottom[1]/hstride*bottom[2]/wstride,hsize,wsize,bottom[3])void CPoolingLayer::extract_image_patches(const Tensor4xf &bottom, Tensor5xf &patches) {//这个Eigen tensor类的extract_image_patches函数,由于有数据存储列排列、行排列两种不同的模式。//在下面函数中,如果是采用rowmajor,下面的调用方式才是正确的//不能采用bottom.extract_image_patches(  m_hksize,m_wksize, m_hstride,m_wstride, 1, 1);//switch (m_padding_method)//{//case valid:patches = bottom.extract_image_patches(m_hksize, m_wksize, m_hstride, m_wstride, 1, 1,Eigen::PADDING_VALID);//break;//case same://patches = bottom.extract_image_patches(m_hksize, m_wksize, m_hstride, m_wstride, 1, 1,//Eigen::PADDING_SAME );//break;//default://break;//}}//根据stride、size等计算输出数据的维度形状Eigen::DSizes<int, 4> CPoolingLayer::get_top_shape(const Tensor4xf&bottom) {Eigen::DSizes<int, 4>top_shape;top_shape[0] = bottom.dimension(0);//switch (m_padding_method)//{//case valid:top_shape[1] = Eigen::divup(float(bottom.dimension(1) - m_hksize + 1), float(m_hstride));top_shape[2] = Eigen::divup(float(bottom.dimension(2) - m_wksize + 1), float(m_wstride));//break;//case same://top_shape[1] = Eigen::divup(float(bottom.dimension(1)), float(m_hstride));//top_shape[2] = Eigen::divup(float(bottom.dimension(2)), float(m_wstride));//break;//default://break;//}top_shape[3] = bottom.dimension(3);return top_shape;}//需要特别注意这边的均值池化,与tesorflow,在same模式下处理方式不同,tensorflow的在计算池化的时候,//不管有没有padding,padding值在计算池化操作都被忽略// bottom(batch_size, input_height, input_width, input_channel);void CPoolingLayer::forward(const Tensor4xf&bottom,Tensor4xf&top, const Eigen::ThreadPoolDevice &device) {Tensor4xf pad_bottom;CBaseFunction::padding_forward(bottom, m_padding, m_padding, pad_bottom);Eigen::array<int, 2> reduction_dims{1,2};//第二维、第三维的大小等于(hksize、wksize)Eigen::DSizes<int, 4>post_reduce_dims=get_top_shape(pad_bottom);Tensor5xf patches; //(bottom[0],  hsize, wsize,bottom[1] / hstride*bottom[2] / wstride, bottom[3])extract_image_patches(pad_bottom, patches);Tensor3xf pooling(post_reduce_dims[0],post_reduce_dims[1]*post_reduce_dims[2],post_reduce_dims[3]);switch (m_pooling_method){case avg:pooling.device(device) = patches.mean(reduction_dims);//对reduction_dims内对应的维度索引进行统计,比如统计第3、2break;case max:pooling.device(device) = patches.maximum(reduction_dims);//最大池化break;default:break;}        top=pooling.reshape(post_reduce_dims);}//本函数主要用于索引解码,从一维索引到获取多维下标值。主要原因在于:maxstd::vector<int> CPoolingLayer::decode_index(std::vector<int>dim,int index) {std::vector<int>result;for (int i=0;i<5;i++){int accu = 1;for (int j=5-1;j>i;j--){accu *= dim[j];}result.push_back(std::floor(index / accu));index = index%accu;}return result;}//主要是重叠池化的时候,反向求导的时候是微分值累加。void CPoolingLayer::maxpooling_backward(const Tensor4xf&bottom,const Tensor4xf&dtop,Tensor4xf&dbottom) {Tensor4xf pad_bottom;CBaseFunction::padding_forward(bottom, m_padding, m_padding, pad_bottom);Tensor5xf patches; extract_image_patches(pad_bottom, patches);Tensor4xf dpad_bottom(pad_bottom.dimension(0), pad_bottom.dimension(1), pad_bottom.dimension(2), pad_bottom.dimension(3));dpad_bottom.setZero();Eigen::DSizes<int, 4>post_reduce_dims = get_top_shape(pad_bottom);Eigen::array<Eigen::DenseIndex, 2> reduce_dims{ 1,2 };auto index_tuples = patches.index_tuples();Eigen::Tensor<Eigen::Tuple<Eigen::DenseIndex, float>, 3, Eigen::internal::traits<Tensor5xf>::Layout> reduced_by_dims;reduced_by_dims = index_tuples.reduce(reduce_dims, Eigen::internal::ArgMaxTupleReducer<Eigen::Tuple<Eigen::DenseIndex, float> >());int batch = dtop.dimension(0);int height = dtop.dimension(1);int widht = dtop.dimension(2);int channel = dtop.dimension(3);bool isColMajor = (Eigen::internal::traits<Tensor4xf>::Layout ==Eigen::ColMajor);for (int b= 0; b < batch; b++){for (int h = 0; h< height; h++){for (int w = 0; w < widht; w++){for (int c = 0; c <channel ; c++){const auto &dmax_element = dtop(b, h, w, c);int max_inpatch_height;int max_inpatch_width;if (isColMajor) {//如果是列主元存储,那么维度的序号刚好相反,由(b,h,w,c)变成(c,w,h,b)const Eigen::Tuple<Eigen::DenseIndex, float>&v = reduced_by_dims(c*widht*height*batch + w*height*batch + h*batch + b);int index_in_patch = v.first % (m_wksize*m_hksize);//最大值在每个块中的索引max_inpatch_height = index_in_patch%m_hksize;max_inpatch_width = index_in_patch / m_hksize;}else{                       const Eigen::Tuple<Eigen::DenseIndex, float>&v = reduced_by_dims(b*height*widht*channel + h*widht*channel + w*channel + c);int index_in_patch = v.first % (m_wksize*m_hksize);//最大值在每个块中的索引max_inpatch_height = index_in_patch/m_wksize;max_inpatch_width = index_in_patch % m_wksize;}int patch_height = h*m_hstride + max_inpatch_height;int patch_width = w*m_wstride + max_inpatch_width;dpad_bottom(b, patch_height, patch_width, c) += dmax_element;/*if (patch_height < dbottom.dimension(1) && patch_width < dbottom.dimension(2)){dbottom(b, patch_height, patch_width, c) += dmax_element;}else{std::cout << "out of range" << std::endl;}*/         }}}}CBaseFunction::padding_backward(dpad_bottom, m_padding, m_padding, dbottom);}//均值池化,也可以看成是卷积void CPoolingLayer::avgpooling_backward(const Tensor4xf&dtop, Tensor4xf&dbottom) {Tensor4xf mean_coffe = dtop*(1.f / (m_wksize*m_hksize));//均值池化反向求导要除以均值系数for (int b=0;b<mean_coffe.dimension(0);b++){for (int h=0;h<mean_coffe.dimension(1);h++){for (int w=0;w<mean_coffe.dimension(2);w++){for (int c=0;c<mean_coffe.dimension(3);c++){const auto &mean_element= mean_coffe(b, h, w, c);for (int kh=0;kh<m_hksize;kh++){for (int kw=0;kw<m_wksize;kw++){int patch_height = h*m_hstride + kh - m_padding;int patch_width = w*m_wstride + kw - m_padding;if (patch_height>=0 &&patch_width>=0&&patch_width<dbottom.dimension(2)&&patch_height<dbottom.dimension(1)){dbottom(b, patch_height, patch_width,c) += mean_element;}                        }}}}}}//CBaseFunction::padding_backward(dpad_bottom, m_padding_method, m_padding_method, dbottom);}void CPoolingLayer::backward(const Tensor4xf&bottom,const Tensor4xf&dtop, Tensor4xf&dbottom, const Eigen::ThreadPoolDevice &device) {dbottom.setZero();//计算第2、3维的降维switch (m_pooling_method){case max:maxpooling_backward(bottom, dtop, dbottom);break;case avg:avgpooling_backward(dtop, dbottom);break;default:break;}}
private:int m_hksize;//池化块的长宽int m_wksize;int m_hstride;//池化步长int m_wstride;int m_padding;//边界处理方法PoolingMethod m_pooling_method;//池化方法:均值池化、最大池化等};class CPoolingLayer_test
{
public:static void CPoolingLayer_test::test() {Eigen::ThreadPool *tp = new Eigen::ThreadPool(8);Eigen::ThreadPoolDevice device(tp, 8);int batch_size = 1;int input_channel =1;int input_height =5;int input_width =5;int kenel_height = 3;int kenel_widht = 2;int khstride =2;int kwstride = 3;int pad = 0;Tensor4xf bottom(batch_size, input_height, input_width, input_channel);int count = 0;for (int i=0;i<batch_size;i++){for (int j=0;j<input_height;j++){for (int k=0;k<input_width;k++){for (int h=0;h<input_channel;h++){bottom(i, j, k, h) = 0.1f*count;count++;}}}}Tensor1xf label_1d(batch_size);for (int i = 0; i < batch_size; i++){label_1d(i) = i;}//第一层:pooling层CPoolingLayer layer({kenel_height,kenel_widht,khstride,kwstride },PoolingMethod::max,pad);Tensor4xf top;layer.forward(bottom, top,device);Tensor2xf top_flatten;CBaseFunction::flatten(top, top_flatten);//第二层:sotfmax网络层Tensor2xf one_hot;CBaseFunction::onehot(label_1d, top_flatten.dimension(1), one_hot);Tensor2xf dtop_flatten(top_flatten);float loss = CBaseFunction::softmax_with_loss(top_flatten, one_hot, dtop_flatten, device);Tensor4xf dtop;CBaseFunction::reshape_like(dtop_flatten, top, dtop);Tensor4xf dbottom(bottom);layer.backward(bottom, dtop,dbottom,device);//Tensor4rf dbottom_swap = dbottom.swap_layout();std::cout << "***************forward************" << std::endl;//CBaseFunction::print_shape(one_hot);CBaseFunction::print_shape(dbottom);CBaseFunction::print_element(dbottom);//std::cout << "bottom" << bottom<< std::endl;//std::cout << "top" << top << std::endl;//std::cout << "dbottom" << dbottom << std::endl;std::cout << "loss" << loss << std::endl;//std::cout << "dbottom" << dbottom << std::endl;//std::cout << "dtop" << top << std::endl;}};

二、tensorflow 验证结果:

import  tensorflow as tfbatch_size = 1
input_channel = 1
input_height =5
input_width = 5kenel_height =3
kenel_widht =2
khstride =2
kwstride=3
pad=0
bottom=tf.constant([i*0.1 for i in range(batch_size*input_channel*input_height*input_width)],shape=(batch_size,input_height,input_width,input_channel),dtype=tf.float32)pool1=tf.nn.max_pool(tf.pad(bottom,[[0,0],[pad,pad],[pad,pad],[0,0]]),[1,kenel_height,kenel_widht,1],strides=[1,khstride,kwstride,1],padding='VALID')
pool_flatten=tf.reshape(pool1,[batch_size,-1])label=tf.constant([i for i in range(batch_size)])
one_hot=tf.one_hot(label,pool_flatten.get_shape().as_list()[1])predicts=tf.nn.softmax(pool_flatten)
loss =-tf.reduce_mean(one_hot * tf.log(predicts))#打印相关变量,梯度等,验证是否与c++结果相同
dbottom,dpool1=tf.gradients(loss,[bottom,pool1])with tf.Session() as sess:sess.run(tf.global_variables_initializer())print (sess.run([dbottom]))print (sess.run(loss))#print ('dbottom_data',dbottom_data)

从零开始编写深度学习库(五)PoolingLayer 网络层CPU编写相关推荐

  1. 从零开始编写深度学习库(三)ActivationLayer网络层CPU实现

    从零开始编写深度学习库(三)ActivationLayer网络层CPU实现 博客:http://blog.csdn.net/hjimce 微博:黄锦池-hjimce   qq:1393852684 一 ...

  2. 从零开始编写深度学习库(四)Eigen::Tensor学习使用及代码重构

    从零开始编写深度学习库(四)Eigen::Tensor学习使用及代码重构 博客:http://blog.csdn.net/hjimce 微博:黄锦池-hjimce   qq:1393852684 一. ...

  3. 从零开始编写深度学习库(二)FullyconnecteLayer CPU编写

    从零开始编写深度学习库(二)FullyconnecteLayer CPU编写 博客:http://blog.csdn.net/hjimce 微博:黄锦池-hjimce   qq:1393852684 ...

  4. 从零开始编写深度学习库(一)SoftmaxWithLoss CPU编写

    从零开始编写深度学习库(一)SoftmaxWithLoss CPU编写 博客:http://blog.csdn.net/hjimce 微博:黄锦池-hjimce   qq:1393852684 一.C ...

  5. 从零开始编写深度学习库(五)Eigen Tensor学习笔记2.0

    1.extract_image_patches函数的使用: 假设Eigen::Tensor形状为(3,8,8,9),现在要对第二维.第三维根据size大小为(2,2),stride=(2,2),那么如 ...

  6. 从零开始编写深度学习库(五)ConvolutionLayer CPU编写

    对于池化层来说,需要注意的问题是:重叠池化,还有边界处理模式:valid.same模式这两个细节.由于我采用的边界处理方式,与tensorflow 在same模式下边界处理方式不同,valid模式下是 ...

  7. python深度学习库keras——各类网络层

    全栈工程师开发手册 (作者:栾鹏) python教程全解 一.网络层 keras的层主要包括: 常用层(Core).卷积层(Convolutional).池化层(Pooling).局部连接层.递归层( ...

  8. 人脸识别学习一(Keras: 基于 Python 的深度学习库)

    只是把一些文字部分复制出来方便看,完整还是参考源地址,参考文章https://keras.io/zh/ 写在前面: 第一次接触Keras,用自己的话简单概括一下什么事Keras,(不准确之处欢迎批评指 ...

  9. 如何从零开始构建深度学习项目?这里有一份详细的教程

    点击上方"迈微AI研习社",选择"星标★"公众号 重磅干货,第一时间送达 来源丨机器之心 在学习了有关深度学习的理论课程之后,很多人都会有兴趣尝试构建一个属于自 ...

最新文章

  1. Python3常用数据结构
  2. HTTPS 能否避免流量劫持?
  3. java反射 Method
  4. 【华为云技术分享】《跟唐老师学习云网络》 - 我的网络概念
  5. 【转载】关系型数据库设计范式
  6. mybatis基础总结02 -配置详解
  7. glassfish 是oracle的,GlassFish“百天”小版本 彰显Oracle的大功力
  8. NIO的epoll空轮询bug
  9. 电子设计教程42:限流软启动电路
  10. PowerDesigner16.5 逆向生成物理模型
  11. MySQL [1093] You can‘t specify target table ‘titles_test‘ for update in FROM clause
  12. 无学历与低学历的看过来!!
  13. 运行多个mysql service_同时运行多个MySQL服务器的方法
  14. Python学习记录(4)元组:戴了紧箍咒的列表
  15. XML简介及两种C#读取方式
  16. 微信指数来了,营销人又有新工具
  17. Pomelo PRC
  18. html5图片向上滚动代码,图片上下滚动代码使用方法
  19. 简述文件服务器的主要功能,文件服务器的作用
  20. 光阴静好,你我素行。

热门文章

  1. 各纬度气候分布图_印度和中国都是季风气候显著的国家,但冬夏季风的强弱却完全不同...
  2. mysql 批量建表_mysql 如何实现循环批量插入?
  3. 可视化idea_给IDEA换个酷炫的主题,这个有点哇塞啊!
  4. 优化级别linux gcc,c - 有多少GCC优化级别?
  5. C#设计模式之单例模式
  6. 运行APP脚本的步骤
  7. OpenCV trackbar 避免使用全局变量
  8. git拉取远程分支并创建本地分支和Git中从远程的分支获取最新的版本到本地
  9. iOS开发常用快捷键
  10. C#调用存储过程详解