才云开源的基于镜像仓库的机器学习模型分发组件 ormb(OCI-Based Registry for ML/DL Model Bundle)能帮助企业像管理容器镜像一样管理机器学习模型。它不仅提供版本化的模型管理能力,还可利用符合 OCI 标准的容器镜像仓库存储和分发机器学习模型。通过 Harbor 2.0,它可以实现在多个镜像仓库间的同步,满足更多企业级需求。
以 Docker 为代表的容器虚拟化技术业已成为云计算的中流砥柱,也在世界各地吸引了无数软件工程师成为它的拥趸。凭借 Open Container Initiative,容器生态在过去几年中迎来了迅速演化,Docker Compose、Kata Containers 等项目百花齐放,Kubernetes 也成了集群调度领域的事实标准。
回首往昔,以 Docker 为代表的容器虚拟化技术之所以能席卷世界, 最大的依仗当属其镜像分发能力 。在应用部署场景下,它很好地解决了传统应用分发的问题——Docker 通过 Build Once、Deploy Anywhere 的能力,使得 Java、NodeJS、Rust 等各种语言,各种依赖库可以在本地一次构建成符合 OCI 规范的容器镜像,随后利用镜像仓库进行存储和分发。
随着人工智能取代传统应用逐步落地企业生产环境,面对新的机器学习、深度学习场景,我们是否需要一种新的分发能力?
为什么需要分发模型
与传统应用场景相比,机器学习场景存在一定差异,因此它对分发能力的要求也有一些不同。
在传统应用场景下,开发团队需要分发的通常是二进制可执行程序,或是带有语言解释器的脚本代码、带着语言虚拟机的字节码。他们的迭代对象是代码,同一个版本的代码多次编译出来的输出工件是一致的。
但当我们需要部署机器学习应用时,一般情况下,我们需要一个 模型推理服务器 (也被称作模型服务器)和 被部署的模型 。
模型服务器之于机器学习应用,就好比 Tomcat 之于 Java Web 应用。机器学习模型本身只是模型的权重和结构等信息的集合,并不能直接对外提供服务。它需要通过一个服务器,对外提供 RESTful 或者基于 gRPC 的服务。这个服务器就是模型服务器。调用方的请求会首先到达模型服务器,随后模型服务器会利用模型进行前向计算,并且将结果作为响应返回给调用方。
如下图所示,当我们想在生产环境利用云原生技术部署模型服务时,刚开始,出于简单方便的考虑,我们会将模型服务器与模型文件打包成一个镜像,同时复用镜像分发的能力来实现对模型的分发。
但这样的做法存在一些问题:
首先,模型服务器本身属于不可变的基础设施,在业务中通常会由基础设施团队维护或者直接采用开源的模型服务器。而模型服务器通常是非常重的,以 Nvidia Triton Inference Server 20.03 版本为例,它的镜像有 43 层,解压后的大小有 6.3G。因此将模型服务器和模型文件一同打包, 非常容易出现超大镜像 。在镜像 P2P 分发还没有广泛落地的当下,不易于维护和分发;
第二, 模型的更新是非常频繁的 。如果这一打包过程不能自动化,需要算法工程师始终保持参与,总体成本将难以控制;
最后,由于镜像不能保存模型的元数据,这种方式不能很好地横向扩展。随着模型和版本增多,开发团队在 多模型和多版本的管理 上会遇到相当艰巨的挑战,而模型的超参数、训练指标、存储格式等元数据的缺失更会加剧这一问题。
随着规模发展,我们可以把模型服务器(如 Nvidia Triton Inference Server、ONNX Runtime 等)当成底层的“运行时”,是不变的基础设施;把模型服务器以 Docker 镜像的方式分发。而模型,则可以被类比为是在运行时上运行的脚本代码,它可以被模型服务器容器在运行时通过挂载的方式进入容器。
上述设想使我们可以解耦模型服务器和模型本身: 基础设施团队负责分发模型服务器镜像,而算法团队只需要分发模型 。
State-of-the-art
事实上,机器学习场景下模型的分发是不少模型仓库项目的关注重点。目前在业界主流的先进方案中,模型的分发问题主要有两类实现思路。
第一个方向是以才云科技 Caicloud Clever 第一代模型仓库为代表的维护自有存储的实现 。用户需要通过 SDK 或者 UI 的方式,上传模型到模型仓库中。在模型上传后,模型仓库会将模型和模型的元数据存储在自身维护的存储后端中。当需要利用模型进行推理时,用户可利用模型仓库提供的 SDK 或者接口将模型下载下来,进行推理服务。
另外一个方向是以 ModelDB 为代表的实现 。在这种实现中,模型仓库并不真正存储模型,而只存储模型的元数据。模型本身通过借助第三方的对象存储(如 MinIO、S3 等)实现。模型存储的路径作为元数据之一被模型仓库管理。在需要下载模型时,用户可以利用第三方的对象存储提供的 SDK 或者接口进行下载。
这两种实现方式各有优劣:前者可以更好地进行模型的权限控制,但是引入了私有的 SDK 和接口来进行模型的上传与下载,对用户而言有一定的学习成本;后者利用成熟的第三方存储进行模型文件的保存,学习成本较低,但是元数据与模型分离的实现使得权限控制非常困难,用户通过第三方存储的 SDK 或者接口可以绕过模型仓库直接对模型文件进行下载。除此之外,两种方式都需要自己造轮子来实现对模型的元数据和模型文件的处理逻辑。
那么,有没有结合两种实现的优点,同时又可以规避它们的缺点的设计方案呢?
利用镜像仓库分发机器学习模型
让我们把目光投向了镜像仓库。
镜像仓库是云原生时代一个至关重要的基础设施,它为容器镜像提供了标准化的分发能力。试想一下,在机器学习场景下,如果用户也能够通过复用镜像仓库的能力来分发模型,这样不仅可以避免让数据科学家重复造轮子,同时也可以提供类似 docker pull 与 docker push 的使用体验,对用户而言没有过多学习成本。
在这一创想指引下, ormb 应运而生 !
ormb 的名称源自 OCI-Based Registry for ML/DL Model Bundle,它旨在实现 将模型和模型的元数据利用已有镜像仓库进行分发 。
端到端的示例
接下来,我们以图像识别作为示例,介绍一下如何利用 ormb 进行机器学习模型的分发。
在这一示例中,我们会在本地利用 Fashion MNIST 训练一个简单的 CNN 图像识别模型,并利用 ormb 将其推送到远端镜像仓库中。随后,在服务器上,我们同样利用 ormb 将模型拉取下来,利用第三方的模型服务器对外提供服务。最后,我们再利用 RESTful 的接口调用这一服务,查看结果。
下方是模型训练的代码,我们将训练好的模型保存为 SavedModel 格式,并将模型提供在 ormb 示例中:
接下来,我们将在本地训练好的模型推送到远端镜像仓库中:
以 Harbor 为例,在 Harbor 镜像仓库中,我们可以看到这一模型的元数据等信息:
随后,我们可以在服务器上将模型下载下来,模型下载过程与推送到镜像仓库的方法类似:
接下来,我们就可以利用 TFServing 将模型部署为 RESTful 服务,并利用 Fashion MNIST 数据集的数据进行推理:
或者,我们也可以使用 Seldon Core 将模型服务直接部署在 Kubernetes 集群上,具体可以参见我们提供的文档:
算法工程师迭代新版本的模型时,可以打包新的版本,利用 ormb 拉取新的镜像后重新部署。ormb 可以配合任何符合 OCI Distribution Specification 的镜像仓库使用, 这意味着 ormb 支持公有云上的镜像仓库和 Harbor 等开源镜像仓库项目 。
我们也可以利用 Harbor 提供的 Webhook 功能,实现模型服务的持续部署。通过在 Harbor UI 中注册一个 Webhook,所有对 Harbor 的推送模型请求事件都会被转发到我们定义的 HTTP Endpoint 上。而我们可以在 Webhook 中实现对应的部署逻辑,比如根据新的模型来更新 Seldon 部署模型服务的版本,实现模型服务的持续部署等。
利用镜像仓库分发机器学习模型
一言以蔽之,ormb 的设计目标是成为机器学习场景下的 Docker,它想解决的是机器学习模型的分发问题。在实现上,ormb 也站在了 OCI Artifacts、OCI Distribution Specification 和 Harbor 等巨人的肩膀上。
Docker Pull 发生了什么
在介绍设计之前,先让我们来了解一下当用户下载容器镜像时,到底发生了什么?
对于符合 OCI 规范的镜像仓库而言,它们都遵循同样的规范,这一规范就是 OCI Distribution Specification。
首先,Docker 会先向镜像仓库请求镜像的 Manifest。Manifest 是一个 JSON 文件,其定义包括两个部分,分别是 Config 和 Layers。Config 是一个 JSON 对象,Layers 是一个由 JSON 对象组成的数组。可以看到,Config 与 Layers 中的每一个对象的结构相同,都包括三个字段:
digest:可以理解为是这一对象的 ID;
mediaType:表明这一内容的类型;
size:是这一内容的大小。
容器镜像的 Config 有着固定的 mediaType application/vnd.oci.image.config.v1+json。一个 Config 的示例配置如下,它记录了关于容器镜像的配置,可以理解为是镜像的元数据。通常它会被镜像仓库用来在 UI 中展示信息,以及区分不同操作系统的构建等。
而容器镜像的 Layers 是由多层 mediaType 为 application/vnd.oci.image.layer.v1.*的内容组成的(其中最常见的是 application/vnd.oci.image.layer.v1.tar+gzip) 。众所周知,容器镜像是分层构建的,每一层就对应着 Layers 中的一个对象。
容器镜像的 Config 和 Layers 中的每一层,都是以 Blob 的方式存储在镜像仓库中的,它们的 digest 作为 Key 存在。因此,在请求到镜像的 Manifest 后,Docker 会利用 digest 并行下载所有的 Blobs,其中就包括 Config 和所有的 Layers。
镜像仓库又该如何支持模型分发
在上述介绍中,我们可以看到镜像仓库对于镜像的支持,是通过定义了 Manifest、mediaType 为 application/vnd.oci.image.config.v1+json 的 Image Config 和 mediaType 为 application/vnd.oci.image.layer.v1.* 的 Layers 来实现的。
而随着云原生的开疆扩土,除了容器镜像之外,社区也出现了很多其他类型的工件,如 Helm Chart、CNAB 等,希望复用镜像仓库对工件的版本化管理、分发以及分层存储能力。为了满足这样的需求,OCI Artifacts 出现了。
镜像仓库通过 Manifest、Config 和 Layers 来支持对镜像的存储和分发。而如果我们可以自己定义 Config 和 Layers 的类型和结构,我们就可以扩展镜像仓库的能力,存储和分发其他类型的工件。OCI Artifacts 就是为了这一需求提供的指导性文件。由于镜像仓库本身已经有这样的能力,因此 OCI Artifacts 并不是一个规范,而是一个指导开发者如何利用镜像仓库的扩展性能力支持其他工件类型的指导性文件。
具体到对模型这一工件类型的支持的设计上,我们为模型定义了自己的 Config 结构,目前 mediaType 暂定为 application/vnd.caicloud.model.config.v1alpha1+json。它的示例配置如下所示。
对于 Layers,由于模型文件较难分层存储,因此在目前的设计中,模型文件以 application/tar+gzip 的 mediaType 压缩归档后上传到镜像仓库。
因此,从镜像仓库中下载一个模型的过程如图所示:
在实现中,我们与 Harbor 团队紧密合作,将 Harbor 作为默认采用的镜像仓库,也在积极为 Harbor 在 OCI Artifact 支持的扩展性上做贡献。
ormb 可以复用 Harbor 的诸多能力,比如通过利用 Harbor 的 Replication 特性, ormb 可以在训练环境、模型生产环境等多个环境的镜像仓库之间进行模型同步。我们也可以为模型配置 Webhook,在模型的生命周期中添加自定义的处理逻辑。
开源只是一个开始
目前, 才云已经开源 ormb 。基于这个项目,我们之后会继续开源基于 Harbor 实现的模型仓库,提供更多能力。其中包括但不限于模型的格式转换、模型自动压缩等高级特性。
同时,为了以云原生标准化方式解决机器学习场景下的模型分发问题,我们也希望得到工业界的帮助——我们正在 迫切寻求更多愿意参与云原生机器学习基础设施建设的同仁 ,一起制定更加完善的模型 Config 规范,满足更多场景需要。在社区开源贡献上,才云对于开放治理模式一直保持开放心态,因此我们会积极推动以 Vendor 中立的方式运营和管理 ormb。
更进一步,我们也将基于 ormb 和镜像仓库, 在 2020 年下半年推出才云云化 AI 平台 Clever 的社区版 ,以开源社区的方式维护 Clever 的推理能力,解决用户端到端的推理需求。ormb 和模型仓库作为连结开发与推理平台的关键组件,是我们踏出的第一步,欢迎广大开发者试用交流!
本文转载自公众号才云 Caicloud(ID:Caicloud2015)。
原文链接:
评论