diff options
author | Manjunath Kudlur <keveman@gmail.com> | 2016-02-04 15:23:44 -0800 |
---|---|---|
committer | Vijay Vasudevan <vrv@google.com> | 2016-02-04 15:37:38 -0800 |
commit | 9c7f71244e7e91e83b7ef79ef0841cfe08d2154d (patch) | |
tree | 553e425dc6354ac2342e5c72a24edfc45d46d76a | |
parent | 6607d30417454bec519c6d75c91936d4ab4e9c1c (diff) |
Updated documentation for adding an op.
- Added functions to get the directory where the headers and the library are
installed for use with -I and -L options to the compiler.
Change: 113894868
-rw-r--r-- | tensorflow/g3doc/how_tos/adding_an_op/index.md | 131 | ||||
-rw-r--r-- | tensorflow/g3doc/how_tos/adding_an_op/zero_out_1_test.py | 4 | ||||
-rw-r--r-- | tensorflow/g3doc/how_tos/adding_an_op/zero_out_2_test.py | 6 | ||||
-rw-r--r-- | tensorflow/g3doc/how_tos/adding_an_op/zero_out_3_test.py | 10 | ||||
-rw-r--r-- | tensorflow/g3doc/how_tos/adding_an_op/zero_out_op_1.py | 31 | ||||
-rw-r--r-- | tensorflow/g3doc/how_tos/adding_an_op/zero_out_op_2.py | 33 | ||||
-rw-r--r-- | tensorflow/g3doc/how_tos/adding_an_op/zero_out_op_3.py | 31 | ||||
-rw-r--r-- | tensorflow/python/__init__.py | 5 | ||||
-rw-r--r-- | tensorflow/python/platform/sysconfig.py | 46 | ||||
-rw-r--r-- | tensorflow/tools/pip_package/setup.py | 13 |
10 files changed, 248 insertions, 62 deletions
diff --git a/tensorflow/g3doc/how_tos/adding_an_op/index.md b/tensorflow/g3doc/how_tos/adding_an_op/index.md index 119935f598..38f232104f 100644 --- a/tensorflow/g3doc/how_tos/adding_an_op/index.md +++ b/tensorflow/g3doc/how_tos/adding_an_op/index.md @@ -3,7 +3,10 @@ PREREQUISITES: * Some familiarity with C++. -* Must have [downloaded TensorFlow source](../../get_started/os_setup.md#installing-from-sources), +* Must have installed the + [TensorFlow binary](../../get_started/os_setup.md#pip-installation), or must + have + [downloaded TensorFlow source](../../get_started/os_setup.md#installing-from-sources), and be able to build it. If you'd like to incorporate an operation that isn't covered by the existing @@ -16,13 +19,13 @@ to: * Implement the Op in C++. This implementation is called a "kernel", and there can be multiple kernels for different architectures (e.g. CPUs, GPUs) or input / output types. -* Create a Python wrapper. This wrapper is the public API to create the Op. A - default wrapper is generated from the Op registration, which can be used - directly or added to. +* Optionally, create a Python wrapper. This wrapper is the public API to create + the Op. A default wrapper is generated from the Op registration, which can be + used directly or added to. * Optionally, write a function to compute gradients for the Op. * Optionally, write a function that describes the input and output shapes for the Op. This allows shape inference to work with your Op. -* Test the Op, typically in Python. If you define gradients, you can verify them with the Python [`GradientChecker`](https://www.tensorflow.org/code/tensorflow/python/kernel_tests/gradient_checker.py). +* Test the Op, typically in Python. If you define gradients, you can verify them with the Python [`GradientChecker`](https://www.tensorflow.org/code/tensorflow/python/kernel_tests/gradient_checker.py). [TOC] @@ -106,68 +109,96 @@ To do this for the `ZeroOut` op, add the following to `zero_out.cc`: REGISTER_KERNEL_BUILDER(Name("ZeroOut").Device(DEVICE_CPU), ZeroOutOp); ``` -Once you -[build and reinstall TensorFlow](../../get_started/os_setup.md#create-the-pip-package-and-install), the -Tensorflow system can reference and use the Op when requested. +## Building the Op library +### With TensorFlow binary installation + +You should be able to compile `zero_out.cc` with a `C++` compiler such as `g++` +or `clang` available on your system. The binary PIP package installs the header +files and the library that you need to compile your Op in locations that are +system specific. However, the TensorFlow python library provides functions +`get_include` and `get_lib` to get the header and library directories +respectively. Here is the output of those functions on a Ubuntu machine. + +```bash +$ python +>>> import tensorflow as tf +>>> tf.sysconfig.get_include() +'/usr/local/lib/python2.7/site-packages/tensorflow/include' +>>> tf.sysconfig.get_lib() +'/usr/local/lib/python2.7/site-packages/tensorflow/core' +>>> -## Generate the client wrapper -### The Python Op wrapper - -Python op wrappers are created automatically in -`bazel-genfiles/tensorflow/python/ops/gen_user_ops.py` for all ops placed in the -[`tensorflow/core/user_ops`][user_ops] directory when you build Tensorflow. +``` -> Note: The generated function will be given a snake\_case name (to comply with -> [PEP8](https://www.python.org/dev/peps/pep-0008/)). So if your op is named -> `ZeroOut` in the C++ files, the python function will be called `zero_out`. +Assuming you have `g++` installed, here is the sequence of commands you can use +to compile your Op into a dynamic library. -Those ops are imported into -[`tensorflow/python/user_ops/user_ops.py`][python-user_ops] with the statement: +```bash +$ TF_INC=$(python -c 'import tensorflow as tf; print(tf.sysconfig.get_include())') +$ TF_LIB=$(python -c 'import tensorflow as tf; print(tf.sysconfig.get_lib())') -```python -from tensorflow.python.ops.gen_user_ops import * +$ g++ -std=c++11 -shared zero_out.cc -o zero_out.so \ +-I $TF_INC -l tensorflow_framework -L $TF_LIB \ +-fPIC -Wl,-rpath $TF_LIB ``` -You may optionally use your own function instead. To do this, you first hide -the generated code for that op by adding its name to the `hidden` list in the -`"user_ops"` rule in -[`tensorflow/python/BUILD`](https://www.tensorflow.org/code/tensorflow/python/BUILD): +### With TensorFlow source installation + +If you have TensorFlow sources installed, you can make use of TensorFlow's build +system to compile your Op. The following Bazel build rule would build +`zero_out.so`. ```python -tf_gen_op_wrapper_py( - name = "user_ops", - hidden = [ - "Fact", +cc_binary( + name = "zero_out.so", + srcs = ["zero_out.cc"], + linkopts = [ + "-Wl,-Bsymbolic", + "-lm", + ], + linkshared = 1, + linkstatic = 1, + deps = [ + "//third_party/tensorflow/core:framework", ], - require_shape_functions = False, ) ``` -List your op next to `"Fact"`. Next you add your replacement function to -[`tensorflow/python/user_ops/user_ops.py`](https://www.tensorflow.org/code/tensorflow/python/user_ops/user_ops.py). -Typically your function will call the generated function to actually add the op -to the graph. The hidden version of the generated function will be in the -`gen_user_ops` package and start with an underscore ("`_`"). For example: +## Using the Op in Python + +TensorFlow Python API provides the +[load_op_library](../../api_docs/python/framework#load_op_library) function to +load the dynamic library and register the Op with the TensorFlow +framework. `load_op_library` returns a Python module, that contains the Python +wrappers for the Op. Thus, once you have built the op, you can do the following +to run it from Python : ```python -def my_fact(): - """Example of overriding the generated code for an Op.""" - return gen_user_ops._fact() +import tensorflow as tf +zero_out_module = tf.load_op_library('zero_out.so') +with tf.Session(''): + zero_out_module.zero_out([[1, 2], [3, 4]]).eval() + +# Prints +array([[1, 0], + [0, 0]], dtype=int32) ``` -### The C++ Op wrapper +> Note: The generated function will be given a snake\_case name (to comply with +> [PEP8](https://www.python.org/dev/peps/pep-0008/)). So if your op is named +> `ZeroOut` in the C++ files, the python function will be called `zero_out`. -C++ op wrappers are created automatically for all ops placed in the -[`tensorflow/core/user_ops`][user_ops] directory, when you build Tensorflow. For -example, ops in `tensorflow/core/user_ops/zero_out.cc` will generate wrappers in -`bazel-genfiles/tensorflow/cc/ops/user_ops.{h,cc}`. +To make the Op available as a regular function `import`-able from a Python +module, it maybe useful to have the `load_op_library` call in a Python source +file as follows (see +[zero_out_op_1.py](https://www.tensorflow.org/code/tensorflow/g3doc/how_tos/adding_an_op/zero_out_op_1.py)) +: -All generated wrappers for user ops are automatically -imported into [`tensorflow/cc/ops/standard_ops.h`][standard_ops-cc] with the -statement +```python +import tensorflow as tf -```c++ -#include "tensorflow/cc/ops/user_ops.h" +_zero_out_module = tf.load_op_library('zero_out_op_kernel_1.so') +zero_out = _zero_out_module.zero_out ``` ## Verify it works @@ -179,11 +210,11 @@ test for it. Create the file ```python import tensorflow as tf - class ZeroOutTest(tf.test.TestCase): def testZeroOut(self): + zero_out_module = tf.load_op_library('zero_out.so') with self.test_session(): - result = tf.user_ops.zero_out([5, 4, 3, 2, 1]) + result = zero_out_module.zero_out([5, 4, 3, 2, 1]) self.assertAllEqual(result.eval(), [5, 0, 0, 0, 0]) ``` diff --git a/tensorflow/g3doc/how_tos/adding_an_op/zero_out_1_test.py b/tensorflow/g3doc/how_tos/adding_an_op/zero_out_1_test.py index 84defddc64..3b6efc53d8 100644 --- a/tensorflow/g3doc/how_tos/adding_an_op/zero_out_1_test.py +++ b/tensorflow/g3doc/how_tos/adding_an_op/zero_out_1_test.py @@ -22,14 +22,14 @@ from __future__ import print_function import tensorflow.python.platform import tensorflow as tf -from tensorflow.g3doc.how_tos.adding_an_op import gen_zero_out_op_1 +from tensorflow.g3doc.how_tos.adding_an_op import zero_out_op_1 class ZeroOut1Test(tf.test.TestCase): def test(self): with self.test_session(): - result = gen_zero_out_op_1.zero_out([5, 4, 3, 2, 1]) + result = zero_out_op_1.zero_out([5, 4, 3, 2, 1]) self.assertAllEqual(result.eval(), [5, 0, 0, 0, 0]) diff --git a/tensorflow/g3doc/how_tos/adding_an_op/zero_out_2_test.py b/tensorflow/g3doc/how_tos/adding_an_op/zero_out_2_test.py index 25229213b9..e72dba9f78 100644 --- a/tensorflow/g3doc/how_tos/adding_an_op/zero_out_2_test.py +++ b/tensorflow/g3doc/how_tos/adding_an_op/zero_out_2_test.py @@ -22,7 +22,7 @@ from __future__ import print_function import tensorflow.python.platform import tensorflow as tf -from tensorflow.g3doc.how_tos.adding_an_op import gen_zero_out_op_2 +from tensorflow.g3doc.how_tos.adding_an_op import zero_out_op_2 from tensorflow.g3doc.how_tos.adding_an_op import zero_out_grad_2 @@ -30,14 +30,14 @@ class ZeroOut2Test(tf.test.TestCase): def test(self): with self.test_session(): - result = gen_zero_out_op_2.zero_out([5, 4, 3, 2, 1]) + result = zero_out_op_2.zero_out([5, 4, 3, 2, 1]) self.assertAllEqual(result.eval(), [5, 0, 0, 0, 0]) def test_grad(self): with self.test_session(): shape = (5,) x = tf.constant([5, 4, 3, 2, 1], dtype=tf.float32) - y = gen_zero_out_op_2.zero_out(x) + y = zero_out_op_2.zero_out(x) err = tf.test.compute_gradient_error(x, shape, y, shape) self.assertLess(err, 1e-4) diff --git a/tensorflow/g3doc/how_tos/adding_an_op/zero_out_3_test.py b/tensorflow/g3doc/how_tos/adding_an_op/zero_out_3_test.py index 99ab43c93d..5c9be2f8d5 100644 --- a/tensorflow/g3doc/how_tos/adding_an_op/zero_out_3_test.py +++ b/tensorflow/g3doc/how_tos/adding_an_op/zero_out_3_test.py @@ -22,30 +22,30 @@ from __future__ import print_function import tensorflow.python.platform import tensorflow as tf -from tensorflow.g3doc.how_tos.adding_an_op import gen_zero_out_op_3 +from tensorflow.g3doc.how_tos.adding_an_op import zero_out_op_3 class ZeroOut3Test(tf.test.TestCase): def test(self): with self.test_session(): - result = gen_zero_out_op_3.zero_out([5, 4, 3, 2, 1]) + result = zero_out_op_3.zero_out([5, 4, 3, 2, 1]) self.assertAllEqual(result.eval(), [5, 0, 0, 0, 0]) def testAttr(self): with self.test_session(): - result = gen_zero_out_op_3.zero_out([5, 4, 3, 2, 1], preserve_index=3) + result = zero_out_op_3.zero_out([5, 4, 3, 2, 1], preserve_index=3) self.assertAllEqual(result.eval(), [0, 0, 0, 2, 0]) def testNegative(self): with self.test_session(): - result = gen_zero_out_op_3.zero_out([5, 4, 3, 2, 1], preserve_index=-1) + result = zero_out_op_3.zero_out([5, 4, 3, 2, 1], preserve_index=-1) with self.assertRaisesOpError("Need preserve_index >= 0, got -1"): result.eval() def testLarge(self): with self.test_session(): - result = gen_zero_out_op_3.zero_out([5, 4, 3, 2, 1], preserve_index=17) + result = zero_out_op_3.zero_out([5, 4, 3, 2, 1], preserve_index=17) with self.assertRaisesOpError("preserve_index out of range"): result.eval() diff --git a/tensorflow/g3doc/how_tos/adding_an_op/zero_out_op_1.py b/tensorflow/g3doc/how_tos/adding_an_op/zero_out_op_1.py new file mode 100644 index 0000000000..a54e8ec4c8 --- /dev/null +++ b/tensorflow/g3doc/how_tos/adding_an_op/zero_out_op_1.py @@ -0,0 +1,31 @@ +# Copyright 2015 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +"""ZeroOut op Python library.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +# pylint: disable=g-bad-import-order +# pylint: disable=unused-import +import os.path + +import tensorflow.python.platform + +import tensorflow as tf + +_zero_out_module = tf.load_op_library( + os.path.join(tf.resource_loader.get_data_files_path(), + 'zero_out_op_kernel_1.so')) +zero_out = _zero_out_module.zero_out diff --git a/tensorflow/g3doc/how_tos/adding_an_op/zero_out_op_2.py b/tensorflow/g3doc/how_tos/adding_an_op/zero_out_op_2.py new file mode 100644 index 0000000000..8889e9ae4e --- /dev/null +++ b/tensorflow/g3doc/how_tos/adding_an_op/zero_out_op_2.py @@ -0,0 +1,33 @@ +# Copyright 2015 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +"""ZeroOut ops Python library.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +# pylint: disable=g-bad-import-order +# pylint: disable=unused-import +import os.path + +import tensorflow.python.platform + +import tensorflow as tf + +_zero_out_module = tf.load_op_library( + os.path.join(tf.resource_loader.get_data_files_path(), + 'zero_out_op_kernel_2.so')) +zero_out = _zero_out_module.zero_out +zero_out2 = _zero_out_module.zero_out2 +zero_out3 = _zero_out_module.zero_out3 diff --git a/tensorflow/g3doc/how_tos/adding_an_op/zero_out_op_3.py b/tensorflow/g3doc/how_tos/adding_an_op/zero_out_op_3.py new file mode 100644 index 0000000000..239247f1c7 --- /dev/null +++ b/tensorflow/g3doc/how_tos/adding_an_op/zero_out_op_3.py @@ -0,0 +1,31 @@ +# Copyright 2015 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +"""ZeroOut op Python library.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +# pylint: disable=g-bad-import-order +# pylint: disable=unused-import +import os.path + +import tensorflow.python.platform + +import tensorflow as tf + +_zero_out_module = tf.load_op_library( + os.path.join(tf.resource_loader.get_data_files_path(), + 'zero_out_op_kernel_3.so')) +zero_out = _zero_out_module.zero_out diff --git a/tensorflow/python/__init__.py b/tensorflow/python/__init__.py index 4ac298a3bf..08c815a171 100644 --- a/tensorflow/python/__init__.py +++ b/tensorflow/python/__init__.py @@ -80,12 +80,13 @@ from tensorflow.python.platform import app from tensorflow.python.platform import flags from tensorflow.python.platform import logging from tensorflow.python.platform import resource_loader +from tensorflow.python.platform import sysconfig from tensorflow.python.platform import test # Don't export modules except for the few we really want _whitelist = set([app, compat, contrib, errors, flags, image, learn, logging, nn, - python_io, resource_loader, test, train, unsupported, - user_ops]) + python_io, resource_loader, sysconfig, test, train, + unsupported, user_ops]) # TODO(b/25561952): tf.tensor_util is DEPRECATED. Please avoid. _whitelist.update([tensor_util]) # pylint: disable=undefined-variable __all__ = [name for name, x in locals().items() if not name.startswith('_') and diff --git a/tensorflow/python/platform/sysconfig.py b/tensorflow/python/platform/sysconfig.py new file mode 100644 index 0000000000..8b5b4069d6 --- /dev/null +++ b/tensorflow/python/platform/sysconfig.py @@ -0,0 +1,46 @@ +# Copyright 2015 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== + +"""Load a file resource and return the contents.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import os.path + + +# pylint: disable=g-import-not-at-top +def get_include(): + """Get the directory containing the TensorFlow C++ header files. + + Returns: + The directory as string. + """ + # Import inside the function. + # sysconfig is imported from the tensorflow module, so having this + # import at the top would cause a circular import, resulting in + # the tensorflow module missing symbols that come after sysconfig. + import tensorflow as tf + return os.path.join(os.path.dirname(tf.__file__), 'include') + + +def get_lib(): + """Get the directory containing the TensorFlow framework library. + + Returns: + The directory as string. + """ + import tensorflow as tf + return os.path.join(os.path.dirname(tf.__file__), 'core') diff --git a/tensorflow/tools/pip_package/setup.py b/tensorflow/tools/pip_package/setup.py index 128ad8a52d..44eb136c8d 100644 --- a/tensorflow/tools/pip_package/setup.py +++ b/tensorflow/tools/pip_package/setup.py @@ -99,6 +99,19 @@ class InstallHeaders(Command): # directories for -I install_dir = re.sub('/google/protobuf/src', '', install_dir) + # Copy eigen code into tensorflow/include and + # tensorflow/include/external/eigen_archive/eigen-eigen-<revision>. + # A symlink would do, but the wheel file that gets created ignores + # symlink within the directory hierarchy. + # NOTE(keveman): Figure out how to customize bdist_wheel package so + # we can do the symlink. + if re.search(r'(external/eigen_archive/eigen-eigen-\w+)', install_dir): + extra_dir = re.sub(r'/external/eigen_archive/eigen-eigen-\w+', '', + install_dir) + if not os.path.exists(extra_dir): + self.mkpath(extra_dir) + self.copy_file(header, extra_dir) + if not os.path.exists(install_dir): self.mkpath(install_dir) return self.copy_file(header, install_dir) |