Airbnb 爱彼迎房源图片分类实践

Airbnb 爱彼迎房源图片分类实践

本文最早于 2018 年 5 月 3 日发表(原文链接)。主要介绍了 Airbnb 使用大规模深度学习对房源图片按房间类型进行分类的实践。
作者:Shijing Yao, Qiang Zhu, Phillippe Siclait,Airbnb 数据科学家
译者:Xiangming Chu,Airbnb 中国工程师
校对:Chelsea Zhang,Airbnb 中国用户体验研究;Xiangyang Shi,Airbnb 中国工程师;Lei Shi,Airbnb 中国工程师

大规模深度学习 (Large-scale deep learning) 模型正在改变我们对平台上房源图片的思考方式。

Airbnb 是一个有数百万房源的民宿平台。来自世界各地的旅客们都在 Airbnb 上搜索房源,发现最适合他们旅途的落脚地。在用户搜索时,除了位置和价格,房源图片也是最关键的决策因素之一。然而长久以来,我们对这些非常重要的照片知之甚少。当用户在浏览房源的图片时,我们无法帮助他们找到最含信息量的图片,也难以保证这些图片中传达的信息是准确的,同时也无法成规模地指导房东如何提升其房源图片的吸引力。

得益于计算机视觉和深度学习领域的最新进展,我们已能够利用技术成规模地解决这些问题。我们的第一个项目是要将房源图片按照不同的房间类型进行分类。一方面,分类能将同种房间的图片分到一组,让用户更为顺畅地参观房源;另一方面,分类大大简化了校验每种房间数量以及房源基本信息的工作。相信接下去还有许多振奋人心的机会能让我们进一步深化对 Airbnb 平台上图片内容的了解。我们将在文末展示一些例子。

图像分类

对优化用户体验而言,将给定房源图片按房间类型正确分类是极其有用的。在用户端,它能帮助我们按不同的房间类型对房源照片进行重新排序和布局,优先展示用户最感兴趣的照片。在房东端,它能帮助我们自动审核房源,保证它们符合我们平台的高标准。精准的图片分类功能是这些核心功能的关键所在。

我们尝试分类的首批房间类型包括卧室、浴室、客厅、厨房、泳池和房源景观。我们还将根据产品团队的需求拓展到其他房间类型。

房间类型分类问题和 ImageNet 分类问题相当类似,只不过我们的模型输出的是预先定义好的的房间类型。这就导致我们无法直接应用当今成熟的 DNN(深度神经网络)模型里最先进的模型,如 VGG,ResNet。网上有很多很好的文章告诉大家如何处理这个问题。总的来说,我们应该:1. 修改 DNN 的顶部几层以确保其结果维度匹配我们的模型;2. 对 DNN 进行一定程度的重新训练(retrain)以获得满意的效果。 几番测试后,我们选择了 ResNet50 作为我们的引擎,原因是它能很好地平衡模型效果和计算时间。为了兼容我们的应用场景,我们额外添加了两个全连接层(fully connected layers),还在最后加了一个 Softmax 激活函数。我们还尝试了一些训练参数,这些将在下一节中讨论。

重新训练后的 ResNet50

修改后的 ResNet50 结构,基础模型图片引用自 Kaiming He

重新训练 ResNet50 包含以下三种方法:

  1. 保持基本的 resnet50 模型不变,使用最少的数据重新训练新增的两个层。这通常也被称为微调(fine-tuning)。
  2. 使用更多的数据执行与 1 中相同的微调。
  3. 重新训练整个改良的 ResNet50。

大多数在线教程都使用第一种方法,因为它速度很快,而且通常会产生不错的效果。我们尝试了第一种方法,确实得到了一些合理的初步结果。然而,为了开发出高质量的图像分类功能,我们需要大幅提高模型性能——理想情况下应达到 95% 以上的准确率和 80% 以上的召回率。

为了同时实现高准确率和高召回率,我们意识到必须要用海量数据对 DNN 进行重新训练。然而,有两个主要的挑战:1. 尽管我们有很多房东上传的房源照片,但是我们没有这些照片准确的房间类型标签,或者根本就没有标签;2. 重新训练像 ResNet50 这样的 DNN 绝非易事——需要训练超过 2500 万个参数,这需要大量的 GPU 支持。我们将在接下来的两个部分中讨论这两个挑战。

利用图片标题进行有监督的学习

许多公司找第三方供应商为图像数据打上高质量的标签。但这显然不是最经济的解决方案,因为我们需要打标签的照片有数百万张。为了兼顾成本和效果,我们采取了折中的方式处理打标签的问题。一方面,我们要求供应商为数量相对较少的照片打上标签,通常为数千或数万张。这些带标签的数据将被用作我们评估模型的黄金集合。我们使用随机抽样来获得这个黄金集合,并确保数据是无偏的。另一方面,我们利用房东创建的图像标题里包含的房间类型信息,并从中提取标签。这个想法对我们来说有巨大的价值,正因为有了它,昂贵的打标签任务变得基本免费了。我们只需要一个恰当的方法来确保从图像标题中提取的房间类型标签是准确可靠的。

我们很容易想到的一个从图像标题中提取房间类型标签的方法是:如果在图像标题中找到某个房间类型关键字,则该图像将被标记为该类型。然而现实要复杂得多。这个策略下产生的结果会让你非常失望。我们发现在许多情况下,图像标题与图像的实际内容相差甚远。下面是几个反例。

从图片标题中提取出来的错误标签

为了过滤掉这样的反例,我们在从图像标题提取房间类型标签时添加了额外的规则。经过几轮筛选检查,标签质量有了很大的提高。下面是我们如何过滤厨房数据以获得“干净”的「厨房」图像的示例。

AND LOWER(caption) like '%kitchen%'
AND LENGTH(caption) <= 22
AND LOWER(caption) NOT LIKE '%bed%'
AND LOWER(caption) NOT LIKE '%bath%'
AND LOWER(caption) NOT LIKE '%pool%'
AND LOWER(caption) NOT LIKE '%living%'
AND LOWER(caption) NOT LIKE '%view%'
AND LOWER(caption) NOT LIKE '%door%'
AND LOWER(caption) NOT LIKE '%table%'
AND LOWER(caption) NOT LIKE '%deck%'
AND LOWER(caption) NOT LIKE '%cabinet%'
AND LOWER(caption) NOT LIKE '%entrance%'

由于这些额外的过滤器,我们丢失了相当多的图像数据。这对我们来说是可以接受的,因为即使经过这样严格的过滤,我们最终还是得到了几百万张照片,每种类型的房间有几十万张。而且现在这些照片的标签质量要好多了。此处我们先假设数据分布没有随着过滤而改变(我们只要在一个无偏的黄金数据集上测试模型就可以验证这个假设)。

当然,我们原本也可以用一些 NLP 技术来对图像标题进行动态聚类,而不是使用基于规则的启发式方法。不过,我们还是决定先用它,未来再考虑 NLP。

模型建立、评估和生产

左图:8 核并行训练 GPU 表现 右图:分布式 SGD 图片引用自 Quoc V. Le.

使用几百万图片重新训练 ResNet50 这样的 DNN 需要大量的计算资源。我们在实践中,使用了AWS P2.8xlarge Instance 搭载 Nvidia 8-core K80 GPU, 每个训练步骤向 8 个GPU批量推送 128 张图片。后台使用 Tensorflow 进行并行训练。我们需要把模型的编译放在并行化之后,否则训练不起作用。为了进一步给训练提速,我们使用从 keras.applications.resnet50.resnet50 加载预先训练的 imagenet 权重初始化模型权重。经过 3 个阶段的训练,获得了最佳模型,持续时间约 6 小时。之后,模型开始过拟合,验证集的性能不再改善。

特别注意,在生产环境我们为每种不同的房间类型构建了二分模型(binary-class models),而不是为所有房间类型构建了一个多重分类模型(multi-class model)。这并不理想,但由于我们的模型服务大部分是离线的,因此多个模型调用造成的额外延迟对我们的影响非常小。我们的生产环境将很快过渡到多类模型(multi-class model)。

我们根据准确率和召回率来评估我们的模型。我们还监控了 F1 分数和准确率等指标。其定义重申如下。简言之,准确率代表了我们对正面预测的精度有多自信,召回率代表了我们正面预测覆盖所有实际正面预测的百分比。准确率和召回率通常是相互矛盾的。在我们的应用场景下,我们为准确率设置了一个很高的标准(95%),因为当我们声称照片是某种房间类型时,我们必须十分确定。

TP: True Positive, TN: True Negative, FP: False Positive, FN: False Negative

混淆矩阵(confusion matrix)是计算这些指标的关键。我们的模型的原始输出是每个图像的概率分数从 0 到 1。要为一组预测计算一个混淆矩阵,首先必须设置一个特定的阈值,将预测得分转换为 0 和 1。然后通过将阈值从 0 扫到 1,生成精确召回(P-R)曲线。原则上,P-R 曲线的 AUC(曲线下面积)越接近 1,模型越精确。

在评估模型时,我们使用了前面提到的黄金集合,其中给图片打的标签由真人提供的。有趣的是,我们发现不同房间类型的准确率不同。卧室和浴室的模型是最精确的,而其他的模型则不那么精确。为了简洁起见,这里只显示卧室和客厅的 P-R 曲线。虚线的交叉点表示给定特定阈值的最终表现。

卧室 P-R 曲线
客厅 P-R 曲线

两个重要的观察结果:

  1. 「卧室」模型的整体表现要比客厅好得多。有两种解释:1. 「卧室」比「客厅」更容易分类,因为「卧室」设置相对标准,而「客厅」可以有更多的种类。2. 从「卧室」照片中提取的标签比从「客厅」照片中提取的标签质量更高,因为「客厅」照片偶尔也包括「餐厅」甚至「厨房」。
  2. 在每个房间类型中,完全重新训练的模型(红色曲线)比部分重新训练的模型(蓝色曲线)具有更好的性能,并且「客厅」模型之间的差距大于「卧室」模型之间的差距。这表明,重新训练一个完整的 resnet50 模型对难以识别的房间类型有不同的影响。

对于我们产出的 6 种模型,总体准确率在 95% 以上,召回率一般在 50% 以上。大家可以通过设置不同的阈值来进行权衡。该模型在 Airbnb 内部支持了多个产品团队的许多不同的需求。

用户将我们的结果与知名的第三方图像识别 API 进行了比较。他们的结论是,Airbnb 内部研发模型的表现整体上要优于第三方通用模型。这告诉我们,如果能充分利用数据集的特点,在特定业务需求的场景下,自己训练的模型甚至有机会超越业界最先进的模型。

在这一节的最后,我们将举一些具体的例子来说明这个模型的能力。

左边两张图被正确地预测为卧室;右边两张图被正确地预测为不是卧室
左边两张图被正确地预测为浴室;右边两张图被正确地预测为不是浴室
左边两张图被正确地预测为泳池;右边两张图被正确地预测为不是浴室,因为它们分别是水族馆和滨水区

分类(功能)之外

在做这个项目的时候,我们还尝试了除了房间类型分类之外的一些有趣的想法。我们想举两个例子,来让大家感受一下这些问题是多么令人兴奋。

无监督(unsupervised)场景分类

当我们第一次尝试使用预先训练的 ResNet50 模型进行房间类型分类时,我们为房源封面照片生成了图片 embeddings(2048 x 1向量)。为了解释这些 embedding 的含义,我们使用 PCA 技术将这些长向量投影到二维平面上。让我们吃惊的是,预测的数据自然地聚集成两组。观察这两组结果,我们发现左侧几乎都是室内场景,右侧几乎都是室外场景。这意味着不需要任何再培训,只需在图像 embedding 的第一个主要组件上设置一条切割线,我们就可以区分室内和室外场景。这一发现为一些真正有趣的领域打开了大门,转移学习(embedding)与无监督学习相结合。

室内和室外照片自动聚类

物体检测

我们尝试的另一个领域是物体检测。一个预先训练的 Faster R-CNN 模型在 Open Images Dataset(公开图像数据集)上已经产生了极好的结果。如下例所示,该模型能够检测窗户,门,餐桌以及他们的位置。我们使用 Tenserflow 物体检测 API(Tenserflow Object Detection API)对房源图片做了一些快速的评估。使用现成的结果可以检测到很多家庭设施。未来我们计划使用 Airbnb 的自定义便利设施标签重新训练 Faster R-CNN 模型。由于这些标签中的一些在开源数据中丢失了,我们很可能会自己创建标签。有了这些算法检测到的便利设施,我们将能够确认房东的房源质量,使客人更容易找到具有特定便利设施需求的房源。这将把 Airbnb 的照片智能化推到一个新的水平。

成功检测到了窗户,门和餐桌

总结

我们总结了一些关键点,希望对其他深度学习从业者有所帮助:

第一,深度学习只是一种特殊的有监督学习(supervised learning)。高质量标签对数据是极其重要的。由于深度学习通常需要大量的训练数据来实现最好的效果,因此找到一种有效的标记方法至关重要。幸运的是,我们发现了一种经济、可扩展和可靠的折中方法。

第二,从头开始训练像 resnet50 这样的 DNN 可能会非常复杂。建议从更简单而快速的方式开始——用一个小的数据集训练顶层。如果你确实有一个大的可训练数据集,从头开始重新训练一个 DNN 也许能带来最优秀的效果。

第三,尽可能并行训练。在我们的例子中,我们通过使用 8 GPU 获得了大约 6 倍(准线性)的速度。这使得构建一个复杂的 DNN 模型在计算上是可行的,并且更容易在超参数(hyper parameters)和模型结构上迭代。

编辑于 2019-04-10