读懂本文让你和深度学习模型“官宣”

点击上方关注,All in AI中国

作者——Isaac Godfried

近年来,学术界和工业界的研究人员在深度学习领域进行了许多令人兴奋和开创性的研究。他们开发了许多强大得令人难以置信的新模型。然而,这些研究成果的大部分(少数科技巨头除外)仍然只是停留在实验阶段,而不是成为生产应用程序的一部分。尽管新的发现/发明源源不断,但在生产过程中,使用这些模型仍然非常困难(甚至做相关方面的论文也是如此),部署机器学习模型仍然是一个重大挑战。在本文中,我将概述各种"生产型"机器学习模型的方法,并权衡它们各自的优缺点,但不会涉及太多细节。并将用具体的代码和示例来探讨这些方法。

读懂本文让你和深度学习模型“官宣”

挑战

我们假设我们已经训练了模型,或者已经从互联网上获得了训练过的权重。为了在应用程序中使用你的模型,你必须:

  1. 加载模型的权重
  2. 对数据进行预处理
  3. 进行测试
  4. 处理预测响应数据

听起来很简单吗?实际上,这个过程非常复杂。

与许多事情一样,对于将机器学习模型部署到生产环境的最佳方式,没有一个明确的答案。你应该问自己的问题是:

  1. 我的要求是什么?(例如,你希望每秒有多少请求,需要的延迟是什么…等等)
  2. 我将如何评估模型在生产中的性能?(我将如何收集和存储来自交互的额外数据)
  3. 我打算多久训练一次我的模型?
  4. 数据预处理需要什么?
  5. 生产输入数据的格式是否与模型训练数据有很大的差异?它是分批生产还是成批生产?
  6. 模型需要离线运行吗?

这些都是在部署模型之前必须想到的基本问题。

直接将模型加载到应用程序中

这个选项本质上认为模型是整个应用程序的一部分,因此才在应用程序中加载它。这种方法在某些情况下比其他方法更容易上手。

例如,如果核心应用程序本身是用Python编写的,那么这个过程(即,将模型加载到应用程序中)会很顺利。总的来说,它通常需要向设置/配置文件添加依赖项,并修改你的预测值函数,以便通过用户的交互调用。总的来说,模型作为应用程序的一部分加载,所有依赖项都必须包含在应用程序中。

如果应用程序不是用Python编写的,这个过程就会变得更加困难。例如,我现在还没有将PyTorch或Caffe加载到Java程序中的好方法。即使是拥有Java库的Tensorflow也需要编写大量附加代码才能完全集成到应用程序中。而且,这并没有明确地解决可伸缩性问题。

然而,正如前面所述,当你希望快速部署用Python编写的应用程序时,这种方法仍然是有益的。因为对于没有互联网连接的设备来说,它仍然是更好的选择之一。

调用API

第二个选择涉及创建API并从应用程序调用API。这可以通过许多不同的方式来实现。我在这里详细介绍了最常见的方法。

Kubernetes

Docker在许多方面似乎是部署机器学习模型最好的选择。模型及其所有依赖项可以整齐地打包在一个容器中。此外,服务器可以通过在需要时添加更多Docker容器来自动扩展。Kubernetes是管理Docker容器的最佳方法之一。

最近,Kubernetes推出了旨在将机器学习引入Kubernetes框架的Kubeflow。Kubeflow试图使训练、测试和部署模型以及收集评估指标变得容易(https://techcrunch.com/2018/05/04/google-kubeflow-machine-learning-for-kubernetes-begins-to-take-shape/)。由于它可能相当复杂,我们现在只需要了解,它是一个全面的包,旨在使大规模开发和部署机器学习微服务变得容易。

使用Flask/Django定制REST-API

另一个选择是从头创建你自己的REST-API(这个选项也可以与Docker结合使用),这取决于你对API的熟悉程度(https://blog.hyperiondev.com/index.php/2018/02/01/deploy-machine-learning-model-flask-api/)。这可以用Flask比较容易做到。我将不再详细介绍如何用它进行此工作,因为有许多现有的教程正好涵盖了这个主题。根据请求的数量,你通常可以通过调整Flask以做出改变。

然而,由于ML框架、加载时间和特定模型预处理需求的不同,我们的工作往往会变得一团糟。目前,我正在开发一个与模型无关的实例化类,以便与Flask/Django一起使用,目的是容易地使用模型,并为以后的模型提供一个标准化的模板(https://github.com/isaacmg/model_agnostic_prediction)。因此,不必为不同的模型记住和实现不同的函数,你可以调用model.preprocess()和model.predict(),而不管后端是什么。

AWS Lambda/ Serverless

AWS Lambda是另一种可能的选择。你可以阅读AWS关于如何设置此功能的文章(https://aws.amazon.com/blogs/machine-learning/how-to-deploy-deep-learning-models-with-aws-lambda-and-tensorflow/)。在"机器学习"中有一篇在Caffe2中使用AWS lambda的好文章(https://machinelearnings.co/serving-pytorch-models-on-aws-lambda-with-caffe2-onnx-7b096806cfac)。

其他方法

Apache Beam -我对这种方法不太了解,但这种方法似乎涉及到使用Beam进行模型预处理,然后使用Tensorflow(目前只支持框架)进行实际预测。有关更多信息,请点击链接:https://qcon.ai/system/files/presentation-slides/simplifying_ml_workflows_with_apache_beam.pdf 。

Spark/ Flink

Spark和Flink等几个主要的数据处理框架正在开发帮助部署机器学习模型的包(https://github.com/FlinkML/flink-tensorflow)(https://github.com/databricks/spark-deep-learning)。此外,雅虎最近发布了自己的库,允许分布式训练并提供相关服务(https://github.com/yahoo/TensorFlowOnSpark)。

读懂本文让你和深度学习模型“官宣”

有段时间,由于大量的工作,我陷入了困境,在此期间Oracle出现了一个名为GraphPipe的新模型部署框架。那时候我本来打算选用Kubeflow,因为它有很多不错的特性,但是我发现GraphPipe更容易使用,至少从它的网站上看,它比JSON类型的api(比如Kubeflow)快得多(https://blogs.oracle.com/developers/introducing-graphpipe)。

所以我决定在这里介绍一个使用GraphPipe和与我自己的模型无关(MA)的库(现在它包含了对GraphPipe的支持)部署经过训练的PyTorch模型的示例。在这个例子中,我选择了ChexNet和arroweng的实现,后者在GitHub上公开使用(https://github.com/arnoweng/CheXNet)。

1.重构代码以支持"单一示例"处理(或生产所需的任何模式)。

在几乎所有情况下,第一步都是相同的。它通常也是最耗时和最辛苦的。这通常需要非常仔细地阅读实现代码。我的model_agnostic类的目的是让这个过程稍微简单一些(不幸的是,它仍然很糟糕)。

1(a)加载模型权重

在我们尝试重构之前,我们需要确保我们可以加载模型权重(令人惊讶的是,这导致了比你想象中更多的问题)。为此,我们从MA中子类化PytorchModel。现在我们不需要担心预处理和process_result步骤。相反,我们将只关注于加载模型权重。

读懂本文让你和深度学习模型“官宣”

读懂本文让你和深度学习模型“官宣”

MA通常有三种方法来实现,分别是__init__(通常只是对super的调用)、create_model和预处理。根据你的模型和设置,你可能还希望实现process_result。

装载PyTorch模型有多种方式,MA支持两种方式。第一种方法是,你将模型保存到torch.save(the_model, some_path)。在实践中,这种情况非常少见,相反,大多数时候你只保存state_dict,而不是将整个模型保存到torch.save(the_model.state_dict(),path))中。在这里arroweng提供了保存的state_dict,所以我们必须调用create_model。而ChexNet实际上只是一种DenseNet121。因此,我们在create_model中需要做的就是返回一个DenseNet121(https://github.com/isaacmg/s2i_pytorch_chex/blob/master/densenet/dense_ne.py)。PytorchModel中的rest MA处理方法就好似将state_dict转换为一个可用的CPU格式(反之亦然)。最后,我们将训练模式设置为false,因为我们并不打算继续下去。

读懂本文让你和深度学习模型“官宣”

1(b)实现预处理和process_result

为了测试这个设置是否有效,我们需要在一些数据上运行创建的模型。这些相同的函数也将在稍后部署模型时使用。所以现在是时候让他们尽可能"瘦身"了。这通常是比较耗时的步骤之一,因为代码经常分批评估模型。此外,当我们需要使用单独的示例来运行模型时,模型通常有一个数据加载器。此过程将根据你的特定模型或用例而有所不同。有趣的是,对于这个特殊的模型,arroweng甚至在测试期间使用TenCrop来增加数据。因此,在我的预处理方法中,我决定有一个增强参数,用户可以标记为true或not。

读懂本文让你和深度学习模型“官宣”

读懂本文让你和深度学习模型“官宣”

读懂本文让你和深度学习模型“官宣”

代码为预处理部分的算法。GitHub:https://gist.github.com/isaacmg/767f069e8d25a22dbf2d88abda288ad3/

读懂本文让你和深度学习模型“官宣”

2.将PyTorch转换为Caffe2

这个部分相对简单,并且在PyTorch网站上有详细的文档说明。基本上,它包括Caffe2追踪模型的PyTorch执行情况。

读懂本文让你和深度学习模型“官宣”

这是我直接从PyTorch网站的官方教程中获得的代码(https://pytorch.org/tutorials/advanced/super_resolution_with_caffe2.html)。

为了测试模型是否被成功地转换为Caffe2,我使用了以下代码。

读懂本文让你和深度学习模型“官宣”

你可以使用NumPY测试框架来确保PyTorch执行的结果大致等于Caffe2执行的结果(无论需要多少小数位数)。

如果一切正常,下面的代码运行时应该没有错误。

3.服务与GraphPipe

3(a)运行GraphPipe docker容器

现在,将保存的Caffe ONNX文件(在本例中是chexnet-py.onnx)保存到云中或本地目录中。然后运行以下命令。

读懂本文让你和深度学习模型“官宣”

这个命令的作用是将GraphPipe拖放到nx CPU docker、模型和其他信息中,以运行容器。"Value inputs"是输入值的维度。

读懂本文让你和深度学习模型“官宣”

因此,在本例中(如果计划不使用裁剪增强),输入将是批处理大小1、三个通道(即RGB)和224x224图像大小。如果一切顺利,你应该在终端中使用"INFO[0005] Listening on 0.0.0.0:9000"之类的内容。

3(b)定义GraphPipe服务类

读懂本文让你和深度学习模型“官宣”

读懂本文让你和深度学习模型“官宣”

读懂本文让你和深度学习模型“官宣”

我在这里出了些小问题,你可以点击链接查看完整视图(https://gist.github.com/isaacmg/554be1338025bc4529d2bb7cffda9d49)。

因此,预处理函数将保持不变,如果还没有定义process_result函数,则需要定义process_result函数。MA将处理GraphPipe docker容器的所有幕后调用。现在要使用它,你所需要的是以下内容。

读懂本文让你和深度学习模型“官宣”

要创建ChexNet GraphPipe对象,只需传入容器的url。然后简单地运行标准模型无关命令。

现在你可以在任何Django或Flask API中来完成部署。

结语

现在,你可能会想,为什么这比简单地在Flask/Django REST API中运行PyTorch更好呢?答案是(1)这种方法通常更快,因为GraphPipe优化了模型预测阶段,(2)该方法具有高度的可伸缩性,(3)任何应用程序都可以通过任何语言调用此API(假设你可以执行等效的预处理)。在我看来,最大的好处是(2)。

我的成品是GitHub:https://github.com/isaacmg/ml_serving_flask。

更多资源

graphpipe官方网址:https://oracle.github.io/graphpipe/#/

graphpipe发布的文章:https://blogs.oracle.com/developers/introducing-graphpipe

散乱模型加载代码:https://github.com/isaacmg/s2i_pytorch_chex

读懂本文让你和深度学习模型“官宣”

相关推荐