稀疏连接性


CNN通过增强相邻两层中神经元的局部的连接来发掘局部空间相关性. m层的隐输入单元和m-1层的一部分空间相邻,并具有连续可视野的神经元相连接. 它们的关系如下图所示:

 deeplearning----卷积神经网络
我们可以假设m-1层为输入视网膜, 在它之上,m层的视觉神经元具有宽度为3的可视野,因此一个单元可以连接视网膜层的三个相邻的神经元. m层的神经元和m-1层具有类似的连接属性. 因此m+1层的神经元对于m层,仍具有宽度为3的可视野,但是相对于m-1层,可视野的宽度更大(结果为5). 这种结构把训练好的过滤器构建成一种局部空间模式. 如上图所示, 过滤器由多个感知层堆积而成,它变得更加地全局. 比如,m+1层的一个神经元可以对m-1层的宽度为5的特征进行编码.

共享权重


 

在CNN中,每一个稀疏的过滤器$h_i$在整个可视野上是叠加的重复的. 这些重复的单元形成了一种特征图,它可以共享相同的参数,比如共同的权向量和偏差.

deeplearning----卷积神经网络

上图中, 属于同一个的特征图的三个隐单元,因为需要共享相同颜色的权重, 他们的被限制成相同的. 梯度下降算法,在进行了一个轻微的改动之后, 仍然可以用来学习这些共享的参数.  共享权重的梯度可以对共享参数的梯度进行简单的求和得到.

为什么要对共享权重感兴趣呢? 在这种方式中,重复单元可以检测特征,无论他们在可视野中的位置在什么地方. 而权重的共享为此提供了一种非常有效的方法, 因为这样做可以在很大程度上减少需要学习的参数. 通过控制模型的容量,CNN在视觉问题上达到了更好的泛化.

具体细节


 

从概念上讲,特征图通过对输入图像在一个线性滤波器上的卷积运算,增加一个便宜量,在结果上作用一个非线性函数得到.如果我们把某层的第k个的特征图记为$h^k$,其过滤器由权重$W$和偏移量$b_k$决定, 那么,特征图可以通过下面的函数得到:

$$h^k_{ij} = tanh ( (W^k * x)_{ij} + b_k ) $$

为了更好的表达数据, 隐层由一系列的多个特征图构成${h^{(k)}, k= 0 .. K}$. 其权重$W$由四个参数决定: 目标特征图的索引,源特征图的索引,源水平位置索引和源垂直位置索引. 偏移量为一个向量,其中每一个元素对应目标特征图的一个索引. 其逻辑关系通过下图表示: 

deeplearning----卷积神经网络

Figure 1: 卷积层实例 (这个图和下面的说明有点冲突,下面的特征权重表示成了$W^0$,$W^1$,图中是 $W^1$,$W^2$)

这里是一个两层的CNN,它有 m-1层的四个特征图和m层的两个特征图($h^0, h^1$)构成. 神经元在$h^0$和$h^1$的输出(蓝色和红色的框所示)是由m-1层落入其相应的2*2的可视野的像素计算得到, 这里需要注意可视野如何地跨四个特征图.其权重为3D张量,分别表示了输入特征图的索引,以及像素的坐标.

整合以上概念, $W_{ij}^{kl}$表示了连接m层第k个特征图的特征图上每一个像素的权重, 像素为m-1层的第l个特征图,其位置为 $(i,j)$. 

ConvOp


 

Convop是Theano中实现卷积的函数, 它主要重复了scipy工具包中signal.convolve2d的函数功能. 总的来讲,ConvOp包含两个参数:

  • 对应输入图像的mini-batch的4D张量. 其每个张量的大小为:[mini-batch的大小, 输入的特征图的数量, 图像的高度,图像的宽度]
  • 对应权重矩阵$W$的4D张量,其每个张量的大小为:[m层的特征图的数量,m-1层的特征图的数量,过滤器的高度,过滤器的宽度].

下面的代码实现了一个类似图1里面的卷积层. 输入图像包括大小为120*160的三个特征图(对应RGB). 我们可以用两个具有9*9的可视野的卷积过滤器.

from theano.tensor.nnet import conv
rng = numpy.random.RandomState(23455)

# instantiate 4D tensor for input
input = T.tensor4(name='input')

# initialize shared variable for weights.
w_shp = (2, 3, 9, 9)
w_bound = numpy.sqrt(3 * 9 * 9)
# Square root 就是平方根的意思 W
= theano.shared( numpy.asarray( rng.uniform( #产生一个所有数位于(low和high)之间的大小为size的矩阵 low=-1.0 / w_bound, high=1.0 / w_bound, size=w_shp), dtype=input.dtype), name ='W')
# initialize shared variable for bias (1D tensor) with random values # IMPORTANT: biases are usually initialized to zero. However in this # particular application, we simply apply the convolutional layer to # an image without learning the parameters. We therefore initialize # them to random values to "simulate" learning. b_shp = (2,) b = theano.shared(numpy.asarray( rng.uniform(low=-.5, high=.5, size=b_shp), dtype=input.dtype), name ='b') # build symbolic expression that computes the convolution of input with filters in w conv_out = conv.conv2d(input, W) # build symbolic expression to add bias and apply activation function, i.e. produce neural net layer output # A few words on ``dimshuffle`` : # ``dimshuffle`` is a powerful tool in reshaping a tensor; # what it allows you to do is to shuffle dimension around # but also to insert new ones along which the tensor will be # broadcastable; # dimshuffle('x', 2, 'x', 0, 1) # This will work on 3d tensors with no broadcastable # dimensions. The first dimension will be broadcastable, # then we will have the third dimension of the input tensor as # the second of the resulting tensor, etc. If the tensor has # shape (20, 30, 40), the resulting tensor will have dimensions # (1, 40, 1, 20, 30). (AxBxC tensor is mapped to 1xCx1xAxB tensor) # More examples: # dimshuffle('x') -> make a 0d (scalar) into a 1d vector # dimshuffle(0, 1) -> identity # dimshuffle(1, 0) -> inverts the first and second dimensions # dimshuffle('x', 0) -> make a row out of a 1d vector (N to 1xN) # dimshuffle(0, 'x') -> make a column out of a 1d vector (N to Nx1) # dimshuffle(2, 0, 1) -> AxBxC to CxAxB # dimshuffle(0, 'x', 1) -> AxB to Ax1xB # dimshuffle(1, 'x', 0) -> AxB to Bx1xA output = T.nnet.sigmoid(conv_out + b.dimshuffle('x', 0, 'x', 'x')) # create theano function to compute filtered images f = theano.function([input], output)

首先我们用得到的函数f做点有意思的事情:

import pylab
from PIL import Image

# open random image of dimensions 639x516
img = Image.open(open('images/3wolfmoon.jpg'))
img = numpy.asarray(img, dtype='float64') / 256.

# put image in 4D tensor of shape (1, 3, height, width)
img_ = img.swapaxes(0, 2).swapaxes(1, 2).reshape(1, 3, 639, 516)
filtered_img = f(img_)

# plot original image and first and second components of output
pylab.subplot(1, 3, 1); pylab.axis('off'); pylab.imshow(img)
pylab.gray();
# recall that the convOp output (filtered image) is actually a "minibatch",
# of size 1 here, so we take index 0 in the first dimension:
pylab.subplot(1, 3, 2); pylab.axis('off'); pylab.imshow(filtered_img[0, 0, :, :])
pylab.subplot(1, 3, 3); pylab.axis('off'); pylab.imshow(filtered_img[0, 1, :, :])
pylab.show()

运行正确应该出现下面的图:

deeplearning----卷积神经网络