最近在看GAN(生成对抗网络,深度学习三巨头之一的杨立昆称GAN为“机器学习领域近20年来最酷的想法”。非常有意思的一个领域)相关的东西,复习了一下大神斋藤康毅的《深度学习入门》的第七章--卷积神经网络,发现了一点小错误,记录之。(这个错误只是这本优秀著作的一个小小的瑕疵,瑕不掩瑜,它还是我读过的最好的深度学习入门图书)
书中的第222-223页讲述了卷积神经网络池化层的实现,之前在读这部分的时候重点关注的是其理论,所以代码部分直接略过了,这次回头再读时配合代码理解原理时发现了一点问题:代码和理论是矛盾的。所以两者之间必有一错。
为了方便数据处理,池化层在拿到经激活函数处理过的来自于卷积层的数据之后需要使用im2col函数把4维张量展开成2维张量。下图是书中所配的展开过程示例插图:
并配有文字“池化的应用区域按通道单独展开”的说明。也就是如上图右边所示的展开后每个通道的数据是连续自成一块的。再看python代码:
def forward(self, x):
N, C, H, W = x.shape
out_h = int(1 + (H - self.pool_h) / self.stride)
out_w = int(1 + (W - self.pool_w) / self.stride)
col = im2col(x, self.pool_h, self.pool_w, self.stride, self.pad)
col = col.reshape(-1, self.pool_h*self.pool_w)
arg_max = np.argmax(col, axis=1)
out = np.max(col, axis=1)
out = out.reshape(N, out_h, out_w, C).transpose(0, 3, 1, 2)
return out
如果说数据展开方式如上面的插图所示,那么这里的第12行代码就有问题,应该直接使用:
out = out.reshape(N, C, out_h, out_w)
而不是先reshape一下再转置。这就是矛盾。
那么到底是代码错了还是插图错了呢?其实很简单,我们跑一下代码打印一下数据就可以知道谁错了。
插图中的第二和第三通道的数据看不见,我补在这里
np.array([[
[
[1,2,3,0],
[0,1,2,4],
[1,0,4,2],
[3,2,0,1]
],
[
[3,0,6,5],
[4,2,4,3],
[3,0,1,0],
[2,3,3,1]
],
[
[4,2,1,2],
[0,1,0,4],
[3,0,6,2],
[4,2,4,5]
],
]])
这里我打印出来的结果是:
[[1. 2. 0. 1.] --通道1左上角的的2X2的块
[3. 0. 4. 2.] --通道2左上角的的2X2的块
[4. 2. 0. 1.] --通道3左上角的的2X2的块
[3. 0. 2. 4.] --通道1右上角的的2X2的块
[6. 5. 4. 3.] --通道2右上角的的2X2的块
[1. 2. 0. 4.] --通道3右上角的的2X2的块
[1. 0. 3. 2.] --通道1左下角的的2X2的块
[3. 0. 2. 3.] --通道2左下角的的2X2的块
[3. 0. 4. 2.] --通道3左下角的的2X2的块
[4. 2. 0. 1.] --通道1右下角的的2X2的块
[1. 0. 3. 1.] --通道2右下角的的2X2的块
[6. 2. 4. 5.]] --通道3右下角的的2X2的块
可以清楚的看到三个通道的数据是交叉存放的,所以是插图错了。