原则性机器学习:高效协作的实践与工具
原则
机器学习项目的原则性数据和模型存储
机器学习项目的原则性工作流程描述
结论
由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!
机器学习项目往往比预想的要难。我们处理的是数据和软件,按理说应该很简单:运行代码,迭代调整算法,过一段时间就能得到一个训练完善的AI模型。但三个月后,训练数据可能已经更改或删除,对训练脚本的理解也可能只剩下模糊的记忆。你是否在训练好的模型和创建模型的过程之间造成了脱节?你如何与同事分享工作成果,以便协作或复现结果?
与所有软件项目一样,我们需要更好地管理代码版本和项目资产。我们可能需要回顾项目过去任何阶段的状态。在软件工程中,我们经常这样做(审查旧的提交记录)。机器学习项目难道不应该也偶尔这样做吗?不仅如此。它还需要类似于 Pull Request 的机制,或者其他领域常用的团队管理方法。
我本人也刚刚开始学习机器学习工具。在学习资料中,我观看了一些教程视频,讲师们有时会提到一些问题,让我想起了我软件工程生涯早期的一段经历。例如,在 1993-1994 年,我曾担任一个开发电子邮件用户代理的团队的首席工程师。当时我们没有任何源代码管理 (SCM) 系统。我每天都要询问所有其他团队成员当天做了哪些更改。我唯一的工具是运行 diff 命令来比较他们的源代码树和主源代码树diff -c | less,然后手动应用更改。后来,团队成员又开始手动从主源代码树更新他们的源代码树。直到我们找到一个早期的 SCM 系统(CVS),情况才有所改善。这个工具让项目运行得顺畅得多。
随着我学习机器学习和数据科学项目中使用的工具,我发现很多案例都与此类似。即使在今天,机器学习研究人员有时仍然会像我1993年那样,将实验数据(数据、代码等)存储在并行的目录结构中,以便于进行差异比较。
原则
让我们先简要概述一些可能有助于改进机器学习项目软件管理工具现状的原则。
在任何机器学习项目中,科学家都会进行大量实验,以开发出针对目标场景的最佳训练模型。实验包括:
- 代码和配置:实验中使用的软件及其配置参数
- 数据集:任何使用的输入数据——其大小很容易达到数GB,例如用于识别音频、图像或视频文件内容的项目。
- 输出:训练好的机器学习模型以及实验的任何其他输出。
机器学习项目本质上就是运行软件。但通常情况下,与同事共享文件或复现结果会遇到困难。要获得可重复的结果,并能与同事共享,还能回溯评估项目的早期阶段,就需要更全面的管理工具。
解决方案需要包含以下理念(摘自 Patrick Ball 题为“基于原则的数据处理”的演讲):
- 透明度:检查机器学习项目的各个方面。
- 使用哪些代码、配置和数据文件?
- 该项目使用了哪些处理步骤,以及这些步骤的顺序。
- 可审计性:检查管道的中间结果
- 既要看最终结果,也要看任何中间结果
- 可复现性:能够在项目开发的任何阶段精确地重新执行项目,并且能够让合作者精确地重新执行项目。
- 记录处理步骤,以便任何人都能自动重新运行。
- 记录项目进展过程中的状态。“状态”指的是代码、配置和数据集。
- 能够重现项目历史中任何时间点可用的精确数据集,对于确保审计功能的有效性至关重要。
- 可扩展性:能够支持多个同事共同参与一个项目,以及能够同时处理多个项目。
机器学习项目与常规软件工程有何不同?
你是不是已经得出结论:如果机器学习项目和软件工程是一样的,那么为什么我们不直接在机器学习项目中使用常规的软件工程工具呢?别急!
常规软件工程项目中使用的许多工具对机器学习研究人员也很有用。代码和实验配置可以轻松地在像 Git 这样的常规源代码管理系统中进行管理,并且可以使用拉取请求等技术来管理这些文件的更新。持续集成/持续交付(CI/CD,例如 Jenkins)系统甚至可以用于自动化项目运行。
但机器学习项目与其他项目存在一些差异,使得常规软件开发工具无法满足所有需求。以下几点值得注意:
- 指标驱动开发与特性驱动开发:在常规软件工程中,“是否发布”的决策取决于团队是否达到了特性里程碑。相比之下,机器学习研究人员关注的是完全不同的衡量标准——所生成的机器学习模型的预测价值。研究人员会迭代生成数十个(甚至更多)模型,并衡量每个模型的准确率。由于目标是找到最准确的模型,因此项目由每次实验中获得的指标指导。
- 机器学习模型的训练需要大量资源:普通软件项目将文件整理编译成软件产品,而机器学习项目则训练一个描述人工智能算法的“模型”。大多数情况下,编译软件产品只需几分钟,成本极低,因此许多团队都采用持续集成策略。训练机器学习模型耗时极长,因此除非必要,否则最好避免进行此类训练。
- 海量数据集和训练模型:前文所述,机器学习开发阶段几乎总是需要用于训练机器学习模型的海量数据集,而且训练好的模型本身也可能非常庞大。普通的源代码管理工具(例如 Git)处理大型文件的能力有限,而像 Git-LFS 这样的插件也不适用于机器学习项目。(参见我之前的文章)
- 流程:机器学习项目包含一系列步骤,例如下载数据、准备数据、将数据划分为训练集/验证集、训练模型以及验证模型。许多人使用“流程”一词,将机器学习项目的每个步骤都用独立的命令来组织,而不是把所有内容都塞进一个程序中,这样会更有帮助。
- 专用硬件:软件公司可以将软件基础设施托管在任何类型的服务器设备上。如果他们需要云部署,可以从他们常用的云计算提供商那里租用普通的虚拟专用服务器 (VPS)。机器学习研究人员有着巨大的计算需求。高性能 GPU 不仅可以加快视频编辑速度,还可以显著提升机器学习算法的运行速度,大幅缩短训练机器学习模型所需的时间。
如果中间结果是三个月前生成的,而情况已经发生变化,导致您不记得当时软件是如何运行的,该怎么办?如果数据集已被覆盖或更改,又该怎么办?一个支持机器学习项目透明度、可审计性和可复现性的系统必须考虑到所有这些因素。
既然我们已经有了一系列原则,让我们来看看这方面的一些开源工具。
有很多工具可能适用于数据科学和机器学习从业者。在接下来的章节中,我们将重点讨论两种工具(MLFlow和DVC),同时也会探讨一些通用原则。
机器学习项目的原则性数据和模型存储
这场讨论的一个方面可以归结为:
- 跟踪每一轮机器学习模型训练所使用的数据文件。
- 跟踪训练模型和评估指标的结果
- 通过任何形式的文件共享系统与同事共享数据文件的简单方法。
需要一个数据跟踪系统来进行透明的审计或结果复现。还需要一个数据共享系统,以便将项目团队扩展到多位同事。
这一点或许已经很明显,但使用 Git 或其他源代码管理系统 (SCM) 来存储机器学习项目中使用的数据文件是不切实际的。如果存储代码和配置文件的 SCM 也能存储数据文件,那将是一件非常方便的事情。Git-LFS 也不是一个好的解决方案。我之前的文章《为什么 Git 和 Git-LFS 不足以解决机器学习可复现性危机》详细阐述了其中的原因。
一些库提供 API 来简化对远程存储文件的处理,并管理文件上传到远程存储或从远程存储上传文件的操作。虽然这对于共享访问远程数据集很有用,但它并不能解决本文描述的问题。首先,这是一种嵌入式配置,因为文件名被嵌入到软件中。任何将配置设置嵌入源代码的程序,在其他情况下都更难重用。其次,它无法关联每个脚本版本所使用的数据文件。
请参考以下 MLFlow 示例代码:
mlflow.pytorch.load_model("runs:/<mlflow_run_id>/run-relative/path/to/model")
这支持多种备选文件访问方案,包括 S3 等云存储系统。此处的示例从“运行”区域加载一个文件,在本例中是一个训练好的模型。每次执行“一段数据科学代码”时,都会生成一个 MLFlow“运行”。您可以配置“运行”数据的存储位置,并且显然,每次运行都会生成一个“运行 ID”,用于索引数据存储区域。
这看起来很有用,因为它会自动将数据与存储代码和配置文件的 SCM 仓库中的提交关联起来。此外,由于 MLFlow API 支持多种语言,因此您不仅限于 Python。
DVC 采用了不同的方法。它不是将文件 API 集成到机器学习脚本中,而是让脚本直接使用普通的文件系统 API 来输入和输出文件。例如:
model = torch.load(‘path/to/model.pkl’)
理想情况下,这个路径名应该从命令行传入。关键在于,代码无需任何特殊操作,因为 DVC 会在训练或验证模型的代码上下文之外提供其值。
DVC 使这一过程变得透明,因为数据文件版本控制与 Git 结合使用。可以使用以下命令将文件或目录置于 DVC 控制之下:
$ dvc add path/to/model.pkl
数据存储在工作目录中,位置很方便。浏览不同运行结果就像浏览 Git 历史记录一样简单。查看特定结果也只需运行命令git checkout,系统会调用 DVC 来确保正确的数据文件已链接到工作区。
DVC 会创建一个“DVC 文件”来跟踪每个文件或目录,并将其插入到工作区中。它们有两个用途,一是跟踪数据和模型文件,二是记录工作流命令,我们将在下一节中详细介绍。
这些 DVC 文件记录了被跟踪文件或目录的 MD5 校验和。它们会被提交到 Git 工作区,因此,每次 Git 提交时,DVC 文件都会记录工作区中每个文件的校验和。在后台,DVC 使用所谓的“DVC 缓存目录”来存储每个文件的多个实例。这些实例通过校验和进行索引,并使用引用链接或符号链接链接到工作区。当 DVC 响应操作时git checkout,它能够根据 DVC 文件中记录的校验和快速重新排列工作区中的链接文件。
DVC 支持远程缓存目录,用于与他人共享数据和模型。
$ dvc remote add remote1 ssh://user@host.name/path/to/dir
$ dvc push
$ dvc pull
DVC 远程缓存是一个存储池,数据可以通过它进行共享。它支持多种存储服务,包括 S3 和其他服务、HTTP 和 FTP。创建 DVC 远程缓存非常简单。`dvc push`和 ` dvc pushdvc get` 命令与 ` dvc get` 命令dvc pull非常相似。`dvc push` 将数据发送到远程 DVC 缓存,而我们使用 `dvc get` 从 DVC 缓存中检索数据。git pushgit pulldvc pull
机器学习项目的原则性工作流程描述
讨论的另一个方面是如何最好地描述机器学习项目中使用的工作流程或流水线。我们是将所有步骤都塞进一个程序里吗?还是使用多个工具?
将工作流实现为管道或有向无环图,并采用可重用的命令,以命令行参数的形式提供配置选项,从而实现最大的灵活性。这与 Unix 的理念非常相似,即使用功能明确、范围窄且协同工作的小型工具,并通过命令行选项或环境变量来定制其行为,并可根据需要灵活组合使用。这种理念背后有着悠久的历史渊源。
相比之下,许多机器学习框架采用不同的方法,即编写一个单独的程序来驱动特定项目的工作流程。该程序可能首先将数据拆分为训练集和验证集,然后进行模型训练和验证。这使得我们在其他项目中重用代码的机会非常有限。
将机器学习项目构建成流水线有一些好处。
- 管理复杂性:将步骤作为单独的命令来实现可以提高透明度,并使您能够专注于特定目标。
- 优化执行:如果文件没有更改,则能够跳过不需要重新运行的步骤。
- 可重用性:在多个项目之间使用同一工具的可能性。
- 可扩展性:不同的工具可以由不同的团队成员独立开发。
在 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。每个函数的第一个参数都是entrypoint在MLproject文件中定义的。入口点包含环境设置、要运行的命令以及要传递给该命令的选项。例如:
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 项目的设置更加复杂。
- 如何避免运行不必要的任务?在许多机器学习项目中,训练模型需要数天时间。只有在必要时,例如数据变更、参数变更或算法变更时,才应该消耗这些资源。
DVC 采用了一种可与常规命令行工具配合使用的方法,无需设置服务器或编写驱动程序。DVC 支持使用前面提到的 DVC 文件集将工作流定义为有向无环图 (DAG)。
我们之前提到过 DVC 文件,它与添加到工作区的文件相关联。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 可以计算命令的执行顺序。
所有输出(包括训练好的模型)都会像工作区中的任何其他数据文件一样,自动跟踪到 DVC 缓存中。
由于 DVC 会计算校验和,因此它可以检测到已更改的文件。当用户请求 DVC 重新执行流程时,它只会执行发生更改的阶段。如果所有输入文件均未发生更改,DVC 可以跳过您为期三天的模型训练任务。
所有操作都在常规命令行中执行,无需设置服务器。如果您希望在云计算环境或配备 GPU 的服务器上执行,只需将代码和数据部署到该服务器,然后在该服务器的命令行中执行 DVC 命令即可。
结论
我们在探索改进机器学习实践的一些原则方面取得了长足的进步。正如许多人所认识到的,机器学习领域需要更好的管理工具,以便机器学习团队能够更高效、更可靠地工作。
能够复现结果意味着其他人可以评估你的工作,或与你合作进行进一步开发。可复现性有许多前提条件,包括能够检查系统的每个部分,以及能够精确地重新运行软件和输入数据。
有些机器学习项目使用的工具拥有美观的用户界面,例如 Jupyter Notebook。这类工具在机器学习工作中自有其用武之地。然而,图形用户界面 (GUI) 工具与本文讨论的原则并不契合。命令行工具非常适合处理后台运行的任务,并且能够轻松满足我们概述的所有原则,而典型的 GUI 则会与其中大部分原则相冲突。
正如我们在本文中所看到的,一些工具和实践可以借鉴自常规软件工程。然而,机器学习项目的需求决定了需要使用更适合其用途的工具。一些值得推荐的工具包括MLFlow、DVC、ModelDb,甚至还有Git-LFS(尽管我们之前对它有所批评)。
文章来源:https://dev.to/robogeek/principled-machine-learning-4eho


