Maxpooling 2x2 数组仅使用 numpy

Posted

技术标签:

【中文标题】Maxpooling 2x2 数组仅使用 numpy【英文标题】:Maxpooling 2x2 array only using numpy 【发布时间】:2021-11-18 06:58:00 【问题描述】:

我需要关于使用 numpy 进行最大池化的帮助。 我正在学习用于数据科学的 Python,这里我必须对 2x2 矩阵进行最大池化和平均池化,输入可以是 8x8 或更多,但我必须对每个 2x2 矩阵进行最大池化。我使用

创建了一个矩阵
k = np.random.randint(1,64,64).reshape(8,8)

因此,我将得到8x8 矩阵作为随机输出。形成我想做的结果2x2 max pooling。提前感谢

【问题讨论】:

你已经尝试了什么? 我尝试拆分数组,但没有按预期工作 你能发布代码吗?发生了什么你没想到的事情?只是复制粘贴别人给你的功能不会帮助你学习它 这是我在 kaggle notebook 中执行的,我不知道如何详细说明,这是我的作业,我对 Python numpy 完全陌生 到目前为止,我们所能看到的只是创建一个矩阵。你说你试图拆分数组是 oyu 做的吗?为什么它没有达到您的预期? 【参考方案1】:

您可以使用np.lib.stride_tricks 解决卷积部分,这实际上是 numpy 在后台从其方法生成视图的方式。不过要小心,这是对 numpy 数组的内存级别访问。

    对 (8,8) 矩阵进行卷积得到 (2,2) 形状的 (4,4) 矩阵。 使用池化操作(例如均值)减少 (2,2) 矩阵以获得 (4,4) 输出。

这种方法无需任何修改即可扩展到更大的矩阵,并且还可以适应更大的卷积。

k = np.random.randint(1,64,64).reshape(8,8)

#Strides
x,y = 2,2

shape = k.shape[0]//x, k.shape[1]//y, x, y  
strides = k.strides[0]*x, k.strides[1]*y, k.strides[0], k.strides[1]

print('expected shape:',shape)
print('required strides:',strides)

convolve = np.lib.stride_tricks.as_strided(k, shape=shape, strides=strides)
print('convolution output shape:',convolve.shape)

maxpool = np.mean(convolve, axis=(-1,-2))
print('maxpooled output shape:',maxpool.shape)


print(' ')
print('Input matrix:')
print(k)
print('--------')
print('Output matrix:')
print(maxpool)

expected shape: (4, 4, 2, 2)
required strides: (128, 16, 64, 8)
convolution output shape: (4, 4, 2, 2)
maxpooled output shape: (4, 4)
 
Input matrix:
[[19 32 28 25 31 49 17 18]
 [ 4 19 50 57 29 42  5  8]
 [44 16 54 13 15  1 58 50]
 [18 36 29 12 39 45 47 44]
 [34 31 17 28 35 62 30 54]
 [38 50 14 50 25 24 36  4]
 [58 27 20 34 55 22 63 59]
 [61 30 37 24 23 34  5 16]]
--------
Output matrix:
[[18.5  40.   37.75 12.  ]
 [28.5  27.   25.   49.75]
 [38.25 27.25 36.5  31.  ]
 [44.   28.75 33.5  35.75]]

确认一下,如果您只取矩阵的第一个 (2,2) 窗口并对其应用均值池化,您将得到 18.5,这是您的输出矩阵的第一个值,正如预期的那样。

first_window = [[19,32],
                 [4,19]]

np.mean(first_window)

# 18.5

解释

Numpy 将其 ndarray 存储为连续的内存块。每个元素在前一个元素之后每隔 n 个字节按顺序存储。

所以如果你的 3D 数组看起来像这样 -

np.arange(0,16).reshape(2,2,4)

#array([[[ 0,  1,  2,  3],
#        [ 4,  5,  6,  7]],
#
#       [[ 8,  9, 10, 11],
#        [12, 13, 14, 15]]])

然后在内存中存储为 -

当检索一个元素(或一个元素块)时,NumPy 计算它需要遍历多少个strides(每个 8 个字节)才能获取下一个元素in that direction/axis。因此,对于上面的示例,对于axis=2,它必须遍历 8 个字节(取决于datatype),但对于axis=1,它必须遍历8*4 字节,而axis=0 它需要8*8 字节。

这就是arr.strides 的用武之地。它显示了访问该方向的下一个元素所需的字节数。

对于您使用 (8,8) 矩阵的情况 -

    您希望将 8x8 矩阵在每个方向上按 (2,2) 步进行卷积,从而得到 (4,4,2,2) 形状的矩阵。然后你想在你的 maxpooling 步骤中减少最后 2 个维度,平均得到一个 (4,4) 矩阵。

    shape 是您定义的预期形状,在这种情况下为 (4,4,2,2)

    卷积需要访问内存,但是通过在每个方向上采取 2 步(k.strides[0]*2 = 128 字节和 k.strides1*2 = 16 字节来获得 (2 ,2) 窗口,然后是另一个 (64,8) 字节。

注意:尽量不要在这个函数中硬编码步幅/形状。可能导致内存问题。始终使用原始矩阵的步幅和形状计算预期的步幅和形状。

希望这会有所帮助。阅读更多关于 stride_tricks here 和 here 的信息。

【讨论】:

太棒了,太棒了,但我必须了解 strides 和其他方面的知识,无论如何,谢谢伙计 绝对可以。如果你想掌握 numpy,stride_tricks 绝对是必不可少的,因为它允许你在内存级别使用数组并用它们做任何你想做的事情。它非常强大,并且是 numpy 中的大多数函数在其后台实际使用的实际方法。 检查我在答案中链接的最后一个链接。它是一个包含 25 个示例的很棒的教程,用于使用、理解和掌握 numpy 数组的跨步技巧。包括以 zig zag 方式访问值或简单转置之类的东西。【参考方案2】:

您不必自己计算必要的步幅,只需注入两个辅助维度来创建一个 4d 数组,该数组是 2x2 块矩阵的 2d 集合,然后在块上取元素最大值:

import numpy as np

# use 2-by-3 size to prevent some subtle indexing errors
arr = np.random.randint(1, 64, 6*4).reshape(6, 4)

m, n = arr.shape
pooled = arr.reshape(m//2, 2, n//2, 2).max((1, 3))

上面的一个例子:

>>> arr
array([[40, 24, 61, 60],
       [ 8, 11, 27,  5],
       [17, 41,  7, 41],
       [44,  5, 47, 13],
       [31, 53, 40, 36],
       [31, 23, 39, 26]])

>>> pooled
array([[40, 61],
       [44, 47],
       [53, 40]])

对于不假设 2×2 块的完全通用块池:

import numpy as np

# again use coprime dimensions for debugging safety
block_size = (2, 3)
num_blocks = (7, 5)
arr_shape = np.array(block_size) * np.array(num_blocks)
numel = arr_shape.prod()
arr = np.random.randint(1, numel, numel).reshape(arr_shape)

m, n = arr.shape  # pretend we only have this
pooled = arr.reshape(m//block_size[0], block_size[0],
                     n//block_size[1], block_size[1]).max((1, 3))

【讨论】:

@ArockiaJegan 我建议避免使用stride_tricks.as_strided,除非真的有必要。很容易得到垃圾数据。我们拥有像 transposereshape 这样的高级工具来安全地做所有事情。 当你说真的有必要时,你的意思是当涉及不同的步幅或扩张时,比如 pytorch 中的 MaxPool2d? reshape 也能​​处理这些情况吗?谢谢! @Sam-gege “真正需要”是 reshapetransposeview 无法解决的问题。到目前为止,我有一个使用 as_strided 的用例,它被 numpy.org/devdocs/reference/generated/… 渲染为没有实际意义 而且我不知道 pytorch。但是看看github.com/vdumoulin/conv_arithmetic/blob/master/README.md(从pytorch docs链接):似乎填充不是问题,但确实任意步幅可能是有问题的。我可能会选择这种方法(如果适用)或sliding_window_view(但根据需要跳过窗口)。 谢谢安德拉斯。看起来sliding_window_view 更容易。我在开始尝试as_strided 时遇到了一些困难,经常以垃圾数据结束哈哈。顺便说一句,我有另一个关于最大池的类似问题,你有兴趣看看吗? ***.com/questions/69423484/…

以上是关于Maxpooling 2x2 数组仅使用 numpy的主要内容,如果未能解决你的问题,请参考以下文章

nump库的简单函数介绍

Nump库的基本使用

如何矢量化 numpy 数组的 2x2 子数组的平均值?

flex-box 2x2 网格的内部填充和边框

Keras 中的 MaxPool 和 MaxPooling 层有啥区别?

基于 BERT 的 CNN - 卷积和 Maxpooling