FashionAI服装属性标签大赛参赛经验


#1

FashionAI服装属性标签识别竞赛

初赛结束前一个月在论坛上看到了hetong007的帖子实战阿里天池竞赛——服饰属性标签识别,正好当时并无太多事情要忙,于是下载了代码和数据集,准备小试一下。
两个月后,比赛终于落下帷幕。尽管最后只取得了初赛78,复赛52的成绩,但在比赛的过程中的亲身经历还是让我对于参数的修改,超参的选择和网络结构的选择等诸多内容有了更深的了解。所以准备写个帖子总结一下,方便以后自己回顾,顺便也希望借此能够和大家多多交流。

初赛

赛题介绍

不管是初赛还是复赛,服装属性标签的比赛当中所需要完成的任务共两大类(length和design)八小种,即pant_length,coat_length, skirt_length,sleeve_length,collar_design,neck_design,neckline_design和label_design。尽管对于所有的任务都可以使用相同的模型进行分类,但是由于两大类任务之间存在着明显的不同,因而在初赛中进行了特殊处理。
首先,在离线训练评估的过程中发现,比赛的八种任务在准确率和mAP上存在着显著的差异,具体数据如下所示:

collar_design neckline_design neck_design lapel_design pant_length skirt_length sleeve_length coat_length
acc 0.874 0.878 0.844 0.894 0.893 0.961 0.929 0.885
mAp 0.889 0.888 0.864 0.915 0.902 0.966 0.929 0.885

从上表中的数据中可以看出,design相关的任务的准确率和mAP明显要低于length任务的数据,因此在初赛阶段将提高design任务的结果作为了比赛重点。

参数固定

在hetong007的代码当中,其选择了resnet50_v2作为分类的模型,并利用fine tuning进行模型的训练;在之前的kaggle dog breed比赛当中,ypw分享的代码则是选择了将模型的卷积层部分作为特征提取器,对得到的向量训练一个MLP作为分类模型。因此在训练的过程中对卷积层的参数是否进行固定会有差别吗?
试验的结果表明将卷积层的参数固定后进行训练得到的结果会稍好一些。(因忘记记录相关数据,故此处未列出相关具体数据,大约有1%左右的提升。)不管是resnet50_v2还是其他的模型,MXNet当中提供的预训练的模型都是在ImageNet这样的巨型数据集上进行训练的结果,因此其应该学习到了足够多的特征。而本次比赛所使用的服饰数据和ImageNet中的数据差别并非巨大,因此使用预训练的模型并对卷积层参数固定的方式比从头开始学习或者fine tuning的结果都要好。
P.S.然而在其他的数据上,例如医疗相关的数据,采用此种方式是否能够得到类似的结果暂未可知。、
P.P.S.在通过设定卷积层参数梯度为null后进行训练的过程中发现,似乎此种方式训练的速度更快。大概MXNet对此进行了优化。既然卷积层参数的梯度被设定为null,那么在反向传播的过程中卷积层的梯度即无需计算卷积层的参数梯度。

外部数据

在使用MXNet提供的预训练模型之前曾尝试过使用外部数据帮助训练。最初的设想为,既然使用在ImageNet上预训练的模型能够很好的提取相关特征,那么使用服饰相关的数据集训练一个模型,并在此模型的基础上进行固定卷积层参数训练是否会更好呢?于是选择了使用DeepFashion当中的数据对resnet50_v2等模型进行训练,并在其基础上进行FashionAI模型的训练。
但是最终的结果表明,个人训练得到的模型性能不如MXNet提供的预训练模型。我个人的猜测,一方面可能是DeepFashion的数据量太小(只有几十万张图片)导致了过拟合,另一方面则可能是因为在单卡(对,我只有一块1080Ti)上训练得到的模型的性能远未达到模型的理想性能。故最终这一设想被舍弃。
P.S.与之类似的一个想法有,在pant_length这样的任务上训练好一个模型后将其作为后一个模型的pretrained model,在其上进行fine tuning,最后结果证明效果不佳。

more data, better model

在提到深度学习时,常常会听到这样一种说法,数据、算力和算法是深度学习的三驾马车。在本次比赛中,通过试验也确实能证明这一点。在离线的训练当中,通常会选择9/10的数据作为训练集,1/10的数据作为验证集。但是在最终对测试集进行预测之前会选择在全部的数据上进行训练,这样一来使用的数据更多,模型的过拟合程度也会更低。
比赛中分别使用训练集数据和全部数据进行训练,并在测试集上进行预测,提交后的得分结果表明,使用全体数据训练得到的结果相比只使用训练集数据约有0.5%的提升。
P.S.尽管数据越多,得到的结果越好,但是想要引入更多的数据是相当困难的。根据数据集的说明在网络上进行数据爬取需要付出较大的代价,得到的数据还需要进行数据清洗才能够真正使用。故对于个人参赛的选手而言,这个选项基本是不可行的。

m label

在FashionAI的数据中,每一件衣物所属的类别并非是全然确定的。通过对标签数据的统计发现,其中包含了少量的m label(大约2%,具体数据已经忘了。),所谓m label即模型也有可能是这一类别。查阅官方的比赛评分说明可知“MaxAttrValue对应的标注位是’y’时,记为正确: PRED_COUNT++,PRED_CORRECT_COUNT++;MaxAttrValue对应的标注位是’m’时,不记入准确率评测:无操作;MaxAttrValue对应的标注位是’n’时,记为错误: PRED_COUNT++”。为此想要利用m label帮助模型训练是相对较为困难的,因此在多次尝试(将图片放入m对应的label文件夹,并在y对应的文件夹中将图片复制若干倍)而评分并不能增加后放弃了m label。

类别减少

在官方的数据解释当中,sleeve_length(袖长)数据中有9个类别需要区分。但是通过对实际数据的观察可以发现,sleeve_length中label=0的数据极少(不足其他类别的1%),因此考虑在模型训练时可否将类别减少为8个,而将label=0的概率强制设定为0.0。由于FC层的参数数量和最终的类别数目密切相关,因此从理论上来讲,通过减少类别可以提升模型的性能;但与此同时,由于强制将label=0的概率设置为0.0,最终部分数据肯定会得到错误的结果。因此最终两者谁会占据主导需要试验来说明,结果如下表所示。

acc mAP loss
9类 0.854 0.921 0.533
8类 0.862 0.924 0.534

可以看出减少类别后模型的acc有0.8%的提升,而mAP有0.3%的提升,所以最终参数减少带来的优势占据了主导,因此在随后的任务中将sleeve_length的类别减少为8。
但是在复赛阶段,sleeve_length中label=0的数据比例约占全部数据的2.5%,此时则选用9个类别进行训练。

模型选择

在选择了固定卷积层参数进行模型训练的方式后,一个很自然的想法就是:既然卷积层的参数固定,那么实际上模型需要训练的参数只有最后的FC层的参数;那么一个更加复杂的模型和一个相对简单的模型在参数相同的情况下,显然前者的性能会更好,因此对resnet50_v2,resnet101_v2和resnet_152_v2进行了对比。得到的结论是在固定卷积层参数进行训练的前提下,更为的复杂的模型带来了更强的特征提取能力,因而得到的结果也就越好。图表结果如下

model weight decay fixed? Val-mAP Val-los Val-acc
resnet101_v2 1.00E-04 non-fixed 0.897 0.546 0.815
resnet101_v2 1.00E-04 fixed 0.900 0.510 0.820
resnet152_v2 1.00E-04 fixed 0.906 0.623 0.830
resnet152_v2 5.00E-04 fixed 0.915 0.553 0.847

从上表中可以得到的结论有:

  1. 卷积层参数fixed好于non-fixed;
  2. 在卷积层参数fixed的前提下,模型越复杂越高;
  3. 对于resnet152_v2而言,存在一个最佳的weight decay。
    P.S.weight decay的大小,sgd/sgd+momentum的选择等超参都需要根据模型本身进行,本身似乎并没有普适的规则。
    P.P.S.但是需要注意的是,在比赛中可以为了追求更高的准确率和mAP而选择非常复杂的模型,但是在实际的项目中,考虑到复杂模型带来的计算量的大幅增加,有时候需要在两者之间进行权衡取舍。
    P.P.P.S.比赛中了解到,有其他的选手使用了resnext的模型,但由于MXNet并未提供此模型的结构文件和预训练模型,因此并未使用此模型。所以其性能究竟如何也无法判断。

更小的模型,更好的结果?

由于过拟合的问题始终存在,因此考虑是否采用较小的模型能够解决这一问题呢?考虑到对于服饰而言,即便将其从rgb三通道图像变成灰度的单通道图像,人仍能够区分(颜色是相对不重要的属性)其款式如何。因此在resnet50_v2的基础上进行通道数的缩减,将模型的全部通道数缩减为原来的三分之一左右。
最后的离线训练数据现实,此时模型的训练速度极快,验证集准确率和原始resnet50_v2 fine tuning的结果近似,但不如固定参数后的resnet50_v2。因此最后选择放弃这一思路。

多模型融合

既然选择复杂的模型能够带来较大的提升,那么更多复杂的模型是否会带来更好的结果呢?毕竟在kaggle的诸多比赛当中,很多TOP1选手就选择了大量模型进行融合。在本比赛中,最终选择使用了resnet152_v2,inception_v3和densenet201三个模型进行训练,在预测结果得到相应的结果后取其平均值得到最终提交的结果。
离线训练的结果显示,使用多模型融合后,mAP能有接近1%的提升,而准确率则有2%左右的提升。
P.S.即便是简单的对结果进行取平均,在实际中仍然可以通过细微的变动来略微改善相应的结果(毕竟在比赛中,万分之一的提升都能够反超)。具体而言,对于不同模型得到的结果进行加和求平均时并非一定具有相同的权重。具体来讲,可以通过对模型赋予不同的权重(保持权重之和为1)在验证集上进行评估进而得到最佳组合。但在本次比赛中,因时间限制,最终(忘了)未对权重进行调整。

MLP

多模型融合的方式并未只有上述的方式,另外一种方式就行ypw曾经使用的,可以将模型作为feature extractor提取特征,拼接后训练一个MLP。尽管此种方法对于GPU要求不高,但是其缺陷为无法使用更多的data augmentation带来的好处。因此曾经尝试在训练的过程中,对data batch使用三个不同的模型进行实时的特征提取和拼接,并在其上进行MLP的训练。
需要注意的是,由于resnet152_v2和densenet201要求输入图片的尺寸(常为224)和inception_v3(常为299)不同,因此需要增加一个额外的函数对data batch进行处理。
但是由于时间限制(初赛后期还有其他工作要做),并未对这一想法进行验证。
P.S.将特征进行组合的方式并非只有拼接一种方式,在VQA等任务中将不同维度的向量整合还可以使用FFT重采样等更为复杂的方式,可能效果会更好。

目标检测

由于design任务的低准确率拖累了最终结果的提升,因此比赛后期将重点集中在了design任务之上。通过对原始图片的分析发现,原始图像大多为512*512像素的模特图片。在design任务中所要预测的collar,neck,neckline和lapel(领子,立领,翻领,领线等)在图像中只占据较小的一部分区域,即图片中超过8/9的部分包含的信息都是无效的噪声信息。因此考虑使用目标检测模型将脖子的部分从原始图像中剪裁出来再进行进一步的分类。
在本次比赛中使用了基于resnet101的faster rcnn,但实际上可以使用ssd或者yolo等相对简单的模型。由于初赛中gluon-vision工具包尚未放出且需要制作rec文件进行,因此最后选择了相对更简单的faster rcnn(可以直接使用类似以VOC2007数据集的xml文件进行训练)。考虑到四个任务当中所需要剪裁的目标尽管有相似性,但仍然略有不同,例如翻领在图片中的比例要大于普通的领子占据的比例。所以想要使用一个检测器对四类任务进行检测在标注的过程中就需要更大的宽容度,即选择较大的范围以保证真正重要的信息不被遗漏。于是在标注过程中,选择了唇部以下,胸部以上,两肩之间的区域作为检测目标。最后人工标记的图片数目约2000张。

data-preprocess task model fixed train-acc train-mAP train-loss val-acc val-mAP val-loss
norm collar_design_labels resnet50_v2 no 0.9966 0.9982 0.0177 0.8296 0.9042 0.6669
croped collar_design_labels resnet50_v2 no 0.9957 0.9978 0.0217 0.8854 0.9358 0.4556
croped collar_design_labels resnet50_v2 yes 0.9805 0.9896 0.0654 0.8866 0.9364 0.4519
croped collar_design_labels resnet152_v2 yes 0.9903 0.995 0.0312 0.9069 0.9462 0.44

从上表的结果可以看出,在使用了目标检测预处理之后不管是acc还是mAP均有了较大幅度的提升。
P.S.当然,如果想要分别训练得到一个检测器,那么可以选择更细致的标注方式,这样得到的结果也会更好。

采用了上述的这些手段之后,最终的初赛二轮取得了78的排名,勉强进入复赛。

复赛

复赛的赛题和之前保持一致,但是在本阶段design任务保持不变,但length的任务难度大幅增加。在初赛阶段,所要进行预测的图片全部为真人模特的实拍图片;但是在复赛阶段,length的任务中增加了大量(约40%)的平铺图片。平铺图片的难度急剧增加(未经训练普通人都难以区分一条裤子到底是七分裤,九分裤或者更长),导致了结果变差。
在本阶段延续了之前的一些思路,对四个length任务分别训练了四个不同的目标检测器,将外套,裤子,裙子和上身从原始图片中剪裁出来进行预测。除此之外,对图片预处理等尽心了细微的调整。

图片去重

由于初赛训练数据+一二阶段的评测数据+复赛的训练数据中存在着较多的重复,因此通过md5校验的方式将重复的图片删除以保证数据的准确。

图片预处理

在使用训练集中的图片进行模型训练之前,原始代码使用了resize_short的方式对图片进行缩放后随机剪裁。由于原始的图像大部分为512*512的正方向图片,因此resize_short的效果等同于resize。
但是需要注意的是,由于目标检测算法对原始图像进行预处理,由此得到的剪裁后的图片长宽比发生了较大改变。例如,在design的图片中,有较多的图片长宽比在2:1左右。如果此时仍然采用之前的resize_short就会导致图片的宽度远远大于所需要剪裁的大小,这会导致有效信息的丢失。为了解决这一问题,对剪裁后的图片进行补齐处理,即为图片增加无信息的margin使其长宽比保持为1:1。实际所使用的margin的像素值既非0,也非255,而是通过零中心时所使用的mean+std计算得到。
试验结果最终证明,这样的预处理操作也会增加最终结果的准确率(具体数据遗漏)。

data augmentation

  1. 在hetong007的代码中数据增广的使用相对较弱,因此在比赛中选用了更多的数据增广方式,例如色彩抖动和对比度抖动等,以帮助缓解过拟合(初赛中也使用了此策略)。
  2. 在训练阶段,hetong007的原始代码将图片缩放至256后剪裁至224,但是对于design的任务由于采用了目标检测,过于激进的剪裁策略会导致有效信息的损失,因此更改为缩放至234后剪裁224图片进行训练。
  3. 在预测阶段,hetong007对图片采用了ten_crop的处理(即镜像翻转+上下左右中心剪裁)来增加结果的可信度;在本次比赛中,个人采用eighteen_crop的方式,即在之前的剪裁基础上增加更多的剪裁处理(镜像翻转+上中下各三次剪裁),对比之前的结果有微弱的提升(初赛中也使用了此策略)。

图片分离

由于真人模型和平铺图片具有不同的数据分布,因此将其合并在一起进行训练可能并不好。为此训练了一个较小的网络区分一件衣服是平铺图片还是真人图片。将两类数据分离后分别使用之前的策略进行独立的模型训练,在预测过程中首先利用小模型进行类型判断,然后调取对应的模型进行预测。

关键点监测

在FashionAI的比赛中,除了服装标签属性比赛之外还有关键点检测的任务。对于平铺服饰的检测,个人认为使用关键点检测得到的数据进行ML建模分析可能会有较好的结果。但是由于之前并未写过相关代码,而网络公开的代码大多基于tf等,因此最终并未尝试这一思路。
如果有小伙伴这样做了,欢迎分享。

最后在复赛二阶段取得了52的排名

总结

总之,参加这场耗时两个月的比赛消耗了大量的精力和空闲时间,但也由此对计算机视觉的任务更加熟悉。除了调参和debug更加熟练之外,代码之外对于深度学习和项目落地等也有了一些感觉。

  1. 在复赛二阶段,组委会邮件通知所有人为了比赛更加有意义,因此限制了复赛所使用的模型不能超过两个,总大小不能超过600m(貌似是这么大)。由此可见,在项目的具体落地实施上并非准确率越高越高;为了取得1%的准确率提升需要付出多少代价是需要权衡的。
  2. 深度学习的局限性。归根到底,深度学习也算是流形学习,也不是万能的。例如对原始数据观察后发现了一些数据是超出DL能力范围的。大部分情况下,一张照片中的目标是完整的,但是存在部分数据存在着遮掩等等。尽管如此,人能够通过逻辑推理进行判断(有些图片中有着裸露的脚踝和裤脚,是长裤;有些露着大腿,那就只能是短裤等等),但如果此类数据数量较少,那么DL大概率会出错。

欢迎转载,但请注明出处,谢谢。


#2

我们团队输入尺寸设为512,初赛46,复赛12


#3

模型什么的没有太大改动吗?和224的输入相比有多少提高呢?我确实有看到别人说使用大尺寸的图片会提高分数,但是最后没有去尝试。


#4

差距的话,比如你之前是0.94的,改为512以后,是0.95了


#5

卧槽,一个百分点的提升,真是没想到有这么大的改进。看来如果使用这个这个尺度的输入我的最终排名能跑到三十几。
真是多谢了啊,可以记下来以后试一下。


#6

非常赞!能转到mxnet公众号吗?


#7

当然可以


#8

对的,进20了


#9

羡慕大佬们


#10

可以公布分享下代码吗


#11

@hetong007 有时间的话你转到mxnet blog?


#12

blog的PR在这里: https://github.com/mxnet-zh/blog/pull/10

@yulangwx 欢迎补充你的个人信息,以及对文章进行进一步的加工。


#13

需要补充什么个人信息呢?


#14

现在文章署名用的是你的论坛ID,你愿意的话可以改成真名+身份的信息。


#15

这样,那就改成真名好了,详细信息如下:
王鑫,前化学狗,现某不知名公司深度学习(计算机视觉方向)工程师。
谢谢。


#16

你给我个邮箱吧,我发你看下。
其实也没有特别多的改动,目标检测用的mxnet/example中faster rcnn的代码,图片边缘补齐的脚本也挺好写的。
后面的训练和检测就是更改了所使用的模型,修改了训练方式之类的超参。


#17

blog:https://zh.mxnet.io/blog/fashion-ai-summary

微信:https://mp.weixin.qq.com/s/HcGAL2kICdK9Fobgsj2KjQ


#18

email_yufl@163.com,谢谢你了,这是我的邮箱


#19

回复你了,注意查收一下。


#20

嗯嗯,收到了,谢谢你啦