cherry pick files for public release

This commit is contained in:
Patrick Wieschollek
2018-09-23 12:44:08 +02:00
committed by PatWie
commit 5a9cd25aa9
32 changed files with 3947 additions and 0 deletions
+10
View File
@@ -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
Binary file not shown.

After

Width:  |  Height:  |  Size: 337 KiB

+136
View File
@@ -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/
+1
View File
@@ -0,0 +1 @@
filter=-legal/copyright,-readability/casting,-build/include_subdir,-whitespace/tab
+201
View File
@@ -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.
+60
View File
@@ -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:
<p align="center"> <img src=".github/flexconv.jpg" width="100%"> </p>
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}
}
```
+47
View File
@@ -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())
+215
View File
@@ -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])
+3
View File
@@ -0,0 +1,3 @@
[flake8]
max-line-length = 120
ignore = D102,E402,F403,N806,N802,N803,F405,E111,E114
+40
View File
@@ -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")
+273
View File
@@ -0,0 +1,273 @@
# Patrick Wieschollek, <mail@patwie.com>
# 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("<op-name>") expects the following files to exists:
# - kernels/<op-name>_kernel.cc
# - kernels/<op-name>_kernel_gpu.cu.cc (kernels/<op-name>_kernel.cu is supported as well)
# - kernels/<op-name>_op.cc
# - kernels/<op-name>_op.h
# - ops/<op-name>.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")
+130
View File
@@ -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)
+176
View File
@@ -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]
+173
View File
@@ -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 <typename Dtype>
struct FlexConvFunctor<CPUDevice, Dtype> {
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<Dtype, 3>();
const auto theta = theta_.tensor<Dtype, 4>();
const auto bias = bias_.tensor<Dtype, 2>();
const auto neighborhood = neighborhood_.tensor<int, 3>();
const auto positions = positions_.tensor<Dtype, 3>();
auto output = output_->tensor<Dtype, 3>();
// 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<CPUDevice, float>;
template <typename Dtype>
struct FlexConvGrad<CPUDevice, Dtype> {
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<Dtype, 3>();
const auto theta = theta_.tensor<Dtype, 4>();
const auto bias = bias_.tensor<Dtype, 2>();
const auto neighborhood = neighborhood_.tensor<int, 3>();
const auto positions = positions_.tensor<Dtype, 3>();
const auto topdiff = topdiff_.tensor<Dtype, 3>();
auto grad_features = grad_features_->tensor<Dtype, 3>();
auto grad_theta = grad_theta_->tensor<Dtype, 4>();
auto grad_bias = grad_bias_->tensor<Dtype, 2>();
// 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<CPUDevice, int>;
template struct FlexConvGrad<CPUDevice, float>;
// template struct FlexConvGrad<CPUDevice, double>;
} // namespace functor
} // namespace tensorflow
+532
View File
@@ -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 <cub/cub.cuh>
#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 <typename Dtype, typename NBtype, int Dp = 3, int C_N = 256,
int C_Dout = 32, int C_Din = 64>
struct ForwardKernel;
template <typename Dtype, typename NBtype, int Dp, int C_N, int C_Dout,
int C_Din>
__global__ void runForwardKernel(
const ForwardKernel<Dtype, NBtype, Dp, C_N, C_Dout, C_Din> kernel) {
kernel();
}
template <typename Dtype, typename NBtype, int Dp, int C_N, int C_Dout,
int C_Din>
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<<<grid, block, shm_size>>>((*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 <typename Dtype>
struct BackwardThetaKernel;
template <typename T>
__global__ void runBackwardKernel(const BackwardThetaKernel<T> kernel) {
kernel();
}
template <typename Dtype>
struct BackwardThetaKernel {
enum { C_N = 256, DP_MAX = 3, DEGREE_MAX = 2 };
void launch() {
dim3 block(C_N);
dim3 grid(Dout, Din);
runBackwardKernel<<<grid, block>>>((*this));
}
__device__ __forceinline__ void operator()() const {
typedef cub::BlockReduce<Dtype, C_N> 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 <typename Dtype>
struct BackwardFeatureKernel;
template <typename T>
__global__ void runBackwardKernel(const BackwardFeatureKernel<T> kernel) {
kernel();
}
template <typename Dtype>
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<<<fgrid, fblock, shm>>>((*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 <typename Dtype>
struct FlexConvFunctor<GPUDevice, Dtype> {
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<Dtype, NBtype, 3, 128, 32, 64> fwk;
fwk.N = N;
fwk.K = K;
fwk.Din = Din;
fwk.Dout = Dout;
fwk.d_features = features.flat<Dtype>().data();
fwk.d_positions = positions.flat<Dtype>().data();
fwk.d_neighborhood = neighborhood.flat<NBtype>().data();
fwk.d_theta = theta.flat<Dtype>().data();
fwk.d_bias = bias.flat<Dtype>().data();
fwk.d_output = output->flat<Dtype>().data();
fwk.launch(B);
if (!ctx->eigen_gpu_device().ok()) {
ctx->SetStatus(tensorflow::errors::Internal(
"FlexConvInvFunctor::forward::ForwardKernel execution failed"));
}
}
};
template struct FlexConvFunctor<GPUDevice, float>;
template <typename Dtype>
struct FlexConvGrad<GPUDevice, Dtype> {
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<Dtype, 3>();
const auto theta = theta_.tensor<Dtype, 4>();
const auto bias = bias_.tensor<Dtype, 2>();
const auto neighborhood = neighborhood_.tensor<int, 3>();
const auto positions = positions_.tensor<Dtype, 3>();
const auto topdiff = topdiff_.tensor<Dtype, 3>();
auto grad_features = grad_features_->tensor<Dtype, 3>();
auto grad_theta = grad_theta_->tensor<Dtype, 4>();
auto grad_bias = grad_bias_->tensor<Dtype, 2>();
// 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<const int*>(neighborhood.data());
const Dtype* positions_ptr =
reinterpret_cast<const Dtype*>(positions.data());
const Dtype* features_ptr = reinterpret_cast<const Dtype*>(features.data());
const Dtype* theta_ptr = reinterpret_cast<const Dtype*>(theta.data());
const Dtype* bias_ptr = reinterpret_cast<const Dtype*>(bias.data());
const Dtype* topdiff_ptr = reinterpret_cast<const Dtype*>(topdiff.data());
Dtype* grad_features_ptr = reinterpret_cast<float*>(grad_features.data());
Dtype* grad_theta_ptr = reinterpret_cast<Dtype*>(grad_theta.data());
Dtype* grad_bias_ptr = reinterpret_cast<Dtype*>(grad_bias.data());
cudaMemset(grad_features_ptr, 0, B * Din * N * sizeof(Dtype));
::tensorflow::CudaLaunchConfig cfg =
::tensorflow::GetCudaLaunchConfig(N, ctx->eigen_device<GPUDevice>());
typedef FlexConvCuda::BackwardFeatureKernel<Dtype> 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<Dtype> 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<GPUDevice, float>;
} // namespace functor
} // namespace tensorflow
#endif // GOOGLE_CUDA
+122
View File
@@ -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 <stdio.h>
#include <type_traits>
#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 <typename Device, typename Dtype>
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<Device, Dtype>()(
ctx, features_, theta_, bias_, neighborhood_, positions_, output_);
}
private:
TF_DISALLOW_COPY_AND_ASSIGN(FlexConvOp);
};
// Backward-Pass (CPU, GPU)
// --------------------------------------------------
template <typename Device, typename Dtype>
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<Device, Dtype>()(
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>("T"), \
FlexConvOp<CPUDevice, T>) \
REGISTER_KERNEL_BUILDER( \
Name("FlexConvGrad").Device(DEVICE_CPU).TypeConstraint<T>("T"), \
FlexConvGradOp<CPUDevice, T>)
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>("T"), \
FlexConvOp<GPUDevice, T>) \
REGISTER_KERNEL_BUILDER( \
Name("FlexConvGrad").Device(DEVICE_GPU).TypeConstraint<T>("T"), \
FlexConvGradOp<GPUDevice, T>)
TF_CALL_float(REGISTER_FLEXCONV_OP_GPU);
#undef REGISTER_FLEXCONV_OP_GPU
// #endif // GOOGLE_CUDA
} // namespace tensorflow
+53
View File
@@ -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 <typename Device, typename Dtype>
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 <typename Device, typename Dtype>
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_
+173
View File
@@ -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 <typename Dtype>
struct FlexDeconvFunctor<CPUDevice, Dtype> {
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<Dtype, 3>();
const auto theta = theta_.tensor<Dtype, 4>();
const auto bias = bias_.tensor<Dtype, 2>();
const auto neighborhood = neighborhood_.tensor<int, 3>();
const auto positions = positions_.tensor<Dtype, 3>();
auto output = output_->tensor<Dtype, 3>();
// 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<CPUDevice, float>;
template <typename Dtype>
struct FlexDeconvGrad<CPUDevice, Dtype> {
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<Dtype, 3>();
const auto theta = theta_.tensor<Dtype, 4>();
const auto bias = bias_.tensor<Dtype, 2>();
const auto neighborhood = neighborhood_.tensor<int, 3>();
const auto positions = positions_.tensor<Dtype, 3>();
const auto topdiff = topdiff_.tensor<Dtype, 3>();
auto grad_features = grad_features_->tensor<Dtype, 3>();
auto grad_theta = grad_theta_->tensor<Dtype, 4>();
auto grad_bias = grad_bias_->tensor<Dtype, 2>();
// 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<CPUDevice, int>;
template struct FlexDeconvGrad<CPUDevice, float>;
// template struct FlexDeconvGrad<CPUDevice, double>;
} // namespace functor
} // namespace tensorflow
@@ -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 <cub/cub.cuh>
#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 <typename Dtype>
__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 <typename Dtype>
__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 <typename Dtype>
struct FlexDeconvFunctor<GPUDevice, Dtype> {
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<Dtype>().data(), 0,
output_->NumElements() * sizeof(Dtype));
forward<Dtype><<<grid, block>>>(
B, N, K, Dp, Din, Dout, positions_.flat<Dtype>().data(),
features_.flat<Dtype>().data(), neighborhood_.flat<int>().data(),
theta_.flat<Dtype>().data(), bias_.flat<Dtype>().data(),
output_->flat<Dtype>().data());
}
};
template struct FlexDeconvFunctor<GPUDevice, float>;
template <typename Dtype>
struct FlexDeconvGrad<GPUDevice, Dtype> {
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<Dtype>().data(), 0,
grad_features_->NumElements() * sizeof(Dtype));
cudaMemset(grad_theta_->flat<Dtype>().data(), 0,
grad_theta_->NumElements() * sizeof(Dtype));
cudaMemset(grad_bias_->flat<Dtype>().data(), 0,
grad_bias_->NumElements() * sizeof(Dtype));
backward<Dtype><<<grid, block>>>(
B, N, K, Dp, Din, Dout,
positions_.flat<Dtype>().data(), features_.flat<Dtype>().data(),
neighborhood_.flat<int>().data(),
theta_.flat<Dtype>().data(), bias_.flat<Dtype>().data(),
topdiff_.flat<Dtype>().data(),
grad_features_->flat<Dtype>().data(), grad_theta_->flat<Dtype>().data(),
grad_bias_->flat<Dtype>().data());
}
};
template struct FlexDeconvGrad<GPUDevice, float>;
} // namespace functor
} // namespace tensorflow
#endif // GOOGLE_CUDA
+103
View File
@@ -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 <stdio.h>
#include <type_traits>
#include "tensorflow/core/framework/op.h"
#include "tensorflow/core/framework/op_kernel.h"
namespace tensorflow {
// Forward-Pass (CPU, GPU)
// --------------------------------------------------
template <typename Device, typename Dtype>
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<Device, Dtype>()(
ctx, features_, theta_, bias_, neighborhood_, positions_, output_);
}
private:
TF_DISALLOW_COPY_AND_ASSIGN(FlexDeconvOp);
};
// Backward-Pass (CPU, GPU)
// --------------------------------------------------
template <typename Device, typename Dtype>
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<Device, Dtype>()(
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<Dtype>("T"), \
OPNAME(NAME) < CPUDevice, Dtype >); \
REGISTER_KERNEL_BUILDER( \
Name(#NAME).Device(DEVICE_GPU).TypeConstraint<Dtype>("T"), \
OPNAME(NAME) < GPUDevice, Dtype >);
REGISTER(FlexDeconv, float);
REGISTER(FlexDeconvGrad, float);
} // namespace tensorflow
+53
View File
@@ -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 <typename Device, typename Dtype>
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 <typename Device, typename Dtype>
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_
+102
View File
@@ -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 <typename Dtype>
struct FlexPoolFunctor<CPUDevice, Dtype> {
void operator()(::tensorflow::OpKernelContext* ctx, const Tensor& features_,
const Tensor& neighborhood_, Tensor* output_,
Tensor* argmax_) {
const auto features = features_.tensor<Dtype, 3>();
const auto neighborhood = neighborhood_.tensor<int, 3>();
auto output = output_->tensor<Dtype, 3>();
auto argmax = argmax_->tensor<int, 3>();
// 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<Dtype>::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<CPUDevice, float>;
template <typename Dtype>
struct FlexPoolGrad<CPUDevice, Dtype> {
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<Dtype, 3>();
const auto neighborhood = neighborhood_.tensor<int, 3>();
const auto topdiff = topdiff_.tensor<Dtype, 3>();
const auto argmax = argmax_.tensor<int, 3>();
auto grad_features = grad_features_->tensor<Dtype, 3>();
// 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<CPUDevice, int>;
template struct FlexPoolGrad<CPUDevice, float>;
// template struct FlexPoolGrad<CPUDevice, double>;
} // namespace functor
} // namespace tensorflow
+171
View File
@@ -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 <cub/cub.cuh>
#include <limits>
#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 <typename Dtype>
__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 <typename Dtype>
__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 <typename Dtype>
struct FlexPoolFunctor<GPUDevice, Dtype> {
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<Dtype><<<grid, block>>>(
B, N, K, D,
features_.flat<Dtype>().data(), neighborhood_.flat<int>().data(),
output_->flat<Dtype>().data(), argmax_->flat<int>().data(),
std::numeric_limits<Dtype>::lowest());
if (!ctx->eigen_gpu_device().ok()) {
ctx->SetStatus(
tensorflow::errors::Internal("CUDA: FlexPoolFunctor Error!"));
}
}
};
template struct FlexPoolFunctor<GPUDevice, float>;
template <typename Dtype>
struct FlexPoolGrad<GPUDevice, Dtype> {
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<Dtype>().data(), 0,
grad_features_->NumElements() * sizeof(Dtype));
backward<Dtype><<<grid, block>>>(
B, N, K, D,
features_.flat<Dtype>().data(), neighborhood_.flat<int>().data(),
topdiff_.flat<Dtype>().data(), argmax_.flat<int>().data(),
grad_features_->flat<Dtype>().data());
if (!ctx->eigen_gpu_device().ok()) {
ctx->SetStatus(tensorflow::errors::Internal("CUDA: FlexPoolGrad Error!"));
}
}
};
template struct FlexPoolGrad<GPUDevice, float>;
} // namespace functor
} // namespace tensorflow
#endif // GOOGLE_CUDA
+113
View File
@@ -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 <stdio.h>
#include <type_traits>
#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 <typename Device, typename Dtype>
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<Device, Dtype>()(
ctx, features_, neighborhood_, output_, argmax_);
}
private:
TF_DISALLOW_COPY_AND_ASSIGN(FlexPoolOp);
};
// Backward-Pass (CPU, GPU)
// --------------------------------------------------
template <typename Device, typename Dtype>
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<Device, Dtype>()(
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>("T"), \
FlexPoolOp<CPUDevice, T>) \
REGISTER_KERNEL_BUILDER( \
Name("FlexPoolGrad").Device(DEVICE_CPU).TypeConstraint<T>("T"), \
FlexPoolGradOp<CPUDevice, T>)
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>("T"), \
FlexPoolOp<GPUDevice, T>) \
REGISTER_KERNEL_BUILDER( \
Name("FlexPoolGrad").Device(DEVICE_GPU).TypeConstraint<T>("T"), \
FlexPoolGradOp<GPUDevice, T>)
TF_CALL_float(REGISTER_FLEXPOOL_OP_GPU);
#undef REGISTER_FLEXPOOL_OP_GPU
#endif // GOOGLE_CUDA
} // namespace tensorflow
+50
View File
@@ -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 <typename Device, typename Dtype>
struct FlexPoolFunctor {
void operator()(::tensorflow::OpKernelContext* ctx, const Tensor& features_,
const Tensor& neighborhood_, Tensor* output_,
Tensor* argmax_);
};
template <typename Device, typename Dtype>
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_
+133
View File
@@ -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
+129
View File
@@ -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
+93
View File
@@ -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
+116
View File
@@ -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)
+109
View File
@@ -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()
+116
View File
@@ -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()
+87
View File
@@ -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()