当前位置:   article > 正文

从零开始编写深度学习库(五)PoolingLayer 网络层CPU编写_pooling 操作 库

pooling 操作 库

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

一、c++、maxpooling、avgpooling

  1. #pragma once
  2. #include "config.h"
  3. #include <vector>
  4. enum PoolingMethod
  5. {
  6. max,
  7. avg
  8. };
  9. class CPoolingLayer
  10. {
  11. public:
  12. CPoolingLayer(std::vector<int>pooling_shape,PoolingMethod pooling_method, int padding = 0) {
  13. m_hksize = pooling_shape[0];
  14. m_wksize = pooling_shape[1];
  15. m_hstride = pooling_shape[2];
  16. m_wstride = pooling_shape[3];
  17. m_padding = padding;
  18. m_pooling_method = pooling_method;
  19. };
  20. ~CPoolingLayer() {
  21. };
  22. //返回(bottom[0],bottom[1]/hstride*bottom[2]/wstride,hsize,wsize,bottom[3])
  23. void CPoolingLayer::extract_image_patches(const Tensor4xf &bottom, Tensor5xf &patches) {
  24. //这个Eigen tensor类的extract_image_patches函数,由于有数据存储列排列、行排列两种不同的模式。
  25. //在下面函数中,如果是采用rowmajor,下面的调用方式才是正确的
  26. //不能采用bottom.extract_image_patches( m_hksize,m_wksize, m_hstride,m_wstride, 1, 1);
  27. //switch (m_padding_method)
  28. //{
  29. //case valid:
  30. patches = bottom.extract_image_patches(m_hksize, m_wksize, m_hstride, m_wstride, 1, 1,
  31. Eigen::PADDING_VALID);
  32. //break;
  33. //case same:
  34. //patches = bottom.extract_image_patches(m_hksize, m_wksize, m_hstride, m_wstride, 1, 1,
  35. //Eigen::PADDING_SAME );
  36. //break;
  37. //default:
  38. //break;
  39. //}
  40. }
  41. //根据stride、size等计算输出数据的维度形状
  42. Eigen::DSizes<int, 4> CPoolingLayer::get_top_shape(const Tensor4xf&bottom) {
  43. Eigen::DSizes<int, 4>top_shape;
  44. top_shape[0] = bottom.dimension(0);
  45. //switch (m_padding_method)
  46. //{
  47. //case valid:
  48. top_shape[1] = Eigen::divup(float(bottom.dimension(1) - m_hksize + 1), float(m_hstride));
  49. top_shape[2] = Eigen::divup(float(bottom.dimension(2) - m_wksize + 1), float(m_wstride));
  50. //break;
  51. //case same:
  52. //top_shape[1] = Eigen::divup(float(bottom.dimension(1)), float(m_hstride));
  53. //top_shape[2] = Eigen::divup(float(bottom.dimension(2)), float(m_wstride));
  54. //break;
  55. //default:
  56. //break;
  57. //}
  58. top_shape[3] = bottom.dimension(3);
  59. return top_shape;
  60. }
  61. //需要特别注意这边的均值池化,与tesorflow,在same模式下处理方式不同,tensorflow的在计算池化的时候,
  62. //不管有没有padding,padding值在计算池化操作都被忽略
  63. // bottom(batch_size, input_height, input_width, input_channel);
  64. void CPoolingLayer::forward(const Tensor4xf&bottom,Tensor4xf&top, const Eigen::ThreadPoolDevice &device) {
  65. Tensor4xf pad_bottom;
  66. CBaseFunction::padding_forward(bottom, m_padding, m_padding, pad_bottom);
  67. Eigen::array<int, 2> reduction_dims{1,2};//第二维、第三维的大小等于(hksize、wksize)
  68. Eigen::DSizes<int, 4>post_reduce_dims=get_top_shape(pad_bottom);
  69. Tensor5xf patches; //(bottom[0], hsize, wsize,bottom[1] / hstride*bottom[2] / wstride, bottom[3])
  70. extract_image_patches(pad_bottom, patches);
  71. Tensor3xf pooling(post_reduce_dims[0],post_reduce_dims[1]*post_reduce_dims[2],post_reduce_dims[3]);
  72. switch (m_pooling_method)
  73. {
  74. case avg:
  75. pooling.device(device) = patches.mean(reduction_dims);//对reduction_dims内对应的维度索引进行统计,比如统计第3、2
  76. break;
  77. case max:
  78. pooling.device(device) = patches.maximum(reduction_dims);//最大池化
  79. break;
  80. default:
  81. break;
  82. }
  83. top=pooling.reshape(post_reduce_dims);
  84. }
  85. //本函数主要用于索引解码,从一维索引到获取多维下标值。主要原因在于:max
  86. std::vector<int> CPoolingLayer::decode_index(std::vector<int>dim,int index) {
  87. std::vector<int>result;
  88. for (int i=0;i<5;i++)
  89. {
  90. int accu = 1;
  91. for (int j=5-1;j>i;j--)
  92. {
  93. accu *= dim[j];
  94. }
  95. result.push_back(std::floor(index / accu));
  96. index = index%accu;
  97. }
  98. return result;
  99. }
  100. //主要是重叠池化的时候,反向求导的时候是微分值累加。
  101. void CPoolingLayer::maxpooling_backward(const Tensor4xf&bottom,const Tensor4xf&dtop,Tensor4xf&dbottom) {
  102. Tensor4xf pad_bottom;
  103. CBaseFunction::padding_forward(bottom, m_padding, m_padding, pad_bottom);
  104. Tensor5xf patches;
  105. extract_image_patches(pad_bottom, patches);
  106. Tensor4xf dpad_bottom(pad_bottom.dimension(0), pad_bottom.dimension(1), pad_bottom.dimension(2), pad_bottom.dimension(3));
  107. dpad_bottom.setZero();
  108. Eigen::DSizes<int, 4>post_reduce_dims = get_top_shape(pad_bottom);
  109. Eigen::array<Eigen::DenseIndex, 2> reduce_dims{ 1,2 };
  110. auto index_tuples = patches.index_tuples();
  111. Eigen::Tensor<Eigen::Tuple<Eigen::DenseIndex, float>, 3, Eigen::internal::traits<Tensor5xf>::Layout> reduced_by_dims;
  112. reduced_by_dims = index_tuples.reduce(reduce_dims, Eigen::internal::ArgMaxTupleReducer<Eigen::Tuple<Eigen::DenseIndex, float> >());
  113. int batch = dtop.dimension(0);
  114. int height = dtop.dimension(1);
  115. int widht = dtop.dimension(2);
  116. int channel = dtop.dimension(3);
  117. bool isColMajor = (Eigen::internal::traits<Tensor4xf>::Layout ==Eigen::ColMajor);
  118. for (int b= 0; b < batch; b++)
  119. {
  120. for (int h = 0; h< height; h++)
  121. {
  122. for (int w = 0; w < widht; w++)
  123. {
  124. for (int c = 0; c <channel ; c++)
  125. {
  126. const auto &dmax_element = dtop(b, h, w, c);
  127. int max_inpatch_height;
  128. int max_inpatch_width;
  129. if (isColMajor) {//如果是列主元存储,那么维度的序号刚好相反,由(b,h,w,c)变成(c,w,h,b)
  130. const Eigen::Tuple<Eigen::DenseIndex, float>&v = reduced_by_dims(c*widht*height*batch + w*height*batch + h*batch + b);
  131. int index_in_patch = v.first % (m_wksize*m_hksize);//最大值在每个块中的索引
  132. max_inpatch_height = index_in_patch%m_hksize;
  133. max_inpatch_width = index_in_patch / m_hksize;
  134. }
  135. else{
  136. const Eigen::Tuple<Eigen::DenseIndex, float>&v = reduced_by_dims(b*height*widht*channel + h*widht*channel + w*channel + c);
  137. int index_in_patch = v.first % (m_wksize*m_hksize);//最大值在每个块中的索引
  138. max_inpatch_height = index_in_patch/m_wksize;
  139. max_inpatch_width = index_in_patch % m_wksize;
  140. }
  141. int patch_height = h*m_hstride + max_inpatch_height;
  142. int patch_width = w*m_wstride + max_inpatch_width;
  143. dpad_bottom(b, patch_height, patch_width, c) += dmax_element;
  144. /*if (patch_height < dbottom.dimension(1) && patch_width < dbottom.dimension(2))
  145. {
  146. dbottom(b, patch_height, patch_width, c) += dmax_element;
  147. }
  148. else
  149. {
  150. std::cout << "out of range" << std::endl;
  151. }*/
  152. }
  153. }
  154. }
  155. }
  156. CBaseFunction::padding_backward(dpad_bottom, m_padding, m_padding, dbottom);
  157. }
  158. //均值池化,也可以看成是卷积
  159. void CPoolingLayer::avgpooling_backward(const Tensor4xf&dtop, Tensor4xf&dbottom) {
  160. Tensor4xf mean_coffe = dtop*(1.f / (m_wksize*m_hksize));//均值池化反向求导要除以均值系数
  161. for (int b=0;b<mean_coffe.dimension(0);b++)
  162. {
  163. for (int h=0;h<mean_coffe.dimension(1);h++)
  164. {
  165. for (int w=0;w<mean_coffe.dimension(2);w++)
  166. {
  167. for (int c=0;c<mean_coffe.dimension(3);c++)
  168. {
  169. const auto &mean_element= mean_coffe(b, h, w, c);
  170. for (int kh=0;kh<m_hksize;kh++)
  171. {
  172. for (int kw=0;kw<m_wksize;kw++)
  173. {
  174. int patch_height = h*m_hstride + kh - m_padding;
  175. int patch_width = w*m_wstride + kw - m_padding;
  176. if (patch_height>=0 &&patch_width>=0&&patch_width<dbottom.dimension(2)&&patch_height<dbottom.dimension(1))
  177. {
  178. dbottom(b, patch_height, patch_width,c) += mean_element;
  179. }
  180. }
  181. }
  182. }
  183. }
  184. }
  185. }
  186. //CBaseFunction::padding_backward(dpad_bottom, m_padding_method, m_padding_method, dbottom);
  187. }
  188. void CPoolingLayer::backward(const Tensor4xf&bottom,const Tensor4xf&dtop, Tensor4xf&dbottom, const Eigen::ThreadPoolDevice &device) {
  189. dbottom.setZero();
  190. //计算第2、3维的降维
  191. switch (m_pooling_method)
  192. {
  193. case max:
  194. maxpooling_backward(bottom, dtop, dbottom);
  195. break;
  196. case avg:
  197. avgpooling_backward(dtop, dbottom);
  198. break;
  199. default:
  200. break;
  201. }
  202. }
  203. private:
  204. int m_hksize;//池化块的长宽
  205. int m_wksize;
  206. int m_hstride;//池化步长
  207. int m_wstride;
  208. int m_padding;//边界处理方法
  209. PoolingMethod m_pooling_method;//池化方法:均值池化、最大池化等
  210. };
  211. class CPoolingLayer_test
  212. {
  213. public:
  214. static void CPoolingLayer_test::test() {
  215. Eigen::ThreadPool *tp = new Eigen::ThreadPool(8);
  216. Eigen::ThreadPoolDevice device(tp, 8);
  217. int batch_size = 1;
  218. int input_channel =1;
  219. int input_height =5;
  220. int input_width =5;
  221. int kenel_height = 3;
  222. int kenel_widht = 2;
  223. int khstride =2;
  224. int kwstride = 3;
  225. int pad = 0;
  226. Tensor4xf bottom(batch_size, input_height, input_width, input_channel);
  227. int count = 0;
  228. for (int i=0;i<batch_size;i++)
  229. {
  230. for (int j=0;j<input_height;j++)
  231. {
  232. for (int k=0;k<input_width;k++)
  233. {
  234. for (int h=0;h<input_channel;h++)
  235. {
  236. bottom(i, j, k, h) = 0.1f*count;
  237. count++;
  238. }
  239. }
  240. }
  241. }
  242. Tensor1xf label_1d(batch_size);
  243. for (int i = 0; i < batch_size; i++)
  244. {
  245. label_1d(i) = i;
  246. }
  247. //第一层:pooling层
  248. CPoolingLayer layer({kenel_height,kenel_widht,khstride,kwstride },PoolingMethod::max,pad);
  249. Tensor4xf top;
  250. layer.forward(bottom, top,device);
  251. Tensor2xf top_flatten;
  252. CBaseFunction::flatten(top, top_flatten);
  253. //第二层:sotfmax网络层
  254. Tensor2xf one_hot;
  255. CBaseFunction::onehot(label_1d, top_flatten.dimension(1), one_hot);
  256. Tensor2xf dtop_flatten(top_flatten);
  257. float loss = CBaseFunction::softmax_with_loss(top_flatten, one_hot, dtop_flatten, device);
  258. Tensor4xf dtop;
  259. CBaseFunction::reshape_like(dtop_flatten, top, dtop);
  260. Tensor4xf dbottom(bottom);
  261. layer.backward(bottom, dtop,dbottom,device);
  262. //Tensor4rf dbottom_swap = dbottom.swap_layout();
  263. std::cout << "***************forward************" << std::endl;
  264. //CBaseFunction::print_shape(one_hot);
  265. CBaseFunction::print_shape(dbottom);
  266. CBaseFunction::print_element(dbottom);
  267. //std::cout << "bottom" << bottom<< std::endl;
  268. //std::cout << "top" << top << std::endl;
  269. //std::cout << "dbottom" << dbottom << std::endl;
  270. std::cout << "loss" << loss << std::endl;
  271. //std::cout << "dbottom" << dbottom << std::endl;
  272. //std::cout << "dtop" << top << std::endl;
  273. }
  274. };
二、tensorflow 验证结果:

  1. import tensorflow as tf
  2. batch_size = 1
  3. input_channel = 1
  4. input_height =5
  5. input_width = 5
  6. kenel_height =3
  7. kenel_widht =2
  8. khstride =2
  9. kwstride=3
  10. pad=0
  11. 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)
  12. 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')
  13. pool_flatten=tf.reshape(pool1,[batch_size,-1])
  14. label=tf.constant([i for i in range(batch_size)])
  15. one_hot=tf.one_hot(label,pool_flatten.get_shape().as_list()[1])
  16. predicts=tf.nn.softmax(pool_flatten)
  17. loss =-tf.reduce_mean(one_hot * tf.log(predicts))
  18. #打印相关变量,梯度等,验证是否与c++结果相同
  19. dbottom,dpool1=tf.gradients(loss,[bottom,pool1])
  20. with tf.Session() as sess:
  21. sess.run(tf.global_variables_initializer())
  22. print (sess.run([dbottom]))
  23. print (sess.run(loss))
  24. #print ('dbottom_data',dbottom_data)



声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/79213
推荐阅读
相关标签
  

闽ICP备14008679号