我用Python实现了一个简单的CNN程序,可以在MNIST数据集上进行机器学习。我实现了三层:
- ConvPoolLayer,这一层进行卷积操作然后进行平均池化
- FullyConnectedLayer,这是一个全连接的隐藏层
- SoftmaxLayer,这一层基本上给出网络的softmax输出
我在ConvPoolLayer中实现了平均池化。以下是前向传播过程中进行平均池化的代码行:
# 'activation' 是来自卷积代码的3D激活的numpy数组(此处未显示) skimage.measure.block_reduce(activation, block_size=(1, 1, 2, 2), func=np.mean)
以下是相应的反向传播代码:
# delta 是从上层反向传播的3D错误矩阵的numpy数组delta = delta.repeat(2, axis=2).repeat(2, axis=3)
它所做的只是放大错误。
我的问题是,如何实现最大池化的反向传播而不损失性能?或者,有没有更好的方法可以避免函数调用?我在几次迭代后使用平均池化获得了大约90-95%的准确率,所以我想看看最大池化对性能的影响如何。
如果这里有任何NumPy技巧可以应用,我很乐意学习。我想自己理解CNN中发生的事情,为什么事情会这样运作,以及操作是否可以优化,所以使用框架对我来说不是一个选项。
谢谢帮助!
回答:
-
[更新] 对于最大池化的前向传播使用:
skimage.measure.block_reduce(activation, block_size=(1, 1, 2, 2), func=np.max)
-
您对平均池化的反向传播并不完全正确。您应该将delta除以池化单元的数量(在您的例子中是4)。参见幻灯片11上的方程 http://www.slideshare.net/kuwajima/cnnbp
-
要传播最大池化,您需要将delta分配给前向传递中具有最高值的单元。因此,在池化层的向前传递过程中,通常会跟踪最大激活的索引(有时也称为开关),以便在反向传播期间梯度路由有效。参见 http://cs231n.github.io/convolutional-networks/#pool
实现这种方法的非常低效的方式:
#forwardactivationPrevious = np.copy(activation)skimage.measure.block_reduce(activation, block_size=(1, 1, 2, 2), func=np.max)maxs = activations.repeat(2, axis=2).repeat(2, axis=3)mask = np.equal(activationPrevious, maxs).astype(int)#backwarddelta = delta.repeat(2, axis=2).repeat(2, axis=3)delta = np.multiply(delta, mask)