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

二、tensorflow 验证结果:

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

从零开始编写深度学习库(五)PoolingLayer 网络层CPU编写的更多相关文章

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

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

  2. Kelp.Net是一个用c#编写的深度学习库

    Kelp.Net是一个用c#编写的深度学习库 基于C#的机器学习--c# .NET中直观的深度学习   在本章中,将会学到: l  如何使用Kelp.Net来执行自己的测试 l  如何编写测试 l  ...

  3. 30个深度学习库:按Python、C++、Java、JavaScript、R等10种语言分类

    30个深度学习库:按Python.C++.Java.JavaScript.R等10种语言分类 包括 Python.C++.Java.JavaScript.R.Haskell等在内的一系列编程语言的深度 ...

  4. Python机器学习库和深度学习库总结

    我们在Github上的贡献者和提交者之中检查了用Python语言进行机器学习的开源项目,并挑选出最受欢迎和最活跃的项目. 1. Scikit-learn(重点推荐) www.github.com/sc ...

  5. python之感知器-从零开始学深度学习

    感知器-从零开始学深度学习 未来将是人工智能和大数据的时代,是各行各业使用人工智能在云上处理大数据的时代,深度学习将是新时代的一大利器,在此我将从零开始记录深度学习的学习历程. 我希望在学习过程中做到 ...

  6. 人工智能不过尔尔,基于Python3深度学习库Keras/TensorFlow打造属于自己的聊天机器人(ChatRobot)

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_178 聊天机器人(ChatRobot)的概念我们并不陌生,也许你曾经在百无聊赖之下和Siri打情骂俏过,亦或是闲暇之余与小爱同学谈 ...

  7. 深度学习库 SynapseML for .NET 发布0.1 版本

    2021年11月 微软开源一款简单的.多语言的.大规模并行的机器学习库 SynapseML(以前称为 MMLSpark),以帮助开发人员简化机器学习管道的创建.具体参见[1]微软深度学习库 Synap ...

  8. 64位Win7下安装并配置Python3的深度学习库:Theano

    注:本文全原创,作者:Noah Zhang  (http://www.cnblogs.com/noahzn/) 这两天在安装Python的深度学习库:Theano.尝试了好多遍,CMake.MinGW ...

  9. MXNet深度学习库简介

    MXNet深度学习库简介 摘要: MXNet是一个深度学习库, 支持C++, Python, R, Scala, Julia, Matlab以及JavaScript等语言; 支持命令和符号编程; 可以 ...

随机推荐

  1. 读取Jar中的json文件

    现在操作json的jar 都是用的fastjson, 如果需要读取的json文件不在jar包里面,则可以这样获取到: String path = this.getClass().getClassLoa ...

  2. LeetCode——largest-rectangle-in-histogram1

    Question Given n non-negative integers representing the histogram's bar height where the width of ea ...

  3. LeetCode——Arithmetic Slices

    Question A sequence of number is called arithmetic if it consists of at least three elements and if ...

  4. contos LINUX搭建LAMP笔记

    LINUX搭建LAMP笔记 .YUM:Yum(全称为 Yellow dog Updater, Modified)是一个在Fedora和RedHat以及CentOS中的Shell前端软件包管理器.基于R ...

  5. eclipse隐藏关闭的工程

    打开上面这个视图

  6. UOJ34 多项式乘法(NTT)

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...

  7. Memcached flush_all 命令

    Memcached flush_all 命令用于用于清理缓存中的所有 key=>value(键=>值) 对. 该命令提供了一个可选参数 time,用于在制定的时间后执行清理缓存操作. 语法 ...

  8. 解决msi文件在XP上安装未完成

    下载Ocra工具,然后删除"DIRCA_CheckFx"和"VSDCA_VsdLaunchConditions"这两个Action即可.第一步,下载并打开Ocr ...

  9. python脚本10_打印斐波那契数列的第101项

    #打印斐波那契数列的第101项 a = 1 b = 1 for count in range(99): a,b = b,a+b else: print(b) 方法2: #打印斐波那契数列的第101项 ...

  10. 【BZOJ1061】【NOI2008】志愿者招募

    [BZOJ1061][NOI2008]志愿者招募 题面 BZOJ 题解 我们设每类志愿者分别招募了\(B[i]\)个 那么,我们可以得到一系列的方程 \[\sum_{S[i]\leq x\leq T[ ...