Skip to content
This repository was archived by the owner on Jun 23, 2025. It is now read-only.

Commit fff2642

Browse files
Making keras-contrib compatible with tf.keras (#387)
* Changed travis * Added the script to convert * fix indentation. * Trying fewer changes. * Added the ignore in pytest. * fix order of replacement. * Removed the hacky replace. * Used the normal backend for normalize data format. * Making really sure we don't get keras_contrib. * Ignore backend tests for tf.keras. * fixed some imports and ignored test for tf_keras. * used pip install to install tf-keras. * Maybe the option is not at the right place. * Trying a separate file. * import the contrib backend. * Moved to the setup.py * Let's not use import * * Added compute_output_shape. * Used a more recent version of tf. * Used custom layer to ensure it works with tf.keras. * Fixing return forgotten. * Changed line endings to unix. * moving to a separate file. * Added verbose option. * Made a more robust check. * hopefully all bug fixed. * Let's not use the setup.py to convert to tf.keras. * Hacked my way through * fixed stupid mistake. * Some pep8 fixes. * Fixed imports in tensorboard. * Fixed import again. * Added some xfails. * Fixed the padam error. * Hopefully fixed tensorboard * Adding an xfail. * Added some xfail. * Fixed pep8 and removed xfail. * Used skipif. * Removed useless diff. * Simplified the base layer. * Added the back and forth conversion for tf.keras. * Removed Useless function in base_layer. * Simplified the hack to work with tensorshapes. * Fix pep8 * Removed small diff. * Put the script in the setup.py. * Removed the import changes from the setup.py. The setup.py gets executed multiple times when installing the packages. This causes problem. * Clarified the docstring of `to_tuple`. * Added install details. * /bin/bash: q: command not found * Typos. * Changed the hard reset to a git stash. * Adding to_tuple to capsule.py. * skipping when tf.keras. * Removing unused imports. * Revert some changes. * Forgot the to_tuple. * Removed custom objects. * Removed git reset. * Added a test. * Fixed typo. * Some fixes here and there. * Pep8
1 parent 83f8878 commit fff2642

File tree

19 files changed

+249
-7
lines changed

19 files changed

+249
-7
lines changed

.travis.yml

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ matrix:
1212
env: KERAS_BACKEND=tensorflow
1313
- python: 3.6
1414
env: KERAS_BACKEND=tensorflow
15+
- python: 3.6
16+
env: KERAS_BACKEND=tensorflow USE_TF_KERAS=1 PYTEST_IGNORE='--ignore=tests/test_doc_auto_generation.py --ignore=tests/keras_contrib/backend --ignore=tests/keras_contrib/utils/save_load_utils_test.py'
1517
- python: 3.6
1618
env: KERAS_BACKEND=theano THEANO_FLAGS=optimizer=fast_compile
1719
# - python: 3.6
@@ -37,7 +39,9 @@ install:
3739
- source activate test-environment
3840

3941
- travis_retry pip install --only-binary=numpy,scipy,pandas numpy nose scipy h5py theano mkdocs pytest pytest-pep8 pandas pygithub --progress-bar off
40-
- pip install git+https://github.com/keras-team/keras.git --progress-bar off
42+
- if [[ "$USE_TF_KERAS" == "" ]]; then
43+
pip install git+https://github.com/keras-team/keras.git --progress-bar off;
44+
fi
4145

4246
# set library path
4347
- export LD_LIBRARY_PATH=$HOME/miniconda/envs/test-environment/lib/:$LD_LIBRARY_PATH
@@ -46,6 +50,9 @@ install:
4650
travis_retry conda install -q mkl mkl-service;
4751
fi
4852

53+
- if [[ "$USE_TF_KERAS" == "1" ]]; then
54+
python convert_to_tf_keras.py;
55+
fi
4956
- pip install -e .[tests] --progress-bar off
5057

5158
# install TensorFlow (CPU version).
@@ -61,15 +68,15 @@ install:
6168
script:
6269
- export MKL_THREADING_LAYER="GNU"
6370
# run keras backend init to initialize backend config
64-
- python -c "import keras.backend"
71+
- python -c "import keras_contrib.backend"
6572
# create dataset directory to avoid concurrent directory creation at runtime
6673
- mkdir ~/.keras/datasets
6774
# set up keras backend
6875
- sed -i -e 's/"backend":[[:space:]]*"[^"]*/"backend":\ "'$KERAS_BACKEND'/g' ~/.keras/keras.json;
6976
- echo -e "Running tests with the following config:\n$(cat ~/.keras/keras.json)"
7077
- if [[ "$TEST_MODE" == "PEP8_DOC" ]]; then
71-
PYTHONPATH=$PWD:$PYTHONPATH py.test --pep8 -m pep8 -n0 && py.test tests/tooling/ && cd contrib_docs && python autogen.py && mkdocs build;
78+
PYTHONPATH=$PWD:$PYTHONPATH py.test --pep8 -m pep8 -n0 && py.test tests/tooling/ convert_to_tf_keras.py && cd contrib_docs && python autogen.py && mkdocs build;
7279
else
73-
PYTHONPATH=$PWD:$PYTHONPATH py.test tests/ --ignore=tests/tooling/
80+
PYTHONPATH=$PWD:$PYTHONPATH py.test tests/ $PYTEST_IGNORE --ignore=tests/tooling/
7481
--cov-config .coveragerc --cov=keras_contrib tests/;
7582
fi

CONTRIBUTING.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,46 @@ We love pull requests. Here's a quick guide:
6161

6262
11. Submit your PR. If your changes have been approved in a previous discussion, and if you have complete (and passing) unit tests, your PR is likely to be merged promptly. Otherwise, well...
6363

64+
## About keras-team/keras and tensorflow.keras
65+
66+
This repo supports both keras-team/keras and tensorflow.keras. The way this is done is by changing all the imports in the code by parsing it. This is checked with travis.ci every time you push a commit in a pull request.
67+
68+
There are a number of reasons why your code would work with keras-team/keras but not with tf.keras. The most common is that you use keras' private API. Since both keras are only similar in behavior with respect to their public API, you should only use this. Otherwise it's likely that the function you are using is not in the same place in tf.keras (or does not even exist at all).
69+
70+
Another gotcha is that when creating custom layers and implementing the `build` function, keras-team/keras expects as `input_shape` a tuple of ints. With tf.keras, `input_shape` is a tuple with `Dimensions` objects. This is likely to make the code incompatible. To solve this problem, you should do:
71+
72+
```python
73+
from keras.layers import Layer
74+
from keras_contrib.utils.test_utils import to_tuple
75+
76+
77+
class MyLayer(Layer):
78+
...
79+
80+
def build(self, input_shape):
81+
input_shape = to_tuple(input_shape)
82+
# now `input_shape` is a tuple of ints or None like in keras-team/keras
83+
...
84+
```
85+
86+
To change all the imports in your code to tf.keras to test compatibility, you can do:
87+
```
88+
python convert_to_tf_keras.py
89+
```
90+
91+
To convert your codebase back to keras-team/keras, do:
92+
```
93+
python convert_to_tf_keras.py --revert
94+
```
95+
96+
Note that you are strongly encouraged to commit your code before in case the parsing would go wrong. To discard all the changes you made since the previous commit:
97+
```
98+
# saves a copy of your current codebase in the git stash and comes back to the previous commit
99+
git stash
100+
101+
git stash pop # get your copy back from the git stash if you need to.
102+
```
103+
64104
## A Note for Contributors
65105

66106
Both Keras-Contrib and Keras operate under the [MIT License](LICENSE). At the discretion of the maintainers of both repositories, code may be moved from Keras-Contrib to Keras and vice versa.

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ As the community contributions in Keras-Contrib are tested, used, validated, and
99
---
1010
## Installation
1111

12+
#### Install keras_contrib for keras-team/keras
1213
For instructions on how to install Keras,
1314
see [the Keras installation page](https://keras.io/#installation).
1415

@@ -24,6 +25,25 @@ Alternatively, using pip:
2425
sudo pip install git+https://www.github.com/keras-team/keras-contrib.git
2526
```
2627

28+
to uninstall:
29+
```pip
30+
pip uninstall keras_contrib
31+
```
32+
33+
#### Install keras_contrib for tensorflow.keras
34+
35+
```shell
36+
git clone https://www.github.com/keras-team/keras-contrib.git
37+
cd keras-contrib
38+
python convert_to_tf_keras.py
39+
USE_TF_KERAS=1 python setup.py install
40+
```
41+
42+
to uninstall:
43+
```shell
44+
pip uninstall tf_keras_contrib
45+
```
46+
2747
For contributor guidelines see [CONTRIBUTING.md](https://github.com/keras-team/keras-contrib/blob/master/CONTRIBUTING.md)
2848

2949
---

convert_to_tf_keras.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import os
2+
import sys
3+
4+
list_conversions = [('import keras.', 'import tensorflow.keras.'),
5+
('import keras ', 'from tensorflow import keras '),
6+
('import keras\n', 'from tensorflow import keras\n'),
7+
('from keras.', 'from tensorflow.keras.'),
8+
('from keras ', 'from tensorflow.keras ')]
9+
10+
11+
def replace_imports_in_text(string, revert):
12+
if revert:
13+
list_imports_to_change = [x[::-1] for x in list_conversions]
14+
else:
15+
list_imports_to_change = list_conversions
16+
17+
text_updated = string
18+
for old_str, new_str in list_imports_to_change:
19+
text_updated = text_updated.replace(old_str, new_str)
20+
return text_updated
21+
22+
23+
def replace_imports_in_file(file_path, revert):
24+
if not file_path.endswith('.py'):
25+
return False
26+
if os.path.abspath(file_path) == os.path.abspath(__file__):
27+
return False
28+
with open(file_path, 'r') as f:
29+
text = f.read()
30+
31+
text_updated = replace_imports_in_text(text, revert)
32+
33+
with open(file_path, 'w+') as f:
34+
f.write(text_updated)
35+
36+
return text_updated != text
37+
38+
39+
def convert_codebase(revert):
40+
nb_of_files_changed = 0
41+
keras_dir = os.path.dirname(os.path.abspath(__file__))
42+
for root, dirs, files in os.walk(keras_dir):
43+
for name in files:
44+
if replace_imports_in_file(os.path.join(root, name), revert):
45+
nb_of_files_changed += 1
46+
print('Changed imports in ' + str(nb_of_files_changed) + ' files.')
47+
print('Those files were found in the directory ' + keras_dir)
48+
49+
50+
def convert_to_tf_keras():
51+
"""Convert the codebase to tf.keras"""
52+
convert_codebase(False)
53+
54+
55+
def convert_to_keras_team_keras():
56+
"""Convert the codebase from tf.keras to keras-team/keras"""
57+
convert_codebase(True)
58+
59+
60+
def test_replace_imports():
61+
python_code = """
62+
import keras
63+
from keras import backend as K
64+
import os
65+
import keras_contrib
66+
import keras_contrib.layers as lay
67+
import keras.layers
68+
from keras.layers import Dense
69+
70+
if K.backend() == 'tensorflow':
71+
import tensorflow as tf
72+
function = tf.max
73+
"""
74+
75+
expected_code = """
76+
from tensorflow import keras
77+
from tensorflow.keras import backend as K
78+
import os
79+
import keras_contrib
80+
import keras_contrib.layers as lay
81+
import tensorflow.keras.layers
82+
from tensorflow.keras.layers import Dense
83+
84+
if K.backend() == 'tensorflow':
85+
import tensorflow as tf
86+
function = tf.max
87+
"""
88+
89+
code_with_replacement = replace_imports_in_text(python_code, False)
90+
assert expected_code == code_with_replacement
91+
assert python_code == replace_imports_in_text(code_with_replacement, True)
92+
93+
94+
if __name__ == '__main__':
95+
if '--revert' in sys.argv:
96+
convert_to_keras_team_keras()
97+
else:
98+
convert_to_tf_keras()

keras_contrib/layers/advanced_activations/pelu.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from keras.layers import Layer, InputSpec
22
from keras import initializers, regularizers, constraints
33
import keras.backend as K
4+
from keras_contrib.utils.test_utils import to_tuple
45

56

67
class PELU(Layer):
@@ -61,6 +62,7 @@ def __init__(self, alpha_initializer='ones',
6162
self.shared_axes = list(shared_axes)
6263

6364
def build(self, input_shape):
65+
input_shape = to_tuple(input_shape)
6466
param_shape = list(input_shape[1:])
6567
self.param_broadcast = [False] * len(param_shape)
6668
if self.shared_axes is not None:

keras_contrib/layers/advanced_activations/srelu.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from keras.layers import Layer, InputSpec
22
from keras import initializers
33
import keras.backend as K
4+
from keras_contrib.utils.test_utils import to_tuple
45

56

67
class SReLU(Layer):
@@ -58,6 +59,7 @@ def __init__(self, t_left_initializer='zeros',
5859
self.shared_axes = list(shared_axes)
5960

6061
def build(self, input_shape):
62+
input_shape = to_tuple(input_shape)
6163
param_shape = list(input_shape[1:])
6264
self.param_broadcast = [False] * len(param_shape)
6365
if self.shared_axes is not None:

keras_contrib/layers/capsule.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from keras import initializers
88
from keras import constraints
99
from keras.layers import Layer
10+
from keras_contrib.utils.test_utils import to_tuple
1011

1112

1213
class Capsule(Layer):
@@ -129,6 +130,7 @@ def __init__(self,
129130
self.constraint = constraints.get(constraint)
130131

131132
def build(self, input_shape):
133+
input_shape = to_tuple(input_shape)
132134
input_dim_capsule = input_shape[-1]
133135
if self.share_weights:
134136
self.W = self.add_weight(name='capsule_kernel',

keras_contrib/layers/convolutional/cosineconvolution2d.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from keras.layers import InputSpec
1313
from keras_contrib.utils.conv_utils import conv_output_length
1414
from keras_contrib.utils.conv_utils import normalize_data_format
15+
from keras_contrib.utils.test_utils import to_tuple
1516
import numpy as np
1617

1718

@@ -128,6 +129,7 @@ def __init__(self, filters, kernel_size,
128129
super(CosineConvolution2D, self).__init__(**kwargs)
129130

130131
def build(self, input_shape):
132+
input_shape = to_tuple(input_shape)
131133
if self.data_format == 'channels_first':
132134
stack_size = input_shape[1]
133135
self.kernel_shape = (self.filters, stack_size, self.nb_row, self.nb_col)

keras_contrib/layers/core.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from keras import constraints
1010
from keras.layers import InputSpec
1111
from keras.layers import Layer
12+
from keras_contrib.utils.test_utils import to_tuple
1213

1314

1415
class CosineDense(Layer):
@@ -107,6 +108,7 @@ def __init__(self, units, kernel_initializer='glorot_uniform',
107108
super(CosineDense, self).__init__(**kwargs)
108109

109110
def build(self, input_shape):
111+
input_shape = to_tuple(input_shape)
110112
ndim = len(input_shape)
111113
assert ndim >= 2
112114
input_dim = input_shape[-1]

keras_contrib/layers/crf.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from keras_contrib.losses import crf_loss
1515
from keras_contrib.metrics import crf_marginal_accuracy
1616
from keras_contrib.metrics import crf_viterbi_accuracy
17+
from keras_contrib.utils.test_utils import to_tuple
1718

1819

1920
class CRF(Layer):
@@ -247,6 +248,7 @@ def __init__(self, units,
247248
self.unroll = unroll
248249

249250
def build(self, input_shape):
251+
input_shape = to_tuple(input_shape)
250252
self.input_spec = [InputSpec(shape=input_shape)]
251253
self.input_dim = input_shape[-1]
252254

0 commit comments

Comments
 (0)