Added helper class for getting tf variables from loss function (#184)

* Added helper class for getting tf variables from loss function

* Updated usage and documentation

* Removed try-catches

* Added futures

* Added documentation

* fixes and tests

* more tests

* install tensorflow in travis
This commit is contained in:
Wapaul1
2017-01-07 01:54:11 -08:00
committed by Philipp Moritz
parent c13d73b4c9
commit 0ac2abee51
6 changed files with 109 additions and 42 deletions
+1
View File
@@ -72,6 +72,7 @@ script:
- python test/runtest.py
- python test/array_test.py
- python test/tensorflow_test.py
- python test/failure_test.py
- python test/microbenchmarks.py
- python test/stress_tests.py
+4 -4
View File
@@ -20,7 +20,7 @@ fi
if [[ "$PYTHON" == "2.7" ]] && [[ "$platform" == "linux" ]]; then
sudo apt-get update
sudo apt-get install -y cmake build-essential autoconf curl libtool python-dev python-numpy python-pip libboost-all-dev unzip
sudo pip install funcsigs colorama psutil redis
sudo pip install funcsigs colorama psutil redis tensorflow
sudo pip install --upgrade git+git://github.com/cloudpipe/cloudpickle.git@0d225a4695f1f65ae1cbb2e0bbc145e10167cce4
elif [[ "$PYTHON" == "3.5" ]] && [[ "$platform" == "linux" ]]; then
sudo apt-get update
@@ -29,7 +29,7 @@ elif [[ "$PYTHON" == "3.5" ]] && [[ "$platform" == "linux" ]]; then
wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh
bash miniconda.sh -b -p $HOME/miniconda
export PATH="$HOME/miniconda/bin:$PATH"
pip install numpy funcsigs colorama psutil redis
pip install numpy funcsigs colorama psutil redis tensorflow
pip install --upgrade git+git://github.com/cloudpipe/cloudpickle.git@0d225a4695f1f65ae1cbb2e0bbc145e10167cce4
elif [[ "$PYTHON" == "2.7" ]] && [[ "$platform" == "macosx" ]]; then
# check that brew is installed
@@ -43,7 +43,7 @@ elif [[ "$PYTHON" == "2.7" ]] && [[ "$platform" == "macosx" ]]; then
fi
brew install cmake automake autoconf libtool boost
sudo easy_install pip
sudo pip install numpy funcsigs colorama psutil redis --ignore-installed six
sudo pip install numpy funcsigs colorama psutil redis tensorflow --ignore-installed six
sudo pip install --upgrade git+git://github.com/cloudpipe/cloudpickle.git@0d225a4695f1f65ae1cbb2e0bbc145e10167cce4
elif [[ "$PYTHON" == "3.5" ]] && [[ "$platform" == "macosx" ]]; then
# check that brew is installed
@@ -60,7 +60,7 @@ elif [[ "$PYTHON" == "3.5" ]] && [[ "$platform" == "macosx" ]]; then
wget https://repo.continuum.io/miniconda/Miniconda3-latest-MacOSX-x86_64.sh -O miniconda.sh
bash miniconda.sh -b -p $HOME/miniconda
export PATH="$HOME/miniconda/bin:$PATH"
pip install numpy funcsigs colorama psutil redis
pip install numpy funcsigs colorama psutil redis tensorflow
pip install --upgrade git+git://github.com/cloudpipe/cloudpickle.git@0d225a4695f1f65ae1cbb2e0bbc145e10167cce4
else
echo "Unrecognized environment."
+13 -38
View File
@@ -37,28 +37,14 @@ init = tf.initialize_all_variables()
sess = tf.Session()
```
To extract the weights and set the weights, we need to write a couple lines of
boilerplate code.
To extract the weights and set the weights, you can call
```python
def get_and_set_weights_methods():
assignment_placeholders = []
assignment_nodes = []
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()
variables = ray.experimental.TensorFlowVariables(loss, sess)
```
which gives you methods to set and get the weights as well as collecting all of the variables in the model.
Now we can use these methods to extract the weights, and place them back in the
network as follows.
@@ -66,9 +52,9 @@ network as follows.
# First initialize the weights.
sess.run(init)
# Get the weights
weights = get_weights() # Returns a list of numpy arrays
weights = variables.get_weights() # Returns a dictionary of numpy arrays
# Set the weights
set_weights(weights)
variables.set_weights(weights)
```
**Note:** If we were to set the weights using the `assign` method like below,
@@ -117,20 +103,9 @@ def net_vars_initializer():
init = tf.initialize_all_variables()
sess = tf.Session()
# Additional code for setting and getting the weights.
def get_and_set_weights_methods():
assignment_placeholders = []
assignment_nodes = []
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()
variables = ray.experimental.TensorFlowVariables(loss, sess)
# Return all of the data needed to use the network.
return get_weights, set_weights, sess, train, loss, x_data, y_data, init
return variables, sess, train, loss, x_data, y_data, init
def net_vars_reinitializer(net_vars):
return net_vars
@@ -142,19 +117,19 @@ ray.reusables.net_vars = ray.Reusable(net_vars_initializer, net_vars_reinitializ
# new weights.
@ray.remote
def step(weights, x, y):
get_weights, set_weights, sess, train, _, x_data, y_data, _ = ray.reusables.net_vars
variables, sess, train, _, x_data, y_data, _ = ray.reusables.net_vars
# Set the weights in the network.
set_weights(weights)
variables.set_weights(weights)
# Do one step of training.
sess.run(train, feed_dict={x_data: x, y_data: y})
# Return the new weights.
return get_weights()
return variables.get_weights()
get_weights, set_weights, sess, _, loss, x_data, y_data, init = ray.reusables.net_vars
variables, sess, _, loss, x_data, y_data, init = ray.reusables.net_vars
# Initialize the network weights.
sess.run(init)
# Get the weights as a list of numpy arrays.
weights = get_weights()
weights = variables.get_weights()
# Define a remote function for generating fake data.
@ray.remote(num_return_vals=2)
+1
View File
@@ -3,3 +3,4 @@ from __future__ import division
from __future__ import print_function
from .utils import copy_directory
from .tfutils import TensorFlowVariables
+37
View File
@@ -0,0 +1,37 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
class TensorFlowVariables(object):
"""An object used to extract variables from a loss function, and provide
methods for getting and setting the weights of said variables.
Attributes:
sess (tf.Session): The tensorflow session used to run assignment.
loss: The loss function passed in by the user.
variables (List[tf.Variable]): Extracted variables from the loss.
assignment_placeholders (List[tf.placeholders]): The nodes that weights get passed to.
assignment_nodes (List[tf.Tensor]): The nodes that assign the weights.
"""
def __init__(self, loss, sess):
"""Creates a TensorFlowVariables instance."""
import tensorflow as tf
self.sess = sess
self.loss = loss
variable_names = [op.node_def.name for op in loss.graph.get_operations() if op.node_def.op == "Variable"]
self.variables = [v for v in tf.trainable_variables() if v.op.node_def.name in variable_names]
self.assignment_placeholders = dict()
self.assignment_nodes = []
# Create new placeholders to put in custom weights.
for var in self.variables:
self.assignment_placeholders[var.op.node_def.name] = tf.placeholder(var.value().dtype, var.get_shape().as_list())
self.assignment_nodes.append(var.assign(self.assignment_placeholders[var.op.node_def.name]))
def get_weights(self):
"""Returns the weights of the variables of the loss function in a list."""
return {v.op.node_def.name: v.eval(session=self.sess) for v in self.variables}
def set_weights(self, new_weights):
"""Sets the weights to new_weights."""
self.sess.run(self.assignment_nodes, feed_dict={self.assignment_placeholders[name]: value for (name, value) in new_weights.items()})
+53
View File
@@ -0,0 +1,53 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import unittest
import tensorflow as tf
import ray
class TensorFlowTest(unittest.TestCase):
def testTensorFlowVariables(self):
ray.init(start_ray_local=True, num_workers=2)
x_data = tf.placeholder(tf.float32, shape=[100])
y_data = tf.placeholder(tf.float32, shape=[100])
w = tf.Variable(tf.random_uniform([1], -1.0, 1.0))
b = tf.Variable(tf.zeros([1]))
y = w * x_data + b
loss = tf.reduce_mean(tf.square(y - y_data))
sess = tf.Session()
sess.run(tf.global_variables_initializer())
variables = ray.experimental.TensorFlowVariables(loss, sess)
weights = variables.get_weights()
for (name, val) in weights.items():
weights[name] += 1.0
variables.set_weights(weights)
self.assertEqual(weights, variables.get_weights())
w2 = tf.Variable(tf.random_uniform([1], -1.0, 1.0), name="w")
b2 = tf.Variable(tf.zeros([1]), name="b")
y2 = w2 * x_data + b2
loss2 = tf.reduce_mean(tf.square(y2 - y_data))
sess.run(tf.global_variables_initializer())
variables2 = ray.experimental.TensorFlowVariables(loss2, sess)
weights2 = variables2.get_weights()
for (name, val) in weights2.items():
weights2[name] += 2.0
variables2.set_weights(weights2)
self.assertEqual(weights2, variables2.get_weights())
ray.worker.cleanup()
if __name__ == "__main__":
unittest.main(verbosity=2)