commit 5a9cd25aa9e2338887dc4b6fd4296b6bd26a7f14 Author: Patrick Wieschollek Date: Sun Sep 23 12:07:50 2018 +0200 cherry pick files for public release diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..d48cbda --- /dev/null +++ b/.drone.yml @@ -0,0 +1,10 @@ +pipeline: + custom_op: + image: tensorflow:ubuntu16.04-cuda9.2-bazel0.16.1-tensorflow1.10.0 + environment: + - CUB_INC=/extra/cub-1.8.0/ + commands: + - export LD_LIBRARY_PATH=/usr/local/cuda/lib64/stubs:$${LD_LIBRARY_PATH} + - cd user_ops + - cmake . -DPYTHON_EXECUTABLE=python2 + - make diff --git a/.github/flexconv.jpg b/.github/flexconv.jpg new file mode 100644 index 0000000..e93931c Binary files /dev/null and b/.github/flexconv.jpg differ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f5c4259 --- /dev/null +++ b/.gitignore @@ -0,0 +1,136 @@ +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + +### Python Patch ### +.venv/ + + +### CMake ### +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake + + +### custom ### + +user_ops/.profiling_outputs + +.project +.cproject +tensorflow_config.txt + +.vscode +.cquery + +.settings +.local/ +local/ diff --git a/CPPLINT.cfg b/CPPLINT.cfg new file mode 100644 index 0000000..9b48e1a --- /dev/null +++ b/CPPLINT.cfg @@ -0,0 +1 @@ +filter=-legal/copyright,-readability/casting,-build/include_subdir,-whitespace/tab \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..06e5dd5 --- /dev/null +++ b/README.md @@ -0,0 +1,60 @@ +# Flex-Convolution (Million-Scale Pointcloud Learning Beyond Grid-Worlds) +Fabian Groh, Patrick Wieschollek, Hendrik P.A. Lensch + +![Build Status TensorFlow v1.10 ](https://ci.patwie.com/api/badges/cgtuebingen/Flex-Convolution/status.svg) + +Abstract +------------------- + +Traditional convolution layers are specifically designed to exploit the natural data representation of images -- a fixed and regular grid. However, unstructured data like 3D point clouds containing irregular neighborhoods constantly breaks the grid-based data assumption. Therefore applying best-practices and design choices from 2D-image learning methods towards processing point clouds are not readily possible. In this work, we introduce a natural generalization flex-convolution of the conventional convolution layer along with an efficient GPU implementation. We demonstrate competitive performance on rather small benchmark sets using fewer parameters and lower memory consumption and obtain significant improvements on a million-scale real-world dataset. Ours is the first which allows to efficiently process 7 million points concurrently. + + +The following figure shows the *raw* network semantic segmentation prediction on a real-world example: + +

+ + +This repository contains contains the source code of our FlexConv Layer from our 2018 ACCV paper "Flex-Convolution (Million-Scale Pointcloud Learning Beyond Grid-Worlds)". + +Example - Usage +------------------- + +We provide GPU-tailored cuda implementation of our novel FlexConv in TensorFlow. + +```console +user@host $ cd user_ops +user@host $ cmake . -DPYTHON_EXECUTABLE=python2 && make -j +user@host $ cd .. +user@host $ python example +``` + + +Experiments +------------------- + +Deep learning on point-clouds is a complex matter and our codebase reflect that complexity. +We are currently working on refactoring our research implementation to ease the usage. Therefore, +`layers.py` contains a Keras/tf.layers compatible implementation. + +More Resources +------------------- + +- [Arxiv Pre-Print](https://arxiv.org/abs/1803.07289) +- [Video](https://www.youtube.com/watch?v=5ftWmuQXU_s) + + +Citation +------------------- +If you use the code in this repository, please cite our paper: +``` +@inproceedings{accv2018/Groh, + author = {Fabian Groh and + Patrick Wieschollek and + Hendrik P. A. Lensch + }, + title = {Flex-Convolution (Million-Scale Pointcloud Learning Beyond Grid-Worlds)}, + booktitle = {Asian Conference on Computer Vision (ACCV)}, + month = {Dezember}, + year = {2018} +} +``` diff --git a/example.py b/example.py new file mode 100644 index 0000000..1d07355 --- /dev/null +++ b/example.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright 2017 ComputerGraphics Tuebingen. 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. +# ============================================================================== +# Authors: Fabian Groh, Patrick Wieschollek, Hendrik P.A. Lensch + + +""" +Demonstration of using FlexConvolution Layer. +""" + +import numpy as np +import tensorflow as tf +from layers import flex_convolution + +B, Din, Dout, Dp, N, K = 1, 2, 4, 3, 10, 5 + +features = np.random.randn(B, Din, N).astype(np.float32) +positions = np.random.randn(B, Dp, N).astype(np.float32) +neighbors = np.random.randint(0, N, [B, K, N]).astype(np.int32) + +features = tf.convert_to_tensor(features) +positions = tf.convert_to_tensor(positions) +neighbors = tf.convert_to_tensor(neighbors) + +features2 = flex_convolution(features, positions, neighbors, Dout) +features3 = flex_convolution(features2, positions, neighbors, Dout, trainable=False) + +with tf.Session() as sess: + sess.run(tf.global_variables_initializer()) + sess.run(features2) + sess.run(features3) + + print(tf.trainable_variables()) diff --git a/layers.py b/layers.py new file mode 100644 index 0000000..ebbb13d --- /dev/null +++ b/layers.py @@ -0,0 +1,215 @@ +# -*- coding: utf-8 -*- + +# Copyright 2017 ComputerGraphics Tuebingen. 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. +# ============================================================================== +# Authors: Fabian Groh, Patrick Wieschollek, Hendrik P.A. Lensch + + +import tensorflow as tf +from user_ops import flex_convolution as _flex_convolution +from user_ops import flex_pooling as _flex_pooling +from user_ops import flex_convolution_transpose as _flex_convolution_transpose + +from tensorflow.python.keras import activations +from tensorflow.python.keras import initializers +from tensorflow.python.keras.engine.base_layer import Layer +from tensorflow.python.util.tf_export import tf_export +from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import ops + +flex_pooling = _flex_pooling +flex_convolution_transpose = _flex_convolution_transpose + + +def _remove_dim(x, axis): + shape = x.shape.as_list() + assert shape[axis] == 1 + del shape[axis] + return tf.reshape(x, shape) + + +@tf_export('keras.layers.Dense') +class FlexConvolution(Layer): + """flex convolution layer. + + This layer convolves elements in arbitrary neighborhoods with a kernel to + produce a tensor of outputs. + If `use_feature_bias` is True (and a `features_bias_initializer` is provided), + a bias vector is created and added to the outputs after te convolution. + Finally, if `activation` is not `None`, it is applied to the outputs as well. + + Remarks: + In contrast to traditional convolutions, this operation has two + bias terms: + - bias term when dynamically computing the weight [Din, Dout] + - bias term which is added tot the features [Dout] + + Arguments: + features: A `Tensor` of the format [B, Din, (1), N]. + positions: A `Tensor` of the format [B, Dp, (1), N]. + neighborhoods: A `Tensor` of the format [B, K, (1), N] (tf.int32). + filters: Integer, the dimensionality of the output space (i.e. the number + of filters in the convolution). + activation: Activation function. Set it to None to maintain a + linear activation. + kernel_initializer: An initializer for the convolution kernel. + position_bias_initializer: An initializer for the bias vector within + the convolution. If None, the default initializer will be used. + features_bias_initializer: An initializer for the bias vector after + the convolution. If None, the default initializer will be used. + use_feature_bias: Boolean, whether the layer uses a bias. + data_format: A string, one of `simple` (default) or `expaned`. + If `simple` the shapes are [B, Din, N], when `expanded` the shapes + are assumed to be [B, Din, 1, N] to match `channels_first` in trad + convolutions. + trainable: Boolean, if `True` also add variables to the graph collection + `GraphKeys.TRAINABLE_VARIABLES` (see `tf.Variable`). + name: A string, the name of the layer. + + """ + + def __init__(self, + features, + positions, + neighborhoods, + filters, + activation=None, + kernel_initializer=None, + position_bias_initializer=tf.zeros_initializer(), + features_bias_initializer=tf.zeros_initializer(), + use_feature_bias=True, + data_format='simple', + trainable=True, + name=None): + + super(FlexConvolution, self).__init__(trainable=trainable, + name=name) + self.features = features + self.positions = positions + self.neighborhoods = neighborhoods + + self.filters = int(filters) + self.activation = activations.get(activation) + self.use_feature_bias = use_feature_bias + self.data_format = data_format + self.kernel_initializer = initializers.get(kernel_initializer) + self.position_bias_initializer = initializers.get(position_bias_initializer) + self.features_bias_initializer = initializers.get(features_bias_initializer) + + def compute_output_shape(self, input_shape): + input_shape = tensor_shape.TensorShape(input_shape) + input_shape[1] = self.filters + return input_shape + + def build(self, input_shape): + if self.data_format == 'expanded': + features = _remove_dim(self.features, 2) + positions = _remove_dim(self.positions, 2) + else: + features = self.features + positions = self.positions + + [B, Din, N] = features.shape + Din = int(Din) + N = int(N) + Dp = int(positions.shape[1]) + Dout = self.filters + + self.position_theta = self.add_weight( + 'position_theta', + shape=[1, Dp, Din, Dout], + initializer=self.kernel_initializer, + dtype=self.dtype, + trainable=True) + + self.position_bias = self.add_weight( + 'position_bias', + shape=[Din, Dout], + initializer=self.position_bias_initializer, + dtype=self.dtype, + trainable=True) + + if self.use_feature_bias: + self.feature_bias = self.add_weight( + 'feature_bias', + shape=[Dout, 1], + initializer=self.features_bias_initializer, + dtype=self.dtype, + trainable=True) + else: + self.feature_bias = None + self.built = True + + def call(self, inputs): + + if not isinstance(inputs, list): + raise ValueError('A flexconv layer should be called ' + 'on a list of inputs.') + + features = ops.convert_to_tensor(inputs[0], dtype=self.dtype) + positions = ops.convert_to_tensor(inputs[1], dtype=self.dtype) + neighborhoods = ops.convert_to_tensor(inputs[2], dtype=tf.int32) + + if self.data_format == 'expanded': + features = _remove_dim(inputs[0], 2) + positions = _remove_dim(inputs[1], 2) + neighborhoods = _remove_dim(inputs[2], 2) + else: + features = inputs[0] + positions = inputs[1] + neighborhoods = inputs[2] + + y = _flex_convolution(features, positions, neighborhoods, + self.position_theta, self.position_bias) + + if self.use_feature_bias: + y = tf.add(y, self.feature_bias) + + if self.activation is not None: + y = self.activation(y) + + if self.data_format == 'expanded': + y = tf.expand_dims(y, axis=2) + + return y + + +def flex_convolution(features, + positions, + neighborhoods, + filters, + activation=None, + kernel_initializer=None, + position_bias_initializer=tf.zeros_initializer(), + features_bias_initializer=tf.zeros_initializer(), + use_feature_bias=True, + data_format='simple', + trainable=True, + name=None): + + layer = FlexConvolution(features, + positions, + neighborhoods, + filters, + activation=activation, + kernel_initializer=kernel_initializer, + position_bias_initializer=position_bias_initializer, + features_bias_initializer=features_bias_initializer, + use_feature_bias=use_feature_bias, + data_format=data_format, + trainable=trainable, + name=name) + + return layer.apply([features, positions, neighborhoods]) diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..d882862 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,3 @@ +[flake8] +max-line-length = 120 +ignore = D102,E402,F403,N806,N802,N803,F405,E111,E114 \ No newline at end of file diff --git a/user_ops/CMakeLists.txt b/user_ops/CMakeLists.txt new file mode 100644 index 0000000..7b40ae9 --- /dev/null +++ b/user_ops/CMakeLists.txt @@ -0,0 +1,40 @@ +# University Tuebingen, 2018 +# Authors: Fabian Groh, Patrick Wieschollek, Hendrik P.A. Lensch + +cmake_minimum_required( VERSION 2.8 ) + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +project( FlexConv ) + +list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}) + +find_package(CUDA 9 REQUIRED) +find_package(TensorFlow REQUIRED) + + +if (DEFINED ENV{CUB_INC}) + message(STATUS "Use Cuda-CUB from " $ENV{CUB_INC}) + set(CUB_INC $ENV{CUB_INC}) +else() + message(FATAL_ERROR "requires 'export CUB_INC=/path/to/cub") +endif() + +# set necessary flags +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SSE_FLAGS} -march=native -fopenmp -O3 -D_GLIBCXX_USE_CXX11_ABI=${TensorFlow_ABI}") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fPIC --shared -D_GLIBCXX_USE_CXX11_ABI=${TensorFlow_ABI}" ) +set(CUDA_NVCC_FLAGS "${CUDA_NVCC_FLAGS} -std=c++11 -O3 -Xptxas=-v --expt-relaxed-constexpr -D GOOGLE_CUDA=1 --gpu-architecture=sm_52 -D_GLIBCXX_USE_CXX11_ABI=${TensorFlow_ABI}" ) + +# quick fix for drone-ci +include_directories(SYSTEM "/usr/local/") +# fix cgtuebingen +include_directories(SYSTEM "/graphics/opt/opt_Ubuntu18.04/cuda/toolkit_9.2") + +include_directories(SYSTEM ${CUB_INC}) +include_directories(SYSTEM ${TensorFlow_INCLUDE_DIR}) +include_directories(SYSTEM kernels) + +add_tensorflow_gpu_operation("flex_conv") +add_tensorflow_gpu_operation("flex_deconv") +add_tensorflow_gpu_operation("flex_pool") diff --git a/user_ops/FindTensorFlow.cmake b/user_ops/FindTensorFlow.cmake new file mode 100644 index 0000000..d70a4b3 --- /dev/null +++ b/user_ops/FindTensorFlow.cmake @@ -0,0 +1,273 @@ +# Patrick Wieschollek, +# FindTENSORFLOW.cmake +# https://github.com/PatWie/tensorflow-cmake/blob/master/cmake/modules/FindTensorFlow.cmake +# ------------- +# +# Find TensorFlow library and includes +# +# Automatically set variables have prefix "TensorFlow", +# while variables you need to specify have prefix "TENSORFLOW" +# This module will set the following variables in your project: +# +# ``TensorFlow_VERSION`` +# exact TensorFlow version obtained from runtime +# ``TensorFlow_ABI`` +# ABI specification of TensorFlow library obtained from runtime +# ``TensorFlow_INCLUDE_DIR`` +# where to find tensorflow header files obtained from runtime +# ``TensorFlow_LIBRARY`` +# the libraries to link against to use TENSORFLOW obtained from runtime +# ``TensorFlow_FOUND TRUE`` +# If false, do not try to use TENSORFLOW. +# +# for some examples, you will need to specify on of the following paths +# ``TensorFlow_SOURCE_DIR`` +# Path to source of TensorFlow, when env-var 'TENSORFLOW_SOURCE_DIR' is set and path exists +# ``TensorFlow_C_LIBRARY`` +# Path to libtensorflow_cc.so (require env-var 'TENSORFLOW_BUILD_DIR') +# +# +# USAGE +# ------ +# add "list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}../../path/to/this/file)" to your project +# +# "add_tensorflow_gpu_operation" is a macro to compile a custom operation +# +# add_tensorflow_gpu_operation("") expects the following files to exists: +# - kernels/_kernel.cc +# - kernels/_kernel_gpu.cu.cc (kernels/_kernel.cu is supported as well) +# - kernels/_op.cc +# - kernels/_op.h +# - ops/.cc + +if(APPLE) + message(WARNING "This FindTensorflow.cmake is not tested on APPLE\n" + "Please report if this works\n" + "https://github.com/PatWie/tensorflow-cmake") +endif(APPLE) + +if(WIN32) + message(WARNING "This FindTensorflow.cmake is not tested on WIN32\n" + "Please report if this works\n" + "https://github.com/PatWie/tensorflow-cmake") +endif(WIN32) + +set(PYTHON_EXECUTABLE "python3" CACHE STRING "specify the python version TensorFlow is installed on.") + +if(TensorFlow_FOUND) + # reuse cached variables + message(STATUS "Reuse cached information from TensorFlow ${TensorFlow_VERSION} ") +else() + message(STATUS "Detecting TensorFlow using ${PYTHON_EXECUTABLE}" + " (use -DPYTHON_EXECUTABLE=... otherwise)") + execute_process( + COMMAND ${PYTHON_EXECUTABLE} -c "import tensorflow as tf; print(tf.__version__); print(tf.__cxx11_abi_flag__); print(tf.sysconfig.get_include()); print(tf.sysconfig.get_lib() + '/libtensorflow_framework.so')" + OUTPUT_VARIABLE TF_INFORMATION_STRING + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE retcode) + + if(NOT "${retcode}" STREQUAL "0") + message(FATAL_ERROR "Detecting TensorFlow info - failed \n Did you installed TensorFlow?") + else() + message(STATUS "Detecting TensorFlow info - done") + endif() + + string(REPLACE "\n" ";" TF_INFORMATION_LIST ${TF_INFORMATION_STRING}) + list(GET TF_INFORMATION_LIST 0 TF_DETECTED_VERSION) + list(GET TF_INFORMATION_LIST 1 TF_DETECTED_ABI) + list(GET TF_INFORMATION_LIST 2 TF_DETECTED_INCLUDE_DIR) + list(GET TF_INFORMATION_LIST 3 TF_DETECTED_LIBRARY) + + # set(TF_DETECTED_VERSION 1.8) + + set(_packageName "TF") + if (DEFINED TF_DETECTED_VERSION) + string (REGEX MATCHALL "[0-9]+" _versionComponents "${TF_DETECTED_VERSION}") + list (LENGTH _versionComponents _len) + if (${_len} GREATER 0) + list(GET _versionComponents 0 TF_DETECTED_VERSION_MAJOR) + endif() + if (${_len} GREATER 1) + list(GET _versionComponents 1 TF_DETECTED_VERSION_MINOR) + endif() + if (${_len} GREATER 2) + list(GET _versionComponents 2 TF_DETECTED_VERSION_PATCH) + endif() + if (${_len} GREATER 3) + list(GET _versionComponents 3 TF_DETECTED_VERSION_TWEAK) + endif() + set (TF_DETECTED_VERSION_COUNT ${_len}) + else() + set (TF_DETECTED_VERSION_COUNT 0) + endif() + + + # -- prevent pre 1.9 versions + # Note: TensorFlow 1.7 supported custom ops and all header files. + # TensorFlow 1.8 broke that promise and 1.9, 1.10 are fine again. + # This cmake-file is only tested against 1.9+. + if("${TF_DETECTED_VERSION}" VERSION_LESS "1.9") + message(FATAL_ERROR "Your installed TensorFlow version ${TF_DETECTED_VERSION} is too old.") + endif() + + if(TF_FIND_VERSION_EXACT) + # User requested exact match of TensorFlow. + # TensorFlow release cycles are currently just depending on (major, minor) + # But we test against both. + set(_TensorFlow_TEST_VERSIONS + "${TF_FIND_VERSION_MAJOR}.${TF_FIND_VERSION_MINOR}.${TF_FIND_VERSION_PATCH}" + "${TF_FIND_VERSION_MAJOR}.${TF_FIND_VERSION_MINOR}") + else(TF_FIND_VERSION_EXACT) + # User requested not an exact TensorFlow version. + # However, only TensorFlow versions 1.9, 1.10 support all header files + # for custom ops. + set(_TensorFlow_KNOWN_VERSIONS ${TensorFlow_ADDITIONAL_VERSIONS} + "1.9" "1.9.0" "1.10" "1.10.0") + set(_TensorFlow_TEST_VERSIONS) + + if(TF_FIND_VERSION) + set(_TF_FIND_VERSION_SHORT "${TF_FIND_VERSION_MAJOR}.${TF_FIND_VERSION_MINOR}") + # Select acceptable versions. + foreach(version ${_TensorFlow_KNOWN_VERSIONS}) + if(NOT "${version}" VERSION_LESS "${TF_FIND_VERSION}") + # This version is high enough. + list(APPEND _TensorFlow_TEST_VERSIONS "${version}") + endif() + endforeach(version) + else(TF_FIND_VERSION) + # Any version is acceptable. + set(_TensorFlow_TEST_VERSIONS "${_TensorFlow_KNOWN_VERSIONS}") + endif(TF_FIND_VERSION) + endif() + + # test all given versions + set(TensorFlow_FOUND FALSE) + FOREACH(_TensorFlow_VER ${_TensorFlow_TEST_VERSIONS}) + if("${TF_DETECTED_VERSION_MAJOR}.${TF_DETECTED_VERSION_MINOR}" STREQUAL "${_TensorFlow_VER}") + # found appropriate version + set(TensorFlow_VERSION ${TF_DETECTED_VERSION}) + set(TensorFlow_ABI ${TF_DETECTED_ABI}) + set(TensorFlow_INCLUDE_DIR ${TF_DETECTED_INCLUDE_DIR}) + set(TensorFlow_LIBRARY ${TF_DETECTED_LIBRARY}) + set(TensorFlow_FOUND TRUE) + message(STATUS "Found TensorFlow: (found appropriate version \"${TensorFlow_VERSION}\")") + message(STATUS "TensorFlow-ABI is ${TensorFlow_ABI}") + message(STATUS "TensorFlow-INCLUDE_DIR is ${TensorFlow_INCLUDE_DIR}") + message(STATUS "TensorFlow-LIBRARY is ${TensorFlow_LIBRARY}") + + add_definitions("-DTENSORFLOW_ABI=${TensorFlow_ABI}") + add_definitions("-DTENSORFLOW_VERSION=${TensorFlow_VERSION}") + break() + endif() + ENDFOREACH(_TensorFlow_VER) + + if(NOT TensorFlow_FOUND) + message(FATAL_ERROR "Your installed TensorFlow version ${TF_DETECTED_VERSION_MAJOR}.${TF_DETECTED_VERSION_MINOR} is not supported\n" + "We tested against ${_TensorFlow_TEST_VERSIONS}") + endif(NOT TensorFlow_FOUND) + +endif() + +find_library(TensorFlow_C_LIBRARY + NAMES libtensorflow_cc.so + PATHS $ENV{TENSORFLOW_BUILD_DIR} + DOC "TensorFlow CC library." ) + +if(TensorFlow_C_LIBRARY) + message(STATUS "TensorFlow-CC-LIBRARY is ${TensorFlow_C_LIBRARY}") +else() + message(STATUS "No TensorFlow-CC-LIBRARY detected") +endif() + +find_path(TensorFlow_SOURCE_DIR + NAMES + tensorflow/c + tensorflow/cc + tensorflow/core + tensorflow/core/framework + tensorflow/core/platform + tensorflow/python + third_party + PATHS $ENV{TENSORFLOW_SOURCE_DIR}) + +if(TensorFlow_SOURCE_DIR) + message(STATUS "TensorFlow-SOURCE-DIRECTORY is ${TensorFlow_SOURCE_DIR}") +else() + message(STATUS "No TensorFlow source repository detected") +endif() + +macro(TensorFlow_REQUIRE_C_LIBRARY) + if(TensorFlow_C_LIBRARY) + else() + message(FATAL_ERROR "Project requires libtensorflow_cc.so, please specify the path in ENV-VAR 'TENSORFLOW_BUILD_DIR'") + endif() +endmacro() + +macro(TensorFlow_REQUIRE_SOURCE) + if(TensorFlow_SOURCE_DIR) + else() + message(FATAL_ERROR "Project requires TensorFlow source directory, please specify the path in ENV-VAR 'TENSORFLOW_SOURCE_DIR'") + endif() +endmacro() + +macro(add_tensorflow_cpu_operation op_name) + # Compiles a CPU-only operation without invoking NVCC + message(STATUS "will build custom TensorFlow operation \"${op_name}\" (CPU only)") + + add_library(${op_name}_op SHARED kernels/${op_name}_op.cc kernels/${op_name}_kernel.cc ops/${op_name}.cc ) + + set_target_properties(${op_name}_op PROPERTIES PREFIX "") + target_link_libraries(${op_name}_op LINK_PUBLIC ${TensorFlow_LIBRARY}) +endmacro() + + +macro(add_tensorflow_gpu_operation op_name) +# Compiles a CPU + GPU operation with invoking NVCC + message(STATUS "will build custom TensorFlow operation \"${op_name}\" (CPU+GPU)") + + set(kernel_file "") + if(EXISTS "kernels/${op_name}_kernel.cu") + message(WARNING "you should rename your file ${op_name}_kernel.cu to ${op_name}_kernel_gpu.cu.cc") + set(kernel_file kernels/${op_name}_kernel.cu) + else() + set_source_files_properties(kernels/${op_name}_kernel_gpu.cu.cc PROPERTIES CUDA_SOURCE_PROPERTY_FORMAT OBJ) + set(kernel_file kernels/${op_name}_kernel_gpu.cu.cc) + endif() + + cuda_add_library(${op_name}_op_cu SHARED ${kernel_file}) + set_target_properties(${op_name}_op_cu PROPERTIES PREFIX "") + + add_library(${op_name}_op SHARED kernels/${op_name}_op.cc kernels/${op_name}_kernel.cc ops/${op_name}.cc ) + + set_target_properties(${op_name}_op PROPERTIES PREFIX "") + set_target_properties(${op_name}_op PROPERTIES COMPILE_FLAGS "-DGOOGLE_CUDA") + target_link_libraries(${op_name}_op LINK_PUBLIC ${op_name}_op_cu ${TensorFlow_LIBRARY}) +endmacro() + +# simplify TensorFlow dependencies +add_library(TensorFlow_DEP INTERFACE) +TARGET_INCLUDE_DIRECTORIES(TensorFlow_DEP INTERFACE ${TensorFlow_SOURCE_DIR}) +TARGET_INCLUDE_DIRECTORIES(TensorFlow_DEP INTERFACE ${TensorFlow_INCLUDE_DIR}) +TARGET_LINK_LIBRARIES(TensorFlow_DEP INTERFACE -Wl,--allow-multiple-definition -Wl,--whole-archive ${TensorFlow_C_LIBRARY} -Wl,--no-whole-archive) +TARGET_LINK_LIBRARIES(TensorFlow_DEP INTERFACE -Wl,--allow-multiple-definition -Wl,--whole-archive ${TensorFlow_LIBRARY} -Wl,--no-whole-archive) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + TENSORFLOW + FOUND_VAR TENSORFLOW_FOUND + REQUIRED_VARS + TensorFlow_LIBRARY + TensorFlow_INCLUDE_DIR + VERSION_VAR + TensorFlow_VERSION + ) + +mark_as_advanced(TF_INFORMATION_STRING TF_DETECTED_VERSION TF_DETECTED_VERSION_MAJOR TF_DETECTED_VERSION_MINOR TF_DETECTED_VERSION TF_DETECTED_ABI + TF_DETECTED_INCLUDE_DIR TF_DETECTED_LIBRARY + TensorFlow_C_LIBRARY TensorFlow_LIBRARY TensorFlow_SOURCE_DIR TensorFlow_INCLUDE_DIR TensorFlow_ABI) + +SET(TensorFlow_INCLUDE_DIR ${TensorFlow_INCLUDE_DIR} CACHE PATH "path to tensorflow header files") +SET(TensorFlow_VERSION ${TensorFlow_VERSION} CACHE INTERNAL "The Python executable Version") +SET(TensorFlow_ABI ${TensorFlow_ABI} CACHE STRING "The Python executable Version") +SET(TensorFlow_LIBRARY ${TensorFlow_LIBRARY} CACHE PATH "The Python executable Version") +SET(TensorFlow_FOUND ${TensorFlow_FOUND} CACHE BOOL "The Python executable Version") \ No newline at end of file diff --git a/user_ops/PointTestCase.py b/user_ops/PointTestCase.py new file mode 100644 index 0000000..81c634a --- /dev/null +++ b/user_ops/PointTestCase.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright 2017 ComputerGraphics Tuebingen. 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. +# ============================================================================== +# Authors: Fabian Groh, Patrick Wieschollek, Hendrik P.A. Lensch + + +import numpy as np +import tensorflow as tf + +np.random.seed(42) +tf.set_random_seed(42) + + +class FakePointCloud(object): + """docstring for FakePointCloud""" + + def __init__(self, B, N, K, Din, Dout, Dp, N2, scaling=1): + super(FakePointCloud, self).__init__() + assert K < N + self.B = B + self.N = N + self.K = K + self.Din = Din + self.Dout = Dout + self.Dp = Dp + self.N2 = N2 + + def expected_feature_shape(self): + return [self.B, self.Din, self.N] + + def expected_output_shape(self): + return [self.B, self.Dout, self.N] + + +def random_values(shape, human_readable=False): + """Return random values within range [-10, 10] and precision 2 + """ + length = np.prod(shape) + return np.arange(length).astype(np.float32).reshape(shape) / float(length) + + +def summary(numeric_grad, graph_grad, name, eps=0.001, max_outputs=20): + a, b = numeric_grad.flatten(), graph_grad.flatten() + print("summary: %s" % name) + print("\ttheirs\t\tours\t\tabs-diff") + for i in range(np.prod(numeric_grad.shape)): + if np.abs(a[i] - b[i]) > eps and max_outputs > 0: + print('%i\t%f\t%f\t%f' % (i, a[i], b[i], np.abs(a[i] - b[i]))) + max_outputs -= 1 + if max_outputs == 20: + for i in range(max_outputs): + print('%i\t%f\t%f\t%f' % (i, a[i], b[i], np.abs(a[i] - b[i]))) + # print( np.stack([numeric_grad, graph_grad], axis=-1) + print("%s - abs-diff (sum): " % name, np.abs( + graph_grad - numeric_grad).sum()) + print("%s - abs-diff (max): " % name, np.abs( + graph_grad - numeric_grad).max()) + print("%s - abs-diff (mean): " % name, np.abs( + graph_grad - numeric_grad).mean()) + + +# TestPointCloud(B, N, K, Din, Dout, Dp, N2) +# TPC = FakePointCloud(2, 32, 16, 5, 6, 3, 16) +# TPC = FakePointCloud(2, 16, 8, 5, 6, 3, 8) +TPC = FakePointCloud(2, 32, 4, 2, 6, 3, 16) +# TPC = FakePointCloud(2, 64, 8, 1, 6, 3, 64) + + +class PointTestCase(tf.test.TestCase): + + def __init__(self, methodName="runTest", data=None): + + if data is None: + data = TPC + + self.position = random_values([data.B, data.Dp, data.N]) + self.features = random_values([data.B, data.Din, data.N]) + + # make sure, each neighbor hood has no duplicates and first entry is point n + # THIS IS IMPORTANT!! + self.neighborhood = np.zeros((data.B, data.K, data.N), dtype=np.int32) + for b in range(data.B): + for n in range(data.N): + x = np.arange(data.N) + # does not support axis, hence the loop + np.random.shuffle(x) + offset = np.argwhere(x == n)[0][0] + # roll array such that n is first entry + x = np.roll(x, -offset) + self.neighborhood[b, :, n] = x[:data.K].astype(np.int32) + + self.neighborhood_ds = np.zeros((data.B, data.K, data.N2), dtype=np.int32) + for b in range(data.B): + for n in range(data.N2): + x = np.arange(data.N2) + # does not support axis, hence the loop + np.random.shuffle(x) + offset = np.argwhere(x == n)[0][0] + # roll array such that n is first entry + x = np.roll(x, -offset) + self.neighborhood[b, :, n] = x[:data.K].astype(np.int32) + + self.theta = random_values([1, data.Dp, data.Din, data.Dout]) + self.bias = random_values([data.Din, data.Dout]) + + super(PointTestCase, self).__init__(methodName) + + def init_ops(self): + # needs to be called in each method, otherwise graph is empty + # probably tf.reset_graph between calls + self.features_op = tf.convert_to_tensor(self.features) + self.position_op = tf.convert_to_tensor(self.position) + self.neighborhood_op = tf.convert_to_tensor(self.neighborhood) + self.neighborhood_ds_op = tf.convert_to_tensor(self.neighborhood_ds) + self.theta_op = tf.convert_to_tensor(self.theta) + self.bias_op = tf.convert_to_tensor(self.bias) diff --git a/user_ops/__init__.py b/user_ops/__init__.py new file mode 100644 index 0000000..5e1862b --- /dev/null +++ b/user_ops/__init__.py @@ -0,0 +1,176 @@ +# Copyright 2018 ComputerGraphics Tuebingen. 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. +# ============================================================================ +"""Tensorflow op performing flex convolution operation.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import tensorflow as tf +from tensorflow.python.framework import ops +from tensorflow.contrib.util import loader +from tensorflow.python.platform import resource_loader + +_flex_convolution_op_so = loader.load_op_library( + resource_loader.get_path_to_datafile("flex_conv_op.so")) + +_flex_pooling_op_so = loader.load_op_library( + resource_loader.get_path_to_datafile("flex_pool_op.so")) + +_flex_deconvolution_op_so = loader.load_op_library( + resource_loader.get_path_to_datafile("flex_deconv_op.so")) + + +# undocumented version +flex_conv = _flex_convolution_op_so.flex_conv +flex_conv_grad = _flex_convolution_op_so.flex_conv_grad +flex_pool = _flex_pooling_op_so.flex_pool +flex_pool_grad = _flex_pooling_op_so.flex_pool_grad +flex_deconv = _flex_deconvolution_op_so.flex_deconv +flex_deconv_grad = _flex_deconvolution_op_so.flex_deconv_grad + +# pylint: disable=redefined-builtin + + +def flex_convolution(features, + position, + neighborhood, + theta, + bias, + name=None): + """Flex-Convolution computation. + + Computes a convolution over arbitrary neighborhoods with elements of + arbitrary positions: + + output(c', l) = sum_{c} sum_{l'} w(c, l, l') * f(c, l') + + Args: + features: A `Tensor` of the format [B, Din, N]. + position: A `Tensor` of the format [B, Dp, N]. + neighborhood: A `Tensor` of the format [B, K, N] (tf.int32). + theta: A `Tensor` of the format [1, Dp, Din, Dout]. + bias: A `Tensor` of the format [Din, Dout]. + name: A name for the operation (optional). + + Returns: + A `Tensor` of the format [B, Dout, N]. + """ + + with ops.name_scope(name, "flex_convolution"): + return flex_conv(features, theta, bias, neighborhood, position) + + +@ops.RegisterGradient("FlexConv") +def _FlexConvGrad(op, *grads): # noqa + features = ops.convert_to_tensor(op.inputs[0]) + theta = ops.convert_to_tensor(op.inputs[1]) + bias = ops.convert_to_tensor(op.inputs[2]) + neighborhood = ops.convert_to_tensor(op.inputs[3], dtype=tf.int32) + positions = ops.convert_to_tensor(op.inputs[4]) + topdiff = ops.convert_to_tensor(grads[0]) + + df, dt, db = flex_conv_grad( + features, theta, bias, neighborhood, positions, topdiff) + + df = ops.convert_to_tensor(df, name='gradient_features') + dt = ops.convert_to_tensor(dt, name='gradient_theta') + db = ops.convert_to_tensor(db, name='gradient_bias') + + return [df, dt, db, None, None] + + +# pylint: disable=redefined-builtin +def flex_pooling(features, + neighborhood, + name=None): + """Flex-Pooling computation. + + Computes a pooling over arbitrary neighborhoods: + + output(n) = max_l' f(l') + + Args: + features: A `Tensor` of the format [B, D, N]. + neighborhood: A `Tensor` of the format [B, K, N] (tf.int32). + name: A name for the operation (optional). + + Returns: + A `Tensor` of the format [B, D, N] containing the max values. + A `Tensor` of the format [B, D, N] containing the max indicies. + """ + + with ops.name_scope(name, "flex_pooling"): + return flex_pool(features, neighborhood) + + +@ops.RegisterGradient("FlexPool") +def _FlexPoolGrad(op, *grads): # noqa + features = ops.convert_to_tensor(op.inputs[0]) + neighborhood = ops.convert_to_tensor(op.inputs[1]) + argmax = ops.convert_to_tensor(op.outputs[1]) + topdiff = ops.convert_to_tensor(grads[0]) + + df = flex_pool_grad(features, neighborhood, topdiff, argmax) + df = ops.convert_to_tensor(df, name='gradient_features') + + return [df, None] + + +# pylint: disable=redefined-builtin +def flex_convolution_transpose(features, + position, + neighborhood, + theta, + bias, + name=None): + """Flex-Convolution computation. + + Computes a tranposed convolution over arbitrary neighborhoods with elements of + arbitrary positions. + + Args: + features: A `Tensor` of the format [B, Din, N]. + position: A `Tensor` of the format [B, Dp, N]. + neighborhood: A `Tensor` of the format [B, K, N] (tf.int32). + theta: A `Tensor` of the format [1, Dp, Din, Dout]. + bias: A `Tensor` of the format [Din, Dout]. + name: A name for the operation (optional). + + Returns: + A `Tensor` of the format [B, Dout, N]. + """ + + with ops.name_scope(name, "flex_convolution_transpose"): + return flex_deconv(features, theta, bias, neighborhood, position) + + +@ops.RegisterGradient("FlexDeconv") +def _FlexDeconvGrad(op, *grads): # noqa + features = ops.convert_to_tensor(op.inputs[0]) + theta = ops.convert_to_tensor(op.inputs[1]) + bias = ops.convert_to_tensor(op.inputs[2]) + neighborhood = ops.convert_to_tensor(op.inputs[3], dtype=tf.int32) + positions = ops.convert_to_tensor(op.inputs[4]) + topdiff = ops.convert_to_tensor(grads[0]) + + df, dt, db = flex_deconv_grad( + features, theta, bias, neighborhood, positions, topdiff) + + df = ops.convert_to_tensor(df, name='gradient_features') + dt = ops.convert_to_tensor(dt, name='gradient_theta') + db = ops.convert_to_tensor(db, name='gradient_bias') + + return [df, dt, db, None, None] diff --git a/user_ops/kernels/flex_conv_kernel.cc b/user_ops/kernels/flex_conv_kernel.cc new file mode 100644 index 0000000..5ad7c96 --- /dev/null +++ b/user_ops/kernels/flex_conv_kernel.cc @@ -0,0 +1,173 @@ +/* Copyright 2017 ComputerGraphics Tuebingen. 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. +==============================================================================*/ +//Authors: Fabian Groh, Patrick Wieschollek, Hendrik P.A. Lensch + +#include "flex_conv_op.h" +#include "tensorflow/core/framework/op.h" + +namespace tensorflow { + +namespace functor { + +template +struct FlexConvFunctor { + void operator()(::tensorflow::OpKernelContext* ctx, const Tensor& features_, + const Tensor& theta_, const Tensor& bias_, + const Tensor& neighborhood_, const Tensor& positions_, + Tensor* output_) { + const auto features = features_.tensor(); + const auto theta = theta_.tensor(); + const auto bias = bias_.tensor(); + const auto neighborhood = neighborhood_.tensor(); + const auto positions = positions_.tensor(); + + auto output = output_->tensor(); + + // get dimensions + const int B = neighborhood_.dim_size(0); + const int K = neighborhood_.dim_size(1); + const int N = neighborhood_.dim_size(2); + const int Dp = theta_.dim_size(1); + const int Din = theta_.dim_size(2); + const int Dout = theta_.dim_size(3); + + output.setZero(); + + for (int b = 0; b < B; ++b) { + for (int n = 0; n < N; ++n) { + for (int k_ = 0; k_ < K; ++k_) { + int k = neighborhood(b, k_, n); + + for (int dout = 0; dout < Dout; ++dout) { + for (int din = 0; din < Din; ++din) { + const Dtype v = features(b, din, k); + + Dtype W = bias(din, dout); + for (int dp = 0; dp < Dp; ++dp) { + Dtype delta = positions(b, dp, k) - + positions(b, dp, neighborhood(b, 0, n)); + W += theta(0, dp, din, dout) * delta; + } + output(b, dout, n) = output(b, dout, n) + W * v; + } + } + } + } + } + } +}; + +template struct FlexConvFunctor; + +template +struct FlexConvGrad { + void operator()(::tensorflow::OpKernelContext* ctx, const Tensor& features_, + const Tensor& theta_, const Tensor& bias_, + const Tensor& neighborhood_, const Tensor& positions_, + const Tensor& topdiff_, Tensor* grad_features_, + Tensor* grad_theta_, Tensor* grad_bias_) { + const auto features = features_.tensor(); + const auto theta = theta_.tensor(); + const auto bias = bias_.tensor(); + const auto neighborhood = neighborhood_.tensor(); + const auto positions = positions_.tensor(); + const auto topdiff = topdiff_.tensor(); + + auto grad_features = grad_features_->tensor(); + auto grad_theta = grad_theta_->tensor(); + auto grad_bias = grad_bias_->tensor(); + + // get dimensions + const int B = neighborhood_.dim_size(0); + const int K = neighborhood_.dim_size(1); + const int N = neighborhood_.dim_size(2); + const int Ddegree = theta_.dim_size(0); + const int Dp = theta_.dim_size(1); + const int Din = theta_.dim_size(2); + const int Dout = theta_.dim_size(3); + + grad_features.setZero(); + grad_theta.setZero(); + grad_bias.setZero(); + + // ========================= bias ============================== + for (int b = 0; b < B; ++b) { + for (int n = 0; n < N; ++n) { + for (int k_ = 0; k_ < K; ++k_) { + int k = neighborhood(b, k_, n); + + for (int j = 0; j < Din; ++j) { + for (int l = 0; l < Dout; ++l) { + grad_bias(j, l) += features(b, j, k) * topdiff(b, l, n); + } + } + } + } + } + + // ========================= theta ============================== + for (int b = 0; b < B; ++b) { + for (int n = 0; n < N; ++n) { + for (int k_ = 0; k_ < K; ++k_) { + int k = neighborhood(b, k_, n); + + for (int j = 0; j < Din; ++j) { + for (int l = 0; l < Dout; ++l) { + for (int i = 0; i < Dp; ++i) { + const Dtype delta = + positions(b, i, k) - positions(b, i, neighborhood(b, 0, n)); + // printf("delta %f\n", delta); + + for (int dd = 0; dd < Ddegree; ++dd) { + grad_theta(dd, i, j, l) += + features(b, j, k) * pow(delta, dd + 1) * topdiff(b, l, n); + } + } + } + } + } + } + } + + // ========================= features ============================== + for (int b = 0; b < B; ++b) { + for (int n = 0; n < N; ++n) { + for (int k_ = 0; k_ < K; ++k_) { + int k = neighborhood(b, k_, n); + + for (int j = 0; j < Din; ++j) { + for (int l = 0; l < Dout; ++l) { + Dtype W = bias(j, l); + for (int i = 0; i < Dp; ++i) { + const Dtype delta = + positions(b, i, k) - positions(b, i, neighborhood(b, 0, n)); + for (int dd = 0; dd < Ddegree; ++dd) + W += theta(dd, i, j, l) * pow(delta, dd + 1); + } + grad_features(b, j, k) += W * topdiff(b, l, n); + } + } + } + } + } + } +}; + +// template struct FlexConvGrad; +template struct FlexConvGrad; +// template struct FlexConvGrad; + +} // namespace functor +} // namespace tensorflow diff --git a/user_ops/kernels/flex_conv_kernel_gpu.cu.cc b/user_ops/kernels/flex_conv_kernel_gpu.cu.cc new file mode 100644 index 0000000..3925bbf --- /dev/null +++ b/user_ops/kernels/flex_conv_kernel_gpu.cu.cc @@ -0,0 +1,532 @@ +/* Copyright 2017 ComputerGraphics Tuebingen. 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. +==============================================================================*/ +//Authors: Fabian Groh, Patrick Wieschollek, Hendrik P.A. Lensch + +#if GOOGLE_CUDA + +#define EIGEN_USE_GPU + +#include + +#include "flex_conv_op.h" +#include "tensorflow/core/util/cuda_kernel_helper.h" + +namespace FlexConvCuda { + +using CudaLaunchConfig = ::tensorflow::CudaLaunchConfig; + +constexpr __host__ __device__ int pmin(int x, int y) { return x <= y ? x : y; } + +template +struct ForwardKernel; + +template +__global__ void runForwardKernel( + const ForwardKernel kernel) { + kernel(); +} + +template +struct ForwardKernel { + enum { + PMIN = 3 // only for unrolling + }; + + void launch(int B) { + dim3 block(C_N); + dim3 grid((N - 1) / C_N + 1, (Dout - 1) / C_Dout + 1, B); + + size_t shm_size = (Dp + 1) * C_Din * C_Dout * sizeof(Dtype); + + runForwardKernel<<>>((*this)); + } + + __device__ __forceinline__ void operator()() const { + extern __shared__ Dtype s_shm[]; + + Dtype* s_theta = (float*)&s_shm[0]; + Dtype* s_bias = (float*)&s_shm[Dp * C_Din * C_Dout]; + + // glob ids + int b = blockIdx.z; + int n = blockIdx.x * C_N + threadIdx.x; + + Dtype result[C_Dout]; + for (int dout = 0; dout < C_Dout; ++dout) { + result[dout] = 0.0; + } + + Dtype p0[Dp]; +#pragma unroll pmin(Dp, PMIN) + for (int dp = 0; dp < Dp && n < N; ++dp) { + p0[dp] = d_positions[b * Dp * N + dp * N + n]; + } + + for (int o_din = 0; o_din < Din; o_din += C_Din) { + // load shm + __syncthreads(); + for (int tid = threadIdx.x; tid < Dp * C_Din * C_Dout; tid += C_N) { + int dp = tid / (C_Din * C_Dout); + int din = (tid % (C_Din * C_Dout)) / C_Dout; + int dout = tid % C_Dout; + + int g_dout = (dout + blockIdx.y * C_Dout); + int g_din = o_din + din; + + if (g_dout < Dout && g_din < Din) { + s_theta[dp * C_Din * C_Dout + din * C_Dout + dout] = + d_theta[dp * Din * Dout + g_din * Dout + g_dout]; + + if (!dp) s_bias[din * C_Dout + dout] = d_bias[g_din * Dout + g_dout]; + } + } + __syncthreads(); + + if (n < N) { + // Loop over K + for (int k = 0; k < K && n < N; ++k) { + NBtype nk = d_neighborhood[b * K * N + k * N + n]; + + Dtype q[Dp]; +#pragma unroll pmin(Dp, PMIN) + for (int dp = 0; dp < Dp; ++dp) { + q[dp] = d_positions[b * Dp * N + dp * N + nk] - p0[dp]; + } + + // Loop over Din + for (int din = 0; din < C_Din && (o_din + din) < Din; ++din) { + Dtype fk = d_features[b * Din * N + (o_din + din) * N + nk]; + + // Loop over partial Dout + for (int dout = 0; + dout < C_Dout && (dout + blockIdx.y * C_Dout) < Dout; ++dout) { + Dtype w = 0.0; + + for (int dp = 0; dp < Dp; ++dp) + w += q[dp] * s_theta[dp * C_Din * C_Dout + din * C_Dout + dout]; + w += s_bias[din * C_Dout + dout]; + result[dout] += w * fk; + } + } + } + } + } + + for (int dout = 0; + dout < C_Dout && (dout + blockIdx.y * C_Dout) < Dout && n < N; + ++dout) { + d_output[b * Dout * N + (dout + blockIdx.y * C_Dout) * N + n] = + result[dout]; + } + } + + // features: incoming features [B, Din, N]. + // position: each datapoint in nd space [B, Dp, N]. + // neighborhood: all K nearest neighbors [B, K, N]. + const Dtype* d_features; + const Dtype* d_positions; + const NBtype* d_neighborhood; + + // theta: parameters for kernel function [Dp, + // Din, Dout]. bias: parameters for kernel function [Din, Dout]. + const Dtype* d_theta; + const Dtype* d_bias; + + // output: each feature description for each point [B, Dout, N]. + Dtype* d_output; + + int N; + int K; + int Din; + int Dout; +}; + +template +struct BackwardThetaKernel; + +template +__global__ void runBackwardKernel(const BackwardThetaKernel kernel) { + kernel(); +} + +template +struct BackwardThetaKernel { + enum { C_N = 256, DP_MAX = 3, DEGREE_MAX = 2 }; + + void launch() { + dim3 block(C_N); + dim3 grid(Dout, Din); + + runBackwardKernel<<>>((*this)); + } + + __device__ __forceinline__ void operator()() const { + typedef cub::BlockReduce BlockReduce; + __shared__ typename BlockReduce::TempStorage temp_storage; + + Dtype theta_diff[DP_MAX]; + for (int dp = 0; dp < Dp; ++dp) theta_diff[dp] = 0; + + Dtype bias_diff = 0; + + int dout = blockIdx.x; + int din = blockIdx.y; + + for (int b = 0; b < B; ++b) { + for (int n = threadIdx.x; n < N; n += C_N) { + Dtype topdiff = d_topdiff[b * Dout * N + dout * N + n]; + + for (int k = 0; k < K; ++k) { + int nk0 = d_neigh[b * N * K + 0 * N + n]; + int nk = d_neigh[b * N * K + k * N + n]; + + Dtype feature = d_features[b * Din * N + din * N + nk]; + for (int dp = 0; dp < Dp; ++dp) { + Dtype diffpos = d_pos[b * Dp * N + dp * N + nk] - + d_pos[b * Dp * N + dp * N + nk0]; + theta_diff[dp] += feature * diffpos * topdiff; + } + bias_diff += feature * topdiff; + } + } + } + + for (int dp = 0; dp < Dp; ++dp) { + // for (int dd = 0; dd < Ddegree; ++dd) { + Dtype thread_data = theta_diff[dp]; + Dtype aggregate = BlockReduce(temp_storage).Sum(thread_data, N); + + if (!threadIdx.x) { + d_theta_out[dp * Din * Dout + din * Dout + dout] = aggregate; + } + // } + __syncthreads(); + } + + Dtype thread_data = bias_diff; + + Dtype aggregate = BlockReduce(temp_storage).Sum(thread_data, N); + + if (!threadIdx.x) d_bias_out[din * Dout + dout] = aggregate; + } + + const Dtype* d_topdiff; + + const Dtype* d_pos; + const Dtype* d_features; + const int* d_neigh; + + const Dtype* d_theta; + const Dtype* d_bias; + + Dtype* d_theta_out; + Dtype* d_bias_out; + + int B; + int N; + int K; + int Ddegree; + int Dp; + int Din; + int Dout; +}; + +template +struct BackwardFeatureKernel; + +template +__global__ void runBackwardKernel(const BackwardFeatureKernel kernel) { + kernel(); +} + +template +struct BackwardFeatureKernel { + enum { + C_N = 32, + C_Dout = 32, // multiple of Warpsize is better + + C_Din = 8 // reduce first + }; + + void launch(int B) { + dim3 fblock(C_N, C_Din); + dim3 fgrid((N - 1) / C_N + 1, (Din - 1) / C_Din + 1, B); + + const int theta_size = Dp * C_Din * C_Dout; + const int bias_size = C_Din * C_Dout; + const int topdiff_size = C_N * C_Dout; + const int pos_size = C_N * K * Dp; + const int nk_size = C_N * K; + + int shm = + (theta_size + bias_size + topdiff_size + pos_size) * sizeof(Dtype) + + (nk_size) * sizeof(int); + + runBackwardKernel<<>>((*this)); + } + + __device__ __forceinline__ void operator()() const { + extern __shared__ float s_shm[]; + + int i_n = threadIdx.x; + int i_din = threadIdx.y; + + int b = blockIdx.z; + int n = blockIdx.x * C_N + i_n; + int din = blockIdx.y * C_Din + i_din; + + Dtype* s_theta = (Dtype*)&s_shm[0]; + Dtype* s_bias = (Dtype*)&s_theta[Dp * C_Din * C_Dout]; + Dtype* s_topdiff = (Dtype*)&s_bias[C_Din * C_Dout]; + Dtype* s_pos = (Dtype*)&s_topdiff[C_N * C_Dout]; + int* s_nk = (int*)&s_pos[C_N * K * Dp]; + + for (int k = threadIdx.y; k < K && n < N; k += blockDim.y) { + int nk = d_neigh[b * K * N + k * N + n]; + s_nk[k * C_N + i_n] = nk; + + for (int i_dp = 0; i_dp < Dp; ++i_dp) { + s_pos[k * C_N * Dp + i_dp * C_N + i_n] = + d_pos[b * Dp * N + i_dp * N + nk]; + } + } + + __syncthreads(); + + for (int i_dp = 0; i_dp < Dp; ++i_dp) { + Dtype val0 = s_pos[0 * C_N * Dp + i_dp * C_N + i_n]; + __syncthreads(); + for (int k = threadIdx.y; k < K && n < N; k += blockDim.y) { + s_pos[k * C_N * Dp + i_dp * C_N + i_n] -= val0; + } + } + + for (int dout_outer = 0; dout_outer < (Dout - 1) / C_Dout + 1; + ++dout_outer) { + __syncthreads(); + + // fill s_theta + int dout = dout_outer * C_Dout + i_n; + if (din < Din && dout < Dout) { + for (int i_dp = 0; i_dp < Dp; ++i_dp) + s_theta[i_dp * C_Din * C_Dout + i_din * C_Dout + i_n] = + d_theta[i_dp * Din * Dout + din * Dout + dout]; + + s_bias[i_din * C_Dout + i_n] = d_bias[din * Dout + dout]; + } + + if (n < N) { + for (int i_dout = threadIdx.y; + i_dout < C_Dout && (dout_outer * C_Dout + i_dout) < Dout; + i_dout += blockDim.y) + s_topdiff[i_dout * C_N + i_n] = + d_topdiff[b * Dout * N + (dout_outer * C_Dout + i_dout) * N + n]; + } + + for (int dout_inner = 0; + dout_inner < C_Dout && (dout_outer * C_Dout + dout_inner) < Dout; + ++dout_inner) { + for (int k = 0; k < K; k++) { + __syncthreads(); + + if (n < N && din < Din) { + Dtype W = 0; + for (int dp = 0; dp < Dp; ++dp) { + const Dtype diffpos = s_pos[k * C_N * Dp + dp * C_N + i_n]; + W += s_theta[dp * C_Din * C_Dout + i_din * C_Dout + dout_inner] * + diffpos; + } + W += s_bias[i_din * C_Dout + dout_inner]; + Dtype value = W * s_topdiff[dout_inner * C_N + i_n]; + + atomicAdd( + &d_features_out[b * Din * N + din * N + s_nk[k * C_N + i_n]], + value); + } + } + } + } + } + + const Dtype* d_topdiff; + const Dtype* d_pos; + const Dtype* d_features; + const int* d_neigh; + const Dtype* d_theta; + const Dtype* d_bias; + + Dtype* d_features_out; + + int N; + int K; + int Dp; + int Din; + int Dout; +}; + +} // namespace FlexConvCuda + +namespace tensorflow { +namespace functor { + +template +struct FlexConvFunctor { + void operator()(::tensorflow::OpKernelContext* ctx, const Tensor& features, + const Tensor& theta, const Tensor& bias, + const Tensor& neighborhood, const Tensor& positions, + Tensor* output) { + typedef int NBtype; + + const int B = neighborhood.dim_size(0); + const int K = neighborhood.dim_size(1); + const int N = neighborhood.dim_size(2); + const int Dp = theta.dim_size(1); + const int Din = theta.dim_size(2); + const int Dout = theta.dim_size(3); + + FlexConvCuda::ForwardKernel fwk; + fwk.N = N; + fwk.K = K; + fwk.Din = Din; + fwk.Dout = Dout; + + fwk.d_features = features.flat().data(); + fwk.d_positions = positions.flat().data(); + fwk.d_neighborhood = neighborhood.flat().data(); + fwk.d_theta = theta.flat().data(); + fwk.d_bias = bias.flat().data(); + fwk.d_output = output->flat().data(); + + fwk.launch(B); + + if (!ctx->eigen_gpu_device().ok()) { + ctx->SetStatus(tensorflow::errors::Internal( + "FlexConvInvFunctor::forward::ForwardKernel execution failed")); + } + } +}; + +template struct FlexConvFunctor; + +template +struct FlexConvGrad { + void operator()(::tensorflow::OpKernelContext* ctx, const Tensor& features_, + const Tensor& theta_, const Tensor& bias_, + const Tensor& neighborhood_, const Tensor& positions_, + const Tensor& topdiff_, Tensor* grad_features_, + Tensor* grad_theta_, Tensor* grad_bias_) { + const auto features = features_.tensor(); + const auto theta = theta_.tensor(); + const auto bias = bias_.tensor(); + const auto neighborhood = neighborhood_.tensor(); + const auto positions = positions_.tensor(); + const auto topdiff = topdiff_.tensor(); + + auto grad_features = grad_features_->tensor(); + auto grad_theta = grad_theta_->tensor(); + auto grad_bias = grad_bias_->tensor(); + + // get dimensions + const int B = neighborhood_.dim_size(0); + const int K = neighborhood_.dim_size(1); + const int N = neighborhood_.dim_size(2); + const int Dp = theta_.dim_size(1); + const int Din = theta_.dim_size(2); + const int Dout = theta_.dim_size(3); + + const int* neighborhood_ptr = + reinterpret_cast(neighborhood.data()); + const Dtype* positions_ptr = + reinterpret_cast(positions.data()); + const Dtype* features_ptr = reinterpret_cast(features.data()); + const Dtype* theta_ptr = reinterpret_cast(theta.data()); + const Dtype* bias_ptr = reinterpret_cast(bias.data()); + + const Dtype* topdiff_ptr = reinterpret_cast(topdiff.data()); + + Dtype* grad_features_ptr = reinterpret_cast(grad_features.data()); + Dtype* grad_theta_ptr = reinterpret_cast(grad_theta.data()); + Dtype* grad_bias_ptr = reinterpret_cast(grad_bias.data()); + + cudaMemset(grad_features_ptr, 0, B * Din * N * sizeof(Dtype)); + + ::tensorflow::CudaLaunchConfig cfg = + ::tensorflow::GetCudaLaunchConfig(N, ctx->eigen_device()); + + typedef FlexConvCuda::BackwardFeatureKernel BFK; + + BFK bfk; + bfk.N = N; + bfk.K = K; + bfk.Dp = Dp; + bfk.Din = Din; + bfk.Dout = Dout; + + bfk.d_pos = positions_ptr; + bfk.d_neigh = neighborhood_ptr; + bfk.d_features = features_ptr; + bfk.d_theta = theta_ptr; + bfk.d_bias = bias_ptr; + + bfk.d_topdiff = topdiff_ptr; + + bfk.d_features_out = grad_features_ptr; + + bfk.launch(B); + + if (!ctx->eigen_gpu_device().ok()) { + ctx->SetStatus( + tensorflow::errors::Internal("CUDA: BackwardFeatureKernel Error!\n")); + } + + typedef FlexConvCuda::BackwardThetaKernel BTK; + + BTK btk; + btk.B = B; + btk.N = N; + btk.K = K; + btk.Dp = Dp; + btk.Din = Din; + btk.Dout = Dout; + + btk.d_pos = positions_ptr; + btk.d_neigh = neighborhood_ptr; + btk.d_features = features_ptr; + btk.d_theta = theta_ptr; + btk.d_bias = bias_ptr; + + btk.d_topdiff = topdiff_ptr; + + btk.d_theta_out = grad_theta_ptr; + btk.d_bias_out = grad_bias_ptr; + + btk.launch(); + + if (!ctx->eigen_gpu_device().ok()) { + ctx->SetStatus( + tensorflow::errors::Internal("CUDA: BackwardThetaKernel Error!\n")); + } + } +}; + +template struct FlexConvGrad; + +} // namespace functor +} // namespace tensorflow + +#endif // GOOGLE_CUDA diff --git a/user_ops/kernels/flex_conv_op.cc b/user_ops/kernels/flex_conv_op.cc new file mode 100644 index 0000000..2b47b0b --- /dev/null +++ b/user_ops/kernels/flex_conv_op.cc @@ -0,0 +1,122 @@ +/* Copyright 2017 ComputerGraphics Tuebingen. 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. +==============================================================================*/ +//Authors: Fabian Groh, Patrick Wieschollek, Hendrik P.A. Lensch + +#include "flex_conv_op.h" + +#include +#include + +#include "tensorflow/core/framework/op.h" +#include "tensorflow/core/framework/op_kernel.h" +#include "tensorflow/core/framework/register_types.h" + +namespace tensorflow { + +// Forward-Pass (CPU, GPU) +// -------------------------------------------------- +template +class FlexConvOp : public OpKernel { + public: + explicit FlexConvOp(OpKernelConstruction* ctx) : OpKernel(ctx) {} + + void Compute(OpKernelContext* ctx) override { + // printf("--> Compute CPU Version <--\n"); + const Tensor& features_ = ctx->input(0); + const Tensor& theta_ = ctx->input(1); + const Tensor& bias_ = ctx->input(2); + const Tensor& neighborhood_ = ctx->input(3); + const Tensor& positions_ = ctx->input(4); + + const int B = neighborhood_.shape().dim_size(0); + const int N = neighborhood_.shape().dim_size(2); + const int Dout = theta_.shape().dim_size(3); + + Tensor* output_ = nullptr; + OP_REQUIRES_OK( + ctx, ctx->allocate_output(0, TensorShape({B, Dout, N}), &output_)); + + ::tensorflow::functor::FlexConvFunctor()( + ctx, features_, theta_, bias_, neighborhood_, positions_, output_); + } + + private: + TF_DISALLOW_COPY_AND_ASSIGN(FlexConvOp); +}; + +// Backward-Pass (CPU, GPU) +// -------------------------------------------------- +template +class FlexConvGradOp : public OpKernel { + public: + explicit FlexConvGradOp(OpKernelConstruction* ctx) : OpKernel(ctx) {} + + void Compute(OpKernelContext* ctx) override { + // printf("--> Compute CPU Version <--\n"); + const Tensor& features_ = ctx->input(0); + const Tensor& theta_ = ctx->input(1); + const Tensor& bias_ = ctx->input(2); + const Tensor& neighborhood_ = ctx->input(3); + const Tensor& positions_ = ctx->input(4); + + const Tensor& topdiff_ = ctx->input(5); + + // specify output shape + Tensor* grad_features_ = nullptr; + Tensor* grad_theta_ = nullptr; + Tensor* grad_bias_ = nullptr; + + const int Degree = theta_.shape().dim_size(0); + + OP_REQUIRES_OK(ctx, + ctx->allocate_output(0, features_.shape(), &grad_features_)); + OP_REQUIRES_OK(ctx, ctx->allocate_output(1, theta_.shape(), &grad_theta_)); + OP_REQUIRES_OK(ctx, ctx->allocate_output(2, bias_.shape(), &grad_bias_)); + + ::tensorflow::functor::FlexConvGrad()( + ctx, features_, theta_, bias_, neighborhood_, positions_, topdiff_, + grad_features_, grad_theta_, grad_bias_); + } +}; + +// Register the CPU kernels. +#define REGISTER_FLEXCONV_OP_CPU(T) \ + REGISTER_KERNEL_BUILDER( \ + Name("FlexConv").Device(DEVICE_CPU).TypeConstraint("T"), \ + FlexConvOp) \ + REGISTER_KERNEL_BUILDER( \ + Name("FlexConvGrad").Device(DEVICE_CPU).TypeConstraint("T"), \ + FlexConvGradOp) + +TF_CALL_float(REGISTER_FLEXCONV_OP_CPU); +#undef REGISTER_FLEXCONV_OP_CPU + +// Register the GPU kernels. +// #ifdef GOOGLE_CUDA + +#define REGISTER_FLEXCONV_OP_GPU(T) \ + REGISTER_KERNEL_BUILDER( \ + Name("FlexConv").Device(DEVICE_GPU).TypeConstraint("T"), \ + FlexConvOp) \ + REGISTER_KERNEL_BUILDER( \ + Name("FlexConvGrad").Device(DEVICE_GPU).TypeConstraint("T"), \ + FlexConvGradOp) + +TF_CALL_float(REGISTER_FLEXCONV_OP_GPU); +#undef REGISTER_FLEXCONV_OP_GPU + +// #endif // GOOGLE_CUDA + +} // namespace tensorflow diff --git a/user_ops/kernels/flex_conv_op.h b/user_ops/kernels/flex_conv_op.h new file mode 100644 index 0000000..d5bfd3f --- /dev/null +++ b/user_ops/kernels/flex_conv_op.h @@ -0,0 +1,53 @@ +/* Copyright 2017 ComputerGraphics Tuebingen. 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. +==============================================================================*/ +//Authors: Fabian Groh, Patrick Wieschollek, Hendrik P.A. Lensch + +#ifndef USER_OPS_KERNELS_FLEX_CONV_OP_H_ +#define USER_OPS_KERNELS_FLEX_CONV_OP_H_ + +#include "tensorflow/core/framework/op_kernel.h" + +namespace tensorflow { +class OpKernelContext; +class Tensor; + +using CPUDevice = Eigen::ThreadPoolDevice; +using GPUDevice = Eigen::GpuDevice; +} // namespace tensorflow + +namespace tensorflow { +namespace functor { + +template +struct FlexConvFunctor { + void operator()(::tensorflow::OpKernelContext* ctx, const Tensor& features_, + const Tensor& theta_, const Tensor& bias_, + const Tensor& neighborhood_, const Tensor& positions_, + Tensor* output_); +}; + +template +struct FlexConvGrad { + void operator()(::tensorflow::OpKernelContext* ctx, const Tensor& features_, + const Tensor& theta_, const Tensor& bias_, + const Tensor& neighborhood_, const Tensor& positions_, + const Tensor& topdiff_, Tensor* grad_features_, + Tensor* grad_theta_, Tensor* grad_bias_); +}; + +} // namespace functor +} // namespace tensorflow + +#endif // USER_OPS_KERNELS_FLEX_CONV_OP_H_ diff --git a/user_ops/kernels/flex_deconv_kernel.cc b/user_ops/kernels/flex_deconv_kernel.cc new file mode 100644 index 0000000..d4c618c --- /dev/null +++ b/user_ops/kernels/flex_deconv_kernel.cc @@ -0,0 +1,173 @@ +/* Copyright 2017 ComputerGraphics Tuebingen. 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. +==============================================================================*/ +//Authors: Fabian Groh, Patrick Wieschollek, Hendrik P.A. Lensch + +#include "flex_deconv_op.h" +#include "tensorflow/core/framework/op.h" + +namespace tensorflow { + +namespace functor { + +template +struct FlexDeconvFunctor { + void operator()(::tensorflow::OpKernelContext* ctx, const Tensor& features_, + const Tensor& theta_, const Tensor& bias_, + const Tensor& neighborhood_, const Tensor& positions_, + Tensor* output_) { + const auto features = features_.tensor(); + const auto theta = theta_.tensor(); + const auto bias = bias_.tensor(); + const auto neighborhood = neighborhood_.tensor(); + const auto positions = positions_.tensor(); + + auto output = output_->tensor(); + + // get dimensions + const int B = neighborhood_.dim_size(0); + const int K = neighborhood_.dim_size(1); + const int N = neighborhood_.dim_size(2); + const int Dp = theta_.dim_size(1); + const int Din = theta_.dim_size(2); + const int Dout = theta_.dim_size(3); + + output.setZero(); + + for (int b = 0; b < B; ++b) { + for (int n = 0; n < N; ++n) { + const int self_k = neighborhood(b, 0, n); + for (int k_ = 0; k_ < K; ++k_) { + const int other_k = neighborhood(b, k_, n); + + for (int dout = 0; dout < Dout; ++dout) { + for (int din = 0; din < Din; ++din) { + const Dtype v = features(b, din, self_k); + + Dtype W = bias(din, dout); + for (int dp = 0; dp < Dp; ++dp) { + Dtype delta = + positions(b, dp, other_k) - positions(b, dp, self_k); + W += theta(0, dp, din, dout) * delta; + } + output(b, dout, other_k) = output(b, dout, other_k) + W * v; + } + } + } + } + } + } +}; + +template struct FlexDeconvFunctor; + +template +struct FlexDeconvGrad { + void operator()(::tensorflow::OpKernelContext* ctx, const Tensor& features_, + const Tensor& theta_, const Tensor& bias_, + const Tensor& neighborhood_, const Tensor& positions_, + const Tensor& topdiff_, Tensor* grad_features_, + Tensor* grad_theta_, Tensor* grad_bias_) { + const auto features = features_.tensor(); + const auto theta = theta_.tensor(); + const auto bias = bias_.tensor(); + const auto neighborhood = neighborhood_.tensor(); + const auto positions = positions_.tensor(); + const auto topdiff = topdiff_.tensor(); + + auto grad_features = grad_features_->tensor(); + auto grad_theta = grad_theta_->tensor(); + auto grad_bias = grad_bias_->tensor(); + + // get dimensions + const int B = neighborhood_.dim_size(0); + const int K = neighborhood_.dim_size(1); + const int N = neighborhood_.dim_size(2); + const int Dp = theta_.dim_size(1); + const int Din = theta_.dim_size(2); + const int Dout = theta_.dim_size(3); + + grad_features.setZero(); + grad_theta.setZero(); + grad_bias.setZero(); + + // ========================= bias ============================== + for (int b = 0; b < B; ++b) { + for (int n = 0; n < N; ++n) { + const int self_k = neighborhood(b, 0, n); + for (int k_ = 0; k_ < K; ++k_) { + const int other_k = neighborhood(b, k_, n); + + for (int din = 0; din < Din; ++din) { + for (int dout = 0; dout < Dout; ++dout) { + grad_bias(din, dout) += + features(b, din, self_k) * topdiff(b, dout, other_k); + } + } + } + } + } + + // ========================= theta ============================== + for (int b = 0; b < B; ++b) { + for (int n = 0; n < N; ++n) { + const int self_k = neighborhood(b, 0, n); + for (int k_ = 0; k_ < K; ++k_) { + const int other_k = neighborhood(b, k_, n); + + for (int din = 0; din < Din; ++din) { + for (int dout = 0; dout < Dout; ++dout) { + for (int dp = 0; dp < Dp; ++dp) { + const Dtype delta = + positions(b, dp, other_k) - positions(b, dp, self_k); + grad_theta(0, dp, din, dout) += features(b, din, self_k) * + delta * + topdiff(b, dout, other_k); + } + } + } + } + } + } + + // ========================= features ============================== + for (int b = 0; b < B; ++b) { + for (int n = 0; n < N; ++n) { + const int self_k = neighborhood(b, 0, n); + for (int k_ = 0; k_ < K; ++k_) { + const int other_k = neighborhood(b, k_, n); + + for (int din = 0; din < Din; ++din) { + for (int dout = 0; dout < Dout; ++dout) { + Dtype W = bias(din, dout); + for (int dp = 0; dp < Dp; ++dp) { + const Dtype delta = + positions(b, dp, other_k) - positions(b, dp, self_k); + W += theta(0, dp, din, dout) * delta; + } + grad_features(b, din, self_k) += W * topdiff(b, dout, other_k); + } + } + } + } + } + } +}; + +// template struct FlexDeconvGrad; +template struct FlexDeconvGrad; +// template struct FlexDeconvGrad; + +} // namespace functor +} // namespace tensorflow diff --git a/user_ops/kernels/flex_deconv_kernel_gpu.cu.cc b/user_ops/kernels/flex_deconv_kernel_gpu.cu.cc new file mode 100644 index 0000000..05518a7 --- /dev/null +++ b/user_ops/kernels/flex_deconv_kernel_gpu.cu.cc @@ -0,0 +1,227 @@ +/* Copyright 2017 ComputerGraphics Tuebingen. 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. +==============================================================================*/ +//Authors: Fabian Groh, Patrick Wieschollek, Hendrik P.A. Lensch + +#if GOOGLE_CUDA + +#define EIGEN_USE_GPU + +#include + +#include "flex_deconv_op.h" +#include "tensorflow/core/util/cuda_kernel_helper.h" + +namespace { +inline int up2(int len, int th) { return (len - 1) / th + 1; } + +template +__global__ void forward(const int B, const int N, const int K, const int Dp, + const int Din, const int Dout, const Dtype* positions, + const Dtype* features, const int* neighborhood, + const Dtype* theta, const Dtype* bias, Dtype* output) { + /* + positions B, Dp, N + features B, Din, N + neighborhood B, K, N + theta Dp, Din, Dout + bias Din, Dout + output B, Dout, N + */ + + const int b = blockIdx.z; + + for (int n = blockIdx.y * blockDim.y + threadIdx.y; n < N; + n += blockDim.y * gridDim.y) { + const int self_k = neighborhood[b * K * N + 0 * N + n]; + + for (int k_ = 0; k_ < K; ++k_) { + const int other_k = neighborhood[b * K * N + k_ * N + n]; + + for (int dout = blockIdx.x * blockDim.x + threadIdx.x; dout < Dout; + dout += blockDim.x * gridDim.x) { + for (int din = 0; din < Din; ++din) { + const Dtype v = features[b * Din * N + din * N + self_k]; + Dtype W = bias[din * Dout + dout]; + + for (int dp = 0; dp < Dp; ++dp) { + Dtype delta = positions[b * Dp * N + dp * N + other_k] - + positions[b * Dp * N + dp * N + self_k]; + W += theta[dp * Din * Dout + din * Dout + dout] * delta; + } + + Dtype Wv = W * v; + tensorflow::CudaAtomicAdd(&output[b * Dout * N + dout * N + other_k], + Wv); + } + } + } + } +} + +template +__global__ void backward(const int B, const int N, const int K, const int Dp, + const int Din, const int Dout, + + const Dtype* positions, const Dtype* features, + const int* neighborhood, + + const Dtype* theta, const Dtype* bias, + + const Dtype* top_diff, + + Dtype* grad_features, Dtype* grad_theta, + Dtype* grad_bias) { + /* + B, Dp, N positions, grad_positions + B, Din, N features, grad_features + B, K, N neighborhood + Dp, Din, Dout theta, grad_theta + Din, Dout bias, grad_bias + B, Dout, N output, top_diff + */ + + const int b = blockIdx.z; + + // Compute + // --------------------------------------------------------------- + + for (int n = blockIdx.y * blockDim.y + threadIdx.y; n < N; + n += blockDim.y * gridDim.y) { + const int self_k = neighborhood[b * K * N + 0 * N + n]; + + for (int k_ = 0; k_ < K; ++k_) { + const int other_k = neighborhood[b * K * N + k_ * N + n]; + + for (int dout = blockIdx.x * blockDim.x + threadIdx.x; dout < Dout; + dout += blockDim.x * gridDim.x) { + for (int din = 0; din < Din; ++din) { + const Dtype current_top_diff = + top_diff[b * Dout * N + dout * N + other_k]; + const Dtype v = features[b * Din * N + din * N + self_k]; + + // update bias + Dtype bias_update = v * current_top_diff; + tensorflow::CudaAtomicAdd(&grad_bias[din * Dout + dout], bias_update); + + Dtype W = bias[din * Dout + dout]; + + // update theta + for (int dp = 0; dp < Dp; ++dp) { + Dtype delta = positions[b * Dp * N + dp * N + other_k] - + positions[b * Dp * N + dp * N + self_k]; + Dtype theta_update = v * delta * current_top_diff; + tensorflow::CudaAtomicAdd( + &grad_theta[dp * Din * Dout + din * Dout + dout], theta_update); + + W += theta[dp * Din * Dout + din * Dout + dout] * delta; + } + + // update features + Dtype feature_update = W * current_top_diff; + tensorflow::CudaAtomicAdd( + &grad_features[b * Din * N + din * N + self_k], feature_update); + // tensorflow::CudaAtomicAdd(&grad_features[b * Din * N + din * N + + // self_k], 1); + } + } + } + } +} + +} // namespace + +namespace tensorflow { +namespace functor { + +template +struct FlexDeconvFunctor { + void operator()(::tensorflow::OpKernelContext* ctx, const Tensor& features_, + const Tensor& theta_, const Tensor& bias_, + const Tensor& neighborhood_, const Tensor& positions_, + Tensor* output_) { + // printf("GPU::FlexDeconvFunctor:operator()\n"); + + // get dimensions + const int B = neighborhood_.dim_size(0); + const int K = neighborhood_.dim_size(1); + const int N = neighborhood_.dim_size(2); + const int Dp = theta_.dim_size(1); + const int Din = theta_.dim_size(2); + const int Dout = theta_.dim_size(3); + + const int threads = 32; + dim3 block(threads, threads, 1); + dim3 grid(up2(Dout, threads), up2(N, threads), B); + + cudaMemset(output_->flat().data(), 0, + output_->NumElements() * sizeof(Dtype)); + + forward<<>>( + B, N, K, Dp, Din, Dout, positions_.flat().data(), + features_.flat().data(), neighborhood_.flat().data(), + theta_.flat().data(), bias_.flat().data(), + output_->flat().data()); + } +}; + +template struct FlexDeconvFunctor; + +template +struct FlexDeconvGrad { + void operator()(::tensorflow::OpKernelContext* ctx, const Tensor& features_, + const Tensor& theta_, const Tensor& bias_, + const Tensor& neighborhood_, const Tensor& positions_, + const Tensor& topdiff_, Tensor* grad_features_, + Tensor* grad_theta_, Tensor* grad_bias_) { + // get dimensions + const int B = neighborhood_.dim_size(0); + const int K = neighborhood_.dim_size(1); + const int N = neighborhood_.dim_size(2); + const int Dp = theta_.dim_size(1); + const int Din = theta_.dim_size(2); + const int Dout = theta_.dim_size(3); + + const int threads = 32; + dim3 block(threads, threads, 1); + dim3 grid(up2(Dout, threads), up2(N, threads), B); + + cudaMemset(grad_features_->flat().data(), 0, + grad_features_->NumElements() * sizeof(Dtype)); + cudaMemset(grad_theta_->flat().data(), 0, + grad_theta_->NumElements() * sizeof(Dtype)); + cudaMemset(grad_bias_->flat().data(), 0, + grad_bias_->NumElements() * sizeof(Dtype)); + + backward<<>>( + B, N, K, Dp, Din, Dout, + + positions_.flat().data(), features_.flat().data(), + neighborhood_.flat().data(), + + theta_.flat().data(), bias_.flat().data(), + + topdiff_.flat().data(), + + grad_features_->flat().data(), grad_theta_->flat().data(), + grad_bias_->flat().data()); + } +}; + +template struct FlexDeconvGrad; + +} // namespace functor +} // namespace tensorflow + +#endif // GOOGLE_CUDA diff --git a/user_ops/kernels/flex_deconv_op.cc b/user_ops/kernels/flex_deconv_op.cc new file mode 100644 index 0000000..05e5d64 --- /dev/null +++ b/user_ops/kernels/flex_deconv_op.cc @@ -0,0 +1,103 @@ +/* Copyright 2017 ComputerGraphics Tuebingen. 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. +==============================================================================*/ +//Authors: Fabian Groh, Patrick Wieschollek, Hendrik P.A. Lensch + +#include "flex_deconv_op.h" + +#include +#include + +#include "tensorflow/core/framework/op.h" +#include "tensorflow/core/framework/op_kernel.h" + +namespace tensorflow { + +// Forward-Pass (CPU, GPU) +// -------------------------------------------------- +template +class FlexDeconvOp : public OpKernel { + public: + explicit FlexDeconvOp(OpKernelConstruction* ctx) : OpKernel(ctx) {} + + void Compute(OpKernelContext* ctx) override { + // printf("--> Compute CPU Version <--\n"); + const Tensor& features_ = ctx->input(0); + const Tensor& theta_ = ctx->input(1); + const Tensor& bias_ = ctx->input(2); + const Tensor& neighborhood_ = ctx->input(3); + const Tensor& positions_ = ctx->input(4); + + const int B = neighborhood_.shape().dim_size(0); + const int N = neighborhood_.shape().dim_size(2); + const int Dout = theta_.shape().dim_size(3); + + Tensor* output_ = nullptr; + OP_REQUIRES_OK( + ctx, ctx->allocate_output(0, TensorShape({B, Dout, N}), &output_)); + + ::tensorflow::functor::FlexDeconvFunctor()( + ctx, features_, theta_, bias_, neighborhood_, positions_, output_); + } + + private: + TF_DISALLOW_COPY_AND_ASSIGN(FlexDeconvOp); +}; + +// Backward-Pass (CPU, GPU) +// -------------------------------------------------- +template +class FlexDeconvGradOp : public OpKernel { + public: + explicit FlexDeconvGradOp(OpKernelConstruction* ctx) : OpKernel(ctx) {} + + void Compute(OpKernelContext* ctx) override { + // printf("--> Compute CPU Version <--\n"); + const Tensor& features_ = ctx->input(0); + const Tensor& theta_ = ctx->input(1); + const Tensor& bias_ = ctx->input(2); + const Tensor& neighborhood_ = ctx->input(3); + const Tensor& positions_ = ctx->input(4); + + const Tensor& topdiff_ = ctx->input(5); + + // specify output shape + Tensor* grad_features_ = nullptr; + Tensor* grad_theta_ = nullptr; + Tensor* grad_bias_ = nullptr; + + OP_REQUIRES_OK(ctx, + ctx->allocate_output(0, features_.shape(), &grad_features_)); + OP_REQUIRES_OK(ctx, ctx->allocate_output(1, theta_.shape(), &grad_theta_)); + OP_REQUIRES_OK(ctx, ctx->allocate_output(2, bias_.shape(), &grad_bias_)); + + ::tensorflow::functor::FlexDeconvGrad()( + ctx, features_, theta_, bias_, neighborhood_, positions_, topdiff_, + grad_features_, grad_theta_, grad_bias_); + } +}; + +#define OPNAME(NAME) NAME##Op +#define REGISTER(NAME, Dtype) \ + REGISTER_KERNEL_BUILDER( \ + Name(#NAME).Device(DEVICE_CPU).TypeConstraint("T"), \ + OPNAME(NAME) < CPUDevice, Dtype >); \ + REGISTER_KERNEL_BUILDER( \ + Name(#NAME).Device(DEVICE_GPU).TypeConstraint("T"), \ + OPNAME(NAME) < GPUDevice, Dtype >); + +REGISTER(FlexDeconv, float); +REGISTER(FlexDeconvGrad, float); + +} // namespace tensorflow diff --git a/user_ops/kernels/flex_deconv_op.h b/user_ops/kernels/flex_deconv_op.h new file mode 100644 index 0000000..49ed8a9 --- /dev/null +++ b/user_ops/kernels/flex_deconv_op.h @@ -0,0 +1,53 @@ +/* Copyright 2017 ComputerGraphics Tuebingen. 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. +==============================================================================*/ +//Authors: Fabian Groh, Patrick Wieschollek, Hendrik P.A. Lensch + +#ifndef USER_OPS_KERNELS_FLEX_DECONV_OP_H_ +#define USER_OPS_KERNELS_FLEX_DECONV_OP_H_ + +#include "tensorflow/core/framework/op_kernel.h" + +namespace tensorflow { +class OpKernelContext; +class Tensor; + +using CPUDevice = Eigen::ThreadPoolDevice; +using GPUDevice = Eigen::GpuDevice; +} // namespace tensorflow + +namespace tensorflow { +namespace functor { + +template +struct FlexDeconvFunctor { + void operator()(::tensorflow::OpKernelContext* ctx, const Tensor& features_, + const Tensor& theta_, const Tensor& bias_, + const Tensor& neighborhood_, const Tensor& positions_, + Tensor* output_); +}; + +template +struct FlexDeconvGrad { + void operator()(::tensorflow::OpKernelContext* ctx, const Tensor& features_, + const Tensor& theta_, const Tensor& bias_, + const Tensor& neighborhood_, const Tensor& positions_, + const Tensor& topdiff_, Tensor* grad_features_, + Tensor* grad_theta_, Tensor* grad_bias_); +}; + +} // namespace functor +} // namespace tensorflow + +#endif // USER_OPS_KERNELS_FLEX_DECONV_OP_H_ diff --git a/user_ops/kernels/flex_pool_kernel.cc b/user_ops/kernels/flex_pool_kernel.cc new file mode 100644 index 0000000..4158286 --- /dev/null +++ b/user_ops/kernels/flex_pool_kernel.cc @@ -0,0 +1,102 @@ +/* Copyright 2017 ComputerGraphics Tuebingen. 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. +==============================================================================*/ +//Authors: Fabian Groh, Patrick Wieschollek, Hendrik P.A. Lensch + +#include "flex_pool_op.h" +#include "tensorflow/core/framework/op.h" + +namespace tensorflow { + +namespace functor { + +template +struct FlexPoolFunctor { + void operator()(::tensorflow::OpKernelContext* ctx, const Tensor& features_, + const Tensor& neighborhood_, Tensor* output_, + Tensor* argmax_) { + const auto features = features_.tensor(); + const auto neighborhood = neighborhood_.tensor(); + + auto output = output_->tensor(); + auto argmax = argmax_->tensor(); + + // get dimensions + const int B = neighborhood_.dim_size(0); + const int K = neighborhood_.dim_size(1); + const int N = neighborhood_.dim_size(2); + const int D = features_.dim_size(1); + + output.setConstant(Eigen::NumTraits::lowest()); + argmax.setZero(); // stores global id + + for (int b = 0; b < B; ++b) { + for (int d = 0; d < D; ++d) { + for (int n = 0; n < N; ++n) { + // max in neighborhood + for (int k_ = 0; k_ < K; ++k_) { + const int other_global_id = neighborhood(b, k_, n); + if (output(b, d, n) < features(b, d, other_global_id)) { + argmax(b, d, n) = other_global_id; + output(b, d, n) = features(b, d, other_global_id); + } + } + } + } + } + } +}; + +template struct FlexPoolFunctor; + +template +struct FlexPoolGrad { + void operator()(::tensorflow::OpKernelContext* ctx, const Tensor& features_, + const Tensor& neighborhood_, const Tensor& topdiff_, + const Tensor& argmax_, Tensor* grad_features_) { + // as only argmax contributes to the output + // only argmax receives the topdiff + + const auto features = features_.tensor(); + const auto neighborhood = neighborhood_.tensor(); + const auto topdiff = topdiff_.tensor(); + const auto argmax = argmax_.tensor(); + + auto grad_features = grad_features_->tensor(); + + // get dimensions + const int B = neighborhood_.dim_size(0); + const int K = neighborhood_.dim_size(1); + const int N = neighborhood_.dim_size(2); + const int D = features_.dim_size(1); + // printf("B %i K %i N %i D %i\n", B, K ,N, D); + + grad_features.setZero(); + + for (int b = 0; b < B; ++b) { + for (int d = 0; d < D; ++d) { + for (int n = 0; n < N; ++n) { + grad_features(b, d, argmax(b, d, n)) += topdiff(b, d, n); + } + } + } + } +}; + +// template struct FlexPoolGrad; +template struct FlexPoolGrad; +// template struct FlexPoolGrad; + +} // namespace functor +} // namespace tensorflow diff --git a/user_ops/kernels/flex_pool_kernel_gpu.cu.cc b/user_ops/kernels/flex_pool_kernel_gpu.cu.cc new file mode 100644 index 0000000..ecb39d2 --- /dev/null +++ b/user_ops/kernels/flex_pool_kernel_gpu.cu.cc @@ -0,0 +1,171 @@ +/* Copyright 2017 ComputerGraphics Tuebingen. 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. +==============================================================================*/ +//Authors: Fabian Groh, Patrick Wieschollek, Hendrik P.A. Lensch + +#if GOOGLE_CUDA + +#define EIGEN_USE_GPU + +#include +#include + +#include "flex_pool_op.h" +#include "tensorflow/core/util/cuda_kernel_helper.h" + +namespace { +inline int up2(int len, int th) { return (len - 1) / th + 1; } + +template +__global__ void forward(const int B, const int N, const int K, const int D, + const Dtype* features, const int* neighborhood, + Dtype* output, int* argmax, float float_min_value) { + // features: each feature description for each point [B, D, N]. + // neighborhood: all K nearest neighbors [B, K, N]. + // output: each feature description for each point [B, D, N]. + // argmax: global id in neighborhood who was winning the pooling [B, D, N]. + const int b = blockIdx.z; + + for (int d = blockIdx.y * blockDim.y + threadIdx.y; d < D; + d += blockDim.y * gridDim.y) { + for (int n = blockIdx.x * blockDim.x + threadIdx.x; n < N; + n += blockDim.x * gridDim.x) { + float best_value = float_min_value; + int best_id = 0; + + const int current_flat = b * D * N + d * N + n; + + for (int k_ = 0; k_ < K; ++k_) { + const int other_global_id = neighborhood[b * K * N + k_ * N + n]; + const float v = features[b * D * N + d * N + other_global_id]; + + if (best_value < v) { + best_id = other_global_id; + best_value = v; + } + } + + output[current_flat] = best_value; + argmax[current_flat] = best_id; + } + } +} + +template +__global__ void backward(const int B, const int N, const int K, const int D, + + const Dtype* features, const int* neighborhood, + const Dtype* topdiff, const int* argmax, + + Dtype* grad_features) { + // features: each feature description for each point [B, D, N]. + // neighborhood: all K nearest neighbors [B, K, N]. + // gradients: topdiff[B, D, N]. + // argmax: argmax[B, D, N]. + // grad_features: gradient to each feature description for each point [B, D, + // N]. + const int b = blockIdx.z; + + for (int d = blockIdx.y * blockDim.y + threadIdx.y; d < D; + d += blockDim.y * gridDim.y) { + for (int n = blockIdx.x * blockDim.x + threadIdx.x; n < N; + n += blockDim.x * gridDim.x) { + const int top_id_flat = b * D * N + d * N + n; + const int argmax_id = argmax[top_id_flat]; + const int bottom_id_flat = b * D * N + d * N + argmax_id; + + // TODO(patwie): scattered write, yeah :-( + tensorflow::CudaAtomicAdd(&grad_features[bottom_id_flat], + topdiff[top_id_flat]); + } + } +} + +} // namespace + +namespace tensorflow { +namespace functor { + +template +struct FlexPoolFunctor { + void operator()(::tensorflow::OpKernelContext* ctx, const Tensor& features_, + const Tensor& neighborhood_, Tensor* output_, + Tensor* argmax_) { + // get dimensions + const int B = neighborhood_.dim_size(0); + const int K = neighborhood_.dim_size(1); + const int N = neighborhood_.dim_size(2); + const int D = features_.dim_size(1); + + const int threads = 32; + dim3 block(threads, threads, 1); + dim3 grid(up2(N, threads), up2(D, threads), B); + + forward<<>>( + B, N, K, D, + + features_.flat().data(), neighborhood_.flat().data(), + + output_->flat().data(), argmax_->flat().data(), + + std::numeric_limits::lowest()); + + if (!ctx->eigen_gpu_device().ok()) { + ctx->SetStatus( + tensorflow::errors::Internal("CUDA: FlexPoolFunctor Error!")); + } + } +}; + +template struct FlexPoolFunctor; + +template +struct FlexPoolGrad { + void operator()(::tensorflow::OpKernelContext* ctx, const Tensor& features_, + const Tensor& neighborhood_, const Tensor& topdiff_, + const Tensor& argmax_, Tensor* grad_features_) { + // get dimensions + const int B = neighborhood_.dim_size(0); + const int K = neighborhood_.dim_size(1); + const int N = neighborhood_.dim_size(2); + const int D = features_.dim_size(1); + + const int threads = 32; + dim3 block(threads, threads, 1); + dim3 grid(up2(N, threads), up2(D, threads), B); + + cudaMemset(grad_features_->flat().data(), 0, + grad_features_->NumElements() * sizeof(Dtype)); + + backward<<>>( + B, N, K, D, + + features_.flat().data(), neighborhood_.flat().data(), + + topdiff_.flat().data(), argmax_.flat().data(), + + grad_features_->flat().data()); + + if (!ctx->eigen_gpu_device().ok()) { + ctx->SetStatus(tensorflow::errors::Internal("CUDA: FlexPoolGrad Error!")); + } + } +}; + +template struct FlexPoolGrad; + +} // namespace functor +} // namespace tensorflow + +#endif // GOOGLE_CUDA diff --git a/user_ops/kernels/flex_pool_op.cc b/user_ops/kernels/flex_pool_op.cc new file mode 100644 index 0000000..508a8dd --- /dev/null +++ b/user_ops/kernels/flex_pool_op.cc @@ -0,0 +1,113 @@ +/* Copyright 2017 ComputerGraphics Tuebingen. 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. +==============================================================================*/ +//Authors: Fabian Groh, Patrick Wieschollek, Hendrik P.A. Lensch + +#include "flex_pool_op.h" + +#include +#include + +#include "tensorflow/core/framework/op.h" +#include "tensorflow/core/framework/op_kernel.h" +#include "tensorflow/core/framework/register_types.h" + +namespace tensorflow { + +// Forward-Pass (CPU, GPU) +// -------------------------------------------------- +template +class FlexPoolOp : public OpKernel { + public: + explicit FlexPoolOp(OpKernelConstruction* ctx) : OpKernel(ctx) {} + + void Compute(OpKernelContext* ctx) override { + // printf("--> Compute CPU Version <--\n"); + const Tensor& features_ = ctx->input(0); + const Tensor& neighborhood_ = ctx->input(1); + + const int B = features_.dim_size(0); + const int D = features_.dim_size(1); + const int N = features_.dim_size(2); + + Tensor* output_ = nullptr; + OP_REQUIRES_OK(ctx, + ctx->allocate_output(0, TensorShape({B, D, N}), &output_)); + + Tensor* argmax_ = nullptr; + OP_REQUIRES_OK(ctx, + ctx->allocate_output(1, TensorShape({B, D, N}), &argmax_)); + + ::tensorflow::functor::FlexPoolFunctor()( + ctx, features_, neighborhood_, output_, argmax_); + } + + private: + TF_DISALLOW_COPY_AND_ASSIGN(FlexPoolOp); +}; + +// Backward-Pass (CPU, GPU) +// -------------------------------------------------- +template +class FlexPoolGradOp : public OpKernel { + public: + explicit FlexPoolGradOp(OpKernelConstruction* ctx) : OpKernel(ctx) {} + + void Compute(OpKernelContext* ctx) override { + // printf("--> Compute CPU Version <--\n"); + const Tensor& features_ = ctx->input(0); + const Tensor& neighborhood_ = ctx->input(1); + const Tensor& topdiff_ = ctx->input(2); + const Tensor& argmax_ = ctx->input(3); + + // specify output shape + Tensor* grad_features_ = nullptr; + + OP_REQUIRES_OK(ctx, + ctx->allocate_output(0, features_.shape(), &grad_features_)); + + ::tensorflow::functor::FlexPoolGrad()( + ctx, features_, neighborhood_, topdiff_, argmax_, grad_features_); + } +}; + +// Register the CPU kernels. +#define REGISTER_FLEXPOOL_OP_CPU(T) \ + REGISTER_KERNEL_BUILDER( \ + Name("FlexPool").Device(DEVICE_CPU).TypeConstraint("T"), \ + FlexPoolOp) \ + REGISTER_KERNEL_BUILDER( \ + Name("FlexPoolGrad").Device(DEVICE_CPU).TypeConstraint("T"), \ + FlexPoolGradOp) + +TF_CALL_float(REGISTER_FLEXPOOL_OP_CPU); +#undef REGISTER_FLEXPOOL_OP_CPU + +// Register the GPU kernels. +#ifdef GOOGLE_CUDA + +#define REGISTER_FLEXPOOL_OP_GPU(T) \ + REGISTER_KERNEL_BUILDER( \ + Name("FlexPool").Device(DEVICE_GPU).TypeConstraint("T"), \ + FlexPoolOp) \ + REGISTER_KERNEL_BUILDER( \ + Name("FlexPoolGrad").Device(DEVICE_GPU).TypeConstraint("T"), \ + FlexPoolGradOp) + +TF_CALL_float(REGISTER_FLEXPOOL_OP_GPU); +#undef REGISTER_FLEXPOOL_OP_GPU + +#endif // GOOGLE_CUDA + +} // namespace tensorflow diff --git a/user_ops/kernels/flex_pool_op.h b/user_ops/kernels/flex_pool_op.h new file mode 100644 index 0000000..bc43b35 --- /dev/null +++ b/user_ops/kernels/flex_pool_op.h @@ -0,0 +1,50 @@ +/* Copyright 2017 ComputerGraphics Tuebingen. 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. +==============================================================================*/ +//Authors: Fabian Groh, Patrick Wieschollek, Hendrik P.A. Lensch + +#ifndef USER_OPS_KERNELS_FLEX_POOL_OP_H_ +#define USER_OPS_KERNELS_FLEX_POOL_OP_H_ + +#include "tensorflow/core/framework/op_kernel.h" + +namespace tensorflow { +class OpKernelContext; +class Tensor; + +using CPUDevice = Eigen::ThreadPoolDevice; +using GPUDevice = Eigen::GpuDevice; +} // namespace tensorflow + +namespace tensorflow { +namespace functor { + +template +struct FlexPoolFunctor { + void operator()(::tensorflow::OpKernelContext* ctx, const Tensor& features_, + const Tensor& neighborhood_, Tensor* output_, + Tensor* argmax_); +}; + +template +struct FlexPoolGrad { + void operator()(::tensorflow::OpKernelContext* ctx, const Tensor& features_, + const Tensor& neighborhood_, const Tensor& topdiff_, + const Tensor& argmax_, Tensor* grad_features_); +}; + +} // namespace functor +} // namespace tensorflow + +#endif // USER_OPS_KERNELS_FLEX_POOL_OP_H_ diff --git a/user_ops/ops/flex_conv.cc b/user_ops/ops/flex_conv.cc new file mode 100644 index 0000000..19e696d --- /dev/null +++ b/user_ops/ops/flex_conv.cc @@ -0,0 +1,133 @@ +/* Copyright 2017 ComputerGraphics Tuebingen. 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. +==============================================================================*/ +//Authors: Fabian Groh, Patrick Wieschollek, Hendrik P.A. Lensch + +#include "tensorflow/core/framework/op.h" +#include "tensorflow/core/framework/shape_inference.h" + +namespace tensorflow { + +using ::tensorflow::shape_inference::InferenceContext; +using ::tensorflow::shape_inference::ShapeHandle; + +REGISTER_OP("FlexConv") + .Input("features: T") + .Input("theta: T") + .Input("bias: T") + .Input("neighborhood: int32") + .Input("position: T") + .Output("output: T") + .Attr("T: realnumbertype") + .SetShapeFn([](::tensorflow::shape_inference::InferenceContext* c) { + const auto features = c->input(0); + const auto theta = c->input(1); + const auto bias = c->input(2); + const auto neighborhood = c->input(3); + const auto position = c->input(4); + + // we require the input to have 4 axes + ::tensorflow::shape_inference::ShapeHandle shape_hnd; + TF_RETURN_IF_ERROR(c->WithRank(features, 3, &shape_hnd)); // B x Din x Ng + TF_RETURN_IF_ERROR( + c->WithRank(theta, 4, &shape_hnd)); // 1 x Dp x Din x Dout + TF_RETURN_IF_ERROR(c->WithRank(bias, 2, &shape_hnd)); // Din x Dout + TF_RETURN_IF_ERROR( + c->WithRank(neighborhood, 3, &shape_hnd)); // B x K x N + TF_RETURN_IF_ERROR(c->WithRank(position, 3, &shape_hnd)); // B x Dp x Ng + + shape_inference::DimensionHandle merged; + + // assert B equal + TF_RETURN_IF_ERROR( + c->Merge(c->Dim(features, 0), c->Dim(neighborhood, 0), &merged)); + TF_RETURN_IF_ERROR( + c->Merge(c->Dim(features, 0), c->Dim(position, 0), &merged)); + + // assert Ng equal + TF_RETURN_IF_ERROR( + c->Merge(c->Dim(features, 2), c->Dim(neighborhood, 2), + &merged)); // TODO(fabi?) fix global access and remove + TF_RETURN_IF_ERROR( + c->Merge(c->Dim(features, 2), c->Dim(position, 2), &merged)); + + // assert Dp equal + TF_RETURN_IF_ERROR( + c->Merge(c->Dim(theta, 1), c->Dim(position, 1), &merged)); + + // assert Dout equal + TF_RETURN_IF_ERROR(c->Merge(c->Dim(theta, 3), c->Dim(bias, 1), &merged)); + + // assert Din equal + TF_RETURN_IF_ERROR( + c->Merge(c->Dim(features, 1), c->Dim(theta, 2), &merged)); + TF_RETURN_IF_ERROR( + c->Merge(c->Dim(features, 1), c->Dim(bias, 0), &merged)); + + // specify output-shape + auto B = c->Dim(features, 0); + auto Dout = c->Dim(bias, 1); + auto N = c->Dim(neighborhood, 2); + c->set_output(0, c->MakeShape({B, Dout, N})); + + return Status::OK(); + }) + .Doc(R"doc( +Apply Sparse Convolution to inputs. + +This applies a convolution to a neighborhood of inputs. The formula for computing the output is as follows: + + `output_j = \sum_i w(x_i, x0) * x_i` + `w(x_i, x0) = ??` + +features: each feature description for each point [B, Din, N]. +theta: parameters for kernel function [1, Dp, Din, Dout]. +bias: bias for kernel function [Din, Dout]. +neighborhood: all K nearest neighbors [B, K, N]. +position: each datapoint in 3d space [B, Dp, N]. +output: each feature description for each point [B, Dout, N]. +)doc"); + +REGISTER_OP("FlexConvGrad") + .Input("features: T") + .Input("theta: T") + .Input("bias: T") + .Input("neighborhood: int32") + .Input("position: T") + .Input("gradients: T") + .Output("grad_features: T") + .Output("grad_theta: T") + .Output("grad_bias: T") + .Attr("T: realnumbertype") + .SetShapeFn([](InferenceContext* c) { + c->set_output(0, c->input(0)); // features + c->set_output(1, c->input(1)); // theta + c->set_output(2, c->input(2)); // bias + return ::tensorflow::Status::OK(); + }) + .Doc(R"doc( +Returns gradients of Sparse Convolution to inputs. + +gradients: topdiff[B, N, Dout]. +neighborhood: all K nearest neighbors [B, K, N]. +position: each datapoint in 3d space [B, Dp, N]. +features: each feature description for each point [B, Din, N]. +theta: parameters for kernel function [1, Dp, Din, Dout]. +bias: bias for kernel function [Din, Dout]. +grad_features: gradient to each feature description for each point [B, N, Din]. +grad_theta: gradient to parameters for kernel function [1, Dp, Din, Dout]. +grad_bias: gradient to bias for kernel function [Din, Dout]. +)doc"); + +} // namespace tensorflow diff --git a/user_ops/ops/flex_deconv.cc b/user_ops/ops/flex_deconv.cc new file mode 100644 index 0000000..90028db --- /dev/null +++ b/user_ops/ops/flex_deconv.cc @@ -0,0 +1,129 @@ +/* Copyright 2017 ComputerGraphics Tuebingen. 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. +==============================================================================*/ +//Authors: Fabian Groh, Patrick Wieschollek, Hendrik P.A. Lensch + +#include "tensorflow/core/framework/op.h" +#include "tensorflow/core/framework/shape_inference.h" + +namespace tensorflow { + +using ::tensorflow::shape_inference::InferenceContext; +using ::tensorflow::shape_inference::ShapeHandle; + +REGISTER_OP("FlexDeconv") + .Input("features: T") + .Input("theta: T") + .Input("bias: T") + .Input("neighborhood: int32") + .Input("position: T") + .Output("output: T") + .Attr("T: realnumbertype") + .SetShapeFn([](::tensorflow::shape_inference::InferenceContext* c) { + const auto features = c->input(0); + const auto theta = c->input(1); + const auto bias = c->input(2); + const auto neighborhood = c->input(3); + const auto position = c->input(4); + + // we require the input to have 4 axes + ::tensorflow::shape_inference::ShapeHandle shape_hnd; + TF_RETURN_IF_ERROR(c->WithRank(features, 3, &shape_hnd)); // B x Din x N + TF_RETURN_IF_ERROR( + c->WithRank(theta, 4, &shape_hnd)); // 1 x Dp x Din x Dout + TF_RETURN_IF_ERROR(c->WithRank(bias, 2, &shape_hnd)); // Din x Dout + TF_RETURN_IF_ERROR( + c->WithRank(neighborhood, 3, &shape_hnd)); // B x K x N + TF_RETURN_IF_ERROR(c->WithRank(position, 3, &shape_hnd)); // B x Dp x N + + shape_inference::DimensionHandle merged; + + // assert B equal + TF_RETURN_IF_ERROR( + c->Merge(c->Dim(features, 0), c->Dim(neighborhood, 0), &merged)); + TF_RETURN_IF_ERROR( + c->Merge(c->Dim(features, 0), c->Dim(position, 0), &merged)); + + // assert N equal + TF_RETURN_IF_ERROR( + c->Merge(c->Dim(features, 2), c->Dim(neighborhood, 2), &merged)); + TF_RETURN_IF_ERROR( + c->Merge(c->Dim(features, 2), c->Dim(position, 2), &merged)); + + // assert Dp equal + TF_RETURN_IF_ERROR( + c->Merge(c->Dim(theta, 1), c->Dim(position, 1), &merged)); + + // assert Dout equal + TF_RETURN_IF_ERROR(c->Merge(c->Dim(theta, 3), c->Dim(bias, 1), &merged)); + + // assert Din equal + TF_RETURN_IF_ERROR( + c->Merge(c->Dim(features, 1), c->Dim(theta, 2), &merged)); + TF_RETURN_IF_ERROR( + c->Merge(c->Dim(features, 1), c->Dim(bias, 0), &merged)); + + // specify output-shape + auto B = c->Dim(features, 0); + auto Dout = c->Dim(bias, 1); + auto N = c->Dim(neighborhood, 2); + c->set_output(0, c->MakeShape({B, Dout, N})); + + return Status::OK(); + }) + .Doc(R"doc( +Apply Sparse Convolution to inputs. + +This applies a deconvolution to a neighborhood of inputs. F_i spread its value to all neighbors. + +features: each feature description for each point [B, Din, N]. +theta: parameters for kernel function [1, Dp, Din, Dout]. +bias: bias for kernel function [Din, Dout]. +neighborhood: all K nearest neighbors [B, K, N]. +position: each datapoint in 3d space [B, Dp, N]. +output: each feature description for each point [B, Dout, N]. +)doc"); + +REGISTER_OP("FlexDeconvGrad") + .Input("features: T") + .Input("theta: T") + .Input("bias: T") + .Input("neighborhood: int32") + .Input("position: T") + .Input("gradients: T") + .Output("grad_features: T") + .Output("grad_theta: T") + .Output("grad_bias: T") + .Attr("T: realnumbertype") + .SetShapeFn([](InferenceContext* c) { + c->set_output(0, c->input(0)); // features + c->set_output(1, c->input(1)); // theta + c->set_output(2, c->input(2)); // bias + return ::tensorflow::Status::OK(); + }) + .Doc(R"doc( +Returns gradients of Sparse Deconvolution to inputs. + +gradients: topdiff[B, N, Dout]. +neighborhood: all K nearest neighbors [B, K, N]. +position: each datapoint in 3d space [B, Dp, N]. +features: each feature description for each point [B, Din, N]. +theta: parameters for kernel function [1, Dp, Din, Dout]. +bias: bias for kernel function [Din, Dout]. +grad_features: gradient to each feature description for each point [B, N, Din]. +grad_theta: gradient to parameters for kernel function [1, Dp, Din, Dout]. +grad_bias: gradient to bias for kernel function [Din, Dout]. +)doc"); + +} // namespace tensorflow diff --git a/user_ops/ops/flex_pool.cc b/user_ops/ops/flex_pool.cc new file mode 100644 index 0000000..26ecf1e --- /dev/null +++ b/user_ops/ops/flex_pool.cc @@ -0,0 +1,93 @@ +/* Copyright 2017 ComputerGraphics Tuebingen. 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. +==============================================================================*/ +//Authors: Fabian Groh, Patrick Wieschollek, Hendrik P.A. Lensch + +#include "tensorflow/core/framework/op.h" +#include "tensorflow/core/framework/shape_inference.h" + +namespace tensorflow { + +using ::tensorflow::shape_inference::InferenceContext; +using ::tensorflow::shape_inference::ShapeHandle; + +REGISTER_OP("FlexPool") + .Input("features: T") + .Input("neighborhood: int32") + .Output("output: T") + .Output("argmax: int32") + .Attr("T: realnumbertype") + .SetShapeFn([](::tensorflow::shape_inference::InferenceContext* c) { + const auto features = c->input(0); + const auto neighborhood = c->input(1); + + // we require the input to have 3 axes + ::tensorflow::shape_inference::ShapeHandle shape_hnd; + TF_RETURN_IF_ERROR(c->WithRank(features, 3, &shape_hnd)); // B x D x N + TF_RETURN_IF_ERROR( + c->WithRank(neighborhood, 3, &shape_hnd)); // B x K x N + + shape_inference::DimensionHandle merged; + + // assert B equal + TF_RETURN_IF_ERROR( + c->Merge(c->Dim(features, 0), c->Dim(neighborhood, 0), &merged)); + + // assert N equal + TF_RETURN_IF_ERROR( + c->Merge(c->Dim(features, 2), c->Dim(neighborhood, 2), &merged)); + + // specify output-shape + auto B = c->Dim(features, 0); + auto D = c->Dim(features, 1); + auto N = c->Dim(features, 2); + c->set_output(0, c->MakeShape({B, D, N})); + c->set_output(1, c->MakeShape({B, D, N})); + + return Status::OK(); + }) + .Doc(R"doc( +Apply Sparse Pooling to inputs. + +This applies a max-pooling to a neighborhood of inputs. T + +features: each feature description for each point [B, D, N]. +neighborhood: all K nearest neighbors [B, K, N]. +output: each feature description for each point [B, D, N]. +argmax: global id in neighborhood who was winning the pooling [B, D, N]. This is needed for gradients. +)doc"); + +REGISTER_OP("FlexPoolGrad") + .Input("features: T") + .Input("neighborhood: int32") + .Input("gradients: T") + .Input("argmax: int32") + .Output("grad_features: T") + .Attr("T: realnumbertype") + .SetShapeFn([](InferenceContext* c) { + c->set_output(0, c->input(0)); // features + return ::tensorflow::Status::OK(); + }) + .Doc(R"doc( +Returns gradients of MaxPool to inputs. + +features: each feature description for each point [B, D, N]. +neighborhood: all K nearest neighbors [B, K, N]. +gradients: topdiff[B, D, N]. +argmax: argmax[B, D, N]. +grad_features: gradient to each feature description for each point [B, D, N]. + +)doc"); + +} // namespace tensorflow diff --git a/user_ops/profile_flexconv.py b/user_ops/profile_flexconv.py new file mode 100644 index 0000000..3cc38e7 --- /dev/null +++ b/user_ops/profile_flexconv.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright 2017 ComputerGraphics Tuebingen. 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. +# ============================================================================== +# Authors: Fabian Groh, Patrick Wieschollek, Hendrik P.A. Lensch + + +import numpy as np +import tensorflow as tf + +from PointTestCase import FakePointCloud, random_values +from __init__ import flex_conv + +""" +export LD_LIBRARY_PATH=/graphics/opt/opt_Ubuntu16.04/cuda/toolkit_9.0/cuda/extras/CUPTI/lib64:$LD_LIBRARY_PATH +""" + + +np.random.seed(42) +tf.set_random_seed(42) + +N = 8 +TPC = FakePointCloud(8, 4096, N, 64, 64, 2, 1, N) + + +class PointTestCase(object): + + def __init__(self, data): + self.position = random_values([data.B, data.Dp, data.N]) + self.features = random_values([data.B, data.Din, data.N]) + + # make sure, each neighbor hood has no duplicates and first entry is point n + # THIS IS IMPORTANT!! + self.neighborhood = np.zeros((data.B, data.K, data.N), dtype=np.int32) + for b in range(data.B): + for n in range(data.N): + x = np.arange(data.N) + # does not support axis, hence the loop + np.random.shuffle(x) + offset = np.argwhere(x == n)[0][0] + # roll array such that n is first entry + x = np.roll(x, -offset) + self.neighborhood[b, :, n] = x[:data.K].astype(np.int32) + + self.neighborhood_ds = np.zeros((data.B, data.K, data.N2), dtype=np.int32) + for b in range(data.B): + for n in range(data.N2): + x = np.arange(data.N2) + # does not support axis, hence the loop + np.random.shuffle(x) + offset = np.argwhere(x == n)[0][0] + # roll array such that n is first entry + x = np.roll(x, -offset) + self.neighborhood[b, :, n] = x[:data.K].astype(np.int32) + + self.theta = random_values([data.Degree, data.Dp, data.Din, data.Dout]) + self.bias = random_values([data.Din, data.Dout]) + + def init_ops(self): + self.features_op = tf.Variable(self.features, name='f') + self.position_op = tf.Variable(self.position, name='p') + self.neighborhood_op = tf.Variable(self.neighborhood, name='n') + self.neighborhood_ds_op = tf.Variable(self.neighborhood_ds, name='m') + self.theta_op = tf.Variable(self.theta, name='t') + self.bias_op = tf.Variable(self.bias, name='b') + + +TestCase = PointTestCase(data=TPC) +TestCase.init_ops() + + +forward_op = flex_conv(TestCase.features_op, + TestCase.theta_op, TestCase.bias_op, TestCase.neighborhood_op, + TestCase.position_op, degree=1) + +builder = tf.profiler.ProfileOptionBuilder +opts = builder(builder.time_and_memory()).order_by('micros').build() + +with tf.contrib.tfprof.ProfileContext('./.profiling_outputs/flex_conv') as pctx: + + with tf.Session(config=tf.ConfigProto(log_device_placement=True)) as sess: + sess.run(tf.global_variables_initializer()) + + back_prop = tf.gradients(forward_op, [TestCase.theta_op, TestCase.bias_op, TestCase.features_op]) + back_prop2 = tf.gradients(forward_op, [TestCase.theta_op, TestCase.bias_op, + TestCase.features_op, TestCase.position_op]) + + # warmup + for i in range(2): + actual = sess.run([forward_op, back_prop]) + + # benchmark + for i in range(10): + pctx.trace_next_step() + pctx.dump_next_step() + _ = sess.run([forward_op]) + pctx.profiler.profile_operations(options=opts) + + for i in range(10): + pctx.trace_next_step() + pctx.dump_next_step() + _ = sess.run([back_prop]) + pctx.profiler.profile_operations(options=opts) diff --git a/user_ops/test_flex_convolution.py b/user_ops/test_flex_convolution.py new file mode 100644 index 0000000..4465474 --- /dev/null +++ b/user_ops/test_flex_convolution.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright 2017 ComputerGraphics Tuebingen. 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. +# ============================================================================== +# Authors: Fabian Groh, Patrick Wieschollek, Hendrik P.A. Lensch + + +from PointTestCase import TPC, PointTestCase, summary +import tensorflow as tf + +from __init__ import flex_convolution + + +class FlexConvTest(PointTestCase): + def __init__(self, methodName="runTest"): + super(FlexConvTest, self).__init__(methodName) + + def _forward(self, use_gpu=False, force_gpu=False): + self.init_ops() + with self.test_session(use_gpu=use_gpu, force_gpu=use_gpu) as sess: + actual_op = flex_convolution(self.features_op, + self.position_op, self.neighborhood_op, + self.theta_op, self.bias_op) + actual = sess.run(actual_op) + return actual + + def test_forward(self): + cpu = self._forward(use_gpu=False) + gpu = self._forward(use_gpu=True) + self.assertAllClose(cpu, gpu, 1e-5, 1e-5) + + def _backward_features(self, use_gpu=False): + self.init_ops() + with self.test_session(use_gpu=use_gpu, force_gpu=use_gpu): + actual_op = flex_convolution(self.features_op, + self.position_op, self.neighborhood_op, + self.theta_op, self.bias_op) + graph_features_grad, num_features_grad = tf.test.compute_gradient( + [self.features_op], [self.features.shape], actual_op, + TPC.expected_output_shape())[0] + summary(num_features_grad, graph_features_grad, 'self.features') + + err = tf.test.compute_gradient_error([self.features_op], + [self.features.shape], + actual_op, TPC.expected_output_shape()) + self.assertLess(err, 1e-2) + + def _backward_bias(self, use_gpu=False): + self.init_ops() + with self.test_session(use_gpu=use_gpu, force_gpu=use_gpu): + actual_op = flex_convolution(self.features_op, + self.position_op, self.neighborhood_op, + self.theta_op, self.bias_op) + + graph_bias_grad, num_bias_grad = tf.test.compute_gradient( + [self.bias_op], [self.bias.shape], actual_op, + TPC.expected_output_shape())[0] + summary(num_bias_grad, graph_bias_grad, 'self.bias') + + err = tf.test.compute_gradient_error([self.bias_op], + [self.bias.shape], actual_op, + TPC.expected_output_shape()) + self.assertLess(err, 1e-2) + + def _backward_theta(self, use_gpu=False): + self.init_ops() + with self.test_session(use_gpu=use_gpu, force_gpu=use_gpu): + actual_op = flex_convolution(self.features_op, + self.position_op, self.neighborhood_op, + self.theta_op, self.bias_op) + + graph_theta_grad, num_theta_grad = tf.test.compute_gradient( + [self.theta_op], [self.theta.shape], actual_op, + TPC.expected_output_shape())[0] + summary(num_theta_grad, graph_theta_grad, 'self.theta') + + err = tf.test.compute_gradient_error([self.theta_op], + [self.theta.shape], actual_op, + TPC.expected_output_shape()) + self.assertLess(err, 1e-2) + + def test_backward_features(self): + self._backward_features(use_gpu=False) + self._backward_features(use_gpu=True) + + def test_backward_bias(self): + self._backward_bias(use_gpu=False) + self._backward_bias(use_gpu=True) + + def test_backward_theta(self): + self._backward_theta(use_gpu=False) + self._backward_theta(use_gpu=True) + + +if __name__ == '__main__': + tf.test.main() diff --git a/user_ops/test_flex_convolution_transpose.py b/user_ops/test_flex_convolution_transpose.py new file mode 100644 index 0000000..d627793 --- /dev/null +++ b/user_ops/test_flex_convolution_transpose.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright 2017 ComputerGraphics Tuebingen. 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. +# ============================================================================== +# Authors: Fabian Groh, Patrick Wieschollek, Hendrik P.A. Lensch + + +from PointTestCase import TPC, PointTestCase, summary +import tensorflow as tf +from __init__ import flex_convolution_transpose + + +class FlexDeconvTest(PointTestCase): + def __init__(self, methodName="runTest"): + super(FlexDeconvTest, self).__init__(methodName) + + def _forward(self, use_gpu=False, force_gpu=False): + self.init_ops() + with self.test_session(use_gpu=use_gpu, force_gpu=use_gpu) as sess: + actual = flex_convolution_transpose(self.features_op, + self.position_op, self.neighborhood_op, + self.theta_op, self.bias_op) + + actual = sess.run(actual) + + return actual + + def test_forward(self): + cpu = self._forward(use_gpu=False) + gpu = self._forward(use_gpu=True) + summary(cpu, gpu, 'self.features') + self.assertAllClose(cpu, gpu, rtol=1e-05) + + def _backward_features(self, use_gpu=False): + self.init_ops() + with self.test_session(use_gpu=use_gpu, force_gpu=use_gpu): + actual_op = flex_convolution_transpose(self.features_op, + self.position_op, + self.neighborhood_op, + self.theta_op, self.bias_op) + + graph_features_grad, num_features_grad = tf.test.compute_gradient( + [self.features_op], [self.features.shape], actual_op, + TPC.expected_output_shape())[0] + summary(num_features_grad, graph_features_grad, + 'self.features', max_outputs=1200000) + + err = tf.test.compute_gradient_error([self.features_op], + [self.features.shape], + actual_op, TPC.expected_output_shape()) + self.assertLess(err, 1e-2) + + def _backward_bias(self, use_gpu=False): + self.init_ops() + with self.test_session(use_gpu=use_gpu, force_gpu=use_gpu): + actual_op = flex_convolution_transpose(self.features_op, + self.position_op, + self.neighborhood_op, + self.theta_op, self.bias_op) + + graph_bias_grad, num_bias_grad = tf.test.compute_gradient( + [self.bias_op], [self.bias.shape], actual_op, + TPC.expected_output_shape())[0] + summary(num_bias_grad, graph_bias_grad, 'self.bias') + + err = tf.test.compute_gradient_error([self.bias_op], + [self.bias.shape], actual_op, + TPC.expected_output_shape()) + self.assertLess(err, 1e-2) + + def _backward_theta(self, use_gpu=False): + self.init_ops() + with self.test_session(use_gpu=use_gpu, force_gpu=use_gpu): + actual_op = flex_convolution_transpose(self.features_op, + self.position_op, + self.neighborhood_op, + self.theta_op, self.bias_op) + + graph_theta_grad, num_theta_grad = tf.test.compute_gradient( + [self.theta_op], [self.theta.shape], actual_op, + TPC.expected_output_shape())[0] + summary(num_theta_grad, graph_theta_grad, 'self.theta') + + err = tf.test.compute_gradient_error([self.theta_op], + [self.theta.shape], actual_op, + TPC.expected_output_shape()) + self.assertLess(err, 1e-2) + + def test_backward_features(self): + self._backward_features(use_gpu=False) + self._backward_features(use_gpu=True) + + def test_backward_bias(self): + self._backward_bias(use_gpu=False) + self._backward_bias(use_gpu=True) + + def test_backward_theta(self): + self._backward_theta(use_gpu=False) + self._backward_theta(use_gpu=True) + + +if __name__ == '__main__': + tf.test.main() diff --git a/user_ops/test_flex_pooling.py b/user_ops/test_flex_pooling.py new file mode 100644 index 0000000..c97a9bd --- /dev/null +++ b/user_ops/test_flex_pooling.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright 2017 ComputerGraphics Tuebingen. 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. +# ============================================================================== +# Authors: Fabian Groh, Patrick Wieschollek, Hendrik P.A. Lensch + + +from PointTestCase import PointTestCase +import tensorflow as tf +import numpy as np + +from __init__ import flex_pooling + + +class FlexPoolTest(PointTestCase): + def __init__(self, methodName="runTest"): + super(FlexPoolTest, self).__init__(methodName) + + def _forward(self, use_gpu=False): + self.init_ops() + with self.test_session(use_gpu=use_gpu, force_gpu=use_gpu) as sess: + actual_op, winner_op = flex_pooling(self.features_op, self.neighborhood_op) + actual = sess.run(actual_op) + return actual + + def test_forward(self): + cpu = self._forward(use_gpu=False) + gpu = self._forward(use_gpu=True) + self.assertAllClose(cpu, gpu) + + def test_backward(self): + cpu, winner_cpu = self._backward(use_gpu=False) + gpu, winner_gpu = self._backward(use_gpu=True) + self.assertAllClose(cpu, gpu) + self.assertAllClose(cpu[winner_cpu == 0].sum(), 0) + self.assertAllClose(gpu[winner_gpu == 0].sum(), 0) + + def _backward(self, use_gpu=False): + self.init_ops() + with self.test_session(use_gpu=use_gpu, force_gpu=use_gpu) as sess: + actual_op, winner_op = flex_pooling( + self.features_op, + self.neighborhood_op) + + graph_features_grad = tf.gradients(actual_op, [self.features_op])[0] + + dx, winner = sess.run([graph_features_grad, winner_op]) + return dx, winner + + def _simple_backward(self, use_gpu=False): + # BN + x = np.array([[[1], [2], [5], [3]]]).transpose(0, 2, 1) + n = np.array([[[0, 1, 2, 3], [1, 2, 3, 0], [2, 3, 0, 1], [3, 0, 1, 2, ]]]).transpose(0, 2, 1) + + x = tf.convert_to_tensor(x.astype(np.float32)) + n = tf.convert_to_tensor(n.astype(np.int32)) + + with self.test_session(use_gpu=use_gpu, force_gpu=use_gpu) as sess: + actual_op, winner_op = flex_pooling(x, n) + graph_features_grad = tf.gradients(actual_op, [x])[0] + return sess.run(graph_features_grad) + + def test_backward_simple(self): + cpu = self._simple_backward(use_gpu=False) + cpu[0, 0, 2] -= 4 + self.assertEqual(cpu.sum(), 0) + + gpu = self._simple_backward(use_gpu=True) + gpu[0, 0, 2] -= 4 + self.assertEqual(gpu.sum(), 0) + + +if __name__ == '__main__': + tf.test.main()