吴恩达深度学习笔记之卷积神经网络(实例探究)

2.1 为什么要进行实力探究(why look case studies)

这周我们来看看卷积神经网络的实例分析。
为什么要看这些案例分析呢?上周我们讲了基本构建,比如卷积层,池化层以及全连接层这些组件,事实上,过去几年,计算机视觉中的大量研究都集中在如何把这些基本构建组合起来,形成有效的卷积神经网络,找感觉最好的方法之一是看一些案例。
实际上,在计算机视觉中表现良好的神经网络框架往往也适用于其他任务。也就是说如果有人已经训练或者计算出擅长识别猫,狗,人的神经网络,或者框架。而我们的任务是构建一个自动驾驶汽车,那么我们完全可以借鉴别人的神经网络框架解决自己的问题。

下面来看几个经典的网络:

LeNet-5, AlexNet,VGG以及ResNet。其中ResNet又称为残差网络,他高达152层,并且在如何有效训练方面总结出了一些有趣的想法和窍门,课程最后还会讲一个Inception神经网络的实例分析。

2.2 经典网络(Classic networks)

这节将介绍几个经典的神经网络结构,分别为LeNet-5,AlexNet和VGG net。

LeNet-5

我们首先来看看leNet-5的网络结构:

这里写图片描述

由上图可知LeNet-5结构为:卷积——池化——卷积——池化——全连接——全连接。
注:(1)在这篇论文写成的年代里,人们更喜欢使用平均池化,而现在我们可能使用最大池化更多一点。(2)因为论文是在1998年撰写的,当时人们并不使用padding或者有效卷积,所以每进行一次卷积,图像的高度和宽度都会缩小。(3)在现在的版本中输出层使用的是softmax函数输出十种分类结果,而在当时,LeNet-5网络在输出层使用另外一种,现在很少用。
不管如何,如果我们从左往右看,随着网络越来越深,图像的高度和宽度都在缩小,信道数量一直在增加。这个网络中还有一种模式至今仍然使用,那就是一个或者多个卷积层后面跟着一个池化层,然后又是若干个卷积,池化,然后是全连接层,最后是输出。

AlexNet

这里写图片描述

AlexNet要比LeNet-5大得多,LeNet-5只有大约6万个参数,而AlexNet大约有6000万个参数,当用于训练图像和数据集时,AlexNet能够处理非常相似的基本构造模块,这些模块往往包含大量的隐藏单元或数据,这一点另AlexNet表现出色,其次,AlexNet比LeNet-5更为出色的原因是它使用了Relu激活函数。最后经典AlexNet结构还有另一种类型的层叫做“局部响应归一化层”即LRN(Local Response Normalization)。

VGG-16

这里写图片描述

VGG-16是一种只专注于构建圈基层的简单网络。VGG-16总共包含约1.38亿个参数,但是VGG-16的结构并不复杂,都是几个卷积层后面跟着池化层,可以压缩图像大小的池化层,同时卷积层的过滤器数量存在一定的规律。
VGG-16揭示了随着网络的加深,图像的高度和宽度都在以一定的规律不断缩小,每次迭代后刚好缩小一半,而信道数量在不断增加,而且刚好也是在每组卷积操作后增加一倍,也就是说,图像缩小的比例和信道增加的比例是有规律的。

2.3 残差网络(ResNets)

非常深的网络是很难训练的,因为存在梯度消失和梯度爆炸问题,这一节,我们将学习跳远连接,他可以从某一网络层获取激活,然后迅速反馈给另外一层,甚至是神经网络的更深层,ResNets是由残差块构造的。残差块如下:

这里写图片描述

与普通块不同,残差块的信息流直接从 a[l] a[l+2] 这里这个等式去掉了 a[l+2]=g(z[l+2]) ,取而代之的是另一个Relu非线性函数 a[l+2]=g(z[l+2]+a[l]) 也就是加上的这个 a[l] 产生的残差块。(在上图中,我们也可以画一条捷径,直达第二层,实际上这条捷径是在进行Relu非线性激活之前加上的,而这里的每一个节点都执行了线性函数和Relu激活函数)
注意: a[l] 插入的时机是在线性激活之后,Relu激活之前,有时除了捷径,还有另一个属于“跳远连接”就是指 a[l] 跳过一层或者几层。从而将信息传递到神经网络的更深层。
接下来我们来看看残差网络的结构。如下所示:

这里写图片描述

残差网络是将一个普通网络加上跳远连接形成的。
凭经验,我们会觉得随着网络深度的加深,训练错误会减少,然后加深,而理论上,随着网络的加深,应该训练得越好才对,但实际上,如果没有残差网络,对于一个普通网络来说,深度越深,用优化算法越难训练,实际上随着网络加深,训练错误会越来越多,但是有了ResNets就不一样了,即使网络再深,训练的表现也不错。
以下是普通网络和残差网络的损失函数图

这里写图片描述

2.4 残差网络为什么有用?(why resnets work)

上一节,我们了解到,一个网络深度越深,它在训练集上训练网络的效率会有所减弱,这也是有时候我们不希望加深网络的原因,而事实并非如此,至少在训练ResNet网络时,并不完全如此。举个例子:假设有一个大型神经网络,用bigNN表示,输出激活值为 a[l] ,如果想增加这个神经网络的深度,再给这个网络额外增加两层,最后输出为 a[l+2]

这里写图片描述

可以将后两层看作一个残差块,为了方便说明,假设我们在整个网络中使用Relu激活函数,所有激活值都大于等于0,包括输入x的非零异常值,因为relu激活函数输出的数字要么是0,要么是正数,我们看一下 a[l+2] 的值,即 a[l+2]=g(z[l+2]+a[l]) ,添加项 a[l] 是刚添加的跳远连接的输入,展开这个表达式, a[l+2]=g(w[l+2]a[l+1]+b[l+2]+a[l]) ,注意一点,如果使用L2正则化或权重衰减,它会压缩 w[l+2] 的值,如果对b进行权重衰减,也可以达到同样的效果。这里的w是关键项。如果 w[l+2] =0,为方便起见,假设 b[l+2] =0,此时 a[l+2] = a[l] ,结果表明,残差块学习这个恒等式并不难,跳远连接使我们很容易得出 a[l+2] = a[l] ,这意味这即使神经网络增加了两层,他的效率并不逊色于更简单的神经网络。
残差网络起作用的主要原因在于残差块学习恒等式非常容易,此时网络性能不受影响,甚至可以提高效率。除此之外,另一个值得探讨的问题是假设 z[l+2] a[l] 具有相同维度,所以ResNets使用了许多相同卷积,所以 a[l] 的维度等于输出层的维度,因为实现了跳远连接。
最后,我们来看看ResNets的图片识别,把普通网络转化为ResNets只需要添加跳远连接,此外ResNets使用1*1过滤器,这个想法很有意思,为什么呢?

普通网络:

这里写图片描述

ResNets:

这里写图片描述

2.5 网络中的网络以及1*1卷积(network in network and 1*1 convolutions)

1*1卷积所实现的功能是用来压缩信道,1*1卷积只是添加了非线性函数,从而减少或保持输入层中信道数量不变,通过1*1卷积的简单操作可以压缩或保持甚至增加信道数量。
如图所示:

这里写图片描述

2.6 谷歌Inception网络简介

构建卷积层时,我们要决定过滤器的大小,以及要不要添加池化层,而Inception网络的作用就是代替我们来做决定,虽然因此网络架构会变得更复杂,但网络表现却非常好。
Inception网络的基本思想是不需要人为决定使用哪个过滤器,或是否需要池化,而是由网络自行决定这些参数,我们可以给网络添加这些参数的所有可能值,然后把这些输出连接起来,让网络自己学习它需要什么样的参数。
如下所示:

这里写图片描述

2.7 Inception 网络(Inception network)

我们首先来看看Inception 模块的结构,如下所示:

这里写图片描述

Inception模块会将之前的激活或者输出作为它的输入。在这里我们主要讲一些重点的细节,为了能在最后,将所有的输出连接起来,我们会使用same类型的padding来池化。其次,我们需要加上一个1*1卷积,将通道的数量缩小到28*28*32,也就是使用了32个1*1*192的过滤器,这样就避免最后输出时,池化层占据所有的通道,最后讲这些方块连接起来,在这个过程中,把得到的各个层的通道都加起来,最后得到28*28*256的输出,这就是一个Inception模块,而Inception网络就是将这些模块都组合在一起,如下为一个Inception网络。

这里写图片描述

在该网络的论文中其实还存在这样的细节,即存在一些分支,这些分支通过隐藏层做出预测,其实就是一个sofamax输出,他确保了隐藏单元和中间层也参与了特征计算,他们能预测图片的分类,它在Inception网络中起到一种调整的效果,并且能防止过拟合。如下所示:

这里写图片描述

2.8 数据增强(Data augmentation)

大部分的计算机视觉任务都需要使用很多的数据,所以数据增强是一种经常使用的技巧来提高计算机视觉系统的表现。
常用的数据增强方法有,垂直镜像对称,随机裁剪,以及旋转,扭曲变换,还有色彩转换等。

垂直镜像对称:

这里写图片描述

随机裁剪:

这里写图片描述

色彩转换:给定一张图片,然后给 R,G,B三通道加上不同的失真值。

这里写图片描述

最后,我们举一个例子,下面是一个基于tensorflow框架,加载VGG-19网络进行特征提取的实例代码,在这里需要先加载VGG模型,模型下载链接如下:[链接: https://pan.baidu.com/s/1hsQtXVQ 密码: tvbb]
代码如下:

import scipy.io
import numpy as np
import os
import scipy.misc
import matplotlib.pyplot as plt
import tensorflow as tf
def _conv_layer(input, weights, bias):
    conv = tf.nn.conv2d(input, tf.constant(weights), strides=(1, 1, 1, 1),
            padding='SAME')
    return tf.nn.bias_add(conv, bias)
def _pool_layer(input):
    return tf.nn.max_pool(input, ksize=(1, 2, 2, 1), strides=(1, 2, 2, 1),
            padding='SAME')
def preprocess(image, mean_pixel):
    return image - mean_pixel
def unprocess(image, mean_pixel):
    return image + mean_pixel
def imread(path):
    return scipy.misc.imread(path).astype(np.float)
def imsave(path, img):
    img = np.clip(img, 0, 255).astype(np.uint8)
    scipy.misc.imsave(path, img)
print ("Functions for VGG ready")

def net(data_path, input_image):
    layers = (
        'conv1_1', 'relu1_1', 'conv1_2', 'relu1_2', 'pool1',
        'conv2_1', 'relu2_1', 'conv2_2', 'relu2_2', 'pool2',
        'conv3_1', 'relu3_1', 'conv3_2', 'relu3_2', 'conv3_3',
        'relu3_3', 'conv3_4', 'relu3_4', 'pool3',
        'conv4_1', 'relu4_1', 'conv4_2', 'relu4_2', 'conv4_3',
        'relu4_3', 'conv4_4', 'relu4_4', 'pool4',
        'conv5_1', 'relu5_1', 'conv5_2', 'relu5_2', 'conv5_3',
        'relu5_3', 'conv5_4', 'relu5_4'
    )
    data = scipy.io.loadmat(data_path)
    mean = data['normalization'][0][0][0]
    mean_pixel = np.mean(mean, axis=(0, 1))
    weights = data['layers'][0]
    net = {}
    current = input_image
    for i, name in enumerate(layers):
        kind = name[:4]
        if kind == 'conv':
            kernels, bias = weights[i][0][0][0][0]
            # matconvnet: weights are [width, height, in_channels, out_channels]
            # tensorflow: weights are [height, width, in_channels, out_channels]
            kernels = np.transpose(kernels, (1, 0, 2, 3))
            bias = bias.reshape(-1)
            current = _conv_layer(current, kernels, bias)
        elif kind == 'relu':
            current = tf.nn.relu(current)
        elif kind == 'pool':
            current = _pool_layer(current)
        net[name] = current
    assert len(net) == len(layers)
    return net, mean_pixel, layers
print ("Network for VGG ready")

cwd = os.getcwd()
VGG_PATH = cwd + "/data/imagenet-vgg-verydeep-19.mat"
IMG_PATH = cwd + "/data/resistance.jpg"
input_image = imread(IMG_PATH)
shape = (1, input_image.shape[0], input_image.shape[1], input_image.shape[2])
with tf.Session() as sess:
    image = tf.placeholder('float', shape=shape)
    nets, mean_pixel, all_layers = net(VGG_PATH, image)
    input_image_pre = np.array([preprocess(input_image, mean_pixel)])
    layers = all_layers  # For all layers
    # layers = ('relu2_1', 'relu3_1', 'relu4_1')
    for i, layer in enumerate(layers):
        print("[%d/%d] %s" % (i + 1, len(layers), layer))
        features = nets[layer].eval(feed_dict={image: input_image_pre})

        print(" Type of 'features' is ", type(features))
        print(" Shape of 'features' is %s" % (features.shape,))
        # Plot response
        if 1:
            plt.figure(i + 1, figsize=(10, 5))
            plt.matshow(features[0, :, :, 0], cmap=plt.cm.gray, fignum=i + 1)
            plt.title("" + layer)
            plt.colorbar()
            plt.show()

猜你喜欢

转载自blog.csdn.net/liushao123456789/article/details/78837508
今日推荐