随着机器学习的算法和技术的进步,越来越多的机器学习应用程序需要多台机器,并且必须利用并行性。但是,在集群上进行机器学习的基础设施仍然是特设的。尽管针对特定用例(如 参数服务器或超参数搜索)和AI(人工智能)之外的高质量分布式系统(如Hadoop或Spark)提供了良好的解决方案,但在边界开发算法的从业者往往从头构建自己的系统基础架构。这相当于多余的努力。
举例来说,采取一个概念上简单的算法,如强化学习的进化策略。该算法大约有十几行伪代码,其Python实现并不多。但是,在较大的机器或集群上高效地运行算法需要更多的软件工程。作者的实现涉及数千行代码,并且必须定义通信协议,消息序列化和反序列化策略以及各种数据处理策略。
Ray(高性能分布式执行框架)的其中一个目标是使从业者能够将一个运行在笔记本电脑上的原型算法,用相对较少的代码行使之成为一个高性能的分布式应用程序并在集群(或多个单核机器)上高效运行。这样的框架应该包括手动优化系统的性能优势,而不需要用户对调度,数据传输和机器故障进行推理。
与深度学习框架的关系: Ray与TensorFlow,PyTorch和MXNet等深度学习框架完全兼容,在许多应用中与Ray一起使用一个或多个深度学习框架是很自然的(例如,我们的强化学习库大量地使用TensorFlow和PyTorch)。
与其他分布式系统的关系:目前使用了许多流行的分布式系统,但是其中大多数并不是用AI应用程序构建的,且缺乏支持所需的性能以及表示AI应用程序的API(应用程序编程接口)。目前的分布式系统中缺少以下功能(在各种组合中):
一个嵌套并行的简单例子。一个应用程序运行两个并行的实验(每个都是一个长期运行的任务),每个实验运行一些并行的模拟(每个都是一个任务)。
有两种使用Ray的主要方法:通过其较低级别的API和更高级别的库。较高级别的库建立在较低级别的API之上。目前这些包括Ray RLlib,一个可扩展的强化学习库和Ray.tune,一个高效的分布式超参数搜索库。
Ray API的目标是自然地表达非常普遍的计算模式和应用程序,而不局限于像MapReduce这样的固定模式。
Ray应用程序或作业中的基础基元是一个动态任务图。这与TensorFlow中的计算图非常不同。在TensorFlow中,一个计算图代表一个神经网络,并且在单个应用程序中执行多次,而在Ray中,任务图代表整个应用程序,并且只执行一次。任务图不是事先知道的。它是在应用程序运行时动态构建的,执行一个任务可能会触发创建更多任务。
一个计算图的例子。白色的椭圆形表示任务,蓝色的方框表示对象。箭头表示任务取决于对象或任务创建对象。
任意的Python函数都可以作为任务执行,并且可以任意地依赖于其他任务的输出。这在下面的例子中说明。
# 定义两个远程函数。 调用这些函数创造任务
# 这是远程执行的。
@ray.remote
def multiply(x, y):
return np.dot(x, y)
@ray.remote
def zeros(size):
return np.zeros(size)
# 并行启动两个任务。这些立即返回预期结果,且
# 任务在后台执行。
x_id zeros.remote((100, 100))
y_id = zeros.remote((100, 100))
# 开始第三个任务。在前两个任务完成之前,不会安排这一任务.
z_id = multiply.remote(x_id, y_id)
# 得到结果。直到第三个任务完成才可以得到。
z = ray.get(z_id)
只有上述远程功能和任务不能完成的一件事情是让多个任务在相同的共享可变状态下运行。这出现在机器学习中的多处上下文中,其中共享状态可以是模拟器的状态、神经网络的权重、或完全其他的东西。Ray使用actor抽象来封装多个任务间共享的可变状态。下面是一个如何用Atari模拟器做到这一点的虚构示例。
import gym
@ray.remote
class Simulator(object):
def __init__(self):
self.env = gym.make("Pong-v0")
self.env.reset()
def step(self, action):
return self.env.step(action)
# 创建一个模拟器,这将启动一个远程进程为
# 该actor运行所有方法。
simuator = Simulator.remote()
observations = []
for _ in range(4):
# 在模拟器中执行操作0.此调用不阻塞并且
# 返回一个预期结果
observations.append(simulator.step.remote(0))
虽然简单,一个actor可以用非常灵活的方式使用。例如,actor可以封装模拟器或神经网络策略,并可用于分布式培训(如 使用参数服务器)或在实时应用程序中进行策略服务。
左:一个为许多客户端进程提供预测/操作的actor。 右:多个参数服务器actor使用多个工作进程执行分布式培训。
一个参数服务器可以被实现为Ray actor,如下所示:
@ray.remote
class ParameterServer(object):
def __init__(self, keys, values):
# 这些值将会发生改变,因此我们需要创建一个本地副本。
values = [value.copy() for value in values]
self.parameters = dict(zip(keys, values))
def get(self, keys):
return [self.parameters[key] for key in keys]
def update(self, keys, values):
# 这个更新函数使现有的值增加,但是更新
# 函数可以被任意定义
for key, value in zip(keys, values):
self.parameters[key] += value
看一个更完整的例子。
要实例化参数服务器,请执行以下操作。
parameter_server = ParameterServer.remote(initial_keys, initial_values)
要创建连续检索和更新参数的四个长时间运行的工作进程,请执行以下操作:
@ray.remote
def worker_task(parameter_server):
while True:
keys = ['key1', 'key2', 'key3']
# 获取最新的参数。
values = ray.get(parameter_server.get.remote(keys))
# 计算一些需更新参数。
updates = …
# 更新参数。
parameter_server.update.remote(keys, updates)
# 启动四个长期任务。
for _ in range(4):
worker_task.remote(parameter_server)
Ray RLlib是一个可扩展的强化学习库,可在许多机器上运行。它可以通过示例培训脚本以及Python API使用。它目前包括以下的实现:
我们正在努力增加更多的算法。RLlib与OpenAI体育馆完全兼容。
Ray.tune是一个高效的分布式超参数搜索库。它提供了用于深度学习,强化学习和其他计算密集型任务的Python API。下面是个说明用法的虚构示例:
from ray.tune import register_trainable, grid_search, run_experiments
# 优化功能。超参数是在配置
# 参数。
def my_func(config, reporter):
import time, numpy as np
i = 0
while True:
reporter(timesteps_total=i, mean_accuracy=(i ** config['alpha']))
i += config['beta']
time.sleep(0.01)
register_trainable('my_func', my_func)
run_experiments({
'my_experiment': {
'run': 'my_func',
'resources': {'cpu': 1, 'gpu': 0},
'stop': {'mean_accuracy': 100},
'config': {
'alpha': grid_search([0.2, 0.4, 0.6]),
'beta': grid_search([1, 2]),
},
}
})
正在进行的结果可以使用Tensorboard和rllab的VisKit(或者您可以直接阅读JSON日志)等工具进行实时可视化。Ray.tune支持网格搜索、随机搜索和更复杂的早期停止算法,如HyperBand。
有关Ray的更多信息,请查看以下链接。
Ray可以用pip install ray安装
。我们鼓励你尝试Ray,看看你的想法。如果您对我们有任何反馈意见,请通过我们的邮件列表ray-dev@googlegroups.com告诉我们。