机器学习工程的实例化:部署机器学习的推理代码

2024-06-23#机器学习#MLOps

在机器学习项目中,数据科学家在服务器上的 Jupyter Notebook 完成机器学习的模型的搭建,包括读取数据、清洗数据、特征工程、模型训练,最终创建推理代码和模型。这些代码和模型需要被部署到生产环境运行。数据科学家完成的一系列工作可认为是在自己的“沙盒”或者实验环境里离线完成的,与生产环境有很大的差异,还需要考虑更多非功能的因素;而数据科学家没有经过软件工程的训练,编写的代码很可能不是生产就绪(Production Ready)的。因此,需要对数据科学家编写的代码进行工程化的重组和改造,才可以部署到生产环境。

每个项目的目标和软件资源是不一样的,每个项目的人员配比也是不一样的。因此工程化的范围也不千差万别。基于笔者近期一个简单的机器学习应用,本文介绍一下部署工作的内容。

背景 🔗

数据科学家在 Jupyter Notebook 上编写 Python 代码,完成数据的提取和模型的搭建。最终的产出物是模型文件和用于推理的 Jupyter Notebook文件。在生产环境中,需要每天运行模型和推理代码,对生产数据进行推测或者回归,并将结果写入数据库。

工程化的内容 🔗

代码工程化 🔗

版本化 🔗

如果有有可能,应该将代码和模型(甚至数据)进行版本化管理,既防止意外丢失,也为了今后的回溯跟踪。这需要专有的服务支持。比如对于代码可使用 Git 管理;对于生成的模型,应该进行备份和版本标记。

特别注意的是,不能将敏感信息的明文保存在版本控制系统中。如果有,那么需要对代码进行改造。

安全性 🔗

在机器代码中,需要从数据库中读取数据,于是涉及到保存和读取凭证的问题。数据科学家在开发模型时,可能会顺手将用户名密码写在本地的 Jupyter Notebook 中,但将这些敏感信息编码到代码中,并不是好的实践。

将敏感信息放在代码之外,然后通过环境变量传给代码,是一种常见的实践;或者说将配置与代码解耦,是一种基本的软件工程实践。在工具层面上,使用 python-dotenv,可从 .env 文件中读取环境变量。如果有代码的版本控制,那么此时将 .env 排除在版本控制之外。

还有其他的方式。比如在本地从环境变量读取,在线上从专门的密钥管理服务中读取;在云环境或者容器环境中,将敏感信息所在的文件(比如 .env)挂在到容器中,然后在代码中读取等等。情况可能会变得相当复杂,实际中找到安全与实现成本的平衡。

依赖管理 🔗

每一个数据科学家,都会在搭建和维护 Python 上花很多的实践。其实,对于软件开发者而言,在本地搭建“干净”的Python开发环境,或者配置Python的生产运行环境,也是很有挑战的任务。

数据科学家的开发环境中,可能已经在不同时期安装了很多Python包,在Jupyter Notebook直接导入即可使用。但是,在生产环境中,首先需要安装必要的软件包,再者要跟数据科学家所使用的软件包版本尽可能地一致。

在位于云服务的生产环境中,尽管云厂商提供了专门用于机器学习的系统或者容器镜像,但可能与数据科学家所使用的版本不一致。因此最好的方式是自行安装软件包。

而在无服务器的云环境中,可能每次运行机器学习任务,都是崭新的环境。此时,要么需要定制系统或者容器镜像;要么在每次任务运行,安装依赖包。

如果使用自定义的系统或者镜像,那么需要确定一下相关的费用。尤其是镜像服务的费用。有些云厂商的镜像服务价格对于中小企业用户来说很不划算。当使用频率不高时,起步价就会让人望而却步。

如果每次运行任务时安装依赖包,那就要考虑安装时候的网速是否满足需求了。某云厂商的 PyPI 服务器的下载速度是 50ß0KB/s 左右,但 xgboost 的包有 297MB 左右。也许需要花费数十分钟才能下载软件包,这在生产环境是不可接受的。

还有一种思路:将代码所需的依赖上传到云服务的对象存储,在代码运行时加载即可。通常在云服务器上下载对象存储上的压缩文件是很快的,所以突破了网速的限制。在实际中,可能会遇到两种情况:

第一种是云服务器支持运行命令行工具(比如 pip),那么可使用 wheel 依赖提取成 whl 文件,然后把依赖包上传到云服务的对象存储,最后在运行代码时从对象存储下载软件包并使用 pip install 从本地安装。有些云服务器可以挂载对象存储,那么挂载依赖包所在的路径即可。

第二种是云服务器不支持执行命令。那么在本地使用 virtualenv 创建虚拟环境,将依赖包安装到虚拟环境目录中,然后打包压缩上传到对象存储。然后在代码在服务器运行时,从对象存储下载压缩包,解压到服务上的某个目录,然后将 site-packages 添加到 sys.path 中。

存储资源 🔗

在生产环境中,需要存储和加载代码、模型以及数据。而为了提供效率,也可能要存储和加载软件依赖包。

资源编排 🔗

计算资源 🔗

根据不同的计算任务,选择合适的服务器配置和系统镜像。

任务调度 🔗

机器学习的运行时机,以及运行的监控报警都需要考虑。

结尾 🔗

本文介绍了在真实场景下的机器学习工程化的主要事项。其中版本化、安全性和依赖管理相关的事项可能会变得相当复杂,这需要有一定的心理预期。


加载中...