那里有很多关于人工智能的教程。诸如如何进行对象检测、图像分类、NLP和构建聊天机器人之类的事情,这个列表还在继续。但是当我查找如何正确扩展AI时,几乎没有。更令人惊讶的是,现有的极少数资源反复强调同一点:使用TensorFlow这样的可扩展框架构建模型。将其打包到客户端(TF.js、TFLite、TF-slim等)或部署为基于容器的微服务。我对第二点更感兴趣,因为我已经开发了一个模型,但令我惊讶的是,没有任何关于如何实现第二点的细节,更没有关于每个解决方案的缺点的信息。在Crane.ai上研究和扩展AI几天后,我整理了一些关于如何部署这些方法、它们的缺点以及如何在低级别优化TensorFlow模型的内容。将模型打包给客户——这种方法很糟糕!最常见的方法之一是使用TensorFlow.js、TFLite或TensorFlowSlim等工具将AI打包到您选择的客户端中。我不会详细介绍这些框架的工作原理,但会强调它们的缺点。计算能力。部署这些模型的问题是它们需要大量内存(我指的是移动应用程序或浏览器限制,即>1-2GBRAM)。很多手机没有那种计算能力,桌面浏览器延迟UI线程,拖慢用户电脑,打开浏览器,打开风扇等推断时间。当您在计算能力未知的设备上运行模型时,推理时间通常也是未知的;这些设备不是GPU驱动的高RAM、高CPU机器,它们只是运行在普通计算机上的手机、浏览器和桌面应用程序。较大模型的推理时间很容易超过一分钟,从用户体验的角度来看这是不可行的。大文件。不幸的是,大多数模型都存储在相当大的文件中(我的意思是数十、数百MB)。因此,加载这些文件很慢,需要相对较大的内存,并且会显着增加应用程序包的大小。不安全。除非您使用的是开源模型,否则您应该将您的AI模型和预训练检查点保密。然而,当您将模型打包到应用程序中时,不仅您的推理代码容易被反编译,而且应用程序包内的预训练检查点也容易被窃取。更新困难。如果你想更新你的模型,你在客户端有两个选项。要么通过集中管理器(即PlayStore、AppStore等)发布更新,这会导致频繁的大更新(这对用户来说很烦人,并且可能会被用户中断,具体取决于设置,或者根本不更新)。或者应用程序本身可以获取新模型的检查点和元数据。后者听起来好多了,但这意味着你可能会在不稳定的用户连接上下载超过100MB的文件,这可能需要一段时间,所以你的应用程序至少应该在后台运行直到下载过程完成,并且会有很高的互联网输出成本(取决于您的云计算)。缺乏可训练性。根据新用户的数据训练的模型提供了一定程度的个性化,同时提高了准确性并构建了核心高信号数据集。不幸的是,大多数设备都缺乏训练模型的计算能力,即使它们有足够的计算能力,也无法将训练结果传递给服务器或其他运行应用程序的设备。这些缺点使得在客户端部署和维护大型神经网络几乎是不可能的,因此我们将此项目排除在扩展模型的候选对象之外。部署为云点图源:https://xkcd.com/908/云是一个强大的工具,可以大规模部署模型。您可以根据需要自定义环境、将应用程序容器化并立即横向扩展应用程序,同时提供与大型公司相当的SLA和正常运行时间。大多数TensorFlow模型的部署过程都是相同的:将图像强化为Protobuf二进制文件调整推理代码,使其能够处理强化后的图形容器化应用程序在顶部添加API层第一部分相对简单。“修复”一个图会创建一个包含所有命名节点、权重、模式和检查点元数据的protobuf二进制文件。这一步可以用多种工具来实现,最常用的是TF自带的工具,它可以固化任何给定输出节点名的图。有关此技术及其实现的更多信息,请参阅:https://www.tensorflow.org/guide/extend/model_files#freezing。调整推理代码也不难。在大多数情况下,feed_dict没有变化,主要区别在于添加了加载模型的代码,也许还有输出节点的规范。容器化也很简单——只需在Dockerfile中设置环境即可。当我们开始添加API层时,事情变得一团糟。通常使用两种方法:部署可以运行推理脚本的扩展容器。这些容器在启动会话的输入上运行脚本,执行推理,并通过管道返回输出。这是相当有问题的:对于大多数云提供商来说,添加一个可以操纵容器和管道进出的API层并不容易(例如,AWS有一个API网关,但它并不像你想象的那么方便),并且这种方法是您可以采用的效率最低的方式。这里的问题是您在启动容器、分配硬件、启动会话和推断时浪费了宝贵的时间。如果你让stdin打开并保持管道输出,你的脚本会加速但失去可伸缩性(现在你连接到容器的STDIN,它不能接受多个请求)。部署一个运行API层的扩展容器。尽管在体系结构上相似,但出于多种原因,这种方法更有效。将API层内置到容器中可以缓解之前提出的大部分问题。虽然这需要更多资源,但它已经使用了最少的资源并且不能垂直扩展;它允许每个容器保持运行,并且由于在这种情况下API是分散的,因此可以将特定的标准输入/stout连接到请求路由器上的main。这意味着消除了启动时间,可以保持速度并且可以在服务多个请求时保证水平缩放。您可以使用负载均衡器集中容器并使用Kubernetes来保证接近100%的正常运行时间并管理集群。这种方法简单有效。部署集群!将API分散在容器集群上的主要缺点是计算成本会相对快速地增加。不幸的是,这在AI中是不可避免的,但有一些方法可以缓解这种情况。重用会话。集群根据负载按比例增长和收缩,因此您的目标是最大限度地减少执行推理所需的时间,释放容器以处理额外的请求。实现这个想法的方法是将tf.Session和tf.Graph初始化后存储起来,作为全局变量传递,达到复用tf.Session和tf.Graph的目的。对于TF,此举可以通过减少启动会话和构建图形的时间来大大提高推理任务的速度。这种方法甚至适用于单个容器,并且这种技术被广泛用于最小化资源重新分配和最大化效率。缓存输入,如果可能,也缓存输出。在人工智能中,动态规划范式是人工智能中最重要的。通过缓存输入,您可以节省预处理输入或远程获取输入的时间;通过缓存输出,您可以节省运行推理的时间。这在Python中很容易做到,但这取决于您。通常,你的模型会随着时间的推移变得更好,但这会极大地影响你的输出缓存机制。我自己的系统使用我所说的“80-20”规则。当模型的准确率低于80%时,我不会缓存任何输出;一旦准确率达到80%,我就开始缓存,设置为当准确率达到一定值(不是某个时间点)时停止缓存。这样,随着模型变得更准确,输出会发生变化,但在“80-20”缓存中,性能和速度之间的权衡较少。使用任务队列。通常需要运行或多或少的推理任务(在我们的例子中,更大和更小、复杂和简单的图像)。对于UX,使用堆队列可能会更好,它优先考虑较小的任务,因此想要运行一个简单步骤的用户可以等待该步骤完成,而不是等待另一个用户的较大推理任务先完成.(也许你会想为什么我这里不水平缩放,你可以这样做但是会增加计算成本)。使用任务队列在专用GPU上训练模型。训练是一项漫长而艰巨的任务,需要大量可用资源,并且模型在训练期间不可用。如果您将每次交互反馈回模型进行训练,请考虑在单独的服务器或GPU上运行它。训练完成后,您可以将模型(在AWS中,您可以将模型存储库集中在S3中)部署到容器中。结论经过深思熟虑,我们提出了大规模部署AI的高效工作流程:固化图并在API下封装推理重用会话和图,缓存输入和输出应用程序使用Kubernetes部署在您选择的云上将训练与推理分开构建任务队列优先处理较小的任务使用这些技术,您可以以最低的成本实现最高的速度和效率大规模部署AI。原文链接:https://towardsdatascience.com/scaling-ai-2be294368504【本文为《机器之心》专栏原文翻译,微信公众号“机器之心(id:almosthuman2014)”】点击在这里可以看到作者更多的好文章
