卷积神经网络

卷积神经网络(Convolutional Neural Network, CNN)是一种前馈神经网络,它的人工神经元可以响应一部分覆盖范围内的周围单元,对于大型图像处理有出色表现。卷积神经网络由一个或多个卷积层和顶端的全连通层(对应经典的神经网络)组成,同时也包括关联权重和池化层(pooling layer)。这一结构使得卷积神经网络能够利用输入数据的二维结构。与其他深度学习结构相比,卷积神经网络在图像和语音识别方面能够给出更优的结果。相比较其他深度、前馈神经网络,卷积神经网络需要估计的参数更少,使之成为一种颇具吸引力的深度学习结构。

卷积神经网络的核心思想

  1. 局部感受野:普通的多层感知器中,隐层节点会全连接到一个图像的每个像素点上;而在卷积神经网络中,每个隐层节点只连接到图像某个足够小局部的像素点上,从而大大减少需要训练的权值参数。比如1000×1000的图像,使用10×10的感受野,那么每个神经元只需要100个权值参数;不过由于需要将输入图像扫描一遍,共需要991×991个神经元(参数数目减少了一个数量级,不过还是太多)。
  2. 权值共享:在卷积神经网中,同一个卷积核内,所有的神经元的权值是相同的,从而大大减少需要训练的参数。继续前面的例子,虽然需要991×991个神经元,但是它们的权值是共享的,所以只需要100个权值参数,以及1个偏置参数。作为补充,在CNN中的每个隐藏,一般会有多个卷积核。
  3. 池化:在卷积神经网络中,没有必要一定就要对原图像做处理,而是可以使用某种“压缩”方法,这就是池化,也就是每次将原图像卷积后,都通过一个下采样的过程,来减小图像的规模。以最大池化(Max Pooling)为例,1000×1000的图像经过10×10的卷积核卷积后,得到的是991×991的特征图,然后使用2×2的池化规模,即每4个点组成的小方块中,取最大的一个作为输出,最终得到的是496×496大小的特征图。

Multilayer Perceptron (MLP, 多层感知机)

感知机只能解决线形可分问题,但对非线形可分问题(比如简单的异或)就无能为力了。通过将多个感知器按照一定的结构和系数进行组合,就构成了多层感知机。多层感知机层与层之间是全连接的(全连接的意思就是:上一层的任何一个神经元与下一层的所有神经元都有连接)。多层感知机最底层是输入层,中间是隐藏层,最后是输出层。

多层感知器存在的问题:

  1. 权值多:它是一个全连接的网络,在输入比较大的时候,权值会特别多。比如一个有1000个节点的隐层,连接到一个1000×1000的图像上,那么就需要 10^9 个权值参数(外加1000个偏置参数)!这一方面限制了每层能够容纳的最大神经元数目,另一方面也限制了多层感知器的层数即深度。
  2. 梯度发散:一般情况下,我们需要把输入归一化,而每个神经元的输出在激活函数的作用下也是归一化的;另外,有效的参数其绝对值也一般是小于1的;这样,在BP过程中,多个小于1的数连乘,得到的会是更小的值。也就是说,在深度增加的情况下,从后传播到前边的残差会越来越小,甚至对更新权值起不到帮助,从而失去训练效果,使得前边层的参数趋于随机化。

CNN结构

每个层次的详细说明见这里.

Convolution (卷积,局部感受野)

对于离散函数$$f(x,y), g(x,y)$$的卷积一般定义为

$$f(m, n)*g(m, n) = \sum_u^\infty \sum_v^\infty {f(u, v)g(m - u, n - v)}$$

对应的,图像的卷积定义为

$$f(x) = act(\sum_{i, j}^n \theta_{(n - i)(n - j)} x_{ij} + b)$$

其计算过程为

卷积的动态展示:

由于特征图的变长不一定是2的倍数,所以在边缘处理上也要注意:

import tensorflow as tf

input_batch = tf.constant([
    [  # First Input
        [[0.0], [1.0]],
        [[2.0], [3.0]]
    ],
    [  # Second Input
        [[2.0], [4.0]],
        [[6.0], [8.0]]
    ]
])

kernel = tf.constant([
    [
        [[1.0, 2.0]]
    ]
])

# strides parameter highlights how a convolution operation
# is working with a kernel.
# The data format has to be [batch_size, height, width, channel]
conv2d = tf.nn.conv2d(input_batch,
                      kernel,
                      strides=[1, 1, 1, 1],
                      padding='SAME')
with tf.Session() as sess:
    print sess.run(conv2d)

# output ==>
# [[[[  0.   0.]
#    [  1.   2.]]
#
#   [[  2.   4.]
#    [  3.   6.]]]
#
#
#  [[[  2.   4.]
#    [  4.   8.]]
#
#   [[  6.  12.]
#    [  8.  16.]]]]


input_batch = tf.constant([
    [  # First Input (6x6x1)
        [[0.0], [1.0], [2.0], [3.0], [4.0], [5.0]],
        [[0.1], [1.1], [2.1], [3.1], [4.1], [5.1]],
        [[0.2], [1.2], [2.2], [3.2], [4.2], [5.2]],
        [[0.3], [1.3], [2.3], [3.3], [4.3], [5.3]],
        [[0.4], [1.4], [2.4], [3.4], [4.4], [5.4]],
        [[0.5], [1.5], [2.5], [3.5], [4.5], [5.5]],
    ],
])

kernel = tf.constant([  # Kernel (3x3x1)
    [[[0.0]], [[0.5]], [[0.0]]],
    [[[0.0]], [[1.0]], [[0.0]]],
    [[[0.0]], [[0.5]], [[0.0]]]
])

# NOTE: the change in the size of the strides parameter.
conv2d = tf.nn.conv2d(input_batch,
                      kernel,
                      strides=[1, 3, 3, 1],
                      padding='SAME')
with tf.Session() as sess:
    print sess.run(conv2d)

# output ==>
# [[[[ 2.20000005]
#    [ 8.19999981]]

#   [[ 2.79999995]
#    [ 8.80000019]]]]

Pooling(池化, 下采样)

通过卷积层获得了图像的特征之后,理论上我们可以直接使用这些特征训练分类器(如softmax),但是这样做将面临巨大的计算量的挑战,而且容易产生过拟合的现象。为了进一步降低网络训练参数及模型的过拟合程度,对卷积层进行池化/采样(Pooling)处理。池化/采样的方式通常有以下两种:

  1. 最大池化(Max Pooling: 选择Pooling窗口中的最大值作为采样值;
  2. 均值池化(Mean Pooling): 将Pooling窗口中的所有值相加取平均,以平均值作为采样值
  3. 高斯池化:借鉴高斯模糊的方法。不常用。
  4. 可训练池化:使用一个训练函数$y=f(x)$。不常用。

图像经过池化后,得到的是一系列的特征图,而多层感知器接受的输入是一个向量。因此需要将这些特征图中的像素依次取出,排列成一个向量(这个过程被称为光栅化)。

CNN Summary

参考文档