Hands_On_Machine_Learning_with_Scikit_Learn_and_TensorFlow(第13章)

虽然IBM的“深蓝”超级计算机早在1996年就击败了国际象棋世界冠军加里•卡斯•帕洛夫(Garry Kas parov),但就在不久前,计算机还无法可靠地完成一些看似微不足道的任务,比如在图像中发现一只小狗,或识别语音单词。为什么这些任务对我们人类来说如此轻松。答案在于,知觉在很大程度上发生在我们意识之外的领域,在我们大脑中特殊的视觉、听觉和其他感官模块中。当感官信息到达我们的意识时,它已经被高级特征所修饰

卷积神经网络(tional neural networks, CNNs)起源于对大脑视觉皮层的研究,自20世纪80年代以来被用于图像识别。在过去的几年里,由于计算能力的增加,可用的训练数据的数量,以及在第11章中为训练深网所提出的技巧,CNNs已经在一些复杂的视觉任务上已经达到了超人的表现。

The Architecture of the Visual Cortex

 处于视觉皮层的很多神经元都有一个local receptive feld(局部感受野),意味着这些神经元这对受限视觉域的视觉产生刺激。如下图,5个神经元的感受野在下图用虚线表示,不同神经元的感受野会重叠,它们一起铺满了整个视觉场。此外,作者还表明,一些神经元只对水平线的图像作出反应,而另一些神经元只对不同方向的线作出反应(两个神经元可能有相同的感受野,但对不同的线方向作出反应)。其中一些神经元有更大的感受野,这些神经元对更复杂的模式(由低层特征组合)起作用。这种强大的体系结构能够在视觉领域的任何领域中,检测各种复杂的模式。

CNN最重要的组成部分是卷积层:第一个卷积层的神经元不是与输入图像中的每一个像素都连接,只是和它们的感受野内的像素作连接。随后,第二层中每个神经元连接到  第一层中的一块长方形区域中的神经元。这种体系结构允许网络专注于第一个隐藏层的底层特征,然后将它们整合到下一个隐藏层的高阶特征中,以此重复进行。这种分层结构在现实图像中很常见,这也是为什么CNNs在图像识别中如此有效的原因之一。

 对于给定层的第i行,第j列的单个神经元,是与上一层中的第i到i+f_{h}-1行,第j到j+f_{w}-1列这一矩形范围内的所有神经元相连接。如下图f_{h},f_{w}是感受野的长,宽。为了使得神经网络的前后两层的长,宽一致,引入零填充,具体做法为输入层的周围填充0。

 也可以通过分隔感受野的方法来减少输出的维度,两个连续感受野之间的距离称为步长(stride).对于给定层的第i行,第j列的单个神经元,是与上一层中的第i*s_{h}i*s_{h}+f_{h}-1行,第j*s_{w}j*s_{w}+f_{w}-1列这一矩形范围内的所有神经元相连接。如下图s_{h},s_{w}是垂直,水平步长。如下图,需要注意的是i,j是从0开始的。

Filters

左上角的图的垂直白线被强化,其余被弱化(因为滤波器的中就是垂直白线被强化,白色代表1,黑色代表0)。 右上角的水平白色被强化,其余被弱化。

 在训练中,CNN发现了最有用的过滤器,并学会将它们组合在一起以发现更复杂的模式(例如一个十字同时激活水平和垂直滤波器)

Stacking Multiple Feature Maps

 在实际中,每一层的卷积层是由相同尺寸的特征图共同叠加而成。feature map中的所有神经元都共享相同的参数,这一事实极大地减少了模型中的参数数量,但最重要的是,这意味着一旦CNN学会在一个位置识别一个模式,它就可以在任何其他位置识别它。

 一个卷积的输出为

TensorFlow Implementation

 在tensorflow中,一张图片被表示为一个3D的tensor[height,width,channels],一个mini-batch表示为一个4D的tensor[nimi-batch size,height,width,channels]。权重也为一个4D的tensor,bias为一个1D的tensor

其中tf.nn.conv2d()这个函数中,

  • ‘valid’:不使用0填充,并且会基于stide忽略底部或者右边的某些数值,
  • 'same':卷积会自动的使用0填充,输出的神经元数等于 :输入神经元数/stride,使得这个式子一定会被整除。在下面例子中ceil (13 / 5) = 3,然后在输入周围尽可能均匀地添加0。

在tensorflow中,使用 tf.layers.conv2d()函数可以会自动的为你创建variable(kernel),随机初始化,也同样会创建bias,0初始化。如下

X = tf.placeholder(shape=(None, height, width, channels), dtype=tf.float32)
conv = tf.layers.conv2d(X, filters=2, kernel_size=7, strides=[2,2],
                        padding="SAME")

 Memory Requirements

卷积需要大量的RAM, 特别是训练的时候,因为在反向传播的时候需要前向传播中的中间值。

假设使用5X5的滤波器,输出为200个150X100的feature map,stride=1,使用“same”。输入为150X100的RGB图,那么总的参数个数为(5X5X3+1)X200=15200,假设输出的feature map是用32-bit的float来表示,那么一层卷积的输出会占据200X150X100X32=96 million bits(11.4M,1 MB = 1,024 kB = 1,024 × 1,024 bytes = 1,024 × 1,024 × 8 bits),这只是一个例子的情况。

其实:在推理阶段(进行预测),只要下一层的值被计算出来,那么上一层所占用的RAM会自动释放,所以只需要支持两个连续卷积层的RAM,但是在训练中,需要每一层的值,所以总的RAM至少也是每一层需要的RAM的累和。

假如由于内存不足而造成训练崩溃,可以通过减少mini-batch,或者使用stride减少维度,或者去掉一些层。或者用16-bit的float来代替32-bit的float。或者你可以把CNN放在多个device中。、

Pooling Layer

 pooling可以减少计算工作量,内存的使用,以及参数的数量(可以减少过拟合的风险)。

pooling的使用

pooling非常具有破坏性:假设使用一个2x2的kernel,stride=2,那么输出在每一个维度都会减少两倍(所以总的会减少4倍),每一个维度都减少2倍会把水平像素值减少75%,垂直像素值减少75%,也就是说每一个方向上都减少75%的值 。

pool单独应用于输入的每一个通道,所以输出的通道数和输入的通道数是一样的,以下代码为pooling代码

batch_size, height, width, channels = dataset.shape

filters = np.zeros(shape=(7, 7, channels, 2), dtype=np.float32)
filters[:, 3, :, 0] = 1  # vertical line
filters[3, :, :, 1] = 1  # horizontal line
X = tf.placeholder(tf.float32, shape=(None, height, width, channels))
max_pool = tf.nn.max_pool(X, ksize=[1,2,2,1], strides=[1,2,2,1],padding="VALID")
####ksize为pooling的尺寸,对应于输入的X的4个[batch size, height, width, channels]
with tf.Session() as sess:
    output = sess.run(max_pool, feed_dict={X: dataset})

plt.imshow(output[0].astype(np.uint8))  # plot the output for the 1st image
plt.show()

 左图为经过pooling之后的图,右图为未经过pooling的图。可以看到坐标相对于有图,减少一倍。

 ksize为pooling的尺寸,对应于输入的X的4个维度[batch size, height, width, channels],tensorflow暂时不支持pooling多个例子,所以第一个batch-size必须为1,并且也不支持平面维度于channel维度一起作pooling,所以要么ksize[1] 和 ksize[2]都为1,要么ksize[3]为1.

使用平均pooling,只需要把max_pool()函数替换成avg_pool()。

CNN Architectures

 不能把卷积核的尺寸弄得过大,使用一个9X9的卷积核对比使用2个3X3的卷积核,效果是一样的,但是后者可以减少计算和参数。

LeNet-5

以上结构是用来处理MNIST,MNIST图片为28X28,被填充为32X32,在输入进神经网络时,使用归一化。

average pooling与常规的有所不同,在算出均值之后,乘以一个可以学习的系数(一个特征图(map)一个系数),在添加一个可以学习的bias项(也是一个特征图(map)一个) ,然后在进行activation

C3这一层只使用了S2这一层的4个map,而不是6个。

AlexNet

 

在F8和F9中,使用了比率为50%的dropout,并使用了数据增强。

在C1,C3层之后,使用RELU之后,进行规范化,称为local response normalization(LRN)。 这种形式的规范使  最强烈激活的神经元在相同的位置中  抑制处于相邻的特征图中神经元。可以使用tf.nn.local_response_normalization()来完成。

For example, if r = 2 and a neuron has a strong activation, it will inhibit the activation of the neurons located in the feature maps immediately above and below its own.
 在ALEXNET中,通常的设置为:r = 2, α = 0.00002, β = 0.75, and k= 1

GoogLeNet

googlenet比上面的两种要深,是因为它的子网结构,名为inception模块,这些模块使得网络能够有效的使用参数,所以能有效的减少参数的个数。下图中3 × 3 +2(S)意味着3X3的卷积核,2的stride,same的padding
 

因为图中所有的块都是使用stride=1,same的情况,那么使得所有的特征图的长宽都一致。最后使用tf.concat()函数,指定axis=3来进行连接,在通道维度进行叠加。

使用1X1的原因

  1. 相当于瓶颈层,用来减少维度。在3X3或者5X5之前使用,因为3X3或者5X5的计算非常复杂。
  2. ([1 × 1, 3 × 3] and [1 × 1, 5 × 5])可以捕捉更高等级的特征。

inception能够输出 具有不同尺度的复杂模式的feature map (就是把不同的feature map叠加在一起)

注意到 这边的64代表的是输出feature map 的个数。与如下图对应 代表每一层的输出feature map的个数

  1. 首先前两层的作用为把图片的尺寸缩小,也就是对图片的长和宽同时除以4,也就是总共缩小的16倍。

  2. 随后的LRN确保之前那层学到了各种各样的特征。

  3. 随后的两层,第一层的1X1卷积相当于瓶颈层,这两个组合在一起,就相当于一个卷积层。

  4. 之后的LRN确保之前的那层捕捉到了广泛的特征。

  5. 随后的pooling同时使得长和宽减少两倍,减少了运算负载。

  6. 后面的作用都相同

  7. 平均pooling使用和特征图相同尺寸的 kernel,输出为1X1的feature map,这种策略叫做global average pooling(全局平均池化),这种方法有效的强迫之前的所有层来产生    对每一个目标类的confidence(信心) maps(来表明这个类属于这个类)confidence maps就是feature map  ,这种方法有效是因为:其他类型的feature将会被平均这种操作破坏。使用这种方法之后就不需要添加多个全连接层(像ALEXNET一样),这样就减少了参数的个数以及过拟合的风险。

上面图中是被简化过的,在原始的图中,在第3个和第6个inception之上,还带着两个辅助分类器。 由one average pooling layer, one convolutional layer, two fully connected layers, and a softmax activation layer组成。在训练中,这两个分类器的损失被缩放70%放在总的损失当中,添加辅助层的作用为:对抗梯度消失问题以及正则网络。事实上,它们的影响很小。

ResNet

 使得网络能够如此深的关键在于:使用了skip connections(shortcut connections),将信号输入到一个层中,也会被添加到位于更高位置的层的输出中。

假如你训练一个神经网络,就是使神经网络模拟一个目标函数,如果把输入x添加到神经网络的输出上,也就是创建跳跃连接。如图。

假如你初始化一个常规的神经网络,那么它的权重比较接近于0,导致神经网络输出的数值也相对接近于0。假如你添加一个跳跃连接,那么神经网络输出的基本就是它的输入。换句话说,最开始,神经网络模拟的是一个恒等函数(identity function),如果目标函数与恒等函数相当接近(大多数都是这种情况),通常能加速训练。

即使中间的层没有学到东西,残差网络也能使用进行预测,如下图

因为跳跃连接,信号可以轻易的穿过整个网络。 这个残差网络的结构如下。

在如下卷积的过程中,把stride的参数调为2(下图黑色的字体),可以使得长和宽都减半。当这种情况发生的时候,输入和输出的维度不能匹配,所以导致不能简单的相加,为了克服这种情况,使输入经过一个stride=2的1X1的卷积,这样维度就能对应,能够进行相加。

ResNet-34是包含34层的ResNet(只计算convolutional layers 和the fully connected layer) ,包含有3个输出64个feature map的残差块,4 RUs with 128 maps, 6 RUs with 256 maps, and 3 RUs with 512 maps(RU表示残差块)。

ResNet-152和上面的有稍稍的不同,不是使用两个3X3的卷积层带着256个feature map(256是假设的),而是使用3个卷积层,第一个卷积层为1X1的卷积层带着64个特征图,然后3X3卷积层带着64的特征图,最后一个1X1的卷积层带着256个特征图(恢复原始的通道数)。ResNet-152 contains three such RUs that output 256 maps, then 8 RUs with 512 maps, a whopping 36 RUs with 1,024 maps, and finally 3 RUs with 2,048 maps
神经网络的趋势是变得越来越深,同时参数越来越少

https://blog.csdn.net/mao_xiao_feng/article/details/78003476 

dilation https://blog.csdn.net/Quincuntial/article/details/78743033

  1. tf.nn.atrous_conv2d() creates an atrous convolutional layer (“à trous” is Frenchfor “with holes”).黑色为0,白色为0,插入0代表插入白色,相当于添加孔。For example, a 1× 3 filter equal to [[1,2,3]] may be dilated with a dilation rate of 4,resulting in a dilated flter [[1, 0, 0, 0, 2, 0, 0, 0, 3]].能够使得卷积层获得更大的感受野(receptive field),但是没有增加额外的计算负担,并且没有增加额外的参数。
  2. tf.layers.conv2d_transpose()对图像进行上采样,通过在输入中插入0,可以想象成常规的卷积,但是使用的分数步长(fractional stride),
  3. tf.nn.depthwise_conv2d(),创建一个卷积层,把每一个滤波器应用于图像的单个通道中,为特征图的个数。

  4. tf.layers.separable_conv2d()

  5.  

 Exercises

  1. 对于分类任务,CNN对于全连接的DNN有什么优势
  2.  计算参数个数,对单个预测需要多少RAM,50mini-batch需要多少RAM
  3. 当你训练一个CNN时,GPU遇到内存不足,需要怎么解决。
  4. 为什么你要使用max pooling,而不是使用与max pooling尺寸相同的stride来代替:pooling没有参数,而使用卷积会有参数, 
  5. 什么时候需要添加local response normalization layer LRN:一个LRN层使    最强烈激活的神经元在同一位置抑制   相邻的特征映射中神经元,这鼓励不同的feature map学习不同的东西并将它们分开,迫使它们探索更广泛的特征范围。被用于较低的层中,使得低层拥有更大的低阶特征,在后面的层中,可以根据这些低阶特征来组合高阶特征。
  6. AlexNet,相比于LeNet-5,的创新?GoogLeNet 相对于ResNet的创新。

  7.  

猜你喜欢

转载自blog.csdn.net/Gussss/article/details/81296078