Keras 深度学习之猫狗大战

项目地址和代码:Project_Dogs_vs_Cats
项目详细报告:Report_dogs_vs_cats.pdf
keras 版本:2.1.5


1.问题定义和数据集获取:
        项目属于计算机视觉领域中的图像分类问题。图像分类的过程非常明确:给定已经标记的数据集,提取特征,训练得到分类器。项目使用Kaggle竞赛提供的Dogs vs. Cats数据集,任务是对给定的猫和狗的图片进行分类,因此是二分类问题(Binary Classification)。
        项目要解决的问题是使用12500张猫和12500张狗的图片作为测试集,训练一个合适的模型,能够在给定的12500张未见过的图像中分辨猫和狗。在机器学习领域,用于分类的策略,包括K均值聚类、支持向量机等,均能够用于处理该二分类问题。但在图像分类领域,神经网络技术具有更加明显的优势,特别是深度卷积神经网络,已成功地应用于图像识别领域。

2.选择度量成功的标准(损失函数):
        
分类准确度(accuracy)和代价函数(Cost Function)是常用的分类评估指标。为了对模型进行更细致的评价,代价函数更加合理。通过代价函数计算结果的值越小,就代表模型拟合的越好。神经网络的代价函数是用于logistic回归的一个泛化。Kaggle官网在此次竞赛中对预测结果的评估采用的是对数损失函数log loss:
                                                  \textrm{LogLoss} = - \frac{1}{n} \sum_{i=1}^n \left[ y_i \log(\hat{y}_i) + (1 - y_i) \log(1 - \hat{y}_i)\right],                                                           
        式中, n为测试集图像总数量; \hat{y}_{i}指图像类别为狗的预测概率;如果图像类别为狗, \hat{y}_{i}为1,如果为猫, \hat{y}_{i}为0。对数损失值越小,预测结果越优。

3.确定评估方法:
        
三种常见的评估方法(evaluation protocols):(1)维持一个验证集不变,这通常在数据充足的情况下使用;(2)k-折交叉验证,数据量较少情况下;(2)迭代的k-折交叉验证,数据量较少而要求高精度模型评价时使用。这里选择第一种方法。
        
4.准备数据(数据预处理):
        训练集包括25000张被标记的猫和狗的图片(各占一半)。测试集为12500张未被标记的图片,同样是猫狗图片各占一半。测试集将作为输入来训练分类器。在数据用于训练前须进行预处理,使数据格式与模型相匹配。另外,数据预处理还可以应用数据增强技术,包括图片裁剪或填充、归一化。如果计算资源有限,还可以将图像转化为灰度图像存储。数据集的部分图片如图所示:
 

Keras 深度学习之猫狗大战_第1张图片


        训练集图像的尺寸分布如下图所示。通过该散点图,我们可以非常直观地了解数据的构成:绝大部分图像的尺寸都分布在500×500内。猫和狗的图片各自都只有一张图像的尺寸偏离正常范围内,属于异常值。于是下一步,我们首先找出这两幅图像。
 

Keras 深度学习之猫狗大战_第2张图片


      这两幅图像如下方左图所示。但是,我们的图像在输入模型训练前,是需要进行数据预处理的,例如我们可能对图像进行尺寸缩放或裁剪以使输入数据格式统一。图像裁剪可能会影响模型的训练,因为它可能将图像的有用信息丢弃。而在这个项目中,我们将只使用尺寸缩放。缩放后的猫狗图像如下方右图所示,我们还是可以很确定地辨别出猫和狗,这说明了图像缩放这种预处理方式并不会对模型分类带来坏影响,并不影响其学习,所以这对异常值是没有必要处理的!
                                                     Keras 深度学习之猫狗大战_第3张图片               Keras 深度学习之猫狗大战_第4张图片

       既然图像的尺寸对模型的训练不会带来影响或者影响甚微,那么便接着探索数据集是否存在其他异常值。由于读取图像时三个通道的值都限制在[0,255]内,分布范围较小,因此图像通道值的最大值和最小值和均值都不会成为异常值。因而,将目光投向通道值的标准差,原因是通过标准差更可能发现异常值。设想一下,假如一幅图像中通道值的标准差很小,这意味着图像越接近于纯色,则其包含的信息量越少,越不容易用于提取可用于分类的特征。为了简化问题,将图像读取为灰度图,这样就只需要分析单通道了。
       下面两幅图像分别是所有猫狗图像中,灰度图模型下,通道值标准差最小的情况。可以看到,虽然图像颜色比较单一,但是我们还是能够通过人眼清晰地识别出猫和狗,因此这部分不存在异常值!
 

Keras 深度学习之猫狗大战_第5张图片

       训练数据的异常值还有另一种情况,那就是类别标签标注上的异常值。寻找这些异常值最直接但同时也是最繁琐的方法,就是耐心地将训练集里面的图片都手动检查一遍!但事实上我们可以用其他方式解决,那就是使用预处理模型来进行排查,这将大大减少我们的工作量。基本思路是,使用表现最佳的预处理模型(这里选择在Imagenet上表现很好的Xception)对训练集的图片进行预测,给出预测正确分类概率最高的n个分类。检验猫、狗标签是否在这n个类别之内,若不在则将该图片视为可能的异常值。
       利用Xception预测后,使用 decode_predictions ,设置top参数为n(一开始均设置为30),将预测结果转化,获得预测正确分类概率最高的n个分类。检验猫、狗标签是否在这n个类别之内,若不在则将图像视为可能的异常值。此时,猫狗图片中的可能异常值数量分别为80和23,如下图所示为部分查找得到的可能异常值。
 

Keras 深度学习之猫狗大战_第6张图片

                                                                     图a. 猫图片可能异常值(Top参数均为30
 

Keras 深度学习之猫狗大战_第7张图片

                                                                  图b. 狗图片可能异常值(Top参数均为30
       可以看到,当top参数均设置为30时,猫图片的异常值统计数量较狗图片多,同时错误统计也多。对于狗图片的异常值统计情况,则几乎没有失误。这可能是由于imagenet在构建数据集时对狗的归类做的比较详细(imagenet上狗的类别有118种,而猫的类别只有7种)。因此,我认为设置top参数时,应该将猫和狗分开讨论,不能一概而论。经过一番尝试,我将猫的top参数设置为35,狗的top参数设置为10,最后获得的部分可能的异常值如下图所示,其中猫有62幅,狗有36幅。相较于完全手动检查一遍,这已经大大降低了工作量。
 

Keras 深度学习之猫狗大战_第8张图片


                                                                 图a. 猫图片可能异常值(Top参数均为35
 

Keras 深度学习之猫狗大战_第9张图片

                                                                 图b. 狗图片可能异常值(Top参数均为10

       从上面的分析可以看到,猫狗大战数据集的异常值主要是标注上的异常值。在将训练数据输入模型训练之前,需要将这些异常数据进行预处理。我将考虑一下处理方式:(1)将与主题完全无关的图片删除;(2)对于分类错误的图片,修改类别标注(例如本来是狗的图片却标注为猫,这时候就需要将标注修改);(3)将背景复杂的图片进行裁剪。
        这些异常值很难通过数据科学的方法(均值、平均值等)进行描述和发现,在处理的时候选择也只能手动处理。


5.设计一个优于随机预测的简单模型:
       对于图片分类问题,存在一些分类效果很好的深度卷积网络,例如AlexNet、Inception,但在一开始,我们会先使用简单的分类模型进行训练并观察其表现。在该项目中,我首先建立了如下结构的简单模型,它的分类准确性确实要明显优于随机分类:

Keras 深度学习之猫狗大战_第10张图片


       可以看到,该简单CNN模型由若干个层“堆叠”而成,且可以分成这三部分:(1)输入层;(2)卷积层和池化层的组合;(3)全连接的多层感知机分类器。在需要处理过拟合问题时通常还会引入dropout层。

6.设计过拟合模型:
       
自己从头设计CNN有时并不是一个好的决定,它既费时而且又不一定能获得优秀的表现。所以,在该项目将采用迁移学习的方法,其中使用的用于fine tune的预训练模型包括 VGG16,ResNet50,Inception v3和Xception。
       本项目中,在使用各个预训练模型时,都采取了同一个流程。首先,尝试使用特征提取方法。第一步,加载去掉顶层分类器的模型(留下卷积层)之结构和权重,并将训练数据输入模型,提取特征向量;第二步,为卷积层添加全连接层和drop out层,输入上一步提取的特征向量进行分类,观察分类结果并评价。第三步,为卷积层添加一个包含卷积核的顶层分类器,将加载的卷积基础层冻结(不冻结顶层分类器的卷积核),重新编译模型。输入图片数据进行训练,得到一个训练过的顶层分类器。之后,尝试使用参数微调方法。在使用特征提取手段时,我们已经得到了一个包含卷积层的顶层分类器。现在,可以将冻结的卷积层解开部分层,与顶层分类器一起训练。参数微调就是微调加载的模型的部分卷积层以及新加入的顶层分类器的权重。
       这里需要注意的是:为了顺利地进行参数微调,模型的各个层都需要以预先训练过的权重为初始值。所以,不能将随机初始化权重的全连接层放在预训练过的卷积层之上,否则会破坏卷积层预训练获得的权重。体现在前面谈到的流程中,就是先使用特征提取方法训练顶层分类器,再基于这个顶层分类器进行参数微调。
       另外,参数微调时不会选择训练整个网络的权重,而只微调位于模型中较深的部分卷积层,这在一定程度上可以防止过拟合。在前面已经提到,因为由底层卷积模块学习到的特征更具一般性,而不是抽象性。
       对于VGG16模型,我首先尝试选择将其卷积模块的末尾3个卷积层冻结然后再进行参数微调,但效果并不理想。于是我又将冻结层扩大至末尾4个卷积层,进行参数微调;对于InceptionV3,选择将249层之前的层冻结;对于Resnet50,选择将168层之前的层冻结;对于Xception,选择将126层之前的层冻结。下图所示表示了VGG16参数微调的设置方法,其他模型的设置原理与此类似。
 

Keras 深度学习之猫狗大战_第11张图片


7.调整模型、调整超参数:
       应用参数微调技术时应该在比较低的学习率下训练,这里使用的学习率为0.00001,优化器为RMSprop。较低的学习率可以使训练过程中保持较低的更新幅度,以防止破坏模型卷积层预训练的特征。
       在一开始,我将Batch size 设置为50,但是在训练时发现模型不容易收敛,而且在测试集和验证集上的损失函数值波动较大,引入Keras的回调函数EarlyStopping(EarlyStopping能够让模型在训练时根据设定的条件停止)时,需将patience参数设置为较大的值。故后面将Batch size调整为较大值。但Batch size太大对GPU显存资源也是一个很大的负荷,最终将训练时的Batch size调整为100。
       为了应对模型过拟合问题,我使用了数据增强技术来训练一个新的网络,所以所有epoch中是不会有两次相同的输入的。但是,这些输入仍然是相互关联的,因为它们来自有限的原始图像,数据增强并不能产生新的信息,而只能重新混合现有的信息。因此,这可能不足以完全摆脱过度拟合。为了进一步克服过度拟合,我还将在模型中全连接层分类器的前面添加一个dropout层这是一种正则化手段,不过跟Regularization不同的是,它是通过将训练的层中的部分神经元的输出置零来实现的。在这里,使用的Dropout参数为0.5。
       但在参数微调过程中,我发现Xception很快就停止训练了,因为使用了EarlyStopping回调函数,而且val_acc和val_loss都呈现出模型的性能在下降的趋势,最后参数微调效果也不理想。于是我增大了数据增强的幅度,重新跑了一遍程序,这时Xception模型依然很快就停止训练,但是val_acc和val_loss却是往好的趋势变化。在这之后,我果断将EarlyStopping中的stop参数调整为较大的整数,从而增加Xception训练的epoch数量。
       所有模型的训练都在Jupyter Notebook中完成。程序批量读取图片,并根据具体模型进行相应的数据预处理,包括数据增加技术。各模型训练过程如图中所示,由于参数微调时的模型的顶层分类器之权重是由特征提取过程中训练过的,因此在训练的一开始,模型就已经表现得比较好了。后三个模型在整个参数微调过程中,模型的改进幅度并不明显。最终训练得到的结果准确率已经很高了。
 

Keras 深度学习之猫狗大战_第12张图片

       可以看到模型表现相对较优的是Xception、Inception V3,它们在验证集上的分类准确率均高于99%,损失值均低于0.03。而Inception V3在验证集上的分类准确率已经达到了0.993%,logloss值为0.025左右,参数微调效果非常理想,和Xception十分接近!ResNet50虽然也达到了不错的分类准确性,但结果却和Inception V3想比却处于劣势。初步猜测是模型训练时参数设置不够合理导致的,例如数据增强的设置,在往后的学习中会进行验证。VGG16的表现相对于前面三种模型则处于明显的劣势,毕竟VGG16相对较老,网络结构也较浅。

8.特征融合:
       
使用预训练的模型进行参数微调确实比使用自己搭建的模型能够获得更小的损失函数值,但是训练过程还是十分缓慢的。在对每一个模型进行参数微调前,我都有都尝试直接使用特征向量来进行分类,而且个别模型的分类结果十分理想,包括ResNet50、InceptionV3和Xception。因此,尝试将提取的多个较优模型的特征进行组合,是一个非常可行的方案!
       整个构建过程很简单。首先,我们选择在前面的训练中表现最好的三个模型ResNet50、InceptionV3和Xception作为特征融合的对象模型。接着,先将数据输入各个模型中,提取特征向量并保存下来。然后,将对应同一个训练样本的来自这三个模型的三个特征向量在长度上进行堆叠,最终得到共12500个维度为3×2048=6144的特征向量。
       最后,构建一个简单的神经网络,输入上一步特征融合得到的特征向量集合,进行训练。应用特征融合技术构建的模型如图所示(这部分代码和思路借鉴自杨培文老师,由衷感谢!)。
 

Keras 深度学习之猫狗大战_第13张图片

       运用特征融合方法的训练曲线如下图所示,最终在验证集上的准确率很轻松地就达到了99.6%,val_loss也只有0.012。
 

Keras 深度学习之猫狗大战_第14张图片




 

你可能感兴趣的:(机器学习(Machine,Learning))