mirror of
https://github.com/wassname/ray.git
synced 2026-07-21 12:50:45 +08:00
Update documentation (#445)
* Update documentation for serialization. * Update documentation for reusable variables. * Update documentation for using Ray with TensorFlow. This change is to allow code blocks to be copied and pasted into a Python interpreter. * Fix documentation for hyperparameter optimization example.
This commit is contained in:
committed by
Philipp Moritz
parent
c6ec36865d
commit
1c3aaf7189
+17
-16
@@ -2,28 +2,29 @@
|
||||
|
||||
This document explains how to create and use reusable variables in Ray.
|
||||
|
||||
A reusable variable is a per-worker variable which is (1) created when the worker starts, and (2) is reinitialized before a task reuses it. Thus, while a task can modify a reusable variable, the variable is reinitialized before the next task uses it. Reusable variables obviates the need for serialization/deserialization and, like Ray objects, avoid side effects.
|
||||
|
||||
Reusable variables are Python objects that are created once on each worker and
|
||||
are reused by all subsequent tasks that run on that worker. There are two
|
||||
primary reasons for doing this.
|
||||
can be used by all subsequent tasks that run on that worker. Reusable variables
|
||||
will be reinitialized between tasks. There are several primary reasons for doing
|
||||
this.
|
||||
|
||||
1. You want to use an object that is not serializable, and so the code that
|
||||
creates the object must run on each worker.
|
||||
2. Creating the object is slow (like a TensorFlow graph), and so you want to
|
||||
create it only once.
|
||||
1. Reusable variables are created once on each worker and are not shipped
|
||||
between machines, so they do not need to be serialized or deserialized (however,
|
||||
the code that creates the reusable variable does need to be pickled).
|
||||
2. Objects that are slow to construct (like a TensorFlow graph) only need to be
|
||||
constructed once on each worker.
|
||||
3. By reinitializing between tasks that use them, they help avoid side effects.
|
||||
|
||||
To elaborate on the first point, certain Python objects cannot be easily shipped
|
||||
between processes or machines. Certain objects simply are not meaningful on
|
||||
other machines (such as a filehandle). For others, their classes may be largely
|
||||
defined in C, and so their internals may be relatively opaque to Python.
|
||||
|
||||
In these situations, if we need the object on all of the workers, then each
|
||||
worker needs to run the code that creates the object. We accomplish this using
|
||||
reusable variables. These variables are created once on each worker and are
|
||||
reused by every task that runs on that worker.
|
||||
To elaborate on the first point, standard Python serialization libraries like
|
||||
pickle fail on some objects. With these kinds of objects, it may be easier to
|
||||
ship the code that creates the object to each worker and to run the code on each
|
||||
worker than it would be to create the object on the driver and ship the object
|
||||
to each worker.
|
||||
|
||||
## Creating a Reusable Variable
|
||||
|
||||
To give an example, consider a gym environment, which is essentially provides a
|
||||
To give an example, consider a gym environment, which essentially provides a
|
||||
Python wrapper for an Atari simulator.
|
||||
|
||||
```python
|
||||
|
||||
+37
-23
@@ -1,7 +1,7 @@
|
||||
# Serialization in the Object Store
|
||||
|
||||
This document describes what Python objects Ray can and cannot serialize into
|
||||
the object store.
|
||||
the object store. Once an object is placed in the object store, it is immutable.
|
||||
|
||||
There are a number of situations in which Ray will place objects in the object
|
||||
store.
|
||||
@@ -16,8 +16,8 @@ A normal Python object may have pointers all over the place, so to place an
|
||||
object in the object store or send it between processes, it must first be
|
||||
converted to a contiguous string of bytes. This process is known as
|
||||
serialization. The process of turning the string of bytes back into a Python
|
||||
object is known as deserialization. The process of serialization and
|
||||
deserialization is often a bottleneck in distributed computing.
|
||||
object is known as deserialization. The processes of serialization and
|
||||
deserialization are often bottlenecks in distributed computing.
|
||||
|
||||
Pickle is one example of a library for serialization and deserialization in
|
||||
Python.
|
||||
@@ -34,19 +34,24 @@ large variety of Python objects. However, for numerical workloads, pickling and
|
||||
unpickling can be inefficient. For example, when unpickling a list of numpy
|
||||
arrays, pickle will create completely new arrays in memory. In Ray, when we
|
||||
deserialize a list of numpy arrays from the object store, we will create a list
|
||||
of numpy arrays in Python, but each numpy array will internally just wrap a
|
||||
pointer to the object store's memory. This allows for minimal deserialization.
|
||||
of numpy array objects in Python, but each numpy array object is essentially
|
||||
just a pointer to the relevant location in the object store's memory. There are
|
||||
some advantages to this form of serialization.
|
||||
|
||||
- Deserialization can be very fast.
|
||||
- Memory is shared between processes so worker processes can all read the same
|
||||
data without having to copy it.
|
||||
|
||||
## What Objects Does Ray Handle
|
||||
|
||||
However, Ray is not currently capable of serializing arbitrary Python objects.
|
||||
The set of Python objects that Ray can serialize is the smallest set S with the
|
||||
following properties.
|
||||
The set of Python objects that Ray can serialize includes the following.
|
||||
|
||||
1. S contains primitive types: ints, floats, longs, bools, strings, unicode,
|
||||
numpy arrays.
|
||||
2. S contains objects whose classes can be registered with `ray.register_class`.
|
||||
3. S contains any list, dictionary, or tuple whose elements belong in S.
|
||||
1. Primitive types: ints, floats, longs, bools, strings, unicode, and numpy
|
||||
arrays.
|
||||
2. Any list, dictionary, or tuple whose elements can be serialized by Ray.
|
||||
3. Objects whose classes can be registered with `ray.register_class`. This point
|
||||
is described below.
|
||||
|
||||
## Registering Custom Classes
|
||||
|
||||
@@ -60,9 +65,13 @@ class Foo(object):
|
||||
self.b = b
|
||||
```
|
||||
|
||||
Simply calling `ray.put(Foo(1, 2))` will fail with a message like `Ray does not know
|
||||
how to serialize the object <__main__.Foo object at 0x1077d7c50>.` This can be
|
||||
addressed by calling `ray.register_class(Foo)`.
|
||||
Simply calling `ray.put(Foo(1, 2))` will fail with a message like
|
||||
|
||||
```
|
||||
Ray does not know how to serialize the object <__main__.Foo object at 0x1077d7c50>.
|
||||
```
|
||||
|
||||
This can be addressed by calling `ray.register_class(Foo)`.
|
||||
|
||||
```python
|
||||
import ray
|
||||
@@ -75,8 +84,8 @@ class Foo(object):
|
||||
self.a = a
|
||||
self.b = b
|
||||
|
||||
# Calling ray.register_class(Foo) used to ship the class definition to all of
|
||||
# the workers so that workers know how to construct new Foo objects.
|
||||
# Calling ray.register_class(Foo) ships the class definition to all of the
|
||||
# workers so that workers know how to construct new Foo objects.
|
||||
ray.register_class(Foo)
|
||||
|
||||
# Create a Foo object, place it in the object store, and retrieve it.
|
||||
@@ -111,21 +120,26 @@ efficient data structures like arrays.
|
||||
|
||||
**Note:** Another setting where the naive replacement of an object with its
|
||||
`__dict__` attribute fails is where an object recursively contains itself (or
|
||||
multiple objects recursively contain each other). For example, the code below
|
||||
currently fails.
|
||||
multiple objects recursively contain each other). For example, consider the code
|
||||
below.
|
||||
|
||||
```python
|
||||
l = []
|
||||
l.append(l)
|
||||
# Running ray.put(l) will crash due to an infinite loop.
|
||||
```
|
||||
|
||||
It will throw an exception with a message like the following.
|
||||
|
||||
```
|
||||
This object exceeds the maximum recursion depth. It may contain itself recursively.
|
||||
```
|
||||
|
||||
# Last Resort Workaround
|
||||
|
||||
If you find cases where Ray does the wrong thing, please let us know so we can
|
||||
fix it. In the meantime, you can do your own custom serialization and
|
||||
deserialization (for example by calling pickle by hand). Or by writing your own
|
||||
custom serializer and deserializer.
|
||||
If you find cases where Ray doesn't work or does the wrong thing, please let us
|
||||
know so we can fix it. In the meantime, you can do your own custom serialization
|
||||
and deserialization (for example by calling pickle by hand). Or by writing your
|
||||
own custom serializer and deserializer.
|
||||
|
||||
```python
|
||||
import pickle
|
||||
|
||||
@@ -47,13 +47,13 @@ def get_and_set_weights_methods():
|
||||
for var in tf.trainable_variables():
|
||||
assignment_placeholders.append(tf.placeholder(var.value().dtype, var.get_shape().as_list()))
|
||||
assignment_nodes.append(var.assign(assignment_placeholders[-1]))
|
||||
|
||||
# Define a function for getting the network weights.
|
||||
def get_weights():
|
||||
return [v.eval(session=sess) for v in tf.trainable_variables()]
|
||||
|
||||
# Define a function for setting the network weights.
|
||||
def set_weights(new_weights):
|
||||
sess.run(assignment_nodes, feed_dict={p: w for p, w in zip(assignment_placeholders, new_weights)})
|
||||
|
||||
# Return the methods.
|
||||
return get_weights, set_weights
|
||||
|
||||
get_weights, set_weights = get_and_set_weights_methods()
|
||||
@@ -102,21 +102,20 @@ NUM_ITERS = 201
|
||||
def net_vars_initializer():
|
||||
# Seed TensorFlow to make the script deterministic.
|
||||
tf.set_random_seed(0)
|
||||
|
||||
# Define the inputs.
|
||||
x_data = tf.placeholder(tf.float32, shape=[BATCH_SIZE])
|
||||
y_data = tf.placeholder(tf.float32, shape=[BATCH_SIZE])
|
||||
|
||||
# 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.
|
||||
loss = tf.reduce_mean(tf.square(y - y_data))
|
||||
optimizer = tf.train.GradientDescentOptimizer(0.5)
|
||||
train = optimizer.minimize(loss)
|
||||
|
||||
# Define the weight initializer and session.
|
||||
init = tf.initialize_all_variables()
|
||||
sess = tf.Session()
|
||||
|
||||
# Additional code for setting and getting the weights.
|
||||
def get_and_set_weights_methods():
|
||||
assignment_placeholders = []
|
||||
@@ -124,17 +123,13 @@ def net_vars_initializer():
|
||||
for var in tf.trainable_variables():
|
||||
assignment_placeholders.append(tf.placeholder(var.value().dtype, var.get_shape().as_list()))
|
||||
assignment_nodes.append(var.assign(assignment_placeholders[-1]))
|
||||
|
||||
def get_weights():
|
||||
return [v.eval(session=sess) for v in tf.trainable_variables()]
|
||||
|
||||
def set_weights(new_weights):
|
||||
sess.run(assignment_nodes, feed_dict={p: w for p, w in zip(assignment_placeholders, new_weights)})
|
||||
|
||||
return get_weights, set_weights
|
||||
|
||||
get_weights, set_weights = get_and_set_weights_methods()
|
||||
|
||||
# Return all of the data needed to use the network.
|
||||
return get_weights, set_weights, sess, train, loss, x_data, y_data, init
|
||||
|
||||
def net_vars_reinitializer(net_vars):
|
||||
|
||||
@@ -140,7 +140,7 @@ remaining_ids = []
|
||||
for _ in range(100):
|
||||
params = generate_random_params()
|
||||
accuracy_id = train_cnn_and_compute_accuracy.remote(randparams, train_images, train_labels, validation_images, validation_labels)
|
||||
result_ids.append(accuracy_id)
|
||||
remaining_ids.append(accuracy_id)
|
||||
|
||||
# Process the tasks one at a time.
|
||||
while len(remaining_ids) > 0:
|
||||
@@ -151,6 +151,9 @@ while len(remaining_ids) > 0:
|
||||
print "Accuracy {}".format(accuracy)
|
||||
```
|
||||
|
||||
Note that the above example does not associate the accuracy with the parameters
|
||||
that produced that accuracy, but this is done in the actual script.
|
||||
|
||||
## Additional notes
|
||||
|
||||
**Early Stopping:** Sometimes when running an optimization, it is clear early on
|
||||
|
||||
Reference in New Issue
Block a user