1.3 数据科学库套件
接下来进入Ray的第二层,本节将简要介绍Ray附带的所有数据科学库。为此,我们首先从整体上了解什么是数据科学。一旦理解了数据科学的背景,就更容易学习Ray的高级库,并了解它们的功能。
1.3.1 Ray AIR和数据科学工作流
近年来,数据科学(Data Science,DS)发展迅速,连该术语本身都变得有些难以捉摸,可以在网上找到许多关于数据科学的定义,这些定义都有一定的道理[11]。我们将“数据科学”定义为利用数据获得洞见,并创建实际应用。这是在实践和应用层面做出的广义定义,重点是理解事物并获得新知。从这个意义上说,将这个领域的从业者描述为“数据科学家”,就像将黑客描述为“计算机科学家”一样不准确[12]。
从宏观上看,数据科学是一个不断迭代的过程,涉及提出需求、收集和处理数据、搭建和评估模型、部署模型。机器学习不一定属于这个过程,但通常是环节之一。如果涉及机器学习,可以进一步列出以下步骤:
处理数据
为了训练机器学习模型,需要将数据转换为模型能够理解的格式。将数据经过转换、选择后再输入模型的过程通常被称为特征工程。特征工程可能非常烦琐,如果你能熟练使用常用工具进行数据处理,可以节省许多精力。
训练模型
在机器学习中,需要利用处理好的数据训练算法。这个步骤需要选择合适的算法,如果你能从多种算法中选出恰当的算法,会很有帮助。
调优超参数
在模型训练步骤中,需要对模型进行参数调优。大多数机器学习模型中还有一组被称为超参数的参数,可以在训练之前对其进行修改。这些参数会对最终模型的性能产生重大影响,需要进行适当的调优。有一些很好的工具可以实现自动化超参数调优。
部署模型
训练好的模型需要部署。部署模型意味着用户通过任何途径都可以访问该模型。在原型中,通常使用简单的HTTP服务器,此外也有许多专门用于机器学习模型部署的软件包。
这个列表并不完整,构建机器学习应用还涉及许多其他内容[13]。但是,可以肯定的是,这4个步骤对于使用机器学习的数据科学项目成功与否至关重要。
Ray为这4个与机器学习息息相关的步骤提供了专门的库。具体而言,你可以使用Ray Dataset处理数据,使用Ray Train进行分布式模型训练,使用Ray RLlib运行强化学习计算任务,使用Ray Tune高效调优超参数,并使用Ray Serve部署模型。而且,Ray在设计这4个组件时也是基于分布式理念的。
另外,所有这些步骤都从属于训练模型的过程,极少单独使用。你希望Ray的库不仅支持无缝协同工作,还能使用高度一致的API,因此极具优势。Ray AIR就是为此设计的,它能提供统一的运行时和API,还能随时进行扩展。图1.2展示了AIR的所有组件。
图1.2:Ray AIR的组件
虽然本章篇幅有限,无法详细介绍Ray AIR的API(详见第10章),但会介绍Ray AIR中的所有组件。
1.3.2 处理数据
我们首先介绍Ray Datasets。该库包含一个名为Dataset的数据结构、多种用于从各种格式和系统加载数据的连接器、用于转换数据集的API,以及使用它们构建数据处理管道的方法,另外还有用于和其他数据处理框架进行集成的插件。Dataset是基于强大的Arrow框架(https://arrow.apache.org)创建的[14]。
要使用Ray Dataset,你需要为Python安装Arrow,使用命令pip install pyarrow进行安装。下面的示例使用Python数据结构创建了一个位于本地Ray集群的分布式Dataset。具体来说,利用包含字符串name和整数值data的Python字典,创建包含10000个条目的数据集:
❶ 通过ray.data模块的from_items方法创建Dataset。
❷ 输出Dataset的前5项。
Dataset的show方法表示输出其中的值。命令行中应该输出5个元素,如下所示:
对于输出的数据,可以进行什么处理呢?Dataset API大量使用了函数式编程,这是因为函数式编程非常适合进行数据转换。
尽管Python 3隐藏了一些函数式编程的功能,但你可能熟悉其中一些,如map、filter、flat_map等。如果不熟悉,这些方法也很容易掌握:map将数据集的每个元素并行转换为其他值;filter根据布尔过滤函数删除数据点;flat_map首先用map将值进行映射,然后“展平”结果。例如,如果map生成了一个嵌套列表,flat_map将展平嵌套的列表,只返回列表。使用这三个API[15],我们查看如何操作数据集ds:
❶ 将ds的每一行map为仅保留其数据项的平方值。
❷ 使用filter过滤掉squares中的奇数,只保留偶数(总共5000个元素)。
❸ 使用flat_map将剩余的值变为立方值。
❹ take(10)表示退出Ray,并返回一个包含这些值的Python列表,支持输出这些值。
Dataset转换的缺点是每个步骤都是同步执行的。虽然在这个示例中不会导致什么问题,但是对于复杂的任务,例如混合读取文件和处理数据,你可能希望将子任务进行重叠,此时可以使用DatasetPipeline。我们将前面的示例重写为管道:
❶ 通过在Dataset上调用.window(),将其转换为管道。
❷ 管道支持链式操作,结果不变。
关于Ray Dataset还有很多内容值得探讨,特别是它与数据处理系统的集成,第6章将对此进行详细讨论。
1.3.3 训练模型
接下来介绍Ray的分布式训练库,一个专门用于强化学习,另一个主要用于监督学习。
使用Ray RLlib进行强化学习
首先介绍使用Ray RLlib库进行强化学习。Ray RLlib基于机器学习框架TensorFlow和PyTorch,这两个框架从概念上越来越相近,不存在太大差异,开发者可自由进行选择。本书示例既使用了TensorFlow,也使用了PyTorch,方便读者进行学习。
可以通过命令pip install tensorflow安装TensorFlow[16]。要运行代码示例,还需要使用pip install安装gym库。
使用RLlib运行示例的简单方法是使用命令行工具rllib,我们在运行pip install "ray[rllib]"时已经隐式安装了它。在第4章运行更复杂的示例时,主要依赖其Python API,本小节将带你初步了解如何使用RLlib运行RL项目。
下面讨论一个经典的控制问题,即在小车上平衡杆子。假设有一个如图1.3所示的杆子,固定在小车的连接处,杆子受到重力的作用。小车可以沿着无摩擦轨道自由移动,我们可以通过给小车向左或向右的力来操纵小车。如果施加的力恰当,杆子将保持直立。对于每个时间步,如果杆子没有倒下,那么我们都会获得1的奖励。我们的目标是训练强化学习算法,以获得更高的奖励。
图1.3:通过向左或向右施加力来控制连接在小车上的杆子
具体而言,我们希望训练一个强化学习智能体,它可以执行两个动作,即向左或向右推动小车,并观察以这种方式与环境交互时会发生什么,并通过最大化奖励从经验中学习。
为了使用Ray RLlib解决这个问题,可以使用一个所谓的调优示例,这是一个预先配置的算法,可以很好地运行给定的问题。可以通过简单的命令运行调优示例。RLlib中有许多这样的示例,可以使用rllib example list列出示例。
其中一个可用的示例是cartpole-ppo,这是一个经过调优的示例,它使用PPO算法解决了平衡车杆问题。具体而言,它解决了OpenAI Gym中的CartPole-v1环境(https://oreil.ly/YNxoz)问题。你可以通过输入rllib example get cartpole-ppo查看此示例的配置,该命令首先从GitHub下载示例文件,然后输出其配置。该配置是以YAML文件格式进行编码的,如下所示:
❶ CartPole-v1环境模拟了车杆问题。
❷ 使用一种强大的强化学习算法,称为近端策略优化算法(Proximal Policy Optimization,PPO)。
❸ 奖励达到150则停止实验。
❹ 为了使PPO算法对该问题生效,需要对强化学习进行配置。
目前,配置文件的详细内容并不重要,这里不进行讨论。重要的是指定Cartpole-v1环境和配置强化学习,以确保训练过程正常进行。运行此配置不需要任何特殊的硬件,通常可在几分钟内完成。要训练此示例,需要使用pip install pygame安装PyGame依赖项,然后运行以下命令:
运行该命令后,RLlib会创建具有命名的项目并记录重要的指标,如reward或episode_reward_mean。在训练运行的输出中,你还应该看到有关机器(loc,表示主机名和端口)以及训练运行状态的信息。如果运行状态为TERMINATED,但在日志中未看到成功运行的项目,则可能出现了问题。下面是训练运行的示例输出:
当成功完成训练时,可以看到如下输出:
现在,可以从任何检查点评估训练好的算法,例如运行以下命令:
默认情况下,本地Ray检查点文件夹是~/ray-results。对于我们使用的训练配置,<checkpoint-path>应该是形式为~/ray_results/cartpole-ppo/PPO_CartPole-v1_<experiment_id>的路径。在训练过程中,中间和最终的模型检查点会生成到此文件夹中。
为了评估训练好的强化学习算法的性能,现在可以从检查点进行评估,只需复制前面示例训练运行输出的命令即可:
运行此命令将输出评估结果,即训练好的强化学习算法在CartPole-v1环境中获得的奖励。
RLlib还支持许多其他功能,详见第4章。这个示例的目的是展示如何通过example和evaluate命令轻松使用RLlib和rllib命令行工具。
使用Ray Train进行分布式训练
Ray RLlib专注于强化学习,但如果你需要为其他类型的机器学习(如监督学习)训练模型,该怎么办呢?这种情况下可以使用Ray Train。目前,我们对诸如TensorFlow之类的框架介绍不多,因此无法提供简洁且信息丰富的Ray Train示例。如果你对分布式训练感兴趣,可以直接跳到第6章。
1.3.4 调优超参数
正如其名,Ray Tune可用于调优各种参数,专门用于为机器学习模型寻找适合的超参数。典型步骤如下:
● 假设运行一个计算量极大的训练函数。在机器学习中,训练过程可能会持续几天甚至几周,但我们假设只需要几分钟。
● 作为训练结果,要对目标函数进行计算。通常情况下,要么希望最大化收益,要么希望最小化性能损失。
● 不过,训练函数可能依赖于某些参数,即超参数,这些参数会影响目标函数的值。
● 你可能只知道如何调优个别超参数,但调优所有超参数很困难。即使将所有参数限制在合理的范围内,测试各种组合通常也是不切实际的,因为训练函数成本过高。
为了高效地采样超参数并获得“足够好”的结果,可以使用超参数调优(Hyperparameter Optimization,HPO)解决此问题,Ray Tune提供了一套庞大的算法来进行超参数调优。接下来,我们使用Ray Tune作为示例处理刚刚的问题。重点仍然是Ray及其API,而不是特定的机器学习任务(我们目前只是进行模拟):
❶ 模拟一个大计算量的训练函数,该函数依赖于从config读取的两个超参数x和y。
❷ 睡眠10s以模拟训练和计算目标函数,然后将分数报告给tune。
❸ 目标函数计算x和y的平方均值,并返回该项的平方根。这种类型的目标函数在机器学习中非常常见。
❹ 使用tune.run初始化training_function上的超参数调优。
❺ 为tune提供x和y参数空间以进行搜索。
这段代码的输出结构与RLlib示例非常类似,这是因为RLlib(和许多其他Ray库一样)在底层使用了Ray Tune。如果仔细观察的话,你会看到等待执行的PENDING任务,以及RUNNING和TERMINATED任务。Tune会自动进行选择、调度和执行训练函数。
具体而言,这个Tune示例会找到参数x和y的最佳选择,用于最小化training_function的给定objective。尽管刚开始接触目标函数可能觉得比较难,因为计算的是x和y的平方和,所以所有值都是非负的。这意味着在x=0和时获得最小值,此时目标函数的值为。
我们对所有可能的参数组合进行网格搜索。由于显式地传入了5个可能的x值和y值,因此总共有25个组合被输入训练函数。由于指示训练函数睡眠10s,因此顺序测试所有超参数的组合将总共需要超过4min。Ray使用了并行化计算,整个实验只花费了约35s的时间,但根据每个人运行的环境,可能需要更长时间。
现在,假设每次训练都需要花费数小时,并且有20个而不是2个超参数。这使得网格搜索变得不可行,特别是在对参数范围没有明确评估的情况下。在这种情况下,必须使用Ray Tune中更复杂的HPO方法,详见第5章。
1.3.5 部署模型
Ray Serve是专门用于部署模型的高级库。为了查看Ray Serve的实际示例,需要一个已经训练好的机器学习模型,我们可以从互联网上找到许多有趣并且已经训练好的模型。例如,Hugging Face提供了许多可以直接在Python中下载的模型。接下来使用GPT-2语言模型,它以文本作为输入并生成后续文本或补全文本。例如,你可以提出一个问题,GPT-2将尝试为其提供完整的答案。
部署模型可以让模型方便访问。你可能不知道如何在计算机上加载和运行TensorFlow模型,但一定知道如何提出问题。模型部署隐藏了解决方案的实现细节,让用户专注于提供输入和理解模型的输出。
要运行示例,需要运行pip install transformers安装Hugging Face库,该库包含接下来要使用的模型[17]。安装好后,可以导入并启动Ray的serve库实例,加载并部署GPT-2模型,并询问它关于生命的意义,如下所示:
❶ 本地启动serve。
❷ @serve.deployment装饰器将带有request参数的函数转化为serve部署。
❸ 虽然对每个请求加载model函数的language_model效率不高,但这样能以最快的方式展示部署。
❹ 指示模型最多用100个字符继续查询。
❺ 正式部署模型,以便可以通过HTTP接收请求。
❻ 使用requests库获取响应。
在第9章中,你将学习如何在各种场景中正确部署模型,本小节只是鼓励读者尝试使用此示例并测试不同的查询。重复运行代码的最后两行,几乎每次都会得到不同的答案。以下是一个问题集合,包含许多问题:
通过以上介绍,我们对Ray的数据科学库做了快速浏览,这是Ray的第二层。本质上,本章介绍的所有高级Ray库都是Ray Core API的扩展。基于API,可以相对容易地创建Ray的扩展,其中包括一些本书无法完整讨论的扩展。例如,相对较新的Ray Workflows(https://oreil.ly/XUT7y)支持使用Ray定义和运行需要长时间运行的应用程序。
在结束本章之前,我们简要了解围绕Ray的蓬勃发展的生态。