案例研究:亚马逊广告使用 PyTorch 和 Amazon Inferentia 扩展广告处理模型
作者:Yashal Kanungo – 应用科学家,Kamran Khan – 高级技术产品经理,Shubha Kumbadakone – ML 框架高级专家
亚马逊广告使用 PyTorch、TorchServe 和 Amazon Inferentia 将推理成本降低 71% 的同时推动横向扩展。
亚马逊广告(Amazon Ads)通过在亚马逊商店内外展示的广告(包括超过 15 个国家/地区的网站、应用和流媒体电视内容)帮助公司建立品牌并与购物者建立联系。 各种规模的企业和品牌,包括注册卖家、供应商、图书供应商、Kindle Direct Publishing (KDP) 作者、应用程序开发人员和代理机构都可以上传自己的广告创意,其中可以包括图像、视频、音频,当然,还有在亚马逊上销售的商品。
为了推广准确、安全和愉悦的购物体验,这些广告必须遵守内容准则。 例如,广告闪烁打开或关闭,产品必须在适当的上下文中展示,图片和文字应适合普通受众。为了帮助确保广告符合所需的政策和标准,我们需要开发可扩展的机制和工具。
作为解决方案,我们使用机器学习 (ML) 模型来展示可能需要修改的广告。随着深度神经网络在过去十年中蓬勃发展,我们的数据科学团队开始探索更通用的深度学习 (DL) 方法,这些方法能够以最少的人工干预处理文本、图像、音频或视频。为此,我们使用PyTorch构建了计算机视觉(CV)和自然语言处理(NLP)模型,这些模型会自动标记可能不合规的广告。PyTorch 直观,灵活且用户友好,并且已经使我们无缝过渡到使用 DL 模型。在基于 Amazon Inferentia 的 Amazon EC2 Inf1 实例而不是基于 GPU 的实例上部署这些新模型,对于相同的工作负载,我们的推理延迟降低了 30%,推理成本降低了 71%。
向深度学习过渡
我们的机器学习系统将经典模型与词嵌入配对以评估广告文本。 但是我们的需求在不断变化,随着提交量的不断扩大,我们需要一种足够灵活的方法来随着我们的业务扩展。此外,我们的模型必须快速并在几毫秒内投放广告,以提供最佳的客户体验。
在过去的十年中,深度学习在许多领域都非常流行,包括自然语言、视觉和音频。由于深度神经网络通过多层传输数据集——逐步提取更高层次的特征——它们可以比经典的ML模型做出更细微的推断。例如,深度学习模型可以拒绝做出虚假声明的广告,而不是简单地检测被禁止的语言。
此外,深度学习技术是可转移的——为一项任务训练的模型可以适应执行相关任务。例如,可以优化预训练的神经网络以检测图像中的对象,然后进行微调以识别不允许在广告中显示的特定对象。
深度神经网络可以自动执行经典ML最耗时的两个步骤:特征工程和数据标记与需要探索性数据分析和手工设计特征的传统监督学习方法不同,深度神经网络直接从数据中学习相关特征。DL 模型还可以分析非结构化数据,例如文本和图像,而无需 ML 中所需的预处理。深度神经网络可以有效地扩展更多数据,并且在涉及大型数据集的应用程序中表现得特别好。
我们选择了 PyTorch 来开发我们的模型,因为它帮助我们最大限度地提高了系统的性能。借助于PyTorch,我们可以更好地为客户服务,同时利用 Python 最直观的概念。PyTorch 中的编程是面向对象的:它将处理函数与它们修改的数据组合在一起。因此,我们的代码库是模块化的,我们可以在不同的应用程序中重用代码片段。此外,PyTorch 的 Eager 模式允许循环和控制结构,因此模型中的操作更复杂。Eager 模式可以很容易地对我们的模型进行原型设计和迭代,并且我们可以使用各种数据结构。这种灵活性有助于我们快速更新模型以满足不断变化的业务需求。
“在此之前,我们尝试了其他‘Pythonic’框架,但 PyTorch 在我们这里显然是赢家。” 应用科学家 Yashal Kanungo 谈到。 “使用 PyTorch 很容易,因为这种结构感觉是 Python 编程的原生结构,而数据科学家对此非常熟悉”。
训练管道
今天,我们完全在 PyTorch 中构建我们的文本模型。为了节省时间和金钱,我们经常通过微调预训练的 NLP 模型来跳过训练的早期阶段以进行语言分析。 如果我们需要一个新模型来评估图像或视频,我们首先浏览 PyTorch 的 torchvision 库,该库为图像和视频分类、对象检测、实例分割和姿势估计提供了预训练选项。对于专门的任务,我们从头开始构建自定义模型。PyTorch 非常适合这一点,因为Eager模式和用户友好的前端使尝试不同的架构变得容易。
要了解如何在 PyTorch 中微调神经网络,请参阅本教程。
在开始训练之前,我们会优化模型的超参数、定义网络架构的变量(例如隐藏层的数量)和训练机制(例如学习率和批量大小)。选择适当的超参数值至关重要,因为它们将塑造模型的训练行为。在这一步中,我们依赖亚马逊云科技的 ML 平台 SageMaker 中的贝叶斯搜索功能。 贝叶斯搜索将超参数调整视为回归问题:它提出可能产生最佳结果的超参数组合并运行训练作业来测试这些值。每次试验后,回归算法会确定下一组要测试的超参数值,并且性能会逐步提高。
我们使用 SageMaker Notebooks 对我们的模型进行原型设计和迭代。Eager 模式让我们通过为每个训练批次构建一个新的计算图来快速原型模型;操作的顺序可以在迭代之间改变,以适应不同的数据结构或与中间结果相结合。这使我们可以在训练期间调整网络,而无需从头开始。这些动态图对于基于可变序列长度的递归计算特别有价值,例如使用 NLP 分析的广告中的单词、句子和段落。
当我们完成模型架构后,我们会在 SageMaker 上部署训练作业。 PyTorch 通过同时运行大量训练作业帮助我们更快地开发大型模型。 PyTorch 的分布式数据并行 (DDP) 模块在 SageMaker 内的多台互连机器上复制单个模型,并且所有进程在数据集自己独特的部分上同时向前运行。在反向传播过程中,模块对所有过程的梯度进行平均,因此每个局部模型都使用相同的参数值进行更新。
模型部署管道
当我们在生产中部署模型时,我们希望确保在不影响预测准确性的情况下降低推理成本。PyTorch的多项功能和亚马逊云科技服务帮助我们应对了这一挑战。
动态图形的灵活性丰富了训练,但在部署中,我们希望最大限度地提高性能和可移植性。在 PyTorch 中开发 NLP 模型的其中一个优点是开箱即用,它们可以被 TorchScript 追踪到静态操作序列中,TorchScript 是专门用于ML应用程序的 Python 子集。Torchscript 将 PyTorch 模型转换为更高效、更易于编译的生产友好型中间表示 (IR) 图。我们通过模型运行一个示例输入,TorchScript 记录在前向传递期间执行的操作。生成的 IR 图可以在高性能环境中运行,包括 C++和其他无 Python 的多线程上下文,并且诸如运算符融合之类的优化可以加快运行时。
Neuron SDK 和 Amazon Inferentia 驱动的计算
我们将模型部署在由 Amazon Inferentia 提供支持的 Amazon EC2 Inf1 实例上,这是 Amazon 的第一个 ML 芯片,旨在加速深度学习推理工作负载。与基于 Amazon EC2 GPU 的实例相比,Inferentia 已证明可将推理成本降低多达 70%。我们使用 Amazon Neuron SDK(一组与Inferentia一起使用的软件工具)来编译和优化我们的模型,以便在 EC2 Inf1 实例上进行部署。
以下的代码片段展示了如何使用 Neuron 编译 Hugging Face BERT 模型。与 torch.jit.trace() 一样,neuron.trace() 在前向传递期间记录模型对示例输入的操作,以构建静态 IR 图。
import torch from transformers import BertModel, BertTokenizer import torch.neuron tokenizer = BertTokenizer.from_pretrained("path to saved vocab") model = BertModel.from_pretrained("path to the saved model", returned_dict=False) inputs = tokenizer ("sample input", return_tensor="pt") neuron_model = torch.neuron.trace(model, example_inputs = (inputs['input_ids'], inputs['attention_mask']), verbose = 1) output = neuron_model(*(inputs['input_ids'], inputs['attention_mask']))
自动转换和重新校准
在底层,Neuron 通过将模型自动转换为更小的数据类型来优化模型的性能。 默认情况下,大多数应用程序以 32 位单精度浮点 (FP32) 数字格式表示神经网络值。将模型自动转换为 16 位格式——半精度浮点 (FP16) 或大脑浮点 (BF16)——减少了模型的内存占用和执行时间。在我们的案例中,我们决定使用 FP16来优化性能,同时保持高精度。
在某些情况下,自动转换为较小的数据类型会触发模型预测的细微差异。 为了确保模型的准确性不受影响,Neuron 比较了 FP16 和 FP32 模型的性能指标和预测。当自动转换降低模型的准确性时,我们可以告诉神经元编译器仅将权重和某些数据输入转换为 FP16,将其余中间结果保留在 FP32 中。此外,我们经常对训练数据进行几次迭代,以重新校准我们的自动铸造模型。这个过程比原来的训练要少得多。
部署
为了分析多媒体广告,我们运行了一组 DL 模型。上传到亚马逊的所有广告都通过专门的模型运行,这些模型评估它们包括的每种类型的内容:图像、视频和音频、标题、文本、背景,甚至语构、语法和可能不恰当的语言。 我们从这些模型收到的信号表明广告是否符合我们的标准。
部署和监控多个模型异常复杂,因此我们依赖于SageMaker的默认 PyTorch 模型服务库TorchServe。TorchServe 由 Facebook的 PyTorch 团队和亚马逊云科技联合开发,旨在简化从原型设计到生产的过渡,帮助我们大规模部署训练有素的 PyTorch 模型,而无需编写自定义代码。它为推理、管理、度量和解释提供了一组安全的 REST API。TorchServe 具有多模型服务、模型版本控制、集成支持和自动批处理等功能,非常适合支持我们巨大的工作量。您可以在这篇博文中阅读更多关于在 SageMaker 上部署 Pytorch 模型并集成原生 TorchServe 的信息。
在某些用例中,我们利用 PyTorch 的面向对象编程范式将多个 DL 模型包装到一个父对象(PyTorch nn.Module)中,并将它们作为一个整体服务。在其他情况下,我们使用 TorchServe 在单独的 SageMaker 终端节点上为单独的模型提供服务,这些终端节点在 Amazon Inf1 实例上运行。
自定义处理程序
我们特别赞赏 TorchServe 允许我们将模型初始化,预处理,推理和后处理代码嵌入到服务器上的单个 Python 脚本 handler.py 中。此脚本(处理程序)预处理广告中未标记的数据,通过我们的模型运行该数据,并将结果推断提供给下游系统。TorchServe提供了多个默认处理程序,用于加载权重和体系结构,并准备模型以在特定设备上运行。我们可以将所有需要的附加工件(如词汇表文件或标签映射)与模型捆绑在一个存档文件中。
当我们需要部署具有复杂初始化过程或源自第三方库的模型时,我们会在 TorchServe中设计自定义处理程序。这让我们可以使用任何所需的过程从任何库加载任何模型。以下代码段显示了一个简单的处理程序,它可以在任何 SageMaker托管端点实例上为Hugging Face BERT模型提供服务。
import torchimport torch.neuronfrom ts.torch_handler.base_handler import BaseHandlerimport transformersfrom transformers import AutoModelForSequenceClassification,AutoTokenizer class MyModelHandler(BaseHandler):def initialize(self, context):self.manifest = ctx.manifestproperties = ctx.system_propertiesmodel_dir = properties.get("model_dir")serialized_file = self.manifest["model"]["serializedFile"]model_pt_path = os.path.join(model_dir, serialized_file) self.tokenizer = AutoTokenizer.from_pretrained( model_dir, do_lower_case=True ) self.model = AutoModelForSequenceClassification.from_pretrained( model_dir ) def preprocess(self, data): input_text = data.get("data") if input_text is None: input_text = data.get("body") inputs = self.tokenizer.encode_plus(input_text, max_length=int(max_length), pad_to_max_length=True, add_special_tokens=True, return_tensors='pt') return inputs def inference(self,inputs): predictions = self.model(**inputs) return predictions def postprocess(self, output): return output
批处理
硬件加速器针对并行性进行了优化,批处理(在一个步骤中为模型提供多个输入)有助于使所有可用容量饱和,通常会导致更高的吞吐量。然而,过高的批量大小会增加延迟,而吞吐量的提升却很小。尝试不同的批量大小有助于我们确定模型和硬件加速器的最佳位置。我们进行实验以确定模型大小、有效负载大小和请求流量模式的最佳批量大小。
Neuron 编译器现在支持可变批量大小。以前,跟踪模型硬编码了预定义的批量大小,因此我们必须填充数据,这会浪费计算、降低吞吐量并加剧延迟。 Inferentia 经过优化,可最大限度地提高小批量的吞吐量,通过减轻系统负载来减少延迟。
批处理
硬件加速器针对并行性进行了优化,批处理(在一个步骤中为模型提供多个输入)有助于使所有可用容量饱和,通常会导致更高的吞吐量。然而,过高的批量大小会增加延迟,而吞吐量的提升却很小。尝试不同的批量大小有助于我们确定模型和硬件加速器的最佳位置。我们进行实验以确定模型大小、有效负载大小和请求流量模式的最佳批量大小。
Neuron 编译器现在支持可变批量大小。以前,跟踪模型硬编码了预定义的批量大小,因此我们必须填充数据,这会浪费计算、降低吞吐量并加剧延迟。 Inferentia 经过优化,可最大限度地提高小批量的吞吐量,通过减轻系统负载来减少延迟。
并行性
多核上的模型并行性还提高了吞吐量和延迟,这对于我们繁重的工作负载至关重要。每个 Inferentia 芯片包含四个 NeuronCore,它们既可以同时运行单独的模型,也可以形成流水线来传输单个模型。在我们的用例中,数据并行配置以最低的成本提供最高的吞吐量,因为它扩展了并发处理请求。
数据并行:
模型并行:
监控
在生产过程中监控推理的准确性至关重要。最初做出良好预测的模型最终会在部署中退化,因为它们暴露在更多种类的数据中。这种现象称为模型漂移,通常发生在输入数据分布或预测目标发生变化时。
We use SageMaker Model Monitor to track parity between the training and production data. Model Monitor notifies us when predictions in production begin to deviate from the training and validation results. Thanks to this early warning, we can restore accuracy — by retraining the model if necessary — before our advertisers are affected. To track performance in real time, Model Monitor also sends us metrics about the quality of predictions, such as accuracy, F-scores, and the distribution of the predicted classes.
我们使用 SageMaker 模型监控器来跟踪训练和生产数据之间的奇偶校验。当生产中的预测开始偏离训练和验证结果时,模型监控器会通知我们。多亏了这种早期预警方式,我们可以在我们的广告商受到影响之前恢复准确性——必要时可以重新训练模型。为了实时跟踪性能,模型监控器还会向我们发送有关预测质量的指标,例如准确度、F分数和预测类别的分布。
为了确定我们的应用程序是否需要扩展,TorchServe 会定期记录 CPU、内存和磁盘的资源利用率指标;它还记录收到的请求数量与服务数量。对于自定义指标,TorchServe 提供了一个 Metrics API。
一种有益的结果
我们的深度学习模型在 PyTorch 中开发并部署在 Inferentia 上,在降低成本的同时加快了广告分析的速度。从我们在 DL 中的第一次探索开始,在 PyTorch 中编程感觉是非常自然的。它的用户友好型功能有助于从我们早期的实验到多模态集成的部署。PyTorch 让我们能够快速制作原型和构建模型,这在我们的广告服务发展和扩展过程中至关重要。为了获得额外的好处,PyTorch 与 Inferentia 和我们的亚马逊云科技机器学习堆栈无缝协作。我们期待使用 PyTorch 构建更多用例,以便我们能够继续为客户提供准确,实时的结果。