还在用Github管理机器学习项目?你早该了解这些更专业的新工具!
大数据文摘出品
编译:钱天培、胡笳
“太复杂了!机器学习(ML)项目实在太复杂了!”
听到这种抱怨,熟悉软件开发的小伙伴们往往是嗤之以鼻的。
机器学习,不过是和数据和软件打交道。那就应该是是运行代码、迭代算法的简单问题呀?一段时间后,我们就能拥有一个完美的训练有素的ML模型。
有什么复杂的?
然而,当真正着手起机器学习项目,你就会发现:事情可没有那么简单!
在项目进行了一段时间后,你的训练数据或许已经被更改或删除,而你对训练脚本的理解可能也已经十分模糊。
回过头看你训练好的模型,你可能也记不得每一个模型是怎么训练出来的了;再或者,你想要查看先前训练好的模型,却发现模型早已被覆盖。
更可怕的是团队协作,你想要把你的工作分享给你的同事们,他们却怎么也无法复现你的结果,更别提参与协作了。
别慌!今天,文摘菌就来带大家系统地学习一下,如何正确地管理机器学习(ML)项目。
正如一般的软件开发项目一样,你需要更好地管理代码版本和项目资产。在软件开发项目中,人们可能需要重新审视项目先前的状态。在机器学习项目中,我们该如何实现类似的审查呢?与Pull Request相对应的又是什么呢?
就我个人而言,我才刚刚开始接触机器学习工具。在学习过程中,我观看了一些教程视频。老师们提到的一些问题会让我想起我在软件工程职业生涯早期碰到的难题。例如,在1993到1994年,我是一个开发电子邮件用户代理的团队首席工程师。我们没有任何源代码管理(SCM)系统。每天我都会咨询其他团队成员,看看他们那天做了哪些改变,也就是在他们的源树和主源树之间运行一个diff操作,然后手动更改代码。稍后,团队成员从主源树手动更新他们的源树。
在我们发现早期的SCM系统(CVS)之前,这真是一团糟。SCM工具使项目运行得更加顺利。
当我了解到机器学习和数据科学项目中使用的工具时,我发现机器学习过程就如上边所说的那样。即使在今天,机器学习研究人员有时会将实验(数据,代码等)存储在并行目录结构中,以便于进行diff审查,就像我在1993年所做的那样。
那么,理想中的机器学习项目管理应该是怎么样的呢?
ML项目管理原则
让我们从一些简要的ML项目管理原则说起。
在任何ML项目中,程序员们都会进行许多实验,为目标场景开发最佳的训练模型。实验一般包含:
- 代码和配置:实验中使用的软件,以及配置参数
- 数据集:任何输入数据的使用——这可以是千兆级别大小的数据,比如语音识别、图像识别项目中所用到的数据
- 输出:训练后的ML模型和实验的任何其他输出
机器学习项目本质也就是软件运行。但是,与同事共享文件或复制结果,并及时回顾以评估项目通常十分困难。我们需要更全面的管理工具。
解决方案需要涵盖以下几点(从Patrick Ball的题为《原则性数据处理》的演讲中摘录):
(1) 透明性:方便检查ML项目的方方面面
- 使用什么代码、配置和数据文件
- 工程项目采用什么工序,工序的次序是什么
(2) 可审核性:方便检查pipeline的中间结果
(3) 可复现性:在开发的任何阶段精确地重新执行项目的能力,以及同事精确地重新执行项目的能力
- 记录处理步骤,以便任何人都可以自动重新运行这些步骤
- 在项目进行过程中记录项目的状态。“状态”表示代码、配置和数据集
- 能够在项目历史的任何时候重新创建可用的精确数据集
(4) 可扩展性:支持多个同事同时处理一个项目的能力,以及同时处理多个项目的能力
为什么不在机器学习项目中使用常规的软件工程工具?
诚然,在常规软件工程项目中使用的许多工具可能对机器学习研究人员有用。
在常规的源代码管理系统(如Git)中可以轻松地管理代码和实验配置,并且可以使用pull request之类的技术来管理对这些文件的更新。CI/CD(Jenkins等)系统甚至可以用于自动化项目运行。
但是,ML项目另有不同之处,使得普通的软件开发工具无法满足所有的需求。下面是几个重要的不同点:
- 度量标准驱动(metrics-driven)的开发与特性驱动(feature-driven)的开发:在常规软件工程中,“是否发布”这一决策基于团队是否达完成了一些特征。相比之下,机器学习研究人员研究的是一种完全不同的测量方法——生成的机器学习模型的预测值。研究人员将迭代地生成几十个(或更多)模型,测量每个模型的准确性。由于目标是找到最精确的模型,因此项目由每个实验中实现的度量指标来指导。
- 机器学习模型需要大量的资源来训练:一个常规的软件项目将文件组织在一起编译一个软件产品,而机器学习项目则训练一个描述AI算法的“模型”。在大多数情况下,编译一个软件产品只需要几分钟,非常快速,因此许多团队遵循持续集成(continuous integration)的策略。训练一个机器学习模型则需要很长时间。除非必要,最好避免持续集成。
- 庞大的数据集和训练好的模型:机器学习开发阶段几乎总是需要庞大的数据集,此外,训练过的模型也可能是巨大的。普通的源代码管理工具(Git等)不能很好地处理大型文件,而且Git- lfs之类的附加组件也不适合ML项目。
- 工作流(pipelines):机器学习项目是一系列步骤——如下载数据、准备数据、将数据分离到培训/验证集、培训模型和验证模型。许多人使用pipelines这个词来描述整个过程,意思是用每个步骤的离散命令来构造机器学习项目,而不是把所有东西都塞进一个程序中。
- 专用硬件:软件开发商可以将其软件基础设施托管在任何类型的服务器设备上。如果他们想要一个云部署,他们可以从他们喜欢的云计算提供商那里租用VPS。然而,机器学习研究人员有巨大的计算需求。高性能GPU不仅可以加速视频编辑,还可以让ML算法加速“飞”起来,大大缩短了训练ML模型所需的时间。
现在,我们已经有了一个机器学习项目开发的原则列表,并了解了ML项目和普通软件开发项目的不同之处。
接下来,让我们看看有哪些开源软件可以帮助我们实现这些原则。
我们将特别讨论两个工具,MLFlow和DVC。当然,还有很多其他软件可以取得类似的效果。
机器学习项目中的数据与模型存储
我们的讨论可以归结为:
- 跟踪每一轮训练机器学习模型使用的数据文件
- 跟踪训练后的模型和评估指标
- 通过任何形式的文件共享系统与同事共享数据文件的简单方法
总的来说,我们需要一个数据跟踪系统来透明地审计、或者复现结果。我们也需要一个数据共享系统来将项目团队扩展到多个同事。
就如我们先前讨论的一样,使用Git或其他SCM(源代码管理系统)来存储机器学习项目中使用的数据文件是不切实际的。
一些库提供了API来简化远程存储上的文件处理,并管理向远程存储上传或获取文件。虽然这有利于远程数据集的共享访问,但却对我们面对的问题没有帮助。
首先,它是嵌入式配置的一种形式,因为文件名被嵌入到软件中。在源代码中嵌入配置设置的任何程序在其他情况下都更难以被重新使用。其次,它没有将脚本版本和其使用的数据文件关联起来。
下面,然后我们看一下MLFlow的示例代码:
mlflow.pytorch.load_model("runs:/<mlflow_run_id>/run-relative/path/to/model")
这支持多种文件访问“方案”,包括S3这样的云存储系统。这里的示例从“run”区域加载一个文件,在本例中是一个经过训练的模型。每次执行一段代码时,MLFlow都会生成一个“run”。你需要配置一个存储“run”数据的位置,并且显然会为每个用于索引到数据存储区域的运行生成一个“run ID”。
这种方式有效地将数据与对应SCM源码管理库中的代码和配置文件的commit提交版本关联起来。此外,MLFLow API有多种实现语言,并不局限于 Python语言。
DVC采用的则是另一种方式。对比上面将文件API集成到ML脚本中,你的脚本可以简单地使用普通文件系统的API实现输入和输出文件。例如:
model=torch.load(‘path/to/model.pkl’)
通过上述代码,路径名称将通过这条命令传入。你无需特别修改代码,因为DVC可以通过外部传递训练代码或验证模型代码需要的值。
DVC让这一切变得透明——数据文件版本与代码的Git版本是相匹配的。
通过下述命令,你可将文件或文件夹加入DVC的版本管理:
$ dvc add path/to/model.pkl
数据存储在你的工作目录中。浏览各项运行的结果也很简单,只需要浏览你的Git历史即可。查看特定结果就像git checkout一样简单,DVC将被调用,并确保将正确的数据文件连接到workspace。
一个”DVC文件”将被创建,用于追踪每个文件及目录,并将被添加到workspace中。这样做有两个目的,一是可以追踪数据和模型文件,另一个则是记录工作流程中的命令。我们将在下一节中介绍这部分。
这些DVC文件记录文件和目录的MD5总和校验码(MD5 checksum)。他们被提交到git workspace上,因此DVC文件记录了每次git提交的每个文件的总和校验码。DVC使用了“DVC缓存目录”来存储每个文件的多个实例。文件实例通过总和校验码进行索引,并使用reflinks或symlinks链接到workspace。当DVC响应git checkout命令时,它能够根据DVC文件中的总和校验码快速地重排链接文件。
DVC支持远程缓存目录,用于共享文件和模型。
$ dvc remote add remote1 ssh://[email protected]/path/to/dir$ dvc push$ dvc pull
DVC remote是一个存储池,可以进行数据共享。它支持许多存储服务,包括S3、HTTP和FTP等。创建一个DVC remote非常简单。dvc push和dvc pull命令高度模拟了git push 和git pull命令。dvc push用于将数据发送到远程DVC的缓存中,dvc pull用于从远程DVC缓存中拉取数据。
机器学习项目中的工作流描述
接下来,我们将讨论如何更好地描述机器学习项目的工作流。我们应该一股脑将所有东西堆成一个程序吗?还是应该使用多种工具?
为了尽可能地创造灵活性,我们可以将工作流通过pipeline或有向无环图(DAG),并采用命令行参数作为配置选项的方式来实现。这有点类似Unix哲学中的小而精巧的工具——小巧但可以很好地协同工作。其行为可由命令行选项或环境变量指定,并且可以根据需要任意搭配使用。
相比之下,很多ML框架采用不同的方式。他们编写单独的程序来驱动特定项目的工作流。程序第一步先将数据拆分为训练集和验证集,然后训练模型并验证模型。这种整套的单独程序可带来重用代码的机会有限。
将ML项目构建pipeline可带来如下好处
- 管理复杂性:将这些步骤作为单独命令实现可以提高透明度,并帮助你更加集中精力。
- 优化执行:可以跳过那些没有修改且不需要返回值的步骤。
- 可重用性:在多个项目中可重用相同的工具。
- 可扩展性:不同的工具可由不同的团队成员独立开发。
在MLFlow中,你需要编写一个“驱动程序”。这个程序包含了所需的执行逻辑,例如处理及生成机器学习模型。在程序背后,MLFlow API发送请求给MLFlow 服务器,通过该服务器生成指定的命令。
下面这个多步骤工作流的MLFlow例子清晰的展示了这一切。
...load_raw_data_run = _get_or_run("load_raw_data", {}, git_commit)ratings_csv_uri = os.path.join(load_raw_data_run.info.artifact_uri, "ratings-csv-dir")etl_data_run = _get_or_run("etl_data", {"ratings_csv": ratings_csv_uri, "max_row_limit": max_row_limit}, git_commit)…als_run = _get_or_run("als", {"ratings_data": ratings_parquet_uri, "max_iter": str(als_max_iter)}, git_commit)…_get_or_run("train_keras", keras_params, git_commit, use_cache=False)...
_get_or_run函数是mlflow.run的一个wrapper。每个调用函数中的第一个参数为在MLproject文件中定义的entrypoint。每个entrypoint包含环境变量,要运行的命令,以及传递给该命令的参数。例如:
etl_data: parameters: ratings_csv: path max_row_limit: {type: int, default: 100000} command: "python etl_data.py --ratings-csv {ratings_csv} --max-row-limit {max_row_limit}"
乍一看感觉非常不错。但是这里有几个问题值得思考:
- 如果你的工作流是比直线流程更复杂的情况怎么办?你可以将传给mlflow.run的同步参数设为false,然后等待SubmittedRun对象标记任务已完成。也就是说,可以在MLFlow API上构建流程管道系统。
- 为什么需要服务器?为什么不直接通过命令行执行命令?增加服务器及其配置使得MLFlow项目的设置更加复杂。
- 如何避免执行那些不需要的任务?在许多ML项目中,训练模型通常需要数天时间。资源应该只有在需要时才应该被使用,例如更换数据,修改参数或算法。
DVC可以使用常规命令行工具,并且既不需要设置服务器也不需要编写驱动程序。DVC支持使用前面提到的,通过一组DVC文件将工作流定义为有向无环图(DAG)。
我们之前提到了,DVC文件会与添加到workspace中的文件相关联。DVC文件同时还描述了要执行的命令:
$ dvc run -d matrix-train.p -d train_model.py \ -o model.p \ python train_model.py matrix-train.p 20180226 model.p$ dvc run -d parsingxml.R -d Posts.xml \ -o Posts.csv \ Rscript parsingxml.R Posts.xml Posts.csv
dvc run命令定义了一个DVC文件,其中包含了需要执行的命令。-d参数记录了对文件的依赖性,DVC将根据校验总和码来检测文件的更改。-o参数表示命令输出设置。一个命令的输出同样可以用于另一个命令的输入。通过查看依赖关系和输出,DVC可以计算出执行命令的顺序。
AI输出(包含训练模型)将被自动的记录在DVC的缓存中,workspace中的其他数据文件也如此。
因为它计算校验和,DVC可以检测到更改的文件。当用户请求DVC重新执行管道时,它只执行有变化的那部分。输入文件没有变化的情况,DVC可以节省大量模型训练任务所需要的时间。
所有的执行都使用常规命令行,不需要设置服务器。如果你希望在云计算环境,或在附加GPU的服务器上执行,只需要将代码和数据部署到该服务器上,并通过命令行执行DVC命令即可。
总结
我们在提高机器学习实践原则的探索上已经走了很长一段路。如我们所知,机器学习领域需要更好的管理工具,以便机器学习团队能够更有效、更可靠地工作。
结果可复现意味着他人可以评估你已完成的工作,或者协作进行更深层的开发。可重复性具有很多先决条件,包括能够检查系统的每个部分,以及能够精确地重新运行软件及输入数据。
机器学习项目中,一些GUI工具有非常漂亮的用户界面,例如Jupyter Notebook。这些工具在机器学习的工作中占有一席之地。但是,GUI工具不太适合本文讨论的原则。命令行工具非常适合处理在后台运行的任务,并且可以轻松地满足我们上述的所有原则。而一般的GUI则会妨碍这些原则。
正如本文中描述的一样,我们可以从常规软件工程中借用很多工具和实践方式。但是,机器学习项目的特殊性决定了我们需要用到更适合其目标的工具。这些有价值的工具包括:MLFlow,DVC,ModelDb,Git-LFS等等。