转自:https://zhuanlan.zhihu.com/p/38031181
https://zhuanlan.zhihu.com/p/151212267
现在又有一个 AI 能干 Photoshop 的活了——自动抠图,一键去除照片背景。
这款 AI 抠图产品已经成型,叫做 GreenScreen。
集智体验了一下,效果还不错。比如,我们让 AI 为象征自由的男人——香蕉君抠个图吧。
熟悉的原图:
然后我们把香蕉君的照片拖到网站上,让 AI 去除背景,说时迟那时快只听嗖的一声:
嗯效果还不错
可能有人会说香蕉君这张照片背景是纯黑色的,也太好抠了。行吧那我们换个复杂点的,让 AI 把灭霸君从这张经典比心心图上抠下来:
同样,我们把照片拖入网站,让 AI 去除背景,结果出来了:
除了旁边物体没有去完整外,整体效果还是不错的吧?
而且抠好的图可以右键保存为 PNG 格式,随时都能使用。
如果你想体验一下,网站传送门:
https://greenscreen-ai.boorgle.com/
那么这款 AI 抠图大师是怎么炼成的?我们来看看作者 Gidi Shperber 分享背后的故事。
过去几年,我(作者Gidi Shperber ——译者注)一直想完成一个真正的机器学习项目。
几个月前学习了深度学习的课程后,我看到了实现这个夙愿的机会:深度学习技术的迅速进步,能让我们完成很多以前无法做到的事情。
本文就分享一下我是如何让深度学习完成以往我们用修图软件所做的工作——移除照片背景。
总的来说,如果使用一些工具比如 Photoshop、PowerPoint,移除照片背景无论是通过手工还是半手工,还是比较容易完成的工作。然而,据我所知,目前完全自动实现照片背景移除,仍然是一项非常有难度的任务,虽然有不少这方面的尝试,但尚无一款产品能在背景移除任务中取得令人满意的效果。
我们要移除什么背景?这就变成了一个很重要的问题,因为一个深度学习模型越具体(比如对物体、角度有详细的要求),图像分割的质量就越高。在刚开始这项任务时,我从宏观上下了个定义:一个通用的图像背景移除 AI,应该能自动识别出任何类型照片的前景和背景。
不过在训练完第一个模型后,我逐渐明白如果重点关注具体类型的照片,效果会更好。因此,我决定重点移除自拍照和人物肖像照片。
自拍照有一些比价显著的特点:首先前景比较突出和集中(一个或多个“人物”),这样能让我们很好的将物体(脸部+上半身)和背景分割,而且能保持稳定和连贯的角度;其次需要处理的物体始终一致(人物)。
脑中做了这些假设后,我就开始了理论研究、代码实现以及数小时的训练过程,创建能够一键轻松实现照片背景移除的自动化服务。
我们工作的主要部分就是训练模型,但是也不能低估部署模型的重要性。即便是表现良好的分割模型,也不如分类模型(比如 SqueezeNet)那样紧凑,因此主动检查了服务器部署和浏览器部署两种选项。
语义分割
在面对深度学习和计算机视觉任务时,显而易见,最适合解决图像背景移除问题的技术就是语义分割。
当然也存在其它的一些方法,比如用深度检测实现分割,但对于解决我们这个问题来说,好像不太成熟。
语义分割,连同分类和物体检测,是计算机视觉领域最有名的三大任务。分割实际上也是一种分类任务,也就是将所有像素进行归类。不想图像分类或图像检测,分割模型真的能让我们看到它“理解”了图像,不仅仅是说“这张图像中有只猫咪”,而且能在像素层面上指出猫咪在哪里、是什么猫咪。
那么分割的工作原理是什么?为了能帮大家更好的理解这一点,我们先来看看该领域早期的一些成果。
最早的想法是利用某些早期分类神经网络,比如 VGG 和 AlexNet。在 2014 年时,VGG 是当时最先进的图像分类模型,在今天仍然非常使用,因为其架构简单直接。查看 VGG 靠前的层级时,你可能会注意到围绕需要分类的数据项有很多较高的激活,更深一些的层有更强的激活,但由于重复的池化操作这些激活的性质会变得粗糙。有了这点理解后,那么就可以假设分类训练模型在略作调整后,也能用于发现或分割物体。
语义分割的早期结果往往伴随着一些分类算法出现。下面是一些用 VGG 产生的较为粗糙分割结果:
靠后层级的结果:
双线性采样之后的结果:
这些结果仅来自于将全连接层转换为原始形状,但保留了空间特征,得到全部卷积的神经网络。在上面的例子中,我们往 VGG 中输入一张 768*1024 的图像,得到一个 24*32*1000 的层。24*32 就是该图像的池化版本(池化了 32),1000 就是 ImageNet 的类别数量,从中可以导出分割结果。
为了能让预测更顺畅些,研究人员使用了一种简单的双线性上采样层。
在 FCN 论文中(https://arxiv.org/abs/1411.4038),研究人员优化了上面的理念。他们将一些层相连,从而获取了更多的特征解释,并且依据上采样率,将其命名为FCN-32,FCN-16和FCN-8:
在层之间添加一些跳转连接,能让预测从初始图像中编码出更精细的细节。进一步训练还能优化结果。
这项技术并不像之前想的那么糟糕,而且证明了深度学习在语义分割任务上有很大的应用潜力。
FCN 解释了分割的概念,研究人员也尝试了几种不同的架构用于语义分割。主要理念还是一样的:使用知名架构,上采样,网络层之间跳跃连接。这些技巧在如今的一些新的模型中仍然常见。
回到我们的项目
在做了一些理论研究后,我确立了三种适用于此项任务的模型:FCN,Unet 和 Tiramisu,最后的 Tiramisu 模型采用了非常深的编码器-解码器架构。期间也考虑了 mask-RCNN,但实现它似乎和我们的任务内容相去甚远。
FCN 看似并不相关,因为它的结果并不理想,但另外两个模型的结果却不错:Unet 和 Tiramisu 不仅结构紧凑,而且运行速度快。在模型实现方面,Unet 实现起来非常直接(我用的是 Keras),Tiramisu 也是易于实现。
接着用一些数据集开始训练这两种模型。我必须提一句,在首次尝试 Tiramisu 后,发现其结果对我有更大的使用潜力,因为它能捕捉图像中的锋利边缘。而 Unet 似乎并不够好,结果要逊于 Tiramisu。
数据
在使用什么模型有了整体的方向后,接着就该搜罗合适的数据集了。用于分割任务的数据并不像用于分类或检测任务的数据那样常见。此外,手动为数据添加标签也不现实。最常见的用于分割任务的数据集是 COCO 数据集(包含 90 个类别下的 8 万张图像)、VOC 数据集(包含 20 类别的 1 万 1 千张图像)和最新的 ADE20K 数据集。
我们选择 COCO 数据集(http://mscoco.org/),因为它里面包含的人物图像更多一些,更符合我们的任务需要。
在考虑移除图像背景的任务时,我比较好奇是使用和任务用途非常相关的图像,还是使用更通用的数据集。一方面,使用包含更多图像和类的更为通用的数据集,能让模型处理更多的场景和挑战。另一方面,一个通宵我们就能训练超过15张图像。如果我们用整个COCO数据集训练模型,最终模型会查看每张图像两次(平均来看),因此略微削减一点对我们更好一点。另外,这样也能生成更专注于我们用途的模型。
另一个值得提的事情是,最初用的 CamVid 数据集(http://mi.eng.cam.ac.uk/research/projects/VideoRec/CamVid/)训练的Tiramisu模型。该数据集虽然有些缺陷,但重要的是,它的图像比较一致:所有的图像都是从汽车上拍摄的道路照片。因此很容易理解,从这样的数据集中学习,对我们的任务基本无用,因此短暂尝试后,决定改用COCO数据集。
CamVid数据集中的图像
COCO 数据集附有非常简单明了的 API,能让我们确切地知道每张图像中包含什么物体。
经过一些试验后,我决定精简数据集:首先我们过滤那些只有人物的图像,这样剩下了 4 万张图像。接着,丢弃所有包含很多人物的图像,这样剩下了只有 2 千张图像,比较契合我们产品的目标。最后,我们只留下 20%-70% 的图像区域被标记为人物的照片,将背景中人物过小或存在奇怪物体的图像。这样经过处理后,最终的数据集包含了 1 万 1 千张图像,我认为对于当前阶段来说足够了。
左图:正常照片,中图:太多人物,右图:人物太小
Tiramisu 模型
如之前所说,我们选用了 Tiramisu 模型。虽然它的全名“100 层 Tiramisu”暗示它是个巨大的模型,但实际上它非常简练,只有 9 百万个参数。作为对比,VGG16 模型的参数多达130万个。
Tiramisu 模型基于 DensNet 模型,它是个比较新颖的图像分类模型,所有的层内部连接。而且,和 Unet 一样,Tiramisu 模型为上采样层添加了跳跃连接。
如果仔细回想一下,这种架构比较符合 FCN 里体现的理念:使用分类架构,上采样,添加跳跃连接以获取更精细的细节。
Tiramisu模型的整体架构
DenseNet 模型可以看作 Resnet 模型的一个自然演变,但不是只“记住”每个层与相邻层的连接,而是整个模型的所有层之间的连接。这些连接被称为“高速连接”。它会导致过滤器数量激增,这被称为“增长率”。Tiramisu 模型有16的增长率,因此我们为每一层添加 16 个新的过滤器,直到最终达到某一层有 1072 个过滤器。你可能觉得会有 1600 个层,毕竟被称为“100 层 Tiramisu”,但上采样层会丢掉一些过滤器。
DenseNet模型架构示意图
训练
我们按照初始论文所描述的方式训练了模型:标准的交叉熵,学习率为 1e-3 的 RMSProp 优化器,较小的衰减值。我将包含 1 万 1 千张图像的数据集进行了分拆,70% 用于训练,20% 用于验证,10% 用于测试。下方所有图像都是取自我们的测试集。
为了能让训练计划和初始论文一致,我将周期大小设置为 500 张图像。这样也能让我们定期以不断优化的结果保存模型,因为我们以更多的数据训练了模型。
此外,我们只用两个类训练模型:背景和人物。我们首先试着训练 COCO 数据集中的一些类,不过很快就发现这对我们训练没多少帮助。
数据问题
数据集的一些缺陷也损害的模型的性能:
- 动物——我们的模型有时会分割动物,这当然会造成很低的IOU。在我们任务中往主要类别增加动物进去或者是其它东西会造成结果变差。
- 身体部位——由于我们是用程序过滤数据集,就没法判断人物类实际上是人还是某些人体部位,比如手和脚。这些图像并不在我们的目标范围内,但还是到处出现。
动物,人体部位,手持物体
- 手持物体——数据集中的很多图像都和运动相关,比如棒球帽、网球拍、滑雪板这些物件到处都有。对于怎么分割这些物体,模型有点混乱。和上面出现动物的情况一样,将它们归为主要类或单独的类,都有助于提高模型性能。
含运动设备的照片
- 粗糙的真实数据——COCO数据集并非按照逐个像素标记的,而是使用多边形标注。有时会很好,但有时数据就比较粗糙,会妨碍模型学习。
非常粗糙的照片
结果
最终结果虽称不上完美,但也算理想:测试集中的 IoU 为 84.6%,而目前最好的结果为 85%。得到这个数据也是非常坎坷的,因为在不同的数据集和类中,它会不断变动。有些类比较容易分割,比如房子、道路,在这些类中,大部分模型都能达到 90% 的 IoU;而在另一些比较麻烦的类中,比如树和人物,大部分模型只能达到 60% 的 IoU。为了估量这方面的难度,我们帮模型专注于单一类别,以及有限类型的图像。