mirror of
https://github.com/wassname/ray.git
synced 2026-08-07 11:27:43 +08:00
Change Python examples in documentation to use 4 space indentation. (#736)
* Ray doc - changed python indentation to 4 spaces in documentation files actors.rst, api.rst, and example-*.rst * Ray documentation - changed Python to 4 space indentation for files install-*.rst, installation-troubleshooting.rst, internals-overview.rst, serialization.rst, troubleshootin.rst, tutorial.rst, using-ray-*.rst
This commit is contained in:
committed by
Robert Nishihara
parent
86a7909149
commit
8fc7dc3ed4
+66
-66
@@ -28,12 +28,12 @@ that instances of the ``Counter`` class will be actors.
|
||||
|
||||
@ray.remote
|
||||
class Counter(object):
|
||||
def __init__(self):
|
||||
self.value = 0
|
||||
def __init__(self):
|
||||
self.value = 0
|
||||
|
||||
def increment(self):
|
||||
self.value += 1
|
||||
return self.value
|
||||
def increment(self):
|
||||
self.value += 1
|
||||
return self.value
|
||||
|
||||
To actually create an actor, we can instantiate this class by calling
|
||||
``Counter.remote()``.
|
||||
@@ -115,15 +115,15 @@ encapsulate the state of these simulators.
|
||||
|
||||
@ray.remote
|
||||
class GymEnvironment(object):
|
||||
def __init__(self, name):
|
||||
self.env = gym.make(name)
|
||||
self.env.reset()
|
||||
def __init__(self, name):
|
||||
self.env = gym.make(name)
|
||||
self.env.reset()
|
||||
|
||||
def step(self, action):
|
||||
return self.env.step(action)
|
||||
def step(self, action):
|
||||
return self.env.step(action)
|
||||
|
||||
def reset(self):
|
||||
self.env.reset()
|
||||
def reset(self):
|
||||
self.env.reset()
|
||||
|
||||
We can then instantiate an actor and schedule a task on that actor as follows.
|
||||
|
||||
@@ -144,19 +144,19 @@ a neural net.
|
||||
import tensorflow as tf
|
||||
|
||||
def construct_network():
|
||||
x = tf.placeholder(tf.float32, [None, 784])
|
||||
y_ = tf.placeholder(tf.float32, [None, 10])
|
||||
x = tf.placeholder(tf.float32, [None, 784])
|
||||
y_ = tf.placeholder(tf.float32, [None, 10])
|
||||
|
||||
W = tf.Variable(tf.zeros([784, 10]))
|
||||
b = tf.Variable(tf.zeros([10]))
|
||||
y = tf.nn.softmax(tf.matmul(x, W) + b)
|
||||
W = tf.Variable(tf.zeros([784, 10]))
|
||||
b = tf.Variable(tf.zeros([10]))
|
||||
y = tf.nn.softmax(tf.matmul(x, W) + b)
|
||||
|
||||
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1]))
|
||||
train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)
|
||||
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
|
||||
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
|
||||
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1]))
|
||||
train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)
|
||||
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
|
||||
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
|
||||
|
||||
return x, y_, train_step, accuracy
|
||||
return x, y_, train_step, accuracy
|
||||
|
||||
We can then define an actor for this network as follows.
|
||||
|
||||
@@ -168,19 +168,19 @@ We can then define an actor for this network as follows.
|
||||
# ray.remote without any arguments and no parentheses.
|
||||
@ray.remote(num_gpus=1)
|
||||
class NeuralNetOnGPU(object):
|
||||
def __init__(self):
|
||||
# Set an environment variable to tell TensorFlow which GPUs to use. Note
|
||||
# that this must be done before the call to tf.Session.
|
||||
os.environ["CUDA_VISIBLE_DEVICES"] = ",".join([str(i) for i in ray.get_gpu_ids()])
|
||||
with tf.Graph().as_default():
|
||||
with tf.device("/gpu:0"):
|
||||
self.x, self.y_, self.train_step, self.accuracy = construct_network()
|
||||
# Allow this to run on CPUs if there aren't any GPUs.
|
||||
config = tf.ConfigProto(allow_soft_placement=True)
|
||||
self.sess = tf.Session(config=config)
|
||||
# Initialize the network.
|
||||
init = tf.global_variables_initializer()
|
||||
self.sess.run(init)
|
||||
def __init__(self):
|
||||
# Set an environment variable to tell TensorFlow which GPUs to use. Note
|
||||
# that this must be done before the call to tf.Session.
|
||||
os.environ["CUDA_VISIBLE_DEVICES"] = ",".join([str(i) for i in ray.get_gpu_ids()])
|
||||
with tf.Graph().as_default():
|
||||
with tf.device("/gpu:0"):
|
||||
self.x, self.y_, self.train_step, self.accuracy = construct_network()
|
||||
# Allow this to run on CPUs if there aren't any GPUs.
|
||||
config = tf.ConfigProto(allow_soft_placement=True)
|
||||
self.sess = tf.Session(config=config)
|
||||
# Initialize the network.
|
||||
init = tf.global_variables_initializer()
|
||||
self.sess.run(init)
|
||||
|
||||
To indicate that an actor requires one GPU, we pass in ``num_gpus=1`` to
|
||||
``ray.remote``. Note that in order for this to work, Ray must have been started
|
||||
@@ -205,45 +205,45 @@ We can put this all together as follows.
|
||||
ray.init(num_gpus=8)
|
||||
|
||||
def construct_network():
|
||||
x = tf.placeholder(tf.float32, [None, 784])
|
||||
y_ = tf.placeholder(tf.float32, [None, 10])
|
||||
x = tf.placeholder(tf.float32, [None, 784])
|
||||
y_ = tf.placeholder(tf.float32, [None, 10])
|
||||
|
||||
W = tf.Variable(tf.zeros([784, 10]))
|
||||
b = tf.Variable(tf.zeros([10]))
|
||||
y = tf.nn.softmax(tf.matmul(x, W) + b)
|
||||
W = tf.Variable(tf.zeros([784, 10]))
|
||||
b = tf.Variable(tf.zeros([10]))
|
||||
y = tf.nn.softmax(tf.matmul(x, W) + b)
|
||||
|
||||
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1]))
|
||||
train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)
|
||||
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
|
||||
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
|
||||
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1]))
|
||||
train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)
|
||||
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
|
||||
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
|
||||
|
||||
return x, y_, train_step, accuracy
|
||||
return x, y_, train_step, accuracy
|
||||
|
||||
@ray.remote(num_gpus=1)
|
||||
class NeuralNetOnGPU(object):
|
||||
def __init__(self, mnist_data):
|
||||
self.mnist = mnist_data
|
||||
# Set an environment variable to tell TensorFlow which GPUs to use. Note
|
||||
# that this must be done before the call to tf.Session.
|
||||
os.environ["CUDA_VISIBLE_DEVICES"] = ",".join([str(i) for i in ray.get_gpu_ids()])
|
||||
with tf.Graph().as_default():
|
||||
with tf.device("/gpu:0"):
|
||||
self.x, self.y_, self.train_step, self.accuracy = construct_network()
|
||||
# Allow this to run on CPUs if there aren't any GPUs.
|
||||
config = tf.ConfigProto(allow_soft_placement=True)
|
||||
self.sess = tf.Session(config=config)
|
||||
# Initialize the network.
|
||||
init = tf.global_variables_initializer()
|
||||
self.sess.run(init)
|
||||
def __init__(self, mnist_data):
|
||||
self.mnist = mnist_data
|
||||
# Set an environment variable to tell TensorFlow which GPUs to use. Note
|
||||
# that this must be done before the call to tf.Session.
|
||||
os.environ["CUDA_VISIBLE_DEVICES"] = ",".join([str(i) for i in ray.get_gpu_ids()])
|
||||
with tf.Graph().as_default():
|
||||
with tf.device("/gpu:0"):
|
||||
self.x, self.y_, self.train_step, self.accuracy = construct_network()
|
||||
# Allow this to run on CPUs if there aren't any GPUs.
|
||||
config = tf.ConfigProto(allow_soft_placement=True)
|
||||
self.sess = tf.Session(config=config)
|
||||
# Initialize the network.
|
||||
init = tf.global_variables_initializer()
|
||||
self.sess.run(init)
|
||||
|
||||
def train(self, num_steps):
|
||||
for _ in range(num_steps):
|
||||
batch_xs, batch_ys = self.mnist.train.next_batch(100)
|
||||
self.sess.run(self.train_step, feed_dict={self.x: batch_xs, self.y_: batch_ys})
|
||||
def train(self, num_steps):
|
||||
for _ in range(num_steps):
|
||||
batch_xs, batch_ys = self.mnist.train.next_batch(100)
|
||||
self.sess.run(self.train_step, feed_dict={self.x: batch_xs, self.y_: batch_ys})
|
||||
|
||||
def get_accuracy(self):
|
||||
return self.sess.run(self.accuracy, feed_dict={self.x: self.mnist.test.images,
|
||||
self.y_: self.mnist.test.labels})
|
||||
def get_accuracy(self):
|
||||
return self.sess.run(self.accuracy, feed_dict={self.x: self.mnist.test.images,
|
||||
self.y_: self.mnist.test.labels})
|
||||
|
||||
|
||||
# Load the MNIST dataset and tell Ray how to serialize the custom classes.
|
||||
|
||||
+16
-15
@@ -51,9 +51,9 @@ processes may be shared between multiple scripts and multiple users. To do this,
|
||||
you simply need to know the address of the cluster's Redis server. This can be
|
||||
done with a command like the following.
|
||||
|
||||
.. code-block:: python
|
||||
.. code-block:: python
|
||||
|
||||
ray.init(redis_address="12.345.67.89:6379")
|
||||
ray.init(redis_address="12.345.67.89:6379")
|
||||
|
||||
In this case, you cannot specify ``num_cpus`` or ``num_gpus`` in ``ray.init``
|
||||
because that information is passed into the cluster when the cluster is started,
|
||||
@@ -87,7 +87,7 @@ Note that arguments to remote functions can be values or object IDs.
|
||||
|
||||
@ray.remote
|
||||
def f(x):
|
||||
return x + 1
|
||||
return x + 1
|
||||
|
||||
x_id = f.remote(0)
|
||||
ray.get(x_id) # 1
|
||||
@@ -102,7 +102,7 @@ passing the ``num_return_vals`` argument into the remote decorator.
|
||||
|
||||
@ray.remote(num_return_vals=2)
|
||||
def f():
|
||||
return 1, 2
|
||||
return 1, 2
|
||||
|
||||
x_id, y_id = f.remote()
|
||||
ray.get(x_id) # 1
|
||||
@@ -121,10 +121,11 @@ IDs.
|
||||
|
||||
@ray.remote
|
||||
def f():
|
||||
return {'key1': ['value']}
|
||||
return {'key1': ['value']}
|
||||
|
||||
# Get one object ID.
|
||||
ray.get(f.remote()) # {'key1': ['value']}
|
||||
|
||||
# Get a list of object IDs.
|
||||
ray.get([f.remote() for _ in range(2)]) # [{'key1': ['value']}, {'key1': ['value']}]
|
||||
|
||||
@@ -169,7 +170,7 @@ copied multiple times.
|
||||
|
||||
@ray.remote
|
||||
def f(x):
|
||||
pass
|
||||
pass
|
||||
|
||||
x = np.zeros(10 ** 6)
|
||||
|
||||
@@ -217,8 +218,8 @@ lists is equal to the list passed in to ``ray.wait`` (up to ordering).
|
||||
|
||||
@ray.remote
|
||||
def f(n):
|
||||
time.sleep(n)
|
||||
return n
|
||||
time.sleep(n)
|
||||
return n
|
||||
|
||||
# Start 3 tasks with different durations.
|
||||
results = [f.remote(i) for i in range(3)]
|
||||
@@ -237,17 +238,17 @@ tasks are executing, and whenever one task finishes, a new one is launched.
|
||||
|
||||
@ray.remote
|
||||
def f():
|
||||
return 1
|
||||
return 1
|
||||
|
||||
# Start 5 tasks.
|
||||
remaining_ids = [f.remote() for i in range(5)]
|
||||
# Whenever one task finishes, start a new one.
|
||||
for _ in range(100):
|
||||
ready_ids, remaining_ids = ray.wait(remaining_ids)
|
||||
# Get the available object and do something with it.
|
||||
print(ray.get(ready_ids))
|
||||
# Start a new task.
|
||||
remaining_ids.append(f.remote())
|
||||
ready_ids, remaining_ids = ray.wait(remaining_ids)
|
||||
# Get the available object and do something with it.
|
||||
print(ray.get(ready_ids))
|
||||
# Start a new task.
|
||||
remaining_ids.append(f.remote())
|
||||
|
||||
.. autofunction:: ray.wait
|
||||
|
||||
@@ -270,7 +271,7 @@ The errors will also be accumulated in Redis and can be accessed with
|
||||
|
||||
@ray.remote
|
||||
def f():
|
||||
raise Exception("This task failed!!")
|
||||
raise Exception("This task failed!!")
|
||||
|
||||
f.remote() # An error message will be printed in the background.
|
||||
|
||||
|
||||
+47
-47
@@ -75,33 +75,33 @@ We use a Ray Actor to simulate the environment.
|
||||
|
||||
@ray.remote
|
||||
class Runner(object):
|
||||
"""Actor object to start running simulation on workers.
|
||||
Gradient computation is also executed on this object."""
|
||||
def __init__(self, env_name, actor_id):
|
||||
# starts simulation environment, policy, and thread.
|
||||
# Thread will continuously interact with the simulation environment
|
||||
self.env = env = create_env(env_name)
|
||||
self.id = actor_id
|
||||
self.policy = LSTMPolicy()
|
||||
self.runner = RunnerThread(env, self.policy, 20)
|
||||
self.start()
|
||||
"""Actor object to start running simulation on workers.
|
||||
Gradient computation is also executed on this object."""
|
||||
def __init__(self, env_name, actor_id):
|
||||
# starts simulation environment, policy, and thread.
|
||||
# Thread will continuously interact with the simulation environment
|
||||
self.env = env = create_env(env_name)
|
||||
self.id = actor_id
|
||||
self.policy = LSTMPolicy()
|
||||
self.runner = RunnerThread(env, self.policy, 20)
|
||||
self.start()
|
||||
|
||||
def start(self):
|
||||
# starts the simulation thread
|
||||
self.runner.start_runner()
|
||||
def start(self):
|
||||
# starts the simulation thread
|
||||
self.runner.start_runner()
|
||||
|
||||
def pull_batch_from_queue(self):
|
||||
# Implementation details removed - gets partial rollout from queue
|
||||
return rollout
|
||||
def pull_batch_from_queue(self):
|
||||
# Implementation details removed - gets partial rollout from queue
|
||||
return rollout
|
||||
|
||||
def compute_gradient(self, params):
|
||||
self.policy.set_weights(params)
|
||||
rollout = self.pull_batch_from_queue()
|
||||
batch = process_rollout(rollout, gamma=0.99, lambda_=1.0)
|
||||
gradient = self.policy.get_gradients(batch)
|
||||
info = {"id": self.id,
|
||||
"size": len(batch.a)}
|
||||
return gradient, info
|
||||
def compute_gradient(self, params):
|
||||
self.policy.set_weights(params)
|
||||
rollout = self.pull_batch_from_queue()
|
||||
batch = process_rollout(rollout, gamma=0.99, lambda_=1.0)
|
||||
gradient = self.policy.get_gradients(batch)
|
||||
info = {"id": self.id,
|
||||
"size": len(batch.a)}
|
||||
return gradient, info
|
||||
|
||||
Driver Code Walkthrough
|
||||
-----------------------
|
||||
@@ -116,32 +116,32 @@ global model parameters. The main training script looks like the following.
|
||||
import ray
|
||||
|
||||
def train(num_workers, env_name="PongDeterministic-v3"):
|
||||
# Setup a copy of the environment
|
||||
# Instantiate a copy of the policy - mainly used as a placeholder
|
||||
env = create_env(env_name, None, None)
|
||||
policy = LSTMPolicy(env.observation_space.shape, env.action_space.n, 0)
|
||||
obs = 0
|
||||
# Setup a copy of the environment
|
||||
# Instantiate a copy of the policy - mainly used as a placeholder
|
||||
env = create_env(env_name, None, None)
|
||||
policy = LSTMPolicy(env.observation_space.shape, env.action_space.n, 0)
|
||||
obs = 0
|
||||
|
||||
# Start simulations on actors
|
||||
agents = [Runner(env_name, i) for i in range(num_workers)]
|
||||
# Start simulations on actors
|
||||
agents = [Runner(env_name, i) for i in range(num_workers)]
|
||||
|
||||
# Start gradient calculation tasks on each actor
|
||||
parameters = policy.get_weights()
|
||||
gradient_list = [agent.compute_gradient.remote(parameters) for agent in agents]
|
||||
|
||||
while True: # Replace with your termination condition
|
||||
# wait for some gradient to be computed - unblock as soon as the earliest arrives
|
||||
done_id, gradient_list = ray.wait(gradient_list)
|
||||
|
||||
# get the results of the task from the object store
|
||||
gradient, info = ray.get(done_id)[0]
|
||||
obs += info["size"]
|
||||
|
||||
# apply update, get the weights from the model, start a new task on the same actor object
|
||||
policy.model_update(gradient)
|
||||
# Start gradient calculation tasks on each actor
|
||||
parameters = policy.get_weights()
|
||||
gradient_list.extend([agents[info["id"]].compute_gradient(parameters)])
|
||||
return policy
|
||||
gradient_list = [agent.compute_gradient.remote(parameters) for agent in agents]
|
||||
|
||||
while True: # Replace with your termination condition
|
||||
# wait for some gradient to be computed - unblock as soon as the earliest arrives
|
||||
done_id, gradient_list = ray.wait(gradient_list)
|
||||
|
||||
# get the results of the task from the object store
|
||||
gradient, info = ray.get(done_id)[0]
|
||||
obs += info["size"]
|
||||
|
||||
# apply update, get the weights from the model, start a new task on the same actor object
|
||||
policy.model_update(gradient)
|
||||
parameters = policy.get_weights()
|
||||
gradient_list.extend([agents[info["id"]].compute_gradient(parameters)])
|
||||
return policy
|
||||
|
||||
|
||||
Benchmarks and Visualization
|
||||
|
||||
@@ -28,23 +28,23 @@ perturbed policies in a given environment.
|
||||
|
||||
@ray.remote
|
||||
class Worker(object):
|
||||
def __init__(self, config, policy_params, env_name, noise):
|
||||
self.env = # Initialize environment.
|
||||
self.policy = # Construct policy.
|
||||
# Details omitted.
|
||||
def __init__(self, config, policy_params, env_name, noise):
|
||||
self.env = # Initialize environment.
|
||||
self.policy = # Construct policy.
|
||||
# Details omitted.
|
||||
|
||||
def do_rollouts(self, params):
|
||||
# Set the network weights.
|
||||
self.policy.set_trainable_flat(params)
|
||||
perturbation = # Generate a random perturbation to the policy.
|
||||
def do_rollouts(self, params):
|
||||
# Set the network weights.
|
||||
self.policy.set_trainable_flat(params)
|
||||
perturbation = # Generate a random perturbation to the policy.
|
||||
|
||||
self.policy.set_trainable_flat(params + perturbation)
|
||||
# Do rollout with the perturbed policy.
|
||||
self.policy.set_trainable_flat(params + perturbation)
|
||||
# Do rollout with the perturbed policy.
|
||||
|
||||
self.policy.set_trainable_flat(params - perturbation)
|
||||
# Do rollout with the perturbed policy.
|
||||
self.policy.set_trainable_flat(params - perturbation)
|
||||
# Do rollout with the perturbed policy.
|
||||
|
||||
# Return the rewards.
|
||||
# Return the rewards.
|
||||
|
||||
In the main loop, we create a number of actors with this class.
|
||||
|
||||
@@ -59,17 +59,17 @@ and use the rewards from the rollouts to update the policy.
|
||||
.. code-block:: python
|
||||
|
||||
while True:
|
||||
# Get the current policy weights.
|
||||
theta = policy.get_trainable_flat()
|
||||
# Put the current policy weights in the object store.
|
||||
theta_id = ray.put(theta)
|
||||
# Use the actors to do rollouts, note that we pass in the ID of the policy
|
||||
# weights.
|
||||
rollout_ids = [worker.do_rollouts.remote(theta_id), for worker in workers]
|
||||
# Get the results of the rollouts.
|
||||
results = ray.get(rollout_ids)
|
||||
# Update the policy.
|
||||
optimizer.update(...)
|
||||
# Get the current policy weights.
|
||||
theta = policy.get_trainable_flat()
|
||||
# Put the current policy weights in the object store.
|
||||
theta_id = ray.put(theta)
|
||||
# Use the actors to do rollouts, note that we pass in the ID of the policy
|
||||
# weights.
|
||||
rollout_ids = [worker.do_rollouts.remote(theta_id), for worker in workers]
|
||||
# Get the results of the rollouts.
|
||||
results = ray.get(rollout_ids)
|
||||
# Update the policy.
|
||||
optimizer.update(...)
|
||||
|
||||
In addition, note that we create a large object representing a shared block of
|
||||
random noise. We then put the block in the object store so that each ``Worker``
|
||||
@@ -79,8 +79,8 @@ actor can use it without creating its own copy.
|
||||
|
||||
@ray.remote
|
||||
def create_shared_noise():
|
||||
noise = np.random.randn(250000000)
|
||||
return noise
|
||||
noise = np.random.randn(250000000)
|
||||
return noise
|
||||
|
||||
noise_id = create_shared_noise.remote()
|
||||
|
||||
|
||||
@@ -66,9 +66,9 @@ returns the accuracy of the trained model on a validation set.
|
||||
train_labels,
|
||||
validation_images,
|
||||
validation_labels):
|
||||
# Construct a deep network, train it, and return the accuracy on the
|
||||
# validation data.
|
||||
return np.random.uniform(0, 1)
|
||||
# Construct a deep network, train it, and return the accuracy on the
|
||||
# validation data.
|
||||
return np.random.uniform(0, 1)
|
||||
|
||||
Basic random search
|
||||
-------------------
|
||||
@@ -80,11 +80,11 @@ hyperparameter configurations.
|
||||
.. code-block:: python
|
||||
|
||||
def generate_hyperparameters():
|
||||
# Randomly choose values for the hyperparameters.
|
||||
return {"learning_rate": 10 ** np.random.uniform(-5, 5),
|
||||
"batch_size": np.random.randint(1, 100),
|
||||
"dropout": np.random.uniform(0, 1),
|
||||
"stddev": 10 ** np.random.uniform(-5, 5)}
|
||||
# Randomly choose values for the hyperparameters.
|
||||
return {"learning_rate": 10 ** np.random.uniform(-5, 5),
|
||||
"batch_size": np.random.randint(1, 100),
|
||||
"dropout": np.random.uniform(0, 1),
|
||||
"stddev": 10 ** np.random.uniform(-5, 5)}
|
||||
|
||||
In addition, let's assume that we've started Ray and loaded some data.
|
||||
|
||||
@@ -113,11 +113,11 @@ bunch of experiments, and we get the results.
|
||||
# Launch some experiments.
|
||||
results = []
|
||||
for hyperparameters in hyperparameter_configurations:
|
||||
results.append(train_cnn_and_compute_accuracy.remote(hyperparameters,
|
||||
train_images,
|
||||
train_labels,
|
||||
validation_images,
|
||||
validation_labels))
|
||||
results.append(train_cnn_and_compute_accuracy.remote(hyperparameters,
|
||||
train_images,
|
||||
train_labels,
|
||||
validation_images,
|
||||
validation_labels))
|
||||
|
||||
# Get the results.
|
||||
accuracies = ray.get(results)
|
||||
@@ -145,25 +145,25 @@ detail in driver.py_.
|
||||
# Launch some experiments.
|
||||
remaining_ids = []
|
||||
for hyperparameters in hyperparameter_configurations:
|
||||
remaining_ids.append(train_cnn_and_compute_accuracy.remote(hyperparameters,
|
||||
train_images,
|
||||
train_labels,
|
||||
validation_images,
|
||||
validation_labels))
|
||||
remaining_ids.append(train_cnn_and_compute_accuracy.remote(hyperparameters,
|
||||
train_images,
|
||||
train_labels,
|
||||
validation_images,
|
||||
validation_labels))
|
||||
|
||||
# Whenever a new experiment finishes, print the value and start a new
|
||||
# experiment.
|
||||
for i in range(100):
|
||||
ready_ids, remaining_ids = ray.wait(remaining_ids, num_returns=1)
|
||||
accuracy = ray.get(ready_ids[0])
|
||||
print("Accuracy is {}".format(accuracy))
|
||||
# Start a new experiment.
|
||||
new_hyperparameters = generate_hyperparameters()
|
||||
remaining_ids.append(train_cnn_and_compute_accuracy.remote(new_hyperparameters,
|
||||
train_images,
|
||||
train_labels,
|
||||
validation_images,
|
||||
validation_labels))
|
||||
ready_ids, remaining_ids = ray.wait(remaining_ids, num_returns=1)
|
||||
accuracy = ray.get(ready_ids[0])
|
||||
print("Accuracy is {}".format(accuracy))
|
||||
# Start a new experiment.
|
||||
new_hyperparameters = generate_hyperparameters()
|
||||
remaining_ids.append(train_cnn_and_compute_accuracy.remote(new_hyperparameters,
|
||||
train_images,
|
||||
train_labels,
|
||||
validation_images,
|
||||
validation_labels))
|
||||
|
||||
.. _driver.py: https://github.com/ray-project/ray/blob/master/examples/hyperopt/driver.py
|
||||
|
||||
@@ -191,12 +191,12 @@ model and to return the updated model.
|
||||
|
||||
@ray.remote
|
||||
def train_cnn_and_compute_accuracy(hyperparameters, model=None):
|
||||
# Construct a deep network, train it, and return the accuracy on the
|
||||
# validation data as well as the latest version of the model. If the model
|
||||
# argument is not None, this will continue training an existing model.
|
||||
validation_accuracy = np.random.uniform(0, 1)
|
||||
new_model = model
|
||||
return validation_accuracy, new_model
|
||||
# Construct a deep network, train it, and return the accuracy on the
|
||||
# validation data as well as the latest version of the model. If the model
|
||||
# argument is not None, this will continue training an existing model.
|
||||
validation_accuracy = np.random.uniform(0, 1)
|
||||
new_model = model
|
||||
return validation_accuracy, new_model
|
||||
|
||||
Here's a different variant that uses the same principles. Divide each training
|
||||
session into a series of shorter training sessions. Whenever a short session
|
||||
@@ -208,33 +208,33 @@ doing well, then terminate it and start a new experiment.
|
||||
import numpy as np
|
||||
|
||||
def is_promising(model):
|
||||
# Return true if the model is doing well and false otherwise. In practice,
|
||||
# this function will want more information than just the model.
|
||||
return np.random.choice([True, False])
|
||||
# Return true if the model is doing well and false otherwise. In practice,
|
||||
# this function will want more information than just the model.
|
||||
return np.random.choice([True, False])
|
||||
|
||||
# Start 10 experiments.
|
||||
remaining_ids = []
|
||||
for _ in range(10):
|
||||
experiment_id = train_cnn_and_compute_accuracy.remote(hyperparameters, model=None)
|
||||
remaining_ids.append(experiment_id)
|
||||
experiment_id = train_cnn_and_compute_accuracy.remote(hyperparameters, model=None)
|
||||
remaining_ids.append(experiment_id)
|
||||
|
||||
accuracies = []
|
||||
for i in range(100):
|
||||
# Whenever a segment of an experiment finishes, decide if it looks promising
|
||||
# or not.
|
||||
ready_ids, remaining_ids = ray.wait(remaining_ids, num_returns=1)
|
||||
experiment_id = ready_ids[0]
|
||||
current_accuracy, current_model = ray.get(experiment_id)
|
||||
accuracies.append(current_accuracy)
|
||||
# Whenever a segment of an experiment finishes, decide if it looks promising
|
||||
# or not.
|
||||
ready_ids, remaining_ids = ray.wait(remaining_ids, num_returns=1)
|
||||
experiment_id = ready_ids[0]
|
||||
current_accuracy, current_model = ray.get(experiment_id)
|
||||
accuracies.append(current_accuracy)
|
||||
|
||||
if is_promising(experiment_id):
|
||||
# Continue running the experiment.
|
||||
experiment_id = train_cnn_and_compute_accuracy.remote(hyperparameters,
|
||||
model=current_model)
|
||||
else:
|
||||
# Start a new experiment.
|
||||
experiment_id = train_cnn_and_compute_accuracy.remote(hyperparameters)
|
||||
if is_promising(experiment_id):
|
||||
# Continue running the experiment.
|
||||
experiment_id = train_cnn_and_compute_accuracy.remote(hyperparameters,
|
||||
model=current_model)
|
||||
else:
|
||||
# Start a new experiment.
|
||||
experiment_id = train_cnn_and_compute_accuracy.remote(hyperparameters)
|
||||
|
||||
remaining_ids.append(experiment_id)
|
||||
remaining_ids.append(experiment_id)
|
||||
|
||||
.. _Hyperband: https://arxiv.org/abs/1603.06560
|
||||
|
||||
@@ -53,20 +53,20 @@ of the loss for that choice of model parameters.
|
||||
.. code-block:: python
|
||||
|
||||
def loss(theta, xs, ys):
|
||||
# compute the loss on a batch of data
|
||||
return loss
|
||||
# compute the loss on a batch of data
|
||||
return loss
|
||||
|
||||
def grad(theta, xs, ys):
|
||||
# compute the gradient on a batch of data
|
||||
return grad
|
||||
# compute the gradient on a batch of data
|
||||
return grad
|
||||
|
||||
def full_loss(theta):
|
||||
# compute the loss on the full data set
|
||||
return sum([loss(theta, xs, ys) for (xs, ys) in batches])
|
||||
# compute the loss on the full data set
|
||||
return sum([loss(theta, xs, ys) for (xs, ys) in batches])
|
||||
|
||||
def full_grad(theta):
|
||||
# compute the gradient on the full data set
|
||||
return sum([grad(theta, xs, ys) for (xs, ys) in batches])
|
||||
# compute the gradient on the full data set
|
||||
return sum([grad(theta, xs, ys) for (xs, ys) in batches])
|
||||
|
||||
Since we are working with a small dataset, we don't actually need to separate
|
||||
these methods into the part that operates on a batch and the part that operates
|
||||
@@ -102,16 +102,16 @@ Now, lets turn ``loss`` and ``grad`` into methods of an actor that will contain
|
||||
.. code-block:: python
|
||||
|
||||
class Network(object):
|
||||
def __init__():
|
||||
# Initialize network.
|
||||
def __init__():
|
||||
# Initialize network.
|
||||
|
||||
def loss(theta, xs, ys):
|
||||
# compute the loss
|
||||
return loss
|
||||
def loss(theta, xs, ys):
|
||||
# compute the loss
|
||||
return loss
|
||||
|
||||
def grad(theta, xs, ys):
|
||||
# compute the gradient
|
||||
return grad
|
||||
def grad(theta, xs, ys):
|
||||
# compute the gradient
|
||||
return grad
|
||||
|
||||
Now, it is easy to speed up the computation of the full loss and the full
|
||||
gradient.
|
||||
@@ -119,14 +119,14 @@ gradient.
|
||||
.. code-block:: python
|
||||
|
||||
def full_loss(theta):
|
||||
theta_id = ray.put(theta)
|
||||
loss_ids = [actor.loss(theta_id) for actor in actors]
|
||||
return sum(ray.get(loss_ids))
|
||||
theta_id = ray.put(theta)
|
||||
loss_ids = [actor.loss(theta_id) for actor in actors]
|
||||
return sum(ray.get(loss_ids))
|
||||
|
||||
def full_grad(theta):
|
||||
theta_id = ray.put(theta)
|
||||
grad_ids = [actor.grad(theta_id) for actor in actors]
|
||||
return sum(ray.get(grad_ids)).astype("float64") # This conversion is necessary for use with fmin_l_bfgs_b.
|
||||
theta_id = ray.put(theta)
|
||||
grad_ids = [actor.grad(theta_id) for actor in actors]
|
||||
return sum(ray.get(grad_ids)).astype("float64") # This conversion is necessary for use with fmin_l_bfgs_b.
|
||||
|
||||
Note that we turn ``theta`` into a remote object with the line ``theta_id =
|
||||
ray.put(theta)`` before passing it into the remote functions. If we had written
|
||||
|
||||
@@ -55,26 +55,26 @@ The core of the script is the actor definition.
|
||||
|
||||
@ray.remote(num_gpus=1)
|
||||
class ResNetTrainActor(object):
|
||||
def __init__(self, data, dataset, num_gpus):
|
||||
# data is the preprocessed images and labels extracted from the dataset.
|
||||
# Thus, every actor has its own copy of the data.
|
||||
# Set the CUDA_VISIBLE_DEVICES environment variable in order to restrict
|
||||
# which GPUs TensorFlow uses. Note that this only works if it is done before
|
||||
# the call to tf.Session.
|
||||
os.environ['CUDA_VISIBLE_DEVICES'] = ','.join([str(i) for i in ray.get_gpu_ids()])
|
||||
with tf.Graph().as_default():
|
||||
with tf.device('/gpu:0'):
|
||||
# We omit the code here that actually constructs the residual network
|
||||
# and initializes it. Uses the definition in the Tensorflow Resnet Example.
|
||||
def __init__(self, data, dataset, num_gpus):
|
||||
# data is the preprocessed images and labels extracted from the dataset.
|
||||
# Thus, every actor has its own copy of the data.
|
||||
# Set the CUDA_VISIBLE_DEVICES environment variable in order to restrict
|
||||
# which GPUs TensorFlow uses. Note that this only works if it is done before
|
||||
# the call to tf.Session.
|
||||
os.environ['CUDA_VISIBLE_DEVICES'] = ','.join([str(i) for i in ray.get_gpu_ids()])
|
||||
with tf.Graph().as_default():
|
||||
with tf.device('/gpu:0'):
|
||||
# We omit the code here that actually constructs the residual network
|
||||
# and initializes it. Uses the definition in the Tensorflow Resnet Example.
|
||||
|
||||
def compute_steps(self, weights):
|
||||
# This method sets the weights in the network, runs some training steps,
|
||||
# and returns the new weights. self.model.variables is a TensorFlowVariables
|
||||
# class that we pass the train operation into.
|
||||
self.model.variables.set_weights(weights)
|
||||
for i in range(self.steps):
|
||||
self.model.variables.sess.run(self.model.train_op)
|
||||
return self.model.variables.get_weights()
|
||||
def compute_steps(self, weights):
|
||||
# This method sets the weights in the network, runs some training steps,
|
||||
# and returns the new weights. self.model.variables is a TensorFlowVariables
|
||||
# class that we pass the train operation into.
|
||||
self.model.variables.set_weights(weights)
|
||||
for i in range(self.steps):
|
||||
self.model.variables.sess.run(self.model.train_op)
|
||||
return self.model.variables.get_weights()
|
||||
|
||||
The main script first creates one actor for each GPU, or a single actor if `num_gpus` is zero.
|
||||
|
||||
@@ -89,9 +89,9 @@ object store.
|
||||
.. code-block:: python
|
||||
|
||||
while True:
|
||||
all_weights = ray.get([actor.compute_steps.remote(weight_id) for actor in train_actors])
|
||||
mean_weights = {k: sum([weights[k] for weights in all_weights]) / num_gpus for k in all_weights[0]}
|
||||
weight_id = ray.put(mean_weights)
|
||||
all_weights = ray.get([actor.compute_steps.remote(weight_id) for actor in train_actors])
|
||||
mean_weights = {k: sum([weights[k] for weights in all_weights]) / num_gpus for k in all_weights[0]}
|
||||
weight_id = ray.put(mean_weights)
|
||||
|
||||
.. _`TensorFlow ResNet example`: https://github.com/tensorflow/models/tree/master/resnet
|
||||
.. _`TensorFlow`: https://www.tensorflow.org/install/
|
||||
|
||||
@@ -60,23 +60,23 @@ the actor.
|
||||
|
||||
@ray.remote
|
||||
class PongEnv(object):
|
||||
def __init__(self):
|
||||
# Tell numpy to only use one core. If we don't do this, each actor may try
|
||||
# to use all of the cores and the resulting contention may result in no
|
||||
# speedup over the serial version. Note that if numpy is using OpenBLAS,
|
||||
# then you need to set OPENBLAS_NUM_THREADS=1, and you probably need to do
|
||||
# it from the command line (so it happens before numpy is imported).
|
||||
os.environ["MKL_NUM_THREADS"] = "1"
|
||||
self.env = gym.make("Pong-v0")
|
||||
def __init__(self):
|
||||
# Tell numpy to only use one core. If we don't do this, each actor may try
|
||||
# to use all of the cores and the resulting contention may result in no
|
||||
# speedup over the serial version. Note that if numpy is using OpenBLAS,
|
||||
# then you need to set OPENBLAS_NUM_THREADS=1, and you probably need to do
|
||||
# it from the command line (so it happens before numpy is imported).
|
||||
os.environ["MKL_NUM_THREADS"] = "1"
|
||||
self.env = gym.make("Pong-v0")
|
||||
|
||||
def compute_gradient(self, model):
|
||||
# Reset the game.
|
||||
observation = self.env.reset()
|
||||
while not done:
|
||||
# Choose an action using policy_forward.
|
||||
# Take the action and observe the new state of the world.
|
||||
# Compute a gradient using policy_backward. Return the gradient and reward.
|
||||
return [gradient, reward_sum]
|
||||
def compute_gradient(self, model):
|
||||
# Reset the game.
|
||||
observation = self.env.reset()
|
||||
while not done:
|
||||
# Choose an action using policy_forward.
|
||||
# Take the action and observe the new state of the world.
|
||||
# Compute a gradient using policy_backward. Return the gradient and reward.
|
||||
return [gradient, reward_sum]
|
||||
|
||||
We then create a number of actors, so that we can perform rollouts in parallel.
|
||||
|
||||
@@ -93,8 +93,8 @@ perform rollouts and compute gradients in parallel.
|
||||
actions = []
|
||||
# Launch tasks to compute gradients from multiple rollouts in parallel.
|
||||
for i in range(batch_size):
|
||||
action_id = actors[i].compute_gradient.remote(model_id)
|
||||
actions.append(action_id)
|
||||
action_id = actors[i].compute_gradient.remote(model_id)
|
||||
actions.append(action_id)
|
||||
|
||||
|
||||
Troubleshooting
|
||||
|
||||
@@ -51,7 +51,7 @@ Now, consider a remote function definition as below.
|
||||
|
||||
@ray.remote
|
||||
def f(x):
|
||||
return x + 1
|
||||
return x + 1
|
||||
|
||||
When the remote function is defined as above, the function is immediately
|
||||
pickled, assigned a unique ID, and stored in a Redis server. You can view the
|
||||
@@ -77,17 +77,17 @@ Notes and limitations
|
||||
|
||||
@ray.remote
|
||||
def f(x):
|
||||
return helper(x)
|
||||
return helper(x)
|
||||
|
||||
def helper(x):
|
||||
return x + 1
|
||||
return x + 1
|
||||
|
||||
If you call ``f.remote(0)``, it will give an error of the form.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "<ipython-input-3-12a5beeb2306>", line 3, in f
|
||||
File "<ipython-input-3-12a5beeb2306>", line 3, in f
|
||||
NameError: name 'helper' is not defined
|
||||
|
||||
On the other hand, if ``helper`` is defined before ``f``, then it will work.
|
||||
|
||||
@@ -116,9 +116,9 @@ calling pickle by hand).
|
||||
|
||||
@ray.remote
|
||||
def f(complicated_object):
|
||||
# Deserialize the object manually.
|
||||
obj = pickle.loads(complicated_object)
|
||||
return "Successfully passed {} into f.".format(obj)
|
||||
# Deserialize the object manually.
|
||||
obj = pickle.loads(complicated_object)
|
||||
return "Successfully passed {} into f.".format(obj)
|
||||
|
||||
# Define a complicated object.
|
||||
l = []
|
||||
|
||||
@@ -135,11 +135,11 @@ the newest version.
|
||||
|
||||
@ray.remote
|
||||
def f():
|
||||
return 1
|
||||
return 1
|
||||
|
||||
@ray.remote
|
||||
def f():
|
||||
return 2
|
||||
return 2
|
||||
|
||||
ray.get(f.remote()) # This should be 2.
|
||||
|
||||
@@ -182,7 +182,7 @@ Ray).
|
||||
.. code-block:: python
|
||||
|
||||
def h():
|
||||
return 1
|
||||
return 1
|
||||
|
||||
And you define remote function ``f`` as
|
||||
|
||||
@@ -190,7 +190,7 @@ Ray).
|
||||
|
||||
@ray.remote
|
||||
def f():
|
||||
return file.h()
|
||||
return file.h()
|
||||
|
||||
You can redefine ``f`` as follows.
|
||||
|
||||
@@ -198,8 +198,8 @@ Ray).
|
||||
|
||||
@ray.remote
|
||||
def f():
|
||||
reload(file)
|
||||
return file.h()
|
||||
reload(file)
|
||||
return file.h()
|
||||
|
||||
This forces the reload to happen on the workers as needed. Note that in
|
||||
Python 3, you need to do ``from importlib import reload``.
|
||||
|
||||
+17
-17
@@ -122,7 +122,7 @@ For example, a normal Python function looks like this.
|
||||
.. code-block:: python
|
||||
|
||||
def add1(a, b):
|
||||
return a + b
|
||||
return a + b
|
||||
|
||||
A remote function looks like this.
|
||||
|
||||
@@ -130,7 +130,7 @@ A remote function looks like this.
|
||||
|
||||
@ray.remote
|
||||
def add2(a, b):
|
||||
return a + b
|
||||
return a + b
|
||||
|
||||
Remote functions
|
||||
~~~~~~~~~~~~~~~~
|
||||
@@ -155,11 +155,11 @@ to parallelize computation.
|
||||
import time
|
||||
|
||||
def f1():
|
||||
time.sleep(1)
|
||||
time.sleep(1)
|
||||
|
||||
@ray.remote
|
||||
def f2():
|
||||
time.sleep(1)
|
||||
time.sleep(1)
|
||||
|
||||
# The following takes ten seconds.
|
||||
[f1() for _ in range(10)]
|
||||
@@ -197,7 +197,7 @@ Note that a remote function can return multiple object IDs.
|
||||
|
||||
@ray.remote(num_return_vals=3)
|
||||
def return_multiple():
|
||||
return 1, 2, 3
|
||||
return 1, 2, 3
|
||||
|
||||
a_id, b_id, c_id = return_multiple.remote()
|
||||
|
||||
@@ -212,7 +212,7 @@ three tasks as follows, each of which depends on the previous task.
|
||||
|
||||
@ray.remote
|
||||
def f(x):
|
||||
return x + 1
|
||||
return x + 1
|
||||
|
||||
x = f.remote(0)
|
||||
y = f.remote(x)
|
||||
@@ -232,11 +232,11 @@ Consider the following implementation of a tree reduce.
|
||||
|
||||
@ray.remote
|
||||
def generate_data():
|
||||
return np.random.normal(size=1000)
|
||||
return np.random.normal(size=1000)
|
||||
|
||||
@ray.remote
|
||||
def aggregate_data(x, y):
|
||||
return x + y
|
||||
return x + y
|
||||
|
||||
# Generate some random data. This launches 100 tasks that will be scheduled on
|
||||
# various nodes. The resulting data will be distributed around the cluster.
|
||||
@@ -244,7 +244,7 @@ Consider the following implementation of a tree reduce.
|
||||
|
||||
# Perform a tree reduce.
|
||||
while len(data) > 1:
|
||||
data.append(aggregate_data.remote(data.pop(0), data.pop(0)))
|
||||
data.append(aggregate_data.remote(data.pop(0), data.pop(0)))
|
||||
|
||||
# Fetch the result.
|
||||
ray.get(data)
|
||||
@@ -260,17 +260,17 @@ following example.
|
||||
|
||||
@ray.remote
|
||||
def sub_experiment(i, j):
|
||||
# Run the jth sub-experiment for the ith experiment.
|
||||
return i + j
|
||||
# Run the jth sub-experiment for the ith experiment.
|
||||
return i + j
|
||||
|
||||
@ray.remote
|
||||
def run_experiment(i):
|
||||
sub_results = []
|
||||
# Launch tasks to perform 10 sub-experiments in parallel.
|
||||
for j in range(10):
|
||||
sub_results.append(sub_experiment.remote(i, j))
|
||||
# Return the sum of the results of the sub-experiments.
|
||||
return sum(ray.get(sub_results))
|
||||
sub_results = []
|
||||
# Launch tasks to perform 10 sub-experiments in parallel.
|
||||
for j in range(10):
|
||||
sub_results.append(sub_experiment.remote(i, j))
|
||||
# Return the sum of the results of the sub-experiments.
|
||||
return sum(ray.get(sub_results))
|
||||
|
||||
results = [run_experiment.remote(i) for i in range(5)]
|
||||
ray.get(results) # [45, 55, 65, 75, 85]
|
||||
|
||||
@@ -68,8 +68,8 @@ following.
|
||||
|
||||
@ray.remote
|
||||
def f():
|
||||
time.sleep(0.01)
|
||||
return ray.services.get_node_ip_address()
|
||||
time.sleep(0.01)
|
||||
return ray.services.get_node_ip_address()
|
||||
|
||||
# Get a list of the IP addresses of the nodes that have joined the cluster.
|
||||
set(ray.get([f.remote() for _ in range(1000)]))
|
||||
|
||||
@@ -174,8 +174,8 @@ following.
|
||||
|
||||
@ray.remote
|
||||
def f():
|
||||
time.sleep(0.01)
|
||||
return ray.services.get_node_ip_address()
|
||||
time.sleep(0.01)
|
||||
return ray.services.get_node_ip_address()
|
||||
|
||||
# Get a list of the IP addresses of the nodes that have joined the cluster.
|
||||
set(ray.get([f.remote() for _ in range(1000)]))
|
||||
|
||||
@@ -42,7 +42,7 @@ remote decorator.
|
||||
|
||||
@ray.remote(num_gpus=1)
|
||||
def gpu_method():
|
||||
return "This function is allowed to use GPUs {}.".format(ray.get_gpu_ids())
|
||||
return "This function is allowed to use GPUs {}.".format(ray.get_gpu_ids())
|
||||
|
||||
Inside of the remote function, a call to ``ray.get_gpu_ids()`` will return a
|
||||
list of integers indicating which GPUs the remote function is allowed to use.
|
||||
@@ -62,10 +62,10 @@ TensorFlow.
|
||||
|
||||
@ray.remote(num_gpus=1)
|
||||
def gpu_method():
|
||||
os.environ["CUDA_VISIBLE_DEVICES"] = ",".join(map(str, ray.get_gpu_ids()))
|
||||
# Create a TensorFlow session. TensorFlow will restrict itself to use the
|
||||
# GPUs specified by the CUDA_VISIBLE_DEVICES environment variable.
|
||||
tf.Session()
|
||||
os.environ["CUDA_VISIBLE_DEVICES"] = ",".join(map(str, ray.get_gpu_ids()))
|
||||
# Create a TensorFlow session. TensorFlow will restrict itself to use the
|
||||
# GPUs specified by the CUDA_VISIBLE_DEVICES environment variable.
|
||||
tf.Session()
|
||||
|
||||
**Note:** It is certainly possible for the person implementing ``gpu_method`` to
|
||||
ignore ``ray.get_gpu_ids`` and to use all of the GPUs on the machine. Ray does
|
||||
@@ -84,8 +84,8 @@ instance requires in the ``ray.remote`` decorator.
|
||||
|
||||
@ray.remote(num_gpus=1)
|
||||
class GPUActor(object):
|
||||
def __init__(self):
|
||||
return "This actor is allowed to use GPUs {}.".format(ray.get_gpu_ids())
|
||||
def __init__(self):
|
||||
return "This actor is allowed to use GPUs {}.".format(ray.get_gpu_ids())
|
||||
|
||||
When the actor is created, GPUs will be reserved for that actor for the lifetime
|
||||
of the actor.
|
||||
@@ -101,12 +101,12 @@ The following is an example of how to use GPUs in an actor through TensorFlow.
|
||||
|
||||
@ray.remote(num_gpus=1)
|
||||
class GPUActor(object):
|
||||
def __init__(self):
|
||||
self.gpu_ids = ray.get_gpu_ids()
|
||||
os.environ["CUDA_VISIBLE_DEVICES"] = ",".join(map(str, self.gpu_ids))
|
||||
# The call to tf.Session() will restrict TensorFlow to use the GPUs
|
||||
# specified in the CUDA_VISIBLE_DEVICES environment variable.
|
||||
self.sess = tf.Session()
|
||||
def __init__(self):
|
||||
self.gpu_ids = ray.get_gpu_ids()
|
||||
os.environ["CUDA_VISIBLE_DEVICES"] = ",".join(map(str, self.gpu_ids))
|
||||
# The call to tf.Session() will restrict TensorFlow to use the GPUs
|
||||
# specified in the CUDA_VISIBLE_DEVICES environment variable.
|
||||
self.sess = tf.Session()
|
||||
|
||||
Troubleshooting
|
||||
---------------
|
||||
|
||||
@@ -105,50 +105,50 @@ complex Python objects.
|
||||
NUM_ITERS = 201
|
||||
|
||||
class Network(object):
|
||||
def __init__(self, x, y):
|
||||
# Seed TensorFlow to make the script deterministic.
|
||||
tf.set_random_seed(0)
|
||||
# Define the inputs.
|
||||
self.x_data = tf.constant(x, dtype=tf.float32)
|
||||
self.y_data = tf.constant(y, dtype=tf.float32)
|
||||
# Define the weights and computation.
|
||||
w = tf.Variable(tf.random_uniform([1], -1.0, 1.0))
|
||||
b = tf.Variable(tf.zeros([1]))
|
||||
y = w * self.x_data + b
|
||||
# Define the loss.
|
||||
self.loss = tf.reduce_mean(tf.square(y - self.y_data))
|
||||
optimizer = tf.train.GradientDescentOptimizer(0.5)
|
||||
self.grads = optimizer.compute_gradients(self.loss)
|
||||
self.train = optimizer.apply_gradients(self.grads)
|
||||
# Define the weight initializer and session.
|
||||
init = tf.global_variables_initializer()
|
||||
self.sess = tf.Session()
|
||||
# Additional code for setting and getting the weights
|
||||
self.variables = ray.experimental.TensorFlowVariables(self.loss, self.sess)
|
||||
# Return all of the data needed to use the network.
|
||||
self.sess.run(init)
|
||||
def __init__(self, x, y):
|
||||
# Seed TensorFlow to make the script deterministic.
|
||||
tf.set_random_seed(0)
|
||||
# Define the inputs.
|
||||
self.x_data = tf.constant(x, dtype=tf.float32)
|
||||
self.y_data = tf.constant(y, dtype=tf.float32)
|
||||
# Define the weights and computation.
|
||||
w = tf.Variable(tf.random_uniform([1], -1.0, 1.0))
|
||||
b = tf.Variable(tf.zeros([1]))
|
||||
y = w * self.x_data + b
|
||||
# Define the loss.
|
||||
self.loss = tf.reduce_mean(tf.square(y - self.y_data))
|
||||
optimizer = tf.train.GradientDescentOptimizer(0.5)
|
||||
self.grads = optimizer.compute_gradients(self.loss)
|
||||
self.train = optimizer.apply_gradients(self.grads)
|
||||
# Define the weight initializer and session.
|
||||
init = tf.global_variables_initializer()
|
||||
self.sess = tf.Session()
|
||||
# Additional code for setting and getting the weights
|
||||
self.variables = ray.experimental.TensorFlowVariables(self.loss, self.sess)
|
||||
# Return all of the data needed to use the network.
|
||||
self.sess.run(init)
|
||||
|
||||
# Define a remote function that trains the network for one step and returns the
|
||||
# new weights.
|
||||
def step(self, weights):
|
||||
# Set the weights in the network.
|
||||
self.variables.set_weights(weights)
|
||||
# Do one step of training.
|
||||
self.sess.run(self.train)
|
||||
# Return the new weights.
|
||||
return self.variables.get_weights()
|
||||
# Define a remote function that trains the network for one step and returns the
|
||||
# new weights.
|
||||
def step(self, weights):
|
||||
# Set the weights in the network.
|
||||
self.variables.set_weights(weights)
|
||||
# Do one step of training.
|
||||
self.sess.run(self.train)
|
||||
# Return the new weights.
|
||||
return self.variables.get_weights()
|
||||
|
||||
def get_weights(self):
|
||||
return self.variables.get_weights()
|
||||
def get_weights(self):
|
||||
return self.variables.get_weights()
|
||||
|
||||
# Define a remote function for generating fake data.
|
||||
@ray.remote(num_return_vals=2)
|
||||
def generate_fake_x_y_data(num_data, seed=0):
|
||||
# Seed numpy to make the script deterministic.
|
||||
np.random.seed(seed)
|
||||
x = np.random.rand(num_data)
|
||||
y = x * 0.1 + 0.3
|
||||
return x, y
|
||||
# Seed numpy to make the script deterministic.
|
||||
np.random.seed(seed)
|
||||
x = np.random.rand(num_data)
|
||||
y = x * 0.1 + 0.3
|
||||
return x, y
|
||||
|
||||
# Generate some training data.
|
||||
batch_ids = [generate_fake_x_y_data.remote(BATCH_SIZE, seed=i) for i in range(NUM_BATCHES)]
|
||||
@@ -166,24 +166,24 @@ complex Python objects.
|
||||
|
||||
# Do some steps of training.
|
||||
for iteration in range(NUM_ITERS):
|
||||
# Put the weights in the object store. This is optional. We could instead pass
|
||||
# the variable weights directly into step.remote, in which case it would be
|
||||
# placed in the object store under the hood. However, in that case multiple
|
||||
# copies of the weights would be put in the object store, so this approach is
|
||||
# more efficient.
|
||||
weights_id = ray.put(weights)
|
||||
# Call the remote function multiple times in parallel.
|
||||
new_weights_ids = [actor.step.remote(weights_id) for actor in actor_list]
|
||||
# Get all of the weights.
|
||||
new_weights_list = ray.get(new_weights_ids)
|
||||
# Add up all the different weights. Each element of new_weights_list is a dict
|
||||
# of weights, and we want to add up these dicts component wise using the keys
|
||||
# of the first dict.
|
||||
weights = {variable: sum(weight_dict[variable] for weight_dict in new_weights_list) / NUM_BATCHES for variable in new_weights_list[0]}
|
||||
# Print the current weights. They should converge to roughly to the values 0.1
|
||||
# and 0.3 used in generate_fake_x_y_data.
|
||||
if iteration % 20 == 0:
|
||||
print("Iteration {}: weights are {}".format(iteration, weights))
|
||||
# Put the weights in the object store. This is optional. We could instead pass
|
||||
# the variable weights directly into step.remote, in which case it would be
|
||||
# placed in the object store under the hood. However, in that case multiple
|
||||
# copies of the weights would be put in the object store, so this approach is
|
||||
# more efficient.
|
||||
weights_id = ray.put(weights)
|
||||
# Call the remote function multiple times in parallel.
|
||||
new_weights_ids = [actor.step.remote(weights_id) for actor in actor_list]
|
||||
# Get all of the weights.
|
||||
new_weights_list = ray.get(new_weights_ids)
|
||||
# Add up all the different weights. Each element of new_weights_list is a dict
|
||||
# of weights, and we want to add up these dicts component wise using the keys
|
||||
# of the first dict.
|
||||
weights = {variable: sum(weight_dict[variable] for weight_dict in new_weights_list) / NUM_BATCHES for variable in new_weights_list[0]}
|
||||
# Print the current weights. They should converge to roughly to the values 0.1
|
||||
# and 0.3 used in generate_fake_x_y_data.
|
||||
if iteration % 20 == 0:
|
||||
print("Iteration {}: weights are {}".format(iteration, weights))
|
||||
|
||||
How to Train in Parallel using Ray
|
||||
----------------------------------
|
||||
@@ -236,49 +236,49 @@ For reference, the full code is below:
|
||||
NUM_ITERS = 201
|
||||
|
||||
class Network(object):
|
||||
def __init__(self, x, y):
|
||||
# Seed TensorFlow to make the script deterministic.
|
||||
tf.set_random_seed(0)
|
||||
# Define the inputs.
|
||||
x_data = tf.constant(x, dtype=tf.float32)
|
||||
y_data = tf.constant(y, dtype=tf.float32)
|
||||
# Define the weights and computation.
|
||||
w = tf.Variable(tf.random_uniform([1], -1.0, 1.0))
|
||||
b = tf.Variable(tf.zeros([1]))
|
||||
y = w * x_data + b
|
||||
# Define the loss.
|
||||
self.loss = tf.reduce_mean(tf.square(y - y_data))
|
||||
optimizer = tf.train.GradientDescentOptimizer(0.5)
|
||||
self.grads = optimizer.compute_gradients(self.loss)
|
||||
self.train = optimizer.apply_gradients(self.grads)
|
||||
# Define the weight initializer and session.
|
||||
init = tf.global_variables_initializer()
|
||||
self.sess = tf.Session()
|
||||
# Additional code for setting and getting the weights
|
||||
self.variables = ray.experimental.TensorFlowVariables(self.loss, self.sess)
|
||||
# Return all of the data needed to use the network.
|
||||
self.sess.run(init)
|
||||
def __init__(self, x, y):
|
||||
# Seed TensorFlow to make the script deterministic.
|
||||
tf.set_random_seed(0)
|
||||
# Define the inputs.
|
||||
x_data = tf.constant(x, dtype=tf.float32)
|
||||
y_data = tf.constant(y, dtype=tf.float32)
|
||||
# Define the weights and computation.
|
||||
w = tf.Variable(tf.random_uniform([1], -1.0, 1.0))
|
||||
b = tf.Variable(tf.zeros([1]))
|
||||
y = w * x_data + b
|
||||
# Define the loss.
|
||||
self.loss = tf.reduce_mean(tf.square(y - y_data))
|
||||
optimizer = tf.train.GradientDescentOptimizer(0.5)
|
||||
self.grads = optimizer.compute_gradients(self.loss)
|
||||
self.train = optimizer.apply_gradients(self.grads)
|
||||
# Define the weight initializer and session.
|
||||
init = tf.global_variables_initializer()
|
||||
self.sess = tf.Session()
|
||||
# Additional code for setting and getting the weights
|
||||
self.variables = ray.experimental.TensorFlowVariables(self.loss, self.sess)
|
||||
# Return all of the data needed to use the network.
|
||||
self.sess.run(init)
|
||||
|
||||
# Define a remote function that trains the network for one step and returns the
|
||||
# new weights.
|
||||
def step(self, weights):
|
||||
# Set the weights in the network.
|
||||
self.variables.set_weights(weights)
|
||||
# Do one step of training. We only need the actual gradients so we filter over the list.
|
||||
actual_grads = self.sess.run([grad[0] for grad in self.grads])
|
||||
return actual_grads
|
||||
# Define a remote function that trains the network for one step and returns the
|
||||
# new weights.
|
||||
def step(self, weights):
|
||||
# Set the weights in the network.
|
||||
self.variables.set_weights(weights)
|
||||
# Do one step of training. We only need the actual gradients so we filter over the list.
|
||||
actual_grads = self.sess.run([grad[0] for grad in self.grads])
|
||||
return actual_grads
|
||||
|
||||
def get_weights(self):
|
||||
return self.variables.get_weights()
|
||||
def get_weights(self):
|
||||
return self.variables.get_weights()
|
||||
|
||||
# Define a remote function for generating fake data.
|
||||
@ray.remote(num_return_vals=2)
|
||||
def generate_fake_x_y_data(num_data, seed=0):
|
||||
# Seed numpy to make the script deterministic.
|
||||
np.random.seed(seed)
|
||||
x = np.random.rand(num_data)
|
||||
y = x * 0.1 + 0.3
|
||||
return x, y
|
||||
# Seed numpy to make the script deterministic.
|
||||
np.random.seed(seed)
|
||||
x = np.random.rand(num_data)
|
||||
y = x * 0.1 + 0.3
|
||||
return x, y
|
||||
|
||||
# Generate some training data.
|
||||
batch_ids = [generate_fake_x_y_data.remote(BATCH_SIZE, seed=i) for i in range(NUM_BATCHES)]
|
||||
@@ -297,26 +297,26 @@ For reference, the full code is below:
|
||||
|
||||
# Do some steps of training.
|
||||
for iteration in range(NUM_ITERS):
|
||||
# Put the weights in the object store. This is optional. We could instead pass
|
||||
# the variable weights directly into step.remote, in which case it would be
|
||||
# placed in the object store under the hood. However, in that case multiple
|
||||
# copies of the weights would be put in the object store, so this approach is
|
||||
# more efficient.
|
||||
weights_id = ray.put(weights)
|
||||
# Call the remote function multiple times in parallel.
|
||||
gradients_ids = [actor.step.remote(weights_id) for actor in actor_list]
|
||||
# Get all of the weights.
|
||||
gradients_list = ray.get(gradients_ids)
|
||||
# Put the weights in the object store. This is optional. We could instead pass
|
||||
# the variable weights directly into step.remote, in which case it would be
|
||||
# placed in the object store under the hood. However, in that case multiple
|
||||
# copies of the weights would be put in the object store, so this approach is
|
||||
# more efficient.
|
||||
weights_id = ray.put(weights)
|
||||
# Call the remote function multiple times in parallel.
|
||||
gradients_ids = [actor.step.remote(weights_id) for actor in actor_list]
|
||||
# Get all of the weights.
|
||||
gradients_list = ray.get(gradients_ids)
|
||||
|
||||
# Take the mean of the different gradients. Each element of gradients_list is a list
|
||||
# of gradients, and we want to take the mean of each one.
|
||||
mean_grads = [sum([gradients[i] for gradients in gradients_list]) / len(gradients_list) for i in range(len(gradients_list[0]))]
|
||||
# Take the mean of the different gradients. Each element of gradients_list is a list
|
||||
# of gradients, and we want to take the mean of each one.
|
||||
mean_grads = [sum([gradients[i] for gradients in gradients_list]) / len(gradients_list) for i in range(len(gradients_list[0]))]
|
||||
|
||||
feed_dict = {grad[0]: mean_grad for (grad, mean_grad) in zip(local_network.grads, mean_grads)}
|
||||
local_network.sess.run(local_network.train, feed_dict=feed_dict)
|
||||
weights = local_network.get_weights()
|
||||
feed_dict = {grad[0]: mean_grad for (grad, mean_grad) in zip(local_network.grads, mean_grads)}
|
||||
local_network.sess.run(local_network.train, feed_dict=feed_dict)
|
||||
weights = local_network.get_weights()
|
||||
|
||||
# Print the current weights. They should converge to roughly to the values 0.1
|
||||
# and 0.3 used in generate_fake_x_y_data.
|
||||
if iteration % 20 == 0:
|
||||
print("Iteration {}: weights are {}".format(iteration, weights))
|
||||
# Print the current weights. They should converge to roughly to the values 0.1
|
||||
# and 0.3 used in generate_fake_x_y_data.
|
||||
if iteration % 20 == 0:
|
||||
print("Iteration {}: weights are {}".format(iteration, weights))
|
||||
|
||||
Reference in New Issue
Block a user