为图像建议预设:在 VSCO 构建“适合此照片”功能
2019 年 6 月 24 日
VSCO 工程团队发布的客座文章

VSCO,我们构建由自我表达驱动的创意工具、空间和连接。我们的应用程序是一个人们可以创作和编辑图像和视频,发现技巧和新创意,并连接到一个充满活力的全球社区的地方,这里没有公开的点赞、评论和关注者数量。
VSCO on mobile app
我们使用机器学习作为个性化和指导每个用户创作过程的工具。VSCO 拥有超过 160 种预设的目录,这些预设是预先配置的编辑调整,可帮助我们的用户转换图像,包括模拟旧胶片相机的底片。
Pictures of a building before and after a filter is applied
在应用任何滤镜之前的一栋建筑物的照片。图像来自 Sarah Hollander(左)。应用了 AU5 预设的建筑物照片。图像来自 Sarah Hollander(右)。
然而,我们的研究表明,用户对大量预设感到不知所措,他们只使用自己熟悉的几个最喜欢的预设,而不是尝试新的预设。

解决方案

我们的挑战是如何克服决策疲劳,提供值得信赖的指导并鼓励发现,同时仍为用户留出空间,让他们可以根据自己的创意来编辑图像。我们的图像团队精心策划了补充不同类型图像的预设,使我们能够为每张照片提供个性化的推荐。我们决定通过使用深度卷积神经网络 (CNN) 模型进行设备上的机器学习来为图像建议预设,因为这些模型可以理解图像中的许多细微差别,使分类比传统的计算机视觉算法更容易、更快。

考虑到所有这些,我们开发了“适合此照片”功能,该功能使用设备上的机器学习来识别用户正在编辑的图像类型,然后从精选列表中建议相关的预设。该功能深受用户喜爱,现在“适合此照片”已成为使用率第二高的类别,仅次于显示所有预设的“全部”。

“适合此照片”功能的实际操作视频


为了理解当用户开始编辑图像时“适合此照片”功能的工作流程,我们将逐步介绍发生的过程。当用户在编辑视图中加载图像时,模型的推理会立即开始,模型会返回图像的类别。然后,类别 ID 与缓存目录中的 ID 匹配,并检索该类别的预设列表。系统会选择六个预设,这些预设是免费预设和作为 VSCO 会员资格一部分的预设的组合,以便在“适合此照片”部分向用户展示。向非会员展示免费预设和付费预设对我们来说很重要,因为这样可以为非会员提供价值。作为 VSCO 会员资格一部分的预设仍然可以被非会员预览,以便让他们了解 VSCO 会员资格的价值。对于会员,这样做的好处是可以让他们了解其会员资格预设可以用于哪些类型的图像。

设备上的机器学习确保可访问性、速度和隐私

从一开始,我们就了解到基于服务器的机器学习不适合此功能。我们希望该功能使用设备上的机器学习主要有三个原因:离线编辑、速度和隐私。

首先,我们不想通过仅在用户在线时提供此功能来限制用户的创意。灵感可能随时随地出现——用户可能在网络连接有限的情况下拍摄和编辑照片,例如在沙漠中或高山上。我们用户群中很大一部分位于美国以外,因此并非所有人都能始终访问高速互联网。

其次,我们希望确保编辑速度很快。如果我们通过将机器学习模型部署在云端来提供“适合此照片”功能,则需要将用户的图像上传到云端进行分类(这需要时间、带宽和数据),并且用户需要下载预设,这会使过程在良好的连接情况下变慢,而在糟糕的连接情况下则无法进行。在设备上进行机器学习意味着所有操作都在本地完成,速度很快,并且不需要连接。对于帮助用户捕捉瞬间并保持创作流程来说,这一点至关重要。

第三,编辑过程是私密的。基于服务器的解决方案需要我们在用户发布照片之前,在用户仍在编辑照片时上传用户照片。我们希望在用户的创作过程中注意保护用户的隐私。

为什么要使用 TensorFlow

由于我们想要使用自定义模型进行设备上的机器学习,因此 TensorFlow Lite 是一个显而易见的选择,因为它可以轻松地将服务器上训练的模型转换为与手机兼容的模型(.tflite 格式),方法是使用 TFLiteConverter

此外,我们已经在生产系统中体验过 TensorFlow 和 TensorFlow Serving 在服务器端机器学习方面的成功。TensorFlow 的库的设计初衷就是将运行机器学习作为主要目标,因此我们认为 TensorFlow Lite 也不会有所不同。

我们使用 ML Kit 在 TensorFlow Lite 模型上直接运行推理,并将其无缝地集成到我们的应用程序中。这使我们能够快速将该功能从原型阶段发展到生产就绪阶段。ML Kit 为我们提供了更高级别的 API,以便我们可以处理模型的初始化和加载,以及在图像上运行推理,而无需直接处理更低级别的 TensorFlow Lite C++ 库,从而使开发过程更快,并让我们有更多时间来完善模型而不是处理其他事情。

VSCO 机器学习堆栈概述

对于我们的机器学习堆栈,我们使用 TensorFlow 对图像进行深度学习,使用 Apache Spark 对行为数据进行浅层学习。

在生产环境中,我们拥有一个基于云的实时推理管道,该管道使用 TensorFlow Serving 实时运行上传到 VSCO 的每张图像,并通过各种卷积神经网络进行处理。然后,这些模型的推理结果用于产品功能,例如相关图像、搜索、仅为您推荐以及发现中的其他部分等。对于设备上的机器学习,我们使用 TensorFlow 生态系统中适合移动端的组件——ML Kit 和 TensorFlow Lite,我们将在下一部分详细介绍堆栈的这部分。
Related Images
相关图像
Discover page
仅为您推荐
search page
搜索
用户建议
我们还有一个基于 Spark 的推荐引擎,它允许我们在来自数据存储中不同来源的大型数据集上训练模型:图像元数据、行为事件和关系数据。然后,我们使用这些模型的结果来提供各种形式的个性化推荐,例如用户建议。

为了为我们的机器学习管道中的其他部分提供支持,我们使用 Elasticsearch 用于搜索和相关性功能,使用 Apache Kafka 用于基于日志的分布式数据流式传输,这些数据充当所有机器学习模型的输入,使用 Kubernetes 用于部署所有微服务。我们使用的语言包括 Python、C++、Go、Scala,以及用于设备上集成的 Java/Kotlin 和 Swift/Obj-C。

设备上的机器学习:“适合此照片”功能的工作原理

步骤一:对图像进行分类

为了构建一个为“适合此照片”功能提供服务的模型,我们首先需要能够为图像分配一个类别,然后建议针对该类别设计的预设。下图描述了对图像进行分类的过程。
Diagram showing process to categorize images
对图像进行分类
我们从由内部人工审核员标记的图像数据开始。这些审核员是摄影专家,他们能够近距离观察用户的行为,因此他们比任何人都更了解正在分享的类型的内容以及正在发展的趋势。他们帮助我们的工程团队为我们的模型确定图像类别,其中包括艺术、肖像、充满活力、沿海、自然、建筑、光影、单色以及更多。俗话说,机器学习 90% 的工作都是清理数据。这些步骤帮助确保了我们的模型所基于的数据是可靠的。

使用我们与人工审核员一起创建的分类数据集,我们基于 SqueezeNet 架构在 TensorFlow 中训练了一个 CNN 模型。我们选择这个架构是因为它体积更小,同时精度损失不大。我们使用 TFLiteConverter 将这个训练过的模型从 TensorFlow 的 Saved Model 格式转换为 TensorFlow Lite(.tflite)格式,以便在 Android 上使用。此阶段初始错误的部分原因是,我们使用的 TFLiteConverter 版本与 ML Kit 通过 Maven 引用到的 TensorFlow Lite 库版本不匹配。ML Kit 团队在解决这些问题方面提供了很大的帮助。
graph_def_file = “model_name.pb”
input_arrays = [“input_tensor_name”] # this array can have more than one input name if the model requires multiple inputs
output_arrays = [“output_tensor_name”] # this array can have more than one input name if the model has multiple outputs


converter = lite.TFLiteConverter.from_frozen_graph(
  graph_def_file, input_arrays, output_arrays)
tflite_model = converter.convert()
open("converted_model.tflite", "wb").write(tflite_model)
在能够为图像分配类别的模型完成后,我们就可以将其打包到我们的应用程序中,并使用 ML Kit 对图像进行推理。由于我们使用的是自己的自定义训练模型,因此我们使用了 ML Kit 中的 Custom Model API。为了获得更高的精度,我们决定在模型转换过程中放弃量化步骤,并决定在 ML Kit 中使用浮点模型。这里遇到了一些挑战,因为 ML Kit 默认情况下会假设使用量化模型。但是,我们没有花费太多精力就能够更改模型初始化中的某些步骤以支持浮点模型。
// create a model interpreter for local model (bundled with app)
FirebaseModelOptions modelOptions = new FirebaseModelOptions.Builder()
    .setLocalModelName(“model_name”)
    .build();
modelInterpreter = FirebaseModelInterpreter.getInstance(modelOptions);

// specify input output details for the model
// SqueezeNet architecture uses 227 x 227 image as input
modelInputOutputOptions = new FirebaseModelInputOutputOptions.Builder()
    .setInputFormat(0, FirebaseModelDataType.FLOAT32, new int[]{1, 227, 227, 3})
    .setOutputFormat(0, FirebaseModelDataType.FLOAT32, new int[]{1, numLabels})
    .build();

// create input data
FirebaseModelInputs input = new FirebaseModelInputs.Builder().add(imgDataArray).build(); // imgDataArray is a float[][][][] array of (1, 227, 227, 3)

// run inference
modelInterpreter.run(input, modelInputOutputOptions);

步骤二:建议预设

下一个挑战是如何根据这些图像类别来建议预设。我们与内部图像团队合作,由他们创建了这些预设,他们帮助我们确定了适用于每种图像类别的预设列表。这个过程包括对每种类别的许多图像进行严格的测试,我们分析了每个预设如何影响各种颜色。最终,我们拥有一个精选的目录,其中预设与每个类别相关联。
为图像建议预设
我们不断更新这些精选目录,因为我们会在会员服务中添加新的预设。为了方便我们在用户无需更新应用的情况下动态更新这些列表,我们决定将这些目录存储在服务器上,并使用 API(一个用 Go 编写的微服务)提供服务,移动客户端可以定期向 API 查询以确保他们拥有最新版本的目录。移动客户端会缓存这个目录,只有在有新版本可用时才会获取。然而,这种方法会导致“冷启动”问题,对于第一次使用此功能且尚未连接互联网的用户来说,应用程序无法与 API 通信并下载这些目录。为了解决这个问题,我们决定在应用程序中附带这些目录的默认版本。这使得所有用户无论其互联网连接状况如何都可以使用此功能,这也是该功能最初的目标之一。

结果与结论

通过“为这张照片”,我们实现了用预设进行编辑更容易导航的目标。我们相信,如果我们的会员没有从获得新的预设中获得价值,那么他们的创作进度就会受到阻碍。我们希望做得更好。我们想帮助更多用户不仅仅是发现新的预设,而是找到最适合他们正在处理的内容的预设。

我们希望继续改进“为这张照片”,提供基于其他图像特征以及用户社区行为(例如关注、收藏和转发)的推荐。此外,我们还想为这些推荐提供更多背景信息,并鼓励我们的创作者社区互相发现和启发。

展望这个功能的未来,我们也反思了过去。我们认识到,如果没有 TensorFlow Lite 和 ML Kit,这项功能和 VSCO 的设备上 ML 功能是不可能实现的。我们很高兴继续投资于这一领域,并在未来构建更多利用这项技术的特性。
下一篇文章
Suggesting Presets for Images: Building: “For This Photo” at VSCO

由 VSCO 工程团队发表的客座文章

VSCO,我们打造以自我表达为驱动的创意工具、空间和连接。我们的应用程序是一个让人们可以创作和编辑图像和视频、发现技巧和新创意以及与充满活力的全球社区连接的地方,这个社区没有公开点赞、评论和粉丝数量。

我们使用机器学习作为个性化和引导每个…