向AI转型的程序员都关注了这个号????????????
机器学习AI算法工程 公众号:datayx
作为迁移学习的一种,finetune能够将general的特征转变为special的特征,从而使得转移后的特征能够更好的适应目标任务,而图像检索最根本的问题,仍在于如何在目标任务上获得更好的特征表达(共性与可区分性)。一种很自然的方式便是在特定的检索任务上,我们对imageNet学得的general的特征通过finetune的方式,使得表达的特征能够更好的适应我们的检索任务。在End-to-end Learning of Deep Visual Representations for Image Retrieval和 Collaborative Index Embedding for Image Retrieval中已经很清楚的指出,通过基本的classification loss的finetune的方式,能够较大幅度的提高检索的mAP。因此,在本篇博文中,小白菜针对检索,主要整理了下面四个方面的内容:
CNN网络中哪一层最适合于做图像检索
基于pre-trained模型做图像检索几种典型的特征表示方法
抽取网络任意层的特征
数据增强(Data Augmentation)
VGGNet16网络模型fine-tuning实践
在采用深度学习做检索的时候,上面四方面的问题和知识基本都回避不了,因此,小白菜以为,掌握这四方面的内容显得非常有必要。
特征表达layer选择
在AlexNet和VGGNet提出伊始,对于检索任务,小白菜相信,在使用pre-trained模型抽取特征的时候,我们最最自然想到的方式是抽取全连接层中的倒数第一层或者倒数第二层的特征,这里说的倒数第一层或者倒数第二层并没有具体指明是哪一层(fcx、fcx_relux、fcx_dropx),以VggNet16网络为例,全连接层包含两层,fc6和fc7,因此我们很自然想到的网络层有fc6、fc6_relu6、fc7、fc7_relu7甚至fc6_drop6和fc7_drop7(后面会说明fc6_drop6和fc6_relu6是一样的,以及fc7_drop7和fc7_relu7也是一样的),所以即便对于我们最最自然最最容易想到的方式,也面临layer的选择问题。为此,我们以VGGNet16网络为例,来分析CNN网络的语义层(全连接层)选择不同层作为特征做object retrieval的mAP的影响。
小白菜选取fc6、fc6_relu6、fc7、fc7_relu7这四层语义层的特征,在Oxford Building上进行实验,评价指标采用mAP,mAP的计算采用Oxford Building提供的计算mAP代码compute_ap.cpp,下表是fc6、fc6_relu6、fc7、fc7_relu7对应的mAP。
从上表可以看到,直接采用pre-trained模型抽取语义层的特征,在Oxford Building上取得的结果在45%左右,并且可以发现,PCA降维后mAP反而会有提升(注意:降维训练数据集用的Oxford本身)。同时我们还可以看出,选取fc6、fc6_relu6、fc7、fc7_relu7对结果的影响并不大。这个结果只能说非常的一般,在基于pre-trained模型做object retrieval的方法中,比如Cross-dimensional Weighting for Aggregated Deep Convolutional Features、Particular object retrieval with integral max-pooling of CNN activations以及What Is the Best Practice for CNNs Applied to Visual Instance Retrieval?指出,选用上层的语义层其实是不利于object retrieval,因为上层的语义层丢失了object的空间信息,并且从实验的角度说明了选取中间层的特征更利于object retrieval。
实际上,在选取中间层来表达特征的过程中,我们可以去掉全连接层,从而使得我们可以摆脱掉输入图像尺寸约束(比如224*224)的约束,而保持原图大小的输入。通常,图像分辨率越大,对于分类、检测等图像任务是越有利的。因而,从这一方面讲,选取上层的全连接层作为特征,并不利于我们的object retrieval任务。一种可能的猜想是,上层全连接层的语义特征,应该更适合做全局的相似。
虽然中间层更适合于做object retrieval,但是在选用中间层的feature map作为raw feature的时候,我们面临的一个主要问题是:如何将3d的tensor转成一个有效的向量特征表示?下面小白菜主要针对这一主要问题总结几种典型的特征表示方法,以及对中间层特征选择做一些探讨与实验。
基于pre-trained模型做Object Retrieval几种典型的特征表示
SUM pooling
基于SUM pooling的中层特征表示方法,指的是针对中间层的任意一个channel(比如VGGNet16, pool5有512个channel),将该channel的feature map的所有像素值求和,这样每一个channel得到一个实数值,N个channel最终会得到一个长度为N的向量,该向量即为SUM pooling的结果。
AVE pooling
AVE pooling就是average pooling,本质上它跟SUM pooling是一样的,只不过是将像素值求和后还除以了feature map的尺寸。小白菜以为,AVE pooling可以带来一定意义上的平滑,可以减小图像尺寸变化的干扰。设想一张224224的图像,将其resize到448448后,分别采用SUM pooling和AVE pooling对这两张图像提取特征,我们猜测的结果是,SUM pooling计算出来的余弦相似度相比于AVE pooling算出来的应该更小,也就是AVE pooling应该稍微优于SUM pooling一些。
MAX pooling
MAX pooling指的是对于每一个channel(假设有N个channel),将该channel的feature map的像素值选取其中最大值作为该channel的代表,从而得到一个N维向量表示。小白菜在flask-keras-cnn-image-retrieval中采用的正是MAX pooling的方式。
from Day 2 Lecture 6 Content-based Image Retrieval
上面所总结的SUM pooling、AVE pooling以及MAX pooling,这三种pooling方式,在小白菜做过的实验中,MAX pooling要稍微优于SUM pooling、AVE pooling。不过这三种方式的pooling对于object retrieval的提升仍然有限。
MOP pooling
MOP Pooling源自Multi-scale Orderless Pooling of Deep Convolutional Activation Features这篇文章,一作是Yunchao Gong,此前在搞哈希的时候,读过他的一些论文,其中比较都代表性的论文是ITQ,小白菜还专门写过一篇笔记论文阅读:Iterative Quantization迭代量化。MOP pooling的基本思想是多尺度与VLAD(VLAD原理可以参考小白菜之前写的博文图像检索:BoF、VLAD、FV三剑客),其具体的pooling步骤如下:
具体地,在L=1的尺度下,也就是全图,直接resize到256256的大小,然后送进网络,得到第七层全连接层4096维的特征;在L=2时,使用128128(步长为32)的窗口进行滑窗,由于网络的图像输入最小尺寸是256256,所以作者将其上采样到256256,这样可以得到很多的局部特征,然后对其进行VLAD编码,其中聚类中心设置为100,4096维的特征降到了500维,这样便得到了50000维的特征,然后将这50000维的特征再降维得到4096维的特征;L=3的处理过程与L=2的处理过程一样,只不过窗口的大小编程了64*64的大小。
作者通过实验论证了MOP pooling这种方式得到的特征一定的不变性。基于这种MOP pooling小白菜并没有做过具体的实验,所以实验效果只能参考论文本身了。
CROW pooling
对于Object Retrieval,在使用CNN提取特征的时候,我们所希望的是在有物体的区域进行特征提取,就像提取局部特征比如SIFT特征构BoW、VLAD、FV向量的时候,可以采用MSER、Saliency等手段将SIFT特征限制在有物体的区域。同样基于这样一种思路,在采用CNN做Object Retrieval的时候,我们有两种方式来更细化Object Retrieval的特征:一种是先做物体检测然后在检测到的物体区域里面提取CNN特征;另一种方式是我们通过某种权重自适应的方式,加大有物体区域的权重,而减小非物体区域的权重。CROW pooling ( Cross-dimensional Weighting for Aggregated Deep Convolutional Features )即是采用的后一种方法,通过构建Spatial权重和Channel权重,CROW pooling能够在一定程度上加大感兴趣区域的权重,降低非物体区域的权重。其具体的特征表示构建过程如下图所示:
其核心的过程是Spatial Weight和Channel Weight两个权重。Spatial Weight具体在计算的时候,是直接对每个channel的feature map求和相加,这个Spatial Weight其实可以理解为saliency map。我们知道,通过卷积滤波,响应强的地方一般都是物体的边缘等,因而将多个通道相加求和后,那些非零且响应大的区域,也一般都是物体所在的区域,因而我们可以将它作为feature map的权重。Channel Weight借用了IDF权重的思想,即对于一些高频的单词,比如“the”,这类词出现的频率非常大,但是它对于信息的表达其实是没多大用处的,也就是它包含的信息量太少了,因此在BoW模型中,这类停用词需要降低它们的权重。借用到Channel Weight的计算过程中,我们可以想象这样一种情况,比如某一个channel,其feature map每个像素值都是非零的,且都比较大,从视觉上看上去,白色区域占据了整个feature map,我们可以想到,这个channel的feature map是不利于我们去定位物体的区域的,因此我们需要降低这个channel的权重,而对于白色区域占feature map面积很小的channel,我们认为它对于定位物体包含有很大的信息,因此应该加大这种channel的权重。而这一现象跟IDF的思想特别吻合,所以作者采用了IDF这一权重定义了Channel Weight。
总体来说,这个Spatial Weight和Channel Weight的设计还是非常巧妙的,不过这样一种pooling的方式只能在一定程度上契合感兴趣区域,我们可以看一下Spatial Weight*Channel Weight的热力图:
从上面可以看到,权重大的部分主要在塔尖部分,这一部分可以认为是discriminate区域,当然我们还可以看到,在图像的其他区域,还有一些比较大的权重分布,这些区域是我们不想要的。当然,从小白菜可视化了一些其他的图片来看,这种crow pooling方式并不总是成功的,也存在着一些图片,其权重大的区域并不是图像中物体的主体。不过,从千万级图库上跑出来的结果来看,crow pooling这种方式还是可以取得不错的效果。
RMAC pooling
RMAC pooling的池化方式源自于Particular object retrieval with integral max-pooling of CNN activations,三作是Hervé Jégou(和Matthijs Douze是好基友)。在这篇文章中,作者提出来了一种RMAC pooling的池化方式,其主要的思想还是跟上面讲过的MOP pooling类似,采用的是一种变窗口的方式进行滑窗,只不过在滑窗的时候,不是在图像上进行滑窗,而是在feature map上进行的(极大的加快了特征提取速度),此外在合并local特征的时候,MOP pooling采用的是VLAD的方式进行合并的,而RMAC pooling则处理得更简单(简单并不代表效果不好),直接将local特征相加得到最终的global特征。其具体的滑窗方式如下图所示:
图中示意的是三种窗口大小,图中‘x’代表的是窗口的中心,对于每一个窗口的feature map,论文中采用的是MAX pooling的方式,在L=3时,也就是采用图中所示的三种窗口大小,我们可以得到20个local特征,此外,我们对整个fature map做一次MAX pooling会得到一个global特征,这样对于一幅图像,我们可以得到21个local特征(如果把得到的global特征也视为local的话),这21个local特征直接相加求和,即得到最终全局的global特征。论文中作者对比了滑动窗口数量对mAP的影响,从L=1到L=3,mAP是逐步提升的,但是在L=4时,mAP不再提升了。实际上RMAC pooling中设计的窗口的作用是定位物体位置的(CROW pooling通过权重图定位物体位置)。如上图所示,在窗口与窗口之间,都是一定的overlap,而最终在构成global特征的时候,是采用求和相加的方式,因此可以看到,那些重叠的区域我们可以认为是给予了较大的权重。
上面说到的20个local特征和1个global特征,采用的是直接合并相加的方式,当然我们还可以把这20个local特征相加后再跟剩下的那一个global特征串接起来。实际实验的时候,发现串接起来的方式比前一种方式有2%-3%的提升。在规模100万的图库上测试,RMAC pooling能够取得不错的效果,跟Crow pooling相比,两者差别不大。
上面总结了6中不同的pooling方式,当然还有很多的pooling方式没涵盖不到,在实际应用的时候,小白菜比较推荐采用RMAC pooling和CROW pooling的方式,主要是这两种pooling方式效果比较好,计算复杂度也比较低。
抽取网络任意层的特征
在上面一节中,我们频繁的对网络的不同层进行特征的抽取,并且我们还提到fc6_dropx和fc6_relux是一样的(比如fc7_drop7和fc7_relu7是一样的),这一节主要讲述使用Caffe抽取网络任意一层的特征,并从实验的角度验证fc6_dropx和fc6_relux是一样的这样一个结论。
为了掌握Caffe中网络任意一层的特征提取,不妨以一个小的题目来说明此问题。题目内容为:给定VGGNet16网络,抽取fc7、fc7_relu7以及fc7_drop7层的特征。
求解过程:VggNet16中deploy.txt中跟fc7相关的层如下:
如果使用net.blobs['fc7'].data[0]
,我们抽取的特征是fc7层的特征,也就是上面:
经过这样的修改后,我们使用net.blobs['fc7_relu7'].data[0]
即可抽取到relu7的特征,使用net.blobs['fc7_drop7'].data[0]
可抽取到drop7的特征。
数据增强
有了上面CNN网络中哪一层最适合于做图像检索、基于pre-trained模型做图像检索几种典型的特征表示方法以及抽取网络任意层的特征三方面的知识储备后,在具体fine-tuning网络进行图像检索实验前,还有一节很重要(虽然我们都很熟悉)内容,即数据增强(Data Augmentation)。数据增强作用有二:一是均衡样本,比如某些类别只有几张图片,而有的类别有上千张,如果不做均衡,分类的时候计算的分类准确率会向样本充足的类别漂移;二是提高网络对于样本旋转、缩放、模糊等的鲁棒性,提高分类精度。在实际工作中,我们拿到了图像数据样本对采用深度学习模型而言,经常是不充足且不均衡的,所以这一步数据的前置处理是非常重要的。
在正式开始数据增强之前,对图片进行异常检测是非常重要的,其具体的异常表现在图片内容缺失、图片不可读取或者可以读取但数据出现莫名的问题,举个例子,比如通过爬虫爬取的图片,可能上半部分是正常的,下半分缺失一片灰色。因此,如果你不能确保你训练的图片数据都是可正常读取的时候,最好对图片做异常检测。假设你的训练图片具有如下层级目录:
下面是小白菜参考网上资料写的图片异常检测代码如下:
通过上面的图片异常检测,我们可以找到那些不可读取或者读取有问题的图片找出来,这样在我们使用Caffe将图片转为LMDB数据存储的时候,不会出现图片读取有问题的异常。在图片异常检测完成后,便可以继续后面的数据增强了。
数据增强的脚本如下:
VGGNet16网络模型fine-tuning实践
在实际中,用CNN做分类任务的时候,一般我们总是用在ImageNet上预训练好的模型去初始化待训练模型的权重,也就是不是train from scratch,主要原因有二:一是在实际中很难获取到大量样本(即便是做了数据增强);二是加快模型训练的速度。因而,针对检索这个任务,我们也采用fine-tuning的方式,让在ImageNet上预训练的模型迁移到我们自己的特定的数据集上,从而提升特征在检索任务上的表达能力。下面小白菜以fine-tuning Neural Codes提供的数据集为例,比较详实的总结一个完整的fine-tuning过程。
在fine-tuning之前,我们先追问一个简单的问题和介绍一下Neural Codes提供的landmark数据集。追问的这个问题很简单:为什么几乎所有的做检索的论文中,使用的都是AlexNet、VGGNet16(偶尔会见到一两篇使用ResNet101)网络模型?难道做研究的只是关注方法,使用AlexNet、VGGNet、ResNet或者Inception系列只是替换一下模型而已?小白菜曾也有过这样的疑问,但是对这些网络测试下来,发觉VGGNet在做基于预训练模型特征再表达里面效果是最好的,对于同一个方法,使用ResNet或Inception系列,其mAP反而没有VGGNet的高。至于为什么会这样,小白菜也没有想明白(如果有小伙伴知道,请告知),我们就暂且把它当做一条经验。
我们再对Neural Codes论文里提供的landmark数据集做一个简单的介绍。该数据集共有680类,有的类别样本数据至于几个,多则上千,样本分布极其不均匀。不过这不是问题,通过第4节介绍的数据增强和提供的脚本,我们可以将每个类别的样本数目控制在800左右。同时,我们可以使用下面脚本将每个类别所在目录的文件夹名字命名为数字:
我们需要将数据集划分为train数据集和val数据集,注意val数据集并不单纯只是在训练的时候测试一下分类的准确率。为了方便划分数据集,小白菜写了如下的脚本,可以很方便的将数据集划分为train数据集和val数据集:
运行上面脚本,会在lmdb目录下(事先需要建立lmdb目录)生成两个文本文件,分别为train.txt
和val.txt
,对于为训练数据集和验证数据集。
图片转成lmdb存储
为了提高图片的读取效率,Caffe将图片转成lmdb进行存储,在上面得到train.txt
和val.txt
后,我们需要借助caffe的convert_imageset
工具将图片resize到某一固定的尺寸,同时转成为lmdb格式存储。下面是小白菜平时使用的完成该任务的一个简单脚本crop.sh
修改train_val.prototxt和solver.prototxt
针对VGGNET16网络,在fine-tuning的时候,我们通常将最后的分类层的学习率设置得比前面网络层的要大,一般10倍左右。当然,我们可以结合自己的需要,可以将前面层的学习率都置为0,这样网络在fine-tuning的时候,只调整最后一层分类层的权重;在或者我们分两个阶段去做fine-tuning,第一阶段只fine-tuning最后的分类层,第二阶段正常的fine-tuning所有的层(包含最后的分类层)。同时,我们还需要对最后一层分类层重新换个名字,并且对应的分类输出类别也需要根据自己数据集的分类类别数目做调整,下面小白菜给出自己在fine-tuning Neural Codes的landmark数据集上train_val.prototxt的前面输入部分和后面分类的部分:
测试
完成了在Neural Codes的landmark数据集上的fine-tuning后,我们使用经过了fine-tuning后的模型在Oxford Building数据集上mAP提升了多少。为了方便对比,我们仍然提取fc6的特征,下面是不做ft(fine-tuning)和做ft的结果对比:
可以看到,经过fine-tuning,mAP有了较大幅度的提升。从而也从实验的角度验证了对于检索任务,在数据允许的条件,对预训练模型进行fine-tuning显得非常的有必要。
项目代码获取方式:
关注微信公众号 datayx 然后回复 CNN 即可获取。
AI项目体验地址 https://loveai.tech
总结
在本篇博文中,小白菜就5个方面的问题展开了总结和整理,分别是:
CNN网络中哪一层最适合于做图像检索
基于pre-trained模型做图像检索几种典型的特征表示方法
抽取网络任意层的特征
数据增强(Data Augmentation)
VGGNet16网络模型fine-tuning实践
整个文章的基本组织结构依照典型的工科思维方式进行串接,即从理论到实践。
阅读过本文的人还看了以下文章:
《深度学习入门:基于Python的理论与实现》高清中文PDF+源码
2019最新《PyTorch自然语言处理》英、中文版PDF+源码
《21个项目玩转深度学习:基于TensorFlow的实践详解》完整版PDF+附书代码
PyTorch深度学习快速实战入门《pytorch-handbook》
【下载】豆瓣评分8.1,《机器学习实战:基于Scikit-Learn和TensorFlow》
李沐大神开源《动手学深度学习》,加州伯克利深度学习(2019春)教材
【Keras】完整实现‘交通标志’分类、‘票据’分类两个项目,让你掌握深度学习图像分类
如何利用全新的决策树集成级联结构gcForest做特征工程并打分?
Machine Learning Yearning 中文翻译稿
斯坦福CS230官方指南:CNN、RNN及使用技巧速查(打印收藏)
中科院Kaggle全球文本匹配竞赛华人第1名团队-深度学习与特征工程
不断更新资源
深度学习、机器学习、数据分析、python
搜索公众号添加: datayx
长按图片,识别二维码,点关注