用Docker服务机器学习模型:你应该避免的5个错误

450 阅读19分钟

正如你已经知道的那样,Docker是一种工具,它允许你使用容器来创建和部署隔离的环境,以运行你的应用程序及其依赖。在我们进入正题之前,让我们简单地了解一下关于Docker的一些基本概念。

为什么数据科学家要将ML模型容器化?

你是否曾经训练过一个机器学习模型,然后决定与同事分享你的代码,但后来发现你的代码一直在崩溃,尽管它在你的笔记本电脑上工作得很好。大多数情况下,这可能是一个包的兼容性问题或环境问题。解决这个问题的一个好办法就是使用容器

Why should data scientists containerize ML models?

容器提供:

  • 可复制性 -通过容器化你的机器学习模型,你可以将你的代码运送到任何其他安装了Docker的系统中,并期望你的应用程序给你的结果与你在本地测试时的结果相似。

  • 协作开发 - 容器化的机器学习模型允许队友协作,同时这也使得版本控制更加容易。

使用Docker来服务你的机器学习模型

现在你知道了为什么需要将你的机器学习模型容器化,接下来就是要了解如何将你的模型容器化。

一些与Docker相关的术语,你可能已经知道了,并在本文中遇到了。

  • Dockerfile:你可以把Dockerfile看作是描述你想如何设置你想运行的系统的操作系统安装。它包含了你设置Docker容器所需的所有代码,从下载Docker镜像到设置环境。

  • Docker镜像:它是一个只读的模板,包含创建Docker容器的指令列表。

  • Docker容器:容器是Docker镜像的一个可运行的实例。

Basic Docker commands

基本的Docker命令|来源:《Docker》。作者

在创建Docker文件时,有一些最佳实践需要考虑,比如在构建Docker镜像时避免安装不必要的库或包,减少Docker文件中的层数等等。请看下面的文章,了解使用Docker时的最佳做法。

如何为机器学习模型提供服务?

模型服务的重要概念是托管机器学习模型(在企业内部或在云中),并通过API提供其功能,以便公司可以将人工智能集成到他们的系统中。

一般来说,有两种模型服务:批量和在线。

批量预测表示对你的模型的输入是大量的数据,通常是作为一个预定的操作,预测结果可以作为一个表格发布。

在线部署 ,需要用一个端点来部署模型,这样应用程序就可以向模型提交请求,并以最小的延迟收到快速响应。

为ML模型服务时需要考虑的重要要求

流量管理

根据目标服务,在一个端点的请求采取不同的路径。为了同时处理请求,流量管理也可以部署一个负载平衡功能。

监控

监测部署在生产中的机器学习模型是很重要的。通过监控ml模型,我们可以发现模型的性能何时恶化,何时重新训练模型。没有模型监控的机器学习生命周期是不完整的。

数据预处理

对于实时服务,机器学习模型要求进入模型的输入是合适的格式。应该有一个专门的转换服务来实现数据预处理的目的。

有不同的工具可以用来为生产中的机器学习模型服务。你可以查看篇文章,了解你可以用于模型服务的不同机器学习工具/平台的全面指南。

使用Docker为你的机器学习模型提供服务时应避免的错误

现在你明白了模型服务的含义,以及如何使用Docker来服务你的模型。重要的是要知道在用Docker为你的机器学习模型提供服务时该做什么,不该做什么。

操作错误是数据科学家在用Docker部署机器学习模型时最常犯的错误。这种错误往往导致应用程序的ML服务性能不佳。衡量一个ML应用的标准是它的整体服务性能--它应该有较低的推理延迟,较低的服务延迟,以及良好的监控架构。

错误1:使用TensorFlow服务和Docker服务机器学习模型时,使用REST API而不是gRPC

TensorFlow服务是由谷歌开发人员开发的,它提供了一个更简单的方法来部署你的算法和运行实验。

要了解更多关于如何使用TensorFlow服务和Docker来服务你的ML模型,请查看这篇文章

当使用TensorFlow服务为你的机器学习模型提供服务时,你需要了解Tensorflow服务提供的不同类型的端点以及何时使用它们。

gRPC和REST API端点

gRPC

是一个由谷歌创建的通信协议。它使用协议缓冲区作为其消息传递格式,它是高度包装的,高效的结构化数据的序列化。通过对负载平衡、追踪、健康检查和认证的可插拔支持,它可以有效地连接数据中心内和跨数据中心的服务。

REST

大多数网络应用程序使用REST作为通信协议。它说明了客户如何与网络服务通信。尽管REST仍然是在客户端和服务器之间交换数据的一个很好的方式,但它也有其缺点,那就是速度和可扩展性。

gRPC和REST API之间的区别

gRPC和REST API在操作方式上有不同的特点。这个表格比较了两种API的不同特点

消息传递格式

gRPC。

Protobuf(协议缓冲区)

通信方式

gRPC。

双向流

如下图所示,大多数服务于API的请求使用REST到达。在使用RESTful APIsgRPC APIs将预处理后的数据发送到Tensorflow服务预测之前,预处理和后处理的步骤都发生在API内部。

How to use gRPC for model serving

大多数数据科学家经常利用REST API进行模型服务,然而,它有其不足之处。您的模型在被输入后进行预测所需的时间被称为ML推理延迟。为了改善你的应用程序的用户体验,你的ML服务必须快速返回预测结果。

对于小的有效载荷,两种API都能产生类似的性能,同时AWS Sagemaker证明,对于计算机视觉任务,如图像分类和物体检测,在Docker端点内使用gRPC可以减少75%或更多的整体延迟。

使用Docker的gRPC API部署你的机器学习模型

第1步:确保Docker安装在你的电脑上

第2步:要使用Tensorflow服务,你需要从容器库中提取Tensorflow服务镜像。

docker pull tensorflow/serving

第3步:建立并训练一个简单的模型

import

第4步:保存模型

当保存你的TensorFlow模型时,你可以把它保存为一个协议缓冲区文件 通过在save_format参数中传递 "tf",把模型保存为一个协议缓冲区文件。

file_path = f

保存的模型可以用 saved_model_cli命令进行调查。

!saved_model_cli show --dir {export_path} --all

第5步:使用gRPC服务模型

你需要安装gRPC库。

Import grpc

你需要使用8500端口在客户端和服务器之间建立一个通道。

channel = grpc.insecure_channel(

服务器的请求有效载荷需要通过指定模型的名称、模型存储的路径、期望的数据类型和数据中的记录数来设置为一个协议缓冲区。

request = predict_pb2.PredictRequest()
request.model_spec.name = 

最后,为了用Docker部署你的模型,你需要运行Docker容器。

docker run -p 

现在,服务器可以接受客户端的请求了。从存根中调用Predict方法来预测请求的结果。

stub.Predict(request, 

按照上面的步骤,你将能够用gRPC API来服务你的TensorFlow服务模型。

错误2:在用Docker服务你的机器学习模型时预处理你的数据

开发人员在使用Docker为他们的机器学习模型提供服务时犯的另一个错误是在进行预测之前对他们的数据进行实时预处理。在ML模型提供预测之前,它希望数据点必须包括训练算法时使用的所有输入特征。

例如,如果你训练一个线性回归算法,根据房子的大小、位置、年龄、房间数量和方向来估计其价格,训练后的模型将需要这些特征的值作为推理过程中的输入,以便提供一个估计的价格。

在大多数情况下,输入的数据需要进行预处理和清理,有些特征甚至需要进行设计。现在想象一下,每次你的模型端点被触发时,都要实时地做这件事,这意味着对一些特征进行重复的预处理,特别是静态特征和高ML模型延迟。在这种情况下,特征存储 被证明是一种无价的资源。

什么是特征存储?

特征存储与存储有关,用于在多个管道分支之间存储和提供特征,从而实现共享计算和优化。

在Docker中为ml模型服务时使用特征存储的重要性

  • 数据科学家可以使用特征存储来平滑维护特征的方式,为更有效的流程铺平道路,同时确保特征被正确存储、记录和测试。

  • 同样的特征在整个公司的许多项目和研究任务中都有使用。数据科学家可以使用特征存储来快速访问他们需要的特征,避免做重复性的工作。

当为你的机器学习模型提供服务时,为了调用模型进行预测,会实时获取两类输入特征。

  1. 静态参考。这些特征值是静态的,或者是需要预测的实体的逐渐变化的属性。这包括描述性属性,如客户的人口信息。它还包括客户的购买行为,如他们花了多少钱,他们花了多长时间,等等。

  2. 实时动态特征。这些特征值是根据实时事件动态地捕捉和计算的。这些特征是实时计算的,通常是在一个事件流处理管道中。

特征服务API使特征数据对生产中的模型可用。创建服务API时,考虑到了对最新特征值的低延迟访问。为了更好地理解特征存储并了解不同的特征存储,请查看这篇文章。特征存储。数据科学工厂的组成部分

错误3:使用IP地址在Docker容器之间通信

最后,你已经用Docker部署了你的机器学习模型,你的应用程序正在生产环境中返回预测结果,但由于某些原因,你需要对容器进行更新。在做了必要的修改并重新启动你的容器化应用程序后,你不断得到 "错误:连接ECONNREFUSED" .

你的应用程序无法建立与数据库的连接,尽管它之前工作得非常好。每个容器都有自己的内部IP地址,每当容器重新启动时,它就会改变。数据科学家犯的错误是使用Docker的默认网络驱动,即bridge,在容器之间进行通信。同一桥接网络内的所有容器都可以通过IP地址相互通信。因为IP地址是波动的,这显然不是最好的方法。

不使用IP地址,如何在Docker容器之间进行通信?

为了与容器通信,你应该使用环境变量来传递主机名,而不是IP地址。你可以通过创建一个用户定义的桥接网络来做到这一点。

How to create a user-defined bridge network

如何创建一个用户定义的桥接网络 |来源

  1. 你需要创建自己的自定义桥接网络。你可以通过运行Docker network create命令来做到这一点。这里我们创建一个名为 "dummy-network "的网络。
Docker network create dummy-network
  1. 用以下命令正常运行你的容器 docker运行命令正常运行你的容器。将其添加到你的用户定义的桥接网络,用 -net选项.你也可以用-name选项添加一个别名。
docker run --rm --net dummy-network --name tulipnginx -d nginx
  1. 将另一个容器连接到你创建的自定义桥接网络。
docker run --net dummy-network -it busybox 
  1. 现在你可以使用容器主机名连接到任何容器,只要它们在同一个自定义桥接网络上,而不用担心重新启动。

错误4:以根用户身份运行你的进程

很多数据科学家都犯了这样的错误,以根用户身份运行他们的进程,我将解释为什么它是错误的,并推荐解决方案。在设计系统时,坚持最小权限的原则是很重要的。这意味着,一个应用程序应该只访问它完成任务所需的资源。准确地授予一个进程执行所需的最小权限是保护自己免受任何意外入侵的最佳策略之一。

因为大多数的容器化进程是应用服务,它们不需要root权限。容器不需要root权限来运行,但Docker需要。写得好的、安全的、可重用的Docker镜像不应该期望以root身份运行,应该提供一种可预测的、简单的方式来限制访问。

默认情况下,当你运行你的容器时,它假定 用户。我也犯过这样的错误,总是以root用户身份运行我的进程,或者总是使用sudo来完成事情。但我已经知道,拥有比它所需要的不必要的权限,会导致灾难性的问题。

让我通过一个例子来证明这一点。这是我过去在一个项目中使用的dockerfile样本。

FROM tiangolo/uvicorn-gunicorn:python3

首先是建立一个Docker镜像并运行Docker容器,你可以用这个命令来做这件事

docker build -t getting-started .
docker run -d p 

下一步是获得容器ID,你可以通过检查你的Docker容器进程来做到这一点。 docker ps,然后你可以运行 whoami命令来查看哪个用户有权限进入该容器。

Running your processes as root users

如果应用程序有漏洞,攻击者可以获得容器的root权限。用户在容器内有root权限,可以做任何他们想做的事情。攻击者不仅可以利用这一点来干扰程序,还可以安装额外的工具,用来透视其他设备或容器。

如何以非root用户身份运行Docker

使用dockerfile。

##########################################
# Dockerfile to change from root to
# non-root privilege
###########################################
 
FROM debian:stretch
 
# You can add a new user "User-tesla" with user id 1099
RUN useradd -u 1099 user-tesla
# Change to non-root privilege
USER user-tesla

作为一个容器用户,对改变用户的支持程度由容器维护者决定。使用-user参数,Docker允许你改变用户(或docker-compose.yml中的用户密钥)。进程应该被改变的用户ID被作为一个参数提供。这限制了任何不需要的访问。

错误5:在用Docker提供ML模型时不监控模型版本

数据科学家犯的一个操作性错误是在将ML系统部署到生产之前没有跟踪它的变化或更新。模型版本有助于ML工程师了解模型中的变化,研究人员更新了哪些功能,以及功能是如何变化的。知道有哪些变化,以及在整合多个功能时,它们会如何影响部署的速度加便捷。

模型版本管理的优势

模型版本管理有助于跟踪你以前部署到生产中的不同模型文件,通过这样做,你可以实现。

  1. 模型脉络的可追溯性:如果最近部署的模型在生产中表现不佳,你可以重新部署一个表现更好的先前版本的模型。

  2. 模型注册表:像Neptune AIMLFlow这样的工具可以作为一个模型注册表,使你很容易记录他们的模型文件。每当你需要模型服务时,你可以获取模型和具体版本。

使用Neptune.ai进行模型的版本管理和用Docker进行部署

Neptune.ai允许你跟踪你的实验、超参数值、用于特定实验运行的数据集以及模型工件。Neptune.ai提供了一个python SDK,你可以在构建你的机器学习模型时使用。

第一步是确保你安装了neptune python客户端。根据你的操作系统,打开你的终端并运行这个命令。

pip install neptune-client

在训练完你的模型后,你可以在Neptune中注册它,以跟踪任何相关的元数据。首先,你需要初始化一个Neptune模型对象。这个模型对象适合用来保存训练过程中所有模型版本共享的通用元数据。

import neptune.new as neptune
model = neptune.init_model(project='<project name>’',
    name="<MODEL_NAME>",
    key="<MODEL>",
    api_token="<token>"
)

这将产生一个通往Neptune仪表板的URL,在那里你可以看到你所创建的不同模型。查看工作区

How to create a model version in neptune.ai

在Neptune.ai中记录的ML模型 |来源

在Neptune中创建一个模型版本,你需要在同一个Neptune项目中注册了你的模型,你可以在仪表板的模型标签下找到你的模型。

要在Neptune上创建一个模型版本,你需要运行这个命令。

import neptune.new as neptune
model_version = neptune.init_model_version(
    model="MODEL_ID",
)

接下来就是存储任何相关的模型元数据和工件,你可以通过将它们分配给你创建的模型对象来实现。要了解你如何记录你的模型元数据,请查看这个文档页面

How to create a model version in neptune.ai

在Neptune的UI中可以看到不同版本的模型 |来源

现在你可以看到你所创建的模型的不同版本,每个模型版本的相关元数据,以及模型度量。你还可以管理每个模型版本的模型阶段。从上面的图片来看,DOC-MODEL-1已被部署到生产中。这样,你就可以看到当前部署到生产中的模型版本以及这种模型的相关元数据。

当构建你的机器学习模型时,你不应该把相关的元数据如超参数、评论和配置数据作为文件存储在Docker容器中。当一个容器被停止、销毁和替换时,你可能会丢失容器中的所有相关数据。使用Neptune-client,你可以记录和存储每次运行的所有相关元数据。

当用Neptune在Docker中提供服务时,如何监控模型版本

由于Neptune通过创建模型、创建模型版本和管理模型暂存转换来管理你的数据,你可以查询和下载你存储的模型,使用Neptune作为模型注册表

创建一个新的脚本,用于服务和导入必要的依赖性。你所需要做的就是指定你需要在生产中提供服务的模型版本。你可以通过传递你的NEPTUNE_API_TOKEN和你的MODEL_VERSION 作为一个Docker环境变量来运行你的Docker容器。

import neptune.new as neptune
import pickle,requests
 
api_token = os.environ['NEPTUNE_API_TOKEN']
model_version = os.environ['MODEL_VERSION']
 
 
def load_pickle(fp):
 
   """
   Load pickle file(data, model or pipeline object).
   Parameters:
       fp: the file path of the pickle files.
      
   Returns:
       Loaded pickle file
   """
   with open(fp, 'rb') as f:
       return pickle.load(f)
      
def predict(data):
   #####
   input_data = requests.get(data)
   #####
   model_version = neptune.init_model_version(project='docker-demo',
   version=model_version,
   api_token=api_token
   )
   model_version['classifier']['pickled_model'].download()
   model = load_pickle('xgb-model.pkl')
   predictions = model.predict(data)
   return predictions

你可以通过创建一个Dockerfile并在你的requirements.txt文件上提供一个依赖性列表,使用Docker来容器化你的机器学习模型服务。

neptune-client
sklearn==1.0.2
# syntax=docker/dockerfile:1
FROM python:3.8-slim-buster
 
RUN apt-get update
RUN apt-get -y install gcc
 
COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt
 
COPY . .
CMD [ "python3", "-W ignore" ,"src/serving.py"]

为了从上面的dockerfile中构建一个Docker镜像,你需要运行这个命令。

docker build --tag <image-name> . # image-name: neptune-docker

docker run -e NEPTUNE_API_TOKEN="<YOUR_API_TOKEN>"  -e MODEL_VERSION =”<YOUR_MODEL_VERSION>” <image-name>

在Docker容器上有几种管理数据的替代方法,你可以在开发过程中绑定-挂载目录。对于调试你的代码来说,这是一个不错的选择。你可以通过运行这个命令来实现。

docker run -it <image-name>:<image-version> -v /home/<user>/my_code:/code

现在你可以同时在容器中调试和执行代码,并且这些变化将在主机上被镜像。这使我们回到了在整个容器中利用相同的主机用户ID和组ID的优势。你所做的所有修改看起来都是来自主机的用户。

为了启动你的Docker容器,你将需要运行这个命令。

docker run -d -e NEPTUNE_API_TOKEN="<YOUR_API_TOKEN>"  -e MODEL_VERSION =”<YOUR_MODEL_VERSION>” <image-name>

-d选项指定容器应以守护模式启动。

最后的想法

可重复性和协作开发是数据科学家应该用Docker容器部署他们的模型的最重要原因。TensorFlow服务是模型服务的流行工具之一,你可以扩展它来服务其他类型的模型和数据。另外,当用TensorFlow服务来服务机器学习模型时,你需要了解不同的客户端API,并选择最适合你的用例。

Docker是一个在生产中部署和服务模型的好工具。尽管如此,确定许多数据科学家所犯的错误并避免犯类似的错误是至关重要的。

数据科学家在用Docker提供机器学习模型时犯的错误围绕着模型延迟、应用安全和监控。模型延迟和模型管理是你的ML系统的重要部分。一个好的ML应用应该在收到请求时返回预测结果。通过避免这些错误,你应该能够有效地用Docker部署一个工作的ML系统。