From 356e46cafcbb68ed68c07e6786899479b3cfe4f2 Mon Sep 17 00:00:00 2001 From: mhuen Date: Wed, 10 Apr 2024 12:18:43 +0200 Subject: [PATCH 1/6] Add pre-commit hook --- .github/CONTRIBUTING.md | 24 ++++++++++++ .github/workflows/test_suite.yml | 44 +++++++++++++++++++++ .pre-commit-config.yaml | 67 ++++++++++++++++++++++++++++++++ .travis.yml | 37 ------------------ 4 files changed, 135 insertions(+), 37 deletions(-) create mode 100644 .github/CONTRIBUTING.md create mode 100644 .github/workflows/test_suite.yml create mode 100644 .pre-commit-config.yaml delete mode 100644 .travis.yml diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..7fb32db --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,24 @@ +# Contributing + +Welcome and thanks for considering to contribute to this repository! + +## Pre-commit Hooks + +When contributing to this project, please utilize the pre-commit hooks. If not installed yet, you will need to add the python package `pre-commit`: + + pip install pre-commit + +Once the package is installed, simply install the pre-commit hooks defined in the repository by executing: + + pre-commit install + +from within the repository directory. + +The pre-commit hooks will now automatically run when invoking `git commit`. Note, however, that this requires an active shell that has `pre-commit` installed. +You can also manually run the pre-commit on single files or on all files via: + + pre-commit run --all-files + +If you need to commit something even though there are errors (this should not have to be done!), then you can add the flag `--no-verify` to the `git commit` command. This will bypass the pre-commit hooks. + +Additional information is provided here: https://pre-commit.com/ diff --git a/.github/workflows/test_suite.yml b/.github/workflows/test_suite.yml new file mode 100644 index 0000000..fd74e67 --- /dev/null +++ b/.github/workflows/test_suite.yml @@ -0,0 +1,44 @@ +name: Unit Tests + +on: + workflow_dispatch: + push: + branches: + - main + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [ + "3.9", + "3.10", + "3.11", + "3.12", + ] + + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - uses: pre-commit/action@v3.0.0 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install .[test] + - name: Test with pytest + run: | + coverage run -m pytest -v -s + - name: Generate Coverage Report + run: | + coverage report -m + continue-on-error: false + + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v3 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..f2d4143 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,67 @@ +# Adapted from simweights +# SPDX-FileCopyrightText: © 2022 the SimWeights contributors +# +# SPDX-License-Identifier: BSD-2-Clause + +ci: + autoupdate_commit_msg: autoupdate pre-commit hooks + autoupdate_schedule: quarterly +repos: + - repo: https://github.com/psf/black + rev: 24.3.0 + hooks: + - id: black + - id: black-jupyter + - repo: https://github.com/asottile/blacken-docs + rev: "1.16.0" + hooks: + - id: blacken-docs + args: [-l 100] + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.3.5 + hooks: + - id: ruff + args: [--fix, --show-fixes] + - repo: https://github.com/codespell-project/codespell + rev: v2.2.6 + hooks: + - id: codespell + args: [-L, livetime] + - repo: https://github.com/pre-commit/pygrep-hooks + rev: v1.10.0 + hooks: + - id: python-no-log-warn + - id: python-no-eval + exclude: ^tests/.* + - id: python-use-type-annotations + - id: rst-backticks + - id: rst-directive-colons + - id: rst-inline-touching-normal + - repo: https://github.com/Lucas-C/pre-commit-hooks + rev: v1.5.5 + hooks: + - id: forbid-crlf + - id: forbid-tabs + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: check-added-large-files + - id: check-ast + - id: check-builtin-literals + - id: check-case-conflict + - id: check-docstring-first + - id: check-executables-have-shebangs + - id: check-json + - id: check-merge-conflict + - id: check-shebang-scripts-are-executable + - id: check-toml + - id: check-vcs-permalinks + - id: debug-statements + - id: detect-private-key + - id: end-of-file-fixer + - id: fix-byte-order-marker + - id: mixed-line-ending + - id: name-tests-test + args: [--pytest-test-first] + - id: trailing-whitespace + exclude: \.svg$ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 7897e9a..0000000 --- a/.travis.yml +++ /dev/null @@ -1,37 +0,0 @@ -language: python - -notifications: - email: false - -python: - - '2.7' - - '3.5' - - '3.6' - - '3.7' - -dist: bionic - -before_install: - - python --version - - pip install -U pip - - pip install -U -r test_requirements.txt - - pip install -U -r requirements.txt - -install: - - pip install ".[test]" . # install package + test dependencies - -script: - - coverage run -m unittest discover # run tests - - coverage report -m # report to console - -after_success: - - codecov # submit coverage - -# deploy: -# provider: pypi -# user: -# secure: YOUR_PYPI_USER_PUBLIC_SECRET_HERE -# password: -# secure: YOUR_PYPI_PASS_PUBLIC_SECRET_HERE -# on: -# tags: true From 60c195db9124fa70c4149a1cb15898a9f53c5ba6 Mon Sep 17 00:00:00 2001 From: mhuen Date: Wed, 10 Apr 2024 13:25:45 +0200 Subject: [PATCH 2/6] Update to pyproject.toml --- .github/workflows/test_suite.yml | 2 + pyproject.toml | 75 ++++++++++++++++++++++++++++++++ setup.py | 26 +---------- 3 files changed, 79 insertions(+), 24 deletions(-) create mode 100644 pyproject.toml diff --git a/.github/workflows/test_suite.yml b/.github/workflows/test_suite.yml index fd74e67..f758646 100644 --- a/.github/workflows/test_suite.yml +++ b/.github/workflows/test_suite.yml @@ -13,6 +13,8 @@ jobs: strategy: matrix: python-version: [ + "3.7", + "3.8", "3.9", "3.10", "3.11", diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..ebde855 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,75 @@ +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" + +[project] +name = "tfscripts" +description = "Helper functions for tensorflow" +readme = "README.md" +dynamic = ["version"] +authors = [ + { name = "Mirco Huennefeld", email = "mirco.huennefeld@tu-dortmund.de" }, +] +maintainers = [ + { name = "Mirco Huennefeld", email = "mirco.huennefeld@tu-dortmund.de" }, +] +requires-python = ">=3.7" + +dependencies = ["numpy", "matplotlib", "tensorflow"] + +classifiers = [ + "Development Status :: 4 - Beta", + "License :: OSI Approved :: BSD License", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Scientific/Engineering :: Physics", + "Topic :: Scientific/Engineering :: Statistics", +] + +[project.optional-dependencies] +dev = ["pre-commit","black","ruff"] +test = ["pytest", "coverage", "codecov"] + +[project.urls] +Homepage = "https://github.com/icecube/TFScripts" +Documentation = "https://github.com/icecube/TFScripts" +"Bug Tracker" = "https://github.com/icecube/TFScripts/issues" +Discussions = "https://github.com/icecube/TFScripts/discussions" + +[tool.setuptools] +packages = ["tfscripts"] + +[tool.setuptools.dynamic] +version = {attr = "tfscripts.__version__"} + +[tool.black] +line-length = 79 +target-version = ["py38"] + +[tool.ruff] +# select = ["ALL"] +fixable = ["I"] +ignore = [ + "D213", # multi-line-summary-second-line incompatible with multi-line-summary-first-line + "D203", # one-blank-line-before-class" incompatible with no-blank-line-before-class + "D401", # non-imperative-mood + "D417", # undocumented-param + "ANN101", # missing-type-self + "ANN401", # any-type + "FBT", # flake8-boolean-trap + "INP", # flake8-no-pep420 + "T20", # flake8-print + "TCH", # flake8-type-checking + "S101", # assert-used + "F401", # imported but unused. NOTE: sooner or later, we should not ignore this + ] +line-length = 79 +target-version = "py38" + +[tool.ruff.pydocstyle] +convention = "numpy" diff --git a/setup.py b/setup.py index 614c40b..6068493 100644 --- a/setup.py +++ b/setup.py @@ -1,25 +1,3 @@ -#!/usr/bin/env python -import os -from setuptools import setup, find_packages +from setuptools import setup -here = os.path.dirname(__file__) - -about = {} -with open(os.path.join(here, 'tfscripts', '__about__.py')) as fobj: - exec(fobj.read(), about) - -setup( - name='tfscripts', - version=about['__version__'], - packages=find_packages(), - install_requires=[ - 'numpy', 'matplotlib', - ], - include_package_data=True, - author=about['__author__'], - author_email=about['__author_email__'], - maintainer=about['__author__'], - maintainer_email=about['__author_email__'], - description=about['__description__'], - url=about['__url__'] -) +setup() From 2e6b07c22e27c63202fde9e1ea24068705417b2b Mon Sep 17 00:00:00 2001 From: mhuen Date: Wed, 10 Apr 2024 13:31:41 +0200 Subject: [PATCH 3/6] Drop python 3.7 --- .github/workflows/test_suite.yml | 1 - README.md | 4 +++- pyproject.toml | 1 - 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test_suite.yml b/.github/workflows/test_suite.yml index f758646..9ecda05 100644 --- a/.github/workflows/test_suite.yml +++ b/.github/workflows/test_suite.yml @@ -13,7 +13,6 @@ jobs: strategy: matrix: python-version: [ - "3.7", "3.8", "3.9", "3.10", diff --git a/README.md b/README.md index d6bd43a..e1875f8 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ | Testing | Coverage | | :-----: | :------: | -| [![Build Status](https://travis-ci.org/icecubeopensource/tfscripts.svg?branch=master)](https://travis-ci.org/icecubeopensource/tfscripts) | [![Coverage Status](https://codecov.io/gh/icecubeopensource/tfscripts/branch/master/graph/badge.svg)](https://codecov.io/gh/icecubeopensource/tfscripts/branch/master) +| [![Unit Tests](https://github.com/icecube/tfscripts/actions/workflows/test_suite.yml/badge.svg)](https://github.com/icecube/tfscripts/actions/workflows/test_suite.yml) +[![codecov](https://codecov.io/github/icecube/tfscripts/graph/badge.svg?token=ZQ6K8V9F4U)](https://codecov.io/github/icecube/tfscripts) + # TFScripts diff --git a/pyproject.toml b/pyproject.toml index ebde855..f935f7e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,6 @@ classifiers = [ "Development Status :: 4 - Beta", "License :: OSI Approved :: BSD License", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", From a4dea60b6543532952e3d31cd03396aa21525ca5 Mon Sep 17 00:00:00 2001 From: mhuen Date: Wed, 10 Apr 2024 14:11:14 +0200 Subject: [PATCH 4/6] Fix pre-commit errors --- .gitignore | 2 - requirements.txt | 2 - test/test_conv.py | 302 ++++---- test/test_layers.py | 112 +-- test_requirements.txt | 3 - tfscripts/compat/v1/conv.py | 883 +++++++++++++---------- tfscripts/compat/v1/core.py | 249 ++++--- tfscripts/compat/v1/hex/conv.py | 270 ++++--- tfscripts/compat/v1/hex/icecube.py | 223 ++++-- tfscripts/compat/v1/hex/rotation.py | 243 ++++--- tfscripts/compat/v1/hex/visual.py | 153 ++-- tfscripts/compat/v1/layers.py | 671 +++++++++-------- tfscripts/compat/v1/pooling.py | 250 ++++--- tfscripts/compat/v1/utils.py | 106 +-- tfscripts/compat/v1/weights.py | 90 +-- tfscripts/conv.py | 1041 +++++++++++++++------------ tfscripts/core.py | 311 ++++---- tfscripts/hex/conv.py | 340 +++++---- tfscripts/hex/icecube.py | 228 ++++-- tfscripts/hex/rotation.py | 303 ++++---- tfscripts/hex/visual.py | 153 ++-- tfscripts/layers.py | 814 +++++++++++---------- tfscripts/model.py | 154 ++-- tfscripts/pooling.py | 336 +++++---- tfscripts/utils.py | 98 +-- tfscripts/weights.py | 164 +++-- 26 files changed, 4320 insertions(+), 3181 deletions(-) delete mode 100755 requirements.txt delete mode 100644 test_requirements.txt diff --git a/.gitignore b/.gitignore index 62d3252..ebb044f 100644 --- a/.gitignore +++ b/.gitignore @@ -148,5 +148,3 @@ configs/user/* # exclude everything tfscripts.egg-info/* - - diff --git a/requirements.txt b/requirements.txt deleted file mode 100755 index aa094d9..0000000 --- a/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -numpy -matplotlib diff --git a/test/test_conv.py b/test/test_conv.py index d9f38a9..5daf603 100644 --- a/test/test_conv.py +++ b/test/test_conv.py @@ -1,4 +1,3 @@ -#!/usr/local/bin/python from __future__ import division, print_function import unittest @@ -15,153 +14,208 @@ def setUp(self): self.random_state = np.random.RandomState(42) def test_locally_connected_2d(self): - """Test locally_connected_2d - """ - data = tf.constant(self.random_state.normal(size=[3, 2, 4, 2]), - dtype=tf.float32) - kernel = tf.constant(self.random_state.normal(size=[8, 18, 5]), - dtype=tf.float32) + """Test locally_connected_2d""" + data = tf.constant( + self.random_state.normal(size=[3, 2, 4, 2]), dtype=tf.float32 + ) + kernel = tf.constant( + self.random_state.normal(size=[8, 18, 5]), dtype=tf.float32 + ) num_outputs = 5 filter_size = [3, 3] true_result = [ - [[[-3.0002763, 0.5384941, 0.7630725, 3.2629867, -0.66444194], - [3.8548074, 0.39627433, -1.8674281, -0.95996654, 0.53664565], - [-4.909938, 1.2740642, -3.9075174, -1.3829101, -1.2598065], - [-0.10984063, 0.8664815, 1.9967539, 1.5672208, -2.882845]], - - [[2.9014444, 1.9103334, -1.027496, 1.3560967, -2.6223845], - [-4.33412, -1.3134818, 3.1853037, 3.4777935, -1.2933552], - [0.21087825, 8.807638, 3.8791056, -2.6520872, -0.607337], - [0.20364004, 1.6297944, -3.6308692, 5.099339, -0.74615014]]], - - - [[[0.8476062, -0.31244925, 2.3357773, -3.1579304, 1.1329677], - [-0.5461242, -7.558393, -3.5357292, 1.6523337, 0.07174261], - [2.671926, 0.0200243, 8.013702, -2.6686308, 5.518297], - [0.09115195, 1.8910196, 1.4207761, -1.258101, 5.327482]], - - [[-1.8102887, 1.4639647, -3.0855474, -2.871198, 4.173253], - [1.1287231, 4.458473, -5.9783406, 0.94382215, 0.49254256], - [-2.6642013, -3.3089342, 3.1150613, -8.594527, -0.5965001], - [3.1609251, 0.6673362, 0.33882388, -0.89896905, 2.7638702]]], - - - [[[1.7869619, 1.4026697, 0.5613424, -3.588409, 0.39702317], - [2.9465265, 0.35991257, -6.4655128, 3.9436753, -1.9748771], - [-0.15422773, 4.3497667, 1.1177152, -3.7317548, -2.9032776], - [-1.547115, 4.481364, 2.3119464, -3.0446053, 4.720777]], - - [[-1.342461, -4.6650257, -2.8820145, -2.1707344, -0.3114946], - [-1.8312358, 3.0615933, 3.7827466, 2.4145176, 0.16694853], - [-1.5634956, -5.2658362, 0.12334719, -4.874626, 4.04391], - [2.1574607, -0.08705652, -2.270496, -0.37243897, 3.500473]]]] - - conv_layer = conv.LocallyConnected2d(input_shape=data.get_shape(), - num_outputs=num_outputs, - filter_size=filter_size, - kernel=kernel) + [ + [ + [-3.0002763, 0.5384941, 0.7630725, 3.2629867, -0.66444194], + [ + 3.8548074, + 0.39627433, + -1.8674281, + -0.95996654, + 0.53664565, + ], + [-4.909938, 1.2740642, -3.9075174, -1.3829101, -1.2598065], + [-0.10984063, 0.8664815, 1.9967539, 1.5672208, -2.882845], + ], + [ + [2.9014444, 1.9103334, -1.027496, 1.3560967, -2.6223845], + [-4.33412, -1.3134818, 3.1853037, 3.4777935, -1.2933552], + [0.21087825, 8.807638, 3.8791056, -2.6520872, -0.607337], + [0.20364004, 1.6297944, -3.6308692, 5.099339, -0.74615014], + ], + ], + [ + [ + [0.8476062, -0.31244925, 2.3357773, -3.1579304, 1.1329677], + [-0.5461242, -7.558393, -3.5357292, 1.6523337, 0.07174261], + [2.671926, 0.0200243, 8.013702, -2.6686308, 5.518297], + [0.09115195, 1.8910196, 1.4207761, -1.258101, 5.327482], + ], + [ + [-1.8102887, 1.4639647, -3.0855474, -2.871198, 4.173253], + [1.1287231, 4.458473, -5.9783406, 0.94382215, 0.49254256], + [-2.6642013, -3.3089342, 3.1150613, -8.594527, -0.5965001], + [3.1609251, 0.6673362, 0.33882388, -0.89896905, 2.7638702], + ], + ], + [ + [ + [1.7869619, 1.4026697, 0.5613424, -3.588409, 0.39702317], + [2.9465265, 0.35991257, -6.4655128, 3.9436753, -1.9748771], + [ + -0.15422773, + 4.3497667, + 1.1177152, + -3.7317548, + -2.9032776, + ], + [-1.547115, 4.481364, 2.3119464, -3.0446053, 4.720777], + ], + [ + [ + -1.342461, + -4.6650257, + -2.8820145, + -2.1707344, + -0.3114946, + ], + [-1.8312358, 3.0615933, 3.7827466, 2.4145176, 0.16694853], + [-1.5634956, -5.2658362, 0.12334719, -4.874626, 4.04391], + [2.1574607, -0.08705652, -2.270496, -0.37243897, 3.500473], + ], + ], + ] + + conv_layer = conv.LocallyConnected2d( + input_shape=data.get_shape(), + num_outputs=num_outputs, + filter_size=filter_size, + kernel=kernel, + ) result = conv_layer(data) self.assertTrue(np.allclose(true_result, result, atol=1e-6)) def test_locally_connected_3d(self): - """Test locally_connected_3d - """ - data = tf.constant(self.random_state.normal(size=[1, 3, 3, 3, 2]), - dtype=tf.float32) - kernel = tf.constant(self.random_state.normal(size=[27, 54, 2]), - dtype=tf.float32) + """Test locally_connected_3d""" + data = tf.constant( + self.random_state.normal(size=[1, 3, 3, 3, 2]), dtype=tf.float32 + ) + kernel = tf.constant( + self.random_state.normal(size=[27, 54, 2]), dtype=tf.float32 + ) num_outputs = 2 filter_size = [3, 3, 3] true_result = [ - [[[[2.0361388, 2.8748353], - [-4.8949614, 3.601464], - [-0.6368098, -3.6394928]], - - [[-1.6584768, 2.1284351], - [-2.4174252, -1.4464463], - [-2.8733394, 1.4183671]], - - [[-0.24431975, -5.925253], - [-5.9550424, 6.0112863], - [-0.5549349, -0.07634258]]], - - [[[-5.9965715, -0.4911141], - [-1.3716308, -8.968927], - [5.7334156, 4.173443]], - - [[-11.042645, -8.164853], - [8.448492, -4.57167], - [-1.2495683, 3.2970955]], - - [[2.1897097, 1.4297469], - [1.5857544, 4.076191], - [0.44746077, -0.31982255]]], - - [[[4.443694, 3.1021364], - [-4.425407, -0.5171719], - [0.19204128, 1.3953105]], - - [[0.6423461, -4.424771], - [-8.816259, -10.956709], - [-2.0773346, 6.9159746]], - - [[2.138038, -5.0552483], - [1.2522638, 8.907527], - [4.3867416, 1.9319328]]]]] - - conv_layer = conv.LocallyConnected3d(input_shape=data.get_shape(), - num_outputs=num_outputs, - filter_size=filter_size, - kernel=kernel) + [ + [ + [ + [2.0361388, 2.8748353], + [-4.8949614, 3.601464], + [-0.6368098, -3.6394928], + ], + [ + [-1.6584768, 2.1284351], + [-2.4174252, -1.4464463], + [-2.8733394, 1.4183671], + ], + [ + [-0.24431975, -5.925253], + [-5.9550424, 6.0112863], + [-0.5549349, -0.07634258], + ], + ], + [ + [ + [-5.9965715, -0.4911141], + [-1.3716308, -8.968927], + [5.7334156, 4.173443], + ], + [ + [-11.042645, -8.164853], + [8.448492, -4.57167], + [-1.2495683, 3.2970955], + ], + [ + [2.1897097, 1.4297469], + [1.5857544, 4.076191], + [0.44746077, -0.31982255], + ], + ], + [ + [ + [4.443694, 3.1021364], + [-4.425407, -0.5171719], + [0.19204128, 1.3953105], + ], + [ + [0.6423461, -4.424771], + [-8.816259, -10.956709], + [-2.0773346, 6.9159746], + ], + [ + [2.138038, -5.0552483], + [1.2522638, 8.907527], + [4.3867416, 1.9319328], + ], + ], + ] + ] + + conv_layer = conv.LocallyConnected3d( + input_shape=data.get_shape(), + num_outputs=num_outputs, + filter_size=filter_size, + kernel=kernel, + ) result = conv_layer(data) self.assertTrue(np.allclose(true_result, result, atol=1e-6)) def test_conv3d_stacked(self): - """Test locally_connected_3d - """ - data = tf.constant(self.random_state.normal(size=[7, 5, 4, 7, 2]), - dtype=tf.float32) - kernel = tf.constant(self.random_state.normal(size=[3, 3, 3, 2, 4]), - dtype=tf.float32) + """Test locally_connected_3d""" + data = tf.constant( + self.random_state.normal(size=[7, 5, 4, 7, 2]), dtype=tf.float32 + ) + kernel = tf.constant( + self.random_state.normal(size=[3, 3, 3, 2, 4]), dtype=tf.float32 + ) strides_list = [[1, 1, 1, 1, 1], [1, 2, 2, 2, 1]] - padding_list = ['SAME'] + padding_list = ["SAME"] for strides, padding in product(strides_list, padding_list): - result_tf = tf.nn.convolution(input=data, filters=kernel, - strides=strides[1:-1], - padding=padding) - result = conv.conv3d_stacked(input=data, filter=kernel, - strides=strides, padding=padding) + result_tf = tf.nn.convolution( + input=data, + filters=kernel, + strides=strides[1:-1], + padding=padding, + ) + result = conv.conv3d_stacked( + input=data, filter=kernel, strides=strides, padding=padding + ) self.assertTrue(np.allclose(result, result_tf, atol=1e-5)) def test_conv4d_stacked(self): - """Test locally_connected_3d - """ - data = tf.constant(self.random_state.normal(size=[1, 2, 2, 2, 2, 1]), - dtype=tf.float32) - kernel = tf.constant(self.random_state.normal(size=[2, 2, 2, 2, 1, 1]), - dtype=tf.float32) - - true_result = [[[[[-3.4794035], - [0.19396555]], - [[3.3645966], - [0.17348471]]], - [[[-0.9240602], - [1.229038]], - [[-0.48166633], - [-0.47118214]]]], - [[[[3.195785], - [-2.968795]], - [[-2.0781631], - [-0.35241044]]], - [[[1.5140774], - [2.4484003]], - [[1.5703532], - [0.5695023]]]]] + """Test locally_connected_3d""" + data = tf.constant( + self.random_state.normal(size=[1, 2, 2, 2, 2, 1]), dtype=tf.float32 + ) + kernel = tf.constant( + self.random_state.normal(size=[2, 2, 2, 2, 1, 1]), dtype=tf.float32 + ) + + true_result = [ + [ + [[[-3.4794035], [0.19396555]], [[3.3645966], [0.17348471]]], + [[[-0.9240602], [1.229038]], [[-0.48166633], [-0.47118214]]], + ], + [ + [[[3.195785], [-2.968795]], [[-2.0781631], [-0.35241044]]], + [[[1.5140774], [2.4484003]], [[1.5703532], [0.5695023]]], + ], + ] result = conv.conv4d_stacked(input=data, filter=kernel) self.assertTrue(np.allclose(true_result, result, atol=1e-6)) diff --git a/test/test_layers.py b/test/test_layers.py index 2b3daed..99c679f 100644 --- a/test/test_layers.py +++ b/test/test_layers.py @@ -1,4 +1,3 @@ -#!/usr/local/bin/python from __future__ import division, print_function import unittest @@ -15,81 +14,90 @@ def setUp(self): self.random_state = np.random.RandomState(42) def test_ConvNdLayers(self): - """Test ConvNdLayers - """ - data = tf.constant(self.random_state.normal(size=[3, 4, 4, 4, 2]), - dtype=tf.float32) + """Test ConvNdLayers""" + data = tf.constant( + self.random_state.normal(size=[3, 4, 4, 4, 2]), dtype=tf.float32 + ) shapes = [[3, 3, 3, 2, 3], [3, 3, 3, 3, 7], [3, 3, 3, 7, 1]] - weights_list = [tf.constant(self.random_state.normal(size=shape), - dtype=tf.float32) for shape in shapes] + weights_list = [ + tf.constant(self.random_state.normal(size=shape), dtype=tf.float32) + for shape in shapes + ] shapes = [[3], [7], [1]] - biases_list = [tf.constant(self.random_state.normal(size=shape), - dtype=tf.float32) for shape in shapes] - - pooling_strides_list = [[1, 1, 1, 1, 1], - [1, 2, 2, 2, 1], - [1, 2, 2, 2, 1]] - pooling_ksize_list = [[1, 1, 1, 1, 1], - [1, 2, 2, 2, 1], - [1, 2, 2, 2, 1]] - pooling_type_list = [None, 'max', 'max'] - activation_list = ['elu', 'relu', ''] + biases_list = [ + tf.constant(self.random_state.normal(size=shape), dtype=tf.float32) + for shape in shapes + ] + + pooling_strides_list = [ + [1, 1, 1, 1, 1], + [1, 2, 2, 2, 1], + [1, 2, 2, 2, 1], + ] + pooling_ksize_list = [ + [1, 1, 1, 1, 1], + [1, 2, 2, 2, 1], + [1, 2, 2, 2, 1], + ] + pooling_type_list = [None, "max", "max"] + activation_list = ["elu", "relu", ""] filter_size_list = [[3, 3, 3], [2, 0, 3], [3, 3, 3]] num_filters_list = [3, 7, 1] - method_list = ['convolution', 'hex_convolution', 'convolution'] + method_list = ["convolution", "hex_convolution", "convolution"] conv_nd_layers = layers.ConvNdLayers( - input_shape=data.get_shape(), - filter_size_list=filter_size_list, - num_filters_list=num_filters_list, - pooling_type_list=pooling_type_list, - pooling_strides_list=pooling_strides_list, - pooling_ksize_list=pooling_ksize_list, - activation_list=activation_list, - method_list=method_list, - weights_list=weights_list, - biases_list=biases_list, - verbose=False) + input_shape=data.get_shape(), + filter_size_list=filter_size_list, + num_filters_list=num_filters_list, + pooling_type_list=pooling_type_list, + pooling_strides_list=pooling_strides_list, + pooling_ksize_list=pooling_ksize_list, + activation_list=activation_list, + method_list=method_list, + weights_list=weights_list, + biases_list=biases_list, + verbose=False, + ) layer = conv_nd_layers(data, is_training=False) - result_true = [[[[[1.5821583]]]], - [[[[1.8506659]]]], - [[[[1.6045672]]]]] + result_true = [[[[[1.5821583]]]], [[[[1.8506659]]]], [[[[1.6045672]]]]] self.assertTrue(np.allclose(result_true, layer[-1].numpy())) def test_FCLayers(self): - """Test FCLayers - """ - data = tf.constant(self.random_state.normal(size=[3, 7]), - dtype=tf.float32) + """Test FCLayers""" + data = tf.constant( + self.random_state.normal(size=[3, 7]), dtype=tf.float32 + ) shapes = [[7, 3], [3, 7], [7, 1]] - weights_list = [tf.constant(self.random_state.normal(size=shape), - dtype=tf.float32) for shape in shapes] + weights_list = [ + tf.constant(self.random_state.normal(size=shape), dtype=tf.float32) + for shape in shapes + ] shapes = [[3], [7], [1]] - biases_list = [tf.constant(self.random_state.normal(size=shape), - dtype=tf.float32) for shape in shapes] + biases_list = [ + tf.constant(self.random_state.normal(size=shape), dtype=tf.float32) + for shape in shapes + ] - activation_list = ['elu', 'relu', ''] + activation_list = ["elu", "relu", ""] fc_sizes = [3, 7, 1] fc_layers = layers.FCLayers( - input_shape=data.get_shape(), - fc_sizes=fc_sizes, - activation_list=activation_list, - weights_list=weights_list, - biases_list=biases_list, - verbose=False, - ) + input_shape=data.get_shape(), + fc_sizes=fc_sizes, + activation_list=activation_list, + weights_list=weights_list, + biases_list=biases_list, + verbose=False, + ) layer = fc_layers(data, is_training=False) - result_true = [[0.06657974], - [-0.3617597], - [0.00241985]] + result_true = [[0.06657974], [-0.3617597], [0.00241985]] self.assertTrue(np.allclose(result_true, layer[-1].numpy())) diff --git a/test_requirements.txt b/test_requirements.txt deleted file mode 100644 index 7f5b4eb..0000000 --- a/test_requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -coverage -codecov -tensorflow diff --git a/tfscripts/compat/v1/conv.py b/tfscripts/compat/v1/conv.py index 99e9d51..9bf6a5e 100644 --- a/tfscripts/compat/v1/conv.py +++ b/tfscripts/compat/v1/conv.py @@ -1,4 +1,4 @@ -''' +""" Conv functions for tfscripts.compat.v1: convolution helper functions, locally connected 2d and 3d convolutions, @@ -8,7 +8,7 @@ stacked convolution 3d and 4d -''' +""" from __future__ import division, print_function @@ -49,20 +49,20 @@ def conv_output_length(input_length, filter_size, padding, stride, dilation=1): """ if input_length is None: return None - assert padding in {'SAME', 'VALID'} + assert padding in {"SAME", "VALID"} dilated_filter_size = filter_size + (filter_size - 1) * (dilation - 1) - if padding == 'SAME': + if padding == "SAME": output_length = input_length - elif padding == 'VALID': + elif padding == "VALID": output_length = input_length - dilated_filter_size + 1 return (output_length + stride - 1) // stride def get_filter_lr(filter_size): - ''' + """ Get the number of elements left and right of the filter position. If filtersize is even, there will be one more element to the @@ -76,11 +76,11 @@ def get_filter_lr(filter_size): Returns ------- (int, int) - Number of elemnts left and right + Number of elements left and right of the filter position. - ''' + """ if filter_size % 2 == 1: # uneven filter size: same size to left and right filter_l = filter_size // 2 @@ -94,7 +94,7 @@ def get_filter_lr(filter_size): def get_start_index(input_length, filter_size, padding, stride, dilation=1): - ''' + """ Get start index for a convolution along an axis. This will be the index of the input axis for the first valid convolution position. @@ -121,7 +121,7 @@ def get_start_index(input_length, filter_size, padding, stride, dilation=1): ------ ValueError Description - ''' + """ filter_l, filter_r = get_filter_lr(filter_size) # The start index is important for strides and dilation @@ -129,11 +129,18 @@ def get_start_index(input_length, filter_size, padding, stride, dilation=1): # that works and is VALID: start_index = 0 found_valid_position = False - if padding == 'VALID': + if padding == "VALID": for i in range(input_length): - if len(range(max(i - dilation*filter_l, 0), - min(i + dilation*filter_r + 1, input_length), - dilation)) == filter_size: + if ( + len( + range( + max(i - dilation * filter_l, 0), + min(i + dilation * filter_r + 1, input_length), + dilation, + ) + ) + == filter_size + ): # we found the first index that doesn't need padding found_valid_position = True break @@ -146,7 +153,7 @@ def get_start_index(input_length, filter_size, padding, stride, dilation=1): def get_conv_indices(position, input_length, filter_size, stride, dilation=1): - ''' + """ Get indices for a convolution patch. The indices will correspond to the elements being convolved with the filter of size @@ -170,17 +177,19 @@ def get_conv_indices(position, input_length, filter_size, stride, dilation=1): ------- list of int Indices of a convolution patch - ''' + """ filter_l, filter_r = get_filter_lr(filter_size) - indices = range(max(position - dilation*filter_l, 0), - min(position + dilation*filter_r + 1, input_length), - dilation) + indices = range( + max(position - dilation * filter_l, 0), + min(position + dilation * filter_r + 1, input_length), + dilation, + ) return indices def get_conv_slice(position, input_length, filter_size, stride, dilation=1): - ''' + """ Get slice for a convolution patch. The slice will correspond to the elements being convolved with the filter of size @@ -206,30 +215,32 @@ def get_conv_slice(position, input_length, filter_size, stride, dilation=1): slice of input being convolved with the filter at 'position'. And number of elements cropped at either end, which are needed for padding - ''' + """ filter_l, filter_r = get_filter_lr(filter_size) - min_index = position - dilation*filter_l - max_index = position + dilation*filter_r + 1 + min_index = position - dilation * filter_l + max_index = position + dilation * filter_r + 1 - conv_slice = slice(max(min_index, 0), - min(max_index, input_length), - dilation) + conv_slice = slice( + max(min_index, 0), min(max_index, input_length), dilation + ) - padding_left = int(np.ceil(max(- min_index, 0) / dilation)) + padding_left = int(np.ceil(max(-min_index, 0) / dilation)) padding_right = int(np.ceil(max(max_index - input_length, 0) / dilation)) return conv_slice, (padding_left, padding_right) -def locally_connected_2d(input, - num_outputs, - filter_size, - kernel=None, - strides=[1, 1], - padding='SAME', - dilation_rate=None): - ''' +def locally_connected_2d( + input, + num_outputs, + filter_size, + kernel=None, + strides=[1, 1], + padding="SAME", + dilation_rate=None, +): + """ Like conv2d, but doesn't share weights. (not tested/validated yet!!) @@ -259,7 +270,7 @@ def locally_connected_2d(input, ------- 2 Tensors: result and kernels. Have the same type as input. - ''' + """ if dilation_rate is None: dilation_rate = [1, 1] @@ -270,29 +281,34 @@ def locally_connected_2d(input, input_shape = input.get_shape().as_list() # sanity checks - assert len(filter_size) == 2, \ - 'Filter size must be of shape [x,y], but is {!r}'.format(filter_size) - assert np.prod(filter_size) > 0, \ - 'Filter sizes must be greater than 0' - assert len(input_shape) == 4, \ - 'Shape is expected to be of length 4, but is {!r}'.format(input_shape) + assert ( + len(filter_size) == 2 + ), "Filter size must be of shape [x,y], but is {!r}".format(filter_size) + assert np.prod(filter_size) > 0, "Filter sizes must be greater than 0" + assert ( + len(input_shape) == 4 + ), "Shape is expected to be of length 4, but is {!r}".format(input_shape) # calculate output shape output_shape = np.empty(4, dtype=int) for i in range(2): - output_shape[i+1] = conv_output_length(input_length=input_shape[i + 1], - filter_size=filter_size[i], - padding=padding, - stride=strides[i], - dilation=dilation_rate[i]) + output_shape[i + 1] = conv_output_length( + input_length=input_shape[i + 1], + filter_size=filter_size[i], + padding=padding, + stride=strides[i], + dilation=dilation_rate[i], + ) output_shape[0] = -1 output_shape[3] = num_outputs num_inputs = input_shape[3] - kernel_shape = (np.prod(output_shape[1:-1]), - np.prod(filter_size) * num_inputs, - num_outputs) + kernel_shape = ( + np.prod(output_shape[1:-1]), + np.prod(filter_size) * num_inputs, + num_outputs, + ) # ------------------ # 1x1 convolution @@ -302,18 +318,23 @@ def locally_connected_2d(input, if kernel is None: kernel = new_weights(shape=input_shape[1:] + [num_outputs]) output = tf.reduce_sum( - input_tensor=tf.expand_dims(input, axis=4) * kernel, axis=3) + input_tensor=tf.expand_dims(input, axis=4) * kernel, axis=3 + ) return output, kernel # ------------------ # get slices # ------------------ - start_indices = [get_start_index(input_length=input_shape[i + 1], - filter_size=filter_size[i], - padding=padding, - stride=strides[i], - dilation=dilation_rate[i]) - for i in range(2)] + start_indices = [ + get_start_index( + input_length=input_shape[i + 1], + filter_size=filter_size[i], + padding=padding, + stride=strides[i], + dilation=dilation_rate[i], + ) + for i in range(2) + ] input_patches = [] # --------------------------- @@ -322,13 +343,15 @@ def locally_connected_2d(input, for x in range(start_indices[0], input_shape[1], strides[0]): # get slice for patch along x-axis - slice_x, padding_x = get_conv_slice(x, - input_length=input_shape[1], - filter_size=filter_size[0], - stride=strides[0], - dilation=dilation_rate[0]) - - if padding == 'VALID' and padding_x != (0, 0): + slice_x, padding_x = get_conv_slice( + x, + input_length=input_shape[1], + filter_size=filter_size[0], + stride=strides[0], + dilation=dilation_rate[0], + ) + + if padding == "VALID" and padding_x != (0, 0): # skip this x position, since it does not provide # a valid patch for padding 'VALID' continue @@ -339,19 +362,21 @@ def locally_connected_2d(input, for y in range(start_indices[1], input_shape[2], strides[1]): # get indices for patch along y-axis - slice_y, padding_y = get_conv_slice(y, - input_length=input_shape[2], - filter_size=filter_size[1], - stride=strides[1], - dilation=dilation_rate[1]) + slice_y, padding_y = get_conv_slice( + y, + input_length=input_shape[2], + filter_size=filter_size[1], + stride=strides[1], + dilation=dilation_rate[1], + ) - if padding == 'VALID' and padding_y != (0, 0): + if padding == "VALID" and padding_y != (0, 0): # skip this y position, since it does not provide # a valid patch for padding 'VALID' continue # At this point, slice_x/y either correspond - # to a vaild patch, or padding is 'SAME' + # to a valid patch, or padding is 'SAME' # Now we need to pick slice and add it to # input patches. These will later be convolved # with the kernel. @@ -361,18 +386,20 @@ def locally_connected_2d(input, # ------------------------------------------ input_patch = input[:, slice_x, slice_y, :] - if padding == 'SAME': + if padding == "SAME": # pad with zeros paddings = [(0, 0), padding_x, padding_y, (0, 0)] if paddings != [(0, 0), (0, 0), (0, 0), (0, 0), (0, 0)]: - input_patch = tf.pad(tensor=input_patch, - paddings=paddings, - mode='CONSTANT', - ) + input_patch = tf.pad( + tensor=input_patch, + paddings=paddings, + mode="CONSTANT", + ) # reshape input_patch = tf.reshape( - input_patch, [-1, 1, np.prod(filter_size) * num_inputs, 1]) + input_patch, [-1, 1, np.prod(filter_size) * num_inputs, 1] + ) # append to list input_patches.append(input_patch) @@ -396,14 +423,16 @@ def locally_connected_2d(input, return output, kernel -def locally_connected_3d(input, - num_outputs, - filter_size, - kernel=None, - strides=[1, 1, 1], - padding='SAME', - dilation_rate=None): - ''' +def locally_connected_3d( + input, + num_outputs, + filter_size, + kernel=None, + strides=[1, 1, 1], + padding="SAME", + dilation_rate=None, +): + """ Like conv3d, but doesn't share weights. Parameters @@ -432,7 +461,7 @@ def locally_connected_3d(input, ------- 2 Tensors: result and kernels. Have the same type as input. - ''' + """ if dilation_rate is None: dilation_rate = [1, 1, 1] @@ -443,28 +472,34 @@ def locally_connected_3d(input, input_shape = input.get_shape().as_list() # sanity checks - assert len(filter_size) == 3, \ - 'Filter size must be of shape [x,y,z], but is {!r}'.format(filter_size) - assert np.prod(filter_size) > 0, 'Filter sizes must be greater than 0' - assert len(input_shape) == 5, \ - 'Shape is expected to be of length 5, but is {!r}'.format(input_shape) + assert ( + len(filter_size) == 3 + ), "Filter size must be of shape [x,y,z], but is {!r}".format(filter_size) + assert np.prod(filter_size) > 0, "Filter sizes must be greater than 0" + assert ( + len(input_shape) == 5 + ), "Shape is expected to be of length 5, but is {!r}".format(input_shape) # calculate output shape output_shape = np.empty(5, dtype=int) for i in range(3): - output_shape[i+1] = conv_output_length(input_length=input_shape[i + 1], - filter_size=filter_size[i], - padding=padding, - stride=strides[i], - dilation=dilation_rate[i]) + output_shape[i + 1] = conv_output_length( + input_length=input_shape[i + 1], + filter_size=filter_size[i], + padding=padding, + stride=strides[i], + dilation=dilation_rate[i], + ) output_shape[0] = -1 output_shape[4] = num_outputs num_inputs = input_shape[4] - kernel_shape = (np.prod(output_shape[1:-1]), - np.prod(filter_size) * num_inputs, - num_outputs) + kernel_shape = ( + np.prod(output_shape[1:-1]), + np.prod(filter_size) * num_inputs, + num_outputs, + ) # ------------------ # 1x1x1 convolution @@ -474,18 +509,23 @@ def locally_connected_3d(input, if kernel is None: kernel = new_weights(shape=input_shape[1:] + [num_outputs]) output = tf.reduce_sum( - input_tensor=tf.expand_dims(input, axis=5) * kernel, axis=4) + input_tensor=tf.expand_dims(input, axis=5) * kernel, axis=4 + ) return output, kernel # ------------------ # get slices # ------------------ - start_indices = [get_start_index(input_length=input_shape[i + 1], - filter_size=filter_size[i], - padding=padding, - stride=strides[i], - dilation=dilation_rate[i]) - for i in range(3)] + start_indices = [ + get_start_index( + input_length=input_shape[i + 1], + filter_size=filter_size[i], + padding=padding, + stride=strides[i], + dilation=dilation_rate[i], + ) + for i in range(3) + ] input_patches = [] # --------------------------- @@ -494,13 +534,15 @@ def locally_connected_3d(input, for x in range(start_indices[0], input_shape[1], strides[0]): # get slice for patch along x-axis - slice_x, padding_x = get_conv_slice(x, - input_length=input_shape[1], - filter_size=filter_size[0], - stride=strides[0], - dilation=dilation_rate[0]) - - if padding == 'VALID' and padding_x != (0, 0): + slice_x, padding_x = get_conv_slice( + x, + input_length=input_shape[1], + filter_size=filter_size[0], + stride=strides[0], + dilation=dilation_rate[0], + ) + + if padding == "VALID" and padding_x != (0, 0): # skip this x position, since it does not provide # a valid patch for padding 'VALID' continue @@ -511,13 +553,15 @@ def locally_connected_3d(input, for y in range(start_indices[1], input_shape[2], strides[1]): # get indices for patch along y-axis - slice_y, padding_y = get_conv_slice(y, - input_length=input_shape[2], - filter_size=filter_size[1], - stride=strides[1], - dilation=dilation_rate[1]) + slice_y, padding_y = get_conv_slice( + y, + input_length=input_shape[2], + filter_size=filter_size[1], + stride=strides[1], + dilation=dilation_rate[1], + ) - if padding == 'VALID' and padding_y != (0, 0): + if padding == "VALID" and padding_y != (0, 0): # skip this y position, since it does not provide # a valid patch for padding 'VALID' continue @@ -529,19 +573,20 @@ def locally_connected_3d(input, # get indices for patch along y-axis slice_z, padding_z = get_conv_slice( - z, - input_length=input_shape[3], - filter_size=filter_size[2], - stride=strides[2], - dilation=dilation_rate[2]) - - if padding == 'VALID' and padding_z != (0, 0): + z, + input_length=input_shape[3], + filter_size=filter_size[2], + stride=strides[2], + dilation=dilation_rate[2], + ) + + if padding == "VALID" and padding_z != (0, 0): # skip this z position, since it does not provide # a valid patch for padding 'VALID' continue # At this point, slice_x/y/z either correspond - # to a vaild patch, or padding is 'SAME' + # to a valid patch, or padding is 'SAME' # Now we need to pick slice and add it to # input patches. These will later be convolved # with the kernel. @@ -551,19 +596,26 @@ def locally_connected_3d(input, # ------------------------------------------ input_patch = input[:, slice_x, slice_y, slice_z, :] - if padding == 'SAME': + if padding == "SAME": # pad with zeros - paddings = [(0, 0), padding_x, padding_y, - padding_z, (0, 0)] + paddings = [ + (0, 0), + padding_x, + padding_y, + padding_z, + (0, 0), + ] if paddings != [(0, 0), (0, 0), (0, 0), (0, 0), (0, 0)]: - input_patch = tf.pad(tensor=input_patch, - paddings=paddings, - mode='CONSTANT', - ) + input_patch = tf.pad( + tensor=input_patch, + paddings=paddings, + mode="CONSTANT", + ) # reshape input_patch = tf.reshape( - input_patch, [-1, 1, np.prod(filter_size) * num_inputs, 1]) + input_patch, [-1, 1, np.prod(filter_size) * num_inputs, 1] + ) # append to list input_patches.append(input_patch) @@ -587,22 +639,23 @@ def locally_connected_3d(input, return output, kernel -def local_translational3d_trafo(input, - num_outputs, - filter_size, - fcn=None, - weights=None, - strides=[1, 1, 1], - padding='SAME', - dilation_rate=None, - is_training=True, - ): - ''' +def local_translational3d_trafo( + input, + num_outputs, + filter_size, + fcn=None, + weights=None, + strides=[1, 1, 1], + padding="SAME", + dilation_rate=None, + is_training=True, +): + """ Applies a transformation defined by the callable fcn(input_patch) to the input_patch. Returns the output of fcn. - Similiar to conv3d, but instead of a convolution, the transformation + Similar to conv3d, but instead of a convolution, the transformation defined by fcn is performed. The transformation is learnable and shared - accross input_patches similar to how the convolutional kernel is sahred. + across input_patches similar to how the convolutional kernel is sahred. Parameters ---------- @@ -643,7 +696,7 @@ def local_translational3d_trafo(input, ------- 2 Tensors: result and kernels. Have the same type as input. - ''' + """ if dilation_rate is None: dilation_rate = [1, 1, 1] @@ -654,34 +707,40 @@ def local_translational3d_trafo(input, input_shape = input.get_shape().as_list() # sanity checks - assert len(filter_size) == 3, \ - 'Filter size must be of shape [x,y,z], but is {!r}'.format(filter_size) - assert np.prod(filter_size) > 0, 'Filter sizes must be greater than 0' - assert len(input_shape) == 5, \ - 'Shape is expected to be of length 5, but is {!r}'.format(input_shape) + assert ( + len(filter_size) == 3 + ), "Filter size must be of shape [x,y,z], but is {!r}".format(filter_size) + assert np.prod(filter_size) > 0, "Filter sizes must be greater than 0" + assert ( + len(input_shape) == 5 + ), "Shape is expected to be of length 5, but is {!r}".format(input_shape) # calculate output shape output_shape = np.empty(5, dtype=int) for i in range(3): - output_shape[i+1] = conv_output_length(input_length=input_shape[i + 1], - filter_size=filter_size[i], - padding=padding, - stride=strides[i], - dilation=dilation_rate[i]) + output_shape[i + 1] = conv_output_length( + input_length=input_shape[i + 1], + filter_size=filter_size[i], + padding=padding, + stride=strides[i], + dilation=dilation_rate[i], + ) output_shape[0] = -1 output_shape[4] = num_outputs - num_inputs = input_shape[4] - # ------------------ # get slices # ------------------ - start_indices = [get_start_index(input_length=input_shape[i + 1], - filter_size=filter_size[i], - padding=padding, - stride=strides[i], - dilation=dilation_rate[i]) - for i in range(3)] + start_indices = [ + get_start_index( + input_length=input_shape[i + 1], + filter_size=filter_size[i], + padding=padding, + stride=strides[i], + dilation=dilation_rate[i], + ) + for i in range(3) + ] output = [] # --------------------------- @@ -690,13 +749,15 @@ def local_translational3d_trafo(input, for x in range(start_indices[0], input_shape[1], strides[0]): # get slice for patch along x-axis - slice_x, padding_x = get_conv_slice(x, - input_length=input_shape[1], - filter_size=filter_size[0], - stride=strides[0], - dilation=dilation_rate[0]) - - if padding == 'VALID' and padding_x != (0, 0): + slice_x, padding_x = get_conv_slice( + x, + input_length=input_shape[1], + filter_size=filter_size[0], + stride=strides[0], + dilation=dilation_rate[0], + ) + + if padding == "VALID" and padding_x != (0, 0): # skip this x position, since it does not provide # a valid patch for padding 'VALID' continue @@ -707,13 +768,15 @@ def local_translational3d_trafo(input, for y in range(start_indices[1], input_shape[2], strides[1]): # get indices for patch along y-axis - slice_y, padding_y = get_conv_slice(y, - input_length=input_shape[2], - filter_size=filter_size[1], - stride=strides[1], - dilation=dilation_rate[1]) + slice_y, padding_y = get_conv_slice( + y, + input_length=input_shape[2], + filter_size=filter_size[1], + stride=strides[1], + dilation=dilation_rate[1], + ) - if padding == 'VALID' and padding_y != (0, 0): + if padding == "VALID" and padding_y != (0, 0): # skip this y position, since it does not provide # a valid patch for padding 'VALID' continue @@ -725,19 +788,20 @@ def local_translational3d_trafo(input, # get indices for patch along y-axis slice_z, padding_z = get_conv_slice( - z, - input_length=input_shape[3], - filter_size=filter_size[2], - stride=strides[2], - dilation=dilation_rate[2]) - - if padding == 'VALID' and padding_z != (0, 0): + z, + input_length=input_shape[3], + filter_size=filter_size[2], + stride=strides[2], + dilation=dilation_rate[2], + ) + + if padding == "VALID" and padding_z != (0, 0): # skip this z position, since it does not provide # a valid patch for padding 'VALID' continue # At this point, slice_x/y/z either correspond - # to a vaild patch, or padding is 'SAME' + # to a valid patch, or padding is 'SAME' # Now we need to pick slice and add it to # input patches. These will later be convolved # with the kernel. @@ -749,15 +813,21 @@ def local_translational3d_trafo(input, # input[:, slice_x, slice_y, slice_z, :], 5) input_patch = input[:, slice_x, slice_y, slice_z, :] - if padding == 'SAME': + if padding == "SAME": # pad with zeros - paddings = [(0, 0), padding_x, padding_y, - padding_z, (0, 0)] + paddings = [ + (0, 0), + padding_x, + padding_y, + padding_z, + (0, 0), + ] if paddings != [(0, 0), (0, 0), (0, 0), (0, 0), (0, 0)]: - input_patch = tf.pad(tensor=input_patch, - paddings=paddings, - mode='CONSTANT', - ) + input_patch = tf.pad( + tensor=input_patch, + paddings=paddings, + mode="CONSTANT", + ) # ------------------------------ # Perform trafo on input_patch @@ -785,14 +855,14 @@ def local_translational3d_trafo(input, def dynamic_conv( - input, - filter, - batch_size=None, - strides=[1, 1, 1], - padding='SAME', - dilation_rate=None, - ): - ''' + input, + filter, + batch_size=None, + strides=[1, 1, 1], + padding="SAME", + dilation_rate=None, +): + """ Equivalent to tf.nn.convolution, but filter has additional batch dimension. This allows the filter to be a function of some input, hence, enabling dynamic convolutions. @@ -840,7 +910,7 @@ def dynamic_conv( Returns ------- A Tensor. Has the same type as input. - ''' + """ input_shape = input.get_shape().as_list() filter_shape = filter.get_shape().as_list() @@ -853,42 +923,40 @@ def dynamic_conv( try: batch_size = dynamic_conv.BATCH_SIZE - except Exception as e: + except Exception: batch_size = input.get_shape().as_list()[0] - split_inputs = tf.split(input, - batch_size, - axis=0) - split_filters = tf.unstack(filter, - batch_size, - axis=0) + split_inputs = tf.split(input, batch_size, axis=0) + split_filters = tf.unstack(filter, batch_size, axis=0) output_list = [] for split_input, split_filter in zip(split_inputs, split_filters): output_list.append( - tf.nn.convolution(split_input, - split_filter, - strides=strides, - padding=padding, - dilations=dilation_rate, - ) - ) + tf.nn.convolution( + split_input, + split_filter, + strides=strides, + padding=padding, + dilations=dilation_rate, + ) + ) output = tf.concat(output_list, axis=0) return output -def trans3d_op(input, - num_out_channel, - filter_size, - method, - trafo=None, - filter=None, - strides=[1, 1, 1], - padding='SAME', - dilation_rate=None, - stack_axis=None, - ): - ''' +def trans3d_op( + input, + num_out_channel, + filter_size, + method, + trafo=None, + filter=None, + strides=[1, 1, 1], + padding="SAME", + dilation_rate=None, + stack_axis=None, +): + """ Applies a transformation to the input_patch. Input patches are obtained as it is done in conv3d. The transformation that is performed on this input patch is defined by the method argument: @@ -976,7 +1044,7 @@ def trans3d_op(input, Description ValueError Description - ''' + """ if dilation_rate is None: dilation_rate = [1, 1, 1] @@ -987,23 +1055,30 @@ def trans3d_op(input, input_shape = input.get_shape().as_list() # sanity checks - if method not in ['dynamic_convolution', 'locally_connected', - 'local_trafo']: - raise ValueError('Method unknown: {!r}'.format(method)) - assert len(filter_size) == 3, \ - 'Filter size must be of shape [x,y,z], but is {!r}'.format(filter_size) - assert np.prod(filter_size) > 0, 'Filter sizes must be greater than 0' - assert len(input_shape) == 5, \ - 'Shape is expected to be of length 5, but is {}'.format(input_shape) + if method not in [ + "dynamic_convolution", + "locally_connected", + "local_trafo", + ]: + raise ValueError("Method unknown: {!r}".format(method)) + assert ( + len(filter_size) == 3 + ), "Filter size must be of shape [x,y,z], but is {!r}".format(filter_size) + assert np.prod(filter_size) > 0, "Filter sizes must be greater than 0" + assert ( + len(input_shape) == 5 + ), "Shape is expected to be of length 5, but is {}".format(input_shape) # calculate output shape output_shape = np.empty(5, dtype=int) for i in range(3): - output_shape[i+1] = conv_output_length(input_length=input_shape[i + 1], - filter_size=filter_size[i], - padding=padding, - stride=strides[i], - dilation=dilation_rate[i]) + output_shape[i + 1] = conv_output_length( + input_length=input_shape[i + 1], + filter_size=filter_size[i], + padding=padding, + stride=strides[i], + dilation=dilation_rate[i], + ) output_shape[0] = -1 output_shape[4] = num_out_channel @@ -1020,8 +1095,8 @@ def trans3d_op(input, # if stack_axis is 2 (z-dimension): # [batch, x, y, filter_x, filter_y, num_in_channel] s_patches_shape = list(patches_shape) - del s_patches_shape[1+stack_axis] - del s_patches_shape[3+stack_axis] + del s_patches_shape[1 + stack_axis] + del s_patches_shape[3 + stack_axis] # define parameters for extract_image_patches ksizes = [1] + list(filter_size) + [1] @@ -1037,35 +1112,43 @@ def trans3d_op(input, # ------------------ # get slices # ------------------ - start_indices = [get_start_index(input_length=input_shape[i + 1], - filter_size=filter_size[i], - padding=padding, - stride=strides[i], - dilation=dilation_rate[i]) - for i in range(3)] + start_indices = [ + get_start_index( + input_length=input_shape[i + 1], + filter_size=filter_size[i], + padding=padding, + stride=strides[i], + dilation=dilation_rate[i], + ) + for i in range(3) + ] output = [] # --------------------------------------- # loop over all positions in dim #stack_axis # ---------------------------------------- - for s in range(start_indices[stack_axis], input_shape[stack_axis + 1], - strides[stack_axis]): + for s in range( + start_indices[stack_axis], + input_shape[stack_axis + 1], + strides[stack_axis], + ): # get slice for patch along stack_axis slice_s, padding_s = get_conv_slice( - s, - input_length=input_shape[stack_axis + 1], - filter_size=filter_size[stack_axis], - stride=strides[stack_axis], - dilation=dilation_rate[stack_axis]) - - if padding == 'VALID' and padding_s != (0, 0): + s, + input_length=input_shape[stack_axis + 1], + filter_size=filter_size[stack_axis], + stride=strides[stack_axis], + dilation=dilation_rate[stack_axis], + ) + + if padding == "VALID" and padding_s != (0, 0): # skip this axis position, since it does not provide # a valid patch for padding 'VALID' continue # At this point, slice_s either corresponds - # to a vaild patch, or padding is 'SAME' + # to a valid patch, or padding is 'SAME' # Now we need to combine the input_patches # ---------------------------------------------------- @@ -1081,17 +1164,20 @@ def trans3d_op(input, # [batch, filter_x, filter_y, in_channel] # input_patches has shape: # [batch, x,y, filter_x*filter_y*in_channel] - input_s_patches = tf.image.extract_patches(input_s, - sizes=ksizes, - strides=strides2d, - rates=rates, - padding=padding) + input_s_patches = tf.image.extract_patches( + input_s, + sizes=ksizes, + strides=strides2d, + rates=rates, + padding=padding, + ) # reshape input_patches to # [batch, x,y, filter_x,filter_y,in_channel] # (assuming stack_axis=2) - reshaped_input_s_patches = tf.reshape(input_s_patches, - shape=s_patches_shape) + reshaped_input_s_patches = tf.reshape( + input_s_patches, shape=s_patches_shape + ) input_patches.append(reshaped_input_s_patches) # input_patches almost has correct shape: @@ -1100,8 +1186,10 @@ def trans3d_op(input, # However, padding must still be applied # Pad input_patches along stack_axis dimension - if padding == 'SAME' and method in ['locally_connected', - 'local_trafo']: + if padding == "SAME" and method in [ + "locally_connected", + "local_trafo", + ]: if padding_s != (0, 0): zeros = tf.zeros_like(input_patches[0]) @@ -1124,16 +1212,17 @@ def trans3d_op(input, # ------------------------------ # Perform dynamic convolution # ------------------------------ - if method == 'dynamic_convolution': + if method == "dynamic_convolution": # filter has shape # [filter_x, filter_y, filter_z, in_channels, out_channels] # Dimensions need to match: expanded_input_patches = tf.expand_dims(input_patches, -1) - begin = [0]*5 - size = [-1]*5 + begin = [0] * 5 + size = [-1] * 5 begin[stack_axis] = padding_s[0] - size[stack_axis] = (filter_size[stack_axis] - padding_s[1] - - padding_s[0]) + size[stack_axis] = ( + filter_size[stack_axis] - padding_s[1] - padding_s[0] + ) sliced_filter = tf.slice(filter, begin, size) output_patch = tf.reduce_sum( input_tensor=expanded_input_patches * sliced_filter, @@ -1144,13 +1233,13 @@ def trans3d_op(input, # ------------------------------ # Locally connected # ------------------------------ - elif method == 'locally_connected': + elif method == "locally_connected": raise NotImplementedError() # ------------------------------ # Perform trafo on input_patch # ------------------------------ - elif method == 'local_trafo': + elif method == "local_trafo": output_patch = trafo(input_patches) output.append(output_patch) @@ -1162,8 +1251,8 @@ def trans3d_op(input, return output -def conv3d_stacked(input, filter, strides=[1, 1, 1, 1, 1], padding='SAME'): - ''' +def conv3d_stacked(input, filter, strides=[1, 1, 1, 1, 1], padding="SAME"): + """ Equivalent to tensorflows conv3d. This method is slightly slower, but appears to use less vram. @@ -1197,7 +1286,7 @@ def conv3d_stacked(input, filter, strides=[1, 1, 1, 1, 1], padding='SAME'): Returns ------- A Tensor. Has the same type as input. - ''' + """ # unpack along z dimension tensors_z = tf.unstack(input, axis=3) @@ -1208,21 +1297,28 @@ def conv3d_stacked(input, filter, strides=[1, 1, 1, 1, 1], padding='SAME'): if len_zs % 2 == 1: # uneven filter size: same size to left and right - filter_l = int(len_zs/2) - filter_r = int(len_zs/2) + filter_l = int(len_zs / 2) + filter_r = int(len_zs / 2) else: # even filter size: one more to right - filter_l = int(len_zs/2) - 1 - filter_r = int(len_zs/2) + filter_l = int(len_zs / 2) - 1 + filter_r = int(len_zs / 2) # The start index is important for strides # The strides start with the first element # that works and is VALID: start_index = 0 - if padding == 'VALID': + if padding == "VALID": for i in range(size_of_z_dim): - if len(range(max(i - filter_l, 0), - min(i + filter_r+1, size_of_z_dim))) == len_zs: + if ( + len( + range( + max(i - filter_l, 0), + min(i + filter_r + 1, size_of_z_dim), + ) + ) + == len_zs + ): # we found the first index that doesn't need padding break start_index = i @@ -1231,10 +1327,11 @@ def conv3d_stacked(input, filter, strides=[1, 1, 1, 1, 1], padding='SAME'): result_z = [] for i in range(start_index, size_of_z_dim, strides[3]): - if padding == 'VALID': + if padding == "VALID": # Get indices z_s - indices_z_s = range(max(i - filter_l, 0), - min(i + filter_r+1, size_of_z_dim)) + indices_z_s = range( + max(i - filter_l, 0), min(i + filter_r + 1, size_of_z_dim) + ) # check if Padding = 'VALID' if len(indices_z_s) == len_zs: @@ -1243,30 +1340,35 @@ def conv3d_stacked(input, filter, strides=[1, 1, 1, 1, 1], padding='SAME'): # sum over all remaining index_z_i in indices_z_s for j, index_z_i in enumerate(indices_z_s): tensors_z_convoluted.append( - tf.nn.conv2d(input=tensors_z[index_z_i], - filters=kernel_z[j], - strides=strides[:3]+strides[4:], - padding=padding) - ) + tf.nn.conv2d( + input=tensors_z[index_z_i], + filters=kernel_z[j], + strides=strides[:3] + strides[4:], + padding=padding, + ) + ) sum_tensors_z_s = tf.add_n(tensors_z_convoluted) # put together result_z.append(sum_tensors_z_s) - elif padding == 'SAME': + elif padding == "SAME": tensors_z_convoluted = [] - for kernel_j, j in enumerate(range(i - filter_l, - (i + 1) + filter_r)): + for kernel_j, j in enumerate( + range(i - filter_l, (i + 1) + filter_r) + ): # we can just leave out the invalid z coordinates - # since they will be padded with 0's and therfore + # since they will be padded with 0's and therefore # don't contribute to the sum if 0 <= j < size_of_z_dim: tensors_z_convoluted.append( - tf.nn.conv2d(input=tensors_z[j], - filters=kernel_z[kernel_j], - strides=strides[:3]+strides[4:], - padding=padding) - ) + tf.nn.conv2d( + input=tensors_z[j], + filters=kernel_z[kernel_j], + strides=strides[:3] + strides[4:], + padding=padding, + ) + ) sum_tensors_z_s = tf.add_n(tensors_z_convoluted) # put together result_z.append(sum_tensors_z_s) @@ -1275,14 +1377,16 @@ def conv3d_stacked(input, filter, strides=[1, 1, 1, 1, 1], padding='SAME'): return tf.stack(result_z, axis=3) -def conv4d_stacked(input, filter, - strides=[1, 1, 1, 1, 1, 1], - padding='SAME', - dilation_rate=None, - stack_axis=None, - stack_nested=False, - ): - ''' +def conv4d_stacked( + input, + filter, + strides=[1, 1, 1, 1, 1, 1], + padding="SAME", + dilation_rate=None, + stack_axis=None, + stack_nested=False, +): + """ Computes a convolution over 4 dimensions. Python generalization of tensorflow's conv3d with dilation. conv4d_stacked uses tensorflows conv3d and stacks results along @@ -1314,14 +1418,14 @@ def conv4d_stacked(input, filter, chosen. This is only an educated guess of the best choice! stack_nested : Bool - If set to True, this will stack in a for loop seperately and afterwards + If set to True, this will stack in a for loop separately and afterwards combine the results. In most cases slower, but maybe less memory needed. Returns ------- A Tensor. Has the same type as input. - ''' + """ # heuristically choose stack_axis if stack_axis is None: @@ -1329,43 +1433,53 @@ def conv4d_stacked(input, filter, dil_array = np.ones(4) else: dil_array = np.asarray(dilation_rate) - outputsizes = (np.asarray(input.get_shape().as_list()[1:5]) / - np.asarray(strides[1:5])) - outputsizes -= dil_array*( - np.asarray(filter.get_shape().as_list()[:4])-1) - stack_axis = np.argmin(outputsizes)+1 + outputsizes = np.asarray( + input.get_shape().as_list()[1:5] + ) / np.asarray(strides[1:5]) + outputsizes -= dil_array * ( + np.asarray(filter.get_shape().as_list()[:4]) - 1 + ) + stack_axis = np.argmin(outputsizes) + 1 if dilation_rate is not None: - dilation_along_stack_axis = dilation_rate[stack_axis-1] + dilation_along_stack_axis = dilation_rate[stack_axis - 1] else: dilation_along_stack_axis = 1 tensors_t = tf.unstack(input, axis=stack_axis) - kernel_t = tf.unstack(filter, axis=stack_axis-1) + kernel_t = tf.unstack(filter, axis=stack_axis - 1) - noOfInChannels = input.get_shape().as_list()[-1] - len_ts = filter.get_shape().as_list()[stack_axis-1] + len_ts = filter.get_shape().as_list()[stack_axis - 1] size_of_t_dim = input.get_shape().as_list()[stack_axis] if len_ts % 2 == 1: # uneven filter size: same size to left and right - filter_l = int(len_ts/2) - filter_r = int(len_ts/2) + filter_l = int(len_ts / 2) + filter_r = int(len_ts / 2) else: # even filter size: one more to right - filter_l = int(len_ts/2) - 1 - filter_r = int(len_ts/2) + filter_l = int(len_ts / 2) - 1 + filter_r = int(len_ts / 2) # The start index is important for strides and dilation # The strides start with the first element # that works and is VALID: start_index = 0 - if padding == 'VALID': + if padding == "VALID": for i in range(size_of_t_dim): - if len(range(max(i - dilation_along_stack_axis*filter_l, 0), - min(i + dilation_along_stack_axis*filter_r+1, - size_of_t_dim), - dilation_along_stack_axis)) == len_ts: + if ( + len( + range( + max(i - dilation_along_stack_axis * filter_l, 0), + min( + i + dilation_along_stack_axis * filter_r + 1, + size_of_t_dim, + ), + dilation_along_stack_axis, + ) + ) + == len_ts + ): # we found the first index that doesn't need padding break start_index = i @@ -1378,13 +1492,16 @@ def conv4d_stacked(input, filter, input_patch = [] tensors_t_convoluted = [] - if padding == 'VALID': + if padding == "VALID": # Get indices t_s - indices_t_s = range(max(i - dilation_along_stack_axis*filter_l, 0), - min(i + dilation_along_stack_axis*filter_r+1, - size_of_t_dim), - dilation_along_stack_axis) + indices_t_s = range( + max(i - dilation_along_stack_axis * filter_l, 0), + min( + i + dilation_along_stack_axis * filter_r + 1, size_of_t_dim + ), + dilation_along_stack_axis, + ) # check if Padding = 'VALID' if len(indices_t_s) == len_ts: @@ -1400,36 +1517,46 @@ def conv4d_stacked(input, filter, tf.nn.convolution( tensors_t[index_t_i], kernel_t[j], - strides=(strides[1:stack_axis+1] - + strides[stack_axis:5]), + strides=( + strides[1 : stack_axis + 1] + + strides[stack_axis:5] + ), padding=padding, dilations=( - dilation_rate[:stack_axis-1] - + dilation_rate[stack_axis:])) + dilation_rate[: stack_axis - 1] + + dilation_rate[stack_axis:] + ), ) + ) else: tensors_t_convoluted.append( - tf.nn.conv3d(input=tensors_t[index_t_i], - filters=kernel_t[j], - strides=(strides[:stack_axis] + - strides[stack_axis+1:]), - padding=padding) + tf.nn.conv3d( + input=tensors_t[index_t_i], + filters=kernel_t[j], + strides=( + strides[:stack_axis] + + strides[stack_axis + 1 :] + ), + padding=padding, ) + ) if stack_nested: sum_tensors_t_s = tf.add_n(tensors_t_convoluted) # put together result_t.append(sum_tensors_t_s) - elif padding == 'SAME': + elif padding == "SAME": # Get indices t_s - indices_t_s = range(i - dilation_along_stack_axis*filter_l, - (i + 1) + dilation_along_stack_axis*filter_r, - dilation_along_stack_axis) + indices_t_s = range( + i - dilation_along_stack_axis * filter_l, + (i + 1) + dilation_along_stack_axis * filter_r, + dilation_along_stack_axis, + ) for kernel_j, j in enumerate(indices_t_s): # we can just leave out the invalid t coordinates - # since they will be padded with 0's and therfore + # since they will be padded with 0's and therefore # don't contribute to the sum if 0 <= j < size_of_t_dim: @@ -1442,21 +1569,29 @@ def conv4d_stacked(input, filter, tf.nn.convolution( tensors_t[j], kernel_t[kernel_j], - strides=(strides[1:stack_axis+1] + - strides[stack_axis:5]), + strides=( + strides[1 : stack_axis + 1] + + strides[stack_axis:5] + ), padding=padding, dilations=( - dilation_rate[:stack_axis-1] + - dilation_rate[stack_axis:])) + dilation_rate[: stack_axis - 1] + + dilation_rate[stack_axis:] + ), ) + ) else: tensors_t_convoluted.append( - tf.nn.conv3d(input=tensors_t[j], - filters=kernel_t[kernel_j], - strides=(strides[:stack_axis] + - strides[stack_axis+1:]), - padding=padding) - ) + tf.nn.conv3d( + input=tensors_t[j], + filters=kernel_t[kernel_j], + strides=( + strides[:stack_axis] + + strides[stack_axis + 1 :] + ), + padding=padding, + ) + ) if stack_nested: sum_tensors_t_s = tf.add_n(tensors_t_convoluted) # put together @@ -1470,18 +1605,24 @@ def conv4d_stacked(input, filter, result_patch = tf.nn.convolution( input_patch, kernel_patch, - strides=(strides[1:stack_axis] + - strides[stack_axis+1:5]), + strides=( + strides[1:stack_axis] + strides[stack_axis + 1 : 5] + ), padding=padding, - dilations=(dilation_rate[:stack_axis-1] + - dilation_rate[stack_axis:])) + dilations=( + dilation_rate[: stack_axis - 1] + + dilation_rate[stack_axis:] + ), + ) else: result_patch = tf.nn.conv3d( - input=input_patch, - filters=kernel_patch, - strides=(strides[:stack_axis] + - strides[stack_axis+1:]), - padding=padding) + input=input_patch, + filters=kernel_patch, + strides=( + strides[:stack_axis] + strides[stack_axis + 1 :] + ), + padding=padding, + ) result_t.append(result_patch) # stack together diff --git a/tfscripts/compat/v1/core.py b/tfscripts/compat/v1/core.py index 03b5a67..204c39c 100644 --- a/tfscripts/compat/v1/core.py +++ b/tfscripts/compat/v1/core.py @@ -1,8 +1,8 @@ -''' +""" Core functions of tfscripts.compat.v1: Add residuals, batch normalisation, activation, -''' +""" from __future__ import division, print_function @@ -16,9 +16,10 @@ from tfscripts.compat.v1 import FLOAT_PRECISION -def add_residual(input, residual, strides=None, use_scale_factor=True, - scale_factor=0.001): - '''Convenience function to add a residual +def add_residual( + input, residual, strides=None, use_scale_factor=True, scale_factor=0.001 +): + """Convenience function to add a residual Will add input + scale*residual where these overlap in the last dimension currently only supports input and residual tensors of same shape in @@ -43,7 +44,7 @@ def add_residual(input, residual, strides=None, use_scale_factor=True, ------- tf.Tensor The output Tensor: input + scale * residual(if use_scale_factor) - ''' + """ # ---------------------- # strides for mismatching @@ -53,61 +54,74 @@ def add_residual(input, residual, strides=None, use_scale_factor=True, # ---------------------- if strides is not None: - assert len(strides) == len(input.get_shape().as_list()), \ - 'Number of dimensions of strides and input must match' - assert strides[0] == 1, 'stride in batch dimension must be 1' + assert len(strides) == len( + input.get_shape().as_list() + ), "Number of dimensions of strides and input must match" + assert strides[0] == 1, "stride in batch dimension must be 1" if not strides == [1 for s in strides]: begin = [0 for s in strides] end = [0] + input.get_shape().as_list()[1:] - input = tf.strided_slice(input, - begin=begin, - end=end, - strides=strides, - begin_mask=1, - end_mask=1, - ) + input = tf.strided_slice( + input, + begin=begin, + end=end, + strides=strides, + begin_mask=1, + end_mask=1, + ) # ---------------------- num_outputs = residual.get_shape().as_list()[-1] num_inputs = input.get_shape().as_list()[-1] # Residuals added over multiple layers accumulate. - # A scale factor < 1 reduces instabilities in beginnning + # A scale factor < 1 reduces instabilities in beginning if use_scale_factor: scale = new_weights([num_outputs], stddev=scale_factor) - residual = residual*scale + residual = residual * scale if num_inputs == num_outputs: output = residual + input elif num_inputs > num_outputs: output = residual + input[..., :num_outputs] elif num_inputs < num_outputs: - output = tf.concat([residual[..., :num_inputs] + input, - residual[..., num_inputs:]], axis=-1) + output = tf.concat( + [ + residual[..., :num_inputs] + input, + residual[..., num_inputs:], + ], + axis=-1, + ) else: if num_inputs == num_outputs: - output = (residual + input)/np.sqrt(2.) + output = (residual + input) / np.sqrt(2.0) elif num_inputs > num_outputs: - output = (residual + input[..., :num_outputs])/np.sqrt(2.) + output = (residual + input[..., :num_outputs]) / np.sqrt(2.0) elif num_inputs < num_outputs: output = tf.concat( - [(residual[..., :num_inputs] + input)/np.sqrt(2.), - residual[..., num_inputs:]], - axis=-1) + [ + (residual[..., :num_inputs] + input) / np.sqrt(2.0), + residual[..., num_inputs:], + ], + axis=-1, + ) return output -def activation(layer, activation_type, - use_batch_normalisation=False, - is_training=None, - verbose=True): - ''' +def activation( + layer, + activation_type, + use_batch_normalisation=False, + is_training=None, + verbose=True, +): + """ Helper-functions to perform activation on a layer for parametric activation functions this assumes that the first dimension is batch size and that for each of the other dimensions - seperate parametrizations should be learned + separate parametrizations should be learned Parameters ---------- @@ -133,18 +147,20 @@ def activation(layer, activation_type, ------ ValueError If wrong settings passed. - ''' + """ # Use batch normalisation? if use_batch_normalisation: if verbose: - print('Using Batch Normalisation') + print("Using Batch Normalisation") if is_training is None: - raise ValueError('To use batch normalisation a boolean is_training' - ' needs to be passed') + raise ValueError( + "To use batch normalisation a boolean is_training" + " needs to be passed" + ) layer = batch_norm_wrapper(layer, is_training) - if activation_type == '': + if activation_type == "": return layer if hasattr(tf.nn, activation_type): @@ -153,79 +169,97 @@ def activation(layer, activation_type, elif hasattr(tf, activation_type): layer = getattr(tf, activation_type)(layer) - elif activation_type == 'leaky': - layer = tf.multiply(tf.maximum(-0.01*layer, layer), tf.sign(layer)) + elif activation_type == "leaky": + layer = tf.multiply(tf.maximum(-0.01 * layer, layer), tf.sign(layer)) # todo: NecroRelu # https://stats.stackexchange.com/questions/176794/ # how-does-rectilinear-activation-function-solve-the- # vanishing-gradient-problem-in # https://github.com/ibmua/learning-to-make-nn-in-python/ # blob/master/nn_classifier.py - elif activation_type == 'requ': - layer = tf.where(tf.less(layer, tf.constant(0, dtype=FLOAT_PRECISION)), - tf.zeros_like(layer, dtype=FLOAT_PRECISION), - tf.square(layer)) - - elif activation_type == 'selu': + elif activation_type == "requ": + layer = tf.where( + tf.less(layer, tf.constant(0, dtype=FLOAT_PRECISION)), + tf.zeros_like(layer, dtype=FLOAT_PRECISION), + tf.square(layer), + ) + + elif activation_type == "selu": lam = 1.0507 alpha = 1.6733 # from https://arxiv.org/abs/1706.02515 # self normalizing networks - layer = tf.where(tf.less(layer, tf.constant(0, dtype=FLOAT_PRECISION)), - tf.exp(layer) * tf.constant(alpha, - dtype=FLOAT_PRECISION) - - tf.constant(alpha,dtype=FLOAT_PRECISION), - layer) + layer = tf.where( + tf.less(layer, tf.constant(0, dtype=FLOAT_PRECISION)), + tf.exp(layer) * tf.constant(alpha, dtype=FLOAT_PRECISION) + - tf.constant(alpha, dtype=FLOAT_PRECISION), + layer, + ) layer = layer * tf.constant(lam, dtype=FLOAT_PRECISION) - elif activation_type == 'centeredRelu': + elif activation_type == "centeredRelu": layer = tf.nn.relu6(layer) - tf.constant(3, dtype=FLOAT_PRECISION) - elif activation_type == 'negrelu': + elif activation_type == "negrelu": layer = -tf.nn.relu(layer) - elif activation_type == 'invrelu': - layer = tf.where(tf.less(layer, tf.constant(0, - dtype=FLOAT_PRECISION)), layer, (layer+1e-8)**-1) - - elif activation_type == 'sign': - layer = tf.where(tf.less(layer, tf.constant(0, dtype=FLOAT_PRECISION)), - layer, tf.sign(layer)) - - elif activation_type == 'prelu': + elif activation_type == "invrelu": + layer = tf.where( + tf.less(layer, tf.constant(0, dtype=FLOAT_PRECISION)), + layer, + (layer + 1e-8) ** -1, + ) + + elif activation_type == "sign": + layer = tf.where( + tf.less(layer, tf.constant(0, dtype=FLOAT_PRECISION)), + layer, + tf.sign(layer), + ) + + elif activation_type == "prelu": slope = new_weights(layer.get_shape().as_list()[1:]) + 1.0 - layer = tf.where(tf.less(layer, tf.constant(0, dtype=FLOAT_PRECISION)), - layer*slope, layer) + layer = tf.where( + tf.less(layer, tf.constant(0, dtype=FLOAT_PRECISION)), + layer * slope, + layer, + ) - elif activation_type == 'pelu': + elif activation_type == "pelu": a = new_weights(layer.get_shape().as_list()[1:]) + 1.0 b = new_weights(layer.get_shape().as_list()[1:]) + 1.0 - layer = tf.where(tf.less(layer, - tf.constant(0, dtype=FLOAT_PRECISION)), - (tf.exp(layer/b) - 1)*a, layer*(a/b)) + layer = tf.where( + tf.less(layer, tf.constant(0, dtype=FLOAT_PRECISION)), + (tf.exp(layer / b) - 1) * a, + layer * (a / b), + ) - elif activation_type == 'gaussian': + elif activation_type == "gaussian": layer = tf.exp(-tf.square(layer)) - elif activation_type == 'pgaussian': - sigma = new_weights(layer.get_shape().as_list()[1:]) + \ - tf.constant(1.0, dtype=FLOAT_PRECISION) + elif activation_type == "pgaussian": + sigma = new_weights(layer.get_shape().as_list()[1:]) + tf.constant( + 1.0, dtype=FLOAT_PRECISION + ) mu = new_weights(layer.get_shape().as_list()[1:]) - layer = tf.exp(tf.square((layer - mu) / sigma) * - tf.constant(-0.5, dtype=FLOAT_PRECISION)) / (sigma) + layer = tf.exp( + tf.square((layer - mu) / sigma) + * tf.constant(-0.5, dtype=FLOAT_PRECISION) + ) / (sigma) elif callable(activation_type): layer = activation_type(layer) else: - raise ValueError('activation: Unknown activation type: {!r}'.format( - activation_type)) + raise ValueError( + "activation: Unknown activation type: {!r}".format(activation_type) + ) return layer def batch_norm_wrapper(inputs, is_training, decay=0.99, epsilon=1e-6): - ''' Batch normalisation + """Batch normalisation Adopted from: http://r2rt.com/implementing-batch-normalization-in-tensorflow.html @@ -239,7 +273,7 @@ def batch_norm_wrapper(inputs, is_training, decay=0.99, epsilon=1e-6): Tensor will be normalised in all but is_training : tf.placeholder of type bool. - Indicates wheter the network is being trained + Indicates whether the network is being trained or whether it is being used in inference mode. If set to true, the population mean and variance will be updated and learned. @@ -253,33 +287,52 @@ def batch_norm_wrapper(inputs, is_training, decay=0.99, epsilon=1e-6): ------- A Tensor. Has the same type as inputs. The batch normalized input - ''' + """ norm_shape = inputs.get_shape().as_list()[1:] - scale = tf.Variable(tf.ones(norm_shape, dtype=FLOAT_PRECISION), - name='BN_scale', dtype=FLOAT_PRECISION) - beta = tf.Variable(tf.zeros(norm_shape,dtype=FLOAT_PRECISION), - name='BN_beta', dtype=FLOAT_PRECISION) - pop_mean = tf.Variable(tf.zeros(norm_shape, dtype=FLOAT_PRECISION), - trainable=False, - name='BN_pop_mean', - dtype=FLOAT_PRECISION) - pop_var = tf.Variable(tf.ones(norm_shape, dtype=FLOAT_PRECISION), - trainable=False, - name='BN_pop_var', - dtype=FLOAT_PRECISION) + scale = tf.Variable( + tf.ones(norm_shape, dtype=FLOAT_PRECISION), + name="BN_scale", + dtype=FLOAT_PRECISION, + ) + beta = tf.Variable( + tf.zeros(norm_shape, dtype=FLOAT_PRECISION), + name="BN_beta", + dtype=FLOAT_PRECISION, + ) + pop_mean = tf.Variable( + tf.zeros(norm_shape, dtype=FLOAT_PRECISION), + trainable=False, + name="BN_pop_mean", + dtype=FLOAT_PRECISION, + ) + pop_var = tf.Variable( + tf.ones(norm_shape, dtype=FLOAT_PRECISION), + trainable=False, + name="BN_pop_var", + dtype=FLOAT_PRECISION, + ) if is_training: - batch_mean, batch_var = tf.nn.moments(x=inputs, axes=[0], keepdims=False) - train_mean = tf.compat.v1.assign(pop_mean, - pop_mean * decay + batch_mean * (1 - decay)) - train_var = tf.compat.v1.assign(pop_var, - pop_var * decay + batch_var * (1 - decay)) + batch_mean, batch_var = tf.nn.moments( + x=inputs, axes=[0], keepdims=False + ) + train_mean = tf.compat.v1.assign( + pop_mean, pop_mean * decay + batch_mean * (1 - decay) + ) + train_var = tf.compat.v1.assign( + pop_var, pop_var * decay + batch_var * (1 - decay) + ) with tf.control_dependencies([train_mean, train_var]): return tf.nn.batch_normalization( inputs, - batch_mean, batch_var, beta, scale, epsilon, # R2RT's blog + batch_mean, + batch_var, + beta, + scale, + epsilon, # R2RT's blog # pop_mean, pop_var, beta, scale, epsilon, - ) + ) else: - return tf.nn.batch_normalization(inputs, pop_mean, pop_var, beta, - scale, epsilon) + return tf.nn.batch_normalization( + inputs, pop_mean, pop_var, beta, scale, epsilon + ) diff --git a/tfscripts/compat/v1/hex/conv.py b/tfscripts/compat/v1/hex/conv.py index 8c010f4..7a21e23 100644 --- a/tfscripts/compat/v1/hex/conv.py +++ b/tfscripts/compat/v1/hex/conv.py @@ -1,11 +1,11 @@ -''' +""" tfscripts.compat.v1 hexagonal convolution utility functions Hex utility functions hex conv 3d and 4d ToDo: - Remove duplicate code -''' +""" from __future__ import division, print_function @@ -14,7 +14,10 @@ # tfscripts.compat.v1 specific imports from tfscripts.compat.v1.weights import ( - new_weights, new_biases, new_kernel_weights) + new_weights, + new_biases, + new_kernel_weights, +) from tfscripts.compat.v1.hex.visual import print_hex_data from tfscripts.compat.v1.hex import rotation from tfscripts.compat.v1.hex.icecube import get_icecube_kernel @@ -44,10 +47,10 @@ def get_num_hex_points(edge_length): Description """ if edge_length < 0: - raise ValueError('get_num_hex_points: expected edge_length >= 0') + raise ValueError("get_num_hex_points: expected edge_length >= 0") if edge_length == 0: return 1 - return (edge_length-1)*6 + get_num_hex_points(edge_length-1) + return (edge_length - 1) * 6 + get_num_hex_points(edge_length - 1) def hex_distance(h1, h2): @@ -68,20 +71,20 @@ def hex_distance(h1, h2): """ a1, b1 = h1 a2, b2 = h2 - c1 = -a1-b1 - c2 = -a2-b2 + c1 = -a1 - b1 + c2 = -a2 - b2 return (abs(a1 - a2) + abs(b1 - b2) + abs(c1 - c2)) / 2 def get_hex_kernel(filter_size, print_kernel=False, get_ones=False): - '''Get hexagonal convolution kernel + """Get hexagonal convolution kernel Create Weights for a hexagonal kernel. The Kernel will be of a hexagonal shape in the first two dimensions, while the other dimensions are normal. The hexagonal kernel is off the shape: [kernel_edge_points, kernel_edge_points, *filter_size[2:]] - But elments with coordinates in the first two dimensions, that don't belong + But elements with coordinates in the first two dimensions, that don't belong to the hexagon are set to a tf.Constant 0. The hexagon is defined by filter_size[0:2]. @@ -145,17 +148,19 @@ def get_hex_kernel(filter_size, print_kernel=False, get_ones=False): ------ ValueError Description - ''' + """ k = filter_size[0] x = filter_size[1] if x >= k: - raise ValueError("get_hex_kernel: filter_size (k,x,z) must fulfill " - "x < k: ({}, {}, {})".format(k, x, filter_size[2])) + raise ValueError( + "get_hex_kernel: filter_size (k,x,z) must fulfill " + "x < k: ({}, {}, {})".format(k, x, filter_size[2]) + ) if x == 0: - kernel_edge_points = 2*k - 1 + kernel_edge_points = 2 * k - 1 else: - kernel_edge_points = 2*k + 1 + kernel_edge_points = 2 * k + 1 zeros = tf.zeros(filter_size[2:], dtype=FLOAT_PRECISION) ones = tf.ones(filter_size[2:], dtype=FLOAT_PRECISION) @@ -170,7 +175,7 @@ def get_hex_kernel(filter_size, print_kernel=False, get_ones=False): # regular aligned hexagons # ------------------------- if x == 0: - if a+b < k - 1 or a + b > 3*k - 3: + if a + b < k - 1 or a + b > 3 * k - 3: weights = zeros test_hex_dict[(a, b)] = 0 else: @@ -187,25 +192,29 @@ def get_hex_kernel(filter_size, print_kernel=False, get_ones=False): inHexagon = False # check if inside normal k.0 aligned hexagon # |----inside normal k.0 rhombus -----------| - if ((a > 0 and a < 2*k) and (b > 0 and b < 2*k) and + if ( + (a > 0 and a < 2 * k) + and (b > 0 and b < 2 * k) + and # |--in k.0 aligned hexagon-| - (a+b > k and a + b < 3*k)): + (a + b > k and a + b < 3 * k) + ): - if a+b > k and a + b < 3*k: + if a + b > k and a + b < 3 * k: inHexagon = True else: # add 6 additional edges outside of k.0 aligned hexagon - if a == 2*k-x and b == 0: # Edge 1 + if a == 2 * k - x and b == 0: # Edge 1 inHexagon = True elif a == k - x and b == x: # Edge 2 inHexagon = True - elif a == 0 and b == k+x: # Edge 3 + elif a == 0 and b == k + x: # Edge 3 inHexagon = True - elif a == x and b == 2*k: # Edge 4 + elif a == x and b == 2 * k: # Edge 4 inHexagon = True - elif a == k+x and b == 2*k-x: # Edge 5 + elif a == k + x and b == 2 * k - x: # Edge 5 inHexagon = True - elif a == 2*k and b == k-x: # Edge 6 + elif a == 2 * k and b == k - x: # Edge 6 inHexagon = True # get weights or constant 0 depending on if point is in hexagon if inHexagon: @@ -226,17 +235,18 @@ def get_hex_kernel(filter_size, print_kernel=False, get_ones=False): return hexKernel -def conv_hex(input_data, - filter_size, - num_filters, - padding='SAME', - strides=[1, 1, 1, 1, 1], - num_rotations=1, - dilation_rate=None, - zero_out=False, - kernel=None, - azimuth=None, - ): +def conv_hex( + input_data, + filter_size, + num_filters, + padding="SAME", + strides=[1, 1, 1, 1, 1], + num_rotations=1, + dilation_rate=None, + zero_out=False, + kernel=None, + azimuth=None, +): """Convolve a hex2d or hex3d layer (2d hex + 1d cartesian) Parameters @@ -323,58 +333,69 @@ def conv_hex(input_data, if kernel is None: if azimuth is not None and filter_size[:2] != [1, 0]: kernel = rotation.get_dynamic_rotation_hex_kernel( - filter_size+[num_channels, num_filters], azimuth) + filter_size + [num_channels, num_filters], azimuth + ) else: if num_rotations > 1: kernel = rotation.get_rotated_hex_kernel( - filter_size+[num_channels, num_filters], num_rotations) + filter_size + [num_channels, num_filters], num_rotations + ) else: kernel = get_hex_kernel( - filter_size+[num_channels, num_filters]) + filter_size + [num_channels, num_filters] + ) if azimuth is not None and filter_size[:2] != [1, 0]: result = dynamic_conv( - input=input_data, - filter=kernel, - strides=strides[1:-1], - padding=padding, - dilation_rate=dilation_rate, - ) + input=input_data, + filter=kernel, + strides=strides[1:-1], + padding=padding, + dilation_rate=dilation_rate, + ) else: - result = tf.nn.convolution(input_data, - kernel, - strides=strides[1:-1], - padding=padding, - dilations=dilation_rate) + result = tf.nn.convolution( + input_data, + kernel, + strides=strides[1:-1], + padding=padding, + dilations=dilation_rate, + ) # zero out elements that don't belong on hexagon or IceCube Strings if zero_out: if result.get_shape().as_list()[1:3] == [10, 10]: # IceCube shape - print('Assuming IceCube shape for layer', result) + print("Assuming IceCube shape for layer", result) zero_out_matrix = get_icecube_kernel( - result.get_shape().as_list()[3:], - get_ones=True) - result = result*zero_out_matrix + result.get_shape().as_list()[3:], get_ones=True + ) + result = result * zero_out_matrix else: # Generic hexagonal shape zero_out_matrix = get_hex_kernel( - [(result.get_shape().as_list()[1]+1) // 2, 0, - result.get_shape().as_list()[3], - num_filters*num_rotations], - get_ones=True) + [ + (result.get_shape().as_list()[1] + 1) // 2, + 0, + result.get_shape().as_list()[3], + num_filters * num_rotations, + ], + get_ones=True, + ) if result.get_shape()[1:] == zero_out_matrix.get_shape(): - result = result*zero_out_matrix + result = result * zero_out_matrix else: print(result, zero_out_matrix) - raise ValueError("conv_hex3d: Shapes do not match for " - "zero_out_matrix and result. " - " {!r} != {!r}".format( - result.get_shape()[1:], - zero_out_matrix.get_shape())) + raise ValueError( + "conv_hex3d: Shapes do not match for " + "zero_out_matrix and result. " + " {!r} != {!r}".format( + result.get_shape()[1:], zero_out_matrix.get_shape() + ) + ) return result, kernel @@ -384,17 +405,19 @@ def conv_hex(input_data, conv_hex3d = conv_hex -def conv_hex4d(input_data, - filter_size, - num_filters, - padding='VALID', - strides=[1, 1, 1, 1, 1, 1], - num_rotations=1, - dilation_rate=None, - kernel=None, - azimuth=None, - stack_axis=None, - zero_out=False): +def conv_hex4d( + input_data, + filter_size, + num_filters, + padding="VALID", + strides=[1, 1, 1, 1, 1, 1], + num_rotations=1, + dilation_rate=None, + kernel=None, + azimuth=None, + stack_axis=None, + zero_out=False, +): """Convolve a hex4d layer (2d hex + 1d cartesian + 1d time) Parameters @@ -481,51 +504,63 @@ def conv_hex4d(input_data, num_in_channels = input_data.get_shape().as_list()[5] if azimuth is not None: kernel = rotation.get_dynamic_rotation_hex_kernel( - filter_size+[num_channels, num_filters], azimuth) + filter_size + [num_in_channels, num_filters], azimuth + ) else: if num_rotations > 1: kernel = rotation.get_rotated_hex_kernel( - filter_size+[num_in_channels, num_filters], num_rotations) + filter_size + [num_in_channels, num_filters], num_rotations + ) else: kernel = get_hex_kernel( - filter_size+[num_in_channels, num_filters]) + filter_size + [num_in_channels, num_filters] + ) # convolve with tf conv4d_stacked - result = conv4d_stacked(input=input_data, - filter=kernel, - strides=strides, - padding=padding, - dilation_rate=dilation_rate, - stack_axis=stack_axis) + result = conv4d_stacked( + input=input_data, + filter=kernel, + strides=strides, + padding=padding, + dilation_rate=dilation_rate, + stack_axis=stack_axis, + ) # zero out elements that don't belong on hexagon if zero_out: zero_out_matrix = get_hex_kernel( - [int((result.get_shape().as_list()[1]+1)/2), 0, - result.get_shape().as_list()[3], - result.get_shape().as_list()[4], - num_filters*num_rotations], - get_ones=True) + [ + int((result.get_shape().as_list()[1] + 1) / 2), + 0, + result.get_shape().as_list()[3], + result.get_shape().as_list()[4], + num_filters * num_rotations, + ], + get_ones=True, + ) if result.get_shape()[1:] == zero_out_matrix.get_shape(): - result = result*zero_out_matrix + result = result * zero_out_matrix else: print(result, zero_out_matrix) - raise ValueError("conv_hex4d: Shapes do not match for " - "zero_out_matrix and result. {!r} != {!r}".format( - result.get_shape()[1:], - zero_out_matrix.get_shape())) + raise ValueError( + "conv_hex4d: Shapes do not match for " + "zero_out_matrix and result. {!r} != {!r}".format( + result.get_shape()[1:], zero_out_matrix.get_shape() + ) + ) return result, kernel -def create_conv_hex_layers_weights(num_input_channels, - filter_size_list, - num_filters_list, - num_rotations_list=1, - azimuth_list=None, - ): - '''Create weights and biases for conv hex n-dimensional layers with n >= 2 +def create_conv_hex_layers_weights( + num_input_channels, + filter_size_list, + num_filters_list, + num_rotations_list=1, + azimuth_list=None, +): + """Create weights and biases for conv hex n-dimensional layers with n >= 2 Parameters ---------- @@ -584,39 +619,42 @@ def create_conv_hex_layers_weights(num_input_channels, ------- list of tf.Tensor, list of tf.Tensor Returns the list of weight and bias tensors for each layer - ''' + """ # create num_rotations_list if isinstance(num_rotations_list, int): - num_rotations_list = [num_rotations_list for i - in range(len(num_filters_list))] + num_rotations_list = [ + num_rotations_list for i in range(len(num_filters_list)) + ] # create azimuth_list if azimuth_list is None or tf.is_tensor(azimuth_list): - azimuth_list = [azimuth_list for i in range(noOfLayers)] + azimuth_list = [azimuth_list for i in range(len(num_filters_list))] weights_list = [] biases_list = [] for filter_size, num_filters, num_rotations, azimuth in zip( - filter_size_list, - num_filters_list, - num_rotations_list, - azimuth_list, - ): + filter_size_list, + num_filters_list, + num_rotations_list, + azimuth_list, + ): if azimuth is not None: - kernel = rotation.get_dynamic_rotation_hex_kernel(filter_size, - azimuth) + kernel = rotation.get_dynamic_rotation_hex_kernel( + filter_size, azimuth + ) else: if num_rotations > 1: kernel = rotation.get_rotated_hex_kernel( - filter_size + - [num_input_channels, num_filters], - num_rotations) + filter_size + [num_input_channels, num_filters], + num_rotations, + ) else: - kernel = get_hex_kernel(filter_size+[num_input_channels, - num_filters]) + kernel = get_hex_kernel( + filter_size + [num_input_channels, num_filters] + ) weights_list.append(kernel) - biases_list.append(new_biases(length=num_filters*num_rotations)) + biases_list.append(new_biases(length=num_filters * num_rotations)) num_input_channels = num_filters return weights_list, biases_list diff --git a/tfscripts/compat/v1/hex/icecube.py b/tfscripts/compat/v1/hex/icecube.py index 46334c4..67c32ea 100644 --- a/tfscripts/compat/v1/hex/icecube.py +++ b/tfscripts/compat/v1/hex/icecube.py @@ -1,6 +1,6 @@ -''' +""" IceCube specific constants and functions -''' +""" from __future__ import division, print_function @@ -17,86 +17,187 @@ # IceCube Constants # ----------------------------------------------------------------------------- string_hex_coord_dict = { - # row 1 - 1: (-4, -1), 2: (-4, 0), 3: (-4, 1), 4: (-4, 2), 5: (-4, 3), 6: (-4, 4), - + 1: (-4, -1), + 2: (-4, 0), + 3: (-4, 1), + 4: (-4, 2), + 5: (-4, 3), + 6: (-4, 4), # row 2 - 7: (-3, -2), 8: (-3, -1), 9: (-3, 0), 10: (-3, 1), 11: (-3, 2), - 12: (-3, 3), 13: (-3, 4), - + 7: (-3, -2), + 8: (-3, -1), + 9: (-3, 0), + 10: (-3, 1), + 11: (-3, 2), + 12: (-3, 3), + 13: (-3, 4), # row 3 - 14: (-2, -3), 15: (-2, -2), 16: (-2, -1), 17: (-2, 0), 18: (-2, 1), - 19: (-2, 2), 20: (-2, 3), 21: (-2, 4), - + 14: (-2, -3), + 15: (-2, -2), + 16: (-2, -1), + 17: (-2, 0), + 18: (-2, 1), + 19: (-2, 2), + 20: (-2, 3), + 21: (-2, 4), # row 4 - 22: (-1, -4), 23: (-1, -3), 24: (-1, -2), 25: (-1, -1), 26: (-1, 0), - 27: (-1, 1), 28: (-1, 2), 29: (-1, 3), 30: (-1, 4), - + 22: (-1, -4), + 23: (-1, -3), + 24: (-1, -2), + 25: (-1, -1), + 26: (-1, 0), + 27: (-1, 1), + 28: (-1, 2), + 29: (-1, 3), + 30: (-1, 4), # row 5 - 31: (0, -5), 32: (0, -4), 33: (0, -3), 34: (0, -2), 35: (0, -1), - 36: (0, 0), 37: (0, 1), 38: (0, 2), 39: (0, 3), 40: (0, 4), - + 31: (0, -5), + 32: (0, -4), + 33: (0, -3), + 34: (0, -2), + 35: (0, -1), + 36: (0, 0), + 37: (0, 1), + 38: (0, 2), + 39: (0, 3), + 40: (0, 4), # row 6 - 41: (1, -5), 42: (1, -4), 43: (1, -3), 44: (1, -2), 45: (1, -1), - 46: (1, 0), 47: (1, 1), 48: (1, 2), 49: (1, 3), 50: (1, 4), - + 41: (1, -5), + 42: (1, -4), + 43: (1, -3), + 44: (1, -2), + 45: (1, -1), + 46: (1, 0), + 47: (1, 1), + 48: (1, 2), + 49: (1, 3), + 50: (1, 4), # row 7 - 51: (2, -5), 52: (2, -4), 53: (2, -3), 54: (2, -2), 55: (2, -1), - 56: (2, 0), 57: (2, 1), 58: (2, 2), 59: (2, 3), - + 51: (2, -5), + 52: (2, -4), + 53: (2, -3), + 54: (2, -2), + 55: (2, -1), + 56: (2, 0), + 57: (2, 1), + 58: (2, 2), + 59: (2, 3), # row 8 - 60: (3, -5), 61: (3, -4), 62: (3, -3), 63: (3, -2), 64: (3, -1), - 65: (3, 0), 66: (3, 1), 67: (3, 2), - + 60: (3, -5), + 61: (3, -4), + 62: (3, -3), + 63: (3, -2), + 64: (3, -1), + 65: (3, 0), + 66: (3, 1), + 67: (3, 2), # row 9 - 68: (4, -5), 69: (4, -4), 70: (4, -3), 71: (4, -2), 72: (4, -1), - 73: (4, 0), 74: (4, 1), - + 68: (4, -5), + 69: (4, -4), + 70: (4, -3), + 71: (4, -2), + 72: (4, -1), + 73: (4, 0), + 74: (4, 1), # row 10 - 5: (5, -5), 76: (5, -4), 77: (5, -3), 78: (5, -2), + 75: (5, -5), + 76: (5, -4), + 77: (5, -3), + 78: (5, -2), } # first index goes 'up' in hex coord: string 36 to 46 # second index goes 'right' in hex coord: string 36 to 37 hex_string_coord_dict = { # row 1 - (-4, -1): 1, (-4, 0): 2, (-4, 1): 3, (-4, 2): 4, (-4, 3): 5, (-4, 4): 6, - + (-4, -1): 1, + (-4, 0): 2, + (-4, 1): 3, + (-4, 2): 4, + (-4, 3): 5, + (-4, 4): 6, # row 2 - (-3, -2): 7, (-3, -1): 8, (-3, 0): 9, (-3, 1): 10, (-3, 2): 11, - (-3, 3): 12, (-3, 4): 13, - + (-3, -2): 7, + (-3, -1): 8, + (-3, 0): 9, + (-3, 1): 10, + (-3, 2): 11, + (-3, 3): 12, + (-3, 4): 13, # row 3 - (-2, -3): 14, (-2, -2): 15, (-2, -1): 16, (-2, 0): 17, (-2, 1): 18, - (-2, 2): 19, (-2, 3): 20, (-2, 4): 21, - + (-2, -3): 14, + (-2, -2): 15, + (-2, -1): 16, + (-2, 0): 17, + (-2, 1): 18, + (-2, 2): 19, + (-2, 3): 20, + (-2, 4): 21, # row 4 - (-1, -4): 22, (-1, -3): 23, (-1, -2): 24, (-1, -1): 25, (-1, 0): 26, - (-1, 1): 27, (-1, 2): 28, (-1, 3): 29, (-1, 4): 30, - + (-1, -4): 22, + (-1, -3): 23, + (-1, -2): 24, + (-1, -1): 25, + (-1, 0): 26, + (-1, 1): 27, + (-1, 2): 28, + (-1, 3): 29, + (-1, 4): 30, # row 5 - (0, -5): 31, (0, -4): 32, (0, -3): 33, (0, -2): 34, (0, -1): 35, - (0, 0): 36, (0, 1): 37, (0, 2): 38, (0, 3): 39, (0, 4): 40, - + (0, -5): 31, + (0, -4): 32, + (0, -3): 33, + (0, -2): 34, + (0, -1): 35, + (0, 0): 36, + (0, 1): 37, + (0, 2): 38, + (0, 3): 39, + (0, 4): 40, # row 6 - (1, -5): 41, (1, -4): 42, (1, -3): 43, (1, -2): 44, (1, -1): 45, - (1, 0): 46, (1, 1): 47, (1, 2): 48, (1, 3): 49, (1, 4): 50, - + (1, -5): 41, + (1, -4): 42, + (1, -3): 43, + (1, -2): 44, + (1, -1): 45, + (1, 0): 46, + (1, 1): 47, + (1, 2): 48, + (1, 3): 49, + (1, 4): 50, # row 7 - (2, -5): 51, (2, -4): 52, (2, -3): 53, (2, -2): 54, (2, -1): 55, - (2, 0): 56, (2, 1): 57, (2, 2): 58, (2, 3): 59, - + (2, -5): 51, + (2, -4): 52, + (2, -3): 53, + (2, -2): 54, + (2, -1): 55, + (2, 0): 56, + (2, 1): 57, + (2, 2): 58, + (2, 3): 59, # row 8 - (3, -5): 60, (3, -4): 61, (3, -3): 62, (3, -2): 63, (3, -1): 64, - (3, 0): 65, (3, 1): 66, (3, 2): 67, - + (3, -5): 60, + (3, -4): 61, + (3, -3): 62, + (3, -2): 63, + (3, -1): 64, + (3, 0): 65, + (3, 1): 66, + (3, 2): 67, # row 9 - (4, -5): 68, (4, -4): 69, (4, -3): 70, (4, -2): 71, (4, -1): 72, - (4, 0): 73, (4, 1): 74, - + (4, -5): 68, + (4, -4): 69, + (4, -3): 70, + (4, -2): 71, + (4, -1): 72, + (4, 0): 73, + (4, 1): 74, # row 10 - (5, -5): 75, (5, -4): 76, (5, -3): 77, (5, -2): 78, + (5, -5): 75, + (5, -4): 76, + (5, -3): 77, + (5, -2): 78, } # ----------------------------------------------------------------------------- @@ -139,7 +240,7 @@ def get_icecube_string_from_hex_coord(a, b): def get_icecube_kernel(shape, get_ones=False): - ''' + """ Get a kernel of shape 'shape' for IceCube where coordinates of no real strings are set to constant zeros. @@ -157,15 +258,15 @@ def get_icecube_kernel(shape, get_ones=False): ------- tf.Tensor The icecube kernel with the desired shape. - ''' + """ zeros = tf.zeros(shape, dtype=FLOAT_PRECISION) ones = tf.ones(shape, dtype=FLOAT_PRECISION) a_list = [] - for a in xrange(-4, 6): + for a in range(-4, 6): b_list = [] - for b in xrange(-5, 5): + for b in range(-5, 5): if (a, b) in hex_string_coord_dict.keys(): # String exists diff --git a/tfscripts/compat/v1/hex/rotation.py b/tfscripts/compat/v1/hex/rotation.py index 9c7474d..c787b11 100644 --- a/tfscripts/compat/v1/hex/rotation.py +++ b/tfscripts/compat/v1/hex/rotation.py @@ -1,11 +1,11 @@ -''' +""" tfscripts.compat.v1 hexagonal rotation utility functions Hex rotation utility functions Rotated kernels: static and dynamic ToDo: - Remove duplicate code -''' +""" from __future__ import division, print_function @@ -20,7 +20,7 @@ def get_rotated_corner_weights(corner_weights, azimuth): - '''Rotates the points on a given hexagon layer/circle + """Rotates the points on a given hexagon layer/circle Parameters ---------- @@ -35,7 +35,7 @@ def get_rotated_corner_weights(corner_weights, azimuth): list of np.ndarray or list of tf.Tensor [same shape and type as input] A list of the rotated weights along the given hexagon layer/circle. - ''' + """ size = len(corner_weights) degree_steps = 360.0 / size @@ -44,14 +44,15 @@ def get_rotated_corner_weights(corner_weights, azimuth): rotatedcorner_weights = [] for i in range(size): - newCorner_i = corner_weights[i-b] + a/degree_steps * ( - corner_weights[i-b-1] - corner_weights[i-b]) + newCorner_i = corner_weights[i - b] + a / degree_steps * ( + corner_weights[i - b - 1] - corner_weights[i - b] + ) rotatedcorner_weights.append(newCorner_i) return rotatedcorner_weights def tf_get_rotated_corner_weights(corner_weights, azimuth): - '''Rotates the points on a given hexagon layer/circle + """Rotates the points on a given hexagon layer/circle Parameters ---------- @@ -66,13 +67,14 @@ def tf_get_rotated_corner_weights(corner_weights, azimuth): list of tf.Tensor [same shape and type as input] A list of the rotated weights along the given hexagon layer/circle. - ''' + """ size = corner_weights.get_shape().as_list()[0] num_dims = len(corner_weights.get_shape().as_list()[1:]) degree_steps = 360.0 / size - a = tf.reshape(azimuth % degree_steps, - [tf.shape(input=azimuth)[0]] + [1]*num_dims) + a = tf.reshape( + azimuth % degree_steps, [tf.shape(input=azimuth)[0]] + [1] * num_dims + ) b = tf.cast(azimuth / degree_steps, tf.int32) rotatedcorner_weights = [] for i in range(size): @@ -84,16 +86,17 @@ def tf_get_rotated_corner_weights(corner_weights, azimuth): index_1 = tf.where(index_1 < 0, size + index_1, index_1) index_2 = tf.where(index_2 < 0, size + index_2, index_2) - newCorner_i = (tf.gather(corner_weights, index_1) + a / degree_steps * - (tf.gather(corner_weights, index_2) - - tf.gather(corner_weights, index_1))) + newCorner_i = tf.gather(corner_weights, index_1) + a / degree_steps * ( + tf.gather(corner_weights, index_2) + - tf.gather(corner_weights, index_1) + ) rotatedcorner_weights.append(newCorner_i) return rotatedcorner_weights def get_dynamic_rotation_hex_kernel(filter_size, azimuth): - '''Dynamically azimuthally rotated hexagonal kernels. + """Dynamically azimuthally rotated hexagonal kernels. Create Weights for a hexagonal kernel. The Kernel is dynamically rotated by the 'azimuth' angle. @@ -101,7 +104,7 @@ def get_dynamic_rotation_hex_kernel(filter_size, azimuth): while the other dimensions are normal. The hexagonal kernel is off the shape: [kernel_edge_points, kernel_edge_points, *filter_size[2:]] - But elments with coordinates in the first two dimensions, that don't belong + But elements with coordinates in the first two dimensions, that don't belong to the hexagon are set to a tf.Constant 0. The hexagon is defined by filter_size[0:2]. @@ -143,14 +146,14 @@ def get_dynamic_rotation_hex_kernel(filter_size, azimuth): ValueError Description - ''' + """ no_of_dims = len(filter_size) - rotated_filter_size = filter_size[2:-1] + [filter_size[-1]] - Z = tf.zeros([tf.shape(input=azimuth)[0]] + filter_size[2:], - dtype=FLOAT_PRECISION) + Z = tf.zeros( + [tf.shape(input=azimuth)[0]] + filter_size[2:], dtype=FLOAT_PRECISION + ) center_weight = new_weights([1] + filter_size[2:]) - multiples = [tf.shape(input=azimuth)[0]] + [1]*(no_of_dims - 2) + multiples = [tf.shape(input=azimuth)[0]] + [1] * (no_of_dims - 2) center_weight = tf.tile(center_weight, multiples) # HARDCODE MAGIC... ToDo: Generalize @@ -162,7 +165,7 @@ def get_dynamic_rotation_hex_kernel(filter_size, azimuth): corner_weights1 = new_weights([6] + filter_size[2:]) corner_weights2 = [] for i in range(6): - corner_weights2.extend([Z, new_weights(filter_size[2:])]) + corner_weights2.extend([Z, new_weights(filter_size[2:])]) corner_weights2 = tf.stack(corner_weights2) elif filter_size[0:2] == [3, 0]: # hexagonal 3,0 Filter @@ -190,54 +193,74 @@ def get_dynamic_rotation_hex_kernel(filter_size, azimuth): corner_weights2 = new_weights([12] + filter_size[2:]) corner_weights2 = new_weights([18] + filter_size[2:]) else: - raise ValueError("get_dynamic_rotation_hex_kernel: Unsupported " - "hexagonal filter_size: {!r}".formt(filter_size[0:2])) + raise ValueError( + "get_dynamic_rotation_hex_kernel: Unsupported " + "hexagonal filter_size: {!r}".format(filter_size[0:2]) + ) rotated_kernel_rows = [] if filter_size[0:2] == [2, 0]: # hexagonal 2,0 Filter A = tf_get_rotated_corner_weights(corner_weights1, azimuth) rotated_kernel_rows.append(tf.stack([Z, A[5], A[0]], axis=1)) - rotated_kernel_rows.append(tf.stack( - [A[3], center_weight, A[1]], axis=1)) + rotated_kernel_rows.append( + tf.stack([A[3], center_weight, A[1]], axis=1) + ) rotated_kernel_rows.append(tf.stack([A[3], A[2], Z], axis=1)) elif filter_size[0:2] == [2, 1] or filter_size[0:2] == [3, 0]: # hexagonal 2,1 and 3,0 Filter A = tf_get_rotated_corner_weights(corner_weights1, azimuth) B = tf_get_rotated_corner_weights(corner_weights2, azimuth) - rotated_kernel_rows.append(tf.stack( - [Z, Z, B[9], B[10], B[11]], axis=1)) - rotated_kernel_rows.append(tf.stack( - [Z, B[8], A[5], A[0], B[0]], axis=1)) - rotated_kernel_rows.append(tf.stack( - [B[7], A[4], center_weight, A[1], B[1]], axis=1)) - rotated_kernel_rows.append(tf.stack( - [B[6], A[3], A[2], B[2], Z], axis=1)) - rotated_kernel_rows.append(tf.stack( - [B[5], B[4], B[3], Z, Z], axis=1)) - elif (filter_size[0:2] == [3, 1] or filter_size[0:2] == [3, 2] or - filter_size[0:2] == [4, 0]): + rotated_kernel_rows.append( + tf.stack([Z, Z, B[9], B[10], B[11]], axis=1) + ) + rotated_kernel_rows.append( + tf.stack([Z, B[8], A[5], A[0], B[0]], axis=1) + ) + rotated_kernel_rows.append( + tf.stack([B[7], A[4], center_weight, A[1], B[1]], axis=1) + ) + rotated_kernel_rows.append( + tf.stack([B[6], A[3], A[2], B[2], Z], axis=1) + ) + rotated_kernel_rows.append(tf.stack([B[5], B[4], B[3], Z, Z], axis=1)) + elif ( + filter_size[0:2] == [3, 1] + or filter_size[0:2] == [3, 2] + or filter_size[0:2] == [4, 0] + ): # hexagonal 3,1 3,2 and 4,0 filter A = tf_get_rotated_corner_weights(corner_weights1, azimuth) B = tf_get_rotated_corner_weights(corner_weights2, azimuth) C = tf_get_rotated_corner_weights(corner_weights3, azimuth) - rotated_kernel_rows.append(tf.stack( - [Z, Z, Z, C[15], C[16], C[17], C[0]], axis=1)) - rotated_kernel_rows.append(tf.stack( - [Z, Z, C[14], B[9], B[10], B[11], C[1]], axis=1)) - rotated_kernel_rows.append(tf.stack( - [Z, C[13], B[8], A[5], A[0], B[0], C[2]], axis=1)) - rotated_kernel_rows.append(tf.stack( - [C[12], B[7], A[4], center_weight, A[1], B[1], C[3]], axis=1)) - rotated_kernel_rows.append(tf.stack( - [C[11], B[6], A[3], A[2], B[2], C[4], Z], axis=1)) - rotated_kernel_rows.append(tf.stack( - [C[10], B[5], B[4], B[3], C[5], Z, Z], axis=1)) - rotated_kernel_rows.append(tf.stack( - [C[9], C[8], C[7], C[6], Z, Z, Z], axis=1)) + rotated_kernel_rows.append( + tf.stack([Z, Z, Z, C[15], C[16], C[17], C[0]], axis=1) + ) + rotated_kernel_rows.append( + tf.stack([Z, Z, C[14], B[9], B[10], B[11], C[1]], axis=1) + ) + rotated_kernel_rows.append( + tf.stack([Z, C[13], B[8], A[5], A[0], B[0], C[2]], axis=1) + ) + rotated_kernel_rows.append( + tf.stack( + [C[12], B[7], A[4], center_weight, A[1], B[1], C[3]], axis=1 + ) + ) + rotated_kernel_rows.append( + tf.stack([C[11], B[6], A[3], A[2], B[2], C[4], Z], axis=1) + ) + rotated_kernel_rows.append( + tf.stack([C[10], B[5], B[4], B[3], C[5], Z, Z], axis=1) + ) + rotated_kernel_rows.append( + tf.stack([C[9], C[8], C[7], C[6], Z, Z, Z], axis=1) + ) else: - raise ValueError("get_dynamic_rotation_hex_kernel: Unsupported " - "hexagonal filter_size: {!r}".formt(filter_size[0:2])) + raise ValueError( + "get_dynamic_rotation_hex_kernel: Unsupported " + "hexagonal filter_size: {!r}".format(filter_size[0:2]) + ) rotated_kernel = tf.stack(rotated_kernel_rows, axis=1) @@ -248,7 +271,7 @@ def get_dynamic_rotation_hex_kernel(filter_size, azimuth): # hexagonal azimuth rotated filters # ------------------------------------------------------------------------- def get_rotated_hex_kernel(filter_size, num_rotations): - ''' + """ Create Weights for a hexagonal kernel. The kernel is rotated 'num_rotations' many times. Weights are shared over rotated versions. @@ -256,7 +279,7 @@ def get_rotated_hex_kernel(filter_size, num_rotations): while the other dimensions are normal. The hexagonal kernel is off the shape: [kernel_edge_points, kernel_edge_points, *filter_size[2:]] - But elments with coordinates in the first two dimensions, that don't belong + But elements with coordinates in the first two dimensions, that don't belong to the hexagon are set to a tf.Constant 0. The hexagon is defined by filter_size[0:2]. @@ -299,63 +322,53 @@ def get_rotated_hex_kernel(filter_size, num_rotations): ValueError Description - ''' + """ no_of_dims = len(filter_size) - rotated_filter_size = filter_size[2:-1] + [filter_size[-1]*num_rotations] - azimuths = np.linspace(0, 360, num_rotations+1)[:-1] + azimuths = np.linspace(0, 360, num_rotations + 1)[:-1] Z = tf.zeros(filter_size[2:-2], dtype=FLOAT_PRECISION) center_weight = new_weights(filter_size[2:-2]) # HARDCODE MAGIC... ToDo: Generalize if filter_size[0:2] == [2, 0]: # hexagonal 2,0 Filter - corner_weights1 = [new_weights(filter_size[2:-2]) - for i in range(6)] + corner_weights1 = [new_weights(filter_size[2:-2]) for i in range(6)] elif filter_size[0:2] == [2, 1]: # hexagonal 2,1 Filter - corner_weights1 = [new_weights(filter_size[2:-2]) - for i in range(6)] + corner_weights1 = [new_weights(filter_size[2:-2]) for i in range(6)] corner_weights2 = [] for i in range(6): - corner_weights2.extend([Z, new_weights(filter_size[2:-2])]) + corner_weights2.extend([Z, new_weights(filter_size[2:-2])]) elif filter_size[0:2] == [3, 0]: # hexagonal 3,0 Filter - corner_weights1 = [new_weights(filter_size[2:-2]) - for i in range(6)] - corner_weights2 = [new_weights(filter_size[2:-2]) - for i in range(12)] + corner_weights1 = [new_weights(filter_size[2:-2]) for i in range(6)] + corner_weights2 = [new_weights(filter_size[2:-2]) for i in range(12)] elif filter_size[0:2] == [3, 1]: # hexagonal 3,1 Filter - corner_weights1 = [new_weights(filter_size[2:-2]) - for i in range(6)] - corner_weights2 = [new_weights(filter_size[2:-2]) - for i in range(12)] + corner_weights1 = [new_weights(filter_size[2:-2]) for i in range(6)] + corner_weights2 = [new_weights(filter_size[2:-2]) for i in range(12)] corner_weights3 = [] for i in range(6): corner_weights3.extend([Z, new_weights(filter_size[2:-2]), Z]) elif filter_size[0:2] == [3, 2]: # hexagonal 3,2 Filter - corner_weights1 = [new_weights(filter_size[2:-2]) - for i in range(6)] - corner_weights2 = [new_weights(filter_size[2:-2]) - for i in range(12)] + corner_weights1 = [new_weights(filter_size[2:-2]) for i in range(6)] + corner_weights2 = [new_weights(filter_size[2:-2]) for i in range(12)] corner_weights3 = [] for i in range(6): corner_weights3.extend([Z, Z, new_weights(filter_size[2:-2])]) elif filter_size[0:2] == [4, 0]: # hexagonal 4,0 Filter - corner_weights1 = [new_weights(filter_size[2:-2]) - for i in range(6)] - corner_weights2 = [new_weights(filter_size[2:-2]) - for i in range(12)] - corner_weights3 = [new_weights(filter_size[2:-2]) - for i in range(18)] + corner_weights1 = [new_weights(filter_size[2:-2]) for i in range(6)] + corner_weights2 = [new_weights(filter_size[2:-2]) for i in range(12)] + corner_weights3 = [new_weights(filter_size[2:-2]) for i in range(18)] else: - raise ValueError("get_rotated_hex_kernel: Unsupported " - "hexagonal filter_size: {!r}".formt(filter_size[0:2])) + raise ValueError( + "get_rotated_hex_kernel: Unsupported " + "hexagonal filter_size: {!r}".format(filter_size[0:2]) + ) rotated_kernels = [] - in_out_channel_weights = new_weights([num_rotations]+filter_size[-2:]) + in_out_channel_weights = new_weights([num_rotations] + filter_size[-2:]) for i, azimuth in enumerate(azimuths): rotated_kernel_rows = [] @@ -371,33 +384,46 @@ def get_rotated_hex_kernel(filter_size, num_rotations): B = get_rotated_corner_weights(corner_weights2, azimuth) rotated_kernel_rows.append(tf.stack([Z, Z, B[9], B[10], B[11]])) rotated_kernel_rows.append(tf.stack([Z, B[8], A[5], A[0], B[0]])) - rotated_kernel_rows.append(tf.stack( - [B[7], A[4], center_weight, A[1], B[1]])) + rotated_kernel_rows.append( + tf.stack([B[7], A[4], center_weight, A[1], B[1]]) + ) rotated_kernel_rows.append(tf.stack([B[6], A[3], A[2], B[2], Z])) rotated_kernel_rows.append(tf.stack([B[5], B[4], B[3], Z, Z])) - elif (filter_size[0:2] == [3, 1] or filter_size[0:2] == [3, 2] or - filter_size[0:2] == [4, 0]): + elif ( + filter_size[0:2] == [3, 1] + or filter_size[0:2] == [3, 2] + or filter_size[0:2] == [4, 0] + ): # hexagonal 3,1 3,2 and 4,0 filter A = get_rotated_corner_weights(corner_weights1, azimuth) B = get_rotated_corner_weights(corner_weights2, azimuth) C = get_rotated_corner_weights(corner_weights3, azimuth) - rotated_kernel_rows.append(tf.stack( - [Z, Z, Z, C[15], C[16], C[17], C[0]])) - rotated_kernel_rows.append(tf.stack( - [Z, Z, C[14], B[9], B[10], B[11], C[1]])) - rotated_kernel_rows.append(tf.stack( - [Z, C[13], B[8], A[5], A[0], B[0], C[2]])) - rotated_kernel_rows.append(tf.stack( - [C[12], B[7], A[4], center_weight, A[1], B[1], C[3]])) - rotated_kernel_rows.append(tf.stack( - [C[11], B[6], A[3], A[2], B[2], C[4], Z])) - rotated_kernel_rows.append(tf.stack( - [C[10], B[5], B[4], B[3], C[5], Z, Z])) - rotated_kernel_rows.append(tf.stack( - [C[9], C[8], C[7], C[6], Z, Z, Z])) + rotated_kernel_rows.append( + tf.stack([Z, Z, Z, C[15], C[16], C[17], C[0]]) + ) + rotated_kernel_rows.append( + tf.stack([Z, Z, C[14], B[9], B[10], B[11], C[1]]) + ) + rotated_kernel_rows.append( + tf.stack([Z, C[13], B[8], A[5], A[0], B[0], C[2]]) + ) + rotated_kernel_rows.append( + tf.stack([C[12], B[7], A[4], center_weight, A[1], B[1], C[3]]) + ) + rotated_kernel_rows.append( + tf.stack([C[11], B[6], A[3], A[2], B[2], C[4], Z]) + ) + rotated_kernel_rows.append( + tf.stack([C[10], B[5], B[4], B[3], C[5], Z, Z]) + ) + rotated_kernel_rows.append( + tf.stack([C[9], C[8], C[7], C[6], Z, Z, Z]) + ) else: - raise ValueError("get_rotated_hex_kernel: Unsupported hexagonal " - "filter_size: {!r}".formt(filter_size[0:2])) + raise ValueError( + "get_rotated_hex_kernel: Unsupported hexagonal " + "filter_size: {!r}".format(filter_size[0:2]) + ) rotated_kernel_single = tf.stack(rotated_kernel_rows) # Add free parameters for in and out channel @@ -405,14 +431,15 @@ def get_rotated_hex_kernel(filter_size, num_rotations): rotated_kernel_single = tf.expand_dims(rotated_kernel_single, -1) rotated_kernel_single = tf.expand_dims(rotated_kernel_single, -1) - multiples = [1 for i in range(no_of_dims-2)] + filter_size[-2:] + multiples = [1 for i in range(no_of_dims - 2)] + filter_size[-2:] rotated_kernel_tiled = tf.tile(rotated_kernel_single, multiples) # multiply weights to make in and out channels independent - rotated_kernel = rotated_kernel_tiled*in_out_channel_weights[i] + rotated_kernel = rotated_kernel_tiled * in_out_channel_weights[i] rotated_kernels.append(rotated_kernel) - rotated_kernels = tf.concat(values=rotated_kernels, - axis=len(filter_size)-1) + rotated_kernels = tf.concat( + values=rotated_kernels, axis=len(filter_size) - 1 + ) return rotated_kernels diff --git a/tfscripts/compat/v1/hex/visual.py b/tfscripts/compat/v1/hex/visual.py index e721a5e..d394c52 100644 --- a/tfscripts/compat/v1/hex/visual.py +++ b/tfscripts/compat/v1/hex/visual.py @@ -1,12 +1,13 @@ -''' +""" tfscripts.compat.v1 hexagonal convolution visualization functions -''' +""" from __future__ import division, print_function import numpy as np import tensorflow as tf import matplotlib + try: import _tkinter except ImportError: @@ -30,8 +31,8 @@ def print_hex_data(hex_data): hex_data = {(a,b): value)} and a, b hexagonal-coordinates """ if len(hex_data.keys()) == 1: - print('HexKernel is of size 1') - print('\t1') + print("HexKernel is of size 1") + print("\t1") else: # get range @@ -51,20 +52,20 @@ def print_hex_data(hex_data): min_b = b # print on hexagonal grid - for a in range(max_a, min_a-1, -1): - row = '' + for a in range(max_a, min_a - 1, -1): + row = "" for i in range(a - min_a): - row += ' ' - for b in range(min_b, max_b+1, 1): + row += " " + for b in range(min_b, max_b + 1, 1): if (a, b) in hex_data.keys(): - row += ' {:3d}'.format(hex_data[(a, b)]) + row += " {:3d}".format(hex_data[(a, b)]) else: - row += ' ' - print(row+'\n') + row += " " + print(row + "\n") -def plot_hex2D(hex_grid, file=None, hex_grid_spacing=1.0, norm='linear'): - '''Plots a 2D hex_grid heatmap +def plot_hex2D(hex_grid, file=None, hex_grid_spacing=1.0, norm="linear"): + """Plots a 2D hex_grid heatmap Assumes hex_grid is shaped: 1 1 0 @@ -92,62 +93,73 @@ def plot_hex2D(hex_grid, file=None, hex_grid_spacing=1.0, norm='linear'): ------ ValueError Description - ''' + """ fig = plt.figure() hex_grid = np.asarray(hex_grid) if len(hex_grid.shape) != 2: - raise ValueError('Expects 2dim hexgrid, but got {!r}'.format( - hexgrid.shape)) + raise ValueError( + "Expects 2dim hexgrid, but got {!r}".format(hex_grid.shape) + ) if hex_grid.shape[0] != hex_grid.shape[1]: - raise ValueError('Only supports quadratic grids. {!r}'.format( - hex_grid.shape)) + raise ValueError( + "Only supports quadratic grids. {!r}".format(hex_grid.shape) + ) minValue = np.min(hex_grid) maxValue = np.max(hex_grid) - if norm == 'linear': + if norm == "linear": norm = matplotlib.colors.Normalize(vmin=minValue, vmax=maxValue) - elif norm == 'log': - norm = matplotlib.colors.LogNorm(vmin=max(0.0001, minValue), - vmax=maxValue) + elif norm == "log": + norm = matplotlib.colors.LogNorm( + vmin=max(0.0001, minValue), vmax=maxValue + ) else: - raise ValueError('Wrong value for norm: {!r}'.format(norm)) - cmap = matplotlib.cm.ScalarMappable(norm=norm, - cmap=plt.get_cmap('viridis')) + raise ValueError("Wrong value for norm: {!r}".format(norm)) + cmap = matplotlib.cm.ScalarMappable( + norm=norm, cmap=plt.get_cmap("viridis") + ) size = len(hex_grid) - half_dim = size//2 + half_dim = size // 2 # create list to hold patches patch_list = [] for a in range(-half_dim, half_dim + 1): for b in range(-half_dim, half_dim + 1): - offset_x = hex_grid_spacing/2. * b - x = offset_x + a*hex_grid_spacing - y = b*(hex_grid_spacing*np.sqrt(0.75)) + offset_x = hex_grid_spacing / 2.0 * b + x = offset_x + a * hex_grid_spacing + y = b * (hex_grid_spacing * np.sqrt(0.75)) color = cmap.to_rgba(hex_grid[a + half_dim, b + half_dim]) patch_list.append( - matplotlib.patches.RegularPolygon( - xy=(x, y), - numVertices=6, - radius=hex_grid_spacing/np.sqrt(3), - orientation=0., - facecolor=color, - edgecolor='black' - ) + matplotlib.patches.RegularPolygon( + xy=(x, y), + numVertices=6, + radius=hex_grid_spacing / np.sqrt(3), + orientation=0.0, + facecolor=color, + edgecolor="black", + ) ) - pc = matplotlib.collections.PatchCollection(patch_list, - match_original=True) + pc = matplotlib.collections.PatchCollection( + patch_list, match_original=True + ) ax = plt.gca() ax.add_collection(pc) plt.plot(0, 0) - plt.xlim = [-1.1*size*hex_grid_spacing/2, 1.1*size*hex_grid_spacing/2] - plt.ylim = [-0.7*size*hex_grid_spacing/2, 0.7*size*hex_grid_spacing/2] - - plt.axes().set_aspect('equal') + plt.xlim = [ + -1.1 * size * hex_grid_spacing / 2, + 1.1 * size * hex_grid_spacing / 2, + ] + plt.ylim = [ + -0.7 * size * hex_grid_spacing / 2, + 0.7 * size * hex_grid_spacing / 2, + ] + + plt.axes().set_aspect("equal") if file is None: plt.show() else: @@ -155,15 +167,16 @@ def plot_hex2D(hex_grid, file=None, hex_grid_spacing=1.0, norm='linear'): plt.close(fig) -def visualize_rotated_hex_kernel(filter_size, num_rotations, - file='Rotation_{azimuth:2.2f}.png'): - '''Visualize hexagonal azimuth rotated filters +def visualize_rotated_hex_kernel( + filter_size, num_rotations, file="Rotation_{azimuth:2.2f}.png" +): + """Visualize hexagonal azimuth rotated filters Create Weights for a hexagonal kernel. The Kernel will be of a hexagonal shape in the first two dimensions. The hexagonal kernel is off the shape: [kernel_edge_points, kernel_edge_points,*filter_size[2:]] - But elments with coordinates in the first two dimensions, that don't belong + But elements with coordinates in the first two dimensions, that don't belong to the hexagon are set to zero. The hexagon is defined by filter_size[0:2]. @@ -191,9 +204,9 @@ def visualize_rotated_hex_kernel(filter_size, num_rotations, ------ ValueError Description - ''' + """ - azimuths = np.linspace(0, 360, num_rotations+1)[:-1] + azimuths = np.linspace(0, 360, num_rotations + 1)[:-1] Z = 0 center_weight = np.random.uniform(1, high=15, size=1) @@ -207,7 +220,8 @@ def visualize_rotated_hex_kernel(filter_size, num_rotations, corner_weights2 = [] for i in range(6): corner_weights2.extend( - [Z, np.random.uniform(1, high=15, size=1)[0]]) + [Z, np.random.uniform(1, high=15, size=1)[0]] + ) elif filter_size[0:2] == [3, 0]: # hexagonal 3,0 Filter corner_weights1 = np.random.uniform(1, high=15, size=6) @@ -219,7 +233,8 @@ def visualize_rotated_hex_kernel(filter_size, num_rotations, corner_weights3 = [] for i in range(6): corner_weights3.extend( - [Z, np.random.uniform(1, high=15, size=1)[0], Z]) + [Z, np.random.uniform(1, high=15, size=1)[0], Z] + ) elif filter_size[0:2] == [3, 2]: # hexagonal 3,2 Filter corner_weights1 = np.random.uniform(1, high=15, size=6) @@ -227,15 +242,18 @@ def visualize_rotated_hex_kernel(filter_size, num_rotations, corner_weights3 = [] for i in range(6): corner_weights3.extend( - [Z, Z, np.random.uniform(1, high=15, size=1)[0]]) + [Z, Z, np.random.uniform(1, high=15, size=1)[0]] + ) elif filter_size[0:2] == [4, 0]: # hexagonal 4,0 Filter corner_weights1 = np.random.uniform(1, high=15, size=6) corner_weights2 = np.random.uniform(1, high=15, size=12) corner_weights3 = np.random.uniform(1, high=15, size=18) else: - raise ValueError("visualize_rotated_hex_kernel: Unsupported hexagonal " - "filter_size: {!r}".formt(filter_size[0:2])) + raise ValueError( + "visualize_rotated_hex_kernel: Unsupported hexagonal " + "filter_size: {!r}".format(filter_size[0:2]) + ) rotated_kernels = [] for azimuth in azimuths: @@ -255,8 +273,11 @@ def visualize_rotated_hex_kernel(filter_size, num_rotations, rotated_kernel_rows.append([B[7], A[4], center_weight, A[1], B[1]]) rotated_kernel_rows.append([B[6], A[3], A[2], B[2], Z]) rotated_kernel_rows.append([B[5], B[4], B[3], Z, Z]) - elif (filter_size[0:2] == [3, 1] or filter_size[0:2] == [3, 2] or - filter_size[0:2] == [4, 0]): + elif ( + filter_size[0:2] == [3, 1] + or filter_size[0:2] == [3, 2] + or filter_size[0:2] == [4, 0] + ): # hexagonal 3,1 Filter A = get_rotated_corner_weights(corner_weights1, azimuth) B = get_rotated_corner_weights(corner_weights2, azimuth) @@ -264,25 +285,25 @@ def visualize_rotated_hex_kernel(filter_size, num_rotations, rotated_kernel_rows.append([Z, Z, Z, C[15], C[16], C[17], C[0]]) rotated_kernel_rows.append([Z, Z, C[14], B[9], B[10], B[11], C[1]]) rotated_kernel_rows.append( - [Z, C[13], B[8], A[5], A[0], B[0], C[2]]) + [Z, C[13], B[8], A[5], A[0], B[0], C[2]] + ) rotated_kernel_rows.append( - [C[12], B[7], A[4], center_weight, A[1], B[1], C[3]]) + [C[12], B[7], A[4], center_weight, A[1], B[1], C[3]] + ) rotated_kernel_rows.append( - [C[11], B[6], A[3], A[2], B[2], C[4], Z]) + [C[11], B[6], A[3], A[2], B[2], C[4], Z] + ) rotated_kernel_rows.append([C[10], B[5], B[4], B[3], C[5], Z, Z]) rotated_kernel_rows.append([C[9], C[8], C[7], C[6], Z, Z, Z]) else: - raise ValueError("visualize_rotated_hex_kernel: Unsupported " - "hexagonal filter_size: {!r}".formt( - filter_size[0:2])) + raise ValueError( + "visualize_rotated_hex_kernel: Unsupported " + "hexagonal filter_size: {!r}".format(filter_size[0:2]) + ) rotated_kernel = rotated_kernel_rows rotated_kernels.append(rotated_kernel) rotated_kernels = np.asarray(rotated_kernels) - width = 3 - height = int(num_rotations/width) - - num_rows = len(rotated_kernels[0]) for i, rot in enumerate(rotated_kernels): plot_hex2D(rot, file=file.format(azimuth=azimuths[i])) diff --git a/tfscripts/compat/v1/layers.py b/tfscripts/compat/v1/layers.py index 4453647..4063833 100644 --- a/tfscripts/compat/v1/layers.py +++ b/tfscripts/compat/v1/layers.py @@ -1,6 +1,6 @@ -''' +""" tfscripts.compat.v1 layers are defined here -''' +""" from __future__ import division, print_function @@ -20,7 +20,7 @@ def flatten_layer(layer): - ''' + """ Helper-function for flattening a layer Parameters @@ -32,7 +32,7 @@ def flatten_layer(layer): ------- tf.Tensor Flattened layer. - ''' + """ # Get the shape of the input layer. layer_shape = layer.get_shape() @@ -49,7 +49,7 @@ def flatten_layer(layer): def flatten_hex_layer(hex_layer): - '''Helper-function for flattening an hexagonally shaped layer + """Helper-function for flattening an hexagonally shaped layer Assumes hexagonal shape in the first two spatial dimensions of the form: @@ -65,7 +65,7 @@ def flatten_hex_layer(hex_layer): where s is the size of the x- and y-dimension will be discarded. - These correspond to the elments outside of the hexagon + These correspond to the elements outside of the hexagon Parameters ---------- @@ -83,7 +83,7 @@ def flatten_hex_layer(hex_layer): ------ ValueError Description - ''' + """ # Get the shape of the input layer. layer_shape = hex_layer.get_shape().as_list() @@ -93,8 +93,10 @@ def flatten_hex_layer(hex_layer): size = layer_shape[1] if layer_shape[2] != size: - raise ValueError("flatten_hex_layer: size of x- and y- dimension must " - "match, but are {!r}".format(layer_shape[1:3])) + raise ValueError( + "flatten_hex_layer: size of x- and y- dimension must " + "match, but are {!r}".format(layer_shape[1:3]) + ) num_features = np.prod(layer_shape[3:]) @@ -102,9 +104,18 @@ def flatten_hex_layer(hex_layer): flat_elements = [] for a in range(size): for b in range(size): - if not (a + b < (size-1)//2 or a + b > 2*(size-1) - (size-1)//2): - flattened_element = tf.reshape(hex_layer[:, a, b, ], - [-1, num_features]) + if not ( + a + b < (size - 1) // 2 + or a + b > 2 * (size - 1) - (size - 1) // 2 + ): + flattened_element = tf.reshape( + hex_layer[ + :, + a, + b, + ], + [-1, num_features], + ) flat_elements.append(flattened_element) total_num_features += num_features @@ -112,31 +123,32 @@ def flatten_hex_layer(hex_layer): return hex_layer_flat, total_num_features -def new_conv_nd_layer(input, - filter_size, - num_filters, - pooling_type=None, - pooling_strides=None, - pooling_ksize=None, - pooling_padding='SAME', - use_dropout=False, - keep_prob=None, - activation='elu', - strides=None, - padding='SAME', - use_batch_normalisation=False, - dilation_rate=None, - use_residual=False, - method='convolution', - weights=None, - biases=None, - trafo=None, - is_training=None, - hex_num_rotations=1, - hex_azimuth=None, - hex_zero_out=False, - ): - '''Helper-function for creating a new nD Convolutional Layer +def new_conv_nd_layer( + input, + filter_size, + num_filters, + pooling_type=None, + pooling_strides=None, + pooling_ksize=None, + pooling_padding="SAME", + use_dropout=False, + keep_prob=None, + activation="elu", + strides=None, + padding="SAME", + use_batch_normalisation=False, + dilation_rate=None, + use_residual=False, + method="convolution", + weights=None, + biases=None, + trafo=None, + is_training=None, + hex_num_rotations=1, + hex_azimuth=None, + hex_zero_out=False, +): + """Helper-function for creating a new n-dim Convolutional Layer 2 <= n <=4 are supported. For n == 3 (3 spatial dimensions x, y, and z): @@ -280,7 +292,7 @@ def new_conv_nd_layer(input, Description ValueError Description - ''' + """ # check dimension of input num_dims = len(input.shape) - 2 @@ -312,13 +324,15 @@ def new_conv_nd_layer(input, strides = [1, 1, 1, 1] else: - raise ValueError('Currently only 2D, 3D or 4D supported {!r}'.format( - input)) + raise ValueError( + "Currently only 2D, 3D or 4D supported {!r}".format(input) + ) # make sure inferred dimension matches filter_size if not len(filter_size) == num_dims: - err_msg = 'Filter size {!r} does not fit to input shape {!r}'.format( - input.shape) + err_msg = "Filter size {!r} does not fit to input shape {!r}".format( + filter_size, input.shape + ) raise ValueError(err_msg) num_input_channels = input.get_shape().as_list()[-1] @@ -327,7 +341,7 @@ def new_conv_nd_layer(input, shape = list(filter_size) + [num_input_channels, num_filters] # Create new weights aka. filters with the given shape. - if method.lower() == 'convolution': + if method.lower() == "convolution": if weights is None: # weights = new_kernel_weights(shape=shape) weights = new_weights(shape=shape) @@ -339,80 +353,90 @@ def new_conv_nd_layer(input, # ------------------- # Perform convolution # ------------------- - if method.lower() == 'convolution': + if method.lower() == "convolution": if num_dims == 2 or num_dims == 3: - layer = tf.nn.convolution(input, - weights, - strides=strides[1:-1], - padding=padding, - dilations=dilation_rate) + layer = tf.nn.convolution( + input, + weights, + strides=strides[1:-1], + padding=padding, + dilations=dilation_rate, + ) elif num_dims == 4: - layer = conv.conv4d_stacked(input=input, - filter=weights, - strides=strides, - padding=padding, - dilation_rate=dilation_rate) + layer = conv.conv4d_stacked( + input=input, + filter=weights, + strides=strides, + padding=padding, + dilation_rate=dilation_rate, + ) # --------------------- # Hexagonal convolution # --------------------- - elif method.lower() == 'hex_convolution': + elif method.lower() == "hex_convolution": if num_dims == 2 or num_dims == 3: - layer, weights = hx.conv_hex(input_data=input, - filter_size=filter_size, - num_filters=num_filters, - padding=padding, - strides=strides, - num_rotations=hex_num_rotations, - azimuth=hex_azimuth, - dilation_rate=dilation_rate, - zero_out=hex_zero_out, - kernel=weights, - ) + layer, weights = hx.conv_hex( + input_data=input, + filter_size=filter_size, + num_filters=num_filters, + padding=padding, + strides=strides, + num_rotations=hex_num_rotations, + azimuth=hex_azimuth, + dilation_rate=dilation_rate, + zero_out=hex_zero_out, + kernel=weights, + ) elif num_dims == 4: - layer, weights = hx.conv_hex4d(input_data=input, - filter_size=filter_size, - num_filters=num_filters, - padding=padding, - strides=strides, - num_rotations=hex_num_rotations, - azimuth=hex_azimuth, - dilation_rate=dilation_rate, - zero_out=hex_zero_out, - kernel=weights, - ) + layer, weights = hx.conv_hex4d( + input_data=input, + filter_size=filter_size, + num_filters=num_filters, + padding=padding, + strides=strides, + num_rotations=hex_num_rotations, + azimuth=hex_azimuth, + dilation_rate=dilation_rate, + zero_out=hex_zero_out, + kernel=weights, + ) # Create new biases, one for each filter. if biases is None: - biases = new_biases(length=num_filters*hex_num_rotations) + biases = new_biases(length=num_filters * hex_num_rotations) # ------------------- # locally connected # ------------------- - elif method.lower() == 'locally_connected': + elif method.lower() == "locally_connected": if (weights is not None) or (biases is not None): - raise NotImplementedError("Locally conncected layers currently do " - "not support predefined weights") + raise NotImplementedError( + "Locally conncected layers currently do " + "not support predefined weights" + ) if num_dims == 2: layer, weights = conv.locally_connected_2d( - input=input, - num_outputs=num_filters, - filter_size=filter_size, - strides=strides[1:-1], - padding=padding, - dilation_rate=dilation_rate) + input=input, + num_outputs=num_filters, + filter_size=filter_size, + strides=strides[1:-1], + padding=padding, + dilation_rate=dilation_rate, + ) elif num_dims == 3: layer, weights = conv.locally_connected_3d( - input=input, - num_outputs=num_filters, - filter_size=filter_size, - strides=strides[1:-1], - padding=padding, - dilation_rate=dilation_rate) + input=input, + num_outputs=num_filters, + filter_size=filter_size, + strides=strides[1:-1], + padding=padding, + dilation_rate=dilation_rate, + ) elif num_dims == 4: - raise NotImplementedError('4D locally connected not implemented!') + raise NotImplementedError("4D locally connected not implemented!") # Create new biases, one for each filter and position biases = new_weights(shape=layer.get_shape().as_list()[1:]) @@ -420,49 +444,49 @@ def new_conv_nd_layer(input, # ------------------- # local trafo # ------------------- - elif method.lower() == 'local_trafo': + elif method.lower() == "local_trafo": - assert (weights is None and biases is None) + assert weights is None and biases is None if num_dims == 3: layer = conv.trans3d_op( - input=input, - num_out_channel=num_filters, - filter_size=filter_size, - method=method, - trafo=trafo, - filter=weights, - strides=strides[1:-1], - padding=padding, - dilation_rate=dilation_rate, - stack_axis=None, - ) + input=input, + num_out_channel=num_filters, + filter_size=filter_size, + method=method, + trafo=trafo, + filter=weights, + strides=strides[1:-1], + padding=padding, + dilation_rate=dilation_rate, + stack_axis=None, + ) else: - raise NotImplementedError('local_trafo currently only for 3D') + raise NotImplementedError("local_trafo currently only for 3D") # -------------------- # dynamic convolution # -------------------- - elif method.lower() == 'dynamic_convolution': + elif method.lower() == "dynamic_convolution": assert weights is not None if num_dims == 2 or num_dims == 3: layer = conv.dynamic_conv( - input=input, - filter=weights, - strides=strides[1:-1], - padding=padding, - dilation_rate=dilation_rate - ) + input=input, + filter=weights, + strides=strides[1:-1], + padding=padding, + dilation_rate=dilation_rate, + ) elif num_dims == 4: - raise NotImplementedError('4D dynamic_convolution not implemented') + raise NotImplementedError("4D dynamic_convolution not implemented") if biases is None: biases = new_biases(length=num_filters) else: - raise ValueError('Unknown method: {!r}'.format(method)) + raise ValueError("Unknown method: {!r}".format(method)) # repair to get std dev of 1 # In convolution operation, a matrix multiplication is performed @@ -480,7 +504,7 @@ def new_conv_nd_layer(input, # the values need to be divided by this factor. # In the case of the hex_convolution, this factor gets reduced to # the number of non zero elements in the hex kernel. - if method.lower() == 'hex_convolution': + if method.lower() == "hex_convolution": num_filter_vars = hx.get_num_hex_points(filter_size[0]) if len(filter_size) > 2: @@ -498,11 +522,12 @@ def new_conv_nd_layer(input, # Add the biases to the results of the convolution. # A bias-value is added to each filter-channel. if biases is not None: - layer = (layer + biases) / np.sqrt(2.) + layer = (layer + biases) / np.sqrt(2.0) # Apply activation and batch normalisation - layer = core.activation(layer, activation, use_batch_normalisation, - is_training) + layer = core.activation( + layer, activation, use_batch_normalisation, is_training + ) # Use as Residual if use_residual: @@ -510,35 +535,42 @@ def new_conv_nd_layer(input, # Use pooling to down-sample the image resolution? if num_dims == 2: - layer = pooling.pool(layer=layer, - ksize=pooling_ksize, - strides=pooling_strides, - padding=pooling_padding, - pooling_type=pooling_type, - ) + layer = pooling.pool( + layer=layer, + ksize=pooling_ksize, + strides=pooling_strides, + padding=pooling_padding, + pooling_type=pooling_type, + ) elif num_dims == 3: - layer = pooling.pool3d(layer=layer, - ksize=pooling_ksize, - strides=pooling_strides, - padding=pooling_padding, - pooling_type=pooling_type, - ) + layer = pooling.pool3d( + layer=layer, + ksize=pooling_ksize, + strides=pooling_strides, + padding=pooling_padding, + pooling_type=pooling_type, + ) elif num_dims == 4: - if pooling_type == 'max': - layer = pooling.max_pool4d_stacked(input=layer, - ksize=pooling_ksize, - strides=pooling_strides, - padding=pooling_padding) - elif pooling_type == 'avg': - layer = pooling.avg_pool4d_stacked(input=layer, - ksize=pooling_ksize, - strides=pooling_strides, - padding=pooling_padding) + if pooling_type == "max": + layer = pooling.max_pool4d_stacked( + input=layer, + ksize=pooling_ksize, + strides=pooling_strides, + padding=pooling_padding, + ) + elif pooling_type == "avg": + layer = pooling.avg_pool4d_stacked( + input=layer, + ksize=pooling_ksize, + strides=pooling_strides, + padding=pooling_padding, + ) else: - raise NotImplementedError("Pooling type not supported: " - "{!r}".format(pooling_type)) + raise NotImplementedError( + "Pooling type not supported: " "{!r}".format(pooling_type) + ) else: - raise NotImplementedError('Only supported 2d, 3d, 4d!') + raise NotImplementedError("Only supported 2d, 3d, 4d!") if use_dropout: layer = tf.nn.dropout(layer, rate=1 - (keep_prob)) @@ -546,19 +578,20 @@ def new_conv_nd_layer(input, return layer, weights, biases -def new_fc_layer(input, - num_outputs, - use_dropout=False, - keep_prob=None, - activation='elu', - use_batch_normalisation=False, - use_residual=False, - is_training=None, - weights=None, - biases=None, - max_out_size=None, - ): - ''' +def new_fc_layer( + input, + num_outputs, + use_dropout=False, + keep_prob=None, + activation="elu", + use_batch_normalisation=False, + use_residual=False, + is_training=None, + weights=None, + biases=None, + max_out_size=None, +): + """ Helper-function for creating a new Fully-Connected Layer input: 2-dim tensor of shape [batch_size, num_inputs] output: 2-dim tensor of shape [batch_size, num_outputs] @@ -600,7 +633,7 @@ def new_fc_layer(input, tf.Tensor, tf.Tensor, tf.Tensor The layer, weights, and biases are returned as tf.Tensor The shape of the output layer is: [batch, num_outputs] - ''' + """ num_inputs = input.get_shape().as_list()[-1] # Create new weights and biases. @@ -616,23 +649,25 @@ def new_fc_layer(input, # repair to get std dev of 1 layer = layer / np.sqrt(num_inputs) - layer = (layer + biases) / np.sqrt(2.) + layer = (layer + biases) / np.sqrt(2.0) # Apply activation and batch normalisation - layer = core.activation(layer, activation, use_batch_normalisation, - is_training) + layer = core.activation( + layer, activation, use_batch_normalisation, is_training + ) if max_out_size is not None: layer_shape = layer.get_shape().as_list() - assert layer_shape[-1] % max_out_size == 0, \ - "max out needs to match dim" + assert ( + layer_shape[-1] % max_out_size == 0 + ), "max out needs to match dim" layer_shape[-1] = layer_shape[-1] // max_out_size layer = tf.contrib.layers.maxout( - inputs=layer, - num_units=max_out_size, - axis=-1, - ) + inputs=layer, + num_units=max_out_size, + axis=-1, + ) channel_stride = max(1, num_inputs // layer_shape[-1]) res_strides = [1 for i in input.get_shape()[:-1]] + [channel_stride] @@ -641,10 +676,11 @@ def new_fc_layer(input, # Use as Residual if use_residual: - layer = core.add_residual(input=input, - residual=layer, - strides=res_strides, - ) + layer = core.add_residual( + input=input, + residual=layer, + strides=res_strides, + ) if use_dropout: layer = tf.nn.dropout(layer, rate=1 - (keep_prob)) @@ -652,20 +688,21 @@ def new_fc_layer(input, return layer, weights, biases -def new_channel_wise_fc_layer(input, - num_outputs, - use_dropout=False, - keep_prob=None, - activation='elu', - use_batch_normalisation=False, - use_residual=False, - is_training=None, - weights=None, - biases=None, - max_out_size=None, - ): - ''' - Helper-function for creating a new cahnnel wise Fully-Connected Layer +def new_channel_wise_fc_layer( + input, + num_outputs, + use_dropout=False, + keep_prob=None, + activation="elu", + use_batch_normalisation=False, + use_residual=False, + is_training=None, + weights=None, + biases=None, + max_out_size=None, +): + """ + Helper-function for creating a new channel wise Fully-Connected Layer input: 3-dim tensor of shape [batch_size, num_inputs, num_channel] output: 3-dim tensor of shape [batch_size, num_outputs, num_channel] @@ -708,12 +745,13 @@ def new_channel_wise_fc_layer(input, The shape of the output layer is: [batch, num_outputs, num_channel] where num_channel is the same as the input number of channels. - ''' + """ input_shape = input.get_shape().as_list() # input: [batch, num_inputs, num_channel] - assert len(input_shape) == 3, \ - '{} != [batch, num_inputs, num_channel]'.format(input_shape) + assert ( + len(input_shape) == 3 + ), "{} != [batch, num_inputs, num_channel]".format(input_shape) num_inputs = input_shape[1] num_channels = input_shape[2] @@ -737,27 +775,29 @@ def new_channel_wise_fc_layer(input, # repair to get std dev of 1 layer = layer / np.sqrt(num_inputs) - layer = (layer + biases) / np.sqrt(2.) + layer = (layer + biases) / np.sqrt(2.0) # Apply activation and batch normalisation - layer = core.activation(layer, activation, use_batch_normalisation, - is_training) + layer = core.activation( + layer, activation, use_batch_normalisation, is_training + ) # Use as Residual if use_residual: # convert to [batch, num_channel, num_outputs] layer = tf.transpose(a=layer, perm=[0, 2, 1]) - layer = core.add_residual(input=tf.transpose(a=input, perm=[0, 2, 1]), - residual=layer) + layer = core.add_residual( + input=tf.transpose(a=input, perm=[0, 2, 1]), residual=layer + ) # convert back to [batch, num_outputs, num_channel] layer = tf.transpose(a=layer, perm=[0, 2, 1]) if max_out_size is not None: layer = tf.contrib.layers.maxout( - inputs=layer, - num_units=max_out_size, - axis=-1, - ) + inputs=layer, + num_units=max_out_size, + axis=-1, + ) if use_dropout: layer = tf.nn.dropout(layer, rate=1 - (keep_prob)) @@ -765,20 +805,21 @@ def new_channel_wise_fc_layer(input, return layer, weights, biases -def new_fc_layers(input, - fc_sizes, - use_dropout_list=False, - keep_prob=None, - activation_list='elu', - is_training=None, - use_batch_normalisation_list=False, - use_residual_list=False, - weights_list=None, - biases_list=None, - max_out_size_list=None, - verbose=True, - ): - ''' +def new_fc_layers( + input, + fc_sizes, + use_dropout_list=False, + keep_prob=None, + activation_list="elu", + is_training=None, + use_batch_normalisation_list=False, + use_residual_list=False, + weights_list=None, + biases_list=None, + max_out_size_list=None, + verbose=True, +): + """ Helper-function for creating new fully connected layers. Parameters @@ -842,7 +883,7 @@ def new_fc_layers(input, ------ ValueError Description - ''' + """ num_layers = len(fc_sizes) if isinstance(activation_list, str): activation_list = [activation_list for i in range(num_layers)] @@ -850,8 +891,9 @@ def new_fc_layers(input, use_dropout_list = [use_dropout_list for i in range(num_layers)] # create batch normalisation array if isinstance(use_batch_normalisation_list, bool): - use_batch_normalisation_list = [use_batch_normalisation_list - for i in range(num_layers)] + use_batch_normalisation_list = [ + use_batch_normalisation_list for i in range(num_layers) + ] # create use_residual_list if use_residual_list is False or use_residual_list is True: use_residual_list = [use_residual_list for i in range(num_layers)] @@ -875,8 +917,9 @@ def new_fc_layers(input, new_fc_layer_func = new_fc_layer else: - raise ValueError('Input dimension is wrong: {}'.format( - input.get_shape().as_list())) + raise ValueError( + "Input dimension is wrong: {}".format(input.get_shape().as_list()) + ) # create layers: layers = [] @@ -886,22 +929,22 @@ def new_fc_layers(input, if i == 0: previous_layer = input else: - previous_layer = layers[i-1] + previous_layer = layers[i - 1] layer_i, weights_i, biases_i = new_fc_layer_func( - input=previous_layer, - num_outputs=fc_sizes[i], - activation=activation_list[i], - use_dropout=use_dropout_list[i], - keep_prob=keep_prob, - is_training=is_training, - use_batch_normalisation=use_batch_normalisation_list[i], - use_residual=use_residual_list[i], - weights=weights_list[i], - biases=biases_list[i], - max_out_size=max_out_size_list[i], - ) + input=previous_layer, + num_outputs=fc_sizes[i], + activation=activation_list[i], + use_dropout=use_dropout_list[i], + keep_prob=keep_prob, + is_training=is_training, + use_batch_normalisation=use_batch_normalisation_list[i], + use_residual=use_residual_list[i], + weights=weights_list[i], + biases=biases_list[i], + max_out_size=max_out_size_list[i], + ) if verbose: - print('fc_layer_{:03d}'.format(i), layer_i) + print("fc_layer_{:03d}".format(i), layer_i) layers.append(layer_i) weights.append(weights_i) biases.append(biases_i) @@ -909,33 +952,34 @@ def new_fc_layers(input, return layers, weights, biases -def new_conv_nd_layers(input, - filter_size_list, - num_filters_list, - pooling_type_list=None, - pooling_strides_list=None, - pooling_ksize_list=None, - pooling_padding_list='SAME', - padding_list='SAME', - strides_list=None, - keep_prob=None, - activation_list='elu', - is_training=None, - use_batch_normalisation_list=False, - dilation_rate_list=None, - use_residual_list=False, - use_dropout_list=False, - method_list='convolution', - weights_list=None, - biases_list=None, - trafo_list=None, - hex_num_rotations_list=1, - hex_azimuth_list=None, - hex_zero_out_list=False, - name='conv_{}d_layer', - verbose=True, - ): - ''' +def new_conv_nd_layers( + input, + filter_size_list, + num_filters_list, + pooling_type_list=None, + pooling_strides_list=None, + pooling_ksize_list=None, + pooling_padding_list="SAME", + padding_list="SAME", + strides_list=None, + keep_prob=None, + activation_list="elu", + is_training=None, + use_batch_normalisation_list=False, + dilation_rate_list=None, + use_residual_list=False, + use_dropout_list=False, + method_list="convolution", + weights_list=None, + biases_list=None, + trafo_list=None, + hex_num_rotations_list=1, + hex_azimuth_list=None, + hex_zero_out_list=False, + name="conv_{}d_layer", + verbose=True, +): + """ Helper-function for creating new conv2d, conv3d, and conv4d layers. Parameters @@ -1101,7 +1145,7 @@ def new_conv_nd_layers(input, ------ ValueError Description - ''' + """ # check dimension of input num_dims = len(input.shape) @@ -1133,8 +1177,9 @@ def new_conv_nd_layers(input, strides_list = [1, 1, 1, 1] else: - raise ValueError('Currently only 2D, 3D, or 4D supported {!r}'.format( - input)) + raise ValueError( + "Currently only 2D, 3D, or 4D supported {!r}".format(input) + ) num_layers = len(num_filters_list) @@ -1146,28 +1191,36 @@ def new_conv_nd_layers(input, pooling_type_list = [pooling_type_list for i in range(num_layers)] # create pooling_strides_list - if (len(np.array(pooling_strides_list).shape) == 1 and - len(pooling_strides_list) == num_dims): - pooling_strides_list = [pooling_strides_list - for i in range(num_layers)] + if ( + len(np.array(pooling_strides_list).shape) == 1 + and len(pooling_strides_list) == num_dims + ): + pooling_strides_list = [ + pooling_strides_list for i in range(num_layers) + ] # create pooling_ksize_list - if (len(np.array(pooling_ksize_list).shape) == 1 and - len(pooling_ksize_list) == num_dims): + if ( + len(np.array(pooling_ksize_list).shape) == 1 + and len(pooling_ksize_list) == num_dims + ): pooling_ksize_list = [pooling_ksize_list for i in range(num_layers)] # create pooling_padding_list - if pooling_padding_list == 'SAME' or pooling_padding_list == 'VALID': - pooling_padding_list = [pooling_padding_list - for i in range(num_layers)] + if pooling_padding_list == "SAME" or pooling_padding_list == "VALID": + pooling_padding_list = [ + pooling_padding_list for i in range(num_layers) + ] # create padding_list - if padding_list == 'SAME' or padding_list == 'VALID': + if padding_list == "SAME" or padding_list == "VALID": padding_list = [padding_list for i in range(num_layers)] # create strides_list - if (len(np.array(strides_list).shape) == 1 and - len(strides_list) == num_dims): + if ( + len(np.array(strides_list).shape) == 1 + and len(strides_list) == num_dims + ): strides_list = [strides_list for i in range(num_layers)] # create activation_list @@ -1176,13 +1229,15 @@ def new_conv_nd_layers(input, # create batch normalisation array if isinstance(use_batch_normalisation_list, bool): - use_batch_normalisation_list = [use_batch_normalisation_list - for i in range(num_layers)] + use_batch_normalisation_list = [ + use_batch_normalisation_list for i in range(num_layers) + ] # create dilation_rate_list if dilation_rate_list is None or ( - len(np.asarray(dilation_rate_list).shape) == 1 and - len(dilation_rate_list) == num_dims - 2): + len(np.asarray(dilation_rate_list).shape) == 1 + and len(dilation_rate_list) == num_dims - 2 + ): dilation_rate_list = [dilation_rate_list for i in range(num_layers)] # create use_residual_list @@ -1210,12 +1265,12 @@ def new_conv_nd_layers(input, # create hex_num_rotations_list if isinstance(hex_num_rotations_list, int): - hex_num_rotations_list = [hex_num_rotations_list - for i in range(num_layers)] + hex_num_rotations_list = [ + hex_num_rotations_list for i in range(num_layers) + ] # create hex_azimuth_list - if (hex_azimuth_list is None or - tf.is_tensor(hex_azimuth_list)): + if hex_azimuth_list is None or tf.is_tensor(hex_azimuth_list): hex_azimuth_list = [hex_azimuth_list for i in range(num_layers)] # create hex_zero out array @@ -1231,34 +1286,34 @@ def new_conv_nd_layers(input, if i == 0: previous_layer = input else: - previous_layer = layers[i-1] + previous_layer = layers[i - 1] layer_i, weights_i, biases_i = new_conv_nd_layer( - input=previous_layer, - filter_size=filter_size_list[i], - num_filters=num_filters_list[i], - pooling_padding=pooling_padding_list[i], - pooling_strides=pooling_strides_list[i], - pooling_type=pooling_type_list[i], - pooling_ksize=pooling_ksize_list[i], - strides=strides_list[i], - padding=padding_list[i], - use_dropout=use_dropout_list[i], - keep_prob=keep_prob, - activation=activation_list[i], - is_training=is_training, - use_batch_normalisation=use_batch_normalisation_list[i], - dilation_rate=dilation_rate_list[i], - use_residual=use_residual_list[i], - method=method_list[i], - weights=weights_list[i], - biases=biases_list[i], - trafo=trafo_list[i], - hex_num_rotations=hex_num_rotations_list[i], - hex_azimuth=hex_azimuth_list[i], - hex_zero_out=hex_zero_out_list[i], - ) + input=previous_layer, + filter_size=filter_size_list[i], + num_filters=num_filters_list[i], + pooling_padding=pooling_padding_list[i], + pooling_strides=pooling_strides_list[i], + pooling_type=pooling_type_list[i], + pooling_ksize=pooling_ksize_list[i], + strides=strides_list[i], + padding=padding_list[i], + use_dropout=use_dropout_list[i], + keep_prob=keep_prob, + activation=activation_list[i], + is_training=is_training, + use_batch_normalisation=use_batch_normalisation_list[i], + dilation_rate=dilation_rate_list[i], + use_residual=use_residual_list[i], + method=method_list[i], + weights=weights_list[i], + biases=biases_list[i], + trafo=trafo_list[i], + hex_num_rotations=hex_num_rotations_list[i], + hex_azimuth=hex_azimuth_list[i], + hex_zero_out=hex_zero_out_list[i], + ) if verbose: - print('{}_{:02d}'.format(name, i), layer_i) + print("{}_{:02d}".format(name, i), layer_i) layers.append(layer_i) weights.append(weights_i) biases.append(biases_i) diff --git a/tfscripts/compat/v1/pooling.py b/tfscripts/compat/v1/pooling.py index b7a9403..62f9ed7 100644 --- a/tfscripts/compat/v1/pooling.py +++ b/tfscripts/compat/v1/pooling.py @@ -1,6 +1,6 @@ -''' +""" Pooling functions for tfscripts.compat.v1 -''' +""" from __future__ import division, print_function @@ -44,11 +44,13 @@ def pool3d(layer, ksize, strides, padding, pooling_type): # pool over depth, if necessary: if ksize[-1] != 1 or strides[-1] != 1: - layer = pool_over_depth(layer, - ksize=ksize[-1], - stride=strides[-1], - padding=padding, - pooling_type=pooling_type) + layer = pool_over_depth( + layer, + ksize=ksize[-1], + stride=strides[-1], + padding=padding, + pooling_type=pooling_type, + ) ksize = list(ksize) strides = list(strides) ksize[-1] = 1 @@ -56,27 +58,23 @@ def pool3d(layer, ksize, strides, padding, pooling_type): # Use pooling to down-sample the image resolution? if ksize[1:-1] != [1, 1, 1] or strides[1:-1] != [1, 1, 1]: - if pooling_type == 'max': - layer = tf.nn.max_pool3d(input=layer, - ksize=ksize, - strides=strides, - padding=padding) - elif pooling_type == 'avg': - layer = tf.nn.avg_pool3d(input=layer, - ksize=ksize, - strides=strides, - padding=padding) - - elif pooling_type == 'max_avg': - layer_max = tf.nn.max_pool3d(input=layer, - ksize=ksize, - strides=strides, - padding=padding) - layer_avg = tf.nn.avg_pool3d(input=layer, - ksize=ksize, - strides=strides, - padding=padding) - layer = (layer_avg + layer_max) / 2. + if pooling_type == "max": + layer = tf.nn.max_pool3d( + input=layer, ksize=ksize, strides=strides, padding=padding + ) + elif pooling_type == "avg": + layer = tf.nn.avg_pool3d( + input=layer, ksize=ksize, strides=strides, padding=padding + ) + + elif pooling_type == "max_avg": + layer_max = tf.nn.max_pool3d( + input=layer, ksize=ksize, strides=strides, padding=padding + ) + layer_avg = tf.nn.avg_pool3d( + input=layer, ksize=ksize, strides=strides, padding=padding + ) + layer = (layer_avg + layer_max) / 2.0 if was_float64: layer = tf.cast(layer, tf.float64) @@ -118,32 +116,34 @@ def pool(layer, ksize, strides, padding, pooling_type): # pool over depth, if necessary: if ksize[-1] != 1 or strides[-1] != 1: - layer = pool_over_depth(layer, - ksize=ksize[-1], - stride=strides[-1], - padding=padding, - pooling_type=pooling_type) + layer = pool_over_depth( + layer, + ksize=ksize[-1], + stride=strides[-1], + padding=padding, + pooling_type=pooling_type, + ) ksize = list(ksize) strides = list(strides) ksize[-1] = 1 strides[-1] = 1 # Use pooling to down-sample the image resolution? - if pooling_type == 'max': + if pooling_type == "max": layer = tf.nn.max_pool2d( input=layer, ksize=ksize, strides=strides, padding=padding, ) - elif pooling_type == 'avg': + elif pooling_type == "avg": layer = tf.nn.avg_pool2d( input=layer, ksize=ksize, strides=strides, padding=padding, ) - elif pooling_type == 'max_avg': + elif pooling_type == "max_avg": layer_max = tf.nn.max_pool2d( input=layer, ksize=ksize, @@ -156,7 +156,7 @@ def pool(layer, ksize, strides, padding, pooling_type): strides=strides, padding=padding, ) - layer = (layer_avg + layer_max) / 2. + layer = (layer_avg + layer_max) / 2.0 if was_float64: layer = tf.cast(layer, tf.float64) @@ -165,7 +165,7 @@ def pool(layer, ksize, strides, padding, pooling_type): def pool_over_depth(layer, ksize, stride, padding, pooling_type): - ''' + """ Performs pooling over last dimension of layer. Assumes that the last dimension of layer is the depth channel. @@ -187,16 +187,18 @@ def pool_over_depth(layer, ksize, stride, padding, pooling_type): ------- tf.Tensor The pooled output tensor. - ''' + """ num_channels = layer.get_shape().as_list()[-1] # get start index - start_index = get_start_index(input_length=num_channels, - filter_size=ksize, - padding=padding, - stride=stride, - dilation=1) + start_index = get_start_index( + input_length=num_channels, + filter_size=ksize, + padding=padding, + stride=stride, + dilation=1, + ) input_patches = [] # --------------------------- @@ -205,13 +207,15 @@ def pool_over_depth(layer, ksize, stride, padding, pooling_type): for index in range(start_index, num_channels, stride): # get slice for patch along channel-axis - slice_c, padding_c = get_conv_slice(index, - input_length=num_channels, - filter_size=ksize, - stride=stride, - dilation=1) + slice_c, padding_c = get_conv_slice( + index, + input_length=num_channels, + filter_size=ksize, + stride=stride, + dilation=1, + ) - if padding == 'VALID' and padding_c != (0, 0): + if padding == "VALID" and padding_c != (0, 0): # skip this c position, since it does not provide # a valid patch for padding 'VALID' continue @@ -219,26 +223,29 @@ def pool_over_depth(layer, ksize, stride, padding, pooling_type): # ------------------------------------------ # Get input patch at filter position c # ------------------------------------------ - if pooling_type == 'max': - input_patches.append(tf.reduce_max( - input_tensor=layer[..., slice_c], axis=-1)) + if pooling_type == "max": + input_patches.append( + tf.reduce_max(input_tensor=layer[..., slice_c], axis=-1) + ) - elif pooling_type == 'avg': - input_patches.append(tf.reduce_mean( - input_tensor=layer[..., slice_c], axis=-1)) + elif pooling_type == "avg": + input_patches.append( + tf.reduce_mean(input_tensor=layer[..., slice_c], axis=-1) + ) else: - raise ValueError('Pooling_type {!r} is unknown.'.format( - pooling_type)) + raise ValueError( + "Pooling_type {!r} is unknown.".format(pooling_type) + ) - if pooling_type in ['max', 'avg']: + if pooling_type in ["max", "avg"]: layer = tf.stack(input_patches, axis=-1) return layer def max_pool4d_stacked(input, ksize, strides, padding): - '''max_pool4d equivalent + """max_pool4d equivalent Equivalent to tensorflows max_pool3d, but in 4D. @@ -271,30 +278,35 @@ def max_pool4d_stacked(input, ksize, strides, padding): ------ ValueError Description - ''' + """ if ksize[4] != 1: - raise ValueError("max_pool4d_stacked does not yet support " - "pooling in t dimension.") + raise ValueError( + "max_pool4d_stacked does not yet support " + "pooling in t dimension." + ) # unpack along t dimension tensors_t = tf.unstack(input, axis=4) - len_ts = ksize[4] size_of_t_dim = len(tensors_t) # loop over all z_j in t result_t = [] for i in range(0, size_of_t_dim, strides[4]): - result_t.append(tf.nn.max_pool3d(input=tensors_t[i], - ksize=ksize[:4]+ksize[5:], - strides=strides[:4]+strides[5:], - padding=padding)) + result_t.append( + tf.nn.max_pool3d( + input=tensors_t[i], + ksize=ksize[:4] + ksize[5:], + strides=strides[:4] + strides[5:], + padding=padding, + ) + ) # stack together return tf.stack(result_t, axis=4) def avg_pool4d_stacked(input, ksize, strides, padding): - '''avg_pool4d equivalent + """avg_pool4d equivalent Equivalent to tensorflows avg_pool3d, but in 4D. This method is slightly slower, but appears to use less vram. @@ -321,7 +333,7 @@ def avg_pool4d_stacked(input, ksize, strides, padding): Returns ------- A Tensor. Has the same type as input.The average pooled output tensor. - ''' + """ # unpack along t dimension tensors_t = tf.unstack(input, axis=4) @@ -331,21 +343,28 @@ def avg_pool4d_stacked(input, ksize, strides, padding): if len_ts % 2 == 1: # uneven filter size: same size to left and right - filter_l = int(len_ts/2) - filter_r = int(len_ts/2) + filter_l = int(len_ts / 2) + filter_r = int(len_ts / 2) else: # even filter size: one more to right - filter_l = int(len_ts/2) - 1 - filter_r = int(len_ts/2) + filter_l = int(len_ts / 2) - 1 + filter_r = int(len_ts / 2) # The start index is important for strides # The strides start with the first element # that works and is VALID: start_index = 0 - if padding == 'VALID': + if padding == "VALID": for i in range(size_of_t_dim): - if len(range(max(i - filter_l, 0), min(i + filter_r+1, - size_of_t_dim))) == len_ts: + if ( + len( + range( + max(i - filter_l, 0), + min(i + filter_r + 1, size_of_t_dim), + ) + ) + == len_ts + ): # we found the first index that doesn't need padding break start_index = i @@ -354,47 +373,52 @@ def avg_pool4d_stacked(input, ksize, strides, padding): result_t = [] for i in range(start_index, size_of_t_dim, strides[4]): - if padding == 'VALID': - # Get indices z_s - indices_t_s = range(max(i - filter_l, 0), - min(i + filter_r+1, size_of_t_dim)) - - # check if Padding = 'VALID' - if len(indices_t_s) == len_ts: - - tensors_t_averaged = [] - # sum over all remaining index_z_i in indices_t_s - for j, index_z_i in enumerate(indices_t_s): - tensors_t_averaged.append( - tf.nn.avg_pool3d( - input=tensors_t[index_z_i], - ksize=ksize[:4]+ksize[5:], - strides=strides[:4]+strides[5:], - padding=padding) - ) - avg_tensors_t_s = tf.add_n(tensors_t_averaged) / len_ts - - # put together - result_t.append(avg_tensors_t_s) - - elif padding == 'SAME': + if padding == "VALID": + # Get indices z_s + indices_t_s = range( + max(i - filter_l, 0), min(i + filter_r + 1, size_of_t_dim) + ) + + # check if Padding = 'VALID' + if len(indices_t_s) == len_ts: + tensors_t_averaged = [] - for kernel_j, j in enumerate(range(i - filter_l, - (i + 1) + filter_r)): - # we can just leave out the invalid t coordinates - # since they will be padded with 0's and therfore - # don't contribute to the sum - if 0 <= j < size_of_t_dim: - tensors_t_averaged.append( - tf.nn.avg_pool3d(input=tensors_t[j], - ksize=ksize[:4]+ksize[5:], - strides=strides[:4]+strides[5:], - padding=padding) - ) + # sum over all remaining index_z_i in indices_t_s + for j, index_z_i in enumerate(indices_t_s): + tensors_t_averaged.append( + tf.nn.avg_pool3d( + input=tensors_t[index_z_i], + ksize=ksize[:4] + ksize[5:], + strides=strides[:4] + strides[5:], + padding=padding, + ) + ) avg_tensors_t_s = tf.add_n(tensors_t_averaged) / len_ts # put together result_t.append(avg_tensors_t_s) + elif padding == "SAME": + tensors_t_averaged = [] + for kernel_j, j in enumerate( + range(i - filter_l, (i + 1) + filter_r) + ): + # we can just leave out the invalid t coordinates + # since they will be padded with 0's and therefore + # don't contribute to the sum + if 0 <= j < size_of_t_dim: + tensors_t_averaged.append( + tf.nn.avg_pool3d( + input=tensors_t[j], + ksize=ksize[:4] + ksize[5:], + strides=strides[:4] + strides[5:], + padding=padding, + ) + ) + avg_tensors_t_s = tf.add_n(tensors_t_averaged) / len_ts + + # put together + result_t.append(avg_tensors_t_s) + # stack together return tf.stack(result_t, axis=4) diff --git a/tfscripts/compat/v1/utils.py b/tfscripts/compat/v1/utils.py index 2d83d5d..68349be 100644 --- a/tfscripts/compat/v1/utils.py +++ b/tfscripts/compat/v1/utils.py @@ -1,6 +1,6 @@ -''' +""" Some utility functions for tfscripts -''' +""" from __future__ import division, print_function @@ -19,7 +19,7 @@ def count_parameters(var_list=None): var_list : None, optional If a var_list (list of tensors) is given, the number of parameters is calculated. - If var_list is None, all trainable paramters in the current graph + If var_list is None, all trainable parameters in the current graph are counted Returns @@ -33,7 +33,7 @@ def count_parameters(var_list=None): def get_angle(vec1, vec2, dtype=FLOAT_PRECISION): - """ Get the opening angle between two direction vectors. + """Get the opening angle between two direction vectors. vec1/2 : shape: [?,3] or [3] https://www.cs.berkeley.edu/~wkahan/Mindless.pdf @@ -55,10 +55,12 @@ def get_angle(vec1, vec2, dtype=FLOAT_PRECISION): Description """ - assert vec1.get_shape().as_list()[-1] == 3, \ - "Expect shape [?,3] or [3], but got {!r}".format(vec1.get_shape()) - assert vec2.get_shape().as_list()[-1] == 3, \ - "Expect shape [?,3] or [3], but got {!r}".format(vec2.get_shape()) + assert ( + vec1.get_shape().as_list()[-1] == 3 + ), "Expect shape [?,3] or [3], but got {!r}".format(vec1.get_shape()) + assert ( + vec2.get_shape().as_list()[-1] == 3 + ), "Expect shape [?,3] or [3], but got {!r}".format(vec2.get_shape()) norm1 = tf.norm(tensor=vec1, axis=-1, keepdims=True) norm2 = tf.norm(tensor=vec2, axis=-1, keepdims=True) @@ -68,16 +70,21 @@ def get_angle(vec1, vec2, dtype=FLOAT_PRECISION): tmp3 = tf.norm(tensor=tmp1 - tmp2, axis=-1) tmp4 = tf.norm(tensor=tmp1 + tmp2, axis=-1) - theta = 2*tf.atan2(tmp3, tmp4) + theta = 2 * tf.atan2(tmp3, tmp4) return theta -def polynomial_interpolation(x, x_ref_min, x_ref_max, y_ref, - polynomial_order=2, - fill_value=None, - axis=-1, - equidistant_query_points=False, - dtype=FLOAT_PRECISION): +def polynomial_interpolation( + x, + x_ref_min, + x_ref_max, + y_ref, + polynomial_order=2, + fill_value=None, + axis=-1, + equidistant_query_points=False, + dtype=FLOAT_PRECISION, +): """Performs a 1-dimensional polynomial interpolation of degree 1 or 2 along the specified axis. @@ -131,8 +138,7 @@ def polynomial_interpolation(x, x_ref_min, x_ref_max, y_ref, """ if axis != -1: # Transpose if necessary: last axis corresponds to interp points - perm = [i for i in range(len(y_ref.get_shape())) - if i != axis] + [axis] + perm = [i for i in range(len(y_ref.get_shape())) if i != axis] + [axis] y_ref = tf.transpose(a=y_ref, perm=perm) x = tf.transpose(a=x, perm=perm) @@ -143,15 +149,18 @@ def polynomial_interpolation(x, x_ref_min, x_ref_max, y_ref, # sanity checks to make sure shapes match if x_shape[0] != y_shape[0]: - assert x_shape[0] is None or y_shape[0] is None, '{!r} != {!r}'.format( - x_shape[0], y_shape[0]) - assert x_shape[1:-1] == y_shape[1:-1], '{!r} != {!r}'.format( - x_shape[:-1], y_shape[:-1]) + assert x_shape[0] is None or y_shape[0] is None, "{!r} != {!r}".format( + x_shape[0], y_shape[0] + ) + assert x_shape[1:-1] == y_shape[1:-1], "{!r} != {!r}".format( + x_shape[:-1], y_shape[:-1] + ) # We can take advantage of equidistant binning to compute indices bin_width = (x_ref_max - x_ref_min) / nbins - indices = tf.histogram_fixed_width_bins(x, [x_ref_min, x_ref_max], - nbins=nbins) + indices = tf.histogram_fixed_width_bins( + x, [x_ref_min, x_ref_max], nbins=nbins + ) lower_boundary = indices < 1 upper_boundary = indices >= nbins - 1 @@ -168,13 +177,17 @@ def polynomial_interpolation(x, x_ref_min, x_ref_max, y_ref, if y_shape[0] is None or x_shape[0] is None: # Need to obtain y_ref shape dynamically in this case index_offset = tf.tile( - tf.range(tf.math.reduce_prod( - input_tensor=tf.shape(input=y_ref)[:-1])) * nbins, - tf.expand_dims(x_shape[-1], axis=-1)) + tf.range( + tf.math.reduce_prod(input_tensor=tf.shape(input=y_ref)[:-1]) + ) + * nbins, + tf.expand_dims(x_shape[-1], axis=-1), + ) index_offset = tf.reshape(index_offset, [-1] + x_shape[1:]) else: - index_offset = np.tile(np.arange(np.prod(y_shape[:-1])) * nbins, - x_shape[-1]) + index_offset = np.tile( + np.arange(np.prod(y_shape[:-1])) * nbins, x_shape[-1] + ) index_offset = np.reshape(index_offset, x_shape) y_ref_flat = tf.reshape(y_ref, [-1]) @@ -193,9 +206,9 @@ def polynomial_interpolation(x, x_ref_min, x_ref_max, y_ref, L_2 = (x - x_0) * (x - x_1) / (2 * bin_width_squared) else: - L_0 = (x - x_1) * (x - x_2) / ((x_0 - x_1)*(x_0 - x_2)) - L_1 = (x - x_0) * (x - x_2) / ((x_1 - x_0)*(x_1 - x_2)) - L_2 = (x - x_0) * (x - x_1) / ((x_2 - x_0)*(x_2 - x_1)) + L_0 = (x - x_1) * (x - x_2) / ((x_0 - x_1) * (x_0 - x_2)) + L_1 = (x - x_0) * (x - x_2) / ((x_1 - x_0) * (x_1 - x_2)) + L_2 = (x - x_0) * (x - x_1) / ((x_2 - x_0) * (x_2 - x_1)) result = y_0 * L_0 + y_1 * L_1 + y_2 * L_2 @@ -206,22 +219,27 @@ def polynomial_interpolation(x, x_ref_min, x_ref_max, y_ref, result = y_1 + (x - x_1) / (x_2 - x_1) * (y_2 - y_1) else: - raise ValueError('Interpolation order {!r} not supported'.format( - polynomial_order)) + raise ValueError( + "Interpolation order {!r} not supported".format(polynomial_order) + ) if fill_value is None: - result = tf.where(lower_boundary, - tf.zeros_like(result) - + tf.expand_dims(y_ref[..., 0], axis=-1), - result) - result = tf.where(upper_boundary, - tf.zeros_like(result) - + tf.expand_dims(y_ref[..., -1], axis=-1), - result) + result = tf.where( + lower_boundary, + tf.zeros_like(result) + tf.expand_dims(y_ref[..., 0], axis=-1), + result, + ) + result = tf.where( + upper_boundary, + tf.zeros_like(result) + tf.expand_dims(y_ref[..., -1], axis=-1), + result, + ) else: - result = tf.where(tf.logical_or(lower_boundary, upper_boundary), - tf.zeros_like(result) + fill_value, - result) + result = tf.where( + tf.logical_or(lower_boundary, upper_boundary), + tf.zeros_like(result) + fill_value, + result, + ) if axis != -1: # Transpose back if necessary diff --git a/tfscripts/compat/v1/weights.py b/tfscripts/compat/v1/weights.py index ac69499..f7f7c51 100644 --- a/tfscripts/compat/v1/weights.py +++ b/tfscripts/compat/v1/weights.py @@ -1,8 +1,8 @@ -''' +""" Core functions of tfscripts: Create weights and biases -''' +""" from __future__ import division, print_function @@ -31,14 +31,17 @@ def new_weights(shape, stddev=1.0, name="weights"): tf.Tensor A tensor with the weights. """ - return tf.Variable(tf.random.truncated_normal( - shape, stddev=stddev, dtype=FLOAT_PRECISION), - name=name, - dtype=FLOAT_PRECISION) + return tf.Variable( + tf.random.truncated_normal( + shape, stddev=stddev, dtype=FLOAT_PRECISION + ), + name=name, + dtype=FLOAT_PRECISION, + ) def new_kernel_weights(shape, stddev=0.01, name="weights"): - ''' + """ Get weights for a convolutional kernel. The weights will be initialised, so that convolution performs matrix multiplication over a single pixel. @@ -59,11 +62,12 @@ def new_kernel_weights(shape, stddev=0.01, name="weights"): tf.Tensor A tensor with the weights. - ''' + """ weight_initialisation = np.zeros(shape) spatial_shape = shape[:-2] - middle_index = [slice((dim - 1) // 2, - (dim - 1) // 2 + 1) for dim in spatial_shape] + middle_index = [ + slice((dim - 1) // 2, (dim - 1) // 2 + 1) for dim in spatial_shape + ] # Set value in the middle to 1 and divide by the sqrt of # num_inputs to maintain normal distributed output. @@ -71,14 +75,14 @@ def new_kernel_weights(shape, stddev=0.01, name="weights"): weight_initialisation[middle_index] = 1.0 / np.sqrt(shape[-2]) # add random noise to break symmetry - weight_initialisation += np.random.normal(size=shape, loc=0.0, - scale=stddev) + weight_initialisation += np.random.normal( + size=shape, loc=0.0, scale=stddev + ) - return tf.Variable(weight_initialisation, - name=name, dtype=FLOAT_PRECISION) + return tf.Variable(weight_initialisation, name=name, dtype=FLOAT_PRECISION) -def new_biases(length, stddev=1.0, name='biases'): +def new_biases(length, stddev=1.0, name="biases"): """Get new biases. Parameters @@ -93,21 +97,23 @@ def new_biases(length, stddev=1.0, name='biases'): tf.Tensor A tensor with the biases. """ - return tf.Variable(tf.random.truncated_normal(shape=[length], - stddev=stddev, - dtype=FLOAT_PRECISION), - name=name, dtype=FLOAT_PRECISION) + return tf.Variable( + tf.random.truncated_normal( + shape=[length], stddev=stddev, dtype=FLOAT_PRECISION + ), + name=name, + dtype=FLOAT_PRECISION, + ) # return tf.Variable(tf.random_normal(shape=[length], # stddev=2.0/length, # dtype=FLOAT_PRECISION), # name=name, dtype=FLOAT_PRECISION) -def create_conv_nd_layers_weights(num_input_channels, - filter_size_list, - num_filters_list, - name='conv_{}d'): - '''Create weights and biases for conv 3d layers +def create_conv_nd_layers_weights( + num_input_channels, filter_size_list, num_filters_list, name="conv_{}d" +): + """Create weights and biases for conv 3d layers Parameters ---------- @@ -135,23 +141,24 @@ def create_conv_nd_layers_weights(num_input_channels, ------- list of tf.Tensor, list of tf.Tensor Returns the list of weight and bias tensors for each layer - ''' + """ num_dims = len(filter_size_list[0]) name = name.format(num_dims) weights_list = [] biases_list = [] - for i, (filter_size, num_filters) in enumerate(zip(filter_size_list, - num_filters_list)): + for i, (filter_size, num_filters) in enumerate( + zip(filter_size_list, num_filters_list) + ): # Shape of the filter-weights for the convolution. shape = list(filter_size) + [num_input_channels, num_filters] if num_dims == 1: shape = shape.insert(1, 1) - weight_name = 'weights_{}_{:03d}'.format(name, i) - bias_name = 'biases_{}_{:03d}'.format(name, i) + weight_name = "weights_{}_{:03d}".format(name, i) + bias_name = "biases_{}_{:03d}".format(name, i) # weights_list.append(new_kernel_weights(shape=shape, name=weight_name)) weights_list.append(new_weights(shape=shape, name=weight_name)) @@ -163,11 +170,10 @@ def create_conv_nd_layers_weights(num_input_channels, return weights_list, biases_list -def create_fc_layers_weights(num_inputs, - fc_sizes, - max_out_size_list=None, - name='fc'): - ''' +def create_fc_layers_weights( + num_inputs, fc_sizes, max_out_size_list=None, name="fc" +): + """ Create weights and biases for fully connected layers @@ -187,21 +193,23 @@ def create_fc_layers_weights(num_inputs, ------- list of tf.Tensor, list of tf.Tensor Returns the list of weight and bias tensors for each layer - ''' + """ # create max out array if max_out_size_list is None: max_out_size_list = [None for i in range(len(fc_sizes))] weights_list = [] biases_list = [] - for i, (num_outputs, max_out_size) in enumerate(zip(fc_sizes, - max_out_size_list)): + for i, (num_outputs, max_out_size) in enumerate( + zip(fc_sizes, max_out_size_list) + ): - weight_name = 'weights_{}_{:03d}'.format(name, i) - bias_name = 'biases_{}_{:03d}'.format(name, i) + weight_name = "weights_{}_{:03d}".format(name, i) + bias_name = "biases_{}_{:03d}".format(name, i) - weights_list.append(new_weights(shape=[num_inputs, num_outputs], - name=weight_name)) + weights_list.append( + new_weights(shape=[num_inputs, num_outputs], name=weight_name) + ) biases_list.append(new_biases(length=num_outputs, name=bias_name)) if max_out_size is None: diff --git a/tfscripts/conv.py b/tfscripts/conv.py index 44a26e9..880cb2c 100644 --- a/tfscripts/conv.py +++ b/tfscripts/conv.py @@ -1,4 +1,4 @@ -''' +""" Conv functions for tfscripts: convolution helper functions, locally connected 1d, 2d, and 3d convolutions [tf.Modules], @@ -8,7 +8,7 @@ stacked convolution 3d and 4d -''' +""" from __future__ import division, print_function @@ -52,20 +52,20 @@ def conv_output_length(input_length, filter_size, padding, stride, dilation=1): """ if input_length is None: return None - assert padding in {'SAME', 'VALID'} + assert padding in {"SAME", "VALID"} dilated_filter_size = filter_size + (filter_size - 1) * (dilation - 1) - if padding == 'SAME': + if padding == "SAME": output_length = input_length - elif padding == 'VALID': + elif padding == "VALID": output_length = input_length - dilated_filter_size + 1 return (output_length + stride - 1) // stride def get_filter_lr(filter_size): - ''' + """ Get the number of elements left and right of the filter position. If filtersize is even, there will be one more element to the @@ -79,11 +79,11 @@ def get_filter_lr(filter_size): Returns ------- (int, int) - Number of elemnts left and right + Number of elements left and right of the filter position. - ''' + """ if filter_size % 2 == 1: # uneven filter size: same size to left and right filter_l = filter_size // 2 @@ -97,7 +97,7 @@ def get_filter_lr(filter_size): def get_start_index(input_length, filter_size, padding, stride, dilation=1): - ''' + """ Get start index for a convolution along an axis. This will be the index of the input axis for the first valid convolution position. @@ -124,7 +124,7 @@ def get_start_index(input_length, filter_size, padding, stride, dilation=1): ------ ValueError Description - ''' + """ filter_l, filter_r = get_filter_lr(filter_size) # The start index is important for strides and dilation @@ -132,11 +132,18 @@ def get_start_index(input_length, filter_size, padding, stride, dilation=1): # that works and is VALID: start_index = 0 found_valid_position = False - if padding == 'VALID': + if padding == "VALID": for i in range(input_length): - if len(range(max(i - dilation*filter_l, 0), - min(i + dilation*filter_r + 1, input_length), - dilation)) == filter_size: + if ( + len( + range( + max(i - dilation * filter_l, 0), + min(i + dilation * filter_r + 1, input_length), + dilation, + ) + ) + == filter_size + ): # we found the first index that doesn't need padding found_valid_position = True break @@ -149,7 +156,7 @@ def get_start_index(input_length, filter_size, padding, stride, dilation=1): def get_conv_indices(position, input_length, filter_size, stride, dilation=1): - ''' + """ Get indices for a convolution patch. The indices will correspond to the elements being convolved with the filter of size @@ -173,17 +180,19 @@ def get_conv_indices(position, input_length, filter_size, stride, dilation=1): ------- list of int Indices of a convolution patch - ''' + """ filter_l, filter_r = get_filter_lr(filter_size) - indices = range(max(position - dilation*filter_l, 0), - min(position + dilation*filter_r + 1, input_length), - dilation) + indices = range( + max(position - dilation * filter_l, 0), + min(position + dilation * filter_r + 1, input_length), + dilation, + ) return indices def get_conv_slice(position, input_length, filter_size, stride, dilation=1): - ''' + """ Get slice for a convolution patch. The slice will correspond to the elements being convolved with the filter of size @@ -209,36 +218,37 @@ def get_conv_slice(position, input_length, filter_size, stride, dilation=1): slice of input being convolved with the filter at 'position'. And number of elements cropped at either end, which are needed for padding - ''' + """ filter_l, filter_r = get_filter_lr(filter_size) - min_index = position - dilation*filter_l - max_index = position + dilation*filter_r + 1 + min_index = position - dilation * filter_l + max_index = position + dilation * filter_r + 1 - conv_slice = slice(max(min_index, 0), - min(max_index, input_length), - dilation) + conv_slice = slice( + max(min_index, 0), min(max_index, input_length), dilation + ) - padding_left = int(np.ceil(max(- min_index, 0) / dilation)) + padding_left = int(np.ceil(max(-min_index, 0) / dilation)) padding_right = int(np.ceil(max(max_index - input_length, 0) / dilation)) return conv_slice, (padding_left, padding_right) class LocallyConnected1d(tf.Module): - """Like conv1d, but doesn't share weights. - """ - - def __init__(self, - input_shape, - num_outputs, - filter_size, - kernel=None, - strides= [1], - padding='SAME', - dilation_rate=None, - float_precision=FLOAT_PRECISION, - name=None): + """Like conv1d, but doesn't share weights.""" + + def __init__( + self, + input_shape, + num_outputs, + filter_size, + kernel=None, + strides=[1], + padding="SAME", + dilation_rate=None, + float_precision=FLOAT_PRECISION, + name=None, + ): """Initialize object Parameters @@ -285,32 +295,35 @@ def __init__(self, input_shape = input_shape.as_list() # sanity checks - msg = 'Filter size must be of shape [x], but is {!r}' + msg = "Filter size must be of shape [x], but is {!r}" assert len(filter_size) == 1, msg.format(filter_size) - msg = 'Filter sizes must be greater than 0, but are: {!r}' + msg = "Filter sizes must be greater than 0, but are: {!r}" assert np.prod(filter_size) > 0, msg.format(filter_size) - msg = 'Shape is expected to be of length 3, but is {!r}' + msg = "Shape is expected to be of length 3, but is {!r}" assert len(input_shape) == 3, msg.format(input_shape) # calculate output shape output_shape = np.empty(3, dtype=int) for i in range(1): - output_shape[i+1] = conv_output_length( - input_length=input_shape[i + 1], - filter_size=filter_size[i], - padding=padding, - stride=strides[i], - dilation=dilation_rate[i]) + output_shape[i + 1] = conv_output_length( + input_length=input_shape[i + 1], + filter_size=filter_size[i], + padding=padding, + stride=strides[i], + dilation=dilation_rate[i], + ) output_shape[0] = -1 output_shape[2] = num_outputs num_inputs = input_shape[2] - kernel_shape = (np.prod(output_shape[1:-1]), - np.prod(filter_size) * num_inputs, - num_outputs) + kernel_shape = ( + np.prod(output_shape[1:-1]), + np.prod(filter_size) * num_inputs, + num_outputs, + ) # ------------------ # Create Kernel @@ -321,13 +334,15 @@ def __init__(self, kernel = new_locally_connected_weights( shape=input_shape[1:] + [num_outputs], shared_axes=[0], - float_precision=float_precision) + float_precision=float_precision, + ) else: kernel = new_locally_connected_weights( shape=kernel_shape, shared_axes=[0], - float_precision=float_precision) + float_precision=float_precision, + ) self.output_shape = output_shape self.num_outputs = num_outputs @@ -361,18 +376,24 @@ def __call__(self, inputs): # fast shortcut if list(self.filter_size) == [1]: output = tf.reduce_sum( - input_tensor=tf.expand_dims(inputs, axis=3) * self.kernel, axis=2) + input_tensor=tf.expand_dims(inputs, axis=3) * self.kernel, + axis=2, + ) return output # ------------------ # get slices # ------------------ - start_indices = [get_start_index(input_length=input_shape[i + 1], - filter_size=self.filter_size[i], - padding=self.padding, - stride=self.strides[i], - dilation=self.dilation_rate[i]) - for i in range(1)] + start_indices = [ + get_start_index( + input_length=input_shape[i + 1], + filter_size=self.filter_size[i], + padding=self.padding, + stride=self.strides[i], + dilation=self.dilation_rate[i], + ) + for i in range(1) + ] input_patches = [] # --------------------------- @@ -382,13 +403,14 @@ def __call__(self, inputs): # get slice for patch along x-axis slice_x, padding_x = get_conv_slice( - x, - input_length=input_shape[1], - filter_size=self.filter_size[0], - stride=self.strides[0], - dilation=self.dilation_rate[0]) - - if self.padding == 'VALID' and padding_x != (0): + x, + input_length=input_shape[1], + filter_size=self.filter_size[0], + stride=self.strides[0], + dilation=self.dilation_rate[0], + ) + + if self.padding == "VALID" and padding_x != (0): # skip this x position, since it does not provide # a valid patch for padding 'VALID' continue @@ -398,19 +420,21 @@ def __call__(self, inputs): # ------------------------------------------ input_patch = inputs[:, slice_x, :] - if self.padding == 'SAME': + if self.padding == "SAME": # pad with zeros paddings = [(0, 0), padding_x, (0, 0)] if paddings != [(0, 0), (0, 0), (0, 0), (0, 0), (0, 0)]: - input_patch = tf.pad(tensor=input_patch, - paddings=paddings, - mode='CONSTANT', - ) + input_patch = tf.pad( + tensor=input_patch, + paddings=paddings, + mode="CONSTANT", + ) # reshape input_patch = tf.reshape( - input_patch, - [-1, 1, np.prod(self.filter_size)*self.num_inputs, 1]) + input_patch, + [-1, 1, np.prod(self.filter_size) * self.num_inputs, 1], + ) # append to list input_patches.append(input_patch) @@ -429,19 +453,20 @@ def __call__(self, inputs): class LocallyConnected2d(tf.Module): - """Like conv2d, but doesn't share weights. - """ - - def __init__(self, - input_shape, - num_outputs, - filter_size, - kernel=None, - strides=[1, 1], - padding='SAME', - dilation_rate=None, - float_precision=FLOAT_PRECISION, - name=None): + """Like conv2d, but doesn't share weights.""" + + def __init__( + self, + input_shape, + num_outputs, + filter_size, + kernel=None, + strides=[1, 1], + padding="SAME", + dilation_rate=None, + float_precision=FLOAT_PRECISION, + name=None, + ): """Initialize object Parameters @@ -488,32 +513,35 @@ def __init__(self, input_shape = input_shape.as_list() # sanity checks - msg = 'Filter size must be of shape [x,y], but is {!r}' + msg = "Filter size must be of shape [x,y], but is {!r}" assert len(filter_size) == 2, msg.format(filter_size) - msg = 'Filter sizes must be greater than 0, but are: {!r}' + msg = "Filter sizes must be greater than 0, but are: {!r}" assert np.prod(filter_size) > 0, msg.format(filter_size) - msg = 'Shape is expected to be of length 4, but is {!r}' + msg = "Shape is expected to be of length 4, but is {!r}" assert len(input_shape) == 4, msg.format(input_shape) # calculate output shape output_shape = np.empty(4, dtype=int) for i in range(2): - output_shape[i+1] = conv_output_length( - input_length=input_shape[i + 1], - filter_size=filter_size[i], - padding=padding, - stride=strides[i], - dilation=dilation_rate[i]) + output_shape[i + 1] = conv_output_length( + input_length=input_shape[i + 1], + filter_size=filter_size[i], + padding=padding, + stride=strides[i], + dilation=dilation_rate[i], + ) output_shape[0] = -1 output_shape[3] = num_outputs num_inputs = input_shape[3] - kernel_shape = (np.prod(output_shape[1:-1]), - np.prod(filter_size) * num_inputs, - num_outputs) + kernel_shape = ( + np.prod(output_shape[1:-1]), + np.prod(filter_size) * num_inputs, + num_outputs, + ) # ------------------ # Create Kernel @@ -524,13 +552,15 @@ def __init__(self, kernel = new_locally_connected_weights( shape=input_shape[1:] + [num_outputs], shared_axes=[0, 1], - float_precision=float_precision) + float_precision=float_precision, + ) else: kernel = new_locally_connected_weights( shape=kernel_shape, shared_axes=[0], - float_precision=float_precision) + float_precision=float_precision, + ) self.output_shape = output_shape self.num_outputs = num_outputs @@ -565,18 +595,23 @@ def __call__(self, inputs): if list(self.filter_size) == [1, 1]: output = tf.reduce_sum( input_tensor=tf.expand_dims(inputs, axis=4) * self.kernel, - axis=3) + axis=3, + ) return output # ------------------ # get slices # ------------------ - start_indices = [get_start_index(input_length=input_shape[i + 1], - filter_size=self.filter_size[i], - padding=self.padding, - stride=self.strides[i], - dilation=self.dilation_rate[i]) - for i in range(2)] + start_indices = [ + get_start_index( + input_length=input_shape[i + 1], + filter_size=self.filter_size[i], + padding=self.padding, + stride=self.strides[i], + dilation=self.dilation_rate[i], + ) + for i in range(2) + ] input_patches = [] # --------------------------- @@ -586,13 +621,14 @@ def __call__(self, inputs): # get slice for patch along x-axis slice_x, padding_x = get_conv_slice( - x, - input_length=input_shape[1], - filter_size=self.filter_size[0], - stride=self.strides[0], - dilation=self.dilation_rate[0]) - - if self.padding == 'VALID' and padding_x != (0, 0): + x, + input_length=input_shape[1], + filter_size=self.filter_size[0], + stride=self.strides[0], + dilation=self.dilation_rate[0], + ) + + if self.padding == "VALID" and padding_x != (0, 0): # skip this x position, since it does not provide # a valid patch for padding 'VALID' continue @@ -604,19 +640,20 @@ def __call__(self, inputs): # get indices for patch along y-axis slice_y, padding_y = get_conv_slice( - y, - input_length=input_shape[2], - filter_size=self.filter_size[1], - stride=self.strides[1], - dilation=self.dilation_rate[1]) - - if self.padding == 'VALID' and padding_y != (0, 0): + y, + input_length=input_shape[2], + filter_size=self.filter_size[1], + stride=self.strides[1], + dilation=self.dilation_rate[1], + ) + + if self.padding == "VALID" and padding_y != (0, 0): # skip this y position, since it does not provide # a valid patch for padding 'VALID' continue # At this point, slice_x/y either correspond - # to a vaild patch, or padding is 'SAME' + # to a valid patch, or padding is 'SAME' # Now we need to pick slice and add it to # input patches. These will later be convolved # with the kernel. @@ -626,19 +663,21 @@ def __call__(self, inputs): # ------------------------------------------ input_patch = inputs[:, slice_x, slice_y, :] - if self.padding == 'SAME': + if self.padding == "SAME": # pad with zeros paddings = [(0, 0), padding_x, padding_y, (0, 0)] if paddings != [(0, 0), (0, 0), (0, 0), (0, 0), (0, 0)]: - input_patch = tf.pad(tensor=input_patch, - paddings=paddings, - mode='CONSTANT', - ) + input_patch = tf.pad( + tensor=input_patch, + paddings=paddings, + mode="CONSTANT", + ) # reshape input_patch = tf.reshape( - input_patch, - [-1, 1, np.prod(self.filter_size)*self.num_inputs, 1]) + input_patch, + [-1, 1, np.prod(self.filter_size) * self.num_inputs, 1], + ) # append to list input_patches.append(input_patch) @@ -657,19 +696,20 @@ def __call__(self, inputs): class LocallyConnected3d(tf.Module): - """Like conv3d, but doesn't share weights. - """ - - def __init__(self, - input_shape, - num_outputs, - filter_size, - kernel=None, - strides=[1, 1, 1], - padding='SAME', - dilation_rate=None, - float_precision=FLOAT_PRECISION, - name=None): + """Like conv3d, but doesn't share weights.""" + + def __init__( + self, + input_shape, + num_outputs, + filter_size, + kernel=None, + strides=[1, 1, 1], + padding="SAME", + dilation_rate=None, + float_precision=FLOAT_PRECISION, + name=None, + ): """Initialize object Parameters @@ -708,32 +748,35 @@ def __init__(self, input_shape = input_shape.as_list() # sanity checks - msg = 'Filter size must be of shape [x,y,z], but is {!r}' + msg = "Filter size must be of shape [x,y,z], but is {!r}" assert len(filter_size) == 3, msg.format(filter_size) - msg = 'Filter sizes must be greater than 0, but are: {!r}' + msg = "Filter sizes must be greater than 0, but are: {!r}" assert np.prod(filter_size) > 0, msg.format(filter_size) - msg = 'Shape is expected to be of length 5, but is {!r}' + msg = "Shape is expected to be of length 5, but is {!r}" assert len(input_shape) == 5, msg.format(input_shape) # calculate output shape output_shape = np.empty(5, dtype=int) for i in range(3): - output_shape[i+1] = conv_output_length( - input_length=input_shape[i + 1], - filter_size=filter_size[i], - padding=padding, - stride=strides[i], - dilation=dilation_rate[i]) + output_shape[i + 1] = conv_output_length( + input_length=input_shape[i + 1], + filter_size=filter_size[i], + padding=padding, + stride=strides[i], + dilation=dilation_rate[i], + ) output_shape[0] = -1 output_shape[4] = num_outputs num_inputs = input_shape[4] - kernel_shape = (np.prod(output_shape[1:-1]), - np.prod(filter_size) * num_inputs, - num_outputs) + kernel_shape = ( + np.prod(output_shape[1:-1]), + np.prod(filter_size) * num_inputs, + num_outputs, + ) # ------------------ # Create Kernel @@ -744,13 +787,15 @@ def __init__(self, kernel = new_locally_connected_weights( shape=input_shape[1:] + [num_outputs], shared_axes=[0, 1, 2], - float_precision=float_precision) + float_precision=float_precision, + ) else: kernel = new_locally_connected_weights( shape=kernel_shape, shared_axes=[0], - float_precision=float_precision) + float_precision=float_precision, + ) self.output_shape = output_shape self.num_outputs = num_outputs @@ -784,18 +829,24 @@ def __call__(self, inputs): # fast shortcut if list(self.filter_size) == [1, 1, 1]: output = tf.reduce_sum( - input_tensor=tf.expand_dims(inputs, axis=5) * self.kernel, axis=4) + input_tensor=tf.expand_dims(inputs, axis=5) * self.kernel, + axis=4, + ) return output # ------------------ # get slices # ------------------ - start_indices = [get_start_index(input_length=input_shape[i + 1], - filter_size=self.filter_size[i], - padding=self.padding, - stride=self.strides[i], - dilation=self.dilation_rate[i]) - for i in range(3)] + start_indices = [ + get_start_index( + input_length=input_shape[i + 1], + filter_size=self.filter_size[i], + padding=self.padding, + stride=self.strides[i], + dilation=self.dilation_rate[i], + ) + for i in range(3) + ] input_patches = [] # --------------------------- @@ -805,13 +856,14 @@ def __call__(self, inputs): # get slice for patch along x-axis slice_x, padding_x = get_conv_slice( - x, - input_length=input_shape[1], - filter_size=self.filter_size[0], - stride=self.strides[0], - dilation=self.dilation_rate[0]) - - if self.padding == 'VALID' and padding_x != (0, 0): + x, + input_length=input_shape[1], + filter_size=self.filter_size[0], + stride=self.strides[0], + dilation=self.dilation_rate[0], + ) + + if self.padding == "VALID" and padding_x != (0, 0): # skip this x position, since it does not provide # a valid patch for padding 'VALID' continue @@ -823,13 +875,14 @@ def __call__(self, inputs): # get indices for patch along y-axis slice_y, padding_y = get_conv_slice( - y, - input_length=input_shape[2], - filter_size=self.filter_size[1], - stride=self.strides[1], - dilation=self.dilation_rate[1]) - - if self.padding == 'VALID' and padding_y != (0, 0): + y, + input_length=input_shape[2], + filter_size=self.filter_size[1], + stride=self.strides[1], + dilation=self.dilation_rate[1], + ) + + if self.padding == "VALID" and padding_y != (0, 0): # skip this y position, since it does not provide # a valid patch for padding 'VALID' continue @@ -837,24 +890,26 @@ def __call__(self, inputs): # --------------------------- # loop over all z positions # --------------------------- - for z in range(start_indices[2], input_shape[3], - self.strides[2]): + for z in range( + start_indices[2], input_shape[3], self.strides[2] + ): # get indices for patch along y-axis slice_z, padding_z = get_conv_slice( - z, - input_length=input_shape[3], - filter_size=self.filter_size[2], - stride=self.strides[2], - dilation=self.dilation_rate[2]) - - if self.padding == 'VALID' and padding_z != (0, 0): + z, + input_length=input_shape[3], + filter_size=self.filter_size[2], + stride=self.strides[2], + dilation=self.dilation_rate[2], + ) + + if self.padding == "VALID" and padding_z != (0, 0): # skip this z position, since it does not provide # a valid patch for padding 'VALID' continue # At this point, slice_x/y/z either correspond - # to a vaild patch, or padding is 'SAME' + # to a valid patch, or padding is 'SAME' # Now we need to pick slice and add it to # input patches. These will later be convolved # with the kernel. @@ -864,21 +919,38 @@ def __call__(self, inputs): # ------------------------------------------ input_patch = inputs[:, slice_x, slice_y, slice_z, :] - if self.padding == 'SAME': + if self.padding == "SAME": # pad with zeros - paddings = [(0, 0), padding_x, padding_y, - padding_z, (0, 0)] - if paddings != [(0, 0), (0, 0), (0, 0), - (0, 0), (0, 0)]: - input_patch = tf.pad(tensor=input_patch, - paddings=paddings, - mode='CONSTANT', - ) + paddings = [ + (0, 0), + padding_x, + padding_y, + padding_z, + (0, 0), + ] + if paddings != [ + (0, 0), + (0, 0), + (0, 0), + (0, 0), + (0, 0), + ]: + input_patch = tf.pad( + tensor=input_patch, + paddings=paddings, + mode="CONSTANT", + ) # reshape input_patch = tf.reshape( input_patch, - [-1, 1, np.prod(self.filter_size)*self.num_inputs, 1]) + [ + -1, + 1, + np.prod(self.filter_size) * self.num_inputs, + 1, + ], + ) # append to list input_patches.append(input_patch) @@ -896,22 +968,23 @@ def __call__(self, inputs): return output -def local_translational3d_trafo(input, - num_outputs, - filter_size, - fcn=None, - weights=None, - strides=[1, 1, 1], - padding='SAME', - dilation_rate=None, - is_training=True, - ): - ''' +def local_translational3d_trafo( + input, + num_outputs, + filter_size, + fcn=None, + weights=None, + strides=[1, 1, 1], + padding="SAME", + dilation_rate=None, + is_training=True, +): + """ Applies a transformation defined by the callable fcn(input_patch) to the input_patch. Returns the output of fcn. - Similiar to conv3d, but instead of a convolution, the transformation + Similar to conv3d, but instead of a convolution, the transformation defined by fcn is performed. The transformation is learnable and shared - accross input_patches similar to how the convolutional kernel is sahred. + across input_patches similar to how the convolutional kernel is sahred. Parameters ---------- @@ -952,7 +1025,7 @@ def local_translational3d_trafo(input, ------- 2 Tensors: result and kernels. Have the same type as input. - ''' + """ if dilation_rate is None: dilation_rate = [1, 1, 1] @@ -963,34 +1036,38 @@ def local_translational3d_trafo(input, input_shape = input.get_shape().as_list() # sanity checks - msg = 'Filter size must be of shape [x,y,z], but is {!r}' + msg = "Filter size must be of shape [x,y,z], but is {!r}" assert len(filter_size) == 3, msg.format(filter_size) - assert np.prod(filter_size) > 0, 'Filter sizes must be greater than 0' - msg = 'Shape is expected to be of length 5, but is {!r}' + assert np.prod(filter_size) > 0, "Filter sizes must be greater than 0" + msg = "Shape is expected to be of length 5, but is {!r}" assert len(input_shape) == 5, msg.format(input_shape) # calculate output shape output_shape = np.empty(5, dtype=int) for i in range(3): - output_shape[i+1] = conv_output_length(input_length=input_shape[i + 1], - filter_size=filter_size[i], - padding=padding, - stride=strides[i], - dilation=dilation_rate[i]) + output_shape[i + 1] = conv_output_length( + input_length=input_shape[i + 1], + filter_size=filter_size[i], + padding=padding, + stride=strides[i], + dilation=dilation_rate[i], + ) output_shape[0] = -1 output_shape[4] = num_outputs - num_inputs = input_shape[4] - # ------------------ # get slices # ------------------ - start_indices = [get_start_index(input_length=input_shape[i + 1], - filter_size=filter_size[i], - padding=padding, - stride=strides[i], - dilation=dilation_rate[i]) - for i in range(3)] + start_indices = [ + get_start_index( + input_length=input_shape[i + 1], + filter_size=filter_size[i], + padding=padding, + stride=strides[i], + dilation=dilation_rate[i], + ) + for i in range(3) + ] output = [] # --------------------------- @@ -999,13 +1076,15 @@ def local_translational3d_trafo(input, for x in range(start_indices[0], input_shape[1], strides[0]): # get slice for patch along x-axis - slice_x, padding_x = get_conv_slice(x, - input_length=input_shape[1], - filter_size=filter_size[0], - stride=strides[0], - dilation=dilation_rate[0]) - - if padding == 'VALID' and padding_x != (0, 0): + slice_x, padding_x = get_conv_slice( + x, + input_length=input_shape[1], + filter_size=filter_size[0], + stride=strides[0], + dilation=dilation_rate[0], + ) + + if padding == "VALID" and padding_x != (0, 0): # skip this x position, since it does not provide # a valid patch for padding 'VALID' continue @@ -1016,13 +1095,15 @@ def local_translational3d_trafo(input, for y in range(start_indices[1], input_shape[2], strides[1]): # get indices for patch along y-axis - slice_y, padding_y = get_conv_slice(y, - input_length=input_shape[2], - filter_size=filter_size[1], - stride=strides[1], - dilation=dilation_rate[1]) - - if padding == 'VALID' and padding_y != (0, 0): + slice_y, padding_y = get_conv_slice( + y, + input_length=input_shape[2], + filter_size=filter_size[1], + stride=strides[1], + dilation=dilation_rate[1], + ) + + if padding == "VALID" and padding_y != (0, 0): # skip this y position, since it does not provide # a valid patch for padding 'VALID' continue @@ -1034,19 +1115,20 @@ def local_translational3d_trafo(input, # get indices for patch along y-axis slice_z, padding_z = get_conv_slice( - z, - input_length=input_shape[3], - filter_size=filter_size[2], - stride=strides[2], - dilation=dilation_rate[2]) - - if padding == 'VALID' and padding_z != (0, 0): + z, + input_length=input_shape[3], + filter_size=filter_size[2], + stride=strides[2], + dilation=dilation_rate[2], + ) + + if padding == "VALID" and padding_z != (0, 0): # skip this z position, since it does not provide # a valid patch for padding 'VALID' continue # At this point, slice_x/y/z either correspond - # to a vaild patch, or padding is 'SAME' + # to a valid patch, or padding is 'SAME' # Now we need to pick slice and add it to # input patches. These will later be convolved # with the kernel. @@ -1055,18 +1137,24 @@ def local_translational3d_trafo(input, # Get input patch at filter position x,y,z # ------------------------------------------ # input_patch = tf.expand_dims( - # inputs[:, slice_x, slice_y, slice_z, :], 5) - input_patch = inputs[:, slice_x, slice_y, slice_z, :] + # input[:, slice_x, slice_y, slice_z, :], 5) + input_patch = input[:, slice_x, slice_y, slice_z, :] - if padding == 'SAME': + if padding == "SAME": # pad with zeros - paddings = [(0, 0), padding_x, padding_y, - padding_z, (0, 0)] + paddings = [ + (0, 0), + padding_x, + padding_y, + padding_z, + (0, 0), + ] if paddings != [(0, 0), (0, 0), (0, 0), (0, 0), (0, 0)]: - input_patch = tf.pad(tensor=input_patch, - paddings=paddings, - mode='CONSTANT', - ) + input_patch = tf.pad( + tensor=input_patch, + paddings=paddings, + mode="CONSTANT", + ) # ------------------------------ # Perform trafo on input_patch @@ -1077,7 +1165,8 @@ def local_translational3d_trafo(input, output_patch = tf.reduce_sum( input_tensor=expanded_input * weights, axis=[1, 2, 3, 4], - keepdims=False) + keepdims=False, + ) elif fcn is not None: output_patch = fcn(input_patch) @@ -1093,14 +1182,14 @@ def local_translational3d_trafo(input, def dynamic_conv( - inputs, - filters, - batch_size=None, - strides=[1, 1, 1], - padding='SAME', - dilation_rate=None, - ): - ''' + inputs, + filters, + batch_size=None, + strides=[1, 1, 1], + padding="SAME", + dilation_rate=None, +): + """ Equivalent to tf.nn.convolution, but filter has additional batch dimension. This allows the filter to be a function of some input, hence, enabling dynamic convolutions. @@ -1148,7 +1237,7 @@ def dynamic_conv( Returns ------- A Tensor. Has the same type as inputs. - ''' + """ input_shape = inputs.get_shape().as_list() filter_shape = filters.get_shape().as_list() @@ -1161,42 +1250,40 @@ def dynamic_conv( try: batch_size = dynamic_conv.BATCH_SIZE - except Exception as e: + except Exception: batch_size = inputs.get_shape().as_list()[0] - split_inputs = tf.split(inputs, - batch_size, - axis=0) - split_filters = tf.unstack(filters, - batch_size, - axis=0) + split_inputs = tf.split(inputs, batch_size, axis=0) + split_filters = tf.unstack(filters, batch_size, axis=0) output_list = [] for split_input, split_filter in zip(split_inputs, split_filters): output_list.append( - tf.nn.convolution(input=split_input, - filters=split_filter, - strides=strides, - padding=padding, - dilations=dilation_rate, - ) - ) + tf.nn.convolution( + input=split_input, + filters=split_filter, + strides=strides, + padding=padding, + dilations=dilation_rate, + ) + ) output = tf.concat(output_list, axis=0) return output -def trans3d_op(input, - num_out_channel, - filter_size, - method, - trafo=None, - filter=None, - strides=[1, 1, 1], - padding='SAME', - dilation_rate=None, - stack_axis=None, - ): - ''' +def trans3d_op( + input, + num_out_channel, + filter_size, + method, + trafo=None, + filter=None, + strides=[1, 1, 1], + padding="SAME", + dilation_rate=None, + stack_axis=None, +): + """ Applies a transformation to the input_patch. Input patches are obtained as it is done in conv3d. The transformation that is performed on this input patch is defined by the method argument: @@ -1284,7 +1371,7 @@ def trans3d_op(input, Description ValueError Description - ''' + """ if dilation_rate is None: dilation_rate = [1, 1, 1] @@ -1295,23 +1382,28 @@ def trans3d_op(input, input_shape = input.get_shape().as_list() # sanity checks - if method not in ['dynamic_convolution', 'locally_connected', - 'local_trafo']: - raise ValueError('Method unknown: {!r}'.format(method)) - msg = 'Filter size must be of shape [x,y,z], but is {!r}' + if method not in [ + "dynamic_convolution", + "locally_connected", + "local_trafo", + ]: + raise ValueError("Method unknown: {!r}".format(method)) + msg = "Filter size must be of shape [x,y,z], but is {!r}" assert len(filter_size) == 3, msg.format(filter_size) - assert np.prod(filter_size) > 0, 'Filter sizes must be greater than 0' - msg = 'Shape is expected to be of length 5, but is {}' + assert np.prod(filter_size) > 0, "Filter sizes must be greater than 0" + msg = "Shape is expected to be of length 5, but is {}" assert len(input_shape) == 5, msg.format(input_shape) # calculate output shape output_shape = np.empty(5, dtype=int) for i in range(3): - output_shape[i+1] = conv_output_length(input_length=input_shape[i + 1], - filter_size=filter_size[i], - padding=padding, - stride=strides[i], - dilation=dilation_rate[i]) + output_shape[i + 1] = conv_output_length( + input_length=input_shape[i + 1], + filter_size=filter_size[i], + padding=padding, + stride=strides[i], + dilation=dilation_rate[i], + ) output_shape[0] = -1 output_shape[4] = num_out_channel @@ -1328,8 +1420,8 @@ def trans3d_op(input, # if stack_axis is 2 (z-dimension): # [batch, x, y, filter_x, filter_y, num_in_channel] s_patches_shape = list(patches_shape) - del s_patches_shape[1+stack_axis] - del s_patches_shape[3+stack_axis] + del s_patches_shape[1 + stack_axis] + del s_patches_shape[3 + stack_axis] # define parameters for extract_image_patches ksizes = [1] + list(filter_size) + [1] @@ -1345,35 +1437,43 @@ def trans3d_op(input, # ------------------ # get slices # ------------------ - start_indices = [get_start_index(input_length=input_shape[i + 1], - filter_size=filter_size[i], - padding=padding, - stride=strides[i], - dilation=dilation_rate[i]) - for i in range(3)] + start_indices = [ + get_start_index( + input_length=input_shape[i + 1], + filter_size=filter_size[i], + padding=padding, + stride=strides[i], + dilation=dilation_rate[i], + ) + for i in range(3) + ] output = [] # --------------------------------------- # loop over all positions in dim #stack_axis # ---------------------------------------- - for s in range(start_indices[stack_axis], input_shape[stack_axis + 1], - strides[stack_axis]): + for s in range( + start_indices[stack_axis], + input_shape[stack_axis + 1], + strides[stack_axis], + ): # get slice for patch along stack_axis slice_s, padding_s = get_conv_slice( - s, - input_length=input_shape[stack_axis + 1], - filter_size=filter_size[stack_axis], - stride=strides[stack_axis], - dilation=dilation_rate[stack_axis]) - - if padding == 'VALID' and padding_s != (0, 0): + s, + input_length=input_shape[stack_axis + 1], + filter_size=filter_size[stack_axis], + stride=strides[stack_axis], + dilation=dilation_rate[stack_axis], + ) + + if padding == "VALID" and padding_s != (0, 0): # skip this axis position, since it does not provide # a valid patch for padding 'VALID' continue # At this point, slice_s either corresponds - # to a vaild patch, or padding is 'SAME' + # to a valid patch, or padding is 'SAME' # Now we need to combine the input_patches # ---------------------------------------------------- @@ -1389,17 +1489,20 @@ def trans3d_op(input, # [batch, filter_x, filter_y, in_channel] # input_patches has shape: # [batch, x,y, filter_x*filter_y*in_channel] - input_s_patches = tf.image.extract_patches(input_s, - sizes=ksizes, - strides=strides2d, - rates=rates, - padding=padding) + input_s_patches = tf.image.extract_patches( + input_s, + sizes=ksizes, + strides=strides2d, + rates=rates, + padding=padding, + ) # reshape input_patches to # [batch, x,y, filter_x,filter_y,in_channel] # (assuming stack_axis=2) - reshaped_input_s_patches = tf.reshape(input_s_patches, - shape=s_patches_shape) + reshaped_input_s_patches = tf.reshape( + input_s_patches, shape=s_patches_shape + ) input_patches.append(reshaped_input_s_patches) # input_patches almost has correct shape: @@ -1408,8 +1511,10 @@ def trans3d_op(input, # However, padding must still be applied # Pad input_patches along stack_axis dimension - if padding == 'SAME' and method in ['locally_connected', - 'local_trafo']: + if padding == "SAME" and method in [ + "locally_connected", + "local_trafo", + ]: if padding_s != (0, 0): zeros = tf.zeros_like(input_patches[0]) @@ -1432,32 +1537,34 @@ def trans3d_op(input, # ------------------------------ # Perform dynamic convolution # ------------------------------ - if method == 'dynamic_convolution': + if method == "dynamic_convolution": # filter has shape # [filter_x, filter_y, filter_z, in_channels, out_channels] # Dimensions need to match: expanded_input_patches = tf.expand_dims(input_patches, -1) - begin = [0]*5 - size = [-1]*5 + begin = [0] * 5 + size = [-1] * 5 begin[stack_axis] = padding_s[0] - size[stack_axis] = (filter_size[stack_axis] - padding_s[1] - - padding_s[0]) + size[stack_axis] = ( + filter_size[stack_axis] - padding_s[1] - padding_s[0] + ) sliced_filter = tf.slice(filter, begin, size) output_patch = tf.reduce_sum( - input_tensor=expanded_input_patches * sliced_filter, - axis=[3, 4, 5, 6], - keepdims=False) + input_tensor=expanded_input_patches * sliced_filter, + axis=[3, 4, 5, 6], + keepdims=False, + ) output.append(output_patch) # ------------------------------ # Locally connected # ------------------------------ - elif method == 'locally_connected': + elif method == "locally_connected": raise NotImplementedError() # ------------------------------ # Perform trafo on input_patch # ------------------------------ - elif method == 'local_trafo': + elif method == "local_trafo": output_patch = trafo(input_patches) output.append(output_patch) @@ -1469,8 +1576,8 @@ def trans3d_op(input, return output -def conv3d_stacked(input, filter, strides=[1, 1, 1, 1, 1], padding='SAME'): - ''' +def conv3d_stacked(input, filter, strides=[1, 1, 1, 1, 1], padding="SAME"): + """ Equivalent to tensorflows conv3d. This method is slightly slower, but appears to use less vram. @@ -1504,7 +1611,7 @@ def conv3d_stacked(input, filter, strides=[1, 1, 1, 1, 1], padding='SAME'): Returns ------- A Tensor. Has the same type as input. - ''' + """ # unpack along z dimension tensors_z = tf.unstack(input, axis=3) @@ -1515,21 +1622,28 @@ def conv3d_stacked(input, filter, strides=[1, 1, 1, 1, 1], padding='SAME'): if len_zs % 2 == 1: # uneven filter size: same size to left and right - filter_l = int(len_zs/2) - filter_r = int(len_zs/2) + filter_l = int(len_zs / 2) + filter_r = int(len_zs / 2) else: # even filter size: one more to right - filter_l = int(len_zs/2) - 1 - filter_r = int(len_zs/2) + filter_l = int(len_zs / 2) - 1 + filter_r = int(len_zs / 2) # The start index is important for strides # The strides start with the first element # that works and is VALID: start_index = 0 - if padding == 'VALID': + if padding == "VALID": for i in range(size_of_z_dim): - if len(range(max(i - filter_l, 0), - min(i + filter_r+1, size_of_z_dim))) == len_zs: + if ( + len( + range( + max(i - filter_l, 0), + min(i + filter_r + 1, size_of_z_dim), + ) + ) + == len_zs + ): # we found the first index that doesn't need padding break start_index = i @@ -1538,10 +1652,11 @@ def conv3d_stacked(input, filter, strides=[1, 1, 1, 1, 1], padding='SAME'): result_z = [] for i in range(start_index, size_of_z_dim, strides[3]): - if padding == 'VALID': + if padding == "VALID": # Get indices z_s - indices_z_s = range(max(i - filter_l, 0), - min(i + filter_r+1, size_of_z_dim)) + indices_z_s = range( + max(i - filter_l, 0), min(i + filter_r + 1, size_of_z_dim) + ) # check if Padding = 'VALID' if len(indices_z_s) == len_zs: @@ -1550,30 +1665,35 @@ def conv3d_stacked(input, filter, strides=[1, 1, 1, 1, 1], padding='SAME'): # sum over all remaining index_z_i in indices_z_s for j, index_z_i in enumerate(indices_z_s): tensors_z_convoluted.append( - tf.nn.conv2d(input=tensors_z[index_z_i], - filters=kernel_z[j], - strides=strides[:3]+strides[4:], - padding=padding) - ) + tf.nn.conv2d( + input=tensors_z[index_z_i], + filters=kernel_z[j], + strides=strides[:3] + strides[4:], + padding=padding, + ) + ) sum_tensors_z_s = tf.add_n(tensors_z_convoluted) # put together result_z.append(sum_tensors_z_s) - elif padding == 'SAME': + elif padding == "SAME": tensors_z_convoluted = [] - for kernel_j, j in enumerate(range(i - filter_l, - (i + 1) + filter_r)): + for kernel_j, j in enumerate( + range(i - filter_l, (i + 1) + filter_r) + ): # we can just leave out the invalid z coordinates - # since they will be padded with 0's and therfore + # since they will be padded with 0's and therefore # don't contribute to the sum if 0 <= j < size_of_z_dim: tensors_z_convoluted.append( - tf.nn.conv2d(input=tensors_z[j], - filters=kernel_z[kernel_j], - strides=strides[:3]+strides[4:], - padding=padding) - ) + tf.nn.conv2d( + input=tensors_z[j], + filters=kernel_z[kernel_j], + strides=strides[:3] + strides[4:], + padding=padding, + ) + ) sum_tensors_z_s = tf.add_n(tensors_z_convoluted) # put together result_z.append(sum_tensors_z_s) @@ -1582,14 +1702,16 @@ def conv3d_stacked(input, filter, strides=[1, 1, 1, 1, 1], padding='SAME'): return tf.stack(result_z, axis=3) -def conv4d_stacked(input, filter, - strides=[1, 1, 1, 1, 1, 1], - padding='SAME', - dilation_rate=None, - stack_axis=None, - stack_nested=False, - ): - ''' +def conv4d_stacked( + input, + filter, + strides=[1, 1, 1, 1, 1, 1], + padding="SAME", + dilation_rate=None, + stack_axis=None, + stack_nested=False, +): + """ Computes a convolution over 4 dimensions. Python generalization of tensorflow's conv3d with dilation. conv4d_stacked uses tensorflows conv3d and stacks results along @@ -1621,14 +1743,14 @@ def conv4d_stacked(input, filter, chosen. This is only an educated guess of the best choice! stack_nested : Bool - If set to True, this will stack in a for loop seperately and afterwards + If set to True, this will stack in a for loop separately and afterwards combine the results. In most cases slower, but maybe less memory needed. Returns ------- A Tensor. Has the same type as input. - ''' + """ # heuristically choose stack_axis if stack_axis is None: @@ -1636,43 +1758,53 @@ def conv4d_stacked(input, filter, dil_array = np.ones(4) else: dil_array = np.asarray(dilation_rate) - outputsizes = (np.asarray(input.get_shape().as_list()[1:5]) / - np.asarray(strides[1:5])) - outputsizes -= dil_array*( - np.asarray(filter.get_shape().as_list()[:4])-1) - stack_axis = np.argmin(outputsizes)+1 + outputsizes = np.asarray( + input.get_shape().as_list()[1:5] + ) / np.asarray(strides[1:5]) + outputsizes -= dil_array * ( + np.asarray(filter.get_shape().as_list()[:4]) - 1 + ) + stack_axis = np.argmin(outputsizes) + 1 if dilation_rate is not None: - dilation_along_stack_axis = dilation_rate[stack_axis-1] + dilation_along_stack_axis = dilation_rate[stack_axis - 1] else: dilation_along_stack_axis = 1 tensors_t = tf.unstack(input, axis=stack_axis) - kernel_t = tf.unstack(filter, axis=stack_axis-1) + kernel_t = tf.unstack(filter, axis=stack_axis - 1) - noOfInChannels = input.get_shape().as_list()[-1] - len_ts = filter.get_shape().as_list()[stack_axis-1] + len_ts = filter.get_shape().as_list()[stack_axis - 1] size_of_t_dim = input.get_shape().as_list()[stack_axis] if len_ts % 2 == 1: # uneven filter size: same size to left and right - filter_l = int(len_ts/2) - filter_r = int(len_ts/2) + filter_l = int(len_ts / 2) + filter_r = int(len_ts / 2) else: # even filter size: one more to right - filter_l = int(len_ts/2) - 1 - filter_r = int(len_ts/2) + filter_l = int(len_ts / 2) - 1 + filter_r = int(len_ts / 2) # The start index is important for strides and dilation # The strides start with the first element # that works and is VALID: start_index = 0 - if padding == 'VALID': + if padding == "VALID": for i in range(size_of_t_dim): - if len(range(max(i - dilation_along_stack_axis*filter_l, 0), - min(i + dilation_along_stack_axis*filter_r+1, - size_of_t_dim), - dilation_along_stack_axis)) == len_ts: + if ( + len( + range( + max(i - dilation_along_stack_axis * filter_l, 0), + min( + i + dilation_along_stack_axis * filter_r + 1, + size_of_t_dim, + ), + dilation_along_stack_axis, + ) + ) + == len_ts + ): # we found the first index that doesn't need padding break start_index = i @@ -1685,13 +1817,16 @@ def conv4d_stacked(input, filter, input_patch = [] tensors_t_convoluted = [] - if padding == 'VALID': + if padding == "VALID": # Get indices t_s - indices_t_s = range(max(i - dilation_along_stack_axis*filter_l, 0), - min(i + dilation_along_stack_axis*filter_r+1, - size_of_t_dim), - dilation_along_stack_axis) + indices_t_s = range( + max(i - dilation_along_stack_axis * filter_l, 0), + min( + i + dilation_along_stack_axis * filter_r + 1, size_of_t_dim + ), + dilation_along_stack_axis, + ) # check if Padding = 'VALID' if len(indices_t_s) == len_ts: @@ -1707,36 +1842,46 @@ def conv4d_stacked(input, filter, tf.nn.convolution( input=tensors_t[index_t_i], filters=kernel_t[j], - strides=(strides[1:stack_axis+1] - + strides[stack_axis:5]), + strides=( + strides[1 : stack_axis + 1] + + strides[stack_axis:5] + ), padding=padding, dilations=( - dilation_rate[:stack_axis-1] - + dilation_rate[stack_axis:])) + dilation_rate[: stack_axis - 1] + + dilation_rate[stack_axis:] + ), ) + ) else: tensors_t_convoluted.append( - tf.nn.conv3d(input=tensors_t[index_t_i], - filters=kernel_t[j], - strides=(strides[:stack_axis] + - strides[stack_axis+1:]), - padding=padding) + tf.nn.conv3d( + input=tensors_t[index_t_i], + filters=kernel_t[j], + strides=( + strides[:stack_axis] + + strides[stack_axis + 1 :] + ), + padding=padding, ) + ) if stack_nested: sum_tensors_t_s = tf.add_n(tensors_t_convoluted) # put together result_t.append(sum_tensors_t_s) - elif padding == 'SAME': + elif padding == "SAME": # Get indices t_s - indices_t_s = range(i - dilation_along_stack_axis*filter_l, - (i + 1) + dilation_along_stack_axis*filter_r, - dilation_along_stack_axis) + indices_t_s = range( + i - dilation_along_stack_axis * filter_l, + (i + 1) + dilation_along_stack_axis * filter_r, + dilation_along_stack_axis, + ) for kernel_j, j in enumerate(indices_t_s): # we can just leave out the invalid t coordinates - # since they will be padded with 0's and therfore + # since they will be padded with 0's and therefore # don't contribute to the sum if 0 <= j < size_of_t_dim: @@ -1749,21 +1894,29 @@ def conv4d_stacked(input, filter, tf.nn.convolution( input=tensors_t[j], filters=kernel_t[kernel_j], - strides=(strides[1:stack_axis+1] + - strides[stack_axis:5]), + strides=( + strides[1 : stack_axis + 1] + + strides[stack_axis:5] + ), padding=padding, dilations=( - dilation_rate[:stack_axis-1] + - dilation_rate[stack_axis:])) + dilation_rate[: stack_axis - 1] + + dilation_rate[stack_axis:] + ), ) + ) else: tensors_t_convoluted.append( - tf.nn.conv3d(input=tensors_t[j], - filters=kernel_t[kernel_j], - strides=(strides[:stack_axis] + - strides[stack_axis+1:]), - padding=padding) - ) + tf.nn.conv3d( + input=tensors_t[j], + filters=kernel_t[kernel_j], + strides=( + strides[:stack_axis] + + strides[stack_axis + 1 :] + ), + padding=padding, + ) + ) if stack_nested: sum_tensors_t_s = tf.add_n(tensors_t_convoluted) # put together @@ -1775,20 +1928,26 @@ def conv4d_stacked(input, filter, input_patch = tf.concat(input_patch, axis=4) if dilation_rate is not None: result_patch = tf.nn.convolution( - input=input_patch, - filters=kernel_patch, - strides=(strides[1:stack_axis] + - strides[stack_axis+1:5]), - padding=padding, - dilations=(dilation_rate[:stack_axis-1] + - dilation_rate[stack_axis:])) + input=input_patch, + filters=kernel_patch, + strides=( + strides[1:stack_axis] + strides[stack_axis + 1 : 5] + ), + padding=padding, + dilations=( + dilation_rate[: stack_axis - 1] + + dilation_rate[stack_axis:] + ), + ) else: result_patch = tf.nn.conv3d( - input=input_patch, - filters=kernel_patch, - strides=(strides[:stack_axis] + - strides[stack_axis+1:]), - padding=padding) + input=input_patch, + filters=kernel_patch, + strides=( + strides[:stack_axis] + strides[stack_axis + 1 :] + ), + padding=padding, + ) result_t.append(result_patch) # stack together diff --git a/tfscripts/core.py b/tfscripts/core.py index c87050c..9a2bd78 100644 --- a/tfscripts/core.py +++ b/tfscripts/core.py @@ -1,8 +1,8 @@ -''' +""" Core functions of tfscripts: Add residuals, batch normalisation, activation, -''' +""" from __future__ import division, print_function @@ -24,13 +24,15 @@ class AddResidual(tf.Module): other dimensions """ - def __init__(self, - residual_shape, - strides=None, - use_scale_factor=True, - scale_factor=0.001, - float_precision=FLOAT_PRECISION, - name=None): + def __init__( + self, + residual_shape, + strides=None, + use_scale_factor=True, + scale_factor=0.001, + float_precision=FLOAT_PRECISION, + name=None, + ): """Initialize object Parameters @@ -58,14 +60,16 @@ def __init__(self, self.strides = strides # Residuals added over multiple layers accumulate. - # A scale factor < 1 reduces instabilities in beginnning + # A scale factor < 1 reduces instabilities in beginning if self.use_scale_factor: - self.scale = new_weights([self.num_outputs], - stddev=self.scale_factor, - float_precision=float_precision) + self.scale = new_weights( + [self.num_outputs], + stddev=self.scale_factor, + float_precision=float_precision, + ) def __call__(self, input, residual): - '''Apply residual additions + """Apply residual additions Parameters ---------- @@ -78,7 +82,7 @@ def __call__(self, input, residual): ------- tf.Tensor The output Tensor: input + scale * residual(if use_scale_factor) - ''' + """ # make sure the shape of the residual is correct in last dimension assert residual.get_shape()[-1] == self.num_outputs @@ -88,27 +92,28 @@ def __call__(self, input, residual): # ---------------------- if self.strides is not None: - msg = 'Number of dimensions of strides and input must match' + msg = "Number of dimensions of strides and input must match" assert len(self.strides) == len(input.get_shape().as_list()), msg - assert self.strides[0] == 1, 'stride in batch dimension must be 1' + assert self.strides[0] == 1, "stride in batch dimension must be 1" if not self.strides == [1 for s in self.strides]: begin = [0 for s in self.strides] end = [0] + input.get_shape().as_list()[1:] - input = tf.strided_slice(input, - begin=begin, - end=end, - strides=self.strides, - begin_mask=1, - end_mask=1, - ) + input = tf.strided_slice( + input, + begin=begin, + end=end, + strides=self.strides, + begin_mask=1, + end_mask=1, + ) # ---------------------- num_outputs = residual.get_shape().as_list()[-1] num_inputs = input.get_shape().as_list()[-1] # Residuals added over multiple layers accumulate. - # A scale factor < 1 reduces instabilities in beginnning + # A scale factor < 1 reduces instabilities in beginning if self.use_scale_factor: residual = residual * self.scale if num_inputs == num_outputs: @@ -116,18 +121,26 @@ def __call__(self, input, residual): elif num_inputs > num_outputs: output = residual + input[..., :num_outputs] elif num_inputs < num_outputs: - output = tf.concat([residual[..., :num_inputs] + input, - residual[..., num_inputs:]], axis=-1) + output = tf.concat( + [ + residual[..., :num_inputs] + input, + residual[..., num_inputs:], + ], + axis=-1, + ) else: if num_inputs == num_outputs: - output = (residual + input)/np.sqrt(2.) + output = (residual + input) / np.sqrt(2.0) elif num_inputs > num_outputs: - output = (residual + input[..., :num_outputs])/np.sqrt(2.) + output = (residual + input[..., :num_outputs]) / np.sqrt(2.0) elif num_inputs < num_outputs: output = tf.concat( - [(residual[..., :num_inputs] + input)/np.sqrt(2.), - residual[..., num_inputs:]], - axis=-1) + [ + (residual[..., :num_inputs] + input) / np.sqrt(2.0), + residual[..., num_inputs:], + ], + axis=-1, + ) return output @@ -137,16 +150,18 @@ class Activation(tf.Module): For parametric activation functions this assumes that the first dimension is batch size and that for each of the other dimensions - seperate parametrizations should be learned. + separate parametrizations should be learned. """ - def __init__(self, - activation_type, - input_shape=None, - use_batch_normalisation=False, - float_precision=FLOAT_PRECISION, - name=None): + def __init__( + self, + activation_type, + input_shape=None, + use_batch_normalisation=False, + float_precision=FLOAT_PRECISION, + name=None, + ): """Initialize object Parameters @@ -170,25 +185,31 @@ def __init__(self, if self.use_batch_normalisation: self.batch_norm_layer = BatchNormWrapper( - input_shape=input_shape, - float_precision=float_precision, - name=name) - - if activation_type == 'prelu': - self.slope_weight = new_weights(input_shape[1:], - float_precision=float_precision) - - elif activation_type == 'pelu': - self.a_weight = new_weights(input_shape[1:], - float_precision=float_precision) - self.b_weight = new_weights(input_shape[1:], - float_precision=float_precision) - - elif activation_type == 'pgaussian': - self.sigma_weight = new_weights(input_shape[1:], - float_precision=float_precision) - self.mu = new_weights(input_shape[1:], - float_precision=float_precision) + input_shape=input_shape, + float_precision=float_precision, + name=name, + ) + + if activation_type == "prelu": + self.slope_weight = new_weights( + input_shape[1:], float_precision=float_precision + ) + + elif activation_type == "pelu": + self.a_weight = new_weights( + input_shape[1:], float_precision=float_precision + ) + self.b_weight = new_weights( + input_shape[1:], float_precision=float_precision + ) + + elif activation_type == "pgaussian": + self.sigma_weight = new_weights( + input_shape[1:], float_precision=float_precision + ) + self.mu = new_weights( + input_shape[1:], float_precision=float_precision + ) def __call__(self, layer, is_training=None): """Apply Activation Module. @@ -217,11 +238,13 @@ def __call__(self, layer, is_training=None): # Use batch normalisation? if self.use_batch_normalisation: if is_training is None: - raise ValueError('To use batch normalisation a boolean ' - 'is_training needs to be passed') + raise ValueError( + "To use batch normalisation a boolean " + "is_training needs to be passed" + ) layer = self.batch_norm_layer(layer, is_training) - if activation_type == '' or activation_type is None: + if activation_type == "" or activation_type is None: return layer if hasattr(tf.nn, activation_type): @@ -230,64 +253,72 @@ def __call__(self, layer, is_training=None): elif hasattr(tf, activation_type): layer = getattr(tf, activation_type)(layer) - elif activation_type == 'leaky': - layer = tf.multiply(tf.maximum(-0.01*layer, layer), tf.sign(layer)) + elif activation_type == "leaky": + layer = tf.multiply( + tf.maximum(-0.01 * layer, layer), tf.sign(layer) + ) # todo: NecroRelu # https://stats.stackexchange.com/questions/176794/ # how-does-rectilinear-activation-function-solve-the- # vanishing-gradient-problem-in # https://github.com/ibmua/learning-to-make-nn-in-python/ # blob/master/nn_classifier.py - elif activation_type == 'requ': - layer = tf.where(tf.less(layer, 0.), - tf.zeros_like(layer, dtype=self.float_precision), - tf.square(layer)) - - elif activation_type == 'selu': + elif activation_type == "requ": + layer = tf.where( + tf.less(layer, 0.0), + tf.zeros_like(layer, dtype=self.float_precision), + tf.square(layer), + ) + + elif activation_type == "selu": lam = 1.0507 alpha = 1.6733 # from https://arxiv.org/abs/1706.02515 # self normalizing networks - layer = tf.where(tf.less(layer, 0.), - tf.exp(layer) * alpha - alpha, - layer) + layer = tf.where( + tf.less(layer, 0.0), tf.exp(layer) * alpha - alpha, layer + ) layer = layer * lam - elif activation_type == 'centeredRelu': - layer = tf.nn.relu6(layer) - 3. + elif activation_type == "centeredRelu": + layer = tf.nn.relu6(layer) - 3.0 - elif activation_type == 'negrelu': + elif activation_type == "negrelu": layer = -tf.nn.relu(layer) - elif activation_type == 'invrelu': - layer = tf.where(tf.less(layer, 0.), layer, (layer+1e-8)**-1) + elif activation_type == "invrelu": + layer = tf.where(tf.less(layer, 0.0), layer, (layer + 1e-8) ** -1) - elif activation_type == 'sign': - layer = tf.where(tf.less(layer, 0.), layer, tf.sign(layer)) + elif activation_type == "sign": + layer = tf.where(tf.less(layer, 0.0), layer, tf.sign(layer)) - elif activation_type == 'prelu': + elif activation_type == "prelu": slope = self.slope_weight + 1.0 - layer = tf.where(tf.less(layer, 0.), layer*slope, layer) + layer = tf.where(tf.less(layer, 0.0), layer * slope, layer) - elif activation_type == 'pelu': + elif activation_type == "pelu": a = self.a_weight + 1.0 b = self.b_weight + 1.0 - layer = tf.where(tf.less(layer, 0.), - (tf.exp(layer/b) - 1)*a, layer*(a/b)) + layer = tf.where( + tf.less(layer, 0.0), + (tf.exp(layer / b) - 1) * a, + layer * (a / b), + ) - elif activation_type == 'gaussian': + elif activation_type == "gaussian": layer = tf.exp(-tf.square(layer)) - elif activation_type == 'pgaussian': + elif activation_type == "pgaussian": sigma = self.sigma_weight + 1.0 - layer = tf.exp(tf.square((layer - self.mu) / sigma) * - (-0.5)) / (sigma) + layer = tf.exp(tf.square((layer - self.mu) / sigma) * (-0.5)) / ( + sigma + ) elif callable(activation_type): layer = activation_type(layer) else: - msg = 'Unknown activation type: {!r}' + msg = "Unknown activation type: {!r}" raise ValueError(msg.format(activation_type)) return layer @@ -303,12 +334,14 @@ class BatchNormWrapper(tf.Module): BN2015 paper by Sergey Ioffe and Christian Szegedy """ - def __init__(self, - input_shape, - decay=0.99, - epsilon=1e-6, - float_precision=FLOAT_PRECISION, - name=None): + def __init__( + self, + input_shape, + decay=0.99, + epsilon=1e-6, + float_precision=FLOAT_PRECISION, + name=None, + ): """Initialize object Parameters @@ -329,19 +362,28 @@ def __init__(self, norm_shape = input_shape[1:] self.epsilon = epsilon self.decay = decay - self.scale = tf.Variable(tf.ones(norm_shape, dtype=float_precision), - name='BN_scale', dtype=float_precision) - self.beta = tf.Variable(tf.zeros(norm_shape, dtype=float_precision), - name='BN_beta', dtype=float_precision) - self.pop_mean = tf.Variable(tf.zeros(norm_shape, - dtype=float_precision), - trainable=False, - name='BN_pop_mean', - dtype=float_precision) - self.pop_var = tf.Variable(tf.ones(norm_shape, dtype=float_precision), - trainable=False, - name='BN_pop_var', - dtype=float_precision) + self.scale = tf.Variable( + tf.ones(norm_shape, dtype=float_precision), + name="BN_scale", + dtype=float_precision, + ) + self.beta = tf.Variable( + tf.zeros(norm_shape, dtype=float_precision), + name="BN_beta", + dtype=float_precision, + ) + self.pop_mean = tf.Variable( + tf.zeros(norm_shape, dtype=float_precision), + trainable=False, + name="BN_pop_mean", + dtype=float_precision, + ) + self.pop_var = tf.Variable( + tf.ones(norm_shape, dtype=float_precision), + trainable=False, + name="BN_pop_var", + dtype=float_precision, + ) def __call__(self, inputs, is_training): """Apply Batch Normalization Wrapper @@ -353,7 +395,7 @@ def __call__(self, inputs, is_training): Tensor will be normalised in all but batch dimension. is_training : bool, or tf.Tensor of type bool. - Indicates wheter the network is being trained + Indicates whether the network is being trained or whether it is being used in inference mode. If set to true, the population mean and variance will be updated and learned. @@ -364,27 +406,37 @@ def __call__(self, inputs, is_training): The batch normalized output """ if is_training: - batch_mean, batch_var = tf.nn.moments(x=inputs, - axes=[0], - keepdims=False) + batch_mean, batch_var = tf.nn.moments( + x=inputs, axes=[0], keepdims=False + ) train_mean = tf.compat.v1.assign( self.pop_mean, - self.pop_mean * self.decay + batch_mean * (1 - self.decay)) + self.pop_mean * self.decay + batch_mean * (1 - self.decay), + ) train_var = tf.compat.v1.assign( self.pop_var, - self.pop_var * self.decay + batch_var * (1 - self.decay)) + self.pop_var * self.decay + batch_var * (1 - self.decay), + ) with tf.control_dependencies([train_mean, train_var]): return tf.nn.batch_normalization( inputs, - batch_mean, batch_var, # R2RT's blog + batch_mean, + batch_var, # R2RT's blog # self.pop_mean, self.pop_var, - self.beta, self.scale, self.epsilon, - ) + self.beta, + self.scale, + self.epsilon, + ) else: - return tf.nn.batch_normalization(inputs, self.pop_mean, - self.pop_var, self.beta, - self.scale, self.epsilon) + return tf.nn.batch_normalization( + inputs, + self.pop_mean, + self.pop_var, + self.beta, + self.scale, + self.epsilon, + ) def maxout(inputs, num_units, axis=-1): @@ -401,7 +453,7 @@ def maxout(inputs, num_units, axis=-1): Parameters ---------- - inputs : nD tensor + inputs : tf.Tensor The input data tensor on which to apply the the maxout operation. shape: (batch_size, ..., axis_dim, ...) num_units @@ -414,7 +466,7 @@ def maxout(inputs, num_units, axis=-1): Returns ------- - nD tensor + tf.Tensor The output tensor after applying maxout operation. shape: (batch_size, ..., num_units, ...) """ @@ -427,17 +479,17 @@ def maxout(inputs, num_units, axis=-1): shape[i] = tf.shape(inputs)[i] num_channels = shape[axis] - if (not isinstance(num_channels, tf.Tensor) - and num_channels % num_units): - raise ValueError('number of features({}) is not ' - 'a multiple of num_units({})'.format( - num_channels, num_units)) + if not isinstance(num_channels, tf.Tensor) and num_channels % num_units: + raise ValueError( + "number of features({}) is not " + "a multiple of num_units({})".format(num_channels, num_units) + ) if axis < 0: axis = axis + len(shape) else: axis = axis - assert axis >= 0, 'Find invalid axis: {}'.format(axis) + assert axis >= 0, "Find invalid axis: {}".format(axis) expand_shape = shape[:] expand_shape[axis] = num_units @@ -445,5 +497,6 @@ def maxout(inputs, num_units, axis=-1): expand_shape.insert(axis, k) outputs = tf.math.reduce_max( - tf.reshape(inputs, expand_shape), axis, keepdims=False) + tf.reshape(inputs, expand_shape), axis, keepdims=False + ) return outputs diff --git a/tfscripts/hex/conv.py b/tfscripts/hex/conv.py index 81d7f46..6cdc81f 100644 --- a/tfscripts/hex/conv.py +++ b/tfscripts/hex/conv.py @@ -1,11 +1,11 @@ -''' +""" tfscripts hexagonal convolution utility functions Hex utility functions hex conv 3d and 4d [tf.Modules] ToDo: - Remove duplicate code -''' +""" from __future__ import division, print_function @@ -44,10 +44,10 @@ def get_num_hex_points(edge_length): Description """ if edge_length < 0: - raise ValueError('get_num_hex_points: expected edge_length >= 0') + raise ValueError("get_num_hex_points: expected edge_length >= 0") if edge_length == 0: return 1 - return (edge_length-1)*6 + get_num_hex_points(edge_length-1) + return (edge_length - 1) * 6 + get_num_hex_points(edge_length - 1) def hex_distance(h1, h2): @@ -68,21 +68,25 @@ def hex_distance(h1, h2): """ a1, b1 = h1 a2, b2 = h2 - c1 = -a1-b1 - c2 = -a2-b2 + c1 = -a1 - b1 + c2 = -a2 - b2 return (abs(a1 - a2) + abs(b1 - b2) + abs(c1 - c2)) / 2 -def get_hex_kernel(filter_size, print_kernel=False, get_ones=False, - float_precision=FLOAT_PRECISION): - '''Get hexagonal convolution kernel +def get_hex_kernel( + filter_size, + print_kernel=False, + get_ones=False, + float_precision=FLOAT_PRECISION, +): + """Get hexagonal convolution kernel Create Weights for a hexagonal kernel. The Kernel will be of a hexagonal shape in the first two dimensions, while the other dimensions are normal. The hexagonal kernel is off the shape: [kernel_edge_points, kernel_edge_points, *filter_size[2:]] - But elments with coordinates in the first two dimensions, that don't belong + But elements with coordinates in the first two dimensions, that don't belong to the hexagon are set to a tf.Constant 0. The hexagon is defined by filter_size[0:2]. @@ -150,17 +154,19 @@ def get_hex_kernel(filter_size, print_kernel=False, get_ones=False, ------ ValueError Description - ''' + """ k = filter_size[0] x = filter_size[1] if x >= k: - raise ValueError("get_hex_kernel: filter_size (k,x,z) must fulfill " - "x < k: ({}, {}, {})".format(k, x, filter_size[2])) + raise ValueError( + "get_hex_kernel: filter_size (k,x,z) must fulfill " + "x < k: ({}, {}, {})".format(k, x, filter_size[2]) + ) if x == 0: - kernel_edge_points = 2*k - 1 + kernel_edge_points = 2 * k - 1 else: - kernel_edge_points = 2*k + 1 + kernel_edge_points = 2 * k + 1 zeros = tf.zeros(filter_size[2:], dtype=float_precision) ones = tf.ones(filter_size[2:], dtype=float_precision) @@ -176,15 +182,16 @@ def get_hex_kernel(filter_size, print_kernel=False, get_ones=False, # regular aligned hexagons # ------------------------- if x == 0: - if a+b < k - 1 or a + b > 3*k - 3: + if a + b < k - 1 or a + b > 3 * k - 3: weights = zeros test_hex_dict[(a, b)] = 0 else: if get_ones: weights = ones else: - weights = new_weights(filter_size[2:], - float_precision=float_precision) + weights = new_weights( + filter_size[2:], float_precision=float_precision + ) var_list.append(weights) test_hex_dict[(a, b)] = 1 @@ -195,33 +202,38 @@ def get_hex_kernel(filter_size, print_kernel=False, get_ones=False, inHexagon = False # check if inside normal k.0 aligned hexagon # |----inside normal k.0 rhombus -----------| - if ((a > 0 and a < 2*k) and (b > 0 and b < 2*k) and + if ( + (a > 0 and a < 2 * k) + and (b > 0 and b < 2 * k) + and # |--in k.0 aligned hexagon-| - (a+b > k and a + b < 3*k)): + (a + b > k and a + b < 3 * k) + ): - if a+b > k and a + b < 3*k: + if a + b > k and a + b < 3 * k: inHexagon = True else: # add 6 additional edges outside of k.0 aligned hexagon - if a == 2*k-x and b == 0: # Edge 1 + if a == 2 * k - x and b == 0: # Edge 1 inHexagon = True elif a == k - x and b == x: # Edge 2 inHexagon = True - elif a == 0 and b == k+x: # Edge 3 + elif a == 0 and b == k + x: # Edge 3 inHexagon = True - elif a == x and b == 2*k: # Edge 4 + elif a == x and b == 2 * k: # Edge 4 inHexagon = True - elif a == k+x and b == 2*k-x: # Edge 5 + elif a == k + x and b == 2 * k - x: # Edge 5 inHexagon = True - elif a == 2*k and b == k-x: # Edge 6 + elif a == 2 * k and b == k - x: # Edge 6 inHexagon = True # get weights or constant 0 depending on if point is in hexagon if inHexagon: if get_ones: weights = ones else: - weights = new_weights(filter_size[2:], - float_precision=float_precision) + weights = new_weights( + filter_size[2:], float_precision=float_precision + ) var_list.append(weights) test_hex_dict[(a, b)] = 1 else: @@ -237,23 +249,24 @@ def get_hex_kernel(filter_size, print_kernel=False, get_ones=False, class ConvHex(tf.Module): - """Convolve a hex2d or hex3d layer (2d hex + 1d cartesian) - """ - - def __init__(self, - input_shape, - filter_size, - num_filters, - padding='SAME', - strides=[1, 1, 1, 1, 1], - num_rotations=1, - dilation_rate=None, - zero_out=False, - kernel=None, - var_list=None, - azimuth=None, - float_precision=FLOAT_PRECISION, - name=None): + """Convolve a hex2d or hex3d layer (2d hex + 1d cartesian)""" + + def __init__( + self, + input_shape, + filter_size, + num_filters, + padding="SAME", + strides=[1, 1, 1, 1, 1], + num_rotations=1, + dilation_rate=None, + zero_out=False, + kernel=None, + var_list=None, + azimuth=None, + float_precision=FLOAT_PRECISION, + name=None, + ): """Initialize object Parameters @@ -347,17 +360,22 @@ def __init__(self, if kernel is None: if azimuth is not None and filter_size[:2] != [1, 0]: kernel, var_list = rotation.get_dynamic_rotation_hex_kernel( - filter_size+[num_channels, num_filters], azimuth, - float_precision=float_precision) + filter_size + [num_channels, num_filters], + azimuth, + float_precision=float_precision, + ) else: if num_rotations > 1: kernel, var_list = rotation.get_rotated_hex_kernel( - filter_size+[num_channels, num_filters], num_rotations, - float_precision=float_precision) + filter_size + [num_channels, num_filters], + num_rotations, + float_precision=float_precision, + ) else: kernel, var_list = get_hex_kernel( - filter_size+[num_channels, num_filters], - float_precision=float_precision) + filter_size + [num_channels, num_filters], + float_precision=float_precision, + ) self.num_filters = num_filters self.filter_size = filter_size @@ -391,18 +409,20 @@ def __call__(self, inputs): if self.azimuth is not None and self.filter_size[:2] != [1, 0]: result = dynamic_conv( - inputs=inputs, - filters=self.kernel, - strides=self.strides[1:-1], - padding=self.padding, - dilation_rate=self.dilation_rate, - ) + inputs=inputs, + filters=self.kernel, + strides=self.strides[1:-1], + padding=self.padding, + dilation_rate=self.dilation_rate, + ) else: - result = tf.nn.convolution(input=inputs, - filters=self.kernel, - strides=self.strides[1:-1], - padding=self.padding, - dilations=self.dilation_rate) + result = tf.nn.convolution( + input=inputs, + filters=self.kernel, + strides=self.strides[1:-1], + padding=self.padding, + dilations=self.dilation_rate, + ) # zero out elements that don't belong on hexagon or IceCube Strings if self.zero_out: @@ -410,62 +430,70 @@ def __call__(self, inputs): if result.get_shape().as_list()[1:3] == [10, 10]: # Assuming IceCube shape logger = logging.getLogger(__name__) - logger.warning('Assuming IceCube shape for layer', result) + logger.warning("Assuming IceCube shape for layer", result) zero_out_matrix, var_list = get_icecube_kernel( - result.get_shape().as_list()[3:], - get_ones=True, - float_precision=self.float_precision) - result = result*zero_out_matrix + result.get_shape().as_list()[3:], + get_ones=True, + float_precision=self.float_precision, + ) + result = result * zero_out_matrix # Make sure there were no extra variables created. # These would have to be saved to tf.Module, to allow tracking - assert var_list == [], 'No created variables expected!' + assert var_list == [], "No created variables expected!" else: # Generic hexagonal shape zero_out_matrix, var_list = get_hex_kernel( - [(result.get_shape().as_list()[1]+1) // 2, 0, - result.get_shape().as_list()[3], - self.num_filters * self.num_rotations], - get_ones=True, - float_precision=self.float_precision) + [ + (result.get_shape().as_list()[1] + 1) // 2, + 0, + result.get_shape().as_list()[3], + self.num_filters * self.num_rotations, + ], + get_ones=True, + float_precision=self.float_precision, + ) # Make sure there were no extra variables created. # These would have to be saved to tf.Module, to allow tracking - assert var_list == [], 'No created variables expected!' + assert var_list == [], "No created variables expected!" if result.get_shape()[1:] == zero_out_matrix.get_shape(): - result = result*zero_out_matrix + result = result * zero_out_matrix else: - raise ValueError("ConvHex: Shapes do not match for " - "zero_out_matrix and result. " - " {!r} != {!r}".format( - result.get_shape()[1:], - zero_out_matrix.get_shape())) + raise ValueError( + "ConvHex: Shapes do not match for " + "zero_out_matrix and result. " + " {!r} != {!r}".format( + result.get_shape()[1:], zero_out_matrix.get_shape() + ) + ) return result class ConvHex4d(tf.Module): - """Convolve a hex4hex3d layer (2d hex + 1d cartesian) - """ - - def __init__(self, - input_shape, - filter_size, - num_filters, - padding='VALID', - strides=[1, 1, 1, 1, 1, 1], - num_rotations=1, - dilation_rate=None, - kernel=None, - var_list=None, - azimuth=None, - stack_axis=None, - zero_out=False, - float_precision=FLOAT_PRECISION, - name=None): + """Convolve a hex4hex3d layer (2d hex + 1d cartesian)""" + + def __init__( + self, + input_shape, + filter_size, + num_filters, + padding="VALID", + strides=[1, 1, 1, 1, 1, 1], + num_rotations=1, + dilation_rate=None, + kernel=None, + var_list=None, + azimuth=None, + stack_axis=None, + zero_out=False, + float_precision=FLOAT_PRECISION, + name=None, + ): """Initialize object Parameters @@ -565,17 +593,22 @@ def __init__(self, if kernel is None: if azimuth is not None: kernel, var_list = rotation.get_dynamic_rotation_hex_kernel( - filter_size+[num_channels, num_filters], azimuth, - float_precision=float_precision) + filter_size + [num_channels, num_filters], + azimuth, + float_precision=float_precision, + ) else: if num_rotations > 1: kernel, var_list = rotation.get_rotated_hex_kernel( - filter_size+[num_channels, num_filters], num_rotations, - float_precision=float_precision) + filter_size + [num_channels, num_filters], + num_rotations, + float_precision=float_precision, + ) else: kernel, var_list = get_hex_kernel( - filter_size+[num_channels, num_filters], - float_precision=float_precision) + filter_size + [num_channels, num_filters], + float_precision=float_precision, + ) self.num_filters = num_filters self.filter_size = filter_size @@ -609,46 +642,56 @@ def __call__(self, inputs): assert len(inputs.get_shape()) == 6 # convolve with tf conv4d_stacked - result = conv4d_stacked(input=inputs, - filter=self.kernel, - strides=self.strides, - padding=self.padding, - dilation_rate=self.dilation_rate, - stack_axis=self.stack_axis) + result = conv4d_stacked( + input=inputs, + filter=self.kernel, + strides=self.strides, + padding=self.padding, + dilation_rate=self.dilation_rate, + stack_axis=self.stack_axis, + ) # zero out elements that don't belong on hexagon if self.zero_out: zero_out_matrix, var_list = get_hex_kernel( - [int((result.get_shape().as_list()[1]+1)/2), 0, - result.get_shape().as_list()[3], - result.get_shape().as_list()[4], - self.num_filters * self.num_rotations], - get_ones=True, - float_precision=self.float_precision) + [ + int((result.get_shape().as_list()[1] + 1) / 2), + 0, + result.get_shape().as_list()[3], + result.get_shape().as_list()[4], + self.num_filters * self.num_rotations, + ], + get_ones=True, + float_precision=self.float_precision, + ) # Make sure there were no extra variables created. # These would have to be saved to tf.Module, to allow tracking - assert var_list == [], 'No created variables expected!' + assert var_list == [], "No created variables expected!" if result.get_shape()[1:] == zero_out_matrix.get_shape(): result = result * zero_out_matrix else: msg = "conv_hex4d: Shapes do not match for " msg += "zero_out_matrix and result. {!r} != {!r}" - raise ValueError(msg.format(result.get_shape()[1:], - zero_out_matrix.get_shape())) + raise ValueError( + msg.format( + result.get_shape()[1:], zero_out_matrix.get_shape() + ) + ) return result -def create_conv_hex_layers_weights(num_input_channels, - filter_size_list, - num_filters_list, - num_rotations_list=1, - azimuth_list=None, - float_precision=FLOAT_PRECISION, - ): - '''Create weights and biases for conv hex n-dimensional layers with n >= 2 +def create_conv_hex_layers_weights( + num_input_channels, + filter_size_list, + num_filters_list, + num_rotations_list=1, + azimuth_list=None, + float_precision=FLOAT_PRECISION, +): + """Create weights and biases for conv hex n-dimensional layers with n >= 2 Parameters ---------- @@ -713,44 +756,51 @@ def create_conv_hex_layers_weights(num_input_channels, List of bias tensors for each layer. list of tf.Variable A list of tensorflow variables created in this function - ''' + """ # create num_rotations_list if isinstance(num_rotations_list, int): - num_rotations_list = [num_rotations_list for i - in range(len(num_filters_list))] + num_rotations_list = [ + num_rotations_list for i in range(len(num_filters_list)) + ] # create azimuth_list if azimuth_list is None or tf.is_tensor(azimuth_list): - azimuth_list = [azimuth_list for i in range(noOfLayers)] + azimuth_list = [azimuth_list for i in range(len(num_filters_list))] weights_list = [] biases_list = [] variable_list = [] for filter_size, num_filters, num_rotations, azimuth in zip( - filter_size_list, - num_filters_list, - num_rotations_list, - azimuth_list, - ): + filter_size_list, + num_filters_list, + num_rotations_list, + azimuth_list, + ): if azimuth is not None: kernel, var_list = rotation.get_dynamic_rotation_hex_kernel( - filter_size, azimuth, float_precision=float_precision) + filter_size, azimuth, float_precision=float_precision + ) else: if num_rotations > 1: kernel, var_list = rotation.get_rotated_hex_kernel( - filter_size + - [num_input_channels, num_filters], - num_rotations, - float_precision=float_precision) + filter_size + [num_input_channels, num_filters], + num_rotations, + float_precision=float_precision, + ) else: kernel, var_list = get_hex_kernel( - filter_size+[num_input_channels, num_filters], - float_precision=float_precision) + filter_size + [num_input_channels, num_filters], + float_precision=float_precision, + ) variable_list.extend(var_list) weights_list.append(kernel) - biases_list.append(new_biases(length=num_filters*num_rotations, - float_precision=float_precision)) + biases_list.append( + new_biases( + length=num_filters * num_rotations, + float_precision=float_precision, + ) + ) num_input_channels = num_filters return weights_list, biases_list, variable_list diff --git a/tfscripts/hex/icecube.py b/tfscripts/hex/icecube.py index c8c9bc0..c3a609a 100644 --- a/tfscripts/hex/icecube.py +++ b/tfscripts/hex/icecube.py @@ -1,6 +1,6 @@ -''' +""" IceCube specific constants and functions -''' +""" from __future__ import division, print_function @@ -17,86 +17,187 @@ # IceCube Constants # ----------------------------------------------------------------------------- string_hex_coord_dict = { - # row 1 - 1: (-4, -1), 2: (-4, 0), 3: (-4, 1), 4: (-4, 2), 5: (-4, 3), 6: (-4, 4), - + 1: (-4, -1), + 2: (-4, 0), + 3: (-4, 1), + 4: (-4, 2), + 5: (-4, 3), + 6: (-4, 4), # row 2 - 7: (-3, -2), 8: (-3, -1), 9: (-3, 0), 10: (-3, 1), 11: (-3, 2), - 12: (-3, 3), 13: (-3, 4), - + 7: (-3, -2), + 8: (-3, -1), + 9: (-3, 0), + 10: (-3, 1), + 11: (-3, 2), + 12: (-3, 3), + 13: (-3, 4), # row 3 - 14: (-2, -3), 15: (-2, -2), 16: (-2, -1), 17: (-2, 0), 18: (-2, 1), - 19: (-2, 2), 20: (-2, 3), 21: (-2, 4), - + 14: (-2, -3), + 15: (-2, -2), + 16: (-2, -1), + 17: (-2, 0), + 18: (-2, 1), + 19: (-2, 2), + 20: (-2, 3), + 21: (-2, 4), # row 4 - 22: (-1, -4), 23: (-1, -3), 24: (-1, -2), 25: (-1, -1), 26: (-1, 0), - 27: (-1, 1), 28: (-1, 2), 29: (-1, 3), 30: (-1, 4), - + 22: (-1, -4), + 23: (-1, -3), + 24: (-1, -2), + 25: (-1, -1), + 26: (-1, 0), + 27: (-1, 1), + 28: (-1, 2), + 29: (-1, 3), + 30: (-1, 4), # row 5 - 31: (0, -5), 32: (0, -4), 33: (0, -3), 34: (0, -2), 35: (0, -1), - 36: (0, 0), 37: (0, 1), 38: (0, 2), 39: (0, 3), 40: (0, 4), - + 31: (0, -5), + 32: (0, -4), + 33: (0, -3), + 34: (0, -2), + 35: (0, -1), + 36: (0, 0), + 37: (0, 1), + 38: (0, 2), + 39: (0, 3), + 40: (0, 4), # row 6 - 41: (1, -5), 42: (1, -4), 43: (1, -3), 44: (1, -2), 45: (1, -1), - 46: (1, 0), 47: (1, 1), 48: (1, 2), 49: (1, 3), 50: (1, 4), - + 41: (1, -5), + 42: (1, -4), + 43: (1, -3), + 44: (1, -2), + 45: (1, -1), + 46: (1, 0), + 47: (1, 1), + 48: (1, 2), + 49: (1, 3), + 50: (1, 4), # row 7 - 51: (2, -5), 52: (2, -4), 53: (2, -3), 54: (2, -2), 55: (2, -1), - 56: (2, 0), 57: (2, 1), 58: (2, 2), 59: (2, 3), - + 51: (2, -5), + 52: (2, -4), + 53: (2, -3), + 54: (2, -2), + 55: (2, -1), + 56: (2, 0), + 57: (2, 1), + 58: (2, 2), + 59: (2, 3), # row 8 - 60: (3, -5), 61: (3, -4), 62: (3, -3), 63: (3, -2), 64: (3, -1), - 65: (3, 0), 66: (3, 1), 67: (3, 2), - + 60: (3, -5), + 61: (3, -4), + 62: (3, -3), + 63: (3, -2), + 64: (3, -1), + 65: (3, 0), + 66: (3, 1), + 67: (3, 2), # row 9 - 68: (4, -5), 69: (4, -4), 70: (4, -3), 71: (4, -2), 72: (4, -1), - 73: (4, 0), 74: (4, 1), - + 68: (4, -5), + 69: (4, -4), + 70: (4, -3), + 71: (4, -2), + 72: (4, -1), + 73: (4, 0), + 74: (4, 1), # row 10 - 5: (5, -5), 76: (5, -4), 77: (5, -3), 78: (5, -2), + 75: (5, -5), + 76: (5, -4), + 77: (5, -3), + 78: (5, -2), } # first index goes 'up' in hex coord: string 36 to 46 # second index goes 'right' in hex coord: string 36 to 37 hex_string_coord_dict = { # row 1 - (-4, -1): 1, (-4, 0): 2, (-4, 1): 3, (-4, 2): 4, (-4, 3): 5, (-4, 4): 6, - + (-4, -1): 1, + (-4, 0): 2, + (-4, 1): 3, + (-4, 2): 4, + (-4, 3): 5, + (-4, 4): 6, # row 2 - (-3, -2): 7, (-3, -1): 8, (-3, 0): 9, (-3, 1): 10, (-3, 2): 11, - (-3, 3): 12, (-3, 4): 13, - + (-3, -2): 7, + (-3, -1): 8, + (-3, 0): 9, + (-3, 1): 10, + (-3, 2): 11, + (-3, 3): 12, + (-3, 4): 13, # row 3 - (-2, -3): 14, (-2, -2): 15, (-2, -1): 16, (-2, 0): 17, (-2, 1): 18, - (-2, 2): 19, (-2, 3): 20, (-2, 4): 21, - + (-2, -3): 14, + (-2, -2): 15, + (-2, -1): 16, + (-2, 0): 17, + (-2, 1): 18, + (-2, 2): 19, + (-2, 3): 20, + (-2, 4): 21, # row 4 - (-1, -4): 22, (-1, -3): 23, (-1, -2): 24, (-1, -1): 25, (-1, 0): 26, - (-1, 1): 27, (-1, 2): 28, (-1, 3): 29, (-1, 4): 30, - + (-1, -4): 22, + (-1, -3): 23, + (-1, -2): 24, + (-1, -1): 25, + (-1, 0): 26, + (-1, 1): 27, + (-1, 2): 28, + (-1, 3): 29, + (-1, 4): 30, # row 5 - (0, -5): 31, (0, -4): 32, (0, -3): 33, (0, -2): 34, (0, -1): 35, - (0, 0): 36, (0, 1): 37, (0, 2): 38, (0, 3): 39, (0, 4): 40, - + (0, -5): 31, + (0, -4): 32, + (0, -3): 33, + (0, -2): 34, + (0, -1): 35, + (0, 0): 36, + (0, 1): 37, + (0, 2): 38, + (0, 3): 39, + (0, 4): 40, # row 6 - (1, -5): 41, (1, -4): 42, (1, -3): 43, (1, -2): 44, (1, -1): 45, - (1, 0): 46, (1, 1): 47, (1, 2): 48, (1, 3): 49, (1, 4): 50, - + (1, -5): 41, + (1, -4): 42, + (1, -3): 43, + (1, -2): 44, + (1, -1): 45, + (1, 0): 46, + (1, 1): 47, + (1, 2): 48, + (1, 3): 49, + (1, 4): 50, # row 7 - (2, -5): 51, (2, -4): 52, (2, -3): 53, (2, -2): 54, (2, -1): 55, - (2, 0): 56, (2, 1): 57, (2, 2): 58, (2, 3): 59, - + (2, -5): 51, + (2, -4): 52, + (2, -3): 53, + (2, -2): 54, + (2, -1): 55, + (2, 0): 56, + (2, 1): 57, + (2, 2): 58, + (2, 3): 59, # row 8 - (3, -5): 60, (3, -4): 61, (3, -3): 62, (3, -2): 63, (3, -1): 64, - (3, 0): 65, (3, 1): 66, (3, 2): 67, - + (3, -5): 60, + (3, -4): 61, + (3, -3): 62, + (3, -2): 63, + (3, -1): 64, + (3, 0): 65, + (3, 1): 66, + (3, 2): 67, # row 9 - (4, -5): 68, (4, -4): 69, (4, -3): 70, (4, -2): 71, (4, -1): 72, - (4, 0): 73, (4, 1): 74, - + (4, -5): 68, + (4, -4): 69, + (4, -3): 70, + (4, -2): 71, + (4, -1): 72, + (4, 0): 73, + (4, 1): 74, # row 10 - (5, -5): 75, (5, -4): 76, (5, -3): 77, (5, -2): 78, + (5, -5): 75, + (5, -4): 76, + (5, -3): 77, + (5, -2): 78, } # ----------------------------------------------------------------------------- @@ -139,7 +240,7 @@ def get_icecube_string_from_hex_coord(a, b): def get_icecube_kernel(shape, get_ones=False, float_precision=FLOAT_PRECISION): - ''' + """ Get a kernel of shape 'shape' for IceCube where coordinates of no real strings are set to constant zeros. @@ -161,24 +262,25 @@ def get_icecube_kernel(shape, get_ones=False, float_precision=FLOAT_PRECISION): The icecube kernel with the desired shape. list of tf.Variable A list of tensorflow variables created in this function - ''' + """ zeros = tf.zeros(shape, dtype=float_precision) ones = tf.ones(shape, dtype=float_precision) var_list = [] a_list = [] - for a in xrange(-4, 6): + for a in range(-4, 6): b_list = [] - for b in xrange(-5, 5): + for b in range(-5, 5): if (a, b) in hex_string_coord_dict.keys(): # String exists if get_ones: weights = ones else: - weights = new_weights(shape, - float_precision=float_precision) + weights = new_weights( + shape, float_precision=float_precision + ) var_list.append(weights) else: # virtual string, string does not actually exist diff --git a/tfscripts/hex/rotation.py b/tfscripts/hex/rotation.py index b83fe8a..92f0b85 100644 --- a/tfscripts/hex/rotation.py +++ b/tfscripts/hex/rotation.py @@ -1,11 +1,11 @@ -''' +""" tfscripts hexagonal rotation utility functions Hex rotation utility functions Rotated kernels: static and dynamic ToDo: - Remove duplicate code -''' +""" from __future__ import division, print_function @@ -20,7 +20,7 @@ def get_rotated_corner_weights(corner_weights, azimuth): - '''Rotates the points on a given hexagon layer/circle + """Rotates the points on a given hexagon layer/circle Parameters ---------- @@ -35,7 +35,7 @@ def get_rotated_corner_weights(corner_weights, azimuth): list of np.ndarray or list of tf.Tensor [same shape and type as input] A list of the rotated weights along the given hexagon layer/circle. - ''' + """ size = len(corner_weights) degree_steps = 360.0 / size @@ -44,14 +44,15 @@ def get_rotated_corner_weights(corner_weights, azimuth): rotatedcorner_weights = [] for i in range(size): - newCorner_i = corner_weights[i-b] + a/degree_steps * ( - corner_weights[i-b-1] - corner_weights[i-b]) + newCorner_i = corner_weights[i - b] + a / degree_steps * ( + corner_weights[i - b - 1] - corner_weights[i - b] + ) rotatedcorner_weights.append(newCorner_i) return rotatedcorner_weights def tf_get_rotated_corner_weights(corner_weights, azimuth): - '''Rotates the points on a given hexagon layer/circle + """Rotates the points on a given hexagon layer/circle Parameters ---------- @@ -66,13 +67,14 @@ def tf_get_rotated_corner_weights(corner_weights, azimuth): list of tf.Tensor [same shape and type as input] A list of the rotated weights along the given hexagon layer/circle. - ''' + """ size = corner_weights.get_shape().as_list()[0] num_dims = len(corner_weights.get_shape().as_list()[1:]) degree_steps = 360.0 / size - a = tf.reshape(azimuth % degree_steps, - [tf.shape(input=azimuth)[0]] + [1]*num_dims) + a = tf.reshape( + azimuth % degree_steps, [tf.shape(input=azimuth)[0]] + [1] * num_dims + ) b = tf.cast(azimuth / degree_steps, tf.int32) rotatedcorner_weights = [] for i in range(size): @@ -84,17 +86,19 @@ def tf_get_rotated_corner_weights(corner_weights, azimuth): index_1 = tf.where(index_1 < 0, size + index_1, index_1) index_2 = tf.where(index_2 < 0, size + index_2, index_2) - newCorner_i = (tf.gather(corner_weights, index_1) + a / degree_steps * - (tf.gather(corner_weights, index_2) - - tf.gather(corner_weights, index_1))) + newCorner_i = tf.gather(corner_weights, index_1) + a / degree_steps * ( + tf.gather(corner_weights, index_2) + - tf.gather(corner_weights, index_1) + ) rotatedcorner_weights.append(newCorner_i) return rotatedcorner_weights -def get_dynamic_rotation_hex_kernel(filter_size, azimuth, - float_precision=FLOAT_PRECISION): - '''Dynamically azimuthally rotated hexagonal kernels. +def get_dynamic_rotation_hex_kernel( + filter_size, azimuth, float_precision=FLOAT_PRECISION +): + """Dynamically azimuthally rotated hexagonal kernels. Create Weights for a hexagonal kernel. The Kernel is dynamically rotated by the 'azimuth' angle. @@ -102,7 +106,7 @@ def get_dynamic_rotation_hex_kernel(filter_size, azimuth, while the other dimensions are normal. The hexagonal kernel is of the shape: [kernel_edge_points, kernel_edge_points, *filter_size[2:]] - But elments with coordinates in the first two dimensions, that don't belong + But elements with coordinates in the first two dimensions, that don't belong to the hexagon are set to a tf.Constant 0. The hexagon is defined by filter_size[0:2]. @@ -146,135 +150,170 @@ def get_dynamic_rotation_hex_kernel(filter_size, azimuth, ValueError Description - ''' + """ var_list = [] no_of_dims = len(filter_size) - rotated_filter_size = filter_size[2:-1] + [filter_size[-1]] - Z = tf.zeros([tf.shape(input=azimuth)[0]] + filter_size[2:], - dtype=float_precision) - center_weight = new_weights([1] + filter_size[2:], - float_precision=float_precision) + Z = tf.zeros( + [tf.shape(input=azimuth)[0]] + filter_size[2:], dtype=float_precision + ) + center_weight = new_weights( + [1] + filter_size[2:], float_precision=float_precision + ) var_list.append(center_weight) - multiples = [tf.shape(input=azimuth)[0]] + [1]*(no_of_dims - 2) + multiples = [tf.shape(input=azimuth)[0]] + [1] * (no_of_dims - 2) center_weight = tf.tile(center_weight, multiples) # HARDCODE MAGIC... ToDo: Generalize and clean up if filter_size[0:2] == [2, 0]: # hexagonal 2,0 Filter - corner_weights1 = new_weights([6] + filter_size[2:], - float_precision=float_precision) + corner_weights1 = new_weights( + [6] + filter_size[2:], float_precision=float_precision + ) var_list.append(corner_weights1) elif filter_size[0:2] == [2, 1]: # hexagonal 2,1 Filter - corner_weights1 = new_weights([6] + filter_size[2:], - float_precision=float_precision) + corner_weights1 = new_weights( + [6] + filter_size[2:], float_precision=float_precision + ) var_list.append(corner_weights1) corner_weights2 = [] for i in range(6): - weights = new_weights(filter_size[2:], - float_precision=float_precision) + weights = new_weights( + filter_size[2:], float_precision=float_precision + ) var_list.append(weights) - corner_weights2.extend([Z, weights]) + corner_weights2.extend([Z, weights]) corner_weights2 = tf.stack(corner_weights2) elif filter_size[0:2] == [3, 0]: # hexagonal 3,0 Filter - corner_weights1 = new_weights([6] + filter_size[2:], - float_precision=float_precision) + corner_weights1 = new_weights( + [6] + filter_size[2:], float_precision=float_precision + ) var_list.append(corner_weights1) - corner_weights2 = new_weights([12] + filter_size[2:], - float_precision=float_precision) + corner_weights2 = new_weights( + [12] + filter_size[2:], float_precision=float_precision + ) var_list.append(corner_weights2) elif filter_size[0:2] == [3, 1]: # hexagonal 3,1 Filter - corner_weights1 = new_weights([6] + filter_size[2:], - float_precision=float_precision) + corner_weights1 = new_weights( + [6] + filter_size[2:], float_precision=float_precision + ) var_list.append(corner_weights1) - corner_weights2 = new_weights([12] + filter_size[2:], - float_precision=float_precision) + corner_weights2 = new_weights( + [12] + filter_size[2:], float_precision=float_precision + ) var_list.append(corner_weights2) corner_weights3 = [] for i in range(6): - weights = new_weights(filter_size[2:], - float_precision=float_precision) + weights = new_weights( + filter_size[2:], float_precision=float_precision + ) var_list.append(weights) corner_weights3.extend([Z, weights, Z]) corner_weights3 = tf.stack(corner_weights3) elif filter_size[0:2] == [3, 2]: # hexagonal 3,2 Filter - corner_weights1 = new_weights([6] + filter_size[2:], - float_precision=float_precision) + corner_weights1 = new_weights( + [6] + filter_size[2:], float_precision=float_precision + ) var_list.append(corner_weights1) - corner_weights2 = new_weights([12] + filter_size[2:], - float_precision=float_precision) + corner_weights2 = new_weights( + [12] + filter_size[2:], float_precision=float_precision + ) var_list.append(corner_weights2) corner_weights3 = [] for i in range(6): - weights = new_weights(filter_size[2:], - float_precision=float_precision) + weights = new_weights( + filter_size[2:], float_precision=float_precision + ) var_list.append(weights) corner_weights3.extend([Z, Z, weights]) corner_weights3 = tf.stack(corner_weights3) elif filter_size[0:2] == [4, 0]: # hexagonal 4,0 Filter - corner_weights1 = new_weights([6] + filter_size[2:], - float_precision=float_precision) + corner_weights1 = new_weights( + [6] + filter_size[2:], float_precision=float_precision + ) var_list.append(corner_weights1) - corner_weights2 = new_weights([12] + filter_size[2:], - float_precision=float_precision) + corner_weights2 = new_weights( + [12] + filter_size[2:], float_precision=float_precision + ) var_list.append(corner_weights2) - corner_weights3 = new_weights([18] + filter_size[2:], - float_precision=float_precision) + corner_weights3 = new_weights( + [18] + filter_size[2:], float_precision=float_precision + ) var_list.append(corner_weights3) else: - raise ValueError("get_dynamic_rotation_hex_kernel: Unsupported " - "hexagonal filter_size: {!r}".formt(filter_size[0:2])) + raise ValueError( + "get_dynamic_rotation_hex_kernel: Unsupported " + "hexagonal filter_size: {!r}".format(filter_size[0:2]) + ) rotated_kernel_rows = [] if filter_size[0:2] == [2, 0]: # hexagonal 2,0 Filter A = tf_get_rotated_corner_weights(corner_weights1, azimuth) rotated_kernel_rows.append(tf.stack([Z, A[5], A[0]], axis=1)) - rotated_kernel_rows.append(tf.stack( - [A[3], center_weight, A[1]], axis=1)) + rotated_kernel_rows.append( + tf.stack([A[3], center_weight, A[1]], axis=1) + ) rotated_kernel_rows.append(tf.stack([A[3], A[2], Z], axis=1)) elif filter_size[0:2] == [2, 1] or filter_size[0:2] == [3, 0]: # hexagonal 2,1 and 3,0 Filter A = tf_get_rotated_corner_weights(corner_weights1, azimuth) B = tf_get_rotated_corner_weights(corner_weights2, azimuth) - rotated_kernel_rows.append(tf.stack( - [Z, Z, B[9], B[10], B[11]], axis=1)) - rotated_kernel_rows.append(tf.stack( - [Z, B[8], A[5], A[0], B[0]], axis=1)) - rotated_kernel_rows.append(tf.stack( - [B[7], A[4], center_weight, A[1], B[1]], axis=1)) - rotated_kernel_rows.append(tf.stack( - [B[6], A[3], A[2], B[2], Z], axis=1)) - rotated_kernel_rows.append(tf.stack( - [B[5], B[4], B[3], Z, Z], axis=1)) - elif (filter_size[0:2] == [3, 1] or filter_size[0:2] == [3, 2] or - filter_size[0:2] == [4, 0]): + rotated_kernel_rows.append( + tf.stack([Z, Z, B[9], B[10], B[11]], axis=1) + ) + rotated_kernel_rows.append( + tf.stack([Z, B[8], A[5], A[0], B[0]], axis=1) + ) + rotated_kernel_rows.append( + tf.stack([B[7], A[4], center_weight, A[1], B[1]], axis=1) + ) + rotated_kernel_rows.append( + tf.stack([B[6], A[3], A[2], B[2], Z], axis=1) + ) + rotated_kernel_rows.append(tf.stack([B[5], B[4], B[3], Z, Z], axis=1)) + elif ( + filter_size[0:2] == [3, 1] + or filter_size[0:2] == [3, 2] + or filter_size[0:2] == [4, 0] + ): # hexagonal 3,1 3,2 and 4,0 filter A = tf_get_rotated_corner_weights(corner_weights1, azimuth) B = tf_get_rotated_corner_weights(corner_weights2, azimuth) C = tf_get_rotated_corner_weights(corner_weights3, azimuth) - rotated_kernel_rows.append(tf.stack( - [Z, Z, Z, C[15], C[16], C[17], C[0]], axis=1)) - rotated_kernel_rows.append(tf.stack( - [Z, Z, C[14], B[9], B[10], B[11], C[1]], axis=1)) - rotated_kernel_rows.append(tf.stack( - [Z, C[13], B[8], A[5], A[0], B[0], C[2]], axis=1)) - rotated_kernel_rows.append(tf.stack( - [C[12], B[7], A[4], center_weight, A[1], B[1], C[3]], axis=1)) - rotated_kernel_rows.append(tf.stack( - [C[11], B[6], A[3], A[2], B[2], C[4], Z], axis=1)) - rotated_kernel_rows.append(tf.stack( - [C[10], B[5], B[4], B[3], C[5], Z, Z], axis=1)) - rotated_kernel_rows.append(tf.stack( - [C[9], C[8], C[7], C[6], Z, Z, Z], axis=1)) + rotated_kernel_rows.append( + tf.stack([Z, Z, Z, C[15], C[16], C[17], C[0]], axis=1) + ) + rotated_kernel_rows.append( + tf.stack([Z, Z, C[14], B[9], B[10], B[11], C[1]], axis=1) + ) + rotated_kernel_rows.append( + tf.stack([Z, C[13], B[8], A[5], A[0], B[0], C[2]], axis=1) + ) + rotated_kernel_rows.append( + tf.stack( + [C[12], B[7], A[4], center_weight, A[1], B[1], C[3]], axis=1 + ) + ) + rotated_kernel_rows.append( + tf.stack([C[11], B[6], A[3], A[2], B[2], C[4], Z], axis=1) + ) + rotated_kernel_rows.append( + tf.stack([C[10], B[5], B[4], B[3], C[5], Z, Z], axis=1) + ) + rotated_kernel_rows.append( + tf.stack([C[9], C[8], C[7], C[6], Z, Z, Z], axis=1) + ) else: - raise ValueError("get_dynamic_rotation_hex_kernel: Unsupported " - "hexagonal filter_size: {!r}".formt(filter_size[0:2])) + raise ValueError( + "get_dynamic_rotation_hex_kernel: Unsupported " + "hexagonal filter_size: {!r}".format(filter_size[0:2]) + ) rotated_kernel = tf.stack(rotated_kernel_rows, axis=1) @@ -284,9 +323,10 @@ def get_dynamic_rotation_hex_kernel(filter_size, azimuth, # ------------------------------------------------------------------------- # hexagonal azimuth rotated filters # ------------------------------------------------------------------------- -def get_rotated_hex_kernel(filter_size, num_rotations, - float_precision=FLOAT_PRECISION): - ''' +def get_rotated_hex_kernel( + filter_size, num_rotations, float_precision=FLOAT_PRECISION +): + """ Create Weights for a hexagonal kernel. The kernel is rotated 'num_rotations' many times. Weights are shared over rotated versions. @@ -294,7 +334,7 @@ def get_rotated_hex_kernel(filter_size, num_rotations, while the other dimensions are normal. The hexagonal kernel is of the shape: [kernel_edge_points, kernel_edge_points, *filter_size[2:]] - But elments with coordinates in the first two dimensions, that don't belong + But elements with coordinates in the first two dimensions, that don't belong to the hexagon are set to a tf.Constant 0. The hexagon is defined by filter_size[0:2]. @@ -339,19 +379,19 @@ def get_rotated_hex_kernel(filter_size, num_rotations, ValueError Description - ''' + """ # define function to get new weights with correct shape var_list = [] def get_new_weights(var_list): - weights = new_weights(filter_size[2:-2], - float_precision=float_precision) + weights = new_weights( + filter_size[2:-2], float_precision=float_precision + ) var_list.append(weights) return weights no_of_dims = len(filter_size) - rotated_filter_size = filter_size[2:-1] + [filter_size[-1]*num_rotations] - azimuths = np.linspace(0, 360, num_rotations+1)[:-1] + azimuths = np.linspace(0, 360, num_rotations + 1)[:-1] Z = tf.zeros(filter_size[2:-2], dtype=float_precision) center_weight = get_new_weights(var_list) @@ -366,7 +406,7 @@ def get_new_weights(var_list): corner_weights2 = [] for i in range(6): - corner_weights2.extend([Z, get_new_weights(var_list)]) + corner_weights2.extend([Z, get_new_weights(var_list)]) elif filter_size[0:2] == [3, 0]: # hexagonal 3,0 Filter @@ -398,12 +438,15 @@ def get_new_weights(var_list): corner_weights3 = [get_new_weights(var_list) for i in range(18)] else: - raise ValueError("get_rotated_hex_kernel: Unsupported " - "hexagonal filter_size: {!r}".formt(filter_size[0:2])) + raise ValueError( + "get_rotated_hex_kernel: Unsupported " + "hexagonal filter_size: {!r}".format(filter_size[0:2]) + ) rotated_kernels = [] - in_out_channel_weights = new_weights([num_rotations]+filter_size[-2:], - float_precision=float_precision) + in_out_channel_weights = new_weights( + [num_rotations] + filter_size[-2:], float_precision=float_precision + ) var_list.append(in_out_channel_weights) for i, azimuth in enumerate(azimuths): @@ -420,33 +463,46 @@ def get_new_weights(var_list): B = get_rotated_corner_weights(corner_weights2, azimuth) rotated_kernel_rows.append(tf.stack([Z, Z, B[9], B[10], B[11]])) rotated_kernel_rows.append(tf.stack([Z, B[8], A[5], A[0], B[0]])) - rotated_kernel_rows.append(tf.stack( - [B[7], A[4], center_weight, A[1], B[1]])) + rotated_kernel_rows.append( + tf.stack([B[7], A[4], center_weight, A[1], B[1]]) + ) rotated_kernel_rows.append(tf.stack([B[6], A[3], A[2], B[2], Z])) rotated_kernel_rows.append(tf.stack([B[5], B[4], B[3], Z, Z])) - elif (filter_size[0:2] == [3, 1] or filter_size[0:2] == [3, 2] or - filter_size[0:2] == [4, 0]): + elif ( + filter_size[0:2] == [3, 1] + or filter_size[0:2] == [3, 2] + or filter_size[0:2] == [4, 0] + ): # hexagonal 3,1 3,2 and 4,0 filter A = get_rotated_corner_weights(corner_weights1, azimuth) B = get_rotated_corner_weights(corner_weights2, azimuth) C = get_rotated_corner_weights(corner_weights3, azimuth) - rotated_kernel_rows.append(tf.stack( - [Z, Z, Z, C[15], C[16], C[17], C[0]])) - rotated_kernel_rows.append(tf.stack( - [Z, Z, C[14], B[9], B[10], B[11], C[1]])) - rotated_kernel_rows.append(tf.stack( - [Z, C[13], B[8], A[5], A[0], B[0], C[2]])) - rotated_kernel_rows.append(tf.stack( - [C[12], B[7], A[4], center_weight, A[1], B[1], C[3]])) - rotated_kernel_rows.append(tf.stack( - [C[11], B[6], A[3], A[2], B[2], C[4], Z])) - rotated_kernel_rows.append(tf.stack( - [C[10], B[5], B[4], B[3], C[5], Z, Z])) - rotated_kernel_rows.append(tf.stack( - [C[9], C[8], C[7], C[6], Z, Z, Z])) + rotated_kernel_rows.append( + tf.stack([Z, Z, Z, C[15], C[16], C[17], C[0]]) + ) + rotated_kernel_rows.append( + tf.stack([Z, Z, C[14], B[9], B[10], B[11], C[1]]) + ) + rotated_kernel_rows.append( + tf.stack([Z, C[13], B[8], A[5], A[0], B[0], C[2]]) + ) + rotated_kernel_rows.append( + tf.stack([C[12], B[7], A[4], center_weight, A[1], B[1], C[3]]) + ) + rotated_kernel_rows.append( + tf.stack([C[11], B[6], A[3], A[2], B[2], C[4], Z]) + ) + rotated_kernel_rows.append( + tf.stack([C[10], B[5], B[4], B[3], C[5], Z, Z]) + ) + rotated_kernel_rows.append( + tf.stack([C[9], C[8], C[7], C[6], Z, Z, Z]) + ) else: - raise ValueError("get_rotated_hex_kernel: Unsupported hexagonal " - "filter_size: {!r}".formt(filter_size[0:2])) + raise ValueError( + "get_rotated_hex_kernel: Unsupported hexagonal " + "filter_size: {!r}".format(filter_size[0:2]) + ) rotated_kernel_single = tf.stack(rotated_kernel_rows) # Add free parameters for in and out channel @@ -454,14 +510,15 @@ def get_new_weights(var_list): rotated_kernel_single = tf.expand_dims(rotated_kernel_single, -1) rotated_kernel_single = tf.expand_dims(rotated_kernel_single, -1) - multiples = [1 for i in range(no_of_dims-2)] + filter_size[-2:] + multiples = [1 for i in range(no_of_dims - 2)] + filter_size[-2:] rotated_kernel_tiled = tf.tile(rotated_kernel_single, multiples) # multiply weights to make in and out channels independent - rotated_kernel = rotated_kernel_tiled*in_out_channel_weights[i] + rotated_kernel = rotated_kernel_tiled * in_out_channel_weights[i] rotated_kernels.append(rotated_kernel) - rotated_kernels = tf.concat(values=rotated_kernels, - axis=len(filter_size)-1) + rotated_kernels = tf.concat( + values=rotated_kernels, axis=len(filter_size) - 1 + ) return rotated_kernels, var_list diff --git a/tfscripts/hex/visual.py b/tfscripts/hex/visual.py index 19509f2..bc38796 100644 --- a/tfscripts/hex/visual.py +++ b/tfscripts/hex/visual.py @@ -1,12 +1,13 @@ -''' +""" tfscripts hexagonal convolution visualization functions -''' +""" from __future__ import division, print_function import numpy as np import tensorflow as tf import matplotlib + try: import _tkinter except ImportError: @@ -27,8 +28,8 @@ def print_hex_data(hex_data): hex_data = {(a,b): value)} and a, b hexagonal-coordinates """ if len(hex_data.keys()) == 1: - print('HexKernel is of size 1') - print('\t1') + print("HexKernel is of size 1") + print("\t1") else: # get range @@ -48,20 +49,20 @@ def print_hex_data(hex_data): min_b = b # print on hexagonal grid - for a in range(max_a, min_a-1, -1): - row = '' + for a in range(max_a, min_a - 1, -1): + row = "" for i in range(a - min_a): - row += ' ' - for b in range(min_b, max_b+1, 1): + row += " " + for b in range(min_b, max_b + 1, 1): if (a, b) in hex_data.keys(): - row += ' {:3d}'.format(hex_data[(a, b)]) + row += " {:3d}".format(hex_data[(a, b)]) else: - row += ' ' - print(row+'\n') + row += " " + print(row + "\n") -def plot_hex2D(hex_grid, file=None, hex_grid_spacing=1.0, norm='linear'): - '''Plots a 2D hex_grid heatmap +def plot_hex2D(hex_grid, file=None, hex_grid_spacing=1.0, norm="linear"): + """Plots a 2D hex_grid heatmap Assumes hex_grid is shaped: 1 1 0 @@ -89,62 +90,73 @@ def plot_hex2D(hex_grid, file=None, hex_grid_spacing=1.0, norm='linear'): ------ ValueError Description - ''' + """ fig = plt.figure() hex_grid = np.asarray(hex_grid) if len(hex_grid.shape) != 2: - raise ValueError('Expects 2dim hexgrid, but got {!r}'.format( - hexgrid.shape)) + raise ValueError( + "Expects 2dim hexgrid, but got {!r}".format(hex_grid.shape) + ) if hex_grid.shape[0] != hex_grid.shape[1]: - raise ValueError('Only supports quadratic grids. {!r}'.format( - hex_grid.shape)) + raise ValueError( + "Only supports quadratic grids. {!r}".format(hex_grid.shape) + ) minValue = np.min(hex_grid) maxValue = np.max(hex_grid) - if norm == 'linear': + if norm == "linear": norm = matplotlib.colors.Normalize(vmin=minValue, vmax=maxValue) - elif norm == 'log': - norm = matplotlib.colors.LogNorm(vmin=max(0.0001, minValue), - vmax=maxValue) + elif norm == "log": + norm = matplotlib.colors.LogNorm( + vmin=max(0.0001, minValue), vmax=maxValue + ) else: - raise ValueError('Wrong value for norm: {!r}'.format(norm)) - cmap = matplotlib.cm.ScalarMappable(norm=norm, - cmap=plt.get_cmap('viridis')) + raise ValueError("Wrong value for norm: {!r}".format(norm)) + cmap = matplotlib.cm.ScalarMappable( + norm=norm, cmap=plt.get_cmap("viridis") + ) size = len(hex_grid) - half_dim = size//2 + half_dim = size // 2 # create list to hold patches patch_list = [] for a in range(-half_dim, half_dim + 1): for b in range(-half_dim, half_dim + 1): - offset_x = hex_grid_spacing/2. * b - x = offset_x + a*hex_grid_spacing - y = b*(hex_grid_spacing*np.sqrt(0.75)) + offset_x = hex_grid_spacing / 2.0 * b + x = offset_x + a * hex_grid_spacing + y = b * (hex_grid_spacing * np.sqrt(0.75)) color = cmap.to_rgba(hex_grid[a + half_dim, b + half_dim]) patch_list.append( - matplotlib.patches.RegularPolygon( - xy=(x, y), - numVertices=6, - radius=hex_grid_spacing/np.sqrt(3), - orientation=0., - facecolor=color, - edgecolor='black' - ) + matplotlib.patches.RegularPolygon( + xy=(x, y), + numVertices=6, + radius=hex_grid_spacing / np.sqrt(3), + orientation=0.0, + facecolor=color, + edgecolor="black", + ) ) - pc = matplotlib.collections.PatchCollection(patch_list, - match_original=True) + pc = matplotlib.collections.PatchCollection( + patch_list, match_original=True + ) ax = plt.gca() ax.add_collection(pc) plt.plot(0, 0) - plt.xlim = [-1.1*size*hex_grid_spacing/2, 1.1*size*hex_grid_spacing/2] - plt.ylim = [-0.7*size*hex_grid_spacing/2, 0.7*size*hex_grid_spacing/2] - - plt.axes().set_aspect('equal') + plt.xlim = [ + -1.1 * size * hex_grid_spacing / 2, + 1.1 * size * hex_grid_spacing / 2, + ] + plt.ylim = [ + -0.7 * size * hex_grid_spacing / 2, + 0.7 * size * hex_grid_spacing / 2, + ] + + plt.axes().set_aspect("equal") if file is None: plt.show() else: @@ -152,15 +164,16 @@ def plot_hex2D(hex_grid, file=None, hex_grid_spacing=1.0, norm='linear'): plt.close(fig) -def visualize_rotated_hex_kernel(filter_size, num_rotations, - file='Rotation_{azimuth:2.2f}.png'): - '''Visualize hexagonal azimuth rotated filters +def visualize_rotated_hex_kernel( + filter_size, num_rotations, file="Rotation_{azimuth:2.2f}.png" +): + """Visualize hexagonal azimuth rotated filters Create Weights for a hexagonal kernel. The Kernel will be of a hexagonal shape in the first two dimensions. The hexagonal kernel is off the shape: [kernel_edge_points, kernel_edge_points,*filter_size[2:]] - But elments with coordinates in the first two dimensions, that don't belong + But elements with coordinates in the first two dimensions, that don't belong to the hexagon are set to zero. The hexagon is defined by filter_size[0:2]. @@ -188,9 +201,9 @@ def visualize_rotated_hex_kernel(filter_size, num_rotations, ------ ValueError Description - ''' + """ - azimuths = np.linspace(0, 360, num_rotations+1)[:-1] + azimuths = np.linspace(0, 360, num_rotations + 1)[:-1] Z = 0 center_weight = np.random.uniform(1, high=15, size=1) @@ -204,7 +217,8 @@ def visualize_rotated_hex_kernel(filter_size, num_rotations, corner_weights2 = [] for i in range(6): corner_weights2.extend( - [Z, np.random.uniform(1, high=15, size=1)[0]]) + [Z, np.random.uniform(1, high=15, size=1)[0]] + ) elif filter_size[0:2] == [3, 0]: # hexagonal 3,0 Filter corner_weights1 = np.random.uniform(1, high=15, size=6) @@ -216,7 +230,8 @@ def visualize_rotated_hex_kernel(filter_size, num_rotations, corner_weights3 = [] for i in range(6): corner_weights3.extend( - [Z, np.random.uniform(1, high=15, size=1)[0], Z]) + [Z, np.random.uniform(1, high=15, size=1)[0], Z] + ) elif filter_size[0:2] == [3, 2]: # hexagonal 3,2 Filter corner_weights1 = np.random.uniform(1, high=15, size=6) @@ -224,15 +239,18 @@ def visualize_rotated_hex_kernel(filter_size, num_rotations, corner_weights3 = [] for i in range(6): corner_weights3.extend( - [Z, Z, np.random.uniform(1, high=15, size=1)[0]]) + [Z, Z, np.random.uniform(1, high=15, size=1)[0]] + ) elif filter_size[0:2] == [4, 0]: # hexagonal 4,0 Filter corner_weights1 = np.random.uniform(1, high=15, size=6) corner_weights2 = np.random.uniform(1, high=15, size=12) corner_weights3 = np.random.uniform(1, high=15, size=18) else: - raise ValueError("visualize_rotated_hex_kernel: Unsupported hexagonal " - "filter_size: {!r}".formt(filter_size[0:2])) + raise ValueError( + "visualize_rotated_hex_kernel: Unsupported hexagonal " + "filter_size: {!r}".format(filter_size[0:2]) + ) rotated_kernels = [] for azimuth in azimuths: @@ -252,8 +270,11 @@ def visualize_rotated_hex_kernel(filter_size, num_rotations, rotated_kernel_rows.append([B[7], A[4], center_weight, A[1], B[1]]) rotated_kernel_rows.append([B[6], A[3], A[2], B[2], Z]) rotated_kernel_rows.append([B[5], B[4], B[3], Z, Z]) - elif (filter_size[0:2] == [3, 1] or filter_size[0:2] == [3, 2] or - filter_size[0:2] == [4, 0]): + elif ( + filter_size[0:2] == [3, 1] + or filter_size[0:2] == [3, 2] + or filter_size[0:2] == [4, 0] + ): # hexagonal 3,1 Filter A = get_rotated_corner_weights(corner_weights1, azimuth) B = get_rotated_corner_weights(corner_weights2, azimuth) @@ -261,25 +282,25 @@ def visualize_rotated_hex_kernel(filter_size, num_rotations, rotated_kernel_rows.append([Z, Z, Z, C[15], C[16], C[17], C[0]]) rotated_kernel_rows.append([Z, Z, C[14], B[9], B[10], B[11], C[1]]) rotated_kernel_rows.append( - [Z, C[13], B[8], A[5], A[0], B[0], C[2]]) + [Z, C[13], B[8], A[5], A[0], B[0], C[2]] + ) rotated_kernel_rows.append( - [C[12], B[7], A[4], center_weight, A[1], B[1], C[3]]) + [C[12], B[7], A[4], center_weight, A[1], B[1], C[3]] + ) rotated_kernel_rows.append( - [C[11], B[6], A[3], A[2], B[2], C[4], Z]) + [C[11], B[6], A[3], A[2], B[2], C[4], Z] + ) rotated_kernel_rows.append([C[10], B[5], B[4], B[3], C[5], Z, Z]) rotated_kernel_rows.append([C[9], C[8], C[7], C[6], Z, Z, Z]) else: - raise ValueError("visualize_rotated_hex_kernel: Unsupported " - "hexagonal filter_size: {!r}".formt( - filter_size[0:2])) + raise ValueError( + "visualize_rotated_hex_kernel: Unsupported " + "hexagonal filter_size: {!r}".format(filter_size[0:2]) + ) rotated_kernel = rotated_kernel_rows rotated_kernels.append(rotated_kernel) rotated_kernels = np.asarray(rotated_kernels) - width = 3 - height = int(num_rotations/width) - - num_rows = len(rotated_kernels[0]) for i, rot in enumerate(rotated_kernels): plot_hex2D(rot, file=file.format(azimuth=azimuths[i])) diff --git a/tfscripts/layers.py b/tfscripts/layers.py index 2ec6aad..e4e9d88 100644 --- a/tfscripts/layers.py +++ b/tfscripts/layers.py @@ -1,6 +1,6 @@ -''' +""" tfscripts layers are defined here -''' +""" from __future__ import division, print_function @@ -20,7 +20,7 @@ def flatten_layer(layer): - ''' + """ Helper-function for flattening a layer Parameters @@ -32,7 +32,7 @@ def flatten_layer(layer): ------- tf.Tensor Flattened layer. - ''' + """ # Get the shape of the input layer. layer_shape = layer.get_shape() @@ -49,7 +49,7 @@ def flatten_layer(layer): def flatten_hex_layer(hex_layer): - '''Helper-function for flattening an hexagonally shaped layer + """Helper-function for flattening an hexagonally shaped layer Assumes hexagonal shape in the first two spatial dimensions of the form: @@ -65,7 +65,7 @@ def flatten_hex_layer(hex_layer): where s is the size of the x- and y-dimension will be discarded. - These correspond to the elments outside of the hexagon + These correspond to the elements outside of the hexagon Parameters ---------- @@ -83,7 +83,7 @@ def flatten_hex_layer(hex_layer): ------ ValueError Description - ''' + """ # Get the shape of the input layer. layer_shape = hex_layer.get_shape().as_list() @@ -93,8 +93,10 @@ def flatten_hex_layer(hex_layer): size = layer_shape[1] if layer_shape[2] != size: - raise ValueError("flatten_hex_layer: size of x- and y- dimension must " - "match, but are {!r}".format(layer_shape[1:3])) + raise ValueError( + "flatten_hex_layer: size of x- and y- dimension must " + "match, but are {!r}".format(layer_shape[1:3]) + ) num_features = np.prod(layer_shape[3:]) @@ -102,9 +104,18 @@ def flatten_hex_layer(hex_layer): flat_elements = [] for a in range(size): for b in range(size): - if not (a + b < (size-1)//2 or a + b > 2*(size-1) - (size-1)//2): - flattened_element = tf.reshape(hex_layer[:, a, b, ], - [-1, num_features]) + if not ( + a + b < (size - 1) // 2 + or a + b > 2 * (size - 1) - (size - 1) // 2 + ): + flattened_element = tf.reshape( + hex_layer[ + :, + a, + b, + ], + [-1, num_features], + ) flat_elements.append(flattened_element) total_num_features += num_features @@ -113,7 +124,7 @@ def flatten_hex_layer(hex_layer): class ConvNdLayer(tf.Module): - """TF Module for creating a new nD Convolutional Layer + """TF Module for creating a new n-dim Convolutional Layer 1 <= n <=4 are supported. For n == 3 (3 spatial dimensions x, y, and z): @@ -123,32 +134,34 @@ class ConvNdLayer(tf.Module): due to downsizing in pooling operation. """ - def __init__(self, - input_shape, - filter_size, - num_filters, - pooling_type=None, - pooling_strides=None, - pooling_ksize=None, - pooling_padding='SAME', - use_dropout=False, - activation='elu', - strides=None, - padding='SAME', - use_batch_normalisation=False, - dilation_rate=None, - use_residual=False, - method='convolution', - repair_std_deviation=True, - var_list=None, - weights=None, - biases=None, - trafo=None, - hex_num_rotations=1, - hex_azimuth=None, - hex_zero_out=False, - float_precision=FLOAT_PRECISION, - name=None): + def __init__( + self, + input_shape, + filter_size, + num_filters, + pooling_type=None, + pooling_strides=None, + pooling_ksize=None, + pooling_padding="SAME", + use_dropout=False, + activation="elu", + strides=None, + padding="SAME", + use_batch_normalisation=False, + dilation_rate=None, + use_residual=False, + method="convolution", + repair_std_deviation=True, + var_list=None, + weights=None, + biases=None, + trafo=None, + hex_num_rotations=1, + hex_azimuth=None, + hex_zero_out=False, + float_precision=FLOAT_PRECISION, + name=None, + ): """Initialize object Parameters @@ -321,7 +334,7 @@ def __init__(self, pooling_ksize = [1, 2, 2, 1] if strides is None: strides = [1, 1, 1, 1] - + elif num_dims == 1: # 1D convolution if pooling_strides is None: @@ -332,12 +345,12 @@ def __init__(self, strides = [1, 1, 1] else: - msg = 'Currently only 1D, 2D, 3D or 4D supported {!r}' + msg = "Currently only 1D, 2D, 3D or 4D supported {!r}" raise ValueError(msg.format(input_shape)) # make sure inferred dimension matches filter_size if not len(filter_size) == num_dims: - err_msg = 'Filter size {!r} does not fit to input shape {!r}' + err_msg = "Filter size {!r} does not fit to input shape {!r}" raise ValueError(err_msg.format(input_shape)) num_input_channels = input_shape[-1] @@ -346,181 +359,201 @@ def __init__(self, shape = list(filter_size) + [num_input_channels, num_filters] # Create new weights aka. filters with the given shape. - if method.lower() == 'convolution': + if method.lower() == "convolution": if weights is None: # weights = new_kernel_weights(shape=shape) - weights = new_weights(shape=shape, - float_precision=float_precision) + weights = new_weights( + shape=shape, float_precision=float_precision + ) # Create new biases, one for each filter. if biases is None: - biases = new_biases(length=num_filters, - float_precision=float_precision) + biases = new_biases( + length=num_filters, float_precision=float_precision + ) if num_dims == 1 or num_dims == 2 or num_dims == 3: # create a temp function with all parameters set def temp_func(inputs): - return tf.nn.convolution(input=inputs, - filters=weights, - strides=strides[1:-1], - padding=padding, - dilations=dilation_rate) + return tf.nn.convolution( + input=inputs, + filters=weights, + strides=strides[1:-1], + padding=padding, + dilations=dilation_rate, + ) + self.conv_layer = temp_func elif num_dims == 4: # create a temp function with all parameters set def temp_func(inputs): - return conv.conv4d_stacked(input=inputs, - filter=weights, - strides=strides, - padding=padding, - dilation_rate=dilation_rate) + return conv.conv4d_stacked( + input=inputs, + filter=weights, + strides=strides, + padding=padding, + dilation_rate=dilation_rate, + ) + self.conv_layer = temp_func # --------------------- # Hexagonal convolution # --------------------- - elif method.lower() == 'hex_convolution': + elif method.lower() == "hex_convolution": if num_dims == 1: - raise NotImplementedError( - '1D hex_convolution not implemented') + raise NotImplementedError("1D hex_convolution not implemented") elif num_dims == 2 or num_dims == 3: self.conv_layer = hx.ConvHex( - input_shape=input_shape, - filter_size=filter_size, - num_filters=num_filters, - padding=padding, - strides=strides, - num_rotations=hex_num_rotations, - azimuth=hex_azimuth, - dilation_rate=dilation_rate, - zero_out=hex_zero_out, - kernel=weights, - var_list=var_list, - float_precision=float_precision, - ) + input_shape=input_shape, + filter_size=filter_size, + num_filters=num_filters, + padding=padding, + strides=strides, + num_rotations=hex_num_rotations, + azimuth=hex_azimuth, + dilation_rate=dilation_rate, + zero_out=hex_zero_out, + kernel=weights, + var_list=var_list, + float_precision=float_precision, + ) elif num_dims == 4: self.conv_layer = hx.ConvHex4d( - input_shape=input_shape, - filter_size=filter_size, - num_filters=num_filters, - padding=padding, - strides=strides, - num_rotations=hex_num_rotations, - azimuth=hex_azimuth, - dilation_rate=dilation_rate, - zero_out=hex_zero_out, - kernel=weights, - var_list=var_list, - float_precision=float_precision, - ) + input_shape=input_shape, + filter_size=filter_size, + num_filters=num_filters, + padding=padding, + strides=strides, + num_rotations=hex_num_rotations, + azimuth=hex_azimuth, + dilation_rate=dilation_rate, + zero_out=hex_zero_out, + kernel=weights, + var_list=var_list, + float_precision=float_precision, + ) # Create new biases, one for each filter. if biases is None: - biases = new_biases(length=num_filters*hex_num_rotations, - float_precision=float_precision) + biases = new_biases( + length=num_filters * hex_num_rotations, + float_precision=float_precision, + ) # ------------------- # locally connected # ------------------- - elif method.lower() == 'locally_connected': + elif method.lower() == "locally_connected": if num_dims == 1: self.conv_layer = conv.LocallyConnected1d( - input_shape=input_shape, - num_outputs=num_filters, - filter_size=filter_size, - kernel=weights, - strides=strides[1:-1], - padding=padding, - dilation_rate=dilation_rate, - float_precision=float_precision) + input_shape=input_shape, + num_outputs=num_filters, + filter_size=filter_size, + kernel=weights, + strides=strides[1:-1], + padding=padding, + dilation_rate=dilation_rate, + float_precision=float_precision, + ) elif num_dims == 2: self.conv_layer = conv.LocallyConnected2d( - input_shape=input_shape, - num_outputs=num_filters, - filter_size=filter_size, - kernel=weights, - strides=strides[1:-1], - padding=padding, - dilation_rate=dilation_rate, - float_precision=float_precision) + input_shape=input_shape, + num_outputs=num_filters, + filter_size=filter_size, + kernel=weights, + strides=strides[1:-1], + padding=padding, + dilation_rate=dilation_rate, + float_precision=float_precision, + ) elif num_dims == 3: self.conv_layer = conv.LocallyConnected3d( - input_shape=input_shape, - num_outputs=num_filters, - filter_size=filter_size, - kernel=weights, - strides=strides[1:-1], - padding=padding, - dilation_rate=dilation_rate, - float_precision=float_precision) + input_shape=input_shape, + num_outputs=num_filters, + filter_size=filter_size, + kernel=weights, + strides=strides[1:-1], + padding=padding, + dilation_rate=dilation_rate, + float_precision=float_precision, + ) elif num_dims == 4: raise NotImplementedError( - '4D locally connected not implemented!') + "4D locally connected not implemented!" + ) # Create new biases, one for each filter and position if biases is None: biases = new_locally_connected_weights( shape=self.conv_layer.output_shape[1:], shared_axes=[i for i in range(num_dims)], - float_precision=float_precision) + float_precision=float_precision, + ) # ------------------- # local trafo # ------------------- - elif method.lower() == 'local_trafo': + elif method.lower() == "local_trafo": - assert (weights is None and biases is None) + assert weights is None and biases is None if num_dims == 3: # create a temp function with all parameters set def temp_func(inputs): return conv.trans3d_op( - input=inputs, - num_out_channel=num_filters, - filter_size=filter_size, - method=method, - trafo=trafo, - filter=weights, - strides=strides[1:-1], - padding=padding, - dilation_rate=dilation_rate, - stack_axis=None, - ) + input=inputs, + num_out_channel=num_filters, + filter_size=filter_size, + method=method, + trafo=trafo, + filter=weights, + strides=strides[1:-1], + padding=padding, + dilation_rate=dilation_rate, + stack_axis=None, + ) + self.conv_layer = temp_func else: - raise NotImplementedError('local_trafo currently only for 3D') + raise NotImplementedError("local_trafo currently only for 3D") # -------------------- # dynamic convolution # -------------------- - elif method.lower() == 'dynamic_convolution': + elif method.lower() == "dynamic_convolution": assert weights is not None if num_dims == 1 or num_dims == 2 or num_dims == 3: # create a temp function with all parameters set def temp_func(inputs): - return conv.dynamic_conv(inputs=inputs, - filters=weights, - strides=strides[1:-1], - padding=padding, - dilation_rate=dilation_rate) + return conv.dynamic_conv( + inputs=inputs, + filters=weights, + strides=strides[1:-1], + padding=padding, + dilation_rate=dilation_rate, + ) + self.conv_layer = temp_func elif num_dims == 4: raise NotImplementedError( - '4D dynamic_convolution not implemented') + "4D dynamic_convolution not implemented" + ) if biases is None: - biases = new_biases(length=num_filters, - float_precision=float_precision) + biases = new_biases( + length=num_filters, float_precision=float_precision + ) else: - raise ValueError('Unknown method: {!r}'.format(method)) + raise ValueError("Unknown method: {!r}".format(method)) self.biases = biases self.weights = weights @@ -531,12 +564,13 @@ def temp_func(inputs): conv_layer_output = self.conv_layer(dummy_data) self.activation = core.Activation( - activation_type=activation, - input_shape=conv_layer_output.shape, - use_batch_normalisation=use_batch_normalisation, - float_precision=float_precision) + activation_type=activation, + input_shape=conv_layer_output.shape, + use_batch_normalisation=use_batch_normalisation, + float_precision=float_precision, + ) - # assing and keep track of settings + # assign and keep track of settings self.input_shape = input_shape self.num_dims = num_dims self.filter_size = filter_size @@ -559,9 +593,10 @@ def temp_func(inputs): # Use as Residual if use_residual: self.residual_add = core.AddResidual( - residual_shape=self.output_shape, - strides=strides, - float_precision=float_precision) + residual_shape=self.output_shape, + strides=strides, + float_precision=float_precision, + ) def __call__(self, inputs, is_training, keep_prob=None): """Apply Module. @@ -596,8 +631,11 @@ def __call__(self, inputs, is_training, keep_prob=None): # check shape of input tensor if inputs.shape[1:].as_list() != self.input_shape[1:]: - raise ValueError('Shape mismatch: {!r} != {!r}'.format( - inputs.shape[1:].as_list(), self.input_shape[1:])) + raise ValueError( + "Shape mismatch: {!r} != {!r}".format( + inputs.shape[1:].as_list(), self.input_shape[1:] + ) + ) # Perform convolution/transformation layer = self.conv_layer(inputs) @@ -621,7 +659,7 @@ def __call__(self, inputs, is_training, keep_prob=None): # In the case of the hex_convolution, this factor gets reduced to # the number of non zero elements in the hex kernel. if self.repair_std_deviation: - if self.method.lower() == 'hex_convolution': + if self.method.lower() == "hex_convolution": num_filter_vars = hx.get_num_hex_points(self.filter_size[0]) if len(self.filter_size) > 2: @@ -635,14 +673,15 @@ def __call__(self, inputs, is_training, keep_prob=None): layer = layer / np.sqrt(num_filter_vars * num_input_channels) else: - layer = layer / np.sqrt(np.prod(self.filter_size) - * num_input_channels) + layer = layer / np.sqrt( + np.prod(self.filter_size) * num_input_channels + ) # Add the biases to the results of the convolution. # A bias-value is added to each filter-channel. if self.biases is not None: if self.repair_std_deviation: - layer = (layer + self.biases) / np.sqrt(2.) + layer = (layer + self.biases) / np.sqrt(2.0) else: layer = layer + self.biases @@ -670,44 +709,51 @@ def _apply_pooling(self, layer): The layer on which to apply pooling """ if self.num_dims == 1: - layer = pooling.pool1d(layer=layer, - ksize=self.pooling_ksize, - strides=self.pooling_strides, - padding=self.pooling_padding, - pooling_type=self.pooling_type, - ) + layer = pooling.pool1d( + layer=layer, + ksize=self.pooling_ksize, + strides=self.pooling_strides, + padding=self.pooling_padding, + pooling_type=self.pooling_type, + ) elif self.num_dims == 2: - layer = pooling.pool2d(layer=layer, - ksize=self.pooling_ksize, - strides=self.pooling_strides, - padding=self.pooling_padding, - pooling_type=self.pooling_type, - ) + layer = pooling.pool2d( + layer=layer, + ksize=self.pooling_ksize, + strides=self.pooling_strides, + padding=self.pooling_padding, + pooling_type=self.pooling_type, + ) elif self.num_dims == 3: - layer = pooling.pool3d(layer=layer, - ksize=self.pooling_ksize, - strides=self.pooling_strides, - padding=self.pooling_padding, - pooling_type=self.pooling_type, - ) + layer = pooling.pool3d( + layer=layer, + ksize=self.pooling_ksize, + strides=self.pooling_strides, + padding=self.pooling_padding, + pooling_type=self.pooling_type, + ) elif self.num_dims == 4: - if pooling_type == 'max': + if self.pooling_type == "max": layer = pooling.max_pool4d_stacked( - input=layer, - ksize=self.pooling_ksize, - strides=self.pooling_strides, - padding=self.pooling_padding) - elif pooling_type == 'avg': + input=layer, + ksize=self.pooling_ksize, + strides=self.pooling_strides, + padding=self.pooling_padding, + ) + elif self.pooling_type == "avg": layer = pooling.avg_pool4d_stacked( - input=layer, - ksize=self.pooling_ksize, - strides=self.pooling_strides, - padding=self.pooling_padding) + input=layer, + ksize=self.pooling_ksize, + strides=self.pooling_strides, + padding=self.pooling_padding, + ) else: - raise NotImplementedError("Pooling type not supported: " - "{!r}".format(self.pooling_type)) + raise NotImplementedError( + "Pooling type not supported: " + "{!r}".format(self.pooling_type) + ) else: - raise NotImplementedError('Only supported 1d, 2d, 3d, 4d!') + raise NotImplementedError("Only supported 1d, 2d, 3d, 4d!") return layer @@ -715,23 +761,25 @@ def _apply_pooling(self, layer): class FCLayer(tf.Module): """TF Module for creating a new Fully-Connected Layer - input: 2-dim tensor of shape [batch_size, num_inputs] - output: 2-dim tensor of shape [batch_size, num_outputs] + input: 2-dim tensor of shape [batch_size, num_inputs] + output: 2-dim tensor of shape [batch_size, num_outputs] """ - def __init__(self, - input_shape, - num_outputs, - use_dropout=False, - activation='elu', - use_batch_normalisation=False, - use_residual=False, - weights=None, - biases=None, - max_out_size=None, - repair_std_deviation=True, - float_precision=FLOAT_PRECISION, - name=None): + def __init__( + self, + input_shape, + num_outputs, + use_dropout=False, + activation="elu", + use_batch_normalisation=False, + use_residual=False, + weights=None, + biases=None, + max_out_size=None, + repair_std_deviation=True, + float_precision=FLOAT_PRECISION, + name=None, + ): """Initialize object Parameters @@ -786,16 +834,18 @@ def __init__(self, ) if biases is None: biases = new_biases( - length=num_outputs, float_precision=float_precision) + length=num_outputs, float_precision=float_precision + ) self.biases = biases self.weights = weights self.activation = core.Activation( - activation_type=activation, - input_shape=[None, num_outputs], - use_batch_normalisation=use_batch_normalisation, - float_precision=float_precision) + activation_type=activation, + input_shape=[None, num_outputs], + use_batch_normalisation=use_batch_normalisation, + float_precision=float_precision, + ) # calculate residual strides if max_out_size is not None: @@ -806,7 +856,7 @@ def __init__(self, layer_shape[-1] = layer_shape[-1] // max_out_size channel_stride = max(1, num_inputs // layer_shape[-1]) - res_strides = [1 for i in input_shape[:-1]]+[channel_stride] + res_strides = [1 for i in input_shape[:-1]] + [channel_stride] else: res_strides = None @@ -820,9 +870,10 @@ def __init__(self, # Use as Residual if use_residual: self.residual_add = core.AddResidual( - residual_shape=output_shape, - strides=res_strides, - float_precision=float_precision) + residual_shape=output_shape, + strides=res_strides, + float_precision=float_precision, + ) self.output_shape = output_shape self.input_shape = input_shape @@ -865,8 +916,11 @@ def __call__(self, inputs, is_training, keep_prob): # check shape of input tensor if inputs.shape[1:].as_list() != self.input_shape[1:]: - raise ValueError('Shape mismatch: {!r} != {!r}'.format( - inputs.shape[1:].as_list(), self.input_shape[1:])) + raise ValueError( + "Shape mismatch: {!r} != {!r}".format( + inputs.shape[1:].as_list(), self.input_shape[1:] + ) + ) num_inputs = inputs.get_shape().as_list()[-1] @@ -878,7 +932,7 @@ def __call__(self, inputs, is_training, keep_prob): if self.repair_std_deviation: layer = layer / np.sqrt(num_inputs) - layer = (layer + self.biases) / np.sqrt(2.) + layer = (layer + self.biases) / np.sqrt(2.0) else: layer = layer + self.biases @@ -887,9 +941,9 @@ def __call__(self, inputs, is_training, keep_prob): # max out channel dimension if specified if self.max_out_size is not None: - layer = core.maxout(inputs=layer, - num_units=self.max_out_size, - axis=-1) + layer = core.maxout( + inputs=layer, num_units=self.max_out_size, axis=-1 + ) # Use as Residual if self.use_residual: @@ -904,23 +958,25 @@ def __call__(self, inputs, is_training, keep_prob): class ChannelWiseFCLayer(tf.Module): """TF Module for creating a new channel wise Fully-Connected Layer - input: 3-dim tensor of shape [batch_size, num_inputs, num_channel] - output: 3-dim tensor of shape [batch_size, num_outputs, num_channel] + input: 3-dim tensor of shape [batch_size, num_inputs, num_channel] + output: 3-dim tensor of shape [batch_size, num_outputs, num_channel] """ - def __init__(self, - input_shape, - num_outputs, - use_dropout=False, - activation='elu', - use_batch_normalisation=False, - use_residual=False, - weights=None, - biases=None, - max_out_size=None, - repair_std_deviation=True, - float_precision=FLOAT_PRECISION, - name=None): + def __init__( + self, + input_shape, + num_outputs, + use_dropout=False, + activation="elu", + use_batch_normalisation=False, + use_residual=False, + weights=None, + biases=None, + max_out_size=None, + repair_std_deviation=True, + float_precision=FLOAT_PRECISION, + name=None, + ): """Initialize object Parameters @@ -966,7 +1022,7 @@ def __init__(self, input_shape = input_shape.as_list() # input: [batch, num_inputs, num_channel] - msg = '{} != [batch, num_inputs, num_channel]' + msg = "{} != [batch, num_inputs, num_channel]" assert len(input_shape) == 3, msg.format(input_shape) num_inputs = input_shape[1] @@ -989,10 +1045,11 @@ def __init__(self, self.weights = weights self.activation = core.Activation( - activation_type=activation, - input_shape=transpose_shape, - use_batch_normalisation=use_batch_normalisation, - float_precision=float_precision) + activation_type=activation, + input_shape=transpose_shape, + use_batch_normalisation=use_batch_normalisation, + float_precision=float_precision, + ) # # calculate residual strides # if max_out_size is not None: @@ -1012,9 +1069,10 @@ def __init__(self, # Use as Residual if use_residual: self.residual_add = core.AddResidual( - residual_shape=[None, num_channels, num_outputs], - strides=res_strides, - float_precision=float_precision) + residual_shape=[None, num_channels, num_outputs], + strides=res_strides, + float_precision=float_precision, + ) # calculate output shape if max_out_size is not None: @@ -1063,12 +1121,15 @@ def __call__(self, inputs, is_training, keep_prob): # check shape of input tensor if inputs.shape[1:].as_list() != self.input_shape[1:]: - raise ValueError('Shape mismatch: {!r} != {!r}'.format( - inputs.shape[1:].as_list(), self.input_shape[1:])) + raise ValueError( + "Shape mismatch: {!r} != {!r}".format( + inputs.shape[1:].as_list(), self.input_shape[1:] + ) + ) input_shape = inputs.get_shape().as_list() num_inputs = input_shape[1] - num_channels = input_shape[2] + # num_channels = input_shape[2] # input_transpose: [num_channel, batch, num_inputs] input_transpose = tf.transpose(a=inputs, perm=[2, 0, 1]) @@ -1084,7 +1145,7 @@ def __call__(self, inputs, is_training, keep_prob): if self.repair_std_deviation: layer = layer / np.sqrt(num_inputs) - layer = (layer + self.biases) / np.sqrt(2.) + layer = (layer + self.biases) / np.sqrt(2.0) else: layer = layer + self.biases @@ -1096,15 +1157,16 @@ def __call__(self, inputs, is_training, keep_prob): # convert to [batch, num_channel, num_outputs] layer = tf.transpose(a=layer, perm=[0, 2, 1]) layer = self.residual_add( - input=tf.transpose(a=inputs, perm=[0, 2, 1]), residual=layer) + input=tf.transpose(a=inputs, perm=[0, 2, 1]), residual=layer + ) # convert back to [batch, num_outputs, num_channel] layer = tf.transpose(a=layer, perm=[0, 2, 1]) # max out channel dimension if specified if self.max_out_size is not None: - layer = core.maxout(inputs=layer, - num_units=self.max_out_size, - axis=-1) + layer = core.maxout( + inputs=layer, num_units=self.max_out_size, axis=-1 + ) if self.use_dropout and is_training: layer = tf.nn.dropout(layer, 1 - keep_prob) @@ -1113,24 +1175,24 @@ def __call__(self, inputs, is_training, keep_prob): class FCLayers(tf.Module): - """TF Module for creating new fully connected layers. - """ - - def __init__(self, - input_shape, - fc_sizes, - use_dropout_list=False, - activation_list='elu', - use_batch_normalisation_list=False, - use_residual_list=False, - weights_list=None, - biases_list=None, - max_out_size_list=None, - repair_std_deviation_list=True, - float_precision=FLOAT_PRECISION, - name='fc_layer', - verbose=False, - ): + """TF Module for creating new fully connected layers.""" + + def __init__( + self, + input_shape, + fc_sizes, + use_dropout_list=False, + activation_list="elu", + use_batch_normalisation_list=False, + use_residual_list=False, + weights_list=None, + biases_list=None, + max_out_size_list=None, + repair_std_deviation_list=True, + float_precision=FLOAT_PRECISION, + name="fc_layer", + verbose=False, + ): """Initialize object Parameters @@ -1200,8 +1262,9 @@ def __init__(self, use_dropout_list = [use_dropout_list for i in range(num_layers)] # create batch normalisation array if isinstance(use_batch_normalisation_list, bool): - use_batch_normalisation_list = [use_batch_normalisation_list - for i in range(num_layers)] + use_batch_normalisation_list = [ + use_batch_normalisation_list for i in range(num_layers) + ] # create use_residual_list if use_residual_list is False or use_residual_list is True: use_residual_list = [use_residual_list for i in range(num_layers)] @@ -1217,7 +1280,8 @@ def __init__(self, # create repair std deviation array if isinstance(repair_std_deviation_list, bool): repair_std_deviation_list = [ - repair_std_deviation_list for i in range(num_layers)] + repair_std_deviation_list for i in range(num_layers) + ] # pick which fc layers to build if len(input_shape) == 3: @@ -1229,8 +1293,9 @@ def __init__(self, FCLayerClass = FCLayer else: - raise ValueError('Input dimension is wrong: {}'.format( - inputs.get_shape().as_list())) + raise ValueError( + "Input dimension is wrong: {}".format(input_shape) + ) # create layers: self.layers = [] @@ -1238,23 +1303,23 @@ def __init__(self, if i == 0: previous_layer_shape = input_shape else: - previous_layer_shape = self.layers[i-1].output_shape + previous_layer_shape = self.layers[i - 1].output_shape layer_i = FCLayerClass( - input_shape=previous_layer_shape, - num_outputs=fc_sizes[i], - activation=activation_list[i], - use_dropout=use_dropout_list[i], - use_batch_normalisation=use_batch_normalisation_list[i], - use_residual=use_residual_list[i], - weights=weights_list[i], - biases=biases_list[i], - max_out_size=max_out_size_list[i], - repair_std_deviation=repair_std_deviation_list[i], - float_precision=float_precision, - name='{}_{:03d}'.format(name, i), - ) + input_shape=previous_layer_shape, + num_outputs=fc_sizes[i], + activation=activation_list[i], + use_dropout=use_dropout_list[i], + use_batch_normalisation=use_batch_normalisation_list[i], + use_residual=use_residual_list[i], + weights=weights_list[i], + biases=biases_list[i], + max_out_size=max_out_size_list[i], + repair_std_deviation=repair_std_deviation_list[i], + float_precision=float_precision, + name="{}_{:03d}".format(name, i), + ) if verbose: - print('{}_{:03d}'.format(name, i), layer_i.output_shape) + print("{}_{:03d}".format(name, i), layer_i.output_shape) self.layers.append(layer_i) def __call__(self, inputs, is_training, keep_prob=None): @@ -1287,36 +1352,36 @@ def __call__(self, inputs, is_training, keep_prob=None): class ConvNdLayers(tf.Module): - """TF Module for creating new conv1d, conv2d, conv3d, and conv4d layers. - """ - - def __init__(self, - input_shape, - filter_size_list, - num_filters_list, - pooling_type_list=None, - pooling_strides_list=None, - pooling_ksize_list=None, - pooling_padding_list='SAME', - padding_list='SAME', - strides_list=None, - activation_list='elu', - use_batch_normalisation_list=False, - dilation_rate_list=None, - use_residual_list=False, - use_dropout_list=False, - method_list='convolution', - repair_std_deviation_list=True, - weights_list=None, - biases_list=None, - trafo_list=None, - hex_num_rotations_list=1, - hex_azimuth_list=None, - hex_zero_out_list=False, - float_precision=FLOAT_PRECISION, - name='conv_{}d_layer', - verbose=False, - ): + """TF Module for creating new conv1d, conv2d, conv3d, and conv4d layers.""" + + def __init__( + self, + input_shape, + filter_size_list, + num_filters_list, + pooling_type_list=None, + pooling_strides_list=None, + pooling_ksize_list=None, + pooling_padding_list="SAME", + padding_list="SAME", + strides_list=None, + activation_list="elu", + use_batch_normalisation_list=False, + dilation_rate_list=None, + use_residual_list=False, + use_dropout_list=False, + method_list="convolution", + repair_std_deviation_list=True, + weights_list=None, + biases_list=None, + trafo_list=None, + hex_num_rotations_list=1, + hex_azimuth_list=None, + hex_zero_out_list=False, + float_precision=FLOAT_PRECISION, + name="conv_{}d_layer", + verbose=False, + ): """Initialize object Parameters @@ -1484,7 +1549,7 @@ def __init__(self, pooling_ksize_list = [1, 2, 2, 2, 2, 1] if strides_list is None: strides_list = [1, 1, 1, 1, 1, 1] - + elif num_dims == 5: # 3D convolution if pooling_strides_list is None: @@ -1502,7 +1567,7 @@ def __init__(self, pooling_ksize_list = [1, 2, 2, 1] if strides_list is None: strides_list = [1, 1, 1, 1] - + elif num_dims == 3: # 1D convolution if pooling_strides_list is None: @@ -1513,7 +1578,7 @@ def __init__(self, strides_list = [1, 1, 1] else: - msg = 'Currently only 1D, 2D, 3D, or 4D supported {!r}' + msg = "Currently only 1D, 2D, 3D, or 4D supported {!r}" raise ValueError(msg.format(input_shape)) num_layers = len(num_filters_list) @@ -1526,29 +1591,38 @@ def __init__(self, pooling_type_list = [pooling_type_list for i in range(num_layers)] # create pooling_strides_list - if (len(np.array(pooling_strides_list).shape) == 1 and - len(pooling_strides_list) == num_dims): - pooling_strides_list = [pooling_strides_list - for i in range(num_layers)] + if ( + len(np.array(pooling_strides_list).shape) == 1 + and len(pooling_strides_list) == num_dims + ): + pooling_strides_list = [ + pooling_strides_list for i in range(num_layers) + ] # create pooling_ksize_list - if (len(np.array(pooling_ksize_list).shape) == 1 and - len(pooling_ksize_list) == num_dims): - pooling_ksize_list = [pooling_ksize_list for i in - range(num_layers)] + if ( + len(np.array(pooling_ksize_list).shape) == 1 + and len(pooling_ksize_list) == num_dims + ): + pooling_ksize_list = [ + pooling_ksize_list for i in range(num_layers) + ] # create pooling_padding_list - if pooling_padding_list == 'SAME' or pooling_padding_list == 'VALID': - pooling_padding_list = [pooling_padding_list - for i in range(num_layers)] + if pooling_padding_list == "SAME" or pooling_padding_list == "VALID": + pooling_padding_list = [ + pooling_padding_list for i in range(num_layers) + ] # create padding_list - if padding_list == 'SAME' or padding_list == 'VALID': + if padding_list == "SAME" or padding_list == "VALID": padding_list = [padding_list for i in range(num_layers)] # create strides_list - if (len(np.array(strides_list).shape) == 1 and - len(strides_list) == num_dims): + if ( + len(np.array(strides_list).shape) == 1 + and len(strides_list) == num_dims + ): strides_list = [strides_list for i in range(num_layers)] # create activation_list @@ -1557,15 +1631,18 @@ def __init__(self, # create batch normalisation array if isinstance(use_batch_normalisation_list, bool): - use_batch_normalisation_list = [use_batch_normalisation_list - for i in range(num_layers)] + use_batch_normalisation_list = [ + use_batch_normalisation_list for i in range(num_layers) + ] # create dilation_rate_list if dilation_rate_list is None or ( - len(np.asarray(dilation_rate_list).shape) == 1 and - len(dilation_rate_list) == num_dims - 2): - dilation_rate_list = [dilation_rate_list for i in - range(num_layers)] + len(np.asarray(dilation_rate_list).shape) == 1 + and len(dilation_rate_list) == num_dims - 2 + ): + dilation_rate_list = [ + dilation_rate_list for i in range(num_layers) + ] # create use_residual_list if use_residual_list is False or use_residual_list is True: @@ -1582,7 +1659,8 @@ def __init__(self, # create repair std deviation array if isinstance(repair_std_deviation_list, bool): repair_std_deviation_list = [ - repair_std_deviation_list for i in range(num_layers)] + repair_std_deviation_list for i in range(num_layers) + ] # create weights_list if weights_list is None: @@ -1597,12 +1675,12 @@ def __init__(self, # create hex_num_rotations_list if isinstance(hex_num_rotations_list, int): - hex_num_rotations_list = [hex_num_rotations_list - for i in range(num_layers)] + hex_num_rotations_list = [ + hex_num_rotations_list for i in range(num_layers) + ] # create hex_azimuth_list - if (hex_azimuth_list is None or - tf.is_tensor(hex_azimuth_list)): + if hex_azimuth_list is None or tf.is_tensor(hex_azimuth_list): hex_azimuth_list = [hex_azimuth_list for i in range(num_layers)] # create hex_zero out array @@ -1616,35 +1694,35 @@ def __init__(self, if i == 0: previous_layer_shape = input_shape else: - previous_layer_shape = self.layers[i-1].output_shape + previous_layer_shape = self.layers[i - 1].output_shape layer_i = ConvNdLayer( - input_shape=previous_layer_shape, - filter_size=filter_size_list[i], - num_filters=num_filters_list[i], - pooling_padding=pooling_padding_list[i], - pooling_strides=pooling_strides_list[i], - pooling_type=pooling_type_list[i], - pooling_ksize=pooling_ksize_list[i], - strides=strides_list[i], - padding=padding_list[i], - use_dropout=use_dropout_list[i], - activation=activation_list[i], - use_batch_normalisation=use_batch_normalisation_list[i], - dilation_rate=dilation_rate_list[i], - use_residual=use_residual_list[i], - method=method_list[i], - repair_std_deviation=repair_std_deviation_list[i], - weights=weights_list[i], - biases=biases_list[i], - trafo=trafo_list[i], - hex_num_rotations=hex_num_rotations_list[i], - hex_azimuth=hex_azimuth_list[i], - hex_zero_out=hex_zero_out_list[i], - float_precision=float_precision, - name='{}_{:03d}'.format(name, i), - ) + input_shape=previous_layer_shape, + filter_size=filter_size_list[i], + num_filters=num_filters_list[i], + pooling_padding=pooling_padding_list[i], + pooling_strides=pooling_strides_list[i], + pooling_type=pooling_type_list[i], + pooling_ksize=pooling_ksize_list[i], + strides=strides_list[i], + padding=padding_list[i], + use_dropout=use_dropout_list[i], + activation=activation_list[i], + use_batch_normalisation=use_batch_normalisation_list[i], + dilation_rate=dilation_rate_list[i], + use_residual=use_residual_list[i], + method=method_list[i], + repair_std_deviation=repair_std_deviation_list[i], + weights=weights_list[i], + biases=biases_list[i], + trafo=trafo_list[i], + hex_num_rotations=hex_num_rotations_list[i], + hex_azimuth=hex_azimuth_list[i], + hex_zero_out=hex_zero_out_list[i], + float_precision=float_precision, + name="{}_{:03d}".format(name, i), + ) if verbose: - print('{}_{:03d}'.format(name, i), layer_i.output_shape) + print("{}_{:03d}".format(name, i), layer_i.output_shape) self.layers.append(layer_i) def __call__(self, inputs, is_training, keep_prob=None): diff --git a/tfscripts/model.py b/tfscripts/model.py index 2303de0..d657dee 100644 --- a/tfscripts/model.py +++ b/tfscripts/model.py @@ -7,21 +7,19 @@ class DenseNN(tf.keras.Model): - - """Dense Neural Network - """ + """Dense Neural Network""" def __init__( - self, - input_shape, - fc_sizes, - use_dropout_list=False, - activation_list='elu', - use_batch_normalisation_list=False, - use_residual_list=False, - dtype='float32', - verbose=False, - ): + self, + input_shape, + fc_sizes, + use_dropout_list=False, + activation_list="elu", + use_batch_normalisation_list=False, + use_residual_list=False, + dtype="float32", + verbose=False, + ): """Dense NN Model Parameters @@ -68,28 +66,28 @@ def __init__( y_out_shape = (1, self.fc_sizes[-1]) self.trafo_model_initialized = False self.x_mean = self.add_weight( - name='trafo_model_x_mean', + name="trafo_model_x_mean", shape=trafo_shape, initializer="zeros", trainable=False, dtype=tf_dtype, ) self.x_std = self.add_weight( - name='trafo_model_x_std', + name="trafo_model_x_std", shape=trafo_shape, initializer="ones", trainable=False, dtype=tf_dtype, ) self.y_mean = self.add_weight( - name='trafo_model_y_mean', + name="trafo_model_y_mean", shape=y_out_shape, initializer="zeros", trainable=False, dtype=tf_dtype, ) self.y_std = self.add_weight( - name='trafo_model_y_std', + name="trafo_model_y_std", shape=y_out_shape, initializer="ones", trainable=False, @@ -108,7 +106,7 @@ def __init__( biases_list=None, max_out_size_list=None, float_precision=tf_dtype, - name='fc_layer', + name="fc_layer", verbose=verbose, ) @@ -142,12 +140,12 @@ def _check_trafo_model(self): Raises ------ ValueError - If trafo model has not been intialized + If trafo model has not been initialized """ if not self.trafo_model_initialized: raise ValueError( - 'Trafo model is not yet configured. ' - 'Run model.create_trafo_model() first' + "Trafo model is not yet configured. " + "Run model.create_trafo_model() first" ) def call(self, inputs, training=False, keep_prob=None): @@ -178,7 +176,8 @@ def call(self, inputs, training=False, keep_prob=None): inputs_trafo = (inputs - self.x_mean) / (1e-3 + self.x_std) output_trafo = self.fc_layers( - inputs_trafo, is_training=training, keep_prob=keep_prob)[-1] + inputs_trafo, is_training=training, keep_prob=keep_prob + )[-1] # invert normalization of labels output = output_trafo * (1e-3 + self.y_std) + self.y_mean @@ -221,8 +220,7 @@ def load_weights(self, filepath, **kwargs): **kwargs Keyword arguments passed on to tf.keras.Model.load_weights() """ - super().load_weights( - filepath=filepath, **kwargs).expect_partial() + super().load_weights(filepath=filepath, **kwargs).expect_partial() self.trafo_model_initialized = True def load(self, **kwargs): @@ -247,43 +245,42 @@ def get_config(self): to serealize and deserealize the model. """ config = { - 'input_shape': self._input_shape, - 'fc_sizes': self.fc_sizes, - 'use_dropout_list': self.use_dropout_list, - 'activation_list': self.activation_list, - 'use_batch_normalisation_list': self.use_batch_normalisation_list, - 'use_residual_list': self.use_residual_list, - 'dtype': self.dtype, + "input_shape": self._input_shape, + "fc_sizes": self.fc_sizes, + "use_dropout_list": self.use_dropout_list, + "activation_list": self.activation_list, + "use_batch_normalisation_list": self.use_batch_normalisation_list, + "use_residual_list": self.use_residual_list, + "dtype": self.dtype, } for key, value in config.items(): if isinstance(value, (list, tuple)): config[key] = list(value) return deepcopy(config) - -class DenseNNGaussian(DenseNN): - """Dense Neural Network with Gaussian uncertainty prediction - """ + +class DenseNNGaussian(DenseNN): + """Dense Neural Network with Gaussian uncertainty prediction""" def __init__( - self, - fc_sizes_unc, - use_dropout_list_unc=False, - activation_list_unc='elu', - use_batch_normalisation_list_unc=False, - use_residual_list_unc=False, - use_nth_fc_layer_as_input=None, - min_sigma_value=1e-3, - verbose=False, - **kwargs + self, + fc_sizes_unc, + use_dropout_list_unc=False, + activation_list_unc="elu", + use_batch_normalisation_list_unc=False, + use_residual_list_unc=False, + use_nth_fc_layer_as_input=None, + min_sigma_value=1e-3, + verbose=False, + **kwargs, ): - """Guassian Uncertainty NN (Dense NN Model) + """Gaussian Uncertainty NN (Dense NN Model) Parameters ---------- fc_sizes_unc : list of int - The number of nodes for each uncertainty layer. The ith int - denotes the number of nodes for the ith layer. The number of + The number of nodes for each uncertainty layer. The ith int + denotes the number of nodes for the ith layer. The number of layers is inferred from the length of 'fc_sizes_unc'. use_dropout_list_unc : bool, optional Denotes whether to use dropout in the uncertainty layers. @@ -291,21 +288,21 @@ def __init__( layers. activation_list_unc : str or callable, optional The activation function to be used in each uncertainty layer. - If only one activation is provided, it will be used for all + If only one activation is provided, it will be used for all layers. use_batch_normalisation_list_unc : bool or list of bool, optional - Denotes whether to use batch normalisation in the uncertainty + Denotes whether to use batch normalisation in the uncertainty layers. If only a single boolean is provided, it will be used for all layers. use_residual_list_unc : bool or list of bool, optional Denotes whether to use residual additions in the uncertainty - layers. If only a single boolean is provided, it will be used + layers. If only a single boolean is provided, it will be used for all layers. use_nth_fc_layer_as_input : None or int, optional - If None, the same inputs as for the main NN will be used as + If None, the same inputs as for the main NN will be used as input for the uncertainty network. If not None, the nth layer of the main NN will be used as input - for the uncertainty network. + for the uncertainty network. If negative, the layer is counted from the last layer. For example, use_nth_fc_layer_as_input=-2 denotes the second last layer. @@ -316,21 +313,26 @@ def __init__( Keyword arguments that are passed on to DenseNN initializer. """ super().__init__(verbose=verbose, **kwargs) - + self.fc_sizes_unc = fc_sizes_unc self.use_dropout_list_unc = use_dropout_list_unc self.activation_list_unc = activation_list_unc - self.use_batch_normalisation_list_unc = use_batch_normalisation_list_unc + self.use_batch_normalisation_list_unc = ( + use_batch_normalisation_list_unc + ) self.use_residual_list_unc = use_residual_list_unc self.use_nth_fc_layer_as_input = use_nth_fc_layer_as_input self.min_sigma_value = min_sigma_value - + if self.use_nth_fc_layer_as_input is not None: - self._input_shape_unc = [-1, self.fc_sizes[self.use_nth_fc_layer_as_input]] + self._input_shape_unc = [ + -1, + self.fc_sizes[self.use_nth_fc_layer_as_input], + ] else: self._input_shape_unc = self._input_shape - - # create weights and layers for uncertainty network + + # create weights and layers for uncertainty network self.fc_layers_unc = tfs.FCLayers( input_shape=self._input_shape_unc, fc_sizes=self.fc_sizes_unc, @@ -342,7 +344,7 @@ def __init__( biases_list=None, max_out_size_list=None, float_precision=getattr(tf, self.dtype), - name='fc_layer_unc', + name="fc_layer_unc", verbose=verbose, ) @@ -380,17 +382,19 @@ def call(self, inputs, training=False, keep_prob=None): # normalize input data inputs_trafo = (inputs - self.x_mean) / (1e-3 + self.x_std) - # get ouputs of main NN for value prediction + # get outputs of main NN for value prediction main_output = self.fc_layers( - inputs_trafo, is_training=training, keep_prob=keep_prob) - + inputs_trafo, is_training=training, keep_prob=keep_prob + ) + # run uncertainty sub-nework if self.use_nth_fc_layer_as_input is not None: inputs_unc = main_output[self.use_nth_fc_layer_as_input] else: inputs_unc = inputs_trafo outputs_unc = self.fc_layers_unc( - inputs_unc, is_training=training, keep_prob=keep_prob)[-1] + inputs_unc, is_training=training, keep_prob=keep_prob + )[-1] # set initialized value to variance of 1, # which would be a correct initial guess if labels @@ -403,7 +407,7 @@ def call(self, inputs, training=False, keep_prob=None): # force positive value outputs_unc = tf.math.abs(outputs_unc) + self.min_sigma_value - + # return return outputs, outputs_unc @@ -417,13 +421,15 @@ def get_config(self): to serealize and deserealize the model. """ config = super().get_config() - config.update({ - 'fc_sizes_unc': self.fc_sizes_unc, - 'use_dropout_list_unc': self.use_dropout_list_unc, - 'activation_list_unc': self.activation_list_unc, - 'use_batch_normalisation_list_unc': self.use_batch_normalisation_list_unc, - 'use_residual_list_unc': self.use_residual_list_unc, - 'use_nth_fc_layer_as_input': self.use_nth_fc_layer_as_input, - 'min_sigma_value': self.min_sigma_value, - }) + config.update( + { + "fc_sizes_unc": self.fc_sizes_unc, + "use_dropout_list_unc": self.use_dropout_list_unc, + "activation_list_unc": self.activation_list_unc, + "use_batch_normalisation_list_unc": self.use_batch_normalisation_list_unc, + "use_residual_list_unc": self.use_residual_list_unc, + "use_nth_fc_layer_as_input": self.use_nth_fc_layer_as_input, + "min_sigma_value": self.min_sigma_value, + } + ) return deepcopy(config) diff --git a/tfscripts/pooling.py b/tfscripts/pooling.py index cda70f4..71f3176 100644 --- a/tfscripts/pooling.py +++ b/tfscripts/pooling.py @@ -1,6 +1,6 @@ -''' +""" Pooling functions for tfscripts -''' +""" from __future__ import division, print_function @@ -44,11 +44,13 @@ def pool3d(layer, ksize, strides, padding, pooling_type): # pool over depth, if necessary: if ksize[-1] != 1 or strides[-1] != 1: - layer = pool_over_depth(layer, - ksize=ksize[-1], - stride=strides[-1], - padding=padding, - pooling_type=pooling_type) + layer = pool_over_depth( + layer, + ksize=ksize[-1], + stride=strides[-1], + padding=padding, + pooling_type=pooling_type, + ) ksize = list(ksize) strides = list(strides) ksize[-1] = 1 @@ -56,27 +58,23 @@ def pool3d(layer, ksize, strides, padding, pooling_type): # Use pooling to down-sample the image resolution? if ksize[1:-1] != [1, 1, 1] or strides[1:-1] != [1, 1, 1]: - if pooling_type == 'max': - layer = tf.nn.max_pool3d(input=layer, - ksize=ksize, - strides=strides, - padding=padding) - elif pooling_type == 'avg': - layer = tf.nn.avg_pool3d(input=layer, - ksize=ksize, - strides=strides, - padding=padding) - - elif pooling_type == 'max_avg': - layer_max = tf.nn.max_pool3d(input=layer, - ksize=ksize, - strides=strides, - padding=padding) - layer_avg = tf.nn.avg_pool3d(input=layer, - ksize=ksize, - strides=strides, - padding=padding) - layer = (layer_avg + layer_max) / 2. + if pooling_type == "max": + layer = tf.nn.max_pool3d( + input=layer, ksize=ksize, strides=strides, padding=padding + ) + elif pooling_type == "avg": + layer = tf.nn.avg_pool3d( + input=layer, ksize=ksize, strides=strides, padding=padding + ) + + elif pooling_type == "max_avg": + layer_max = tf.nn.max_pool3d( + input=layer, ksize=ksize, strides=strides, padding=padding + ) + layer_avg = tf.nn.avg_pool3d( + input=layer, ksize=ksize, strides=strides, padding=padding + ) + layer = (layer_avg + layer_max) / 2.0 if was_float64: layer = tf.cast(layer, tf.float64) @@ -118,37 +116,35 @@ def pool2d(layer, ksize, strides, padding, pooling_type): # pool over depth, if necessary: if ksize[-1] != 1 or strides[-1] != 1: - layer = pool_over_depth(layer, - ksize=ksize[-1], - stride=strides[-1], - padding=padding, - pooling_type=pooling_type) + layer = pool_over_depth( + layer, + ksize=ksize[-1], + stride=strides[-1], + padding=padding, + pooling_type=pooling_type, + ) ksize = list(ksize) strides = list(strides) ksize[-1] = 1 strides[-1] = 1 # Use pooling to down-sample the image resolution? - if pooling_type == 'max': - layer = tf.nn.max_pool2d(input=layer, - ksize=ksize, - strides=strides, - padding=padding) - elif pooling_type == 'avg': - layer = tf.nn.avg_pool2d(input=layer, - ksize=ksize, - strides=strides, - padding=padding) - elif pooling_type == 'max_avg': - layer_max = tf.nn.max_pool2d(input=layer, - ksize=ksize, - strides=strides, - padding=padding) - layer_avg = tf.nn.avg_pool2d(input=layer, - ksize=ksize, - strides=strides, - padding=padding) - layer = (layer_avg + layer_max) / 2. + if pooling_type == "max": + layer = tf.nn.max_pool2d( + input=layer, ksize=ksize, strides=strides, padding=padding + ) + elif pooling_type == "avg": + layer = tf.nn.avg_pool2d( + input=layer, ksize=ksize, strides=strides, padding=padding + ) + elif pooling_type == "max_avg": + layer_max = tf.nn.max_pool2d( + input=layer, ksize=ksize, strides=strides, padding=padding + ) + layer_avg = tf.nn.avg_pool2d( + input=layer, ksize=ksize, strides=strides, padding=padding + ) + layer = (layer_avg + layer_max) / 2.0 if was_float64: layer = tf.cast(layer, tf.float64) @@ -187,40 +183,38 @@ def pool1d(layer, ksize, strides, padding, pooling_type): was_float64 = True else: was_float64 = False - + # pool over depth, if necessary: if ksize[-1] != 1 or strides[-1] != 1: - layer = pool_over_depth(layer, - ksize=ksize[-1], - stride=strides[-1], - padding=padding, - pooling_type=pooling_type) + layer = pool_over_depth( + layer, + ksize=ksize[-1], + stride=strides[-1], + padding=padding, + pooling_type=pooling_type, + ) ksize = list(ksize) strides = list(strides) ksize[-1] = 1 strides[-1] = 1 # Use pooling to down-sample the image resolution? - if pooling_type == 'max': - layer = tf.nn.max_pool1d(input=layer, - ksize=ksize, - strides=strides, - padding=padding) - elif pooling_type == 'avg': - layer = tf.nn.avg_pool1d(input=layer, - ksize=ksize, - strides=strides, - padding=padding) - elif pooling_type == 'max_avg': - layer_max = tf.nn.max_pool1d(input=layer, - ksize=ksize, - strides=strides, - padding=padding) - layer_avg = tf.nn.avg_pool1d(input=layer, - ksize=ksize, - strides=strides, - padding=padding) - layer = (layer_avg + layer_max) / 2. + if pooling_type == "max": + layer = tf.nn.max_pool1d( + input=layer, ksize=ksize, strides=strides, padding=padding + ) + elif pooling_type == "avg": + layer = tf.nn.avg_pool1d( + input=layer, ksize=ksize, strides=strides, padding=padding + ) + elif pooling_type == "max_avg": + layer_max = tf.nn.max_pool1d( + input=layer, ksize=ksize, strides=strides, padding=padding + ) + layer_avg = tf.nn.avg_pool1d( + input=layer, ksize=ksize, strides=strides, padding=padding + ) + layer = (layer_avg + layer_max) / 2.0 if was_float64: layer = tf.cast(layer, tf.float64) @@ -229,7 +223,7 @@ def pool1d(layer, ksize, strides, padding, pooling_type): def pool_over_depth(layer, ksize, stride, padding, pooling_type): - ''' + """ Performs pooling over last dimension of layer. Assumes that the last dimension of layer is the depth channel. @@ -251,16 +245,18 @@ def pool_over_depth(layer, ksize, stride, padding, pooling_type): ------- tf.Tensor The pooled output tensor. - ''' + """ num_channels = layer.get_shape().as_list()[-1] # get start index - start_index = get_start_index(input_length=num_channels, - filter_size=ksize, - padding=padding, - stride=stride, - dilation=1) + start_index = get_start_index( + input_length=num_channels, + filter_size=ksize, + padding=padding, + stride=stride, + dilation=1, + ) input_patches = [] # --------------------------- @@ -269,13 +265,15 @@ def pool_over_depth(layer, ksize, stride, padding, pooling_type): for index in range(start_index, num_channels, stride): # get slice for patch along channel-axis - slice_c, padding_c = get_conv_slice(index, - input_length=num_channels, - filter_size=ksize, - stride=stride, - dilation=1) - - if padding == 'VALID' and padding_c != (0, 0): + slice_c, padding_c = get_conv_slice( + index, + input_length=num_channels, + filter_size=ksize, + stride=stride, + dilation=1, + ) + + if padding == "VALID" and padding_c != (0, 0): # skip this c position, since it does not provide # a valid patch for padding 'VALID' continue @@ -283,26 +281,29 @@ def pool_over_depth(layer, ksize, stride, padding, pooling_type): # ------------------------------------------ # Get input patch at filter position c # ------------------------------------------ - if pooling_type == 'max': - input_patches.append(tf.reduce_max( - input_tensor=layer[..., slice_c], axis=-1)) + if pooling_type == "max": + input_patches.append( + tf.reduce_max(input_tensor=layer[..., slice_c], axis=-1) + ) - elif pooling_type == 'avg': - input_patches.append(tf.reduce_mean( - input_tensor=layer[..., slice_c], axis=-1)) + elif pooling_type == "avg": + input_patches.append( + tf.reduce_mean(input_tensor=layer[..., slice_c], axis=-1) + ) else: - raise ValueError('Pooling_type {!r} is unknown.'.format( - pooling_type)) + raise ValueError( + "Pooling_type {!r} is unknown.".format(pooling_type) + ) - if pooling_type in ['max', 'avg']: + if pooling_type in ["max", "avg"]: layer = tf.stack(input_patches, axis=-1) return layer def max_pool4d_stacked(input, ksize, strides, padding): - '''max_pool4d equivalent + """max_pool4d equivalent Equivalent to tensorflows max_pool3d, but in 4D. @@ -335,30 +336,35 @@ def max_pool4d_stacked(input, ksize, strides, padding): ------ ValueError Description - ''' + """ if ksize[4] != 1: - raise ValueError("max_pool4d_stacked does not yet support " - "pooling in t dimension.") + raise ValueError( + "max_pool4d_stacked does not yet support " + "pooling in t dimension." + ) # unpack along t dimension tensors_t = tf.unstack(input, axis=4) - len_ts = ksize[4] size_of_t_dim = len(tensors_t) # loop over all z_j in t result_t = [] for i in range(0, size_of_t_dim, strides[4]): - result_t.append(tf.nn.max_pool3d(input=tensors_t[i], - ksize=ksize[:4]+ksize[5:], - strides=strides[:4]+strides[5:], - padding=padding)) + result_t.append( + tf.nn.max_pool3d( + input=tensors_t[i], + ksize=ksize[:4] + ksize[5:], + strides=strides[:4] + strides[5:], + padding=padding, + ) + ) # stack together return tf.stack(result_t, axis=4) def avg_pool4d_stacked(input, ksize, strides, padding): - '''avg_pool4d equivalent + """avg_pool4d equivalent Equivalent to tensorflows avg_pool3d, but in 4D. This method is slightly slower, but appears to use less vram. @@ -385,7 +391,7 @@ def avg_pool4d_stacked(input, ksize, strides, padding): Returns ------- A Tensor. Has the same type as input.The average pooled output tensor. - ''' + """ # unpack along t dimension tensors_t = tf.unstack(input, axis=4) @@ -395,21 +401,28 @@ def avg_pool4d_stacked(input, ksize, strides, padding): if len_ts % 2 == 1: # uneven filter size: same size to left and right - filter_l = int(len_ts/2) - filter_r = int(len_ts/2) + filter_l = int(len_ts / 2) + filter_r = int(len_ts / 2) else: # even filter size: one more to right - filter_l = int(len_ts/2) - 1 - filter_r = int(len_ts/2) + filter_l = int(len_ts / 2) - 1 + filter_r = int(len_ts / 2) # The start index is important for strides # The strides start with the first element # that works and is VALID: start_index = 0 - if padding == 'VALID': + if padding == "VALID": for i in range(size_of_t_dim): - if len(range(max(i - filter_l, 0), min(i + filter_r+1, - size_of_t_dim))) == len_ts: + if ( + len( + range( + max(i - filter_l, 0), + min(i + filter_r + 1, size_of_t_dim), + ) + ) + == len_ts + ): # we found the first index that doesn't need padding break start_index = i @@ -418,49 +431,54 @@ def avg_pool4d_stacked(input, ksize, strides, padding): result_t = [] for i in range(start_index, size_of_t_dim, strides[4]): - if padding == 'VALID': - # Get indices z_s - indices_t_s = range(max(i - filter_l, 0), - min(i + filter_r+1, size_of_t_dim)) - - # check if Padding = 'VALID' - if len(indices_t_s) == len_ts: - - tensors_t_averaged = [] - # sum over all remaining index_z_i in indices_t_s - for j, index_z_i in enumerate(indices_t_s): - tensors_t_averaged.append( - tf.nn.avg_pool3d( - input=tensors_t[index_z_i], - ksize=ksize[:4]+ksize[5:], - strides=strides[:4]+strides[5:], - padding=padding) - ) - avg_tensors_t_s = tf.divide(tf.add_n(tensors_t_averaged), - len_ts) - - # put together - result_t.append(avg_tensors_t_s) - - elif padding == 'SAME': + if padding == "VALID": + # Get indices z_s + indices_t_s = range( + max(i - filter_l, 0), min(i + filter_r + 1, size_of_t_dim) + ) + + # check if Padding = 'VALID' + if len(indices_t_s) == len_ts: + tensors_t_averaged = [] - for kernel_j, j in enumerate(range(i - filter_l, - (i + 1) + filter_r)): - # we can just leave out the invalid t coordinates - # since they will be padded with 0's and therfore - # don't contribute to the sum - if 0 <= j < size_of_t_dim: - tensors_t_averaged.append( - tf.nn.avg_pool3d(input=tensors_t[j], - ksize=ksize[:4]+ksize[5:], - strides=strides[:4]+strides[5:], - padding=padding) - ) - avg_tensors_t_s = tf.divide(tf.add_n(tensors_t_averaged), - len_ts) + # sum over all remaining index_z_i in indices_t_s + for j, index_z_i in enumerate(indices_t_s): + tensors_t_averaged.append( + tf.nn.avg_pool3d( + input=tensors_t[index_z_i], + ksize=ksize[:4] + ksize[5:], + strides=strides[:4] + strides[5:], + padding=padding, + ) + ) + avg_tensors_t_s = tf.divide( + tf.add_n(tensors_t_averaged), len_ts + ) # put together result_t.append(avg_tensors_t_s) + elif padding == "SAME": + tensors_t_averaged = [] + for kernel_j, j in enumerate( + range(i - filter_l, (i + 1) + filter_r) + ): + # we can just leave out the invalid t coordinates + # since they will be padded with 0's and therefore + # don't contribute to the sum + if 0 <= j < size_of_t_dim: + tensors_t_averaged.append( + tf.nn.avg_pool3d( + input=tensors_t[j], + ksize=ksize[:4] + ksize[5:], + strides=strides[:4] + strides[5:], + padding=padding, + ) + ) + avg_tensors_t_s = tf.divide(tf.add_n(tensors_t_averaged), len_ts) + + # put together + result_t.append(avg_tensors_t_s) + # stack together return tf.stack(result_t, axis=4) diff --git a/tfscripts/utils.py b/tfscripts/utils.py index e299175..6c858c8 100644 --- a/tfscripts/utils.py +++ b/tfscripts/utils.py @@ -1,6 +1,6 @@ -''' +""" Some utility functions for tfscripts -''' +""" from __future__ import division, print_function @@ -19,7 +19,7 @@ def count_parameters(var_list=None): var_list : None, optional If a var_list (list of tensors) is given, the number of parameters is calculated. - If var_list is None, all trainable paramters in the current graph + If var_list is None, all trainable parameters in the current graph are counted Returns @@ -33,7 +33,7 @@ def count_parameters(var_list=None): def get_angle(vec1, vec2, dtype=FLOAT_PRECISION): - """ Get the opening angle between two direction vectors. + """Get the opening angle between two direction vectors. vec1/2 : shape: [?,3] or [3] https://www.cs.berkeley.edu/~wkahan/Mindless.pdf @@ -55,7 +55,7 @@ def get_angle(vec1, vec2, dtype=FLOAT_PRECISION): Description """ - msg = 'Expect shape [?,3] or [3], but got {!r}' + msg = "Expect shape [?,3] or [3], but got {!r}" assert vec1.get_shape().as_list()[-1] == 3, msg.format(vec1.get_shape()) assert vec2.get_shape().as_list()[-1] == 3, msg.format(vec2.get_shape()) @@ -67,16 +67,21 @@ def get_angle(vec1, vec2, dtype=FLOAT_PRECISION): tmp3 = tf.norm(tensor=tmp1 - tmp2, axis=-1) tmp4 = tf.norm(tensor=tmp1 + tmp2, axis=-1) - theta = 2*tf.atan2(tmp3, tmp4) + theta = 2 * tf.atan2(tmp3, tmp4) return theta -def polynomial_interpolation(x, x_ref_min, x_ref_max, y_ref, - polynomial_order=2, - fill_value=None, - axis=-1, - equidistant_query_points=False, - dtype=FLOAT_PRECISION): +def polynomial_interpolation( + x, + x_ref_min, + x_ref_max, + y_ref, + polynomial_order=2, + fill_value=None, + axis=-1, + equidistant_query_points=False, + dtype=FLOAT_PRECISION, +): """Performs a 1-dimensional polynomial interpolation of degree 1 or 2 along the specified axis. @@ -130,8 +135,7 @@ def polynomial_interpolation(x, x_ref_min, x_ref_max, y_ref, """ if axis != -1: # Transpose if necessary: last axis corresponds to interp points - perm = [i for i in range(len(y_ref.get_shape())) - if i != axis] + [axis] + perm = [i for i in range(len(y_ref.get_shape())) if i != axis] + [axis] y_ref = tf.transpose(a=y_ref, perm=perm) x = tf.transpose(a=x, perm=perm) @@ -142,15 +146,18 @@ def polynomial_interpolation(x, x_ref_min, x_ref_max, y_ref, # sanity checks to make sure shapes match if x_shape[0] != y_shape[0]: - assert x_shape[0] is None or y_shape[0] is None, '{!r} != {!r}'.format( - x_shape[0], y_shape[0]) - assert x_shape[1:-1] == y_shape[1:-1], '{!r} != {!r}'.format( - x_shape[:-1], y_shape[:-1]) + assert x_shape[0] is None or y_shape[0] is None, "{!r} != {!r}".format( + x_shape[0], y_shape[0] + ) + assert x_shape[1:-1] == y_shape[1:-1], "{!r} != {!r}".format( + x_shape[:-1], y_shape[:-1] + ) # We can take advantage of equidistant binning to compute indices bin_width = (x_ref_max - x_ref_min) / nbins - indices = tf.histogram_fixed_width_bins(x, [x_ref_min, x_ref_max], - nbins=nbins) + indices = tf.histogram_fixed_width_bins( + x, [x_ref_min, x_ref_max], nbins=nbins + ) lower_boundary = indices < 1 upper_boundary = indices >= nbins - 1 @@ -167,13 +174,17 @@ def polynomial_interpolation(x, x_ref_min, x_ref_max, y_ref, if y_shape[0] is None or x_shape[0] is None: # Need to obtain y_ref shape dynamically in this case index_offset = tf.tile( - tf.range(tf.math.reduce_prod( - input_tensor=tf.shape(input=y_ref)[:-1])) * nbins, - tf.expand_dims(x_shape[-1], axis=-1)) + tf.range( + tf.math.reduce_prod(input_tensor=tf.shape(input=y_ref)[:-1]) + ) + * nbins, + tf.expand_dims(x_shape[-1], axis=-1), + ) index_offset = tf.reshape(index_offset, [-1] + x_shape[1:]) else: - index_offset = np.tile(np.arange(np.prod(y_shape[:-1])) * nbins, - x_shape[-1]) + index_offset = np.tile( + np.arange(np.prod(y_shape[:-1])) * nbins, x_shape[-1] + ) index_offset = np.reshape(index_offset, x_shape) y_ref_flat = tf.reshape(y_ref, [-1]) @@ -192,9 +203,9 @@ def polynomial_interpolation(x, x_ref_min, x_ref_max, y_ref, L_2 = (x - x_0) * (x - x_1) / (2 * bin_width_squared) else: - L_0 = (x - x_1) * (x - x_2) / ((x_0 - x_1)*(x_0 - x_2)) - L_1 = (x - x_0) * (x - x_2) / ((x_1 - x_0)*(x_1 - x_2)) - L_2 = (x - x_0) * (x - x_1) / ((x_2 - x_0)*(x_2 - x_1)) + L_0 = (x - x_1) * (x - x_2) / ((x_0 - x_1) * (x_0 - x_2)) + L_1 = (x - x_0) * (x - x_2) / ((x_1 - x_0) * (x_1 - x_2)) + L_2 = (x - x_0) * (x - x_1) / ((x_2 - x_0) * (x_2 - x_1)) result = y_0 * L_0 + y_1 * L_1 + y_2 * L_2 @@ -205,22 +216,27 @@ def polynomial_interpolation(x, x_ref_min, x_ref_max, y_ref, result = y_1 + (x - x_1) / (x_2 - x_1) * (y_2 - y_1) else: - raise ValueError('Interpolation order {!r} not supported'.format( - polynomial_order)) + raise ValueError( + "Interpolation order {!r} not supported".format(polynomial_order) + ) if fill_value is None: - result = tf.where(lower_boundary, - tf.zeros_like(result) - + tf.expand_dims(y_ref[..., 0], axis=-1), - result) - result = tf.where(upper_boundary, - tf.zeros_like(result) - + tf.expand_dims(y_ref[..., -1], axis=-1), - result) + result = tf.where( + lower_boundary, + tf.zeros_like(result) + tf.expand_dims(y_ref[..., 0], axis=-1), + result, + ) + result = tf.where( + upper_boundary, + tf.zeros_like(result) + tf.expand_dims(y_ref[..., -1], axis=-1), + result, + ) else: - result = tf.where(tf.logical_or(lower_boundary, upper_boundary), - tf.zeros_like(result) + fill_value, - result) + result = tf.where( + tf.logical_or(lower_boundary, upper_boundary), + tf.zeros_like(result) + fill_value, + result, + ) if axis != -1: # Transpose back if necessary diff --git a/tfscripts/weights.py b/tfscripts/weights.py index 3c71298..9ead6af 100644 --- a/tfscripts/weights.py +++ b/tfscripts/weights.py @@ -1,8 +1,8 @@ -''' +""" Core functions of tfscripts: Create weights and biases -''' +""" from __future__ import division, print_function @@ -13,8 +13,9 @@ from tfscripts import FLOAT_PRECISION -def new_weights(shape, stddev=1.0, name="weights", - float_precision=FLOAT_PRECISION): +def new_weights( + shape, stddev=1.0, name="weights", float_precision=FLOAT_PRECISION +): """Helper-function to create new weights Parameters @@ -34,15 +35,22 @@ def new_weights(shape, stddev=1.0, name="weights", tf.Tensor A tensor with the weights. """ - return tf.Variable(tf.random.truncated_normal( - shape, stddev=stddev, dtype=float_precision), - name=name, - dtype=float_precision) - - -def new_locally_connected_weights(shape, stddev=1.0, name="weights", - shared_axes=None, - float_precision=FLOAT_PRECISION): + return tf.Variable( + tf.random.truncated_normal( + shape, stddev=stddev, dtype=float_precision + ), + name=name, + dtype=float_precision, + ) + + +def new_locally_connected_weights( + shape, + stddev=1.0, + name="weights", + shared_axes=None, + float_precision=FLOAT_PRECISION, +): """Helper-function to create new weights Parameters @@ -79,7 +87,8 @@ def new_locally_connected_weights(shape, stddev=1.0, name="weights", # sample initial values initial_value = tf.random.truncated_normal( - shape_init, stddev=stddev, dtype=float_precision) + shape_init, stddev=stddev, dtype=float_precision + ) # tile over shared axes initial_value = tf.tile(initial_value, multiples=multiples) @@ -87,9 +96,10 @@ def new_locally_connected_weights(shape, stddev=1.0, name="weights", return tf.Variable(initial_value, name=name, dtype=float_precision) -def new_kernel_weights(shape, stddev=0.01, name="weights", - float_precision=FLOAT_PRECISION): - ''' +def new_kernel_weights( + shape, stddev=0.01, name="weights", float_precision=FLOAT_PRECISION +): + """ Get weights for a convolutional kernel. The weights will be initialised, so that convolution performs matrix multiplication over a single pixel. @@ -112,11 +122,12 @@ def new_kernel_weights(shape, stddev=0.01, name="weights", tf.Tensor A tensor with the weights. - ''' + """ weight_initialisation = np.zeros(shape) spatial_shape = shape[:-2] - middle_index = [slice((dim - 1) // 2, - (dim - 1) // 2 + 1) for dim in spatial_shape] + middle_index = [ + slice((dim - 1) // 2, (dim - 1) // 2 + 1) for dim in spatial_shape + ] # Set value in the middle to 1 and divide by the sqrt of # num_inputs to maintain normal distributed output. @@ -124,15 +135,16 @@ def new_kernel_weights(shape, stddev=0.01, name="weights", weight_initialisation[middle_index] = 1.0 / np.sqrt(shape[-2]) # add random noise to break symmetry - weight_initialisation += np.random.normal(size=shape, loc=0.0, - scale=stddev) + weight_initialisation += np.random.normal( + size=shape, loc=0.0, scale=stddev + ) - return tf.Variable(weight_initialisation, - name=name, dtype=float_precision) + return tf.Variable(weight_initialisation, name=name, dtype=float_precision) -def new_biases(length, stddev=1.0, name='biases', - float_precision=FLOAT_PRECISION): +def new_biases( + length, stddev=1.0, name="biases", float_precision=FLOAT_PRECISION +): """Get new biases. Parameters @@ -152,22 +164,27 @@ def new_biases(length, stddev=1.0, name='biases', tf.Tensor A tensor with the biases. """ - return tf.Variable(tf.random.truncated_normal(shape=[length], - stddev=stddev, - dtype=float_precision), - name=name, dtype=float_precision) + return tf.Variable( + tf.random.truncated_normal( + shape=[length], stddev=stddev, dtype=float_precision + ), + name=name, + dtype=float_precision, + ) # return tf.Variable(tf.random_normal(shape=[length], # stddev=2.0/length, # dtype=float_precision), # name=name, dtype=float_precision) -def create_conv_nd_layers_weights(num_input_channels, - filter_size_list, - num_filters_list, - name='conv_{}d', - float_precision=FLOAT_PRECISION): - '''Create weights and biases for conv 3d layers +def create_conv_nd_layers_weights( + num_input_channels, + filter_size_list, + num_filters_list, + name="conv_{}d", + float_precision=FLOAT_PRECISION, +): + """Create weights and biases for conv 3d layers Parameters ---------- @@ -197,29 +214,38 @@ def create_conv_nd_layers_weights(num_input_channels, ------- list of tf.Tensor, list of tf.Tensor Returns the list of weight and bias tensors for each layer - ''' + """ num_dims = len(filter_size_list[0]) name = name.format(num_dims) weights_list = [] biases_list = [] - for i, (filter_size, num_filters) in enumerate(zip(filter_size_list, - num_filters_list)): + for i, (filter_size, num_filters) in enumerate( + zip(filter_size_list, num_filters_list) + ): # Shape of the filter-weights for the convolution. shape = list(filter_size) + [num_input_channels, num_filters] if num_dims == 1: shape = shape.insert(1, 1) - weight_name = 'weights_{}_{:03d}'.format(name, i) - bias_name = 'biases_{}_{:03d}'.format(name, i) + weight_name = "weights_{}_{:03d}".format(name, i) + bias_name = "biases_{}_{:03d}".format(name, i) # weights_list.append(new_kernel_weights(shape=shape, name=weight_name)) - weights_list.append(new_weights(shape=shape, name=weight_name, - float_precision=float_precision)) - biases_list.append(new_biases(length=num_filters, name=bias_name, - float_precision=float_precision)) + weights_list.append( + new_weights( + shape=shape, name=weight_name, float_precision=float_precision + ) + ) + biases_list.append( + new_biases( + length=num_filters, + name=bias_name, + float_precision=float_precision, + ) + ) # update number of input channels for next layer num_input_channels = num_filters @@ -227,12 +253,14 @@ def create_conv_nd_layers_weights(num_input_channels, return weights_list, biases_list -def create_fc_layers_weights(num_inputs, - fc_sizes, - max_out_size_list=None, - name='fc', - float_precision=FLOAT_PRECISION): - ''' +def create_fc_layers_weights( + num_inputs, + fc_sizes, + max_out_size_list=None, + name="fc", + float_precision=FLOAT_PRECISION, +): + """ Create weights and biases for fully connected layers @@ -254,24 +282,34 @@ def create_fc_layers_weights(num_inputs, ------- list of tf.Tensor, list of tf.Tensor Returns the list of weight and bias tensors for each layer - ''' + """ # create max out array if max_out_size_list is None: max_out_size_list = [None for i in range(len(fc_sizes))] weights_list = [] biases_list = [] - for i, (num_outputs, max_out_size) in enumerate(zip(fc_sizes, - max_out_size_list)): - - weight_name = 'weights_{}_{:03d}'.format(name, i) - bias_name = 'biases_{}_{:03d}'.format(name, i) - - weights_list.append(new_weights(shape=[num_inputs, num_outputs], - name=weight_name, - float_precision=float_precision)) - biases_list.append(new_biases(length=num_outputs, name=bias_name, - float_precision=float_precision)) + for i, (num_outputs, max_out_size) in enumerate( + zip(fc_sizes, max_out_size_list) + ): + + weight_name = "weights_{}_{:03d}".format(name, i) + bias_name = "biases_{}_{:03d}".format(name, i) + + weights_list.append( + new_weights( + shape=[num_inputs, num_outputs], + name=weight_name, + float_precision=float_precision, + ) + ) + biases_list.append( + new_biases( + length=num_outputs, + name=bias_name, + float_precision=float_precision, + ) + ) if max_out_size is None: num_inputs = num_outputs From 79c08c10d045d421c334357dcb8318878d045434 Mon Sep 17 00:00:00 2001 From: mhuen Date: Wed, 10 Apr 2024 14:19:22 +0200 Subject: [PATCH 5/6] Update project meta info --- pyproject.toml | 2 +- tfscripts/__about__.py | 16 ++++++++++++++-- tfscripts/__init__.py | 21 ++++++++++++++++++++- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f935f7e..fa324a8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "tfscripts" -description = "Helper functions for tensorflow" +description = "Collection of TF functions and helpful additions" readme = "README.md" dynamic = ["version"] authors = [ diff --git a/tfscripts/__about__.py b/tfscripts/__about__.py index c18db77..546a541 100644 --- a/tfscripts/__about__.py +++ b/tfscripts/__about__.py @@ -1,5 +1,17 @@ -__version__ = "1.0.0-dev" +__description__ = "Collection of TF functions and helpful additions" + +__version_major__ = 1 +__version_minor__ = 0 +__version_patch__ = 0 +__version_info__ = "-dev" + +__version__ = "{}.{}.{}{}".format( + __version_major__, + __version_minor__, + __version_patch__, + __version_info__, +) __author__ = "Mirco Huennefeld" __author_email__ = "mirco.huennefeld@tu-dortmund.de" __description__ = "Collection of TF functions and helpful additions" -__url__ = "https://github.com/mhuen/TFScripts" +__url__ = "https://github.com/icecube/TFScripts" diff --git a/tfscripts/__init__.py b/tfscripts/__init__.py index 56d8193..6cb1dab 100644 --- a/tfscripts/__init__.py +++ b/tfscripts/__init__.py @@ -1,6 +1,25 @@ -from tfscripts.__about__ import __version__, __description__, __url__ import tensorflow as tf +from .__about__ import ( + __version_major__, + __version_minor__, + __version_patch__, + __version_info__, + __version__, + __description__, + __url__, +) + +__all__ = [ + "__version_major__", + "__version_minor__", + "__version_patch__", + "__version_info__", + "__version__", + "__description__", + "__url__", + "FLOAT_PRECISION", +] # constants FLOAT_PRECISION = tf.float32 From cd178bb63d64de6837bde6394e9e1953104e5bda Mon Sep 17 00:00:00 2001 From: mhuen Date: Wed, 10 Apr 2024 14:27:15 +0200 Subject: [PATCH 6/6] Add try except block around tf import --- tfscripts/__init__.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tfscripts/__init__.py b/tfscripts/__init__.py index 6cb1dab..30870c5 100644 --- a/tfscripts/__init__.py +++ b/tfscripts/__init__.py @@ -1,5 +1,3 @@ -import tensorflow as tf - from .__about__ import ( __version_major__, __version_minor__, @@ -21,5 +19,11 @@ "FLOAT_PRECISION", ] -# constants -FLOAT_PRECISION = tf.float32 +try: + import tensorflow as tf + + # constants + FLOAT_PRECISION = tf.float32 +except ImportError: + FLOAT_PRECISION = "float32" + print("Tensorflow not found. Some functions will not work.")