利用Theano理解深度学习——Convolutional Neural Networks

注:本系列是基于参考文献中的内容,并对其进行整理,注释形成的一系列关于深度学习的基本理论与实践的材料,基本内容与参考文献保持一致,并对这个专题起名为“利用Theano理解深度学习”系列,若文中有任何问题欢迎咨询。本文提供PDF版本,欢迎索取。

“利用Theano理解深度学习”系列分为44个部分,其中第一部分主要包括:

  • 利用Theano理解深度学习——Logistic Regression
  • 利用Theano理解深度学习——Multilayer perceptron
  • 利用Theano理解深度学习——Deep Convolutional Network

一、CNN概述

卷积神经网络(Convolutional Neural Networks, CNN)是多层感知机MLP模型的一个变种,主要是受到生物学的启发。从Hubel和Wiesel早期的有关猫的视觉皮层的工作中我们知道猫的视觉皮层包含了一个复杂的细胞排列。这些细胞对小范围的视觉区域敏感,这样的小范围的视觉区域称为一个感受野(receptive field)。这些小的区域平铺开来可以盖满整个视觉区域。这些细胞就像是一个个的局部过滤器,可以对输入空间的信息进行过滤,并且非常适合在自然图像中发觉在空间上存在极强的局部相关性。

两种基本类型的细胞已经被确认:1、简单的细胞。这些细胞在他们的感受野中最大化的响应特定的类似边缘的模式;2、复杂的细胞。这些细胞拥有较大的感受野,而且对于确切的位置模式是局部不变的。

在现实世界中,动物的视觉皮层是最强大的视觉处理系统,似乎模仿其行为也变得很自然。因此,许多受神经系统启发的模型被发现,如NeoCognitron,HMAX和LeNet-5。

二、CNN的特点

卷积神经网络CNN的特点主要有:1、稀疏连接(局部感受),2、共享权值,3、子采样。其中,稀疏连接主要是通过对数据中的局部区域进行建模以发现局部的一些特性;共享权值的目的为了简化计算的过程,使得需要优化的参数变少;子采样的目的是在解决图像中的平移不变性,即所要识别的内容与其在图像中的具体位置无关。

1、稀疏连接(Sparse Connectivity)

CNN通过在邻接层的神经元之间使用局部连接模式来发现输入特征在空间上存在的局部相关性。如下图所示,第mm层的隐层节点的输入是第m−1m-1层的神经单元的一个子集,这个子集中的神经单元在空间上是连续的,具有连续的感受野,如下图所示:

假设第m−1层是输入层。在上图中,第m层的神经单元具有在输入层上宽度为3的感受野,因此只连接输入层上的3个邻接神经元。在m+1层的神经单元与第m层的神经元之间具有相似的连接特性,即第m+1层的神经单元在第mm层上的感受野的宽度也是3,但是他们关于输入层m−1层的感受野却变大了,如上图中为5。每一个单元对于感受野范围之外的其他的神经单元是没有反应的。这样的设计架构就保证了学习到的“过滤器”能够对局部空间上的输入产生最强烈的反应。

然而,正如上面所示,堆叠如此多的层导致这些非线性的“过滤器”变得急剧的“全局化”,例如,对更大的像素空间区域产生响应。在m+1个隐含层单元上可以对宽度为55的非线性特征进行编码。

2、共享权值(Shared Weights)

在CNN中,每一个过滤器hih_i在整个感受野中重复。这些重复的单元共享同样的参数(权重向量和偏置),然后形成一个特征映射。

在上图中有3个隐含层单元,这些隐含层单元都隶属于同样的特征映射。在上图中,对于同样颜色的特征映射的权重是共享的。梯度下降同样可以被应用于求解这些共享的参数,只是需要对原始的算法进行稍微的修改。共享的权重的梯度只是简单的将每一个共享的权重的梯度相加。

每一个单元重复这样的方法。此外,权值共享可以极大的缩减需要学习的参数的数量,这样就可以加速学习的速度。在模型上的这个约束可以使得CNN在视觉问题上可以获得更好的泛化能力。

3、池化(pooling)

在CNN中,另一个比较重要的概念是池化,在这里使用的是最大池化max-pooling,这是非线性下采样的一种形式。max-pooling将输入图像划分成为一系列的不重叠的正方形区域,然后对于每一个子区域,输出其中的最大值。

对于机器视觉而言,max-pooling的策略是非常有效的,主要有两个原因:

  1. 降低计算量。通过消除非最大值(选择最大值的操作),为上层降低了计算量。
  2. 解决了平移不变性。max-pooling提供了一种变换不变性的形式。假设在卷积层的后面串联一个max-pooling层,对于输入图像中的每一个像素点的位置,与其邻接的有8个方向可以变换,如果max-pooling采用的是2×2大小的正方形区域,在这88种可能的结构中,只要有3种就能产生与卷积层的输出一样的结果,如果在max-pooling层使用3×3大小的正方形区域,则只需要5种就能产生于卷积层的输出一样的结果。因为在max-pooling层对位置信息提供了额外的鲁棒性,max-pooling的方法是在中间表示的过程中降低维度的一种极其有效的方法。

三、CNN的详细说明和符号描述

特征映射的过程是通过对整个图像的局部区域重复应用一个函数得到的,即对于一个输入图像进行卷积操作指的是利用一个线性过滤器,加上一个偏置项,之后再利用一个非线性函数。

四、LeNet

稀疏连接,卷积操作和最大池化是LeNet型的卷积神经网络模型的关键,然而,对于模型的具体细节会差别很大。下图展示的是LeNet模型:

下层主要是由卷积层和最大池化层交替形成的。上层是全连接的,对应为传统的MLP(即隐含层+Logistic回归)。第一个全连接层的输入是所有特征平铺后组成的,这些特征有下层映射得到。

从实现的角度来看,这就意味着哎下层操作的是是44维的张量,为了使得这些张量能够被后续的MLP处理,故徐将其按照指定的大小平铺称为22维的矩阵形式。

五、Tips和Tricks

1、超参数的选择

CNN的训练是比较棘手的问题,因为相比较于MLP来说,CNN比标准的MLP有更多的超参数。一个经验的法则是:对于学习率和正则化参数通常选择常数,这在优化CNN的过程中要记住。

2、卷积层的过滤器的个数

3、卷积层的过滤器的大小

对于卷积层的过滤器的大小,一般取决于具体的数据集。对于数据集MNIST,其大小为28×28,在该数据集上的最好的结果的过滤器大小为:第一层是5×5,然而对于自然图像,这些图像的像素点的维度要高于数据集MNIST的图像的大小,一般更趋向于在第一层取更大的过滤器大小,如12×12或者15×15。

因此,对于卷积层的过滤器的大小的选择一般是根据给定的数据集中图像的大小而决定的。

4、池化层的最大池化的大小

在池化层,池化的目的是利用图像数据固有的特点,在计算的中间过程中降低数据的维度,一般比较常用的最大池化的大小为2×2或者不采用最大池化,即设置为0,对于特别大的输入图像,在低层,可采用4×4的池化大小,但是过大的池化大小会在计算过程中丢弃过多的信息,对模型的准确性会造成影响。

六、Theano实现CNN的代码解析

1、导入数据集

导入数据集的过程与Logistic回归中使用的一致。前面已经比较细致的描述 。代码如下:

def load_data(dataset):
'''导入数据
:type dataset: string
:param dataset: MNIST数据集
'''
    #1、处理文件目录
    data_dir, data_file = os.path.split(dataset)#把路径分割成dirname和basename,返回一个元组
    if data_dir == "" and not os.path.isfile(dataset):
        new_path = os.path.join(
                os.path.split(__file__)[0],#__file__表示的是当前的路径
                ".",
                "data",
                dataset
        )
    if os.path.isfile(new_path) or data_file == 'mnist.pkl.gz':
        dataset = new_path#文件所在的目录

    if (not os.path.isfile(dataset)) and data_file == 'mnist.pkl.gz':
        import urllib
        origin = (
                'http://www.iro.umontreal.ca/~lisa/deep/data/mnist/mnist.pkl.gz'
    )
    print 'Downloading data from %s' % origin
    urllib.urlretrieve(origin, dataset)

    print '... loading data'
    #2、打开文件
    f = gzip.open(dataset, 'rb')# 打开一个gzip已经压缩好的gzip格式的文件,并返回一个文件对象:file object.
    train_set, valid_set, test_set = cPickle.load(f)#载入本地的文件
    f.close()
    '''训练集train_set,验证集valid_set和测试集test_set的格式:元组(input, target)
    其中,input是一个矩阵(numpy.ndarray),每一行代表一个样本;target是一个向量(numpy.ndarray),大小与input的行数对应
    '''

    def shared_dataset(data_xy, borrow=True):
        data_x, data_y = data_xy
        shared_x = theano.shared(numpy.asarray(data_x, dtype=theano.config.floatX), borrow=borrow)
        shared_y = theano.shared(numpy.asarray(data_y, dtype=theano.config.floatX), borrow=borrow)

        return shared_x, T.cast(shared_y, 'int32')#将shared_y转换成整型

    #3、将数据处理成需要的形式
    test_set_x, test_set_y = shared_dataset(test_set)
    valid_set_x, valid_set_y = shared_dataset(valid_set)
    train_set_x, train_set_y = shared_dataset(train_set)

    #4、返回数据集
    rval = [(train_set_x, train_set_y), (valid_set_x, valid_set_y), (test_set_x, test_set_y)]
    return rval

2、建立模型

建立模型阶段主要是定义一个卷积+池化的过程,具体代码如下:

class LeNetConvPoolLayer(object):
    def __init__(self, rng, input, filter_shape, image_shape, poolsize=(2, 2)):
    """
    :type rng: numpy.random.RandomState
    :param rng:用于随机数生成权重 

    :type input: theano.tensor.dtensor4
    :param input:卷积+池化层的输入,是一个4维的张量

    :type filter_shape: tuple or list of length 4
    :param filter_shape: 卷积层过滤器的大小(number of filters, num input feature maps,
                          filter height, filter width)

    :type image_shape: tuple or list of length 4
    :param image_shape: 图像的大小(batch size, num input feature maps,
                         image height, image width)

    :type poolsize: tuple or list of length 2
    :param poolsize: 池化的参数(#rows, #cols)
    """

    assert image_shape[1] == filter_shape[1]
    self.input = input

    #fan_in和fan_out主要用于初始化权重参数
    #卷积层的输入的大小 "num input feature maps * filter height * filter width"
    fan_in = numpy.prod(filter_shape[1:])
    #池化层的输出的大小 "num output feature maps * filter height * filter width" /pooling size
    fan_out = (filter_shape[0] * numpy.prod(filter_shape[2:]) /
               numpy.prod(poolsize))

    #初始化权重
    W_bound = numpy.sqrt(6. / (fan_in + fan_out))
    self.W = theano.shared(
        numpy.asarray(
            rng.uniform(low=-W_bound, high=W_bound, size=filter_shape),
            dtype=theano.config.floatX
        ),
        borrow=True
    )

    #初始化偏置
    b_values = numpy.zeros((filter_shape[0],), dtype=theano.config.floatX)
    self.b = theano.shared(value=b_values, borrow=True)

    #定义卷积层
    conv_out = conv.conv2d(
        input=input,#卷积层的输入
        filters=self.W,#卷积层使用的权重
        filter_shape=filter_shape,#每个过滤器的大小
        image_shape=image_shape
    )

    #定义下采样层
    pooled_out = downsample.max_pool_2d(
        input=conv_out,#池化层的输出
        ds=poolsize,#最大池化的大小
        ignore_border=True
    )

    #加上偏置的过程,最终计算非线性映射
    self.output = T.tanh(pooled_out + self.b.dimshuffle('x', 0, 'x', 'x'))

    #模型中的参数,权重和偏置
    self.params = [self.W, self.b]

    # keep track of model input
    self.input = input

在代码中,注意在卷积层的输出阶段只是将权重乘以输入,然后计算池化,在最终的结果上加上偏置,并进行非线性函数的映射,即利用tanh函数对其进行非线性的映射,这样的操作,主要是因为可以省去很多的计算。

3、训练模型

在模型的训练阶段,主要是将数据通过两个卷积+池化层,最终用过一个MLP计算其分类,并通过梯度下降法对其模型中的参数进行调整,具体的代码如下:

# 三、训练模型
##############################################################################
print '... training'

# early-stopping参数
patience = 10000
patience_increase = 2

improvement_threshold = 0.995

validation_frequency = min(n_train_batches, patience / 2)

best_validation_loss = numpy.inf
best_iter = 0
test_score = 0.
start_time = timeit.default_timer()

epoch = 0
done_looping = False

# 循环训练
while (epoch < n_epochs) and (not done_looping):
    epoch = epoch + 1
    # 针对每一批样本
    for minibatch_index in xrange(n_train_batches):

        iter = (epoch - 1) * n_train_batches + minibatch_index

        if iter % 100 == 0:
            print 'training @ iter = ', iter

        # 计算训练的误差
        cost_ij = train_model(minibatch_index)

        if (iter + 1) % validation_frequency == 0:

            # 在验证集上计算误差
            validation_losses = [validate_model(i) for i
                                 in xrange(n_valid_batches)]
            this_validation_loss = numpy.mean(validation_losses)
            print('epoch %i, minibatch %i/%i, validation error %f %%' %
                  (epoch, minibatch_index + 1, n_train_batches,
                   this_validation_loss * 100.))

            # 记录最优的模型
            if this_validation_loss < best_validation_loss:

                #improve patience if loss improvement is good enough
                if this_validation_loss < best_validation_loss *  \
                   improvement_threshold:
                    patience = max(patience, iter * patience_increase)

                # save best validation score and iteration number
                best_validation_loss = this_validation_loss
                best_iter = iter


                test_losses = [
                    test_model(i)
                    for i in xrange(n_test_batches)
                ]
                test_score = numpy.mean(test_losses)
                print(('     epoch %i, minibatch %i/%i, test error of '
                       'best model %f %%') %
                      (epoch, minibatch_index + 1, n_train_batches,
                       test_score * 100.))


        if patience <= iter:
            done_looping = True
            break

end_time = timeit.default_timer()
print('Optimization complete.')
print('Best validation score of %f %% obtained at iteration %i, '
          'with test performance %f %%' %
                    (best_validation_loss * 100., best_iter + 1, test_score * 100.))
print >> sys.stderr, ('The code for file ' +
                      os.path.split(__file__)[1] +
                      ' ran for %.2fm' % ((end_time - start_time) / 60.))

七、实验的结果

部分实验的结果如下:

若需要PDF版本,请关注我的新浪博客@赵_志_勇,私信你的邮箱地址给我。

参考文献

Convolutional Neural Networks (LeNet)http://www.deeplearning.net/tutorial/lenet.html#tips-and-tricks

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏CreateAMind

神经网络(Neural Networks)课程ppt及视频

10720
来自专栏深度学习

图像分类 | 深度学习PK传统机器学习

图像分类,顾名思义,是一个输入图像,输出对该图像内容分类的描述的问题。它是计算机视觉的核心,实际应用广泛。 图像分类的传统方法是特征描述及检测,这类传统方法可能...

57090
来自专栏人工智能

深度学习入门笔记(2)-线性回归 Linear Regression with autograd

一同前行! 假设我们有一个曲线(或者平面)y=wx+b 我们给定它一个特定的w,和b w = [2,51] b = 21.2 即y=2x1+51x2+21.2 ...

28390
来自专栏AI科技大本营的专栏

深度学习目标检测指南:如何过滤不感兴趣的分类及添加新分类?

AI 科技大本营按:本文编译自 Adrian Rosebrock 发表在 PyImageSearch 上的一篇博文。该博文缘起于一位网友向原作者请教的两个关于目...

14220
来自专栏机器之心

NIPS 2018 | Quoc Le提出卷积网络专属正则化方法DropBlock

深度神经网络在具备大量参数、使用大量正则化和噪声时效果很好,如权重衰减和 dropout [1]。尽管 dropout 的首次成功与卷积网络相关,但近期的卷积架...

9220
来自专栏机器之心

增加检测类别?这是一份目标检测的基础指南

14650
来自专栏深度学习

图像分类 | 深度学习PK传统机器学习

图像分类,顾名思义,是一个输入图像,输出对该图像内容分类的描述的问题。它是计算机视觉的核心,实际应用广泛。

515110
来自专栏ATYUN订阅号

【学术】一文教你如何正确利用kNN进行机器学习

AiTechYun 编辑:xiaoshan k最近邻算法(kNN)是机器学习中最简单的分类方法之一,并且是入门机器学习和分类的好方法。它基本上是通过在训练数据中...

28350
来自专栏专知

【专知-PyTorch手把手深度学习教程02】CNN快速理解与PyTorch实现: 图文+代码

【导读】主题链路知识是我们专知的核心功能之一,为用户提供AI领域系统性的知识学习服务,一站式学习人工智能的知识,包含人工智能( 机器学习、自然语言处理、计算机视...

5K110
来自专栏算法channel

机器学习逻辑回归:算法兑现为python代码

0 回顾 昨天推送了逻辑回归的基本原理:从逻辑回归的目标任务,到二分类模型的构建,再到如何用梯度下降求出二分类模型的权重参数。今天,我们将对这个算法兑现为代码...

36450

扫码关注云+社区

领取腾讯云代金券