图像分类_图像分类的数据集怎么做标签 - CSDN
图像分类 订阅
图像分类,根据各自在图像信息中所反映的不同特征,把不同类别的目标区分开来的图像处理方法。它利用计算机对图像进行定量分析,把图像或图像中的每个像元或区域划归为若干个类别中的某一种,以代替人的视觉判读。 展开全文
图像分类,根据各自在图像信息中所反映的不同特征,把不同类别的目标区分开来的图像处理方法。它利用计算机对图像进行定量分析,把图像或图像中的每个像元或区域划归为若干个类别中的某一种,以代替人的视觉判读。
信息
中文名
图像分类
工    具
计算机
外文名
image classification
处理对象
图像
图像分类分类方法
基于色彩特征的索引技术色彩是物体表面的一种视觉特性,每种物体都有其特有的色彩特征,譬如人们说到绿色往往是和树木或草原相关,谈到蓝色往往是和大海或蓝天相关,同一类物体往拍几有着相似的色彩特征,因此我们可以根据色彩特征来区分物体.用色彩特特征进行图像分类一可以追溯到Swain和Ballard提出的色彩直方图的方法.由于色彩直方图具有简单且随图像的大小、旋转变化不敏感等特点,得到了研究人员的厂泛关注,目前几乎所有基于内容分类的图像数据库系统都把色彩分类方法作为分类的一个重要手段,并提出了许多改进方法,归纳起主要可以分为两类:全局色彩特征索引和局部色彩特征索引。基于纹理的图像分类技术纹理特征也是图像的重要特征之一,其本质是刻画象素的邻域灰度空间分布规律由于它在模式识别和计算机视觉等领域已经取得了丰富的研究成果,因此可以借用到图像分类中。在70年代早期,Haralick等人提出纹理特征的灰度共生矩阵表示法(eo一oeeurrenee matrix representation),这个方法提取的是纹理的灰度级空间相关性(gray level Spatial dependenee),它首先基于象素之间的距离和方向建立灰度共生矩阵,再由这个矩阵提取有意义的统计量作为纹理特征向量。基于一项人眼对纹理的视觉感知的心理研究,Tamuar等人提出可以模拟纹理视觉模型的6个纹理属性,分别是粒度,对比度,方向性,线型,均匀性和粗糙度。QBIC系统和MARS系统就采用的是这种纹理表示方法。在90年代初期,当小波变换的理论结构建一认起来之后,许多研究者开始研究如何用小波变换表示纹理特征。smiht和chang利用从小波子带中提取的统计量(平均值和方差)作为纹理特征。这个算法在112幅Brodatz纹理图像中达到了90%的准确率。为了利用中间带的特征,Chang和Kuo开发出一种树型结构的小波变化来进一步提高分类的准确性。还有一些研究者将小波变换和其他的变换结合起来以得到更好的性能,如Thygaarajna等人结合小波变换和共生矩阵,以兼顾基于统计的和基于变换的纹理分析算法的优点。基于形状的图像分类技术形状是图像的重要可视化内容之一在二维图像空间中,形状通常被认为是一条封闭的轮廓曲线所包围的区域,所以对形状的描述涉及到对轮廓边界的描述以及对这个边界所包围区域的描述.目前的基于形状分类方法大多围绕着从形状的轮廓特征和形状的区域特征建立图像索引。关于对形状轮廓特征的描述主要有:直线段描述、样条拟合曲线、傅立叶描述子以及高斯参数曲线等等。实际上更常用的办法是采用区域特征和边界特征相结合来进行形状的相似分类.如Eakins等人提出了一组重画规则并对形状轮廓用线段和圆弧进行简化表达,然后定义形状的邻接族和形族两种分族函数对形状进行分类.邻接分族主要采用了形状的边界信息,而形状形族主要采用了形状区域信息.在形状进行匹配时,除了每个族中形状差异外,还比较每个族中质心和周长的差异,以及整个形状的位置特征矢量的差异,查询判别距离是这些差异的加权和。基于空间关系的图像分类技术在图像信息系统中,依据图像中对象及对象间的空间位置关系来区别图像库中的不同图像是一个非常重要的方法。因此,如何存贮图像对象及其中对象位置关系以方便图像的分类,是图像数据库系统设计的一个重要问题。而且利用图像中对象间的空间关系来区别图像,符合人们识别图像的习惯,所以许多研究人员从图像中对象空间位置关系出发,着手对基于对象空间位置关系的分类方法进行了研究。早在1976年,Tanimoto提出了用像元方法来表示图像中的实体,并提出了用像元来作为图像对象索引。随后被美国匹兹堡大学chang采纳并提出用二维符号串(2D一String)的表示方法来进行图像空间关系的分类,由于该方法简单,并且对于部分图像来说可以从ZD一String重构它们的符号图,因此被许多人采用和改进,该方法的缺点是仅用对象的质心表示空间位置;其次是对于一些图像来说我们不能根据其ZD一string完个重构其符号图;再则是上述的空间关系太简单,实际中的空间关系要复杂得多。,针对这些问题许多人提出了改进力一法。Jungert根据图像对象的最小包围盒分别在:x轴方向和y轴上的投影区间之间的交叠关系来表示对象之间的空间关系,随后Cllallg和Jungert等人又提出了广义ZD一string(ZDG一String)的方法,将图像对象进一步切分为更小的子对象来表示对象的空间关系,但是该方法不足之处是当图像对象数日比较多且空间关系比较复杂时,需要切分的子对象的数目很多,存储的开销太大,针对此Lee和Hsu等人提出了ZDC一string的方一法,它们采用Anell提出的13种时态间隔关系并应用到空间投影区问上来表达空间关系。在x轴方向和y轴方向的组合关系共有169种,他提出了5种基本关系转换法则,在此基础上又提出了新的对象切分方法。采用ZDC一string的方法比ZDG一string切分子对象的数目明显减少。为了在空间关系中保留两个对象的相对空间距离和对象的大小,Huang等人提出了ZDC书string的方法提高符号图的重构精度,并使得对包含对象相对大小、距离的符号图的推理成为可能。上述方法都涉及到将图像对象进行划分为子对象,且在用符号串重构对象时处理时间的开销都比较大,为解决这些方法的不足,Lee等人又提出了ZDB一String的方法,它不要求对象进一步划分,用对象的名称来表示对象的起点和终点边界。为了解决符号图的重构问题,Chin一ChenCllang等人提出了面向相对坐标解决符号图的重构问题,Chin一ChenChang等人提出了面向相对坐标符号串表示(RCOS串),它们用对象最小外接包围盒的左下角坐标和右上角坐标来表示对象之间的空间关系.对于对象之间的空间关系采用Allen提出的13种区间表示方法。实际上上述所有方法都不是和对象的方位无关,为此Huang等人又提出了RSString表示方法。虽然上述各种方法在对图像对象空间信息的分类起到过一定作用,由于它们都是采用对象的最小外接矩形来表示一个对象空间位置,这对于矩形对象来说是比较合适的,但是当两个对象是不规则形状,且它们在空间关系上是分离时,它们的外接矩形却存在着某种包含和交叠,结果出现对这些对象空间关系的错误表示。用上述空间关系进行图像分类都是定性的分类方一法,将图像的空间关系转换为图像相似性的定量度量是一个较为困难的事情。Nabil综合ZD一String方法和二维平面中对象之间的点集拓扑关系。提出了ZD一PIR分类方法,两个对象之间的相似与否就转换为两个图像的ZD一PIR图之间是否同构。ZD一PIR中只有图像对象之间的空间拓扑关系具有旋转不变性,在进行图像分类的时候没有考虑对象之间的相对距离。
收起全文
  • 从机器学习基础算法开始,然后进入到图像分类领域,使用MNIST手写数据集和CIFAR10图像数据集,从简单神经网络到深度神经网络,再到卷积神经网络,最终完成复杂模型:残差网络的搭建。完成这条主线,学员将可以自如地...
  • 选自Medium机器之心编译参与:蒋思源、黄小天、吴攀图像分类是人工智能领域的基本研究主题之一,研究者也已经开发了大量用于图像分类的算法。近日,Shiyu Mou 在 Medium 上发表了一篇文章,对五种用于图像分类的...
    

    选自Medium

    机器之心编译

    参与:蒋思源、黄小天、吴攀


    图像分类是人工智能领域的基本研究主题之一,研究者也已经开发了大量用于图像分类的算法。近日,Shiyu Mou 在 Medium 上发表了一篇文章,对五种用于图像分类的方法(KNN、SVM、BP 神经网络、CNN 和迁移学习)进行了实验比较,该研究的相关数据集和代码也已经被发布在了 GitHub 上。


    项目地址:https://github.com/Fdevmsy/Image_Classification_with_5_methods


    图像分类,顾名思义,就是为输入图像打上固定类别的标签。这是计算机视觉领域的核心问题之一。尽管听起来很简单,但图像分类有大量不同的实际应用。


    传统方式:特征描述和检测



    也许对一些样本任务有好处,但实际情况要复杂得多。



    因此,我们并没有通过代码的形式直接指出每一类型的外观(visual appearance),而是使用机器学习——为计算机提供每一类的诸多实例,接着开发学习算法观察这些实例,并学习每一类的外观。


    然而,图像分类如此复杂,以至于其处理经常用到深度学习模型,比如 CNN(卷积神经网络)。我们已经知道,我们在课堂上学习的不少算法(如 KNN、SVM)通常很擅长数据挖掘;但是对于图像分类,它们却不是最佳选择。


    因此,我们将对课堂中学到的以及 CNN 和迁移学习等算法做一个对比。


    目标


    我们的目标是:


    1. 把 KNN、SVM、BP 神经网络与业界处理图像识别问题的算法——CNN 和迁移学习——进行对比。

    2. 获得深度学习经验。

    3. 通过 TensorFlow 探索机器学习框架。


    系统设计 & 实现细节


    算法与工具


    本项目使用的 5 个方法是 KNN、SVM、BP 神经网络、CNN 和迁移学习。


    全项目可分为 3 类方法:


    • 第一类方法:使用 KNN、SVM、BP 神经网络这些课堂算法。这些算法强大易实现。我们主要使用 sklearn 实现这些算法。

    • 第二类方法:尽管传统的多层感知器模型已成功应用于图像识别,但由于其节点之间的全连接性,它们遭遇了维度的难题,从而不能很好地扩展到更高分辨率的图像。因此我们使用深度学习框架 TensorFlow 打造了一个 CNN。

    • 第三个方法:重新训练一个被称作 Inception V3 的预训练深度神经网络的最后一层,同样由 TensorFlow 提供。Inception V3 是为 ImageNet 大型视觉识别挑战赛训练的,使用了 2012 年的数据。这是计算机视觉的常规任务,其中模型试图把全部图像分为 1000 个类别,比如斑马、达尔阿提亚人和洗碗机。为了再训练这一预训练网络,我们要保证自己的数据集没有被预训练。


    实现


    第一类方法:预处理数据集,并使用 sklearn 实现 KNN、SVM、BP 神经网络。


    首先,我们使用 OpenCV 包定义了 2 个不同的预处理函数:第一个是图像到特征向量,它可以重调图像大小,并把图像转化为行像素列表;第二个是提取颜色直方图,即使用 cv2.normalize 从 HSV 颜色空间提取 3D 颜色直方图,并平化(flatten)结果。


    接着,建构若干个我们需要解析的参数。由于想要同时测试整个数据集和带不同数量标签的子数据集的精确度,我们构建了一个作为参数的数据集并解析进我们的程序。我们同样构建了用于 k-NN 方法的邻元素数作为解析参数。


    之后,我们开始提取数据集中的每一图像特征,并将其放入数组。我们使用 cv2.imread 读取每一图像,通过从图像名称中提取字符串来拆分标签。在我们的数据集中,我们使用相同格式——类别标签. 图像序号.jpg——设置名称,因此我们可以轻易提取每张图像的分类标签。接着我们使用这两个函数提取 2 种特征并附加到数组 rawImages,而之前提取的标签附加到数组标签。


    下一步是使用从 sklearn 包导入的函数 train_test_split 拆分数据集。这个集具有后缀 RI,RL 是 rawImages 和标签对的拆分结果,另一个是特征和标签对的拆分结果。我们使用 85% 的数据集作为训练集,余下的 15% 作为测试集。


    最后,我们应用 KNN、SVM、BP 神经网络函数评估数据。对于 KNN 我们使用 KNeighborsClassifier,对于 SVM 我们使用 SVC,对于 BP 神经网络我们使用 MLPClassifier。


    第二类方法:使用 TensorFlow 构建 CNN。TensorFlow 的全部目的在于使你打造一张计算图(使用 Python 等语言),接着在 C++ 中执行该图(在相同计算量的情况下,C++比 Python 更高效)。


    TensorFlow 也可自动计算优化图变量所需的梯度,从而使模型表现更好。这是由于该图由简单的数学表达式组合而成,因此可通过导数链式法则计算全图的梯度。


    一张 TensorFlow 图包含以下几个部分,每一部分将在下文详述:


    • 占位符变量,用于输入数据到图。

    • 优化向量以使卷积网络表现更好。

    • 卷积网络的数学公式。

    • 可用于指导变量优化的成本衡量标准。

    • 更新变量的优化方法。

    • CNN 架构由一堆不同的层组成,这些层通过可微分函数可把输入量转化为输出量。


    因此,在我们的实现中,第一层是保存图像,接着我们使用 2 x 2 最大池化和修正线性单元(ReLU)的构建 3 个卷积层。输入是 4 维张量:


    • 图像序号。

    • 每一图像的 Y 轴。

    • 每一图像的 X 轴。

    • 每一图像的通道(channel)。


    输出是另一个 4 维张量:


    • 图像序号,与输入相同。

    • 每一图像的 Y 轴。如果使用 2x2 池化,接着输入图像的高和宽除以 2。

    • 每一图像的 X 轴。同上。

    • 由卷积滤波器生成的通道。


    接着,我们我们在网络末端构建了 2 个全连接层。输入是一个 2 维的形状张量 [num_images、num_inputs]。输出也是一个 2 维的形状张量 [num_images、num_outputs]


    然而,为了连接卷积层和全连接层,我们需要一个平层(Flatten Layer)以把 4 维向量减少至可输入到全连接层的 2 维。


    CNN 末端通常是一个 softmax 层,它可归一化来自全连接层的输出,因此每一元素被限制在 0 与 1 之间,并且所有元素总和为 1。


    为了优化训练结果,我们需要一个成本衡量标准并在每次迭代中将成本降至最少。这里我们使用的成本函数是交叉熵(tf.nn.oftmax_cross_entropy_with_logits()),并在所有的图像分类中取交叉熵的平均值。优化方法是 tf.train.AdamOptimizer(),它是梯度下降的高级形式。这是一个可被调节的参数学习率。


    第三种方法:再训练 Inception V3。现代目标识别模型有数以百万计的参数,并可能需要花费数周的时间才能完全训练一个模型。迁移学习是一种采用在分类数据集(如 ImageNet)中已训练的模型而快速完成这一工作的方法,因为其只需要重新训练新类别的权重就行。虽然这样的模型并没有完全训练的模型表现好,但对于许多应用来说,这是非常高效的,因为其不需要 GPU 并可以在笔记本上花半个小时就完成训练。


    读者可以点击一下链接进一步了解迁移学习的训练过程:https://www.tensorflow.org/tutorials/image_retraining


    首先我们需要获取预训练模型,并移除旧的顶层神经网络,然后再基于我们的数据集重新训练一个输出层。虽然猫的所有品种并没有在原始 ImageNet 数据集和全训练的模型中体现,但迁移学习的神奇之处就在于其可以利用已训练模型用来识别某些目标的底层特征,因为底层特征可以在很多不更改的情况下应用于很多识别任务。然后我们分析本地的所有图片并计算每张的瓶颈值(bottleneck values)。因为每张图片在训练过程中重复使用了多次,所以计算每个瓶颈值需要花费大量时间,但我们可以加快缓存这些瓶颈值,也就可以省去重复的计算。


    该脚本将运行 4000 次训练步。每一步从训练集中随机选择 10 张图片,并从缓存中搜索其瓶颈值,然后再将它们训练最后一层以得到预测。这些预测会通过对比真实标注值而通过反向传播过程更新最后一层的权重。


    实验


    数据集


    Oxford-IIIT Pet 数据集:http://www.robots.ox.ac.uk/~vgg/data/pets/


    该数据集有 25 种狗和 12 种猫。每一种类别有 200 张相片。我们在该项目中只会使用 10 种猫。



    在该项目中我们用的类别为 [斯芬克斯猫、暹罗猫、布偶猫、波斯猫、缅因猫、英国短毛猫、孟买猫、伯曼猫、孟加拉豹猫、阿比西尼亚猫]。


    因此在数据集中我们总共有 2000 张图片。虽然图片的尺寸是不同的,但我们可以调整为固定的大小如 64x64 或 128x128。


    预处理


    在该项目中,我们主要使用 OpenCV 对图片进行预处理,如读取图片放入阵列或调整为我们需要的大小等。


    提升图像训练结果的一个常用方法就是对训练输入随机进行变形、裁剪或亮度调整处理。由于采用了同一图片所有可能的变体,该方法不仅具有扩展有效训练数据大小的优点,同时还倾向帮助网络使用分类器学习处理所有在现实生活中可能出现的畸变。


    具体请查看:https://github.com/aleju/imgaug.


    评估


    第一个方法:第一部分为预处理数据集和使用 sklearn 应用 KNN、SVM 和 BP 神经网络。


    在程序中有很多参数可以调整:在 image_to_feature_vector 函数中,我们设置的图片尺寸为 128x128,我们之前也尝试过使用其他尺寸(如 8x8、 64x64、256x256)进行训练。我们发现虽然图片的尺寸越大效果越好,但大尺寸的图片同样也增加了执行时间和内存需求。因此我们最后决定使用 128x128 的图片尺寸,因为其并不太大,同时还保证了准确度。


    在 extract_color_histogram 函数中,我们将每个通道的二进制值设置为 32,32,32。在先前的函数中,我们还尝试了 8, 8, 8 和 64, 64, 64。虽然更高的数值能有更优的结果,但同时也要求更长的执行时间,因此我们认为 32,32,32 是比较合适的。


    对于数据集,我们训练了 3 种。第一种是有 400 张图片、2 种标注的子数据集。第二种是有 1000 张图片、5 种标注的子数据集。最后一种是有 1997 张图片、10 种标注的全数据集。我们将不同的数据集解析为程序中的参数。


    在 KNeighborsClassifier 中,我们只改变近邻的数量并储存每一种数据集最优 K 值的分类结果。其他所有参数都设为默认。


    在 MLPClassifier 中,我们设置每一个隐藏层有 50 个神经元。我们确实测试了多个隐藏层,但好像对最后的结果没有明显的变化。最大的迭代次数设置为 1000,并且为了确保模型能够收敛,我们容忍差设置为 1e-4。同时还需要设置 L2 罚项的参数 alpha 为默认值,随机状态为 1,求解器设置为学习速率为 0.1 的「sgd」。


    在 SVC 中,最大迭代次数为 1000,类别权重设置为「balanced」。


    我们程序的运行时间并不会太久,对于我们的三种数据集大概分别花 3 到 5 分钟左右。


    第二种方法:使用 TensorFlow 构建 CNN


    使用整个大数据集会需要很长的时间计算模型的梯度,因此我们在优化器每一次迭代中都只使用小批量的图片更新权重,批量大小一般是 32 或 64。该数据集分为包含 1600 张图片的训练集、包含 400 张图片的验证集和包含 300 张图片的测试集。


    该模型同样有许多参数需要调整。


    首先是学习率。优良的学习率因为其足够小而很容易令模型收敛,同时又足够大令模型的收敛速度不至于太慢。所以我们选择了 1 x 10^-4。


    第二个需要调整的参数是投入到网络的图片尺寸。我们训练了 64x64 和 128x128 两种图片尺寸,结果表明尺寸越大模型精度就越高,但代价是运行时间会更长。


    然后是神经网络层级数和它的形状。然而实际上由于这一方面有太多的参数可以调整,所以很难在所有的参数间找到一个最优值。


    根据网上的很多资源,我们发现对于构建神经网络,参数的选择很大一部分都是根据已有的经验。


    最开始,我们希望构建相当复杂的神经网络,其所采用的参数如下:


    • # Convolutional Layer 1. filter_size1 = 5 num_filters1 = 64

    • # Convolutional Layer 2. filter_size2 = 5 num_filters2 = 64

    • # Convolutional Layer 3. filter_size3 = 5 num_filters3 = 128

    • # Fully-connected layer 1. fc1_size = 256

    • # Fully-connected layer 2. fc1_size = 256


    我们采用 3 个卷积层和 2 个全连接层,它们的结构都比较复杂。


    然而,我们的结果是:过拟合。对于这样的复杂网络,训练精度在迭代一千次后就达到了 100%,但测试精度仅仅只有 30%。最开始,我们十分疑惑为什么模型会过拟合,然后开始随机调整参数,但这时候模型的表现却又变好了。幸好几天后我碰巧读到了 Google 在讨论深度学习的一篇文章:https://medium.com/@blaisea/physiognomys-new-clothes-f2d4b59fdd6a 该文章指出他们所主导的项目是有问题的:「一个技术性的问题是如果少于 2000 个样本,那么其是不足以训练和测试如同 AlexNet 那样的卷积神经网络而不出现过拟合情况。」所以我才意识到我们的数据集实在是太小了,而网络构架又太复杂,这才产生了过拟合现象。


    我们的数据集正好包含 2000 张图片


    因此,我开始减少神经网络的层级数和核函数的大小。我尝试调整了很多参数,以下是我们最后使用的神经网络架构参数:


    • # Convolutional Layer 1. filter_size1 = 5 num_filters1 = 64

    • # Convolutional Layer 2. filter_size2 = 3 num_filters2 = 64

    • # Fully-connected layer 1. fc1_size = 128

    • # Number of neurons in fully-connected layer.

    • # Fully-connected layer 2. fc2_size = 128

    • # Number of neurons in fully-connected layer.

    • # Number of color channels for the images: 1 channel for gray-scale. num_channels = 3


    我们仅仅使用 2 个小型的卷积层和 2 个全连接层。训练结果并不好,在迭代 4000 次后同样出现了过拟合现象,但测试精度还是要比前面的模型高 10%。


    我们仍然在寻找解决的办法,然而一个显然易见的原因是我们的数据集实在是太小了,我们也没有足够的时间做更多的改进。


    作为最后的结果,我们在 5000 次迭代后大概实现了 43% 的精度,该训练花了一个半小时。实际上,我们对这一结果比较沮丧,因此我们准备使用另一标准数据集 CIFAR-10。



    CIFAR-10 数据集由 60000 张 32x32 10 类彩色图片,每一个类别都有 6000 张图片。该数据集包含了 50000 张训练集和 10000 张测试集。


    我们使用了和上面相同的神经网络架构,在 10 小时的训练后,我们在测试集上实现了 78% 的准确度。


    第三种方法:再训练 Inception V3,我们随机选取一些图片进行训练,而另一批图片用于验证。


    该模型同样有许多参数需要调整。


    首先是训练步,默认值是 4000 步。我们也可以根据情况增加或减少以尽快获得一个可接受的结果。


    随后是学习率,该参数控制了在训练期间更新至最后一层的量级。直观地说,如果学习速率小,那么需要更多的时间进行学习,但最终其可能收敛到更优的全局精度。训练批量大小控制了在一个训练步中检查图片的多少,又因为学习率应用于每一个批量,如果能以更大的批量获得相似的全局效果,我们需要减少它。


    因为深度学习任务所需要的运行时间通常很长,所以我们并不希望模型在训练几小时后实际上表现很糟糕。所以我们需要经常获得验证精度的报告。这样我们同样可以避免过拟合。数据集的分割是将 80% 的图片投入到主要的训练中,10% 的图片作为训练期间经常进行的验证集,而剩下 10% 的图片作为最终的测试集以预测分类器在现实世界中的表现。


    结果


    第一类方法:预处理数据集并使用 sklearn 实现 KNN、SVM 和 BP 神经网络。


    结果在下表中。由于 SVM 结果非常差,甚至低于随机猜测,我们不再展示其结果。



    从结果中我们看到:


    • 在 k-NN 中,原始像素和直方图精确度是相对等同的。在 5 个标签的子数据集,直方图精确度比原始像素高一点;但是整体来讲,原始像素的结果更好。

    • 在神经网络 MLP 分类器中,原始像素精确度远低于直方图。对于整个数据集(10 个标签),原始像素精确度甚至低于随机猜测。

    • 所有这 2 个 sklearn 方法并没有良好表现,在整个数据集中(10 标签数据集)识别正确分类的精确度仅约有 24%。这些结果说明,通过 sklearn 分类图像效果欠佳,它们在使用多个类别分类复杂图像时表现并不好。但是相比于随机猜测,它们确实有提升,只是还不够。


    基于以上结果,我们发现为了提升精确度,使用一些深度学习方法很必要。


    第二类方法:使用 TensorFlow 构建 CNN。如上所述,由于过拟合我们不能获取好的结果。



    正常情况下训练需要半个小时,然而由于结果过拟合,我们认为这一运行时间并不重要。通过和第一类方法的比较,我们看到:尽管 CNN 过拟合训练数据,我依然得到了更好的结果。

    展开全文
  • 图像分类与检测概述

    2018-10-11 14:13:34
    图像分类与检测是现今计算机视觉处理中最为常见的两项任务,本文尽量综述一下传统的图像分类与检测与现今的图像分类与检测技术。以下是要讲的几个方面: 图像分类与检测概述 传统的图像分类与检测方法 现今的图像...

    图像分类与检测是现今计算机视觉处理中最为常见的两项任务,本文尽量综述一下传统的图像分类与检测与现今的图像分类与检测技术。以下是要讲的几个方面:

    图像分类与检测概述
    传统的图像分类与检测方法
    现今的图像分类与检测方法
    

    1 图像分类与检测概述

    当我们面对一张图片的时候,最基础的任务就是这张图片是什么,是风景图还是人物图、是描写建筑物的还是关于食物的,这就是分类。分类作为一个较为笼统的目标,还是较为好达成的。当知道了图像的类别的时候,进一步的就是检测了,例如我知道这个图像是关于人脸的,那么这个人脸在哪里,能不能把它框出来。检测作为一个较为精细的目标,达成的难度可以说是远大于分类的。

    1.1 图像分类与检测的难点

    不只是图像分类与检测,几乎所有的关于机器学习的难点,都是特征提取这一步,一旦找到好的特征,分类与检测就变的很容易了。所谓的特征提取就是指构建一种提取算法,提取出图像里目标对象的特征,例如人脸的边缘特征、皮肤的颜色特征等,这个特征需要尽可能的将目标物体与其他物体区分开来,例如需要区分的物体是黑猫和白猫,那么毫无疑问颜色特征是一个很好的特征。但是,生活中遇到的难题往往都是很难去提取特征的,例如在嘈杂的街道上检测行人与车辆,这种任务对于检测算法的正确率要求很高,因为一不小心漏检或错检一个人可能就会带来一场车祸。

    1.2 图像分类与检测的评价指标

    图像分类的指标比较常见,就是分类的正确率,例如猫狗分类,100张中99张分类正确,那么正确率就是99%的正确率。对于目标检测来说,评价指标就多了一些,主要是检错率、漏检率以及检测meaniou,检错率是指一张图片上有两只猫一只狗,而你的模型检测出了三只猫,那么那第三只猫就是检错的,检错率就是33%;漏检的意思是如果你将上面的图片只检测出一只猫和一只狗的话,那就是漏检了一只猫,漏检率33%;mean_iou指的是你检测出来的目标物体的框和真实的框之间的交并比,如下图:
    2 传统的图像分类与检测方法

    传统的图像分类与检测的步骤大致是两步:特征提取-》训练分类器

    在特征提取阶段,设计者会尝试各种通用特征或者自己设计的特征对图像进行特征提取,我们以人脸检测为例,通用的特征就是HOG、SURF、LBP等特征,而对于人脸效果比较好的特征有鼎鼎大名的haar特征。在选定了这些备用特征之后,设计者会进行尝试特征与权衡利弊,因为对于计算机来说,特征的唯一性、通用性越好,所意味的计算量就越大,设计者必须在保证速度的情况下选择尽量好的特征。

    当选择了计算量适中同时能达到要求的准确率的特征例如LBP和haar特征之后,使用传统的机器学习方法例如朴素贝叶斯、adaboost、随机森林等建模方法,训练出一个分类模型,使用这个分类模型来进行图像分类或检测。模型的选择通常要比特征选择简单的多,只需要把备选的模型都试一遍,挑效果最好的模型进行调参,调到极致之后,一个人脸检测模型就做出来的。

    走这么一套传统图像检测方法的流程,需要很长的时间,并且每一步都需要做好,最终的模型才会有较好的效果,一旦中间的一步出现错误就会牵一发而动全身。所以使用传统方法做图像处理是需要很大代价的。
    3 现今的图像分类和检测方法

    自从2015年深度学习占领各大图像处理比赛榜首之后,现在的图像处理大部分使用的方法都是深度学习,也就是神经网络。神经网络通过很多的神经元构建成一层一层的网络,通过激活层来使得模型有很强的非线性拟合的能力。设计者只需要将图像输入,然后告诉模型需要的结果是什么,模型便会自动的学习特征提取与结果映射。

    通过深度学习,剩下设计者在传统图像处理时最为费时费力的特征提取的那一部分。设计者只需要设计网络结果,使得网络自动提取的特征越好,效果就会也好,正确率越高。

    神经网络本质上是矩阵相乘与非线性的组合,通过很多很多的滤波核,来过滤对结果最为有用的特征而抑制对结果没有用的特征,来进行学习与分类。

    现在在工程中最为常用的还是vgg、resnet、inception这几种结构,设计者通常会先直接套用原版的模型对数据进行训练一次,然后选择效果较为好的模型进行微调与模型缩减。因为工程上使用的模型必须在精度高的同时速度要快。常用的模型缩减的方法是减少卷积和个数与减少resnet的模块数。

    现在常用的检测模型,还是FRCNN、Mask-RCNN、YOLO、SSD等网络模型,一方面精度确实是高,另一方面速度现在进过优化也可以做到实时了。像上面的人脸检测的功能,只需要准备好人脸图片及对应的框标注文件,便可直接跑模型,得到一个还不错的检测模型。
    4 总结

    深度学习对于图像处理非常有用,但同时也有一些弊端,例如需要大量数据、调参很依靠经验,需要的计算能力很高等,适合处理很复杂的现实生活场景。传统的图像处理对于特定场景下简单的任务例如文本文档的检测、矫正等,还是非常有用且高效的。

    第一次写文章,这是从工程角度写的综述,不足之处恳请批评指正。以后会不定期写写图像处理的一些小项目,欢迎交流。

    展开全文
  • 图像分类的任务就是给定一个图像,正确给出该图像所属的类别。对于超级强大的人类视觉系统来说,判别出一个图像的类别是件很容易的事,但是对于计算机来说,并不能像人眼那样一下获得图像的语义信息。 计算机能看到...

    简介

    图像分类的任务就是给定一个图像,正确给出该图像所属的类别。对于超级强大的人类视觉系统来说,判别出一个图像的类别是件很容易的事,但是对于计算机来说,并不能像人眼那样一下获得图像的语义信息。
    计算机能看到的只是一个个像素的数值,对于一个RGB图像来说,假设图像的尺寸是32*32,那么机器看到的就是一个形状为3*32*32的矩阵,或者更正式地称其为“张量”(“张量”简单来说就是高维的矩阵),那么机器的任务其实也就是寻找一个函数关系,这个函数关系能够将这些像素的数值映射到一个具体的类别(类别可以用某个数值表示)。

    算法

    较为简单的算法

    Nearest Neighbor

    "Nearest Neighbor"是处理图像分类问题一个较为简单、直接、粗暴的方法:首先在系统中“记住”所有已经标注好类别的图像(其实这就是训练集),当遇到一个要判断的还未标注的图像(也就是测试集中的某个图像)时,就去比较这个图像与“记住的”图像的“相似性”,找到那个最相似的已经标注好的图像,用那个图像的类别作为正在分类的图像的类别,这也就是"Nearest Neighbor"名称的含义。

    关于如何判断图像“最相似”,可以有多种方法,比如直接求相同位置像素值的差的总和(也就是两个图像的L1距离),也可以采用两个图像的L2距离,也就是先对相同位置像素值作差并求平方,然后对所有平方值求和,最后总体开方,准确的表述如下:记图像分别为I1I_1I2I_2I1kI^k_1表示图像I1I_1在k位置处的像素值,那么图像I1I_1I2I_2的L1和L2距离可以分别表示为:
    d1(I1,I2)=kI1kI2kd_1(I_1, I_2) = \sum_k|I^k_1 - I^k_2| d2(I1,I2)=k(I1kI2k)2d_2(I_1, I_2) = \sqrt{\sum_k(I^k_1 - I^k_2)^2}

    "Nearest Neighbor"容易实现,但是存在诸多缺点:

    1. 预测/测试的过程太慢,给定一个图像时,需要一个个去比较训练集中的图像,每预测一次的计算量都很大,特别是图片的尺寸较大时
    2. 预测/测试时,仍需要较大的空间去存储训练集
    3. 准确率不高,实验发现,该方法在CIFAR-10上只能取得38.59%左右的正确率(其实也不算太糟糕,因为总共有10个类别,如果完全随机猜的话,正确率只有10%),其实是因为该方法没有足够的理论支撑(只有一定的道理),显然的例子是:对于一个图像,如果将其中的物体向左平移一点获得一个新的图像,则这两个图像应该是相同的类别,但是利用"Nearest Neighbor"判断时,则有可能因为相同位置的像素值不再一样而判断错误(因为L1距离和L2距离都是计算相同位置像素值的接近程度,一平移后相同位置的像素值有可能会发生很大的改变)

    Nearest Neighbor的代码实现

    环境:Python 3.6 + PyTorch 1.0

    import torch
    import torchvision
    
    
    class NearestNeighbor:
        def __init__(self):
            self.x_train = 0
            self.y_train = 0
        
        # 训练阶段:存储训练集
        def train(self, x_train, y_train):
            self.x_train = x_train
            self.y_train = y_train
        
        # 测试阶段:比较待分类的图像与所有训练图像的L1距离,取最小距离的图像的类别
        def test(self, x_test):
            L1_distance = torch.sum(torch.abs(x_test - self.x_train), 1)
            min_index = torch.argmin(L1_distance)
            return self.y_train[min_index]
    
    
    if __name__ == '__main__':
        # 定义要对数据集的原始数据进行的变换,此处的变换是将图像转化为Tensor
        transform = torchvision.transforms.Compose([torchvision.transforms.ToTensor()])
        # 加载数据集
        trainset = torchvision.datasets.CIFAR10(root='./', train=True, download=False, transform=transform)
        trainloader = torch.utils.data.DataLoader(trainset, batch_size=1, shuffle=False)
        testset = torchvision.datasets.CIFAR10(root='./', train=False, download=False, transform=transform)
        testloader = torch.utils.data.DataLoader(testset, batch_size=1, shuffle=False)
        # 构造一个训练集和测试集的迭代器,用于遍历
        train_iterator = iter(trainloader)
        test_iterator = iter(testloader)
    
        # 训练集,x_train是所有图像像素形成的Tensor,y_train是所有图像的label
        # 将每一个形状为3*32*32的图像都展开成了长度为3*32*32的行向量
        x_train = torch.zeros((len(trainset), 3 * 32 * 32))
        y_train = torch.zeros((len(trainset), 1))
        for i, (image, label) in enumerate(train_iterator):
            x_train[i] = image.reshape(-1)
            y_train[i] = label
    
        nn = NearestNeighbor()
        nn.train(x_train, y_train)
        # 正确预测的数目、总数目
        right_num, total_num = 0, 0
        for i, (image, label) in enumerate(test_iterator):
            predict_label = nn.test(image.reshape(1, -1))
            if predict_label.item() == label.item():
                right_num += 1
            total_num += 1
        print('Accuracy is %.2f' % (right_num / total_num * 100) + '%')
    

    K-Nearest Neighbor

    "K-Nearest Neighbor"是很自然想到的一个在"Nearest Neighbor"基础上进行改进的方法,与"Nearest Neighbor"不同的是,"K-Nearest Neighbor"不再将“最接近”的那一个图像的类别作为预测图像的类别,而是选出K个与预测图像“最接近”的图像,看其中哪个类别占的比例最高,就将其作为预测图像的类别,这样可以在某种程度上增加预测模型的稳定性。
    此处的K是一个hyper-parameter(超参数),也就是不能由模型学得,而是需要自己去设定,可以尝试的值如10、5、20等,具体选取哪个值可以通过Cross Validation(交叉验证法)来确定,详见机器学习的模型评估方法
    "K-Nearest Neighbor"的代码实现只需要在与"Nearest Neighbor"的代码上作少量修改即可。

    正确率较高的算法

    Linear Classification

    上面的"Nearest Neighbor"和"K-Nearest Neighbor"方法,都是直接比较图像的相似性,存在测试效率太低的问题,并且不能够提取图像的语义信息,导致错误率很高。
    Linear Classification利用一个“全连接层”(Fully Connected Layer),输入图像的所有像素值,经过全连接层的运算后输出每个类别的“得分”,最终选取得分最高的类别作为图像的类别。

    全连接层

    先看一张图

    如图所示,x1,x2,x3x_1, x_2, x_3都是输入的像素值,真实的图像中,输入可能会有很多个,比如CIFAR-10的数据集,图像尺寸是3*32*32(3是RGB这3个通道),那么输入就有3*32*32个;w11,w12,w23w_{11}, w_{12}, w_{23}等表示权重,y1,y2,y3y_1, y_2, y_3是输出的每个类别的得分,如y1y_1表示猫这个类别的得分
    输出的计算方式是:
    y1=w11x1+w12x2+w13x3y_1 = w_{11}*x_1 + w_{12}*x_2 + w_{13}*x_3y2=w21x1+w22x2+w23x3y_2 = w_{21}*x_1 + w_{22}*x_2 + w_{23}*x_3y3=w31x1+w32x2+w33x3y_3 = w_{31}*x_1 + w_{32}*x_2 + w_{33}*x_3
    将其向量化,令:
    X=[x1  x2  x3]TX = [x_1\ \ x_2\ \ x_3]^TY=[y1  y2  y3]TY = [y_1\ \ y_2\ \ y_3]^TW=[w11w12w13w21w22w23w31w32w33]W = \left[ \begin{matrix} w_{11} & w_{12} & w_{13}\\ w_{21} & w_{22} & w_{23}\\ w_{31} & w_{32} & w_{33} \end{matrix} \right]
    则有:
    Y=WXY = W\cdot X
    在最终实现时,还会给每个类别的得分加一个偏置量(bias),类似于原来是“正比例函数”,总是经过原点,现在加一个偏置,变成了普通的”一次函数“,不再一定经过原点,这样可以让表达式更加一般化,也就是表达能力更强,即:
    y1=w11x1+w12x2+w13x3+b1y_1 = w_{11}*x_1 + w_{12}*x_2 + w_{13}*x_3 + b_1y2=w21x1+w22x2+w23x3+b2y_2 = w_{21}*x_1 + w_{22}*x_2 + w_{23}*x_3 + b_2y3=w31x1+w32x2+w33x3+b3y_3 = w_{31}*x_1 + w_{32}*x_2 + w_{33}*x_3 + b_3
    同样,也可以将其向量化,为了简便起见,可以将偏置结合在输入X中,令:
    X=[x1  x2  x3  1]TX = \left[x_1\ \ x_2\ \ x_3\ \ 1\right]^TY=[y1  y2  y3  1]TY = \left[y_1\ \ y_2\ \ y_3\ \ 1\right]^TW=[w11w12w13b1w21w22w23b2w31w32w33b3]W = \left[ \begin{matrix} w_{11} & w_{12} & w_{13} & b_1\\ w_{21} & w_{22} & w_{23} & b_2\\ w_{31} & w_{32} & w_{33} & b_3 \end{matrix} \right]
    可以发现,Y=WXY = W\cdot X的展开结果与上面的非向量化的形式一致。

    在PyTorch中,全连接层很容易实现,如下

    import torchvision
    import torch
    from torch import nn
    
    
    class LinearClassifier(nn.Module):
        def __init__(self):
            super(LinearClassifier, self).__init__()
            # 定义两个全连接层
            self.fc1 = nn.Linear(3*32*32, 200)
            self.fc2 = nn.Linear(200, 10)
    
        def forward(self, x):
            x = self.fc1(x)
            # sigmoid激活函数
            x = torch.sigmoid(x)
            x = self.fc2(x)
            return x
    
    
    if __name__ == '__main__':
        transform = torchvision.transforms.Compose([torchvision.transforms.ToTensor()])
        trainset = torchvision.datasets.CIFAR10(root='./', train=True, download=False, transform=transform)
        trainloader = torch.utils.data.DataLoader(trainset, batch_size=16, shuffle=False)
        testset = torchvision.datasets.CIFAR10(root='./', train=False, download=False, transform=transform)
        testloader = torch.utils.data.DataLoader(testset, batch_size=64, shuffle=False)
    
    	# 定义一个线性分类的多分类网络
        linear_classifier = LinearClassifier()
        # 由于是多分类问题,所以用交叉熵作为损失函数
        criterion = nn.CrossEntropyLoss()
        # 优化器
        optimizer = torch.optim.Adam(linear_classifier.parameters())
    
        for epoch in range(20):
            print('EPOCH:', epoch, end=' ')
            total_loss = 0
            train_iterator = iter(trainloader)
            for batch in train_iterator:
                batch[0] = batch[0].reshape(batch[0].shape[0], -1)
                scores = linear_classifier(batch[0])
                loss = criterion(scores, batch[1])
                optimizer.zero_grad()
                loss.backward()
                optimizer.step()
                total_loss += loss
            print(total_loss, end=' ')
    
            right_num = 0
            test_loader = iter(testloader)
            for batch in testloader:
                scores = linear_classifier(batch[0])
                predict = torch.argmax(scores, dim=1)
                right_num += torch.sum(predict == batch[1])
            print('Accuracy: %.2f' % (right_num.item() / len(testset) * 100) + '%')
    

    本以为该方法准确率会比较高,但是发现正确率还是在30%-40%左右,可能是因为2个全连接层的模型表达能力不够强,还是不能够获得图像的语义信息。

    Convolutional Neural Network(CNN,卷积神经网络)

    利用CNN可以进一步提高图像分类的正确率,甚至已经可以超过人类,关于CNN的细节以及原理,在后面的文章中会详细写,此处只给出用CNN进行图片分类的代码
    环境:Python 3.6 + PyTorch 1.0

    import torchvision
    import torch
    from torch import nn
    
    
    class LinearClassifier(nn.Module):
        def __init__(self):
            super(LinearClassifier, self).__init__()
            self.conv1 = nn.Conv2d(3, 8, kernel_size=3, stride=1, padding=1).cuda()
            self.pool1 = nn.MaxPool2d(2, 2).cuda()
            self.conv2 = nn.Conv2d(8, 16, kernel_size=3, stride=1, padding=1).cuda()
            self.pool2 = nn.MaxPool2d(2, 2).cuda()
            self.conv3 = nn.Conv2d(16, 32, kernel_size=3, stride=1, padding=1).cuda()
            self.pool3 = nn.MaxPool2d(2, 2).cuda()
            self.fc1 = nn.Linear(32*4*4, 100).cuda()
            self.fc2 = nn.Linear(100, 10).cuda()
    
        def forward(self, x):
            x = self.conv1(x)
            x = torch.relu(x)
            x = self.pool1(x)
            x = self.conv2(x)
            x = torch.relu(x)
            x = self.pool2(x)
            x = self.conv3(x)
            x = torch.relu(x)
            x = self.pool3(x)
            x = self.fc1(x.reshape(x.shape[0], -1))
            x = self.fc2(x)
            return x
    
    
    if __name__ == '__main__':
        transform = torchvision.transforms.Compose([torchvision.transforms.ToTensor(), ])
        trainset = torchvision.datasets.CIFAR10(root='./', train=True, download=False, transform=transform)
        trainloader = torch.utils.data.DataLoader(trainset, batch_size=32, shuffle=False)
        testset = torchvision.datasets.CIFAR10(root='./', train=False, download=False, transform=transform)
        testloader = torch.utils.data.DataLoader(testset, batch_size=32, shuffle=False)
    
        linear_classifier = LinearClassifier()
        criterion = nn.CrossEntropyLoss()
        optimizer = torch.optim.SGD(linear_classifier.parameters(), lr=0.01)
    
        for epoch in range(20):
            print('Epoch:', epoch, end='  ')
            total_loss = 0
            train_iterator = iter(trainloader)
            for batch in train_iterator:
                batch[0] = batch[0].cuda()
                batch[0].requires_grad = False
                scores = linear_classifier(batch[0])
                loss = criterion(scores, batch[1].cuda())
                optimizer.zero_grad()
                loss.backward()
                optimizer.step()
                total_loss += loss
            print('Loss =', total_loss.item())
    
            test_iterator = iter(testloader)
            right_num = 0
            for batch in test_iterator:
                scores = linear_classifier(batch[0].cuda())
                predict = torch.argmax(scores, dim=1)
                right_num += torch.sum(predict == batch[1].cuda())
            print('Accuracy: %.2f' % (right_num.item() / len(testset) * 100) + '%')
    

    实验发现,具有3个卷积层的网络,可以将图片分类的精度提升到70%左右,相较于前面的方法已经有了很大的进步,但其实利用深度神经网络还可以表现的更好(所谓的“深度神经网络”其实就是让网络具有更多的卷积层,变得“更深”),一些深度的网络已经可以将图片分类的精度提升到了95%以上。后面的文章会介绍CNN的原理细节。

    展开全文
  • 教程4:图像分类

    2019-03-03 12:56:59
    图像分类 目标: 这一节我们将介绍图像分类问题。所谓图像分类问题,就是已有固定的分类标签集合,然后对于输入的图像,从分类标签集合中找出一个分类标签,最后把分类标签分配给该输入图像。虽然看起来挺简单的,但...

    注:主要内容转自杜客的知乎专栏:https://zhuanlan.zhihu.com/p/20878530?refer=intelligentunit

    图像分类

    目标: 这一节我们将介绍图像分类问题。所谓图像分类问题,就是已有固定的分类标签集合,然后对于输入的图像,从分类标签集合中找出一个分类标签,最后把分类标签分配给该输入图像。虽然看起来挺简单的,但这可是计算机视觉领域的核心问题之一,并且有着各种各样的实际应用。在后面的课程中,我们可以看到计算机视觉领域中很多看似不同的问题(比如物体检测和分割),都可以被归结为图像分类问题。

    例子: 以下图为例,图像分类模型读取该图片,并生成该图片属于集合 {cat, dog, hat, mug}中各个标签的概率。需要注意的是,对于计算机来说,图像是一个由数字组成的巨大的3维数组。在这个例子中,猫的图像大小是宽248像素,高400像素,有3个颜色通道,分别是红、绿和蓝(简称RGB)。如此,该图像就包含了248X400X3=297600个数字,每个数字都是在范围0-255之间的整型,其中0表示全黑,255表示全白。我们的任务就是把这些上百万的数字变成一个简单的标签,比如“猫”。

    在这里插入图片描述

    图像分类的任务,就是对于一个给定的图像,预测它属于的那个分类标签(或者给出属于一系列不同标签的可能性)。图像是3维数组,数组元素是取值范围从0到255的整数。数组的尺寸是宽度x高度x3,其中这个3代表的是红、绿和蓝3个颜色通道。

    困难和挑战: 对于人来说,识别出一个像“猫”一样视觉概念是简单至极的,然而从计算机视觉算法的角度来看就值得深思了。我们在下面列举了计算机视觉算法在图像识别方面遇到的一些困难,要记住图像是以3维数组来表示的,数组中的元素是亮度值。

    • 视角变化(Viewpoint variation):同一个物体,摄像机可以从多个角度来展现。
    • 大小变化(Scale variation):物体可视的大小通常是会变化的(不仅是在图片中,在真实世界中大小也是变化的)。
    • 形变(Deformation):很多东西的形状并非一成不变,会有很大变化。
    • 遮挡(Occlusion):目标物体可能被挡住。有时候只有物体的一小部分(可以小到几个像素)是可见的。
    • 光照条件(Illumination conditions):在像素层面上,光照的影响非常大。
    • 背景干扰(Background clutter):物体可能混入背景之中,使之难以被辨认。
    • 类内差异(Intra-class variation):一类物体的个体之间的外形差异很大,比如椅子。这一类物体有许多不同的对象,每个都有自己的外形。

    面对以上所有变化及其组合,好的图像分类模型能够在维持分类结论稳定的同时,保持对类间差异足够敏感。

    在这里插入图片描述

    数据驱动方法: 如何写一个图像分类的算法呢?这和写个排序算法可是大不一样。怎么写一个从图像中认出猫的算法?搞不清楚。因此,与其在代码中直接写明各类物体到底看起来是什么样的,倒不如说我们采取的方法和教小孩儿看图识物类似:给计算机很多数据,然后实现学习算法,让计算机学习到每个类的外形。这种方法,就是数据驱动方法。既然该方法的第一步就是收集已经做好分类标注的图片来作为训练集,那么下面就看看数据库到底长什么样:

    在这里插入图片描述

    一个有4个视觉分类的训练集。在实际中,我们可能有上千的分类,每个分类都有成千上万的图像。

    图像分类流程: 在课程视频中已经学习过,图像分类就是输入一个元素为像素值的数组,然后给它分配一个分类标签。完整流程如下:

    • 输入: 输入是包含N个图像的集合,每个图像的标签是K种分类标签中的一种。这个集合称为训练集。
    • 学习: 这一步的任务是使用训练集来学习每个类到底长什么样。一般该步骤叫做训练分类器或者学习一个模型。
    • 评价: 让分类器来预测它未曾见过的图像的分类标签,并以此来评价分类器的质量。我们会把分类器预测的标签和图像真正的分类标签对比。毫无疑问,分类器预测的分类标签和图像真正的分类标签如果一致,那就是好事,这样的情况越多越好。

    Nearest Neighbor分类器

    作为课程介绍的第一个方法,我们来实现一个 Nearest Neighbor分类器 。虽然这个分类器和卷积神经网络没有任何关系,实际中也极少使用,但通过实现它,可以让读者对于解决图像分类问题的方法有个基本的认识。

    图像分类数据集:CIFAR-10。 一个非常流行的图像分类数据集是CIFAR-10。这个数据集包含了60000张32X32的小图像。每张图像都有10种分类标签中的一种。这60000张图像被分为包含50000张图像的训练集和包含10000张图像的测试集。在下图中你可以看见10个类的10张随机图片。

    在这里插入图片描述

    左边: 从CIFAR-10数据库来的样本图像。 右边: 第一列是测试图像,然后第一列的每个测试图像右边是使用Nearest Neighbor算法,根据像素差异,从训练集中选出的10张最类似的图片。

    假设现在我们有CIFAR-10的50000张图片(每种分类5000张)作为训练集,我们希望将余下的10000作为测试集并给他们打上标签。Nearest Neighbor算法将会拿着测试图片和训练集中每一张图片去比较,然后将它认为最相似的那个训练集图片的标签赋给这张测试图片。上面右边的图片就展示了这样的结果。请注意上面10个分类中,只有3个是准确的。比如第8行中,马头被分类为一个红色的跑车,原因在于红色跑车的黑色背景非常强烈,所以这匹马就被错误分类为跑车了。

    那么具体如何比较两张图片呢?在本例中,就是比较32x32x3的像素块。最简单的方法就是逐个像素比较,最后将差异值全部加起来。换句话说,就是将两张图片先转化为两个向量I1I_1I2I_2,然后计算他们的L1距离:
    d1(I1,I2)=pI1pI2p\displaystyle d_1(I_1,I_2)=\sum_p|I^p_1-I^p_2|
    这里的求和是针对所有的像素。下面是整个比较流程的图例:
    在这里插入图片描述
    以图片中的一个颜色通道为例来进行说明。两张图片使用L1距离来进行比较。逐个像素求差值,然后将所有差值加起来得到一个数值。如果两张图片一模一样,那么L1距离为0,但是如果两张图片很是不同,那L1值将会非常大。

    下面,让我们看看如何用代码来实现这个分类器。首先,我们将CIFAR-10的数据加载到内存中,并分成4个数组:训练数据和标签,测试数据和标签。在下面的代码中,Xtr(大小是50000x32x32x3)存有训练集中所有的图像,Ytr是对应的长度为50000的1维数组,存有图像对应的分类标签(从0到9):

    Xtr, Ytr, Xte, Yte = load_CIFAR10('data/cifar10/') # a magic function we provide
    # flatten out all images to be one-dimensional
    Xtr_rows = Xtr.reshape(Xtr.shape[0], 32 * 32 * 3) # Xtr_rows becomes 50000 x 3072
    Xte_rows = Xte.reshape(Xte.shape[0], 32 * 32 * 3) # Xte_rows becomes 10000 x 3072
    

    现在我们得到所有的图像数据,并且把他们拉长成为行向量了。接下来展示如何训练并评价一个分类器:

    nn = NearestNeighbor() # create a Nearest Neighbor classifier class
    nn.train(Xtr_rows, Ytr) # train the classifier on the training images and labels
    Yte_predict = nn.predict(Xte_rows) # predict labels on the test images
    # and now print the classification accuracy, which is the average number
    # of examples that are correctly predicted (i.e. label matches)
    print 'accuracy: %f' % ( np.mean(Yte_predict == Yte) )
    

    作为评价标准,我们常常使用准确率,它描述了我们预测正确的得分。请注意以后我们实现的所有分类器都需要有这个API:**train(X, y)函数。该函数使用训练集的数据和标签来进行训练。从其内部来看,类应该实现一些关于标签和标签如何被预测的模型。这里还有个predict(X)**函数,它的作用是预测输入的新数据的分类标签。现在还没介绍分类器的实现,下面就是使用L1距离的Nearest Neighbor分类器的实现套路:

    import numpy as np
    
    class NearestNeighbor(object):
      def __init__(self):
        pass
    
      def train(self, X, y):
        """ X is N x D where each row is an example. Y is 1-dimension of size N """
        # the nearest neighbor classifier simply remembers all the training data
        self.Xtr = X
        self.ytr = y
    
      def predict(self, X):
        """ X is N x D where each row is an example we wish to predict label for """
        num_test = X.shape[0]
        # lets make sure that the output type matches the input type
        Ypred = np.zeros(num_test, dtype = self.ytr.dtype)
    
        # loop over all test rows
        for i in xrange(num_test):
          # find the nearest training image to the i'th test image
          # using the L1 distance (sum of absolute value differences)
          distances = np.sum(np.abs(self.Xtr - X[i,:]), axis = 1)
          min_index = np.argmin(distances) # get the index with smallest distance
          Ypred[i] = self.ytr[min_index] # predict the label of the nearest example
    
        return Ypred
    

    如果你用这段代码跑CIFAR-10,你会发现准确率能达到38.6%。这比随机猜测的10%要好,但是比人类识别的水平(据研究推测是94%)和卷积神经网络能达到的95%还是差多了。基于CIFAR-10数据的Kaggle算法竞赛排行榜:https://www.kaggle.com/c/cifar-10/leaderboard

    距离选择: 计算向量间的距离有很多种方法,另一个常用的方法是 L2 距离,从几何学的角度,可以理解为它在计算两个向量间的欧式距离。 L2 距离的公式如下:
    d2(I1,I2)=p(I1pI2p)2\displaystyle d_2(I_1,I_2)=\sqrt{ \sum_p(I^p_1-I^p_2)^2}
    换句话说,我们依旧是在计算像素间的差值,只是先求其平方,然后把这些平方全部加起来,最后对这个和开方。在Numpy中,我们只需要替换上面代码中的1行代码就行:

    distances = np.sqrt(np.sum(np.square(self.Xtr - X[i,:]), axis = 1))
    

    注意在这里使用了np.sqrt,但是在实际中可能不用。因为求平方根函数是一个单调函数,它对不同距离的绝对值求平方根虽然改变了数值大小,但依然保持了不同距离大小的顺序。所以用不用它,都能够对像素差异的大小进行正确比较。如果你在CIFAR-10上面跑这个模型,正确率是35.4%,比刚才低了一点。

    L1和L2比较。比较这两个度量方式是挺有意思的。在面对两个向量之间的差异时,L2比L1更加不能容忍这些差异。也就是说,相对于1个巨大的差异,L2距离更倾向于接受多个中等程度的差异。L1和L2都是在p-norm常用的特殊形式。

    k-Nearest Neighbor分类器

    你可能注意到了,为什么只用最相似的1张图片的标签来作为测试图像的标签呢?这不是很奇怪吗!是的,使用k-Nearest Neighbor分类器就能做得更好。它的思想很简单:与其只找最相近的那1个图片的标签,我们找最相似的k个图片的标签,然后让他们针对测试图片进行投票,最后把票数最高的标签作为对测试图片的预测。所以当k=1的时候,k-Nearest Neighbor分类器就是Nearest Neighbor分类器。从直观感受上就可以看到,更高的k值可以让分类的效果更平滑,使得分类器对于异常值更有抵抗力。

    在这里插入图片描述

    上面示例展示了Nearest Neighbor分类器和5-Nearest Neighbor分类器的区别。例子使用了2维的点来表示,分成3类(红、蓝和绿)。不同颜色区域代表的是使用L2距离的分类器的决策边界。白色的区域是分类模糊的例子(即图像与两个以上的分类标签绑定)。需要注意的是,在NN分类器中,异常的数据点(比如:在蓝色区域中的绿点)制造出一个不正确预测的孤岛。5-NN分类器将这些不规则都平滑了,使得它针对测试数据的泛化(generalization)能力更好(例子中未展示)。注意,5-NN中也存在一些灰色区域,这些区域是因为近邻标签的最高票数相同导致的(比如:2个邻居是红色,2个邻居是蓝色,还有1个是绿色)。

    在实际中,大多使用k-NN分类器。但是k值如何确定呢?接下来就讨论这个问题。

    用于超参数调优的验证集

    k-NN分类器需要设定k值,那么选择哪个k值最合适的呢?我们可以选择不同的距离函数,比如L1范数和L2范数等,那么选哪个好?还有不少选择我们甚至连考虑都没有考虑到(比如:点积)。所有这些选择,被称为超参数(hyperparameter)。在基于数据进行学习的机器学习算法设计中,超参数是很常见的。一般说来,这些超参数具体怎么设置或取值并不是显而易见的。

    你可能会建议尝试不同的值,看哪个值表现最好就选哪个。好主意!我们就是这么做的,但这样做的时候要非常细心。特别注意:决不能使用测试集来进行调优。当你在设计机器学习算法的时候,应该把测试集看做非常珍贵的资源,不到最后一步,绝不使用它。如果你使用测试集来调优,而且算法看起来效果不错,那么真正的危险在于:算法实际部署后,性能可能会远低于预期。这种情况,称之为算法对测试集过拟合。从另一个角度来说,如果使用测试集来调优,实际上就是把测试集当做训练集,由测试集训练出来的算法再跑测试集,自然性能看起来会很好。这其实是过于乐观了,实际部署起来效果就会差很多。所以,最终测试的时候再使用测试集,可以很好地近似度量你所设计的分类器的泛化性能(在接下来的课程中会有很多关于泛化性能的讨论)。

    测试数据集只使用一次,即在训练完成后评价最终的模型时使用。

    好在我们有不用测试集调优的方法。其思路是:从训练集中取出一部分数据用来调优,我们称之为验证集(validation set)。以CIFAR-10为例,我们可以用49000个图像作为训练集,用1000个图像作为验证集。验证集其实就是作为假的测试集来调优。下面就是代码:

    # assume we have Xtr_rows, Ytr, Xte_rows, Yte as before
    # recall Xtr_rows is 50,000 x 3072 matrix
    Xval_rows = Xtr_rows[:1000, :] # take first 1000 for validation
    Yval = Ytr[:1000]
    Xtr_rows = Xtr_rows[1000:, :] # keep last 49,000 for train
    Ytr = Ytr[1000:]
    
    # find hyperparameters that work best on the validation set
    validation_accuracies = []
    for k in [1, 3, 5, 10, 20, 50, 100]:
    
      # use a particular value of k and evaluation on validation data
      nn = NearestNeighbor()
      nn.train(Xtr_rows, Ytr)
      # here we assume a modified NearestNeighbor class that can take a k as input
      Yval_predict = nn.predict(Xval_rows, k = k)
      acc = np.mean(Yval_predict == Yval)
      print 'accuracy: %f' % (acc,)
    
      # keep track of what works on the validation set
      validation_accuracies.append((k, acc))
    

    程序结束后,我们会作图分析出哪个k值表现最好,然后用这个k值来跑真正的测试集,并作出对算法的评价。

    把训练集分成训练集和验证集。使用验证集来对所有超参数调优。最后只在测试集上跑一次并报告结果。

    **交叉验证。**有时候,训练集数量较小(因此验证集的数量更小),人们会使用一种被称为交叉验证的方法,这种方法更加复杂些。还是用刚才的例子,如果是交叉验证集,我们就不是取1000个图像,而是将训练集平均分成5份,其中4份用来训练,1份用来验证。然后我们循环着取其中4份来训练,其中1份来验证,最后取所有5次验证结果的平均值作为算法验证结果。

    在这里插入图片描述

    这就是5份交叉验证对k值调优的例子。针对每个k值,得到5个准确率结果,取其平均值,然后对不同k值的平均表现画线连接。本例中,当k=7的时算法表现最好(对应图中的准确率峰值)。如果我们将训练集分成更多份数,直线一般会更加平滑(噪音更少)。

    **实际应用。**在实际情况下,人们不是很喜欢用交叉验证,主要是因为它会耗费较多的计算资源。一般直接把训练集按照50%-90%的比例分成训练集和验证集。但这也是根据具体情况来定的:如果超参数数量多,你可能就想用更大的验证集,而验证集的数量不够,那么最好还是用交叉验证吧。至于分成几份比较好,一般都是分成3、5和10份。

    在这里插入图片描述

    常用的数据分割模式。给出训练集和测试集后,训练集一般会被均分。这里是分成5份。前面4份用来训练,黄色那份用作验证集调优。如果采取交叉验证,那就各份轮流作为验证集。最后模型训练完毕,超参数都定好了,让模型跑一次(而且只跑一次)测试集,以此测试结果评价算法。

    Nearest Neighbor分类器的优劣

    现在对Nearest Neighbor分类器的优缺点进行思考。首先,Nearest Neighbor分类器易于理解,实现简单。其次,算法的训练不需要花时间,因为其训练过程只是将训练集数据存储起来。然而测试要花费大量时间计算,因为每个测试图像需要和所有存储的训练图像进行比较,这显然是一个缺点。在实际应用中,我们关注测试效率远远高于训练效率。其实,我们后续要学习的卷积神经网络在这个权衡上走到了另一个极端:虽然训练花费很多时间,但是一旦训练完成,对新的测试数据进行分类非常快。这样的模式就符合实际使用需求。

    Nearest Neighbor分类器的计算复杂度研究是一个活跃的研究领域,若干**Approximate Nearest Neighbor (ANN)**算法和库的使用可以提升Nearest Neighbor分类器在数据上的计算速度(比如:FLANN)。这些算法可以在准确率和时空复杂度之间进行权衡,并通常依赖一个预处理/索引过程,这个过程中一般包含kd树的创建和k-means算法的运用。

    Nearest Neighbor分类器在某些特定情况(比如数据维度较低)下,可能是不错的选择。但是在实际的图像分类工作中,很少使用。因为图像都是高维度数据(他们通常包含很多像素),而高维度向量之间的距离通常是反直觉的。下面的图片展示了基于像素的相似和基于感官的相似是有很大不同的:
    在这里插入图片描述

    在高维度数据上,基于像素的的距离和感官上的非常不同。上图中,右边3张图片和左边第1张原始图片的L2距离是一样的。很显然,基于像素比较的相似和感官上以及语义上的相似是不同的。

    这里还有个视觉化证据,可以证明使用像素差异来比较图像是不够的。z这是一个叫做t-SNE的可视化技术,它将CIFAR-10中的图片按照二维方式排布,这样能很好展示图片之间的像素差异值。在这张图片中,排列相邻的图片L2距离就小。
    在这里插入图片描述

    上图使用t-SNE的可视化技术将CIFAR-10的图片进行了二维排列。排列相近的图片L2距离小。可以看出,图片的排列是被背景主导而不是图片语义内容本身主导。

    具体说来,这些图片的排布更像是一种颜色分布函数,或者说是基于背景的,而不是图片的语义主体。比如,狗的图片可能和青蛙的图片非常接近,这是因为两张图片都是白色背景。从理想效果上来说,我们肯定是希望同类的图片能够聚集在一起,而不被背景或其他不相关因素干扰。为了达到这个目的,我们不能止步于原始像素比较,得继续前进。

    小结

    简要说来:

    • 介绍了图像分类问题。在该问题中,给出一个由被标注了分类标签的图像组成的集合,要求算法能预测没有标签的图像的分类标签,并根据算法预测准确率进行评价。
    • 介绍了一个简单的图像分类器:最近邻分类器(Nearest Neighbor classifier)。分类器中存在不同的超参数(比如k值或距离类型的选取),要想选取好的超参数不是一件轻而易举的事。
    • 选取超参数的正确方法是:将原始训练集分为训练集和验证集,我们在验证集上尝试不同的超参数,最后保留表现最好那个。
    • 如果训练数据量不够,使用交叉验证方法,它能帮助我们在选取最优超参数的时候减少噪音。
    • 一旦找到最优的超参数,就让算法以该参数在测试集跑且只跑一次,并根据测试结果评价算法。
    • 最近邻分类器能够在CIFAR-10上得到将近40%的准确率。该算法简单易实现,但需要存储所有训练数据,并且在测试的时候过于耗费计算能力。
    • 最后,我们知道了仅仅使用L1和L2范数来进行像素比较是不够的,图像更多的是按照背景和颜色被分类,而不是语义主体分身。

    在接下来的课程中,我们将专注于解决这些问题和挑战,并最终能够得到超过90%准确率的解决方案。该方案能够在完成学习就丢掉训练集,并在一毫秒之内就完成一张图片的分类。

    实际应用k-NN

    如果你希望将k-NN分类器用到实处(最好别用到图像上,若是仅仅作为练手还可以接受),那么可以按照以下流程:

    • 预处理你的数据:对你数据中的特征进行归一化(normalize),让其具有零平均值(zero mean)和单位方差(unit variance)。在后面的小节我们会讨论这些细节。本小节不讨论,是因为图像中的像素都是同质的,不会表现出较大的差异分布,也就不需要标准化处理了。
    • 如果数据是高维数据,考虑使用降维方法,比如PCA(wiki ref, CS229ref, blog ref)或随机投影。
    • 将数据随机分入训练集和验证集。按照一般规律,70%-90% 数据作为训练集。这个比例根据算法中有多少超参数,以及这些超参数对于算法的预期影响来决定。如果需要预测的超参数很多,那么就应该使用更大的验证集来有效地估计它们。如果担心验证集数量不够,那么就尝试交叉验证方法。如果计算资源足够,使用交叉验证总是更加安全的(份数越多,效果越好,也更耗费计算资源)。
    • 在验证集上调优,尝试足够多的k值,尝试L1和L2两种范数计算方式。
    • 如果分类器跑得太慢,尝试使用Approximate Nearest Neighbor库(比如FLANN)来加速这个过程,其代价是降低一些准确率。
    • 对最优的超参数做记录。记录最优参数后,是否应该让使用最优参数的算法在完整的训练集上运行并再次训练呢?因为如果把验证集重新放回到训练集中(自然训练集的数据量就又变大了),有可能最优参数又会有所变化。在实践中,不要这样做。千万不要在最终的分类器中使用验证集数据,这样做会破坏对于最优参数的估计。直接使用测试集来测试用最优参数设置好的最优模型,得到测试集数据的分类准确率,并以此作为你的kNN分类器在该数据上的性能表现。
    展开全文
  • 图像分类 本教程源代码目录在book/image_classification, 初次使用请参考 PaddlePaddle 安装教程,更多内容请参考本教程的视频课堂。 背景介绍 图像相比文字能够提供更加生动、容易理解及更具艺术感的信息,是人们...

    图像分类

    本教程源代码目录在book/image_classification, 初次使用请参考 PaddlePaddle 安装教程,更多内容请参考本教程的视频课堂

    背景介绍

    图像相比文字能够提供更加生动、容易理解及更具艺术感的信息,是人们转递与交换信息的重要来源。在本教程中,我们专注于图像识别领域的一个重要问题,即图像分类

    • 图像分类是根据图像的语义信息将不同类别图像区分开来,是计算机视觉中重要的基本问题,也是图像检测、图像分割、物体跟踪、行为分析等其他高层视觉任务的基础。
    • 图像分类在很多领域有广泛应用,包括安防领域的人脸识别和智能视频分析等,交通领域的交通场景识别,互联网领域基于内容的图像检索和相册自动归类,医学领域的图像识别等。

    • 一般来说,图像分类通过手工特征或特征学习方法对整个图像进行全部描述,然后使用分类器判别物体类别,因此如何提取图像的特征至关重要。

    • 在深度学习算法之前使用较多的是基于词袋(Bag of Words)模型的物体分类方法。
    • 词袋方法从自然语言处理中引入,即一句话可以用一个装了词的袋子表示其特征,袋子中的词为句子中的单词、短语或字。对于图像而言,词袋方法需要构建字典。最简单的词袋模型框架可以设计为底层特征抽取特征编码分类器设计三个过程。

    Now:

    • 而基于深度学习的图像分类方法,可以通过有监督或无监督的方式学习层次化的特征描述,从而取代了手工设计或选择图像特征的工作。
    • 深度学习模型中的卷积神经网络( Convolution Neural Network, CNN ) 近年来在图像领域取得了惊人的成绩, CNN 直接利用图像像素信息作为输入,最大程度上保留了输入图像的所有信息,通过卷积操作进行特征的提取和高层抽象,模型输出直接是图像识别的结果。这种基于”输入-输出”直接端到端的学习方法取得了非常好的效果,得到了广泛的应用。

    本教程主要介绍图像分类的深度学习模型,以及如何使用 PaddlePaddle 训练 CNN 模型。

    效果展示

    图像分类包括通用图像分类、细粒度图像分类等。图 1 展示了通用图像分类效果,即模型可以正确识别图像上的主要物体。


    这里写图片描述
    图1. 通用图像分类展示

    图2 展示了细粒度图像分类-花卉识别的效果,要求模型可以正确识别花的类别。


    这里写图片描述
    图2. 细粒度图像分类展示

    一个好的模型既要对不同类别识别正确,同时也应该能够对不同视角、光照、背景、变形或部分遮挡的图像正确识别(这里我们统一称作图像扰动)。图3 展示了一些图像的扰动,较好的模型会像聪明的人类一样能够正确识别。


    这里写图片描述
    图3. 扰动图片展示[22]

    模型概览

    图像识别领域大量的研究成果都是建立在 PASCAL VOC ImageNet 等公开的数据集上,很多图像识别算法通常在这些数据集上进行测试和比较。 PASCAL VOC 是 2005 年发起的一个视觉挑战赛, ImageNet 是2010年发起的大规模视觉识别竞赛(ILSVRC)的数据集,在本章中我们基于这些竞赛的一些论文介绍图像分类模型。

    在 2012 年之前的传统图像分类方法可以用背景描述中提到的三步完成,但通常完整建立图像识别模型一般包括底层特征学习、特征编码、空间约束、分类器设计、模型融合等几个阶段。

    1). 底层特征提取: 通常从图像中按照固定步长、尺度提取大量局部特征描述。常用的局部特征包括 SIFT(Scale-Invariant Feature Transform, 尺度不变特征转换) [1]、HOG(Histogram of Oriented Gradient, 方向梯度直方图) [2]、LBP(Local Bianray Pattern, 局部二值模式) [3] 等,一般也采用多种特征描述子,防止丢失过多的有用信息。

    2). 特征编码: 底层特征中包含了大量冗余与噪声,为了提高特征表达的鲁棒性,需要使用一种特征变换算法对底层特征进行编码,称作特征编码。常用的特征编码包括向量量化编码 [4]、稀疏编码 [5]、局部线性约束编码 [6]、Fisher 向量编码 [7] 等。

    3). 空间特征约束: 特征编码之后一般会经过空间特征约束,也称作特征汇聚。特征汇聚是指在一个空间范围内,对每一维特征取最大值或者平均值,可以获得一定特征不变形的特征表达。金字塔特征匹配是一种常用的特征聚会方法,这种方法提出将图像均匀分块,在分块内做特征汇聚。

    4). 通过分类器分类: 经过前面步骤之后一张图像可以用一个固定维度的向量进行描述,接下来就是经过分类器对图像进行分类。通常使用的分类器包括 SVM (Support Vector Machine, 支持向量机)、随机森林等。而使用核方法的 SVM 是最为广泛的分类器,在传统图像分类任务上性能很好。

    这种方法在 PASCAL VOC 竞赛中的图像分类算法中被广泛使用 [18]。NEC实验室在 ILSVRC2010 中采用 SIFT 和 LBP 特征,两个非线性编码器以及 SVM 分类器获得图像分类的冠军 [8]。

    Alex Krizhevsky 在 2012 年 ILSVRC 提出的 CNN 模型 [9] 取得了历史性的突破,效果大幅度超越传统方法,获得了 ILSVRC2012 冠军,该模型被称作 AlexNet 。这也是首次将深度学习用于大规模图像分类中。从 AlexNet 之后,涌现了一系列 CNN 模型,不断地在 ImageNet 上刷新成绩,如图4展示。随着模型变得越来越深以及精妙的结构设计,Top-5 的错误率也越来越低,降到了 3.5% 附近。而在同样的 ImageNet 数据集上,人眼的辨识错误率大概在 5.1%,也就是目前的深度学习模型的识别能力已经超过了人眼。


    这里写图片描述
    图4. ILSVRC 图像分类 Top-5错误率

    CNN

    传统CNN 包含卷积层、全连接层等组件,并采用 softmax 多类别分类器和多类交叉熵损失函数,一个典型的卷积神经网络如图5所示,我们先介绍用来构造 CNN 的常见组件。


    这里写图片描述
    图5. CNN 网络示例[20]

    • 卷积层(convolution layer): 执行卷积操作提取底层到高层的特征,发掘出图片局部关联性质空间不变性质
    • 池化层(pooling layer): 执行降采样操作。通过取卷积输出特征图中局部区块的最大值(max-pooling)或者均值(avg-pooling)。降采样也是图像处理中常见的一种操作,可以过滤掉一些不重要的高频信息。
    • 全连接层(fully-connected layer,或者fc layer): 输入层到隐藏层的神经元是全部连接的。
    • 非线性变化: 卷积层、全连接层后面一般都会接非线性变化层,例如 Sigmoid、Tanh、ReLu 等来增强网络的表达能力,在CNN 里最常使用的为 ReLu 激活函数。
    • Dropout [10] : 在模型训练阶段随机让一些隐层节点权重不工作,提高网络的泛化能力,一定程度上防止过拟合。

    另外,在训练过程中由于每层参数不断更新,会导致下一次输入分布发生变化,这样导致训练过程需要精心设计超参数。如 2015 年 Sergey Ioffe 和 Christian Szegedy 提出了 Batch Normalization (BN)算法 [14] 中,每个 batch 对网络中的每一层特征都做归一化,使得每层分布相对稳定。

    BN 算法不仅起到一定的正则作用,而且弱化了一些超参数的设计。经过实验证明,BN算法加速了模型收敛过程,在后来较深的模型中被广泛使用。

    接下来我们主要介绍 VGG ,GoogleNet和 ResNet 网络结构。

    VGG

    牛津大学 VGG (Visual Geometry Group)组在 2014 年 ILSVRC 提出的模型被称作 VGG 模型 [11] 。该模型相比以往模型进一步加宽和加深了网络结构,它的核心是五组卷积操作,每两组之间做 Max-Pooling 空间降维。同一组内采用多次连续的 3X3 卷积,卷积核的数目由较浅组的 64 增多到最深组的 512,同一组内的卷积核数目是一样的。

    卷积之后接两层全连接层,之后是分类层。由于每组内卷积层的不同,有11、13、16、19层这几种模型,下图展示一个 16 层的网络结构。 VGG 模型结构相对简洁,提出之后也有很多文章基于此模型进行研究,如在 ImageNet 上首次公开超过人眼识别的模型[19]就是借鉴 VGG 模型的结构。


    这里写图片描述
    图6. 基于 ImageNet 的 VGG 16模型

    GoogleNet

    GoogleNet [12] 在 2014 年 ILSVRC 的获得了冠军,在介绍该模型之前我们先来了解 NIN(Network in Network) 模型 [13] 和 Inception 模块,因为GoogleNet 模型由多组 Inception 模块组成,模型设计借鉴了 NIN 的一些思想。

    NIN 模型主要有两个特点:

    1) 引入了多层感知卷积网络 (Multi-Layer Perceptron Convolution, MLPconv)代替一层线性卷积网络。MLPconv 是一个微小的多层卷积网络,即在线性卷积后面增加若干层 1x1 的卷积,这样可以提取出高度非线性特征。

    2) 传统的 CNN 最后几层一般都是全连接层,参数较多。而 NIN 模型设计最后一层卷积层包含类别维度大小的特征图,然后采用全局均值池化 (Avg-Pooling) 替代全连接层,得到类别维度大小的向量,再进行分类。这种替代全连接层的方式有利于减少参数。

    Inception 模块如下图 7 所示,图 (a) 是最简单的设计,输出是 3 个卷积层和一个池化层的特征拼接。这种设计的缺点是池化层不会改变特征通道数,拼接后会导致特征的通道数较大,经过几层这样的模块堆积后,通道数会越来越大,导致参数和计算量也随之增大。为了改善这个缺点,图(b) 引入 3 个 1x1 卷积层进行降维,所谓的降维就是减少通道数,同时如 NIN 模型中提到的 1x1 卷积也可以修正线性特征。


    这里写图片描述
    图7. Inception 模块

    GoogleNet 由多组 Inception 模块堆积而成。另外,在网络最后也没有采用传统的多层全连接层,而是像 NIN 网络一样采用了均值池化层;但与 NIN 不同的是,池化层后面接了一层到类别数映射的全连接层。除了这两个特点之外,由于网络中间层特征也很有判别性,GoogleNet 在中间层添加了两个辅助分类器,在后向传播中增强梯度并且增强正则化,而整个网络的损失函数是这个三个分类器的损失加权求和。

    GoogleNet 整体网络结构如图 8 所示,总共 22 层网络:开始由 3 层普通的卷积组成;接下来由三组子网络组成,第一组子网络包含 2 个 Inception 模块,第二组包含 5 个 Inception 模块,第三组包含 2 个 Inception 模块;然后接均值池化层、全连接层。


    图8. GoogleNet[12]

    上面介绍的是 GoogleNet 第一版模型(称作GoogleNet-v1)。GoogleNet-v2 [14] 引入 BN 层;GoogleNet-v3 [16] 对一些卷积层做了分解,进一步提高网络非线性能力和加深网络;GoogleNet-v4 [17] 引入下面要讲的 ResNet 设计思路。从v1 到 v4 每一版的改进都会带来准确度的提升,介于篇幅,这里不再详细介绍 v2 到 v4 的结构。

    ResNet

    ResNet (Residual Network) [15] 是 2015 年 ImageNet 图像分类、图像物体定位和图像物体检测比赛的冠军。针对训练卷积神经网络时加深网络导致准确度下降的问题, ResNet 提出了采用残差学习。在已有设计思路( BN, 小卷积核,全卷积网络)的基础上,引入了残差模块。每个残差模块包含两条路径,其中一条路径是输入特征的直连通路,另一条路径对该特征做两到三次卷积操作得到该特征的残差,最后再将两条路径上的特征相加。

    扩展阅读:深度学习(二十九)Batch Normalization 学习笔记

    残差模块如图 9 所示,左边是基本模块连接方式,由两个输出通道数相同的 3x3 卷积组成。右边是瓶颈模块( Bottleneck)连接方式,之所以称为瓶颈,是因为上面的 1x1 卷积用来降维(图示例即 256->64),下面的 1x1 卷积用来升维(图示例即 64->256 ),这样中间 3x3 卷积的输入和输出通道数都较小(图示例即 64->64)。


    图9. 残差模块

    图10展示了 50、101、152 层网络连接示意图,使用的是瓶颈模块。这三个模型的区别在于每组中残差模块的重复次数不同(见图右上角)。 ResNet 训练收敛较快,成功的训练了上百乃至近千层的卷积神经网络。


    这里写图片描述
    图10. 基于 ImageNet 的 ResNet 模型

    数据准备

    通用图像分类公开的标准数据集常用的有CIFAR ImageNet COCO等,常用的细粒度图像分类数据集包括CUB-200-2011、[Stanford Dog](http://vision.stanford.edu/aditya86/ ImageNet Dogs/)、Oxford-flowers等。其中 ImageNet 数据集规模相对较大,如模型概览一章所讲,大量研究成果基于 ImageNet 。

    ImageNet 数据从2010年来稍有变化,常用的是 ImageNet -2012数据集,该数据集包含 1000个类别:训练集包含1,281,167张图片,每个类别数据 732 至 1300 张不等,验证集包含 50,000张图片,平均每个类别50张图片。

    由于 ImageNet 数据集较大,下载和训练较慢,为了方便大家学习,我们使用CIFAR10数据集。CIFAR10 数据集包含 60,000 张32x32 的彩色图片,10 个类别,每个类包含 6,000 张。其中 50,000 张图片作为训练集,10000 张作为测试集。图11从每个类别中随机抽取了 10 张图片,展示了所有的类别。


    这里写图片描述
    图11. CIFAR10数据集[21]

    Paddle API 提供了自动加载 cifar 数据集模块 paddle.dataset.cifar

    通过输入python train.py,就可以开始训练模型了,以下小节将详细介绍train.py的相关内容。

    模型结构

    Paddle 初始化

    通过 paddle.init,初始化 Paddle 是否使用 GPU, trainer 的数目等等。

    import sys
    import paddle.v2 as paddle
    from vgg import vgg_bn_drop
    from resnet import resnet_cifar10
    
    #   PaddlePaddle  init
    paddle.init(use_gpu=False, trainer_count=1)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    本教程中我们提供了 VGG 和 ResNet 两个模型的配置。

    VGG

    首先介绍 VGG 模型结构,由于 CIFAR10 图片大小和数量相比 ImageNet 数据小很多,因此这里的模型针对 CIFAR10 数据做了一定的适配。卷积部分引入了 BN 和 Dropout 操作。

    1. 定义数据输入及其维度

    网络输入定义为 data_layer (数据层),在图像分类中即为图像像素信息。CIFRAR10 是RGB 3 通道 32x32 大小的彩色图,因此输入数据大小为 3072(3x32x32),类别大小为 10,即 10分类。

    datadim = 3 * 32 * 32
    classdim = 10
    
    image = paddle.layer.data(
        name="image", type=paddle.data_type.dense_vector(datadim))
    • 1
    • 2
    • 3
    • 4
    • 5
  • 定义 VGG 网络核心模块

  • net = vgg_bn_drop(image)
    • 1

    VGG 核心模块的输入是数据层,vgg_bn_drop 定义了 16 层 VGG 结构,每层卷积后面引入 BN 层和 Dropout 层,详细的定义如下:

    def vgg_bn_drop(input):
        def conv_block(ipt, num_filter, groups, dropouts, num_channels=None):
            return paddle.networks.img_conv_group(
                input=ipt,
                num_channels=num_channels,
                pool_size=2,
                pool_stride=2,
                conv_num_filter=[num_filter] * groups,
                conv_filter_size=3,
                conv_act=paddle.activation.Relu(),
                conv_with_batchnorm=True,
                conv_batchnorm_drop_rate=dropouts,
                pool_type=paddle.pooling.Max())
    
        # 函数嵌套 conv_block() 中的数字对应的是 上述参数说明 
        # 第三个参数 groups 代表执行这一个卷积 执行几次 2+2+3+3+3 = 13
        # 后面 还有 fc1  fc2 两层全连接 调用完该函数后,还会有 out softmax 输出层
        conv1 = conv_block(input, 64, 2, [0.3, 0], 3)
        conv2 = conv_block(conv1, 128, 2, [0.4, 0])
        conv3 = conv_block(conv2, 256, 3, [0.4, 0.4, 0])
        conv4 = conv_block(conv3, 512, 3, [0.4, 0.4, 0])
        conv5 = conv_block(conv4, 512, 3, [0.4, 0.4, 0])
    
        # 随机失活率 dropout_rate=0.5
        drop = paddle.layer.dropout(input=conv5, dropout_rate=0.5)
        fc1 = paddle.layer.fc(input=drop, size=512, act=paddle.activation.Linear())
        # 归一化算法
        bn = paddle.layer.batch_norm(
            input=fc1,
            act=paddle.activation.Relu(),
            layer_attr=paddle.attr.Extra(drop_rate=0.5))
        fc2 = paddle.layer.fc(input=bn, size=512, act=paddle.activation.Linear())
        return fc2
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    2.1. 首先定义了一组卷积网络,即 conv_block。卷积核大小为 3x3,池化窗口大小为2x2,窗口滑动大小为 2,groups 决定每组 VGG 模块是几次连续的卷积操作,dropouts 指定Dropout 操作的概率。所使用的img_conv_group是在paddle.networks中预定义的模块,由若干组 Conv->BN->ReLu->Dropout 和 一组 Pooling 组成。

    2.2. 五组卷积操作,即 5 个 conv_block。 第一、二组采用两次连续的卷积操作。第三、四、五组采用三次连续的卷积操作。每组最后一个卷积后面 Dropout 概率为 0,即不使用Dropout 操作。

    2.3. 最后接两层 512 维的全连接。

  • 定义分类器

  • 通过上面 VGG 网络提取高层特征,然后经过全连接层映射到类别维度大小的向量,再通过 Softmax 归一化得到每个类别的概率,也可称作分类器。

    out = paddle.layer.fc(input=net,
                          size=classdim,
                          act=paddle.activation.Softmax())
    • 1
    • 2
    • 3
  • 定义损失函数和网络输出

  • 在有监督训练中需要输入图像对应的类别信息,同样通过paddle.layer.data来定义。训练中采用多类交叉熵作为损失函数,并作为网络的输出,预测阶段定义网络的输出为分类器得到的概率信息。

    lbl = paddle.layer.data(
        name="label", type=paddle.data_type.integer_value(classdim))
    cost = paddle.layer.classification_cost(input=out, label=lbl)
    • 1
    • 2
    • 3

    ResNet

    ResNet 模型的第1、3、4步和 VGG 模型相同,这里不再介绍。主要介绍第 2 步即 CIFAR10 数据集上 ResNet 核心模块。

    net = resnet_cifar10(image, depth=56)
    • 1

    先介绍resnet_cifar10中的一些基本函数,再介绍网络连接过程。

    • conv_bn_layer : 带 BN 的卷积层。
    • shortcut : 残差模块的”直连”路径,”直连”实际分两种形式:残差模块输入和输出特征通道数不等时,采用 1x1 卷积的升维操作;残差模块输入和输出通道相等时,采用直连操作。
    • basicblock : 一个基础残差模块,即图9 左边所示,由两组 3x3 卷积组成的路径和一条”直连”路径组成。
    • bottleneck : 一个瓶颈残差模块,即图9 右边所示,由上下 1x1 卷积和中间 3x3 卷积组成的路径和一条”直连”路径组成。
    • layer_warp : 一组残差模块,由若干个残差模块堆积而成。每组中第一个残差模块滑动窗口大小与其他可以不同,以用来减少特征图在垂直和水平方向的大小。
    def conv_bn_layer(input,
                      ch_out,
                      filter_size,
                      stride,
                      padding,
                      active_type=paddle.activation.Relu(),
                      ch_in=None):
        tmp = paddle.layer.img_conv(
            input=input,
            filter_size=filter_size,
            num_channels=ch_in,
            num_filters=ch_out,
            stride=stride,
            padding=padding,
            act=paddle.activation.Linear(),
            bias_attr=False)
        return paddle.layer.batch_norm(input=tmp, act=active_type)
    
    def shortcut(ipt, n_in, n_out, stride):
        if n_in != n_out:
            return conv_bn_layer(ipt, n_out, 1, stride, 0,
                                 paddle.activation.Linear())
        else:
            return ipt
    
    def basicblock(ipt, ch_out, stride):
        ch_in = ch_out * 2
        tmp = conv_bn_layer(ipt, ch_out, 3, stride, 1)
        tmp = conv_bn_layer(tmp, ch_out, 3, 1, 1, paddle.activation.Linear())
        short = shortcut(ipt, ch_in, ch_out, stride)
        return paddle.layer.addto(input=[tmp, short], act=paddle.activation.Relu())
    
    def layer_warp(block_func, ipt, features, count, stride):
        tmp = block_func(ipt, features, stride)
        for i in range(1, count):
            tmp = block_func(tmp, features, 1)
        return tmp
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    resnet_cifar10 的连接结构主要有以下几个过程。

    1. 底层输入连接一层 conv_bn_layer,即带 BN 的卷积层。
    2. 然后连接 3 组残差模块即下面配置 3 组 layer_warp ,每组采用图 10 左边残差模块组成。
    3. 最后对网络做均值池化并返回该层。

    注意:除过第一层卷积层和最后一层全连接层之外,要求三组 layer_warp 总的含参层数能够被 6 整除,即 resnet_cifar10 的 depth 要满足 (depth2)(depth−2)

    def resnet_cifar10(ipt, depth=32):
        # depth should be one of 20, 32, 44, 56, 110, 1202
        assert (depth - 2) % 6 == 0
        n = (depth - 2) / 6
        nStages = {16, 64, 128}
        conv1 = conv_bn_layer(
            ipt, ch_in=3, ch_out=16, filter_size=3, stride=1, padding=1)
        res1 = layer_warp(basicblock, conv1, 16, n, 1)
        res2 = layer_warp(basicblock, res1, 32, n, 2)
        res3 = layer_warp(basicblock, res2, 64, n, 2)
        pool = paddle.layer.img_pool(
            input=res3, pool_size=8, stride=1, pool_type=paddle.pooling.Avg())
        return pool
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    训练模型

    定义参数

    首先依据模型配置的cost定义模型参数。

    # Create parameters
    parameters = paddle.parameters.create(cost)
    • 1
    • 2

    可以打印参数名字,如果在网络配置中没有指定名字,则默认生成。

    print parameters.keys()
    • 1

    构造训练(Trainer)

    根据网络拓扑结构和模型参数来构造出 trainer 用来训练,在构造时还需指定优化方法,这里使用最基本的 Momentum 方法,同时设定了学习率、正则等。

    # Create optimizer
    momentum_optimizer = paddle.optimizer.Momentum(
        momentum=0.9,
        regularization=paddle.optimizer.L2Regularization(rate=0.0002 * 128),
        learning_rate=0.1 / 128.0,
        learning_rate_decay_a=0.1,
        learning_rate_decay_b=50000 * 100,
        learning_rate_schedule='discexp')
    
    # Create  trainer 
     trainer  = paddle. trainer .SGD(cost=cost,
                                 parameters=parameters,
                                 update_equation=momentum_optimizer)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    通过 learning_rate_decay_a (简写aa 即为 settings 里设置的 learning_rate

    lr=lr0anblr=lr0∗a⌊nb⌋

    训练

    cifar.train10() 每次产生一条样本,在完成 shuffle 和 batch 之后,作为训练的输入。

    reader=paddle.batch(
        paddle.reader.shuffle(
            paddle.dataset.cifar.train10(), buf_size=50000),
            batch_size=128)
    • 1
    • 2
    • 3
    • 4

    通过 feeding 来指定每一个数据和 paddle.layer.data 的对应关系。例如: cifar.train10() 产生数据的第 0 列对应 image 层的特征。

    feeding={'image': 0,
             'label': 1}
    • 1
    • 2

    可以使用 event_handler 回调函数来观察训练过程,或进行测试等, 该回调函数是trainer .train 函数里设定。

    event_handler_plot 可以用来利用回调数据来打点画图:

    这里写图片描述

    from paddle.v2.plot import Ploter
    
    train_title = "Train cost"
    test_title = "Test cost"
    cost_ploter = Ploter(train_title, test_title)
    
    step = 0
    def event_handler_plot(event):
        global step
        if isinstance(event, paddle.event.EndIteration):
            if step % 1 == 0:
                cost_ploter.append(train_title, step, event.cost)
                cost_ploter.plot()
            step += 1
        if isinstance(event, paddle.event.EndPass):
    
            result =  trainer .test(
                reader=paddle.batch(
                    paddle.dataset.cifar.test10(), batch_size=128),
                feeding=feeding)
            cost_ploter.append(test_title, step, result.cost)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    event_handler 用来在训练过程中输出文本日志

    # End batch and end pass  event handler
    def event_handler(event):
        if isinstance(event, paddle.event.EndIteration):
            if event.batch_id % 100 == 0:
                print "\nPass %d, Batch %d, Cost %f, %s" % (
                    event.pass _id, event.batch_id, event.cost, event.metrics)
            else:
                sys.stdout.write('.')
                sys.stdout.flush()
        if isinstance(event, paddle.event.EndPass):
            # save parameters
            with open('params_pass _%d.tar' % event.pass _id, 'w') as f:
                 trainer .save_parameter_to_tar(f)
    
            result =  trainer .test(
                reader=paddle.batch(
                    paddle.dataset.cifar.test10(), batch_size=128),
                feeding=feeding)
            print "\nTest with Pass %d, %s" % (event.pass _id, result.metrics)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    通过trainer.train函数训练:

     trainer.train(
        reader=reader,
        num_passes=200,
        event_handler=event_handler_plot,
        feeding=feeding)
    • 1
    • 2
    • 3
    • 4
    • 5

    一轮训练 log 示例如下所示,经过 1 个 pass , 训练集上平均 error 为 0.6875 ,测试集上平均 error 为0.8852 。

    Pass 0, Batch 0, Cost 2.473182, {'classification_ error _evaluator': 0.9140625}
    ...................................................................................................
    Pass 0, Batch 100, Cost 1.913076, {'classification_ error _evaluator': 0.78125}
    ...................................................................................................
    Pass 0, Batch 200, Cost 1.783041, {'classification_ error _evaluator': 0.7421875}
    ...................................................................................................
    Pass 0, Batch 300, Cost 1.668833, {'classification_ error _evaluator': 0.6875}
    ..........................................................................................
    Test with Pass 0, {'classification_ error _evaluator': 0.885200023651123}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    图12 是训练的分类错误率曲线图,运行到第 200 个 pass 后基本收敛,最终得到测试集上分类错误率为 8.54%。


    这里写图片描述
    图12. CIFAR10 数据集上 VGG 模型的分类错误率

    应用模型

    可以使用训练好的模型对图片进行分类,下面程序展示了如何使用paddle.infer接口进行推断,可以打开注释,更改加载的模型。

    from PIL import Image
    import numpy as np
    import os
    def load_image(file):
        im = Image.open(file)
        im = im.resize((32, 32), Image.ANTIALIAS)
        im = np.array(im).astype(np.float32)
        # PIL 打开图片存储顺序为 H(高度),W(宽度),C(通道)。
        #  PaddlePaddle 要求数据顺序为 CHW,所以需要转换顺序。
        im = im.transpose((2, 0, 1)) # CHW
        # CIFAR 训练图片通道顺序为B(蓝),G(绿),R(红),
        # 而PIL打开图片默认通道顺序为RGB,因为需要交换通道。
        im = im[(2, 1, 0),:,:] # BGR
        im = im.flatten()
        im = im / 255.0
        return im
    
    test_data = []
    cur_dir = os.getcwd()
    test_data.append((load_image(cur_dir + '/image/dog.png'),))
    
    # with open('params_pass _50.tar', 'r') as f:
    #    parameters = paddle.parameters.Parameters.from_tar(f)
    
    probs = paddle.infer(
        output_layer=out, parameters=parameters, input=test_data)
    lab = np.argsort(-probs) # probs and lab are the results of one batch data
    print "Label of image/dog.png is: %d" % lab[0][0]
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    总结

    传统图像分类方法由多个阶段构成,框架较为复杂,而端到端的 CNN 模型结构可一步到位,而且大幅度提升了分类准确率。

    • 本文我们首先介绍 VGG 、GoogleNet、 ResNet 三个经典的模型;
    • 然后基于 CIFAR10 数据集,介绍如何使用 PaddlePaddle 配置和训练CNN 模型,尤其是 VGG 和 ResNet 模型;
    • 最后介绍如何使用 PaddlePaddle 的 API 接口对图片进行预测和特征提取。对于其他数据集比如 ImageNet ,配置和训练流程是同样的,大家可以自行进行实验。
    展开全文
  • 用于超参数调优的验证集首先,来介绍参数和超参数的基本知识。在机器学习或者深度学习领域,参数和超参数是一个常见的问题,个人根据经验给出了一个很狭隘的区分这两种参数的方法。参...
  • 简介  传统监督学习主要是单标签学习,而现实生活中目标样本往往比较复杂,具有多个语义,含有多个标签。... (1)传统单标签分类  city(person)  (2)多标签分类  city , river, person, Eu...
  • 安装OpenCV二、交通标志分类项目1. 简介2. 神经网络框架3. 项目代码4. 研读代码(1)分析lenet.py代码(2)分析train.py代码(3)分析predict.py代码(5)上传代码(6)运行代码(7)运行结果 一、完成keras环...
  • 这是译自斯坦福CS231n课程笔记image classification notes,由课程教师Andrej Karpathy授权进行翻译。本篇教程由杜客翻译完成。非常...
  • 图像分类的任务是判断该图像所属类别。 2. 常用数据集 难度递增:MNIST、CIFAR-10、CIFAR-100、Image-Net 3. 分类经典网络结构 LeNet-5、AlexNet、VGG-16 / VGG-19、GoogLeNet、Inception v3 / v4、ResNet、...
  • 文章发布于公号【数智物语】 ...图像分类是计算机视觉中最基础的任务,基本上深度学习模型的发展史就是图像分类任务提升的发展历史,不过图像分类并不是那么简单,也没有被完全解决。   01 什么是图像分类...
  • 常用图像分类网络

    2018-10-30 22:27:15
    想对图像分类网络写个简要的概括,如有介绍不当之处,还望指出。 一、VGG网络 更新于2018年10月20日 参考博客:深度学习经典卷积神经网络之VGGNet 论文地址:VERY DEEP CONVOLUTIONAL NETWORKS FOR LARGE-SCALE ...
  • 摘要:本文通过图文详细介绍如何利用支持...点我下载:SVM图像分类的MATLAB完整程序及图片集文件 1. 前言 机器学习是人工智能研究发展到一定阶段的必然产物。二十世纪八十年代是机器学习成为一个独立学...
  • 这篇文章从什么是图像分类任务开始一步步详细介绍支持向量机原理,以及如何用它解决图像多分类任务。将这部分内容分为上下两篇:上篇重点详细介绍实现原理,下篇衔接上篇进行编程实现并对程序进行解释,本篇为上篇。...
  • 1. 图像分类以及基本流程 1.1 什么是图像分类   所谓图像分类问题,就是已有固定的分类标签集合,然后对于输入的图像,从分类标签集合中找出一个分类标签,最后把分类标签分配给该输入图像。虽然看起来挺简单的...
  • 深度学习之图像分类

    2019-05-23 11:49:26
    图像分类,核心是从给定的分类集合中给图像分配一个标签的任务。实际上,这意味着我们的任务是分析一个输入图像并返回一个将图像分类的标签。标签总是来自预定义的可能类别集。 示例:我们假定一个可能的类别集...
  • 遥感图像分类技术

    2019-06-05 21:06:10
    什么是遥感图像分类技术? 图像分类是将土地覆盖类别分配给像素的过程。例如,这9个全球土地覆盖数据集将图像分为森林、城市、农业和其他类别。 https://gisgeography.com/free-global-land-cover-land-use-data/ ...
  • 如何利用稀疏表示进行高光谱图像分类呢?前面我们已经了解了高光谱图像分类的一些基本概念,那这篇文章当中将讲解高光谱图像分类具体的流程是怎么样的。以下是高光谱图像分类的具体详细步骤: 1.导入indian_pines高...
  • 一、图像分类 二、图像识别 三、图像检索 四、三者的联系 五、三者的区别 六、人脸检测 七、人脸识别 一、图像分类  图像分类是给定一幅测试图像,利用训练好的分类器判定它所属的类别,而分类器是利用带...
1 2 3 4 5 ... 20
收藏数 216,585
精华内容 86,634
关键字:

图像分类