当前位置: 首页 > 后端技术 > Python

纯numpy实现滑动窗口计算,继而完成两种池化和寻找稠密区域的任务

时间:2023-03-26 11:53:26 Python

纯numpy实现滑动窗口计算,然后完成pooling和找稠密区域Matrix两个任务,头部的中心位置为1,其他位置为0。我想找到这张图中人口最稠密的区域(areasize是给定的),那怎么找呢?我想到的方法是,将这个区域的大小作为一个固定的窗口,在整个标签矩阵中滑动,计算当前窗口中“1”的个数,每一个个数最大(和最大)的区域当你滑动到一个地方的时候,就是人口最密集的区域,也就是当前窗口。对于滑动窗口的计算,最容易想到的就是用两层for循环来实现,但是首先需要处理边界问题,其次随着图片尺寸的增大,效率会变得很低,内存占用也会很大。本文使用numpy完成滑动窗口计算,计算元素值和最大的区域(区域的大小设置为卷积核的大小)。为了增加算法的普适性,label文件中元素的取值不限于0和1。为了解决这种变化带来的问题,需要增加一个添加元素的动作在窗口滑动计算后的一个小区域。按照这个思路,可以额外完成使用numpy同时实现最大池化和平均池化的任务。举一个简单的例子来完成下面的讨论:$$X=\left[\begin{matrix}1&2&3\\4&5&6\\7&8&9\end{matrix}\right]$$如果kernal的size为2x2,stride为1,那么滑动窗口计算的结果就是由以下4个小矩阵组成的新矩阵A:到这里,滑动窗口的手工计算版本就结束了。在后续任务中,分别对四个小矩阵求和,得到[12,16,24,28]的四个值。可以看出,元素值之和最大的区域是右下角的区域。以下程序用于重现上述讨论。defpool2d(X,kernel_size,stride,padding,pool_mode='avg'):#第一步X=np.pad(X,padding,mode='constant')#第二步output_shape=((X.shape[0]-kernel_size[0]+2*padding)//stride+1,(X.shape[1]-kernel_size[1]+2*padding)//stride+1)#第三步A=as_strided(X,shape=output_shape+kernel_size,strides=(stride*X.strides[0],stride*X.strides[1])+X.strides)#第四步A=A.reshape(-1,*kernel_size)#压缩四维到三维#第五步ifpool_mode=='max':returnA,A.max(axis=(1,2)).reshape(output_shape)#实现最大池化任务elifpool_mode=='avg':returnA,A.mean(axis=(1,2)).reshape(output_shape)#实现均值池化任务第一步:边填充对矩阵进行池化操作,参数常量表示相同value是连续填充的,也就是padding。通常都是零填充。第二步:尺寸预计算预先计算窗口滑动后每个小矩阵Ai的尺寸,这部分可以根据卷积前后尺寸变化公式计算:new=(old-kennel_size+2*padding)/stride+1在上面的示例中,output_shape是(2,2)。【(4-2+2*0)//2+1=2】第三步:滑动窗口计算调用as_strided函数进行窗口滑动计算。这个函数主要有三个参数:要操作的矩阵,不用说了。shape:返回矩阵的大小,与前面的“output_shape”不同。这个shape指的是矩阵A的大小,也就是所有小矩阵放在一起的大小。这个大小不一定等于输入矩阵X的大小,比如上面的例子,shape是(2,2,2,2),输入矩阵X的大小是(3,3).strides:这是numpy数组的一个属性。官方手册给出的解释是数组的每一维需要传递的字节数(bytes)。以上面的矩阵X为例说明:从X[0][0]到X[0][1]需要4个字节,为什么是4个字节呢?因为a的数据类型是int32,正好占用4个字节。从X[0][0]到X[1][0]需要12个字节。为什么有12个字节?因为python是行序优先的编程语言,即在读取矩阵元素时,逐行读取,将矩阵X展平,即[012345678]。从0到3,需要遍历3个元素,每个元素4个字节,所以一共需要12个字节。注1:在常见的编程语言中,只有matlab和fortran是列顺序优先的。注2:为了更深入的理解strides,推荐这篇文章【卷积算法的另一种高效实现,as_strided详解】这一步返回的结果是下面的矩阵A,大小为(2,2,2,2).Step4:Denseareasearchtask其实滑动窗口的计算在第三步就已经结束了。这一步是重新调整大小,将(2,2,2,2)调整为(4,2,2),即第一个维度变成小矩阵的个数,后面两个维度是大小小矩阵。通过对每个小矩阵求和比较,可以知道求和最大的区域,进而完成密集区域搜索任务。第五步:两类pooling任务以averagepooling为例,调用如下函数A.mean(axis=(1,2)).reshape(output_shape)axis=(1,2)因为此时的矩阵A维度为(4,2,2),我们需要从第二个维度开始处理。reshape(output_shape)是因为根据pooling任务的要求,输出结果必须和小矩阵的维度一致,即(2,2)。程序运行结果:参考:https://zhuanlan.zhihu.com/p/...