Run this notebook online: or Colab:

# 6.5. 池化¶

## 6.5.1. 最大池和平均池¶

Fig. 6.5.1 Maximum pooling with a pooling window shape of $$2\times 2$$. The shaded portions represent the first output element and the input element used for its computation: $$\max(0, 1, 3, 4)=4$$

(6.5.1)$\begin{split}\max(0, 1, 3, 4)=4,\\ \max(1, 2, 4, 5)=5,\\ \max(3, 4, 6, 7)=7,\\ \max(4, 5, 7, 8)=8.\\\end{split}$

%load ../utils/djl-imports

NDManager manager = NDManager.newBaseManager();

public NDArray pool2d(NDArray X, Shape poolShape, String mode){

long poolHeight = poolShape.get(0);
long poolWidth = poolShape.get(1);

NDArray Y = manager.zeros(new Shape(X.getShape().get(0) - poolHeight + 1,
X.getShape().get(1) - poolWidth + 1));
for(int i=0; i < Y.getShape().get(0); i++){
for(int j=0; j < Y.getShape().get(1); j++){

if("max".equals(mode)){
Y.set(new NDIndex(i+","+j),
X.get(new NDIndex(i + ":" + (i + poolHeight) + ", " + j + ":" + (j + poolWidth))).max());
}
else if("avg".equals(mode)){
Y.set(new NDIndex(i+","+j),
X.get(new NDIndex(i + ":" + (i + poolHeight) + ", " + j + ":" + (j + poolWidth))).mean());
}

}
}

return Y;
}


NDArray X = manager.arange(9f).reshape(3,3);
pool2d(X, new Shape(2,2), "max");

ND: (2, 2) gpu(0) float32
[[4., 5.],
[7., 8.],
]


pool2d(X, new Shape(2,2), "avg");

ND: (2, 2) gpu(0) float32
[[2., 3.],
[5., 6.],
]


## 6.5.2. 填充和跨步¶

X = manager.arange(16f).reshape(1, 1, 4, 4);
X

ND: (1, 1, 4, 4) gpu(0) float32
[[[[ 0.,  1.,  2.,  3.],
[ 4.,  5.,  6.,  7.],
[ 8.,  9., 10., 11.],
[12., 13., 14., 15.],
],
],
]


// 定义块指定内核和步幅
Block block = Pool.maxPool2dBlock(new Shape(3, 3), new Shape(3, 3));
block.initialize(manager, DataType.FLOAT32, new Shape(1,1,4,4));

ParameterStore parameterStore = new ParameterStore(manager, false);
// 因为池层中没有模型参数，所以我们不需要
// 调用参数初始化函数
block.forward(parameterStore, new NDList(X), true).singletonOrThrow();

ND: (1, 1, 1, 1) gpu(0) float32
[[[[10.],
],
],
]


// 重新定义内核形状、跨步形状和垫块形状
block = Pool.maxPool2dBlock(new Shape(3,3), new Shape(2,2), new Shape(1,1));
// block forward 方法
block.forward(parameterStore, new NDList(X), true).singletonOrThrow();

ND: (1, 1, 2, 2) gpu(0) float32
[[[[ 5.,  7.],
[13., 15.],
],
],
]


// 重新定义内核形状、跨步形状和垫块形状
block = Pool.maxPool2dBlock(new Shape(2,3), new Shape(2,3), new Shape(1,2));
block.forward(parameterStore, new NDList(X), true).singletonOrThrow();

ND: (1, 1, 3, 2) gpu(0) float32
[[[[ 0.,  3.],
[ 8., 11.],
[12., 15.],
],
],
]


## 6.5.3. 多通道¶

X = X.concat(X.add(1), 1);
X

ND: (1, 2, 4, 4) gpu(0) float32
[[[[ 0.,  1.,  2.,  3.],
[ 4.,  5.,  6.,  7.],
[ 8.,  9., 10., 11.],
[12., 13., 14., 15.],
],
[[ 1.,  2.,  3.,  4.],
[ 5.,  6.,  7.,  8.],
[ 9., 10., 11., 12.],
[13., 14., 15., 16.],
],
],
]


block = Pool.maxPool2dBlock(new Shape(3,3), new Shape(2,2), new Shape(1,1));
block.forward(parameterStore, new NDList(X), true).singletonOrThrow();

ND: (1, 2, 2, 2) gpu(0) float32
[[[[ 5.,  7.],
[13., 15.],
],
[[ 6.,  8.],
[14., 16.],
],
],
]


## 6.5.4. 总结¶

• 对于池窗口中的输入元素，最大池操作将最大值指定为输出，平均池操作将平均值指定为输出。

• 池层的主要功能之一是减轻卷积层对位置的过度敏感性。

• 我们可以为池层指定填充和跨步。

• 最大池，再加上大于1的步幅，可以用来降低分辨率。

• 池层的输出通道数与输入通道数相同。

## 6.5.5. 练习¶

1. 你能把平均池作为卷积层的一个特例来实现吗？如果是这样，那就去做。

2. 你能把最大池作为卷积层的特例来实现吗？如果是这样，那就去做。

3. 池层的计算成本是多少？假设池层的输入大小为 $$c\times h\times w$$，池窗口的形状为 $$p_h\times p_w$$ ，填充为 $$(p_h, p_w)$$ ，跨步为$$(s_h, s_w)$$

4. 为什么您希望最大池和平均池的工作方式有所不同？

5. 我们需要一个单独的最小池层吗？你能换个运算吗？

6. 是否可以考虑平均和最大池之间的另一种操作（提示：recall the softmax）？为什么不那么受欢迎？