2019年3月26日

[鹅厂] 惊悚图像分类日志

警告:本文可能出现部分惊悚内容导致不适!

前言

有幸来到鹅厂学习,接手了惊悚图片分类的问题。
这问题一听就很惊悚,事实上也很惊悚,每次打开数据集前我都要做一下心理建设。
数据量很大,有标注的惊悚数据4w,正常数据6w;数据花样很多,囊括了形形色色的网图,从大字报到游戏场景,从屠宰场到显微镜下,就看我能不能玩出点花样了。

要求

目标:二分类,只需要标记出惊悚图片即可
精度:准确率>80% 召回率>95%

难度

  1. 标注不正确,这是人工标注都存在的问题,理论上比较好的情况下能达到95%的准确率。
  2. 标注不全,惊悚图片可能同时具有多个惊悚属性,但仅有一个分类标注,例如手术场景,有器官、有血腥、甚至像虫子蠕动(此类图太扎眼,就不放了)
  3. 某些惊悚概念难以实际描述且不存在实体,如果说下图让人感到害怕是因为红色的眼睛和嘴,那为何红眼睛的兔子就可爱捏
    862311539.jpg
  4. 某些令人毛骨悚然的东西藏在图片一角,占比很小
  5. 图片太丰富,若上线后每天有30T的图片,与训练数据必然有不同分布,那么该如何保证效果?

日志

第一阶段:初探标注情况

随意选择了Resnet50作为主体网络,看中的是强大的表达能力和较少的参数
标注将惊悚图片分为20个子类,直接训练得到效果:验证集正确率Accuracy=0.53,各类正确率分布在0.15-0.56之间。
观察到一个有意思的现象是A类较多(>0.2)识别为B类,B类较多识别为C类,但C较少(<0.05)识别为B,B也较少识别为A。
根据此结果,粗暴的将子类合并为5个超类(血腥、怪兽、器官、密集、其它),又为了平衡类样本数量,拆分部分超类,最终得到10类,直接训练结果:验证集Accuracy=0.589,各类正确率分布在0.39-0.76之间。

第二阶段:初探分类情况-baseline

继续使用Resnet50作为主体网络,使用11类,新增的一类为正常图片。
制作训练集,使得每个Batch内有一半为正常图片,另一半为均匀分布的10类惊悚图片,每个epoch中图片少的类会重复训练多次。
制作验证集,从惊悚类别中按0.3的比例截取图片,从正常图片中取6000张,数量分布如下图。
制作测试集,将后续新增的标注制作为测试集,因此与训练集和验证集的分布略有不同。
thriller-11number.png
使用softmax进行多分类,验证集p=0.61
观察到在lr=0.001时网络前两个batch的loss数量级大于亿,很可能破坏了Resnet50预训练网络的参数,修改到lr=0.0003时,loss数量级为十,同时最终Accuracy=0.65,此时训练集p=1,loss已小于e-5。因此将此作为第二阶段的baseline,在测试集结果Accuracy=0.574。验证集混淆矩阵如下图,此矩阵按行表示识别为某类的比例:
thriller-11hxjz.png

支线1

考虑到每张图片可能同时属于多个惊悚类别,同时惊悚图片和正常图片是仅是二分类问题,所以这并不是一个多分类问题,修改 softmax 为 sigmod(lr=0.001),Accuracy=0.615,目前效果不明显。

支线2

为了实现每个Batch的类比例相同,采用了直接生成文件,指导每个batch应读取的图片,没有进行动态乱序,因此在第二个epoch后不同图片并没有重新组合,理论上会导致BN层失效。因此加入乱序,继续训练,由于train loss已趋近于0,乱序后并没有增加loss,因此无效果。

主线[task7] 样本增强

由于训练集已经学不到信息,对训练集进行样本增强,增加左右镜像和随机30度旋转,训练集loss<e-3,验证集Accuracy=0.695,混淆矩阵如下:
thriller-11number-m1.png

主线[task8] 聚类

从任务7中可以发现部分类别(含人血腥和非人血腥等)之间难以区分,因此将其聚类。去除样本数量少的分类(显微镜、排泄物),剩余分类聚为5类(惊悚造型、血腥、病态|组织器官、密集虫子|蛙|蛇、正常图片)。重新制作数据集如下图所示:
thriller-task8-dataset.png
训练集,共32396张图片,前16198张图片为按顺序放入的4类惊悚图片,后16198张图片为正常图片。训练时从前后各取batch_size/2张图片,进行一次乱序然后送入网络。每训练一个epoch后,将前后两部分图片分别乱序再组合。
验证集,从惊悚类别中按0.3的比例截取图片。
测试集,将后续新增的标注制作为测试集,因此与训练集和验证集的分布略有不同。

对训练集进行左右镜像和随机30度旋转的样本增强,验证集Accuracy=0.693,与11类并无明显变化。
thriller-task8.png
从混淆矩阵来看,惊悚类别之间的识别错误降低至0.1以下,大部分的错误来自于惊悚类别与正常图片之间的混乱,推测是因为类别数量变少,使得我们想要的惊悚类别与正常图片识别错误的损失减小了。

支线3

使用双GPU训练,第一块GPU负责Batch的前一半,即所有的惊悚图片,第二块GPU负责Batch的后一半,即所有的正常图片。因此对于第一块GPU使用sigmoid,第二块GPU使用softmax。因网络倾向于将所有图片归为正常图片类,失败。

主线 [task11] 修改损失函数

为了强化惊悚类图片和正常图片之间的区分度,重新定义损失函数
令 loss = sigmoid(五类) + 2*softmax(惊悚类和正常类),即从

loss = tf.nn.softmax_cross_entropy_with_logits(labels = labels, logits = logits)

修改为

loss1 = tf.nn.sigmoid_cross_entropy_with_logits(labels = labels, logits = logits)
label_ = tf.concat([tf.reshape(tf.reduce_sum(labels[:,0:-1], axis=1),(-1,1)),labels[:,-1:]],axis=1)
logit_ = tf.concat([tf.reshape(tf.reduce_sum(logits[:,0:-1], axis=1),(-1,1)),logits[:,-1:]],axis=1)
loss2 =  2 * tf.nn.softmax_cross_entropy_with_logits(labels = label_, logits = logit_)
loss = tf.reduce_mean(loss1)+tf.reduce_sum(loss2)

训练100轮中在44轮达到最好效果,验证集Accuracy=0.772,测试集Accuracy=0.754,验证集混淆矩阵如下:
thriller-task11.png
从混淆矩阵可以看出惊悚图片和正常图片的区分度已经很高,剩下的应该是一些困难样本的问题了。将此作为第三阶段的baseline,计算二分类下的指标(惊悚图片为正,正常图片为负)验证集精确率 Precision=0.89,召回率 recall=0.974,测试集精确率 Precision=0.897,召回率 recall=0.946。附一张训练轮数与准确度的图片:
thriller-task11-p.png

支线4 focal loss

为了使用到大量的正常照片,向训练集中加入4万张正常图片样本,修改每个batch中正常图片和惊悚图片比例为4:1,同时为了防止样本过于不均衡,使用focal loss进一步优化损失函数。验证集Accuracy<0.76,失败。

模型分析

浏览了一遍分类错误的图片,发现主要问题有以下几点:

  1. 忽略了小目标,例如图片小部分区域有惊悚内容 [改进网络结构]
  2. 需要先验知识,例如血迹正好被遮挡的车祸现场 [放弃]
  3. 抽象行为,例如动漫中杀了人的暗示(邪典动画) [放弃]
  4. 样本数量不足(雀斑与水泡、各种虫子特写) [增加特定样本]

第三阶段:预上线

新的模型指标

线上数据量每日超过100G,无法通过标注来确定算法效果,因此将使用两个数据集来表示算法性能:

  1. 文章数据集,来自文章配图渠道,共11万,其中已知惊悚图片224张,算法使用此数据集验证精确率
  2. 视频数据集,来自视频帧截图,共4.7万,算法使用此数据集验证召回率

主线 [task13] 预上线模型

  1. 将测试集并入训练集,并使用task11模型清洗数据,将分错数据手工再次分类;
  2. 新增类别’其它’,将令人不适的、排泄物、难以分类的惊悚图片等少量数据加入此类
  3. 在正常图片中扩充大量以下领域图片[口红,黑白照片,爆炸头,游戏CG,黑基调,国画,美食,辣椒、西红柿,医疗知识,海洋生物],同时训练集按正常图片2:1惊悚图片的比例重新生成
    使用正常图片46500张,惊悚图片23250张再次训练,在80轮达到最好效果,验证集Accuracy=0.79,计算二分类下的指标(惊悚图片为正,正常图片为负)验证集精确率 Precision=0.937,召回率 recall=0.983,略有提升。以此作为预上线模型。

线上结果:召回率约0.87,精确率约0.09

分析一下原因:

  1. 验证集中正常图片和惊悚图片的数量近乎1:1,若将比例修改为200:1时,可计算出 p=0.069 r=0.983,而线上数据绝大部分都是正常图片
  2. 观察到召回的图片主要有几个特征:模糊、切肉或炒菜、色彩特别丰富、局部有红色、暗色调、游戏场景。

支线5 尝试空洞卷积提高特征视野

图片经过Resnet50之后,输出11x11x2048的特征图,原本的结构是做global pooling 成 1x1x2048 再依次接dropout=0.5,尺寸为1x1x1000, 1x1x200, 1x1x6的卷积层。提高卷积感受野,尝试使用空洞卷积,将结构修改为:特征图接2x2x1024 r=4的空洞卷积,输出 7x7x1024 的特征图;再接 global pooling 成 1x1x1024;然后接dropout=0.5,尺寸为 1x1x200, 1x1x6的卷积层。网络只能学习到惊悚图片和正常图片之间的区别, 无法细分惊悚类别,同时二分类各项指标均低于[task13]。
若将结构修改为接两层空洞卷积后,模型完全无法学到任何信息,结果分数为均值。

主线 [task15] Part Base Convolutional Baseline 思想

使用Resnet50模型作为骨干网络,输入336×336的均值图像,输出11x11x2048的特征图。对特征图的处理进行了修改:
PCB.png
原结构:11x11x2048–全局均值池化-[/latex]gt;1x1x2048–1×1卷积(dp=0.6)-[/latex]gt;1x1x1000(dp=0.6)–1×1卷积-[/latex]gt;1x1x200–1×1卷积-[/latex]gt;1x1x6;
新结构:11x11x2048–5×5,step=3均值池化-[/latex]gt;3x3x2048–1×1卷积(dropout=0.6)-[/latex]gt;3x3x256–1×1卷积-[/latex]gt;3x3x6–全局最大池化-[/latex]gt;1x1x6;
即将特征图分为9块,分别输出分类结果,取最大分数作为最终输出,属于一种细粒度的做法。算法在83轮达到最优结果,验证集Accuracy=0.90,多分类精度大幅提高;计算二分类下的指标(惊悚图片为正,正常图片为负)验证集精确率 Precision=0.958,召回率 recall=0.976。

为了验证将特征图用5×5卷积核切分是否合适,遍历了几种不同尺寸卷积核,发现5x5s3与6x6s5效果都很好:

  • 3×3,step3,valid: 验证集Accuracy=0.827,二分类F1=0.938
  • 4×4,step3,valid: 验证集Accuracy=0.875,二分类F1=0.957
  • 5×5,step3,valid: 验证集Accuracy=0.868,二分类F1=0.959
  • 6×6,step3,valid: 验证集Accuracy=0.861,二分类F1=0.957
  • 6×6,step5,valid: 验证集Accuracy=0.874,二分类F1=0.959
    PCB-convsize.png

主线 [task16] PCB思想改进

使用PCB思想网络关注的区域变小,可能导致丢失某些全局信息,因此将PCB后的3×3特征图压成1×9的形式,再拼接一个全局均值池化,输出10×2014之后接一维卷积核同上。各项指标无明显变化。
线上结果:召回率约0.886,精确率约0.103

为训练集增加4400张来自视频渠道的新图片,验证集增加1600张,重新训练网络,验证集Accuracy=0.894,二分类精确率 Precision=0.941,召回率 recall=0.963
线上结果:召回率约0.978,精确率约0.117

可以看出补充的数据集有效提高了模型的拟合能力,但可以看出精确率提高缓慢,为了达到要求,需要极大强化网络的性能。

主线 [task26] 更合适的结构

经过了多次失败的尝试,我发现之前的设计中Loss目标和部署时会有略微不同,所以重新修改了网络结构和损失函数。网络结构使用Resnet50[/latex]gt;Conv1024[/latex]gt;conv256[/latex]gt;conv6+conv2,最后一层输出6+2的分类结果,其中6代表6分类,2代表惊悚类和正常类。损失函数使用focalloss(sigmoid(6分类))+α*softmax(2分类),其中α=2。验证集Accurecy=0.912,二分类p=0.967,r=0.955,f1=0.96。
而此时再使用PCB,结果则有不同程度的下降…(嗯..先前的很多结论都被推翻了…)

将图片经过conv256层后的结果可视化后,发现在训练集上网络能将正常图片和惊悚图片明显区分开,并有较大类间距;而在验证集、测试集上,两种类型图片会有混杂。如下图是其中随机的8维结果,x坐标为偶数的是训练集,奇数的是测试集,蓝色是正常图片,红黄紫等颜色是五类惊悚图片。
feature256.png

支线6 尝试其它骨干网络

使用Resnet101网络,验证集Accuracy=0.8298,二分类F1=0.94
使用Resnet101(使用Place365预训练模型)网络,验证集Accuracy=0.89
使用ResNeXt网络
使用Densenet121网络,验证集Accuracy=0.58
使用Densenet169网络,验证集Accuracy=0.78
引入Deformate Conv,无明显效果

主线 弱监督方式

因已离职,不再详述,通过弱监督方式补充大批
量数据参与训练,能较有效的提高精确率,但在后期仍需要迭代模型。

Share

You may also like...

发表评论

您的电子邮箱地址不会被公开。