虽然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+-1行,第j到j+-1列这一矩形范围内的所有神经元相连接。如下图,是感受野的长,宽。为了使得神经网络的前后两层的长,宽一致,引入零填充,具体做法为输入层的周围填充0。
也可以通过分隔感受野的方法来减少输出的维度,两个连续感受野之间的距离称为步长(stride).对于给定层的第i行,第j列的单个神经元,是与上一层中的第到行,第到列这一矩形范围内的所有神经元相连接。如下图,是垂直,水平步长。如下图,需要注意的是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的原因
- 相当于瓶颈层,用来减少维度。在3X3或者5X5之前使用,因为3X3或者5X5的计算非常复杂。
- ([1 × 1, 3 × 3] and [1 × 1, 5 × 5])可以捕捉更高等级的特征。
inception能够输出 具有不同尺度的复杂模式的feature map (就是把不同的feature map叠加在一起)
注意到 这边的64代表的是输出feature map 的个数。与如下图对应 代表每一层的输出feature map的个数
-
首先前两层的作用为把图片的尺寸缩小,也就是对图片的长和宽同时除以4,也就是总共缩小的16倍。
-
随后的LRN确保之前那层学到了各种各样的特征。
-
随后的两层,第一层的1X1卷积相当于瓶颈层,这两个组合在一起,就相当于一个卷积层。
-
之后的LRN确保之前的那层捕捉到了广泛的特征。
-
随后的pooling同时使得长和宽减少两倍,减少了运算负载。
-
后面的作用都相同
-
平均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
- 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),但是没有增加额外的计算负担,并且没有增加额外的参数。
- tf.layers.conv2d_transpose()对图像进行上采样,通过在输入中插入0,可以想象成常规的卷积,但是使用的分数步长(fractional stride),
- tf.nn.depthwise_conv2d(),创建一个卷积层,把每一个滤波器应用于图像的单个通道中,为特征图的个数。
- tf.layers.separable_conv2d()
Exercises
- 对于分类任务,CNN对于全连接的DNN有什么优势
- 计算参数个数,对单个预测需要多少RAM,50mini-batch需要多少RAM
- 当你训练一个CNN时,GPU遇到内存不足,需要怎么解决。
- 为什么你要使用max pooling,而不是使用与max pooling尺寸相同的stride来代替:pooling没有参数,而使用卷积会有参数,
- 什么时候需要添加local response normalization layer LRN:一个LRN层使 最强烈激活的神经元在同一位置抑制 相邻的特征映射中神经元,这鼓励不同的feature map学习不同的东西并将它们分开,迫使它们探索更广泛的特征范围。被用于较低的层中,使得低层拥有更大的低阶特征,在后面的层中,可以根据这些低阶特征来组合高阶特征。
- AlexNet,相比于LeNet-5,的创新?GoogLeNet 相对于ResNet的创新。