你好,游客 登录
背景:
阅读新闻

深度学习带你抠像表演

[日期:2017-09-21] 来源:36大数据  作者: [字体: ]

前情

回顾这些年机器学习的进展,我也一直想弄点真正的机器学习产品。

几个月前,我参加了超棒的Fast.ai深度学习课程之后,我好像就开窍了,我也有了机会:得益于深度学习技术,很多以前不可想象的事情,现在都已不在话下。新开发的工具也使得部署进程比以往更方便。

在之前提到过的课程里,我遇到了Alon Burg,他是位富有经验的Web开发者,我们在合作以求实现这个目标。

我们一同给自己设立了以下目标:

  1. 提高我们自身的深度学习技巧

  2. 提高我们AI产品的部署技巧

  3. 制作一款有市场需求的实用产品

  4. 要有趣(对我们俩和使用者都是)

  5. 分享我们的经验

考虑到以上目标,我们正在探索的点子如下:

  1. 还没人做过的(或者还没人做好的)

  2. 计划与实施起来不会太难的——我们的计划是用2-3个月的时间完成,以及每周只有一个工作日的负荷。

  3. 简洁走心的用户界面——我们想做一款让人们愿意用,而不是仅仅用作展示的产品

  4. 训练数据什么的一应俱全——正如任何机器学习从业者所知,有时数据比算法更宝贵。

  5. 会使用前沿的深度学习技术(谷歌、亚马逊及其云平台伙伴目前仍未将其商业化),但又不那么过于前沿(这样我们就能在网上找到一些例子啦)

  6. 会有潜力达到“可以商品化”这个级别。

我们的早期想法是试试医疗项目,但因为这个领域与我们息息相关,然后我们那时感觉(现在也感觉)在医疗领域短期内就会被深度学习颠覆。但是,我们也意识到我们会碰上数据收集的问题,这或许会涉及合法性及法律法规等问题,而这并不符合我们想要让成果简化这一目标。我们的第二选择就是抠图产品了。

背景移除是手动或半手动操作起来都很简单的任务(Photoshop,甚至PowerPoint都有这类工具),用某种虚拟“马克笔”和边缘检测就可以实现,这里有个例子。但是,全自动抠像就很有挑战性了,而且就我们所知,虽然已经有人在尝试,但目前还没有出现令人满意的产品。

我们会移除什么样的背景呢?这还真是个重要的问题,因为在一群对象、角度及其他因素中的目标越具体,剥离的质量就会越高。当我们开始着手工作时,我们的野心很大:一个适用于各类图片的综合性抠像神器。但在我们完成对首个模型的训练后,我们明白,最好还是专注于特定类型的图片。因此我们决定的图片类型是自拍与人像。

 

熏肉味的笑容

自拍就是有着显著和集中的前景(一个或更多人),可以保证我们可以顺利使之与其他对象(脸+上身)和背景分离,以及有着相当恒定的角度,也总是含有相同的对象(人)。

基于这些假设,我们开始了研究、实施、训练的旅程, 来创造一个简单易用的背景去除工具,只需一键点击。

我们工作的主要内容就是对整个模型进行训练,但我们也不能低估妥善部署的重要性。好的分离模型仍然不如分类模型那样简洁(比如 SqueezeNet),而且我们也主动检查了服务器和浏览器的部署选项。

语义分割Semantic Segmentation

当检测和我们类似的深度学习和计算机视觉任务时,很容易看出,我们的最佳选项就是逻辑语义分割任务了。

其他的策略,比如景深检测分离 ,但对我们来说还不够成熟。

语义分割是个广为人知的计算机视觉任务,与分类及对象检测共占3甲地位。分割,实际上就是一种分类任务,在该意义上,将每一个像素都进行分类。不像图像分类或检测,分割模型真的会展现出一些对图像的“理解”,不只是能识别出“这个图里有只猫”这样的程度,而是在像素级别上识别出这只猫是哪里的哪个。

所以,这种分割是如何生效的呢?为了更好地理解,我们将会检验本领域的一些早期成果。 早期想法是采用一些早期分类网络,比如VGG Alexnet 。VGG 是2014年图像分类领域最先进的模型,因其简单直接的架构,即使在今天也很实用。当检查VGG的前几层时,我们或许会发现在分类目标的周围激活程度很高,以用于分类。更深的层甚至拥有更强的激活程度,然而由于重复的池化行为,它们本质上却是粗糙的。现在懂了这些,我们假设分类训练也可以被用于在伴有调整的状况下进行寻找/分割对象。

语义分割的早期成果是与分类算法融合的。在这篇文章中,你可以看到更多来自使用VGG的粗糙的分割结果:

 

上车,走吧

深层结果:

 

浅紫色是校车

经过双线性上采样之后:

这些结果仅来自于将完全连接的层转换(或维护)为原始形状,保持其空间特征,获得完全的卷积网络。在上面的例子中,我们把一张7681024的图片输入VGG,然后得到一层24321000的图像。2432 是合并版本的图像(by 32),而1000是图像网络类别数。从中我们可以导出上述分割。

为了让预测更加精细,研究者们使用了未经处理的双线性插值层。

在这篇FCN论文中,研究者们改进了以上思路。他们将一些层连在了一起,使得解读更加丰富,根据向上采样率分别命名为FCN-32,FCN-16,和FCN-8:

在层之间添加一些跨层连接可使预测从原始图像编码出更完整的细节。进一步的训练甚至提高了成果。

该技术本身并不像我们以前所认为的那么糟糕,并且证明了其在深入学习中语义分割方面确实很有潜力。

 

来自论文的FCN 结果

FCN解锁了分割的概念,研究者们也为该任务尝试不同的架构。主要的思路还是相似的:使用已知的架构、向上分析、然后使用跨层连接,在较新的模型中依然是这个套路。

你可以在这里找到关于该领域的新进展:here, here and here。你也可以找到很多保留编码-解码架构的架构。

回到我们的项目上来

在做过一些研究之后,我们确定了适用于我们项目的三个模型:FCN, Unet和Tiramisu ——它们都有深层次的编码-解码架构。我们也对mask-RCNN有些意向,但实施起来好像和我们的项目目标不太一致。

FCN 看起来并不相关,因为其结果比我们想象的差一些(即便是初试期),但我们提到的另外两个模型给出的结果还不错:在 CamVid 数据集上的 Tiramisu ,以及 Unet 的主要优势是其紧凑性和速度。在实施方面,Unet 很直接地就实现了(我们用的是Keras),而Tiramisu 也是可实施的。为了启动我们的项目,我们在杰里米·霍华德(Jeremy Howard)强大的深度学习课程的最后一课中使用了Tiramisu进行了一次很好的实现。

有了这两个模型,我们得以继续前进并开始在一些数据集上进行训练。不得不说,在第一次试用Tiramisu后,我们发现其结果对我们而言拥有更多潜力,因为它拥有捕获图片中锋利边缘的能力。另一方面,Unet看起来就不够优秀,结果看上去也不太OK。

 

Unet 的不OK

数据

在设定了我们模型的总体方向后,我们开始寻找合适的数据集。分割数据并不像分类甚至检测那样常见。另外,手动标记也真的不太做得来。最常见的适合分割的数据集是 COCO 数据集,其中包括大概90个类别的8万张图像,还有包含20个类别的1万1千张图像的 VOC pascal 数据集,以及更新的 ADE20K 数据集。

我们选择用COCO数据集,因为它包含了非常多带有“人”这个分类的图像,正好符合我们的目标。

考虑到我们的任务,我们考虑是否仅使用那些和我们相关性较强的图像,还是要使用相对来说更加综合的数据集。一方面,使用图片更多、分类更多的更综合的数据集会让模型见识更多,处理更多挑战。另一方面,在连夜的训练进程中,我们能够处理超过~15万张图像。如果我们继续引入包含整个COCO 数据集的模型,我们将可以让模型(平均)检视每张图像两次,因此做出一点修改还是有些额外的好处的,这会让模型更加专注于我们的目标。

还有一件值得一提的事——Tiramisu模型原本是在 CamVid 数据集上进行训练的,有一些缺陷,但最重要的是,那些图像非常单一:全部图像都是汽车的路拍。这就很好理解了,从这样的数据集(即使里面包含人)中进行学习对我们的任务来说毫无益处,所以在简短的试验之后,我们就上道了。

 

CamVid 数据集的图像

COCO 数据集带来了非常直接的 API,使得我们可以准确获知每张图片上都有什么物体(根据已有的90个预分类)。

在进行一些实验后,我们决定要稀释这些数据集:首先我们筛选出了其中只包含1个人的图像,大概是4万张。接着我们选出了包含多于1个人的图像,然后只剩下一两张,这就是我们的模型应该找出来的。最后,我们只留下了那些20%-70% 的标记为“人”的图像,去除了背景中包含很小的人或是其他迷之生物(很不幸,并没有排查完全)的图像。我们最终的数据集包含了1万1千张图片,在本阶段够用了。

 

左:好图——中:人物较多——右:对象太小

Tiramisu 模型

就像刚才说的,我们在Jeremy Howard’s的课程中接触了 Tiramisu 模型。虽然它的全称“100层Tiramisu”意味着它是个巨大的模型,有9百万的参数,但和拥有1亿3千万参数的VGG16比起来还是蛮经济的。

Tiramisu模型基于DensNet,是最新的图像分类模型,所有的层都是互联的。此外,Tiramisu像Unet一样给向上采样层加入了跨层连接。

如果你能回想起来,这种架构与FCN中提出的想法是一致的:为了精细度而使用分类架构、向上识别以及加入跨层连接。

 

Tiramisu整体架构

DenseNet 模型可被视作Resnet模型的一种自然进化版,但并不是在有新的层进来之后“记住”每一层,Densenet会记住模型中所有的层。这些连接被称为高速路连接。它会导致滤波器数量增长,这被定义为“增长率”。Tiramisu拥有16的增长率,因此每一层都会增加16个新的滤波器,直到达到1072个滤波器。你或许在想,因为是100层的Tiramisu,所以应该会有1600层吧?但是,向上识别层会丢弃一些滤波器。

 

Densenet模型草图——浅层滤波器在整个模型中堆叠

训练

我们采用原论文所述的时间表训练我们的模型:标准交叉熵损失,有1e-3学习率和小规模衰减的RMSProp优化器。我们把1万1千张图像分为70% 用于训练,20% 用于验证,10%用于测试。以下全部图像都来自我们的测试组。

为了让我们的训练进度与原论文保持一致,我们在500张图像上设置了时间段大小。因为我们已经用更多数据对其进行了训练(本文中使用的CamVid 数据集包含了不到1千张图片),这使得我们可以在一段时间里用结果中的每个改进来对模型进行积累。

另外,我们只在两个类别上对其进行训练:背景与人,当然原论文里包含了12个分类。我们首先尝试在COCO的类别上进行训练,但我们发现这对训练来说帮助不大。

数据问题

一些数据集的缺陷妨碍了我们的核心:

  • 动物——我们的模型有时会分割出动物。这当然要归咎于比较低的IOU。在我们任务中同样的主要分类里加入动物或其他对象,或许会移除我们的结果。

  • 身体部位——因为我们程序化地过滤数据集,使得我们无法分辨“人”这个分类到底是“人”还是像手或脚这样的身体部位。这些图像不在我们的目标范围里,但依然会出现在各种地方。

 

动物、身体部位,手持物体

  • 手持物体——数据集中的许多图像都是运动相关的。棒球棒、网球拍和滑雪板到处都是。这样的话模型就会懵X,不知究竟该如何分割这些对象。比如在这个有动物的图里,将其加入作为主类的一部分或者单独的类别在我们看来将有助于模型的表现。

 

包含物体的运动图片

  • 粗劣的正确标记  ——COCO数据集并不是逐个像素注释的,而是用多边形。有时这就足够了,但其他时候参考标准就十分粗劣,很可能会阻碍模型从细节中学习。

 

图像和(非常)粗劣的正确标记

 

他可能会迟到,但不会缺席。

成果

我们的成果虽然还达不到特别完美,但还是比较令人满意的:我们已经在测试组中达到了84.6的IoU,目前的状态则是85。但这个数字很狡猾:它会在不同的数据集和分类中产生波动。这里有些本来就很容易分割的类别,比如房屋、道路等,大多数模型都能轻易达到90 IoU的结果。其他更有挑战性的类别就是树木和人类了,在这些类别上,模型只能达到大概60 IoU。为了衡量这个困难,我们帮助网络专注于一个单一的分类,同时也限制了照片的类型。

我们仍然感觉我们还没能达到之前期望的“可以商品化”的程度,但我们是时候该停下来,讨论一下我们的成果了,因为已经有大概50%的照片可以给出不错的结果了。

这里是一些该程序所具备的能力的优秀例子:

调试和日志记录

训练神经网络的一个重要部分就是调试。当我们着手我们的项目时,我们非常想直捣黄龙,搞来数据和网络、开始训练,然后看看能出来什么样的成果。但是我们发现,跟踪每一步动作,以及为我们自己制作可以检测每一步结果的工具都至关重要。

这里是一些常见的挑战,以及我们是如何应对的:

  1. 早期问题——模型或许没有经过训练。或许是因为一些遗留问题,或者因为某类预处理错误,比如忘记将一些数据标准化。不管咋说,将结果简单地可视化会非常有帮助。这里有一篇关于这个主题的[好文章]。

  2. 调试网络自身—— 在确保没有严重问题后,就可以用预定义的损失和指标开始训练了。在分割中,主要的测量指标就是IoU——交叉点结合。我们经过了好几轮的训练才开始使用 IoU 作为我们模型的主要测量指标(而不是交叉熵损失)。另一个有帮助的实践,就是展示一些模型在每个时段的预测。这里有一篇很棒的关于调试机器学习模型的文章。要记住,IoU 在Keras中不是一个标准的指标/损失,但你很容易就可以在网上找到,比如这里。我们也使用这个行动 gist 来绘制每个时期的损失与一些预测。

  3. 机器学习版本控制——当训练一个模型时会需要很多参数,其中有些很难跟踪。我不得不说,除了热切地编写我们的配置(以及用Keras的召回功能自动保存最好的模型,见下文),我们依然没能找到完美的方法。

  4. 调试工具——在做完上述全部之后,我们终于可以检验我们的每一步工作了,但那并不是无缝衔接的。因此,最重要的一步是将上述步骤结合在一起,并创造一个Jupyter 笔记本,这样我们就可以无缝加载每个模型与每个图像,并快速检测其结果。通过这种方式我们可以简单地看出模型间的区别、缺陷和其他问题。

这是我们模型在调整参数和额外的训练时改进的例子:

用目前最好的验证IoU来保存模型:(Keras提供了一个非常好的召回功能 callbacks 让这些事情变得更加简单)

callbacks = [keras.callbacks.ModelCheckpoint(hist_model, verbose=1,save_best_only =True, monitor= ’val_IOU_calc_loss’), plot_losses]

除了对可能的代码错误进行正常调试外,我们也注意到了模型错误是“可预测的”,比如 “切掉” 身体部分似乎超出了总体的身体计数、在大片分割上的“缺块”、不必要地延续身体部位、照明差、质量差、以及很多细节。其中一些注意事项在添加特定的来自不同数据集的图像时会被处理,但其他的就有待解决了。为了改进下一个版本的结果,我们将对模型使用特定于“困难”图像的扩充。

我们之前已经提到过这个问题了,即数据集的问题。现在来看看我们模型遇到的困难:

  1. 衣物——颜色要么很深要么很浅的衣服有时倾向于被解读为背景

  2. “缺块”——从另一个角度看,有一些缺块也不错

 

衣物与缺块

  1. 光线——光线的缺乏和模糊在图像中很常见,但在COCO 数据集中不是。因此,除了模型处理这些问题的一般困难外,我们的模型甚至还不能处理更难的图像。这个可以通过获得更多的数据,另外也可以通过数据增加来改进。与此同时,也尽量别在晚上测试我们的应用 :)嘻嘻嘻

展望前景

进一步训练

我们的产品成果经过了~300个时段的数据训练。在这段时间后,模型开始过拟合。我们已经达到非常接近可发布状态的结果了,因此我们还没有机会应用数据扩充的基本实践。

在将图像调整到224X224后我们也训练过模型了。用更多数据和更大的图像(COCO图像的原始大小大概是600X1000)进行进一步的训练预计也会对成果有所提升。

CRF 和其他增强

在某些阶段,我们发现我们的成果在边缘部分有些不平整。一个可以对这一点进行改进的模型就是CRF了。在这篇博文中,作者展示了略微无修正地使用CRF的例子。

但是,这对我们来说帮助不大,也许是因为只有结果更粗糙时才会有总体上的效果。

Matting

即使以我们的当前成果来看,分割也不是那么完美。头发、精美的衣服、树枝和其他精细的对象也许还无法被完美地分割开来,因为参考标准的分割也不包含这些细节。分离这种细微分割的任务叫做Matting,并定义了一种不同的挑战。这里是一个最先进的Matting的例子,今年早些时候发表于英伟达大会。

 

Matting示例——输入也包括三分图

Matting任务不同于其他图像相关的任务,因为它的输入不仅包括图片,也包括了一张三分图 —— 图像边缘的轮廓,使其成为了一个“半监督”问题。

我们用Matting实验了一下,使用我们的分割作为三分图,但我们没能取得重大成果。

还有一个问题就是缺少适当的用于训练的数据集。

总结

正如开头所说,我们的目标是制作一款出色的深度学习产品。正如你在 Alon 的文章里看到的,部署变得比以往更加简单更加快速。另一方面,训练一个模型又很棘手——训练,尤其是要求彻夜完成时,就需要缜密的计划、调试和结果记录。

要平衡研究与尝试新事物之间也不简单,还有无聊的训练与改进。自从我们使用了深度学习,我们总有一种感觉,就是我们需要的最好的模型,或者最准确的模型,就近在咫尺,再谷歌搜索一下或者再看一篇文章就找到了。但在实践中,我们真正的改进却是来自于简单地从我们的原始模型里往外“挤”出更多东西。也正如之前所说的,我们仍然感觉可以挤出更多东西来。

收藏 推荐 打印 | 录入:Cstor | 阅读:
相关新闻      
本文评论   查看全部评论 (0)
表情: 表情 姓名: 字数
点评:
       
评论声明
  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款