Comparar commits
130 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| d135eda40e | |||
| 49f7649036 | |||
| 6b16f3c135 | |||
| d9255f15a4 | |||
| b42f6760bc | |||
| 6b51d149ca | |||
| 8040ad72dd | |||
| ec048edffc | |||
| 23269507fd | |||
| eafdffff75 | |||
| 5834747dc7 | |||
| 6c2dea64fc | |||
| ddcf66fbe8 | |||
| 5edd96a9f8 | |||
| c6ec39258b | |||
| 7866fbaa1a | |||
| 94397e08ae | |||
| 98db0285ee | |||
| 7413956e7e | |||
| 9a4598da50 | |||
| a09c9f6c2d | |||
| e4ab777d07 | |||
| 4fab0bf9a8 | |||
| 49a7c7376d | |||
| fb9dbdb10c | |||
| a85263fb3e | |||
| f6c1730cf3 | |||
| 598954d2c8 | |||
| 21c5e5479b | |||
| d1ad183770 | |||
| 9dca90e705 | |||
| 5a7f6b0e74 | |||
| 7dcd2982b2 | |||
| 3462835597 | |||
| 585f33f6b7 | |||
| 1aa9e9199b | |||
| fb97b6e0fa | |||
| 60cf7ca6b2 | |||
| 1b539993aa | |||
| de73eda89a | |||
| 219d6ee5be | |||
| f430de10fb | |||
| a2f6ae2c66 | |||
| b713122e77 | |||
| 75470e380f | |||
| b5ad5334fc | |||
| 04a20177cf | |||
| 58d1d0678f | |||
| abf8691ade | |||
| 7425e68cd6 | |||
| ab6b82c2db | |||
| 6814506528 | |||
| 1ddf23528e | |||
| 86c8d1dd45 | |||
| 929ae992c2 | |||
| 3d9428d344 | |||
| e0543fbfc8 | |||
| 767846e642 | |||
| d852c2d772 | |||
| ff45159b69 | |||
| 7766ab341f | |||
| 4135aeebc4 | |||
| ced84c4b42 | |||
| 8d5b2ce60c | |||
| c0f0b660a6 | |||
| d3c33613a1 | |||
| 5ca5699b00 | |||
| be6503a8a8 | |||
| c73ba916f6 | |||
| e1c3988198 | |||
| f65a56fb65 | |||
| 00a2724260 | |||
| a625fcde5c | |||
| 73f374ec67 | |||
| 21cf50734a | |||
| 4a6f06f06d | |||
| 295e4f8064 | |||
| f4cb890024 | |||
| cd943231d1 | |||
| f7b925a893 | |||
| 5d63ab4251 | |||
| d4b618bf23 | |||
| 5012678e17 | |||
| 11d9c995cc | |||
| d92fab69a2 | |||
| 19463a19b8 | |||
| ca1122fe80 | |||
| 7fc707e13e | |||
| 846d25ab97 | |||
| 8c0a8b4b04 | |||
| 1b1e09a366 | |||
| fd427b8cdb | |||
| 53303fdb10 | |||
| 720ed1adc4 | |||
| 43e418d1d2 | |||
| 75d9415c82 | |||
| 552978dc58 | |||
| 508bb8f541 | |||
| c3c97905fe | |||
| 33cee3f947 | |||
| f0659766fc | |||
| 62973243ae | |||
| 6a0c9a617d | |||
| 3c180eafed | |||
| 763bd6d8f1 | |||
| 36317214ae | |||
| a5f53155a5 | |||
| 78be823518 | |||
| 5810f7a9c7 | |||
| 0bc8fac446 | |||
| aea62d8baf | |||
| 7819b9c14e | |||
| eede3dc43d | |||
| ea29308eaa | |||
| 21b72a3b13 | |||
| f3bbf31497 | |||
| 17e073d87e | |||
| 60c52ea766 | |||
| bfa38fb747 | |||
| 7c73bfc50d | |||
| fccd4f8055 | |||
| 1b67c59de8 | |||
| a9d2a99500 | |||
| 0bb4e0fad5 | |||
| 1e09e0a9d4 | |||
| 04bf5ac57a | |||
| c469f80f81 | |||
| 44b25b80b2 | |||
| 12907534f8 | |||
| 10d7e21efc |
@@ -19,3 +19,4 @@ examples/img/*
|
||||
|
||||
# developer environments
|
||||
.idea
|
||||
.vscode
|
||||
|
||||
+23
-3
@@ -14,9 +14,13 @@ matrix:
|
||||
- python: 3.5
|
||||
env: KERAS_BACKEND=tensorflow
|
||||
- python: 2.7
|
||||
env: KERAS_BACKEND=theano
|
||||
env: KERAS_BACKEND=theano THEANO_FLAGS=optimizer=fast_compile
|
||||
- python: 3.5
|
||||
env: KERAS_BACKEND=theano
|
||||
env: KERAS_BACKEND=theano THEANO_FLAGS=optimizer=fast_compile
|
||||
- python: 2.7
|
||||
env: KERAS_BACKEND=cntk
|
||||
- python: 3.5
|
||||
env: KERAS_BACKEND=cntk
|
||||
install:
|
||||
# code below is taken from http://conda.pydata.org/docs/travis.html
|
||||
# We do this conditionally because it saves us some downloading if the
|
||||
@@ -49,6 +53,22 @@ install:
|
||||
|
||||
# install TensorFlow (CPU version).
|
||||
- pip install tensorflow
|
||||
|
||||
# install cntk
|
||||
- if [[ "$TRAVIS_PYTHON_VERSION" == "2.7" ]]; then
|
||||
pip install https://cntk.ai/PythonWheel/CPU-Only/cntk-2.0-cp27-cp27mu-linux_x86_64.whl;
|
||||
elif [[ "$TRAVIS_PYTHON_VERSION" == "3.5" ]]; then
|
||||
pip install https://cntk.ai/PythonWheel/CPU-Only/cntk-2.0-cp35-cp35m-linux_x86_64.whl;
|
||||
fi
|
||||
|
||||
#install open mpi
|
||||
- rm -rf ~/mpi
|
||||
- mkdir ~/mpi
|
||||
- pushd ~/mpi
|
||||
- wget http://cntk.ai/PythonWheel/ForKeras/depends/openmpi_1.10-3.zip
|
||||
- unzip ./openmpi_1.10-3.zip
|
||||
- sudo dpkg -i openmpi_1.10-3.deb
|
||||
- popd
|
||||
|
||||
# command to run tests
|
||||
script:
|
||||
@@ -66,5 +86,5 @@ script:
|
||||
elif [[ "$TEST_MODE" == "DOC" ]]; then
|
||||
PYTHONPATH=$PWD:$PYTHONPATH py.test tests/test_documentation.py;
|
||||
else
|
||||
PYTHONPATH=$PWD:$PYTHONPATH py.test tests/ --ignore=tests/integration_tests --ignore=tests/test_documentation.py --cov=keras tests/ --cov-fail-under 78 --cov-report term-missing;
|
||||
PYTHONPATH=$PWD:$PYTHONPATH py.test tests/ --ignore=tests/integration_tests --ignore=tests/test_documentation.py --cov=keras tests/ --cov-fail-under 80 --cov-report term-missing;
|
||||
fi
|
||||
|
||||
+22
-9
@@ -19,6 +19,7 @@ To easily update Theano: `pip install git+git://github.com/Theano/Theano.git --u
|
||||
|
||||
The more information you provide, the easier it is for us to validate that there is a bug and the faster we'll be able to take action. If you want your issue to be resolved quickly, following the steps above is crucial.
|
||||
|
||||
---
|
||||
|
||||
## Requesting a Feature
|
||||
|
||||
@@ -31,39 +32,50 @@ You can also use Github issues to request features you would like to see in Kera
|
||||
3. After discussing the feature you may choose to attempt a Pull Request. If you're at all able, start writing some code. We always have more work to do than time to do it. If you can write some code then that will speed the process along.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Requests for Contributions
|
||||
|
||||
[This is the board](https://github.com/fchollet/keras/projects/1) where we list current outstanding issues and features to be added. If you want to start contributing to Keras, this is the place to start.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Pull Requests
|
||||
|
||||
**Where should I submit my pull request?**
|
||||
|
||||
1. **Keras improvements and bugfixes** go to the [Keras `master` branch](https://github.com/fchollet/keras/tree/master).
|
||||
2. **New features** such as layers and datasets go to [keras-contrib](https://github.com/farizrahman4u/keras-contrib). Unless it is a new feature listed in [Requests for Contributions](https://github.com/fchollet/keras/projects/1), in which case it belongs in core Keras.
|
||||
2. **Experimental new features** such as layers and datasets go to [keras-contrib](https://github.com/farizrahman4u/keras-contrib). Unless it is a new feature listed in [Requests for Contributions](https://github.com/fchollet/keras/projects/1), in which case it belongs in core Keras. If you think your feature belongs in core Keras, you can submit a design doc to explain your feature and argue for it (see explainations below).
|
||||
|
||||
Here's a quick guide to submitting your improvements:
|
||||
|
||||
1. If your PR introduces a change in functionality, make sure you start by opening an issue to discuss whether the change should be made, and how to handle it. This will save you from having your PR closed down the road! Of course, if your PR is a simple bug fix, you don't need to do that.
|
||||
1. If your PR introduces a change in functionality, make sure you start by writing a design doc and sending it to the Keras mailing list to discuss whether the change should be made, and how to handle it. This will save you from having your PR closed down the road! Of course, if your PR is a simple bug fix, you don't need to do that. The process for writing and submitting design docs is as follow:
|
||||
- Start from [this Google Doc template](https://docs.google.com/document/d/1ZXNfce77LDW9tFAj6U5ctaJmI5mT7CQXOFMEAZo-mAA/edit#), and copy it to new Google doc.
|
||||
- Fill in the content. Note that you will need to insert code examples. To insert code, use a Google Doc extension such as [CodePretty](https://chrome.google.com/webstore/detail/code-pretty/igjbncgfgnfpbnifnnlcmjfbnidkndnh?hl=en) (there are several such extensions available).
|
||||
- Set sharing settings to "everyone with the link is allowed to comment"
|
||||
- Send the document to `keras-users@googlegroups.com` with a subject that starts with `[API DESIGN REVIEW]` (all caps) so that we notice it.
|
||||
- Wait for comments, and answer them as they come. Edit the proposal as necessary.
|
||||
- The proposal will finally be approved or rejected. Once approved, you can send out Pull Requests or ask others to write Pull Requests.
|
||||
|
||||
2. Write the code. This is the hard part!
|
||||
|
||||
2. Write the code (or get others to write it). This is the hard part!
|
||||
|
||||
3. Make sure any new function or class you introduce has proper docstrings. Make sure any code you touch still has up-to-date docstrings and documentation. **Docstring style should be respected.** In particular, they should be formatted in MarkDown, and there should be sections for `Arguments`, `Returns`, `Raises` (if applicable). Look at other docstrings in the codebase for examples.
|
||||
|
||||
4. Write tests. Your code should have full unit test coverage. If you want to see your PR merged promptly, this is crucial.
|
||||
|
||||
5. Run our test suite locally. It's easy: from the Keras folder, simply run: `py.test tests/`.
|
||||
- You will need to install the test requirements as well: `pip install -e .[tests]`.
|
||||
- You will need to install the test requirements as well: `pip install -e .[tests]`.
|
||||
|
||||
6. Make sure all tests are passing:
|
||||
- with the Theano backend, on Python 2.7 and Python 3.5. Make sure you have the development version of Theano.
|
||||
- with the TensorFlow backend, on Python 2.7 and Python 3.5. Make sure you have the development version of TensorFlow.
|
||||
- with the Theano backend, on Python 2.7 and Python 3.5. Make sure you have the development version of Theano.
|
||||
- with the TensorFlow backend, on Python 2.7 and Python 3.5. Make sure you have the development version of TensorFlow.
|
||||
|
||||
7. We use PEP8 syntax conventions, but we aren't dogmatic when it comes to line length. Make sure your lines stay reasonably sized, though. To make your life easier, we recommend running a PEP8 linter:
|
||||
- Install PEP8 packages: `pip install pep8 pytest-pep8 autopep8`
|
||||
- Run a standalone PEP8 check: `py.test --pep8 -m pep8`
|
||||
- You can automatically fix some PEP8 error by running: `autopep8 -i --select <errors> <FILENAME>` for example: `autopep8 -i --select E128 tests/keras/backend/test_backends.py`
|
||||
- Install PEP8 packages: `pip install pep8 pytest-pep8 autopep8`
|
||||
- Run a standalone PEP8 check: `py.test --pep8 -m pep8`
|
||||
- You can automatically fix some PEP8 error by running: `autopep8 -i --select <errors> <FILENAME>` for example: `autopep8 -i --select E128 tests/keras/backend/test_backends.py`
|
||||
|
||||
8. When committing, use appropriate, descriptive commit messages.
|
||||
|
||||
@@ -71,6 +83,7 @@ Here's a quick guide to submitting your improvements:
|
||||
|
||||
10. Submit your PR. If your changes have been approved in a previous discussion, and if you have complete (and passing) unit tests as well as proper docstrings/documentation, your PR is likely to be merged promptly. Otherwise, well...
|
||||
|
||||
---
|
||||
|
||||
## Adding new examples
|
||||
|
||||
|
||||
+5
-1
@@ -8,8 +8,12 @@ All contributions by Google:
|
||||
Copyright (c) 2015, Google, Inc.
|
||||
All rights reserved.
|
||||
|
||||
All contributions by Microsoft:
|
||||
Copyright (c) 2017, Microsoft, Inc.
|
||||
All rights reserved.
|
||||
|
||||
All other contributions:
|
||||
Copyright (c) 2015, the respective contributors.
|
||||
Copyright (c) 2015 - 2017, the respective contributors.
|
||||
All rights reserved.
|
||||
|
||||
Each contributor holds copyright over their respective contributions.
|
||||
|
||||
+8
-3
@@ -1,11 +1,11 @@
|
||||
# Keras: Deep Learning library for TensorFlow and Theano
|
||||
# Keras: Deep Learning for Python
|
||||
|
||||
[](https://travis-ci.org/fchollet/keras)
|
||||
[](https://github.com/fchollet/keras/blob/master/LICENSE)
|
||||
|
||||
## You have just found Keras.
|
||||
|
||||
Keras is a high-level neural networks API, written in Python and capable of running on top of either [TensorFlow](https://github.com/tensorflow/tensorflow) or [Theano](https://github.com/Theano/Theano). It was developed with a focus on enabling fast experimentation. *Being able to go from idea to result with the least possible delay is key to doing good research.*
|
||||
Keras is a high-level neural networks API, written in Python and capable of running on top of [TensorFlow](https://github.com/tensorflow/tensorflow), [CNTK](https://github.com/Microsoft/cntk), or [Theano](https://github.com/Theano/Theano). It was developed with a focus on enabling fast experimentation. *Being able to go from idea to result with the least possible delay is key to doing good research.*
|
||||
|
||||
Use Keras if you need a deep learning library that:
|
||||
|
||||
@@ -125,6 +125,11 @@ Keras uses the following dependencies:
|
||||
- TensorFlow
|
||||
- [See installation instructions](https://www.tensorflow.org/install/).
|
||||
|
||||
*When using the CNTK backend:*
|
||||
|
||||
- CNTK
|
||||
- [See installation instructions](https://docs.microsoft.com/en-us/cognitive-toolkit/setup-cntk-on-your-machine).
|
||||
|
||||
*When using the Theano backend:*
|
||||
|
||||
- Theano
|
||||
@@ -143,7 +148,7 @@ sudo pip install keras
|
||||
------------------
|
||||
|
||||
|
||||
## Switching from TensorFlow to Theano
|
||||
## Switching from TensorFlow to CNTK or Theano
|
||||
|
||||
By default, Keras will use TensorFlow as its tensor manipulation library. [Follow these instructions](http://keras.io/backend/) to configure the Keras backend.
|
||||
|
||||
|
||||
+6
-3
@@ -286,7 +286,8 @@ PAGES = [
|
||||
'page': 'utils.md',
|
||||
'all_module_functions': [utils],
|
||||
'classes': [utils.CustomObjectScope,
|
||||
utils.HDF5Matrix]
|
||||
utils.HDF5Matrix,
|
||||
utils.Sequence]
|
||||
},
|
||||
]
|
||||
|
||||
@@ -320,9 +321,11 @@ def get_classes_ancestors(classes):
|
||||
|
||||
|
||||
def get_function_signature(function, method=True):
|
||||
signature = getattr(function, '_legacy_support_signature', None)
|
||||
if signature is None:
|
||||
wrapped = getattr(function, '_original_function', None)
|
||||
if wrapped is None:
|
||||
signature = inspect.getargspec(function)
|
||||
else:
|
||||
signature = inspect.getargspec(wrapped)
|
||||
defaults = signature.defaults
|
||||
if method:
|
||||
args = signature.args[1:]
|
||||
|
||||
externo
+3
-3
@@ -130,10 +130,10 @@ for i, layer in enumerate(base_model.layers):
|
||||
print(i, layer.name)
|
||||
|
||||
# we chose to train the top 2 inception blocks, i.e. we will freeze
|
||||
# the first 172 layers and unfreeze the rest:
|
||||
for layer in model.layers[:172]:
|
||||
# the first 249 layers and unfreeze the rest:
|
||||
for layer in model.layers[:249]:
|
||||
layer.trainable = False
|
||||
for layer in model.layers[172:]:
|
||||
for layer in model.layers[249:]:
|
||||
layer.trainable = True
|
||||
|
||||
# we need to recompile the model for these modifications to take effect
|
||||
|
||||
externo
+5
-4
@@ -4,12 +4,13 @@
|
||||
|
||||
Keras is a model-level library, providing high-level building blocks for developing deep learning models. It does not handle itself low-level operations such as tensor products, convolutions and so on. Instead, it relies on a specialized, well-optimized tensor manipulation library to do so, serving as the "backend engine" of Keras. Rather than picking one single tensor library and making the implementation of Keras tied to that library, Keras handles the problem in a modular way, and several different backend engines can be plugged seamlessly into Keras.
|
||||
|
||||
At this time, Keras has two backend implementations available: the **TensorFlow** backend and the **Theano** backend.
|
||||
At this time, Keras has three backend implementations available: the **TensorFlow** backend, the **Theano** backend, and the **CNTK** backend.
|
||||
|
||||
- [TensorFlow](http://www.tensorflow.org/) is an open-source symbolic tensor manipulation framework developed by Google, Inc.
|
||||
- [Theano](http://deeplearning.net/software/theano/) is an open-source symbolic tensor manipulation framework developed by LISA/MILA Lab at Université de Montréal.
|
||||
- [CNTK](https://www.microsoft.com/en-us/cognitive-toolkit/) is an open-source, commercial-grade toolkit for deep learning developed by Microsoft.
|
||||
|
||||
In the future, we are likely to add more backend options. Go ask Microsoft about how their CNTK backend project is doing.
|
||||
In the future, we are likely to add more backend options.
|
||||
|
||||
----
|
||||
|
||||
@@ -34,7 +35,7 @@ The default configuration file looks like this:
|
||||
}
|
||||
```
|
||||
|
||||
Simply change the field `backend` to either `"theano"` or `"tensorflow"`, and Keras will use the new configuration next time you run any Keras code.
|
||||
Simply change the field `backend` to `"theano"`, `"tensorflow"`, or `"cntk"`, and Keras will use the new configuration next time you run any Keras code.
|
||||
|
||||
You can also define the environment variable ``KERAS_BACKEND`` and this will
|
||||
override what is defined in your config file :
|
||||
@@ -65,7 +66,7 @@ You can change these settings by editing `$HOME/.keras/keras.json`.
|
||||
- For 3D data, `"channels_last"` assumes `(conv_dim1, conv_dim2, conv_dim3, channels)` while `"channels_first"` assumes `(channels, conv_dim1, conv_dim2, conv_dim3)`.
|
||||
* `epsilon`: float, a numeric fuzzing constant used to avoid dividing by zero in some operations.
|
||||
* `floatx`: string, `"float16"`, `"float32"`, or `"float64"`. Default float precision.
|
||||
* `backend`: string, `"tensorflow"` or `"theano"`.
|
||||
* `backend`: string, `"tensorflow"`, `"theano"`, or `"cntk"`.
|
||||
|
||||
----
|
||||
|
||||
|
||||
externo
+4
-3
@@ -17,6 +17,7 @@ model.add(Dense(64, kernel_constraint=max_norm(2.)))
|
||||
|
||||
## Available constraints
|
||||
|
||||
- __max_norm__(max_value=2, axis=0): maximum-norm constraint
|
||||
- __non_neg__(): non-negativity constraint
|
||||
- __unit_norm__(): unit-norm constraint, enforces the matrix to have unit norm along the last axis
|
||||
- __max_norm(max_value=2, axis=0)__: maximum-norm constraint
|
||||
- __non_neg()__: non-negativity constraint
|
||||
- __unit_norm(axis=0)__: unit-norm constraint
|
||||
- __min_max_norm(min_value=0.0, max_value=1.0, rate=1.0, axis=0)__: minimum/maximum-norm constraint
|
||||
|
||||
+3
-3
@@ -16,7 +16,7 @@
|
||||
- [How can I remove a layer from a Sequential model?](#how-can-i-remove-a-layer-from-a-sequential-model)
|
||||
- [How can I use pre-trained models in Keras?](#how-can-i-use-pre-trained-models-in-keras)
|
||||
- [How can I use HDF5 inputs with Keras?](#how-can-i-use-hdf5-inputs-with-keras)
|
||||
- [Where is the Keras configuration filed stored?](#where-is-the-keras-configuration-filed-stored)
|
||||
- [Where is the Keras configuration file stored?](#where-is-the-keras-configuration-file-stored)
|
||||
|
||||
---
|
||||
|
||||
@@ -38,7 +38,7 @@ Please cite Keras in your publications if it helps your research. Here is an exa
|
||||
|
||||
### How can I run Keras on GPU?
|
||||
|
||||
If you are running on the TensorFlow backend, your code will automatically run on GPU if any available GPU is detected.
|
||||
If you are running on the TensorFlow or CNTK backends, your code will automatically run on GPU if any available GPU is detected.
|
||||
|
||||
If you are running on the Theano backend, you can use one of the following methods:
|
||||
|
||||
@@ -424,7 +424,7 @@ with h5py.File('input/file.hdf5', 'r') as f:
|
||||
|
||||
---
|
||||
|
||||
### Where is the Keras configuration filed stored?
|
||||
### Where is the Keras configuration file stored?
|
||||
|
||||
The default directory where all Keras data is stored is:
|
||||
|
||||
|
||||
@@ -361,7 +361,7 @@ from keras.models import Model, Sequential
|
||||
# First, let's define a vision model using a Sequential model.
|
||||
# This model will encode an image into a vector.
|
||||
vision_model = Sequential()
|
||||
vision_model.add(Conv2D(64, (3, 3) activation='relu', padding='same', input_shape=(3, 224, 224)))
|
||||
vision_model.add(Conv2D(64, (3, 3), activation='relu', padding='same', input_shape=(3, 224, 224)))
|
||||
vision_model.add(Conv2D(64, (3, 3), activation='relu'))
|
||||
vision_model.add(MaxPooling2D((2, 2)))
|
||||
vision_model.add(Conv2D(128, (3, 3), activation='relu', padding='same'))
|
||||
|
||||
externo
+1
-1
@@ -1,3 +1,3 @@
|
||||
# Keras: Deep Learning library for Theano and TensorFlow
|
||||
# Keras: The Python Deep Learning library
|
||||
|
||||
{{autogenerated}}
|
||||
@@ -21,7 +21,8 @@ class MyLayer(Layer):
|
||||
|
||||
def build(self, input_shape):
|
||||
# Create a trainable weight variable for this layer.
|
||||
self.kernel = self.add_weight(shape=(input_shape[1], self.output_dim),
|
||||
self.kernel = self.add_weight(name='kernel',
|
||||
shape=(input_shape[1], self.output_dim),
|
||||
initializer='uniform',
|
||||
trainable=True)
|
||||
super(MyLayer, self).build(input_shape) # Be sure to call this somewhere!
|
||||
|
||||
+56
-9
@@ -2,8 +2,10 @@
|
||||
## text_to_word_sequence
|
||||
|
||||
```python
|
||||
keras.preprocessing.text.text_to_word_sequence(text,
|
||||
filters='!"#$%&()*+,-./:;<=>?@[\\]^_`{|}~\t\n', lower=True, split=" ")
|
||||
keras.preprocessing.text.text_to_word_sequence(text,
|
||||
filters='!"#$%&()*+,-./:;<=>?@[\\]^_`{|}~\t\n',
|
||||
lower=True,
|
||||
split=" ")
|
||||
```
|
||||
|
||||
Split a sentence into a list of words.
|
||||
@@ -12,29 +14,74 @@ Split a sentence into a list of words.
|
||||
|
||||
- __Arguments__:
|
||||
- __text__: str.
|
||||
- __filters__: list (or concatenation) of characters to filter out, such as punctuation. Default: '!"#$%&()*+,-./:;<=>?@[\\]^_`{|}~\t\n' , includes basic punctuation, tabs, and newlines.
|
||||
- __filters__: list (or concatenation) of characters to filter out, such as
|
||||
punctuation. Default: '!"#$%&()*+,-./:;<=>?@[\\]^_`{|}~\t\n' , includes
|
||||
basic punctuation, tabs, and newlines.
|
||||
- __lower__: boolean. Whether to set the text to lowercase.
|
||||
- __split__: str. Separator for word splitting.
|
||||
|
||||
## one_hot
|
||||
|
||||
```python
|
||||
keras.preprocessing.text.one_hot(text, n,
|
||||
filters='!"#$%&()*+,-./:;<=>?@[\\]^_`{|}~\t\n', lower=True, split=" ")
|
||||
keras.preprocessing.text.one_hot(text,
|
||||
n,
|
||||
filters='!"#$%&()*+,-./:;<=>?@[\\]^_`{|}~\t\n',
|
||||
lower=True,
|
||||
split=" ")
|
||||
```
|
||||
|
||||
One-hot encode a text into a list of word indexes in a vocabulary of size n.
|
||||
One-hot encodes a text into a list of word indexes in a vocabulary of size n.
|
||||
|
||||
This is a wrapper to the `hashing_trick` function using `hash` as the hashing function.
|
||||
|
||||
- __Return__: List of integers in [1, n]. Each integer encodes a word (unicity non-guaranteed).
|
||||
|
||||
- __Arguments__: Same as `text_to_word_sequence` above.
|
||||
- __Arguments__:
|
||||
- __text__: str.
|
||||
- __n__: int. Size of vocabulary.
|
||||
- __filters__: list (or concatenation) of characters to filter out, such as
|
||||
punctuation. Default: '!"#$%&()*+,-./:;<=>?@[\\]^_`{|}~\t\n' , includes
|
||||
basic punctuation, tabs, and newlines.
|
||||
- __lower__: boolean. Whether to set the text to lowercase.
|
||||
- __split__: str. Separator for word splitting.
|
||||
|
||||
## hashing_trick
|
||||
|
||||
```python
|
||||
keras.preprocessing.text.hashing_trick(text,
|
||||
n,
|
||||
hash_function=None,
|
||||
filters='!"#$%&()*+,-./:;<=>?@[\\]^_`{|}~\t\n',
|
||||
lower=True,
|
||||
split=' ')
|
||||
```
|
||||
|
||||
Converts a text to a sequence of indices in a fixed-size hashing space
|
||||
|
||||
- __Return__:
|
||||
A list of integer word indices (unicity non-guaranteed).
|
||||
- __Arguments__:
|
||||
- __text__: str.
|
||||
- __n__: Dimension of the hashing space.
|
||||
- __hash_function__: defaults to python `hash` function, can be 'md5' or
|
||||
any function that takes in input a string and returns a int.
|
||||
Note that 'hash' is not a stable hashing function, so
|
||||
it is not consistent across different runs, while 'md5'
|
||||
is a stable hashing function.
|
||||
- __filters__: list (or concatenation) of characters to filter out, such as
|
||||
punctuation. Default: '!"#$%&()*+,-./:;<=>?@[\\]^_`{|}~\t\n' , includes
|
||||
basic punctuation, tabs, and newlines.
|
||||
- __lower__: boolean. Whether to set the text to lowercase.
|
||||
- __split__: str. Separator for word splitting.
|
||||
|
||||
## Tokenizer
|
||||
|
||||
```python
|
||||
keras.preprocessing.text.Tokenizer(num_words=None, filters='!"#$%&()*+,-./:;<=>?@[\\]^_`{|}~\t\n',
|
||||
lower=True, split=" ", char_level=False)
|
||||
keras.preprocessing.text.Tokenizer(num_words=None,
|
||||
filters='!"#$%&()*+,-./:;<=>?@[\\]^_`{|}~\t\n',
|
||||
lower=True,
|
||||
split=" ",
|
||||
char_level=False)
|
||||
```
|
||||
|
||||
Class for vectorizing texts, or/and turning texts into sequences (=list of word indexes, where the word of rank i in the dataset (starting at 1) has index i).
|
||||
|
||||
@@ -136,8 +136,8 @@ def shuffle_mats_or_lists(matrix_list, stop_ind=None):
|
||||
elif isinstance(mat, list):
|
||||
ret.append([mat[i] for i in a])
|
||||
else:
|
||||
raise TypeError('shuffle_mats_or_lists only supports '
|
||||
'numpy.array and list objects')
|
||||
raise TypeError('`shuffle_mats_or_lists` only supports '
|
||||
'numpy.array and list objects.')
|
||||
return ret
|
||||
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ print('Loading data...')
|
||||
print(len(x_train), 'train sequences')
|
||||
print(len(x_test), 'test sequences')
|
||||
|
||||
print("Pad sequences (samples x time)")
|
||||
print('Pad sequences (samples x time)')
|
||||
x_train = sequence.pad_sequences(x_train, maxlen=maxlen)
|
||||
x_test = sequence.pad_sequences(x_test, maxlen=maxlen)
|
||||
print('x_train shape:', x_train.shape)
|
||||
|
||||
@@ -37,7 +37,7 @@ print('Loading data...')
|
||||
X_train = sequence.pad_sequences(X_train, max_length)
|
||||
X_test = sequence.pad_sequences(X_test, max_length)
|
||||
|
||||
# Compile and train different models while meauring performance.
|
||||
# Compile and train different models while measuring performance.
|
||||
results = []
|
||||
for mode in modes:
|
||||
print('Testing mode: implementation={}'.format(mode))
|
||||
|
||||
@@ -57,7 +57,7 @@ from scipy.optimize import fmin_l_bfgs_b
|
||||
import time
|
||||
import argparse
|
||||
|
||||
from keras.applications import vgg16
|
||||
from keras.applications import vgg19
|
||||
from keras import backend as K
|
||||
|
||||
parser = argparse.ArgumentParser(description='Neural style transfer with Keras.')
|
||||
@@ -99,7 +99,7 @@ def preprocess_image(image_path):
|
||||
img = load_img(image_path, target_size=(img_nrows, img_ncols))
|
||||
img = img_to_array(img)
|
||||
img = np.expand_dims(img, axis=0)
|
||||
img = vgg16.preprocess_input(img)
|
||||
img = vgg19.preprocess_input(img)
|
||||
return img
|
||||
|
||||
# util function to convert a tensor into a valid image
|
||||
@@ -137,7 +137,7 @@ input_tensor = K.concatenate([base_image,
|
||||
|
||||
# build the VGG16 network with our 3 images as input
|
||||
# the model will be loaded with pre-trained ImageNet weights
|
||||
model = vgg16.VGG16(input_tensor=input_tensor,
|
||||
model = vgg19.VGG19(input_tensor=input_tensor,
|
||||
weights='imagenet', include_top=False)
|
||||
print('Model loaded.')
|
||||
|
||||
@@ -199,7 +199,7 @@ def total_variation_loss(x):
|
||||
|
||||
# combine these loss functions into a single scalar
|
||||
loss = K.variable(0.)
|
||||
layer_features = outputs_dict['block4_conv2']
|
||||
layer_features = outputs_dict['block5_conv2']
|
||||
base_image_features = layer_features[0, :, :, :]
|
||||
combination_features = layer_features[2, :, :, :]
|
||||
loss += content_weight * content_loss(base_image_features,
|
||||
@@ -273,10 +273,7 @@ evaluator = Evaluator()
|
||||
|
||||
# run scipy-based optimization (L-BFGS) over the pixels of the generated image
|
||||
# so as to minimize the neural style loss
|
||||
if K.image_data_format() == 'channels_first':
|
||||
x = np.random.uniform(0, 255, (1, 3, img_nrows, img_ncols)) - 128.
|
||||
else:
|
||||
x = np.random.uniform(0, 255, (1, img_nrows, img_ncols, 3)) - 128.
|
||||
x = preprocess_image(base_image_path)
|
||||
|
||||
for i in range(iterations):
|
||||
print('Start of iteration', i)
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
'''Compares self-normalizing MLPs with regular MLPs.
|
||||
|
||||
Compares the performance of a simple MLP using two
|
||||
different activation functions: RELU and SELU
|
||||
on the Reuters newswire topic classification task.
|
||||
|
||||
# Reference:
|
||||
Klambauer, G., Unterthiner, T., Mayr, A., & Hochreiter, S. (2017).
|
||||
Self-Normalizing Neural Networks. arXiv preprint arXiv:1706.02515.
|
||||
https://arxiv.org/abs/1706.02515
|
||||
'''
|
||||
from __future__ import print_function
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
import keras
|
||||
from keras.datasets import reuters
|
||||
from keras.models import Sequential
|
||||
from keras.layers import Dense, Activation, Dropout
|
||||
from keras.layers.noise import AlphaDropout
|
||||
from keras.preprocessing.text import Tokenizer
|
||||
|
||||
max_words = 1000
|
||||
batch_size = 16
|
||||
epochs = 40
|
||||
plot = True
|
||||
|
||||
|
||||
def create_network(n_dense=6,
|
||||
dense_units=16,
|
||||
activation='selu',
|
||||
dropout=AlphaDropout,
|
||||
dropout_rate=0.1,
|
||||
kernel_initializer='lecun_normal',
|
||||
optimizer='adam',
|
||||
num_classes=1,
|
||||
max_words=max_words):
|
||||
"""Generic function to create a fully-connected neural network.
|
||||
|
||||
# Arguments
|
||||
n_dense: int > 0. Number of dense layers.
|
||||
dense_units: int > 0. Number of dense units per layer.
|
||||
dropout: keras.layers.Layer. A dropout layer to apply.
|
||||
dropout_rate: 0 <= float <= 1. The rate of dropout.
|
||||
kernel_initializer: str. The initializer for the weights.
|
||||
optimizer: str/keras.optimizers.Optimizer. The optimizer to use.
|
||||
num_classes: int > 0. The number of classes to predict.
|
||||
max_words: int > 0. The maximum number of words per data point.
|
||||
|
||||
# Returns
|
||||
A Keras model instance (compiled).
|
||||
"""
|
||||
model = Sequential()
|
||||
model.add(Dense(dense_units, input_shape=(max_words,),
|
||||
kernel_initializer=kernel_initializer))
|
||||
model.add(Activation(activation))
|
||||
model.add(dropout(dropout_rate))
|
||||
|
||||
for i in range(n_dense - 1):
|
||||
model.add(Dense(dense_units, kernel_initializer=kernel_initializer))
|
||||
model.add(Activation(activation))
|
||||
model.add(dropout(dropout_rate))
|
||||
|
||||
model.add(Dense(num_classes))
|
||||
model.add(Activation('softmax'))
|
||||
model.compile(loss='categorical_crossentropy',
|
||||
optimizer=optimizer,
|
||||
metrics=['accuracy'])
|
||||
return model
|
||||
|
||||
|
||||
network1 = {
|
||||
'n_dense': 6,
|
||||
'dense_units': 16,
|
||||
'activation': 'relu',
|
||||
'dropout': Dropout,
|
||||
'dropout_rate': 0.5,
|
||||
'kernel_initializer': 'glorot_uniform',
|
||||
'optimizer': 'sgd'
|
||||
}
|
||||
|
||||
network2 = {
|
||||
'n_dense': 6,
|
||||
'dense_units': 16,
|
||||
'activation': 'selu',
|
||||
'dropout': AlphaDropout,
|
||||
'dropout_rate': 0.1,
|
||||
'kernel_initializer': 'lecun_normal',
|
||||
'optimizer': 'sgd'
|
||||
}
|
||||
|
||||
print('Loading data...')
|
||||
(x_train, y_train), (x_test, y_test) = reuters.load_data(num_words=max_words,
|
||||
test_split=0.2)
|
||||
print(len(x_train), 'train sequences')
|
||||
print(len(x_test), 'test sequences')
|
||||
|
||||
num_classes = np.max(y_train) + 1
|
||||
print(num_classes, 'classes')
|
||||
|
||||
print('Vectorizing sequence data...')
|
||||
tokenizer = Tokenizer(num_words=max_words)
|
||||
x_train = tokenizer.sequences_to_matrix(x_train, mode='binary')
|
||||
x_test = tokenizer.sequences_to_matrix(x_test, mode='binary')
|
||||
print('x_train shape:', x_train.shape)
|
||||
print('x_test shape:', x_test.shape)
|
||||
|
||||
print('Convert class vector to binary class matrix '
|
||||
'(for use with categorical_crossentropy)')
|
||||
y_train = keras.utils.to_categorical(y_train, num_classes)
|
||||
y_test = keras.utils.to_categorical(y_test, num_classes)
|
||||
print('y_train shape:', y_train.shape)
|
||||
print('y_test shape:', y_test.shape)
|
||||
|
||||
print('\nBuilding network 1...')
|
||||
|
||||
model1 = create_network(num_classes=num_classes, **network1)
|
||||
history_model1 = model1.fit(x_train,
|
||||
y_train,
|
||||
batch_size=batch_size,
|
||||
epochs=epochs,
|
||||
verbose=1,
|
||||
validation_split=0.1)
|
||||
|
||||
score_model1 = model1.evaluate(x_test,
|
||||
y_test,
|
||||
batch_size=batch_size,
|
||||
verbose=1)
|
||||
|
||||
|
||||
print('\nBuilding network 2...')
|
||||
model2 = create_network(num_classes=num_classes, **network2)
|
||||
|
||||
history_model2 = model2.fit(x_train,
|
||||
y_train,
|
||||
batch_size=batch_size,
|
||||
epochs=epochs,
|
||||
verbose=1,
|
||||
validation_split=0.1)
|
||||
|
||||
score_model2 = model2.evaluate(x_test,
|
||||
y_test,
|
||||
batch_size=batch_size,
|
||||
verbose=1)
|
||||
|
||||
print('\nNetwork 1 results')
|
||||
print('Hyperparameters:', network1)
|
||||
print('Test score:', score_model1[0])
|
||||
print('Test accuracy:', score_model1[1])
|
||||
print('Network 2 results')
|
||||
print('Hyperparameters:', network2)
|
||||
print('Test score:', score_model2[0])
|
||||
print('Test accuracy:', score_model2[1])
|
||||
|
||||
plt.plot(range(epochs),
|
||||
history_model1.history['val_loss'],
|
||||
'g-',
|
||||
label='Network 1 Val Loss')
|
||||
plt.plot(range(epochs),
|
||||
history_model2.history['val_loss'],
|
||||
'r-',
|
||||
label='Network 2 Val Loss')
|
||||
plt.plot(range(epochs),
|
||||
history_model1.history['loss'],
|
||||
'g--',
|
||||
label='Network 1 Loss')
|
||||
plt.plot(range(epochs),
|
||||
history_model2.history['loss'],
|
||||
'r--',
|
||||
label='Network 2 Loss')
|
||||
plt.xlabel('Epochs')
|
||||
plt.ylabel('Loss')
|
||||
plt.legend()
|
||||
plt.savefig('comparison_of_networks.png')
|
||||
@@ -79,7 +79,8 @@ decoder_deconv_1 = Conv2DTranspose(filters,
|
||||
padding='same',
|
||||
strides=1,
|
||||
activation='relu')
|
||||
decoder_deconv_2 = Conv2DTranspose(filters, num_conv,
|
||||
decoder_deconv_2 = Conv2DTranspose(filters,
|
||||
kernel_size=num_conv,
|
||||
padding='same',
|
||||
strides=1,
|
||||
activation='relu')
|
||||
|
||||
+2
-2
@@ -1,5 +1,6 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from . import utils
|
||||
from . import activations
|
||||
from . import applications
|
||||
from . import backend
|
||||
@@ -7,7 +8,6 @@ from . import datasets
|
||||
from . import engine
|
||||
from . import layers
|
||||
from . import preprocessing
|
||||
from . import utils
|
||||
from . import wrappers
|
||||
from . import callbacks
|
||||
from . import constraints
|
||||
@@ -20,4 +20,4 @@ from . import regularizers
|
||||
# Importable from root because it's technically not a layer
|
||||
from .layers import Input
|
||||
|
||||
__version__ = '2.0.4'
|
||||
__version__ = '2.0.5'
|
||||
|
||||
@@ -34,6 +34,20 @@ def elu(x, alpha=1.0):
|
||||
return K.elu(x, alpha)
|
||||
|
||||
|
||||
def selu(x):
|
||||
"""Scaled Exponential Linear Unit. (Klambauer et al., 2017)
|
||||
|
||||
# Arguments
|
||||
x: A tensor or variable to compute the activation function for.
|
||||
|
||||
# References
|
||||
- [Self-Normalizing Neural Networks](https://arxiv.org/abs/1706.02515)
|
||||
"""
|
||||
alpha = 1.6732632423543772848170429916717
|
||||
scale = 1.0507009873554804934193349852946
|
||||
return scale * K.elu(x, alpha)
|
||||
|
||||
|
||||
def softplus(x):
|
||||
return K.softplus(x)
|
||||
|
||||
|
||||
@@ -3,3 +3,4 @@ from .vgg19 import VGG19
|
||||
from .resnet50 import ResNet50
|
||||
from .inception_v3 import InceptionV3
|
||||
from .xception import Xception
|
||||
from .mobilenet import MobileNet
|
||||
|
||||
@@ -27,7 +27,6 @@ from ..layers import AveragePooling2D
|
||||
from ..layers import GlobalAveragePooling2D
|
||||
from ..layers import GlobalMaxPooling2D
|
||||
from ..engine.topology import get_source_inputs
|
||||
from ..utils.layer_utils import convert_all_kernels_in_model
|
||||
from ..utils.data_utils import get_file
|
||||
from .. import backend as K
|
||||
from .imagenet_utils import decode_predictions
|
||||
|
||||
@@ -0,0 +1,641 @@
|
||||
"""MobileNet v1 models for Keras.
|
||||
|
||||
MobileNet is a general architecture and can be used for multiple use cases.
|
||||
Depending on the use case, it can use different input layer size and
|
||||
different width factors. This allows different width models to reduce
|
||||
the number of multiply-adds and thereby
|
||||
reduce inference cost on mobile devices.
|
||||
|
||||
MobileNets support any input size greater than 32 x 32, with larger image sizes
|
||||
offering better performance.
|
||||
The number of parameters and number of multiply-adds
|
||||
can be modified by using the `alpha` parameter,
|
||||
which increases/decreases the number of filters in each layer.
|
||||
By altering the image size and `alpha` parameter,
|
||||
all 16 models from the paper can be built, with ImageNet weights provided.
|
||||
|
||||
The paper demonstrates the performance of MobileNets using `alpha` values of
|
||||
1.0 (also called 100 % MobileNet), 0.75, 0.5 and 0.25.
|
||||
For each of these `alpha` values, weights for 4 different input image sizes
|
||||
are provided (224, 192, 160, 128).
|
||||
|
||||
The following table describes the size and accuracy of the 100% MobileNet
|
||||
on size 224 x 224:
|
||||
----------------------------------------------------------------------------
|
||||
Width Multiplier (alpha) | ImageNet Acc | Multiply-Adds (M) | Params (M)
|
||||
----------------------------------------------------------------------------
|
||||
| 1.0 MobileNet-224 | 70.6 % | 529 | 4.2 |
|
||||
| 0.75 MobileNet-224 | 68.4 % | 325 | 2.6 |
|
||||
| 0.50 MobileNet-224 | 63.7 % | 149 | 1.3 |
|
||||
| 0.25 MobileNet-224 | 50.6 % | 41 | 0.5 |
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
The following table describes the performance of
|
||||
the 100 % MobileNet on various input sizes:
|
||||
------------------------------------------------------------------------
|
||||
Resolution | ImageNet Acc | Multiply-Adds (M) | Params (M)
|
||||
------------------------------------------------------------------------
|
||||
| 1.0 MobileNet-224 | 70.6 % | 529 | 4.2 |
|
||||
| 1.0 MobileNet-192 | 69.1 % | 529 | 4.2 |
|
||||
| 1.0 MobileNet-160 | 67.2 % | 529 | 4.2 |
|
||||
| 1.0 MobileNet-128 | 64.4 % | 529 | 4.2 |
|
||||
------------------------------------------------------------------------
|
||||
|
||||
The weights for all 16 models are obtained and translated
|
||||
from Tensorflow checkpoints found at
|
||||
https://github.com/tensorflow/models/blob/master/slim/nets/mobilenet_v1.md
|
||||
|
||||
# Reference
|
||||
- [MobileNets: Efficient Convolutional Neural Networks for
|
||||
Mobile Vision Applications](https://arxiv.org/pdf/1704.04861.pdf))
|
||||
"""
|
||||
from __future__ import print_function
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
import warnings
|
||||
|
||||
from ..models import Model
|
||||
from ..layers import Input
|
||||
from ..layers import Activation
|
||||
from ..layers import Dropout
|
||||
from ..layers import Reshape
|
||||
from ..layers import BatchNormalization
|
||||
from ..layers import GlobalAveragePooling2D
|
||||
from ..layers import GlobalMaxPooling2D
|
||||
from ..layers import Conv2D
|
||||
from .. import initializers
|
||||
from .. import regularizers
|
||||
from .. import constraints
|
||||
from ..utils import conv_utils
|
||||
from ..utils.data_utils import get_file
|
||||
from ..engine.topology import get_source_inputs
|
||||
from ..engine import InputSpec
|
||||
from ..applications.imagenet_utils import _obtain_input_shape
|
||||
from ..applications.imagenet_utils import decode_predictions
|
||||
from .. import backend as K
|
||||
|
||||
|
||||
BASE_WEIGHT_PATH = 'https://github.com/fchollet/deep-learning-models/releases/download/v0.6/'
|
||||
|
||||
|
||||
def relu6(x):
|
||||
return K.relu(x, max_value=6)
|
||||
|
||||
|
||||
def preprocess_input(x):
|
||||
x /= 255.
|
||||
x -= 0.5
|
||||
x *= 2.
|
||||
return x
|
||||
|
||||
|
||||
class DepthwiseConv2D(Conv2D):
|
||||
"""Depthwise separable 2D convolution.
|
||||
|
||||
Depthwise Separable convolutions consists in performing
|
||||
just the first step in a depthwise spatial convolution
|
||||
(which acts on each input channel separately).
|
||||
The `depth_multiplier` argument controls how many
|
||||
output channels are generated per input channel in the depthwise step.
|
||||
|
||||
# Arguments
|
||||
kernel_size: An integer or tuple/list of 2 integers, specifying the
|
||||
width and height of the 2D convolution window.
|
||||
Can be a single integer to specify the same value for
|
||||
all spatial dimensions.
|
||||
strides: An integer or tuple/list of 2 integers,
|
||||
specifying the strides of the convolution along the width and height.
|
||||
Can be a single integer to specify the same value for
|
||||
all spatial dimensions.
|
||||
Specifying any stride value != 1 is incompatible with specifying
|
||||
any `dilation_rate` value != 1.
|
||||
padding: one of `"valid"` or `"same"` (case-insensitive).
|
||||
depth_multiplier: The number of depthwise convolution output channels
|
||||
for each input channel.
|
||||
The total number of depthwise convolution output
|
||||
channels will be equal to `filters_in * depth_multiplier`.
|
||||
data_format: A string,
|
||||
one of `channels_last` (default) or `channels_first`.
|
||||
The ordering of the dimensions in the inputs.
|
||||
`channels_last` corresponds to inputs with shape
|
||||
`(batch, height, width, channels)` while `channels_first`
|
||||
corresponds to inputs with shape
|
||||
`(batch, channels, height, width)`.
|
||||
It defaults to the `image_data_format` value found in your
|
||||
Keras config file at `~/.keras/keras.json`.
|
||||
If you never set it, then it will be "channels_last".
|
||||
activation: Activation function to use
|
||||
(see [activations](../activations.md)).
|
||||
If you don't specify anything, no activation is applied
|
||||
(ie. "linear" activation: `a(x) = x`).
|
||||
use_bias: Boolean, whether the layer uses a bias vector.
|
||||
depthwise_initializer: Initializer for the depthwise kernel matrix
|
||||
(see [initializers](../initializers.md)).
|
||||
bias_initializer: Initializer for the bias vector
|
||||
(see [initializers](../initializers.md)).
|
||||
depthwise_regularizer: Regularizer function applied to
|
||||
the depthwise kernel matrix
|
||||
(see [regularizer](../regularizers.md)).
|
||||
bias_regularizer: Regularizer function applied to the bias vector
|
||||
(see [regularizer](../regularizers.md)).
|
||||
activity_regularizer: Regularizer function applied to
|
||||
the output of the layer (its "activation").
|
||||
(see [regularizer](../regularizers.md)).
|
||||
depthwise_constraint: Constraint function applied to
|
||||
the depthwise kernel matrix
|
||||
(see [constraints](../constraints.md)).
|
||||
bias_constraint: Constraint function applied to the bias vector
|
||||
(see [constraints](../constraints.md)).
|
||||
|
||||
# Input shape
|
||||
4D tensor with shape:
|
||||
`[batch, channels, rows, cols]` if data_format='channels_first'
|
||||
or 4D tensor with shape:
|
||||
`[batch, rows, cols, channels]` if data_format='channels_last'.
|
||||
|
||||
# Output shape
|
||||
4D tensor with shape:
|
||||
`[batch, filters, new_rows, new_cols]` if data_format='channels_first'
|
||||
or 4D tensor with shape:
|
||||
`[batch, new_rows, new_cols, filters]` if data_format='channels_last'.
|
||||
`rows` and `cols` values might have changed due to padding.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
kernel_size,
|
||||
strides=(1, 1),
|
||||
padding='valid',
|
||||
depth_multiplier=1,
|
||||
data_format=None,
|
||||
activation=None,
|
||||
use_bias=True,
|
||||
depthwise_initializer='glorot_uniform',
|
||||
bias_initializer='zeros',
|
||||
depthwise_regularizer=None,
|
||||
bias_regularizer=None,
|
||||
activity_regularizer=None,
|
||||
depthwise_constraint=None,
|
||||
bias_constraint=None,
|
||||
**kwargs):
|
||||
super(DepthwiseConv2D, self).__init__(
|
||||
filters=None,
|
||||
kernel_size=kernel_size,
|
||||
strides=strides,
|
||||
padding=padding,
|
||||
data_format=data_format,
|
||||
activation=activation,
|
||||
use_bias=use_bias,
|
||||
bias_regularizer=bias_regularizer,
|
||||
activity_regularizer=activity_regularizer,
|
||||
bias_constraint=bias_constraint,
|
||||
**kwargs)
|
||||
self.depth_multiplier = depth_multiplier
|
||||
self.depthwise_initializer = initializers.get(depthwise_initializer)
|
||||
self.depthwise_regularizer = regularizers.get(depthwise_regularizer)
|
||||
self.depthwise_constraint = constraints.get(depthwise_constraint)
|
||||
self.bias_initializer = initializers.get(bias_initializer)
|
||||
|
||||
def build(self, input_shape):
|
||||
if len(input_shape) < 4:
|
||||
raise ValueError('Inputs to `DepthwiseConv2D` should have rank 4. '
|
||||
'Received input shape:', str(input_shape))
|
||||
if self.data_format == 'channels_first':
|
||||
channel_axis = 1
|
||||
else:
|
||||
channel_axis = 3
|
||||
if input_shape[channel_axis] is None:
|
||||
raise ValueError('The channel dimension of the inputs to '
|
||||
'`DepthwiseConv2D` '
|
||||
'should be defined. Found `None`.')
|
||||
input_dim = int(input_shape[channel_axis])
|
||||
depthwise_kernel_shape = (self.kernel_size[0],
|
||||
self.kernel_size[1],
|
||||
input_dim,
|
||||
self.depth_multiplier)
|
||||
|
||||
self.depthwise_kernel = self.add_weight(
|
||||
shape=depthwise_kernel_shape,
|
||||
initializer=self.depthwise_initializer,
|
||||
name='depthwise_kernel',
|
||||
regularizer=self.depthwise_regularizer,
|
||||
constraint=self.depthwise_constraint)
|
||||
|
||||
if self.use_bias:
|
||||
self.bias = self.add_weight(shape=(input_dim * self.depth_multiplier,),
|
||||
initializer=self.bias_initializer,
|
||||
name='bias',
|
||||
regularizer=self.bias_regularizer,
|
||||
constraint=self.bias_constraint)
|
||||
else:
|
||||
self.bias = None
|
||||
# Set input spec.
|
||||
self.input_spec = InputSpec(ndim=4, axes={channel_axis: input_dim})
|
||||
self.built = True
|
||||
|
||||
def call(self, inputs, training=None):
|
||||
outputs = K.depthwise_conv2d(
|
||||
inputs,
|
||||
self.depthwise_kernel,
|
||||
strides=self.strides,
|
||||
padding=self.padding,
|
||||
dilation_rate=self.dilation_rate,
|
||||
data_format=self.data_format)
|
||||
|
||||
if self.bias:
|
||||
outputs = K.bias_add(
|
||||
outputs,
|
||||
self.bias,
|
||||
data_format=self.data_format)
|
||||
|
||||
if self.activation is not None:
|
||||
return self.activation(outputs)
|
||||
|
||||
return outputs
|
||||
|
||||
def compute_output_shape(self, input_shape):
|
||||
if self.data_format == 'channels_first':
|
||||
rows = input_shape[2]
|
||||
cols = input_shape[3]
|
||||
out_filters = input_shape[1] * self.depth_multiplier
|
||||
elif self.data_format == 'channels_last':
|
||||
rows = input_shape[1]
|
||||
cols = input_shape[2]
|
||||
out_filters = input_shape[3] * self.depth_multiplier
|
||||
|
||||
rows = conv_utils.conv_output_length(rows, self.kernel_size[0],
|
||||
self.padding,
|
||||
self.strides[0])
|
||||
cols = conv_utils.conv_output_length(cols, self.kernel_size[1],
|
||||
self.padding,
|
||||
self.strides[1])
|
||||
|
||||
if self.data_format == 'channels_first':
|
||||
return (input_shape[0], out_filters, rows, cols)
|
||||
elif self.data_format == 'channels_last':
|
||||
return (input_shape[0], rows, cols, out_filters)
|
||||
|
||||
def get_config(self):
|
||||
config = super(DepthwiseConv2D, self).get_config()
|
||||
config.pop('filters')
|
||||
config.pop('kernel_initializer')
|
||||
config.pop('kernel_regularizer')
|
||||
config.pop('kernel_constraint')
|
||||
config['depth_multiplier'] = self.depth_multiplier
|
||||
config['depthwise_initializer'] = initializers.serialize(self.depthwise_initializer)
|
||||
config['depthwise_regularizer'] = regularizers.serialize(self.depthwise_regularizer)
|
||||
config['depthwise_constraint'] = constraints.serialize(self.depthwise_constraint)
|
||||
return config
|
||||
|
||||
|
||||
def MobileNet(input_shape=None,
|
||||
alpha=1.0,
|
||||
depth_multiplier=1,
|
||||
dropout=1e-3,
|
||||
include_top=True,
|
||||
weights='imagenet',
|
||||
input_tensor=None,
|
||||
pooling=None,
|
||||
classes=1000):
|
||||
"""Instantiates the MobileNet architecture.
|
||||
|
||||
Note that only TensorFlow is supported for now,
|
||||
therefore it only works with the data format
|
||||
`image_data_format='channels_last'` in your Keras config
|
||||
at `~/.keras/keras.json`.
|
||||
|
||||
To load a MobileNet model via `load_model`, import the custom
|
||||
objects `relu6` and `DepthwiseConv2D` and pass them to the
|
||||
`custom_objects` parameter.
|
||||
E.g.
|
||||
model = load_model('mobilenet.h5', custom_objects={
|
||||
'relu6': mobilenet.relu6,
|
||||
'DepthwiseConv2D': mobilenet.DepthwiseConv2D})
|
||||
|
||||
# Arguments
|
||||
input_shape: optional shape tuple, only to be specified
|
||||
if `include_top` is False (otherwise the input shape
|
||||
has to be `(224, 224, 3)` (with `channels_last` data format)
|
||||
or (3, 224, 224) (with `channels_first` data format).
|
||||
It should have exactly 3 inputs channels,
|
||||
and width and height should be no smaller than 32.
|
||||
E.g. `(200, 200, 3)` would be one valid value.
|
||||
alpha: controls the width of the network.
|
||||
- If `alpha` < 1.0, proportionally decreases the number
|
||||
of filters in each layer.
|
||||
- If `alpha` > 1.0, proportionally increases the number
|
||||
of filters in each layer.
|
||||
- If `alpha` = 1, default number of filters from the paper
|
||||
are used at each layer.
|
||||
depth_multiplier: depth multiplier for depthwise convolution
|
||||
(also called the resolution multiplier)
|
||||
dropout: dropout rate
|
||||
include_top: whether to include the fully-connected
|
||||
layer at the top of the network.
|
||||
weights: `None` (random initialization) or
|
||||
`imagenet` (ImageNet weights)
|
||||
input_tensor: optional Keras tensor (i.e. output of
|
||||
`layers.Input()`)
|
||||
to use as image input for the model.
|
||||
pooling: Optional pooling mode for feature extraction
|
||||
when `include_top` is `False`.
|
||||
- `None` means that the output of the model
|
||||
will be the 4D tensor output of the
|
||||
last convolutional layer.
|
||||
- `avg` means that global average pooling
|
||||
will be applied to the output of the
|
||||
last convolutional layer, and thus
|
||||
the output of the model will be a
|
||||
2D tensor.
|
||||
- `max` means that global max pooling will
|
||||
be applied.
|
||||
classes: optional number of classes to classify images
|
||||
into, only to be specified if `include_top` is True, and
|
||||
if no `weights` argument is specified.
|
||||
|
||||
# Returns
|
||||
A Keras model instance.
|
||||
|
||||
# Raises
|
||||
ValueError: in case of invalid argument for `weights`,
|
||||
or invalid input shape.
|
||||
RuntimeError: If attempting to run this model with a
|
||||
backend that does not support separable convolutions.
|
||||
"""
|
||||
|
||||
if K.backend() != 'tensorflow':
|
||||
raise RuntimeError('Only Tensorflow backend is currently supported, '
|
||||
'as other backends do not support '
|
||||
'depthwise convolution.')
|
||||
|
||||
if weights not in {'imagenet', None}:
|
||||
raise ValueError('The `weights` argument should be either '
|
||||
'`None` (random initialization) or `imagenet` '
|
||||
'(pre-training on ImageNet).')
|
||||
|
||||
if weights == 'imagenet' and include_top and classes != 1000:
|
||||
raise ValueError('If using `weights` as ImageNet with `include_top` '
|
||||
'as true, `classes` should be 1000')
|
||||
|
||||
# Determine proper input shape.
|
||||
input_shape = _obtain_input_shape(input_shape,
|
||||
default_size=224,
|
||||
min_size=32,
|
||||
data_format=K.image_data_format(),
|
||||
include_top=include_top or weights)
|
||||
if K.image_data_format() == 'channels_last':
|
||||
row_axis, col_axis = (0, 1)
|
||||
else:
|
||||
row_axis, col_axis = (1, 2)
|
||||
rows = input_shape[row_axis]
|
||||
cols = input_shape[col_axis]
|
||||
|
||||
if weights == 'imagenet':
|
||||
if depth_multiplier != 1:
|
||||
raise ValueError('If imagenet weights are being loaded, '
|
||||
'depth multiplier must be 1')
|
||||
|
||||
if alpha not in [0.25, 0.50, 0.75, 1.0]:
|
||||
raise ValueError('If imagenet weights are being loaded, '
|
||||
'alpha can be one of'
|
||||
'`0.25`, `0.50`, `0.75` or `1.0` only.')
|
||||
|
||||
if rows != cols or rows not in [128, 160, 192, 224]:
|
||||
raise ValueError('If imagenet weights are being loaded, '
|
||||
'input must have a static square shape (one of '
|
||||
'(128,128), (160,160), (192,192), or (224, 224)).'
|
||||
' Input shape provided = %s' % (input_shape,))
|
||||
|
||||
if K.image_data_format() != 'channels_last':
|
||||
warnings.warn('The MobileNet family of models is only available '
|
||||
'for the input data format "channels_last" '
|
||||
'(width, height, channels). '
|
||||
'However your settings specify the default '
|
||||
'data format "channels_first" (channels, width, height).'
|
||||
' You should set `image_data_format="channels_last"` '
|
||||
'in your Keras config located at ~/.keras/keras.json. '
|
||||
'The model being returned right now will expect inputs '
|
||||
'to follow the "channels_last" data format.')
|
||||
K.set_image_data_format('channels_last')
|
||||
old_data_format = 'channels_first'
|
||||
else:
|
||||
old_data_format = None
|
||||
|
||||
if input_tensor is None:
|
||||
img_input = Input(shape=input_shape)
|
||||
else:
|
||||
if not K.is_keras_tensor(input_tensor):
|
||||
img_input = Input(tensor=input_tensor, shape=input_shape)
|
||||
else:
|
||||
img_input = input_tensor
|
||||
|
||||
x = _conv_block(img_input, 32, alpha, strides=(2, 2))
|
||||
x = _depthwise_conv_block(x, 64, alpha, depth_multiplier, block_id=1)
|
||||
|
||||
x = _depthwise_conv_block(x, 128, alpha, depth_multiplier,
|
||||
strides=(2, 2), block_id=2)
|
||||
x = _depthwise_conv_block(x, 128, alpha, depth_multiplier, block_id=3)
|
||||
|
||||
x = _depthwise_conv_block(x, 256, alpha, depth_multiplier,
|
||||
strides=(2, 2), block_id=4)
|
||||
x = _depthwise_conv_block(x, 256, alpha, depth_multiplier, block_id=5)
|
||||
|
||||
x = _depthwise_conv_block(x, 512, alpha, depth_multiplier,
|
||||
strides=(2, 2), block_id=6)
|
||||
x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id=7)
|
||||
x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id=8)
|
||||
x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id=9)
|
||||
x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id=10)
|
||||
x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id=11)
|
||||
|
||||
x = _depthwise_conv_block(x, 1024, alpha, depth_multiplier,
|
||||
strides=(2, 2), block_id=12)
|
||||
x = _depthwise_conv_block(x, 1024, alpha, depth_multiplier, block_id=13)
|
||||
|
||||
if include_top:
|
||||
if K.image_data_format() == 'channels_first':
|
||||
shape = (int(1024 * alpha), 1, 1)
|
||||
else:
|
||||
shape = (1, 1, int(1024 * alpha))
|
||||
|
||||
x = GlobalAveragePooling2D()(x)
|
||||
x = Reshape(shape, name='reshape_1')(x)
|
||||
x = Dropout(dropout, name='dropout')(x)
|
||||
x = Conv2D(classes, (1, 1),
|
||||
padding='same', name='conv_preds')(x)
|
||||
x = Activation('softmax', name='act_softmax')(x)
|
||||
x = Reshape((classes,), name='reshape_2')(x)
|
||||
else:
|
||||
if pooling == 'avg':
|
||||
x = GlobalAveragePooling2D()(x)
|
||||
elif pooling == 'max':
|
||||
x = GlobalMaxPooling2D()(x)
|
||||
|
||||
# Ensure that the model takes into account
|
||||
# any potential predecessors of `input_tensor`.
|
||||
if input_tensor is not None:
|
||||
inputs = get_source_inputs(input_tensor)
|
||||
else:
|
||||
inputs = img_input
|
||||
|
||||
# Create model.
|
||||
model = Model(inputs, x, name='mobilenet_%0.2f_%s' % (alpha, rows))
|
||||
|
||||
# load weights
|
||||
if weights == 'imagenet':
|
||||
if K.image_data_format() == 'channels_first':
|
||||
raise ValueError('Weights for "channels_last" format '
|
||||
'are not available.')
|
||||
if alpha == 1.0:
|
||||
alpha_text = '1_0'
|
||||
elif alpha == 0.75:
|
||||
alpha_text = '7_5'
|
||||
elif alpha == 0.50:
|
||||
alpha_text = '5_0'
|
||||
else:
|
||||
alpha_text = '2_5'
|
||||
|
||||
if include_top:
|
||||
model_name = 'mobilenet_%s_%d_tf.h5' % (alpha_text, rows)
|
||||
weigh_path = BASE_WEIGHT_PATH + model_name
|
||||
weights_path = get_file(model_name,
|
||||
weigh_path,
|
||||
cache_subdir='models')
|
||||
else:
|
||||
model_name = 'mobilenet_%s_%d_tf_no_top.h5' % (alpha_text, rows)
|
||||
weigh_path = BASE_WEIGHT_PATH + model_name
|
||||
weights_path = get_file(model_name,
|
||||
weigh_path,
|
||||
cache_subdir='models')
|
||||
model.load_weights(weights_path)
|
||||
|
||||
if old_data_format:
|
||||
K.set_image_data_format(old_data_format)
|
||||
return model
|
||||
|
||||
|
||||
def _conv_block(inputs, filters, alpha, kernel=(3, 3), strides=(1, 1)):
|
||||
"""Adds an initial convolution layer (with batch normalization and relu6).
|
||||
|
||||
# Arguments
|
||||
inputs: Input tensor of shape `(rows, cols, 3)`
|
||||
(with `channels_last` data format) or
|
||||
(3, rows, cols) (with `channels_first` data format).
|
||||
It should have exactly 3 inputs channels,
|
||||
and width and height should be no smaller than 32.
|
||||
E.g. `(224, 224, 3)` would be one valid value.
|
||||
filters: Integer, the dimensionality of the output space
|
||||
(i.e. the number output of filters in the convolution).
|
||||
alpha: controls the width of the network.
|
||||
- If `alpha` < 1.0, proportionally decreases the number
|
||||
of filters in each layer.
|
||||
- If `alpha` > 1.0, proportionally increases the number
|
||||
of filters in each layer.
|
||||
- If `alpha` = 1, default number of filters from the paper
|
||||
are used at each layer.
|
||||
kernel: An integer or tuple/list of 2 integers, specifying the
|
||||
width and height of the 2D convolution window.
|
||||
Can be a single integer to specify the same value for
|
||||
all spatial dimensions.
|
||||
strides: An integer or tuple/list of 2 integers,
|
||||
specifying the strides of the convolution along the width and height.
|
||||
Can be a single integer to specify the same value for
|
||||
all spatial dimensions.
|
||||
Specifying any stride value != 1 is incompatible with specifying
|
||||
any `dilation_rate` value != 1.
|
||||
|
||||
# Input shape
|
||||
4D tensor with shape:
|
||||
`(samples, channels, rows, cols)` if data_format='channels_first'
|
||||
or 4D tensor with shape:
|
||||
`(samples, rows, cols, channels)` if data_format='channels_last'.
|
||||
|
||||
# Output shape
|
||||
4D tensor with shape:
|
||||
`(samples, filters, new_rows, new_cols)` if data_format='channels_first'
|
||||
or 4D tensor with shape:
|
||||
`(samples, new_rows, new_cols, filters)` if data_format='channels_last'.
|
||||
`rows` and `cols` values might have changed due to stride.
|
||||
|
||||
# Returns
|
||||
Output tensor of block.
|
||||
"""
|
||||
channel_axis = 1 if K.image_data_format() == 'channels_first' else -1
|
||||
filters = int(filters * alpha)
|
||||
x = Conv2D(filters, kernel,
|
||||
padding='same',
|
||||
use_bias=False,
|
||||
strides=strides,
|
||||
name='conv1')(inputs)
|
||||
x = BatchNormalization(axis=channel_axis, name='conv1_bn')(x)
|
||||
return Activation(relu6, name='conv1_relu')(x)
|
||||
|
||||
|
||||
def _depthwise_conv_block(inputs, pointwise_conv_filters, alpha,
|
||||
depth_multiplier=1, strides=(1, 1), block_id=1):
|
||||
"""Adds a depthwise convolution block.
|
||||
|
||||
A depthwise convolution block consists of a depthwise conv,
|
||||
batch normalization, relu6, pointwise convolution,
|
||||
batch normalization and relu6 activation.
|
||||
|
||||
# Arguments
|
||||
inputs: Input tensor of shape `(rows, cols, channels)`
|
||||
(with `channels_last` data format) or
|
||||
(channels, rows, cols) (with `channels_first` data format).
|
||||
pointwise_conv_filters: Integer, the dimensionality of the output space
|
||||
(i.e. the number output of filters in the pointwise convolution).
|
||||
alpha: controls the width of the network.
|
||||
- If `alpha` < 1.0, proportionally decreases the number
|
||||
of filters in each layer.
|
||||
- If `alpha` > 1.0, proportionally increases the number
|
||||
of filters in each layer.
|
||||
- If `alpha` = 1, default number of filters from the paper
|
||||
are used at each layer.
|
||||
depth_multiplier: The number of depthwise convolution output channels
|
||||
for each input channel.
|
||||
The total number of depthwise convolution output
|
||||
channels will be equal to `filters_in * depth_multiplier`.
|
||||
strides: An integer or tuple/list of 2 integers,
|
||||
specifying the strides of the convolution along the width and height.
|
||||
Can be a single integer to specify the same value for
|
||||
all spatial dimensions.
|
||||
Specifying any stride value != 1 is incompatible with specifying
|
||||
any `dilation_rate` value != 1.
|
||||
block_id: Integer, a unique identification designating the block number.
|
||||
|
||||
# Input shape
|
||||
4D tensor with shape:
|
||||
`(batch, channels, rows, cols)` if data_format='channels_first'
|
||||
or 4D tensor with shape:
|
||||
`(batch, rows, cols, channels)` if data_format='channels_last'.
|
||||
|
||||
# Output shape
|
||||
4D tensor with shape:
|
||||
`(batch, filters, new_rows, new_cols)` if data_format='channels_first'
|
||||
or 4D tensor with shape:
|
||||
`(batch, new_rows, new_cols, filters)` if data_format='channels_last'.
|
||||
`rows` and `cols` values might have changed due to stride.
|
||||
|
||||
# Returns
|
||||
Output tensor of block.
|
||||
"""
|
||||
channel_axis = 1 if K.image_data_format() == 'channels_first' else -1
|
||||
pointwise_conv_filters = int(pointwise_conv_filters * alpha)
|
||||
|
||||
x = DepthwiseConv2D((3, 3),
|
||||
padding='same',
|
||||
depth_multiplier=depth_multiplier,
|
||||
strides=strides,
|
||||
use_bias=False,
|
||||
name='conv_dw_%d' % block_id)(inputs)
|
||||
x = BatchNormalization(axis=channel_axis, name='conv_dw_%d_bn' % block_id)(x)
|
||||
x = Activation(relu6, name='conv_dw_%d_relu' % block_id)(x)
|
||||
|
||||
x = Conv2D(pointwise_conv_filters, (1, 1),
|
||||
padding='same',
|
||||
use_bias=False,
|
||||
strides=(1, 1),
|
||||
name='conv_pw_%d' % block_id)(x)
|
||||
x = BatchNormalization(axis=channel_axis, name='conv_pw_%d_bn' % block_id)(x)
|
||||
return Activation(relu6, name='conv_pw_%d_relu' % block_id)(x)
|
||||
@@ -43,7 +43,7 @@ def identity_block(input_tensor, kernel_size, filters, stage, block):
|
||||
|
||||
# Arguments
|
||||
input_tensor: input tensor
|
||||
kernel_size: defualt 3, the kernel size of middle conv layer at main path
|
||||
kernel_size: default 3, the kernel size of middle conv layer at main path
|
||||
filters: list of integers, the filterss of 3 conv layer at main path
|
||||
stage: integer, current stage label, used for generating layer names
|
||||
block: 'a','b'..., current block label, used for generating layer names
|
||||
@@ -77,11 +77,11 @@ def identity_block(input_tensor, kernel_size, filters, stage, block):
|
||||
|
||||
|
||||
def conv_block(input_tensor, kernel_size, filters, stage, block, strides=(2, 2)):
|
||||
"""conv_block is the block that has a conv layer at shortcut
|
||||
"""A block that has a conv layer at shortcut.
|
||||
|
||||
# Arguments
|
||||
input_tensor: input tensor
|
||||
kernel_size: defualt 3, the kernel size of middle conv layer at main path
|
||||
kernel_size: default 3, the kernel size of middle conv layer at main path
|
||||
filters: list of integers, the filterss of 3 conv layer at main path
|
||||
stage: integer, current stage label, used for generating layer names
|
||||
block: 'a','b'..., current block label, used for generating layer names
|
||||
|
||||
@@ -32,7 +32,7 @@ if os.path.exists(_config_path):
|
||||
_epsilon = _config.get('epsilon', epsilon())
|
||||
assert isinstance(_epsilon, float)
|
||||
_backend = _config.get('backend', _BACKEND)
|
||||
assert _backend in {'theano', 'tensorflow'}
|
||||
assert _backend in {'theano', 'tensorflow', 'cntk'}
|
||||
_image_data_format = _config.get('image_data_format',
|
||||
image_data_format())
|
||||
assert _image_data_format in {'channels_last', 'channels_first'}
|
||||
@@ -68,11 +68,14 @@ if not os.path.exists(_config_path):
|
||||
# Set backend based on KERAS_BACKEND flag, if applicable.
|
||||
if 'KERAS_BACKEND' in os.environ:
|
||||
_backend = os.environ['KERAS_BACKEND']
|
||||
assert _backend in {'theano', 'tensorflow'}
|
||||
assert _backend in {'theano', 'tensorflow', 'cntk'}
|
||||
_BACKEND = _backend
|
||||
|
||||
# Import backend functions.
|
||||
if _BACKEND == 'theano':
|
||||
if _BACKEND == 'cntk':
|
||||
sys.stderr.write('Using CNTK backend\n')
|
||||
from .cntk_backend import *
|
||||
elif _BACKEND == 'theano':
|
||||
sys.stderr.write('Using Theano backend.\n')
|
||||
from .theano_backend import *
|
||||
elif _BACKEND == 'tensorflow':
|
||||
|
||||
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -63,7 +63,7 @@ def set_floatx(floatx):
|
||||
"""Sets the default float type.
|
||||
|
||||
# Arguments
|
||||
String: 'float16', 'float32', or 'float64'.
|
||||
floatx: String, 'float16', 'float32', or 'float64'.
|
||||
|
||||
# Example
|
||||
```python
|
||||
@@ -164,7 +164,7 @@ def set_image_dim_ordering(dim_ordering):
|
||||
```
|
||||
|
||||
# Raises
|
||||
ValueError if invalid `dim_ordering`
|
||||
ValueError: if `dim_ordering` is invalid.
|
||||
"""
|
||||
global _IMAGE_DATA_FORMAT
|
||||
if dim_ordering not in {'tf', 'th'}:
|
||||
|
||||
@@ -7,13 +7,14 @@ from tensorflow.python.ops import ctc_ops as ctc
|
||||
from tensorflow.python.ops import variables as tf_variables
|
||||
|
||||
from collections import defaultdict
|
||||
import inspect
|
||||
|
||||
import numpy as np
|
||||
import os
|
||||
|
||||
from .common import floatx
|
||||
from .common import _EPSILON
|
||||
from .common import image_data_format
|
||||
from ..utils.generic_utils import has_arg
|
||||
|
||||
# Legacy functions
|
||||
from .common import set_image_dim_ordering
|
||||
@@ -450,15 +451,15 @@ def shape(x):
|
||||
>>> tf_session = K.get_session()
|
||||
>>> val = np.array([[1, 2], [3, 4]])
|
||||
>>> kvar = K.variable(value=val)
|
||||
>>> input = keras.backend.placeholder(shape=(2, 4, 5))
|
||||
>>> inputs = keras.backend.placeholder(shape=(2, 4, 5))
|
||||
>>> K.shape(kvar)
|
||||
<tf.Tensor 'Shape_8:0' shape=(2,) dtype=int32>
|
||||
>>> K.shape(input)
|
||||
>>> K.shape(inputs)
|
||||
<tf.Tensor 'Shape_9:0' shape=(3,) dtype=int32>
|
||||
# To get integer shape (Instead, you can use K.int_shape(x))
|
||||
>>> K.shape(kvar).eval(session=tf_session)
|
||||
array([2, 2], dtype=int32)
|
||||
>>> K.shape(input).eval(session=tf_session)
|
||||
>>> K.shape(inputs).eval(session=tf_session)
|
||||
array([2, 4, 5], dtype=int32)
|
||||
```
|
||||
"""
|
||||
@@ -477,8 +478,8 @@ def int_shape(x):
|
||||
# Examples
|
||||
```python
|
||||
>>> from keras import backend as K
|
||||
>>> input = K.placeholder(shape=(2, 4, 5))
|
||||
>>> K.int_shape(input)
|
||||
>>> inputs = K.placeholder(shape=(2, 4, 5))
|
||||
>>> K.int_shape(inputs)
|
||||
(2, 4, 5)
|
||||
>>> val = np.array([[1, 2], [3, 4]])
|
||||
>>> kvar = K.variable(value=val)
|
||||
@@ -507,10 +508,10 @@ def ndim(x):
|
||||
# Examples
|
||||
```python
|
||||
>>> from keras import backend as K
|
||||
>>> input = K.placeholder(shape=(2, 4, 5))
|
||||
>>> inputs = K.placeholder(shape=(2, 4, 5))
|
||||
>>> val = np.array([[1, 2], [3, 4]])
|
||||
>>> kvar = K.variable(value=val)
|
||||
>>> K.ndim(input)
|
||||
>>> K.ndim(inputs)
|
||||
3
|
||||
>>> K.ndim(kvar)
|
||||
2
|
||||
@@ -549,7 +550,7 @@ def dtype(x):
|
||||
'float32_ref'
|
||||
```
|
||||
"""
|
||||
return x.dtype.name
|
||||
return x.dtype.base_dtype.name
|
||||
|
||||
|
||||
def eval(x):
|
||||
@@ -856,7 +857,7 @@ def update(x, new_x):
|
||||
"""Update the value of `x` to `new_x`.
|
||||
|
||||
# Arguments
|
||||
x: A Variable.
|
||||
x: A `Variable`.
|
||||
new_x: A tensor of same shape as `x`.
|
||||
|
||||
# Returns
|
||||
@@ -869,7 +870,7 @@ def update_add(x, increment):
|
||||
"""Update the value of `x` by adding `increment`.
|
||||
|
||||
# Arguments
|
||||
x: A Variable.
|
||||
x: A `Variable`.
|
||||
increment: A tensor of same shape as `x`.
|
||||
|
||||
# Returns
|
||||
@@ -882,7 +883,7 @@ def update_sub(x, decrement):
|
||||
"""Update the value of `x` by subtracting `decrement`.
|
||||
|
||||
# Arguments
|
||||
x: A Variable.
|
||||
x: A `Variable`.
|
||||
decrement: A tensor of same shape as `x`.
|
||||
|
||||
# Returns
|
||||
@@ -895,12 +896,13 @@ def moving_average_update(x, value, momentum):
|
||||
"""Compute the moving average of a variable.
|
||||
|
||||
# Arguments
|
||||
x: A Variable.
|
||||
value: A tensor with the same shape as `variable`.
|
||||
x: A `Variable`.
|
||||
value: A tensor with the same shape as `x`.
|
||||
momentum: The moving average momentum.
|
||||
|
||||
# Returns
|
||||
An Operation to update the variable."""
|
||||
An operation to update the variable.
|
||||
"""
|
||||
return moving_averages.assign_moving_average(
|
||||
x, value, momentum, zero_debias=False)
|
||||
|
||||
@@ -1087,10 +1089,10 @@ def transpose(x):
|
||||
```
|
||||
|
||||
```python
|
||||
>>> input = K.placeholder((2, 3))
|
||||
>>> input
|
||||
>>> inputs = K.placeholder((2, 3))
|
||||
>>> inputs
|
||||
<tf.Tensor 'Placeholder_11:0' shape=(2, 3) dtype=float32>
|
||||
>>> input_transposed = K.transpose(input)
|
||||
>>> input_transposed = K.transpose(inputs)
|
||||
>>> input_transposed
|
||||
<tf.Tensor 'transpose_4:0' shape=(3, 2) dtype=float32>
|
||||
|
||||
@@ -1151,7 +1153,7 @@ def max(x, axis=None, keepdims=False):
|
||||
A tensor with maximum values of `x`.
|
||||
"""
|
||||
axis = _normalize_axis(axis, ndim(x))
|
||||
return tf.reduce_max(x, reduction_indices=axis, keep_dims=keepdims)
|
||||
return tf.reduce_max(x, axis=axis, keep_dims=keepdims)
|
||||
|
||||
|
||||
def min(x, axis=None, keepdims=False):
|
||||
@@ -1169,7 +1171,7 @@ def min(x, axis=None, keepdims=False):
|
||||
A tensor with miminum values of `x`.
|
||||
"""
|
||||
axis = _normalize_axis(axis, ndim(x))
|
||||
return tf.reduce_min(x, reduction_indices=axis, keep_dims=keepdims)
|
||||
return tf.reduce_min(x, axis=axis, keep_dims=keepdims)
|
||||
|
||||
|
||||
def sum(x, axis=None, keepdims=False):
|
||||
@@ -1187,7 +1189,7 @@ def sum(x, axis=None, keepdims=False):
|
||||
A tensor with sum of `x`.
|
||||
"""
|
||||
axis = _normalize_axis(axis, ndim(x))
|
||||
return tf.reduce_sum(x, reduction_indices=axis, keep_dims=keepdims)
|
||||
return tf.reduce_sum(x, axis=axis, keep_dims=keepdims)
|
||||
|
||||
|
||||
def prod(x, axis=None, keepdims=False):
|
||||
@@ -1205,7 +1207,7 @@ def prod(x, axis=None, keepdims=False):
|
||||
A tensor with the product of elements of `x`.
|
||||
"""
|
||||
axis = _normalize_axis(axis, ndim(x))
|
||||
return tf.reduce_prod(x, reduction_indices=axis, keep_dims=keepdims)
|
||||
return tf.reduce_prod(x, axis=axis, keep_dims=keepdims)
|
||||
|
||||
|
||||
def cumsum(x, axis=0):
|
||||
@@ -1253,10 +1255,10 @@ def var(x, axis=None, keepdims=False):
|
||||
axis = _normalize_axis(axis, ndim(x))
|
||||
if x.dtype.base_dtype == tf.bool:
|
||||
x = tf.cast(x, floatx())
|
||||
m = tf.reduce_mean(x, reduction_indices=axis, keep_dims=True)
|
||||
m = tf.reduce_mean(x, axis=axis, keep_dims=True)
|
||||
devs_squared = tf.square(x - m)
|
||||
return tf.reduce_mean(devs_squared,
|
||||
reduction_indices=axis,
|
||||
axis=axis,
|
||||
keep_dims=keepdims)
|
||||
|
||||
|
||||
@@ -1294,7 +1296,7 @@ def mean(x, axis=None, keepdims=False):
|
||||
axis = _normalize_axis(axis, ndim(x))
|
||||
if x.dtype.base_dtype == tf.bool:
|
||||
x = tf.cast(x, floatx())
|
||||
return tf.reduce_mean(x, reduction_indices=axis, keep_dims=keepdims)
|
||||
return tf.reduce_mean(x, axis=axis, keep_dims=keepdims)
|
||||
|
||||
|
||||
def any(x, axis=None, keepdims=False):
|
||||
@@ -1310,7 +1312,7 @@ def any(x, axis=None, keepdims=False):
|
||||
"""
|
||||
axis = _normalize_axis(axis, ndim(x))
|
||||
x = tf.cast(x, tf.bool)
|
||||
return tf.reduce_any(x, reduction_indices=axis, keep_dims=keepdims)
|
||||
return tf.reduce_any(x, axis=axis, keep_dims=keepdims)
|
||||
|
||||
|
||||
def all(x, axis=None, keepdims=False):
|
||||
@@ -1326,7 +1328,7 @@ def all(x, axis=None, keepdims=False):
|
||||
"""
|
||||
axis = _normalize_axis(axis, ndim(x))
|
||||
x = tf.cast(x, tf.bool)
|
||||
return tf.reduce_all(x, reduction_indices=axis, keep_dims=keepdims)
|
||||
return tf.reduce_all(x, axis=axis, keep_dims=keepdims)
|
||||
|
||||
|
||||
def argmax(x, axis=-1):
|
||||
@@ -1755,14 +1757,13 @@ def resize_images(x, height_factor, width_factor, data_format):
|
||||
x: Tensor or variable to resize.
|
||||
height_factor: Positive integer.
|
||||
width_factor: Positive integer.
|
||||
data_format: One of `"channels_first"`, `"channels_last"`.
|
||||
data_format: string, `"channels_last"` or `"channels_first"`.
|
||||
|
||||
# Returns
|
||||
A tensor.
|
||||
|
||||
# Raises
|
||||
ValueError: if `data_format` is neither
|
||||
`channels_last` or `channels_first`.
|
||||
ValueError: if `data_format` is neither `"channels_last"` or `"channels_first"`.
|
||||
"""
|
||||
if data_format == 'channels_first':
|
||||
original_shape = int_shape(x)
|
||||
@@ -1794,14 +1795,13 @@ def resize_volumes(x, depth_factor, height_factor, width_factor, data_format):
|
||||
depth_factor: Positive integer.
|
||||
height_factor: Positive integer.
|
||||
width_factor: Positive integer.
|
||||
data_format: One of `"channels_first"`, `"channels_last"`.
|
||||
data_format: string, `"channels_last"` or `"channels_first"`.
|
||||
|
||||
# Returns
|
||||
A tensor.
|
||||
|
||||
# Raises
|
||||
ValueError: if `data_format` is neither
|
||||
`channels_last` or `channels_first`.
|
||||
ValueError: if `data_format` is neither `"channels_last"` or `"channels_first"`.
|
||||
"""
|
||||
if data_format == 'channels_first':
|
||||
output = repeat_elements(x, depth_factor, axis=2)
|
||||
@@ -1987,14 +1987,13 @@ def spatial_2d_padding(x, padding=((1, 1), (1, 1)), data_format=None):
|
||||
# Arguments
|
||||
x: Tensor or variable.
|
||||
padding: Tuple of 2 tuples, padding pattern.
|
||||
data_format: One of `channels_last` or `channels_first`.
|
||||
data_format: string, `"channels_last"` or `"channels_first"`.
|
||||
|
||||
# Returns
|
||||
A padded 4D tensor.
|
||||
|
||||
# Raises
|
||||
ValueError: if `data_format` is neither
|
||||
`channels_last` or `channels_first`.
|
||||
ValueError: if `data_format` is neither `"channels_last"` or `"channels_first"`.
|
||||
"""
|
||||
assert len(padding) == 2
|
||||
assert len(padding[0]) == 2
|
||||
@@ -2030,14 +2029,13 @@ def spatial_3d_padding(x, padding=((1, 1), (1, 1), (1, 1)), data_format=None):
|
||||
# Arguments
|
||||
x: Tensor or variable.
|
||||
padding: Tuple of 3 tuples, padding pattern.
|
||||
data_format: One of `channels_last` or `channels_first`.
|
||||
data_format: string, `"channels_last"` or `"channels_first"`.
|
||||
|
||||
# Returns
|
||||
A padded 5D tensor.
|
||||
|
||||
# Raises
|
||||
ValueError: if `data_format` is neither
|
||||
`channels_last` or `channels_first`.
|
||||
ValueError: if `data_format` is neither `"channels_last"` or `"channels_first"`.
|
||||
|
||||
"""
|
||||
assert len(padding) == 3
|
||||
@@ -2092,9 +2090,6 @@ def one_hot(indices, num_classes):
|
||||
# Returns
|
||||
(n + 1)D one hot representation of the input
|
||||
with shape `(batch_size, dim1, dim2, ... dim(n-1), num_classes)`
|
||||
|
||||
# Returns
|
||||
The one-hot tensor.
|
||||
"""
|
||||
return tf.one_hot(indices, depth=num_classes, axis=-1)
|
||||
|
||||
@@ -2291,8 +2286,7 @@ def function(inputs, outputs, updates=None, **kwargs):
|
||||
"""
|
||||
if kwargs:
|
||||
for key in kwargs:
|
||||
if (key not in inspect.getargspec(tf.Session.run)[0] and
|
||||
key not in inspect.getargspec(Function.__init__)[0]):
|
||||
if not (has_arg(tf.Session.run, key, True) or has_arg(Function.__init__, key, True)):
|
||||
msg = 'Invalid argument "%s" passed to K.function with Tensorflow backend' % key
|
||||
raise ValueError(msg)
|
||||
return Function(inputs, outputs, updates=updates, **kwargs)
|
||||
@@ -2333,12 +2327,12 @@ def rnn(step_function, inputs, initial_states,
|
||||
# Arguments
|
||||
step_function: RNN step function.
|
||||
Parameters:
|
||||
input: tensor with shape `(samples, ...)` (no time dimension),
|
||||
inputs: tensor with shape `(samples, ...)` (no time dimension),
|
||||
representing input for the batch of samples at a certain
|
||||
time step.
|
||||
states: list of tensors.
|
||||
Returns:
|
||||
output: tensor with shape `(samples, output_dim)`
|
||||
outputs: tensor with shape `(samples, output_dim)`
|
||||
(no time dimension).
|
||||
new_states: list of tensors, same length and shapes
|
||||
as 'states'. The first state in the list must be the
|
||||
@@ -2728,14 +2722,14 @@ def softsign(x):
|
||||
return tf.nn.softsign(x)
|
||||
|
||||
|
||||
def categorical_crossentropy(output, target, from_logits=False):
|
||||
def categorical_crossentropy(target, output, from_logits=False):
|
||||
"""Categorical crossentropy between an output tensor and a target tensor.
|
||||
|
||||
# Arguments
|
||||
target: A tensor of the same shape as `output`.
|
||||
output: A tensor resulting from a softmax
|
||||
(unless `from_logits` is True, in which
|
||||
case `output` is expected to be the logits).
|
||||
target: A tensor of the same shape as `output`.
|
||||
from_logits: Boolean, whether `output` is the
|
||||
result of a softmax, or is a tensor of logits.
|
||||
|
||||
@@ -2747,33 +2741,33 @@ def categorical_crossentropy(output, target, from_logits=False):
|
||||
if not from_logits:
|
||||
# scale preds so that the class probas of each sample sum to 1
|
||||
output /= tf.reduce_sum(output,
|
||||
reduction_indices=len(output.get_shape()) - 1,
|
||||
axis=len(output.get_shape()) - 1,
|
||||
keep_dims=True)
|
||||
# manual computation of crossentropy
|
||||
epsilon = _to_tensor(_EPSILON, output.dtype.base_dtype)
|
||||
output = tf.clip_by_value(output, epsilon, 1. - epsilon)
|
||||
return - tf.reduce_sum(target * tf.log(output),
|
||||
reduction_indices=len(output.get_shape()) - 1)
|
||||
axis=len(output.get_shape()) - 1)
|
||||
else:
|
||||
return tf.nn.softmax_cross_entropy_with_logits(labels=target,
|
||||
logits=output)
|
||||
|
||||
|
||||
def sparse_categorical_crossentropy(output, target, from_logits=False):
|
||||
def sparse_categorical_crossentropy(target, output, from_logits=False):
|
||||
"""Categorical crossentropy with integer targets.
|
||||
|
||||
# Arguments
|
||||
target: An integer tensor.
|
||||
output: A tensor resulting from a softmax
|
||||
(unless `from_logits` is True, in which
|
||||
case `output` is expected to be the logits).
|
||||
target: An integer tensor.
|
||||
from_logits: Boolean, whether `output` is the
|
||||
result of a softmax, or is a tensor of logits.
|
||||
|
||||
# Returns
|
||||
Output tensor.
|
||||
"""
|
||||
# Note: tf.nn.softmax_cross_entropy_with_logits
|
||||
# Note: tf.nn.sparse_softmax_cross_entropy_with_logits
|
||||
# expects logits, Keras expects probabilities.
|
||||
if not from_logits:
|
||||
epsilon = _to_tensor(_EPSILON, output.dtype.base_dtype)
|
||||
@@ -2793,12 +2787,12 @@ def sparse_categorical_crossentropy(output, target, from_logits=False):
|
||||
return res
|
||||
|
||||
|
||||
def binary_crossentropy(output, target, from_logits=False):
|
||||
def binary_crossentropy(target, output, from_logits=False):
|
||||
"""Binary crossentropy between an output tensor and a target tensor.
|
||||
|
||||
# Arguments
|
||||
output: A tensor.
|
||||
target: A tensor with the same shape as `output`.
|
||||
output: A tensor.
|
||||
from_logits: Whether `output` is expected to be a logits tensor.
|
||||
By default, we consider that `output`
|
||||
encodes a probability distribution.
|
||||
@@ -2806,7 +2800,7 @@ def binary_crossentropy(output, target, from_logits=False):
|
||||
# Returns
|
||||
A tensor.
|
||||
"""
|
||||
# Note: tf.nn.softmax_cross_entropy_with_logits
|
||||
# Note: tf.nn.sigmoid_cross_entropy_with_logits
|
||||
# expects logits, Keras expects probabilities.
|
||||
if not from_logits:
|
||||
# transform back to logits
|
||||
@@ -2880,7 +2874,7 @@ def dropout(x, level, noise_shape=None, seed=None):
|
||||
if seed is None:
|
||||
seed = np.random.randint(10e6)
|
||||
# the dummy 1. works around a TF bug
|
||||
# (float32_ref vs. float32 incomptability)
|
||||
# (float32_ref vs. float32 incompatibility)
|
||||
return tf.nn.dropout(x * 1., retain_prob, noise_shape, seed=seed)
|
||||
|
||||
|
||||
@@ -2917,13 +2911,33 @@ def in_top_k(predictions, targets, k):
|
||||
|
||||
# CONVOLUTIONS
|
||||
|
||||
def _preprocess_deconv3d_output_shape(x, shape, data_format):
|
||||
"""Get the output_shape for the 3D deconvolution.
|
||||
|
||||
# Arguments
|
||||
x: input tensor.
|
||||
shape: output shape.
|
||||
data_format: string, `"channels_last"` or `"channels_first"`.
|
||||
|
||||
# Returns
|
||||
The output shape.
|
||||
"""
|
||||
if data_format == 'channels_first':
|
||||
shape = (shape[0], shape[2], shape[3], shape[4], shape[1])
|
||||
|
||||
if shape[0] is None:
|
||||
shape = (tf.shape(x)[0], ) + tuple(shape[1:])
|
||||
shape = tf.stack(list(shape))
|
||||
return shape
|
||||
|
||||
|
||||
def _preprocess_deconv_output_shape(x, shape, data_format):
|
||||
"""Get the output_shape for the deconvolution.
|
||||
|
||||
# Arguments
|
||||
x: input tensor.
|
||||
shape: output shape.
|
||||
data_format: string, one of 'channels_last', 'channels_first'.
|
||||
data_format: string, `"channels_last"` or `"channels_first"`.
|
||||
|
||||
# Returns
|
||||
The output shape.
|
||||
@@ -2942,7 +2956,7 @@ def _preprocess_conv2d_input(x, data_format):
|
||||
|
||||
# Arguments
|
||||
x: input tensor.
|
||||
data_format: string, one of 'channels_last', 'channels_first'.
|
||||
data_format: string, `"channels_last"` or `"channels_first"`.
|
||||
|
||||
# Returns
|
||||
A tensor.
|
||||
@@ -2963,7 +2977,7 @@ def _preprocess_conv3d_input(x, data_format):
|
||||
|
||||
# Arguments
|
||||
x: input tensor.
|
||||
data_format: string, one of 'channels_last', 'channels_first'.
|
||||
data_format: string, `"channels_last"` or `"channels_first"`.
|
||||
|
||||
# Returns
|
||||
A tensor.
|
||||
@@ -2980,7 +2994,7 @@ def _preprocess_conv2d_kernel(kernel, data_format):
|
||||
|
||||
# Arguments
|
||||
kernel: kernel tensor.
|
||||
data_format: string, one of 'channels_last', 'channels_first'.
|
||||
data_format: string, `"channels_last"` or `"channels_first"`.
|
||||
|
||||
# Returns
|
||||
A tensor.
|
||||
@@ -2997,7 +3011,7 @@ def _preprocess_conv3d_kernel(kernel, data_format):
|
||||
|
||||
# Arguments
|
||||
kernel: kernel tensor.
|
||||
data_format: string, one of 'channels_last', 'channels_first'.
|
||||
data_format: string, `"channels_last"` or `"channels_first"`.
|
||||
|
||||
# Returns
|
||||
A tensor.
|
||||
@@ -3013,13 +3027,13 @@ def _preprocess_padding(padding):
|
||||
"""Convert keras' padding to tensorflow's padding.
|
||||
|
||||
# Arguments
|
||||
padding: string, one of 'same' , 'valid'
|
||||
padding: string, `"same"` or `"valid"`.
|
||||
|
||||
# Returns
|
||||
a string, one of 'SAME', 'VALID'.
|
||||
a string, `"SAME"` or `"VALID"`.
|
||||
|
||||
# Raises
|
||||
ValueError if invalid `padding'`
|
||||
ValueError: if `padding` is invalid.
|
||||
"""
|
||||
if padding == 'same':
|
||||
padding = 'SAME'
|
||||
@@ -3035,7 +3049,7 @@ def _postprocess_conv2d_output(x, data_format):
|
||||
|
||||
# Arguments
|
||||
x: A tensor.
|
||||
data_format: string, one of "channels_last", "channels_first".
|
||||
data_format: string, `"channels_last"` or `"channels_first"`.
|
||||
|
||||
# Returns
|
||||
A tensor.
|
||||
@@ -3054,7 +3068,7 @@ def _postprocess_conv3d_output(x, data_format):
|
||||
|
||||
# Arguments
|
||||
x: A tensor.
|
||||
data_format: string, one of "channels_last", "channels_first".
|
||||
data_format: string, `"channels_last"` or `"channels_first"`.
|
||||
|
||||
# Returns
|
||||
A tensor.
|
||||
@@ -3076,7 +3090,7 @@ def conv1d(x, kernel, strides=1, padding='valid',
|
||||
kernel: kernel tensor.
|
||||
strides: stride integer.
|
||||
padding: string, `"same"`, `"causal"` or `"valid"`.
|
||||
data_format: string, one of "channels_last", "channels_first".
|
||||
data_format: string, `"channels_last"` or `"channels_first"`.
|
||||
dilation_rate: integer dilate rate.
|
||||
|
||||
# Returns
|
||||
@@ -3112,9 +3126,9 @@ def conv2d(x, kernel, strides=(1, 1), padding='valid',
|
||||
kernel: kernel tensor.
|
||||
strides: strides tuple.
|
||||
padding: string, `"same"` or `"valid"`.
|
||||
data_format: `"channels_last"` or `"channels_first"`.
|
||||
data_format: string, `"channels_last"` or `"channels_first"`.
|
||||
Whether to use Theano or TensorFlow data format
|
||||
for inputs/kernels/ouputs.
|
||||
for inputs/kernels/outputs.
|
||||
dilation_rate: tuple of 2 integers.
|
||||
|
||||
# Returns
|
||||
@@ -3153,9 +3167,9 @@ def conv2d_transpose(x, kernel, output_shape, strides=(1, 1),
|
||||
output_shape: 1D int tensor for the output shape.
|
||||
strides: strides tuple.
|
||||
padding: string, `"same"` or `"valid"`.
|
||||
data_format: `"channels_last"` or `"channels_first"`.
|
||||
data_format: string, `"channels_last"` or `"channels_first"`.
|
||||
Whether to use Theano or TensorFlow data format
|
||||
for inputs/kernels/ouputs.
|
||||
for inputs/kernels/outputs.
|
||||
|
||||
# Returns
|
||||
A tensor, result of transposed 2D convolution.
|
||||
@@ -3190,8 +3204,8 @@ def separable_conv2d(x, depthwise_kernel, pointwise_kernel, strides=(1, 1),
|
||||
depthwise_kernel: convolution kernel for the depthwise convolution.
|
||||
pointwise_kernel: kernel for the 1x1 convolution.
|
||||
strides: strides tuple (length 2).
|
||||
padding: padding mode, "valid" or "same".
|
||||
data_format: data format, "channels_first" or "channels_last".
|
||||
padding: string, `"same"` or `"valid"`.
|
||||
data_format: string, `"channels_last"` or `"channels_first"`.
|
||||
dilation_rate: tuple of integers,
|
||||
dilation rates for the separable convolution.
|
||||
|
||||
@@ -3217,6 +3231,41 @@ def separable_conv2d(x, depthwise_kernel, pointwise_kernel, strides=(1, 1),
|
||||
return _postprocess_conv2d_output(x, data_format)
|
||||
|
||||
|
||||
def depthwise_conv2d(x, depthwise_kernel, strides=(1, 1), padding='valid',
|
||||
data_format=None, dilation_rate=(1, 1)):
|
||||
"""2D convolution with separable filters.
|
||||
|
||||
# Arguments
|
||||
x: input tensor
|
||||
depthwise_kernel: convolution kernel for the depthwise convolution.
|
||||
strides: strides tuple (length 2).
|
||||
padding: string, `"same"` or `"valid"`.
|
||||
data_format: string, `"channels_last"` or `"channels_first"`.
|
||||
dilation_rate: tuple of integers,
|
||||
dilation rates for the separable convolution.
|
||||
|
||||
# Returns
|
||||
Output tensor.
|
||||
|
||||
# Raises
|
||||
ValueError: if `data_format` is neither `channels_last` or `channels_first`.
|
||||
"""
|
||||
if data_format is None:
|
||||
data_format = image_data_format()
|
||||
if data_format not in {'channels_first', 'channels_last'}:
|
||||
raise ValueError('Unknown data_format ' + str(data_format))
|
||||
|
||||
x = _preprocess_conv2d_input(x, data_format)
|
||||
padding = _preprocess_padding(padding)
|
||||
strides = (1,) + strides + (1,)
|
||||
|
||||
x = tf.nn.depthwise_conv2d(x, depthwise_kernel,
|
||||
strides=strides,
|
||||
padding=padding,
|
||||
rate=dilation_rate)
|
||||
return _postprocess_conv2d_output(x, data_format)
|
||||
|
||||
|
||||
def conv3d(x, kernel, strides=(1, 1, 1), padding='valid',
|
||||
data_format=None, dilation_rate=(1, 1, 1)):
|
||||
"""3D convolution.
|
||||
@@ -3226,9 +3275,9 @@ def conv3d(x, kernel, strides=(1, 1, 1), padding='valid',
|
||||
kernel: kernel tensor.
|
||||
strides: strides tuple.
|
||||
padding: string, `"same"` or `"valid"`.
|
||||
data_format: `"channels_last"` or `"channels_first"`.
|
||||
data_format: string, `"channels_last"` or `"channels_first"`.
|
||||
Whether to use Theano or TensorFlow data format
|
||||
for inputs/kernels/ouputs.
|
||||
for inputs/kernels/outputs.
|
||||
dilation_rate: tuple of 3 integers.
|
||||
|
||||
# Returns
|
||||
@@ -3257,6 +3306,43 @@ def conv3d(x, kernel, strides=(1, 1, 1), padding='valid',
|
||||
return _postprocess_conv3d_output(x, data_format)
|
||||
|
||||
|
||||
def conv3d_transpose(x, kernel, output_shape, strides=(1, 1, 1),
|
||||
padding='valid', data_format=None):
|
||||
"""3D deconvolution (i.e. transposed convolution).
|
||||
|
||||
# Arguments
|
||||
x: input tensor.
|
||||
kernel: kernel tensor.
|
||||
output_shape: 1D int tensor for the output shape.
|
||||
strides: strides tuple.
|
||||
padding: string, "same" or "valid".
|
||||
data_format: string, `"channels_last"` or `"channels_first"`.
|
||||
Whether to use Theano or TensorFlow data format
|
||||
for inputs/kernels/outputs.
|
||||
|
||||
# Returns
|
||||
A tensor, result of transposed 3D convolution.
|
||||
|
||||
# Raises
|
||||
ValueError: if `data_format` is neither `channels_last` or `channels_first`.
|
||||
"""
|
||||
if data_format is None:
|
||||
data_format = image_data_format()
|
||||
if data_format not in {'channels_first', 'channels_last'}:
|
||||
raise ValueError('Unknown data_format ' + str(data_format))
|
||||
if isinstance(output_shape, (tuple, list)):
|
||||
output_shape = tf.stack(output_shape)
|
||||
|
||||
x = _preprocess_conv3d_input(x, data_format)
|
||||
output_shape = _preprocess_deconv3d_output_shape(x, output_shape, data_format)
|
||||
padding = _preprocess_padding(padding)
|
||||
strides = (1,) + strides + (1,)
|
||||
|
||||
x = tf.nn.conv3d_transpose(x, kernel, output_shape, strides,
|
||||
padding=padding)
|
||||
return _postprocess_conv3d_output(x, data_format)
|
||||
|
||||
|
||||
def pool2d(x, pool_size, strides=(1, 1),
|
||||
padding='valid', data_format=None,
|
||||
pool_mode='max'):
|
||||
@@ -3266,16 +3352,16 @@ def pool2d(x, pool_size, strides=(1, 1),
|
||||
x: Tensor or variable.
|
||||
pool_size: tuple of 2 integers.
|
||||
strides: tuple of 2 integers.
|
||||
padding: one of `"valid"`, `"same"`.
|
||||
data_format: one of `"channels_first"`, `"channels_last"`.
|
||||
pool_mode: one of `"max"`, `"avg"`.
|
||||
padding: string, `"same"` or `"valid"`.
|
||||
data_format: string, `"channels_last"` or `"channels_first"`.
|
||||
pool_mode: string, `"max"` or `"avg"`.
|
||||
|
||||
# Returns
|
||||
A tensor, result of 2D pooling.
|
||||
|
||||
# Raises
|
||||
ValueError: if `data_format` is neither `channels_last` or `channels_first`.
|
||||
ValueError: if `pool_mode` is neither `max` or `avg`.
|
||||
ValueError: if `data_format` is neither `"channels_last"` or `"channels_first"`.
|
||||
ValueError: if `pool_mode` is neither `"max"` or `"avg"`.
|
||||
"""
|
||||
if data_format is None:
|
||||
data_format = image_data_format()
|
||||
@@ -3306,17 +3392,16 @@ def pool3d(x, pool_size, strides=(1, 1, 1), padding='valid',
|
||||
x: Tensor or variable.
|
||||
pool_size: tuple of 3 integers.
|
||||
strides: tuple of 3 integers.
|
||||
padding: one of `"valid"`, `"same"`.
|
||||
data_format: one of `"channels_first"`, `"channels_last"`.
|
||||
pool_mode: one of `"max"`, `"avg"`.
|
||||
padding: string, `"same"` or `"valid"`.
|
||||
data_format: string, `"channels_last"` or `"channels_first"`.
|
||||
pool_mode: string, `"max"` or `"avg"`.
|
||||
|
||||
# Returns
|
||||
A tensor, result of 3D pooling.
|
||||
|
||||
# Raises
|
||||
ValueError: if `data_format` is neither
|
||||
`channels_last` or `channels_first`.
|
||||
ValueError: if `pool_mode` is neither `max` or `avg`.
|
||||
ValueError: if `data_format` is neither `"channels_last"` or `"channels_first"`.
|
||||
ValueError: if `pool_mode` is neither `"max"` or `"avg"`.
|
||||
"""
|
||||
if data_format is None:
|
||||
data_format = image_data_format()
|
||||
@@ -3345,35 +3430,60 @@ def bias_add(x, bias, data_format=None):
|
||||
# Arguments
|
||||
x: Tensor or variable.
|
||||
bias: Bias tensor to add.
|
||||
data_format: Data format for 3D, 4D or 5D tensors:
|
||||
one of "channels_first", "channels_last".
|
||||
data_format: string, `"channels_last"` or `"channels_first"`.
|
||||
|
||||
# Returns
|
||||
Output tensor.
|
||||
|
||||
# Raises
|
||||
ValueError: In case of invalid `data_format` argument.
|
||||
ValueError: In one of the two cases below:
|
||||
1. invalid `data_format` argument.
|
||||
2. invalid bias shape.
|
||||
the bias should be either a vector or
|
||||
a tensor with ndim(x) - 1 dimension
|
||||
"""
|
||||
if data_format is None:
|
||||
data_format = image_data_format()
|
||||
if data_format not in {'channels_first', 'channels_last'}:
|
||||
raise ValueError('Unknown data_format ' + str(data_format))
|
||||
bias_shape = int_shape(bias)
|
||||
if len(bias_shape) != 1 and len(bias_shape) != ndim(x) - 1:
|
||||
raise ValueError('Unexpected bias dimensions %d, expect to be 1 or %d dimensions'
|
||||
% (len(bias_shape), ndim(x)))
|
||||
if ndim(x) == 5:
|
||||
if data_format == 'channels_first':
|
||||
x += reshape(bias, (1, int_shape(bias)[0], 1, 1, 1))
|
||||
if len(bias_shape) == 1:
|
||||
x += reshape(bias, (1, bias_shape[0], 1, 1, 1))
|
||||
else:
|
||||
x += reshape(bias, (1, bias_shape[3]) + bias_shape[:3])
|
||||
elif data_format == 'channels_last':
|
||||
x += reshape(bias, (1, 1, 1, 1, int_shape(bias)[0]))
|
||||
if len(bias_shape) == 1:
|
||||
x += reshape(bias, (1, 1, 1, bias_shape[0]))
|
||||
else:
|
||||
x += reshape(bias, (1,) + bias_shape)
|
||||
elif ndim(x) == 4:
|
||||
if data_format == 'channels_first':
|
||||
x += reshape(bias, (1, int_shape(bias)[0], 1, 1))
|
||||
if len(bias_shape) == 1:
|
||||
x += reshape(bias, (1, bias_shape[0], 1, 1))
|
||||
else:
|
||||
x += reshape(bias, (1, bias_shape[2]) + bias_shape[:2])
|
||||
elif data_format == 'channels_last':
|
||||
x = tf.nn.bias_add(x, bias,
|
||||
data_format='NHWC')
|
||||
if len(bias_shape) == 1:
|
||||
x = tf.nn.bias_add(x, bias,
|
||||
data_format='NHWC')
|
||||
else:
|
||||
x += reshape(bias, (1,) + bias_shape)
|
||||
elif ndim(x) == 3:
|
||||
if data_format == 'channels_first':
|
||||
x += reshape(bias, (1, int_shape(bias)[0], 1))
|
||||
if len(bias_shape) == 1:
|
||||
x += reshape(bias, (1, bias_shape[0], 1))
|
||||
else:
|
||||
x += reshape(bias, (1, bias_shape[1], bias_shape[0]))
|
||||
elif data_format == 'channels_last':
|
||||
x += reshape(bias, (1, 1, int_shape(bias)[0]))
|
||||
if len(bias_shape) == 1:
|
||||
x += reshape(bias, (1, 1, bias_shape[0]))
|
||||
else:
|
||||
x += reshape(bias, (1, ) + bias_shape)
|
||||
else:
|
||||
x = tf.nn.bias_add(x, bias)
|
||||
return x
|
||||
@@ -3635,6 +3745,113 @@ def foldr(fn, elems, initializer=None, name=None):
|
||||
name: A string name for the foldr node in the graph
|
||||
|
||||
# Returns
|
||||
Same type and shape as initializer
|
||||
Tensor with same type and shape as `initializer`.
|
||||
"""
|
||||
return tf.foldr(fn, elems, initializer=initializer, name=name)
|
||||
|
||||
|
||||
def local_conv1d(inputs, kernel, kernel_size, strides, data_format=None):
|
||||
"""Apply 1D conv with un-shared weights.
|
||||
|
||||
# Arguments
|
||||
inputs: 3D tensor with shape: (batch_size, steps, input_dim)
|
||||
kernel: the unshared weight for convolution,
|
||||
with shape (output_length, feature_dim, filters)
|
||||
kernel_size: a tuple of a single integer,
|
||||
specifying the length of the 1D convolution window
|
||||
strides: a tuple of a single integer,
|
||||
specifying the stride length of the convolution
|
||||
data_format: the data format, channels_first or channels_last
|
||||
|
||||
# Returns
|
||||
the tensor after 1d conv with un-shared weights, with shape (batch_size, output_lenght, filters)
|
||||
|
||||
# Raises
|
||||
ValueError: if `data_format` is neither `channels_last` or `channels_first`.
|
||||
"""
|
||||
if data_format is None:
|
||||
data_format = image_data_format()
|
||||
if data_format not in {'channels_first', 'channels_last'}:
|
||||
raise ValueError('Unknown data_format ' + str(data_format))
|
||||
|
||||
stride = strides[0]
|
||||
kernel_shape = int_shape(kernel)
|
||||
output_length, feature_dim, filters = kernel_shape
|
||||
|
||||
xs = []
|
||||
for i in range(output_length):
|
||||
slice_length = slice(i * stride,
|
||||
i * stride + kernel_size[0])
|
||||
xs.append(reshape(inputs[:, slice_length, :],
|
||||
(1, -1, feature_dim)))
|
||||
x_aggregate = concatenate(xs, axis=0)
|
||||
# Shape: `(output_length, batch_size, filters)`.
|
||||
output = batch_dot(x_aggregate, kernel)
|
||||
return permute_dimensions(output, (1, 0, 2))
|
||||
|
||||
|
||||
def local_conv2d(inputs, kernel, kernel_size, strides, output_shape, data_format=None):
|
||||
"""Apply 2D conv with un-shared weights.
|
||||
|
||||
# Arguments
|
||||
inputs: 4D tensor with shape:
|
||||
(batch_size, filters, new_rows, new_cols)
|
||||
if data_format='channels_first'
|
||||
or 4D tensor with shape:
|
||||
(batch_size, new_rows, new_cols, filters)
|
||||
if data_format='channels_last'.
|
||||
kernel: the unshared weight for convolution,
|
||||
with shape (output_items, feature_dim, filters)
|
||||
kernel_size: a tuple of 2 integers, specifying the
|
||||
width and height of the 2D convolution window.
|
||||
strides: a tuple of 2 integers, specifying the strides
|
||||
of the convolution along the width and height.
|
||||
output_shape: a tuple with (output_row, output_col)
|
||||
data_format: the data format, channels_first or channels_last
|
||||
|
||||
# Returns
|
||||
A 4d tensor with shape:
|
||||
(batch_size, filters, new_rows, new_cols)
|
||||
if data_format='channels_first'
|
||||
or 4D tensor with shape:
|
||||
(batch_size, new_rows, new_cols, filters)
|
||||
if data_format='channels_last'.
|
||||
|
||||
# Raises
|
||||
ValueError: if `data_format` is neither
|
||||
`channels_last` or `channels_first`.
|
||||
"""
|
||||
if data_format is None:
|
||||
data_format = image_data_format()
|
||||
if data_format not in {'channels_first', 'channels_last'}:
|
||||
raise ValueError('Unknown data_format ' + str(data_format))
|
||||
|
||||
stride_row, stride_col = strides
|
||||
output_row, output_col = output_shape
|
||||
kernel_shape = int_shape(kernel)
|
||||
_, feature_dim, filters = kernel_shape
|
||||
|
||||
xs = []
|
||||
for i in range(output_row):
|
||||
for j in range(output_col):
|
||||
slice_row = slice(i * stride_row,
|
||||
i * stride_row + kernel_size[0])
|
||||
slice_col = slice(j * stride_col,
|
||||
j * stride_col + kernel_size[1])
|
||||
if data_format == 'channels_first':
|
||||
xs.append(reshape(inputs[:, :, slice_row, slice_col],
|
||||
(1, -1, feature_dim)))
|
||||
else:
|
||||
xs.append(reshape(inputs[:, slice_row, slice_col, :],
|
||||
(1, -1, feature_dim)))
|
||||
|
||||
x_aggregate = concatenate(xs, axis=0)
|
||||
output = batch_dot(x_aggregate, kernel)
|
||||
output = reshape(output,
|
||||
(output_row, output_col, -1, filters))
|
||||
|
||||
if data_format == 'channels_first':
|
||||
output = permute_dimensions(output, (2, 3, 0, 1))
|
||||
else:
|
||||
output = permute_dimensions(output, (2, 0, 1, 3))
|
||||
return output
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from collections import defaultdict
|
||||
from contextlib import contextmanager
|
||||
import theano
|
||||
from theano import ifelse
|
||||
from theano import tensor as T
|
||||
from theano.sandbox.rng_mrg import MRG_RandomStreams as RandomStreams
|
||||
from theano.tensor.signal import pool
|
||||
@@ -14,9 +13,10 @@ try:
|
||||
from theano.tensor.nnet.nnet import softsign as T_softsign
|
||||
except ImportError:
|
||||
from theano.sandbox.softsign import softsign as T_softsign
|
||||
import inspect
|
||||
|
||||
import numpy as np
|
||||
from .common import _FLOATX, floatx, _EPSILON, image_data_format
|
||||
from ..utils.generic_utils import has_arg
|
||||
# Legacy functions
|
||||
from .common import set_image_dim_ordering, image_dim_ordering
|
||||
|
||||
@@ -445,10 +445,14 @@ def transpose(x):
|
||||
|
||||
|
||||
def gather(reference, indices):
|
||||
"""reference: a tensor.
|
||||
indices: an int tensor of indices.
|
||||
"""Retrieves the elements of indices `indices` in the tensor `reference`.
|
||||
|
||||
Return: a tensor of same type as reference.
|
||||
# Arguments
|
||||
reference: A tensor.
|
||||
indices: An integer tensor of indices.
|
||||
|
||||
# Returns
|
||||
A tensor of same type as `reference`.
|
||||
"""
|
||||
y = reference[indices]
|
||||
if hasattr(reference, '_keras_shape') and hasattr(indices, '_keras_shape'):
|
||||
@@ -929,7 +933,7 @@ def tile(x, n):
|
||||
output_shape += (None,)
|
||||
else:
|
||||
output_shape += (i * j,)
|
||||
elif type(n) is int:
|
||||
elif isinstance(n, int):
|
||||
output_shape = x._keras_shape[:-1]
|
||||
if x._keras_shape[-1] is None:
|
||||
output_shape += (None,)
|
||||
@@ -1051,7 +1055,7 @@ def spatial_2d_padding(x, padding=((1, 1), (1, 1)), data_format=None):
|
||||
slice(top_pad, input_shape[2] + top_pad),
|
||||
slice(left_pad, input_shape[3] + left_pad))
|
||||
|
||||
elif data_format == 'channels_last':
|
||||
else:
|
||||
output_shape = (input_shape[0],
|
||||
input_shape[1] + top_pad + bottom_pad,
|
||||
input_shape[2] + left_pad + right_pad,
|
||||
@@ -1061,8 +1065,6 @@ def spatial_2d_padding(x, padding=((1, 1), (1, 1)), data_format=None):
|
||||
slice(top_pad, input_shape[1] + top_pad),
|
||||
slice(left_pad, input_shape[2] + left_pad),
|
||||
slice(None))
|
||||
else:
|
||||
raise ValueError('Invalid data_format:', data_format)
|
||||
y = T.set_subtensor(output[indices], x)
|
||||
y._keras_shape = output_shape
|
||||
return y
|
||||
@@ -1091,7 +1093,7 @@ def spatial_3d_padding(x, padding=((1, 1), (1, 1), (1, 1)), data_format=None):
|
||||
slice(padding[1][0], input_shape[3] + padding[1][0]),
|
||||
slice(padding[2][0], input_shape[4] + padding[2][0]))
|
||||
|
||||
elif data_format == 'channels_last':
|
||||
else:
|
||||
output_shape = (input_shape[0],
|
||||
input_shape[1] + padding[0][0] + padding[0][1],
|
||||
input_shape[2] + padding[1][0] + padding[1][1],
|
||||
@@ -1103,8 +1105,6 @@ def spatial_3d_padding(x, padding=((1, 1), (1, 1), (1, 1)), data_format=None):
|
||||
slice(padding[1][0], input_shape[2] + padding[1][0]),
|
||||
slice(padding[2][0], input_shape[3] + padding[2][0]),
|
||||
slice(None))
|
||||
else:
|
||||
raise ValueError('Invalid data_format:', data_format)
|
||||
return T.set_subtensor(output[indices], x)
|
||||
|
||||
|
||||
@@ -1141,8 +1141,8 @@ def pattern_broadcast(x, broatcastable):
|
||||
|
||||
def get_value(x):
|
||||
if not hasattr(x, 'get_value'):
|
||||
raise TypeError('get_value() can only be called on a variable. '
|
||||
'If you have an expression instead, use eval().')
|
||||
raise TypeError('`get_value` can only be called on a variable. '
|
||||
'If you have an expression instead, use `eval()`.')
|
||||
return x.get_value()
|
||||
|
||||
|
||||
@@ -1198,9 +1198,8 @@ class Function(object):
|
||||
|
||||
def function(inputs, outputs, updates=[], **kwargs):
|
||||
if len(kwargs) > 0:
|
||||
function_args = inspect.getargspec(theano.function)[0]
|
||||
for key in kwargs.keys():
|
||||
if key not in function_args:
|
||||
if not has_arg(theano.function, key, True):
|
||||
msg = 'Invalid argument "%s" passed to K.function with Theano backend' % key
|
||||
raise ValueError(msg)
|
||||
return Function(inputs, outputs, updates=updates, **kwargs)
|
||||
@@ -1229,12 +1228,12 @@ def rnn(step_function, inputs, initial_states,
|
||||
(at least 3D).
|
||||
step_function:
|
||||
Parameters:
|
||||
input: tensor with shape (samples, ...) (no time dimension),
|
||||
inputs: tensor with shape (samples, ...) (no time dimension),
|
||||
representing input for the batch of samples at a certain
|
||||
time step.
|
||||
states: list of tensors.
|
||||
Returns:
|
||||
output: tensor with shape (samples, ...) (no time dimension),
|
||||
outputs: tensor with shape (samples, ...) (no time dimension),
|
||||
new_states: list of tensors, same length and shapes
|
||||
as 'states'.
|
||||
initial_states: tensor with shape (samples, ...) (no time dimension),
|
||||
@@ -1315,14 +1314,14 @@ def rnn(step_function, inputs, initial_states,
|
||||
if len(initial_states) > 0:
|
||||
initial_states[0] = T.unbroadcast(initial_states[0], 0, 1)
|
||||
|
||||
def _step(input, mask, output_tm1, *states):
|
||||
output, new_states = step_function(input, states)
|
||||
def _step(inputs, mask, output_tm1, *states):
|
||||
outputs, new_states = step_function(inputs, states)
|
||||
# output previous output if masked.
|
||||
output = T.switch(mask, output, output_tm1)
|
||||
outputs = T.switch(mask, outputs, output_tm1)
|
||||
return_states = []
|
||||
for state, new_state in zip(states, new_states):
|
||||
return_states.append(T.switch(mask, new_state, state))
|
||||
return [output] + return_states
|
||||
return [outputs] + return_states
|
||||
|
||||
results, _ = theano.scan(
|
||||
_step,
|
||||
@@ -1348,8 +1347,8 @@ def rnn(step_function, inputs, initial_states,
|
||||
successive_states = []
|
||||
states = initial_states
|
||||
for i in indices:
|
||||
output, states = step_function(inputs[i], states + constants)
|
||||
successive_outputs.append(output)
|
||||
outputs, states = step_function(inputs[i], states + constants)
|
||||
successive_outputs.append(outputs)
|
||||
successive_states.append(states)
|
||||
outputs = T.stack(*successive_outputs)
|
||||
states = []
|
||||
@@ -1357,9 +1356,9 @@ def rnn(step_function, inputs, initial_states,
|
||||
states.append(T.stack(*[states_at_step[i] for states_at_step in successive_states]))
|
||||
|
||||
else:
|
||||
def _step(input, *states):
|
||||
output, new_states = step_function(input, states)
|
||||
return [output] + new_states
|
||||
def _step(inputs, *states):
|
||||
outputs, new_states = step_function(inputs, states)
|
||||
return [outputs] + new_states
|
||||
|
||||
# Theano likes to make shape==1 dimensions in the initial states (outputs_info) broadcastable
|
||||
if len(initial_states) > 0:
|
||||
@@ -1390,7 +1389,18 @@ def rnn(step_function, inputs, initial_states,
|
||||
|
||||
|
||||
def switch(condition, then_expression, else_expression):
|
||||
"""condition: scalar tensor.
|
||||
"""Switches between two operations depending on a scalar value.
|
||||
|
||||
Note that both `then_expression` and `else_expression`
|
||||
should be symbolic tensors of the *same shape*.
|
||||
|
||||
# Arguments
|
||||
condition: scalar tensor (`int` or `bool`).
|
||||
then_expression: either a tensor, or a callable that returns a tensor.
|
||||
else_expression: either a tensor, or a callable that returns a tensor.
|
||||
|
||||
# Returns
|
||||
The selected tensor.
|
||||
"""
|
||||
if callable(then_expression):
|
||||
then_expression = then_expression()
|
||||
@@ -1491,7 +1501,7 @@ def softsign(x):
|
||||
return T_softsign(x)
|
||||
|
||||
|
||||
def categorical_crossentropy(output, target, from_logits=False):
|
||||
def categorical_crossentropy(target, output, from_logits=False):
|
||||
if from_logits:
|
||||
output = T.nnet.softmax(output)
|
||||
else:
|
||||
@@ -1502,14 +1512,14 @@ def categorical_crossentropy(output, target, from_logits=False):
|
||||
return T.nnet.categorical_crossentropy(output, target)
|
||||
|
||||
|
||||
def sparse_categorical_crossentropy(output, target, from_logits=False):
|
||||
def sparse_categorical_crossentropy(target, output, from_logits=False):
|
||||
target = T.cast(T.flatten(target), 'int32')
|
||||
target = T.extra_ops.to_one_hot(target, nb_class=output.shape[-1])
|
||||
target = reshape(target, shape(output))
|
||||
return categorical_crossentropy(output, target, from_logits)
|
||||
|
||||
|
||||
def binary_crossentropy(output, target, from_logits=False):
|
||||
def binary_crossentropy(target, output, from_logits=False):
|
||||
if from_logits:
|
||||
output = T.nnet.sigmoid(output)
|
||||
# avoid numerical instability with _EPSILON clipping
|
||||
@@ -1814,7 +1824,7 @@ def conv2d(x, kernel, strides=(1, 1), padding='valid',
|
||||
padding: string, "same" or "valid".
|
||||
data_format: "channels_last" or "channels_first".
|
||||
Whether to use Theano or TensorFlow data format
|
||||
in inputs/kernels/ouputs.
|
||||
in inputs/kernels/outputs.
|
||||
"""
|
||||
if data_format is None:
|
||||
data_format = image_data_format()
|
||||
@@ -1858,7 +1868,10 @@ def conv2d_transpose(x, kernel, output_shape, strides=(1, 1),
|
||||
padding: string, "same" or "valid".
|
||||
data_format: "channels_last" or "channels_first".
|
||||
Whether to use Theano or TensorFlow data format
|
||||
in inputs/kernels/ouputs.
|
||||
in inputs/kernels/outputs.
|
||||
|
||||
# Raises
|
||||
ValueError: if using an even kernel size with padding 'same'.
|
||||
"""
|
||||
flip_filters = False
|
||||
if data_format is None:
|
||||
@@ -1877,6 +1890,12 @@ def conv2d_transpose(x, kernel, output_shape, strides=(1, 1),
|
||||
else:
|
||||
# Will only work if `kernel` is a shared variable.
|
||||
kernel_shape = kernel.eval().shape
|
||||
|
||||
if padding == 'same' and kernel_shape[0] % 2 == 0:
|
||||
raise ValueError('In `Conv2DTranspose`, with padding mode `same`, '
|
||||
'even kernel sizes are only supported with Tensorflow. '
|
||||
'With Theano, set `kernel_size` to an odd number.')
|
||||
|
||||
kernel_shape = _preprocess_conv2d_filter_shape(kernel_shape, data_format)
|
||||
|
||||
x = _preprocess_conv2d_input(x, data_format)
|
||||
@@ -1899,6 +1918,11 @@ def separable_conv2d(x, depthwise_kernel, pointwise_kernel, strides=(1, 1),
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def depthwise_conv2d(x, depthwise_kernel, strides=(1, 1), padding='valid',
|
||||
data_format=None, dilation_rate=(1, 1)):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def conv3d(x, kernel, strides=(1, 1, 1),
|
||||
padding='valid', data_format=None,
|
||||
dilation_rate=(1, 1, 1)):
|
||||
@@ -1910,7 +1934,7 @@ def conv3d(x, kernel, strides=(1, 1, 1),
|
||||
padding: string, "same" or "valid".
|
||||
data_format: "channels_last" or "channels_first".
|
||||
Whether to use Theano or TensorFlow data format
|
||||
in inputs/kernels/ouputs.
|
||||
in inputs/kernels/outputs.
|
||||
"""
|
||||
if data_format is None:
|
||||
data_format = image_data_format()
|
||||
@@ -1943,6 +1967,63 @@ def conv3d(x, kernel, strides=(1, 1, 1),
|
||||
return conv_out
|
||||
|
||||
|
||||
def conv3d_transpose(x, kernel, output_shape, strides=(1, 1, 1),
|
||||
padding='valid', data_format=None):
|
||||
"""3D deconvolution (transposed convolution).
|
||||
|
||||
# Arguments
|
||||
kernel: kernel tensor.
|
||||
output_shape: desired dimensions of output.
|
||||
strides: strides tuple.
|
||||
padding: string, "same" or "valid".
|
||||
data_format: "channels_last" or "channels_first".
|
||||
Whether to use Theano or TensorFlow data format
|
||||
in inputs/kernels/outputs.
|
||||
|
||||
# Raises
|
||||
ValueError: if using an even kernel size with padding 'same'.
|
||||
"""
|
||||
flip_filters = False
|
||||
if data_format is None:
|
||||
data_format = image_data_format()
|
||||
if data_format not in {'channels_first', 'channels_last'}:
|
||||
raise ValueError('Unknown data_format ' + data_format)
|
||||
|
||||
if data_format == 'channels_last':
|
||||
output_shape = (output_shape[0],
|
||||
output_shape[4],
|
||||
output_shape[1],
|
||||
output_shape[2],
|
||||
output_shape[3])
|
||||
|
||||
if hasattr(kernel, '_keras_shape'):
|
||||
kernel_shape = kernel._keras_shape
|
||||
else:
|
||||
# Will only work if `kernel` is a shared variable.
|
||||
kernel_shape = kernel.eval().shape
|
||||
|
||||
if padding == 'same' and kernel_shape[0] % 2 == 0:
|
||||
raise ValueError('In `Conv3DTranspose`, with padding mode `same`, '
|
||||
'even kernel sizes are only supported with Tensorflow. '
|
||||
'With Theano, set `kernel_size` to an odd number.')
|
||||
|
||||
kernel_shape = _preprocess_conv3d_filter_shape(kernel_shape, data_format)
|
||||
|
||||
x = _preprocess_conv3d_input(x, data_format)
|
||||
kernel = _preprocess_conv3d_kernel(kernel, data_format)
|
||||
|
||||
th_padding = _preprocess_padding(padding)
|
||||
op = T.nnet.abstract_conv.AbstractConv3d_gradInputs(imshp=None,
|
||||
kshp=kernel_shape,
|
||||
subsample=strides,
|
||||
border_mode=th_padding,
|
||||
filter_flip=not flip_filters)
|
||||
conv_out = op(kernel, x, output_shape[2:])
|
||||
conv_out = _postprocess_conv3d_output(conv_out, x, padding,
|
||||
kernel_shape, strides, data_format)
|
||||
return conv_out
|
||||
|
||||
|
||||
def pool2d(x, pool_size, strides=(1, 1), padding='valid',
|
||||
data_format=None, pool_mode='max'):
|
||||
if data_format is None:
|
||||
@@ -1961,9 +2042,6 @@ def pool2d(x, pool_size, strides=(1, 1), padding='valid',
|
||||
else:
|
||||
raise ValueError('Invalid border mode:', padding)
|
||||
|
||||
if data_format not in {'channels_first', 'channels_last'}:
|
||||
raise ValueError('Unknown data_format:', data_format)
|
||||
|
||||
if data_format == 'channels_last':
|
||||
x = x.dimshuffle((0, 3, 1, 2))
|
||||
|
||||
@@ -2011,8 +2089,6 @@ def pool3d(x, pool_size, strides=(1, 1, 1), padding='valid',
|
||||
padding = (0, 0, 0)
|
||||
else:
|
||||
raise ValueError('Invalid padding:', padding)
|
||||
if data_format not in {'channels_first', 'channels_last'}:
|
||||
raise ValueError('Unknown data_format:', data_format)
|
||||
|
||||
if data_format == 'channels_last':
|
||||
x = x.dimshuffle((0, 4, 1, 2, 3))
|
||||
@@ -2050,21 +2126,44 @@ def bias_add(x, bias, data_format=None):
|
||||
data_format = image_data_format()
|
||||
if data_format not in {'channels_first', 'channels_last'}:
|
||||
raise ValueError('Unknown data_format ' + str(data_format))
|
||||
if ndim(bias) != 1 and ndim(bias) != ndim(x) - 1:
|
||||
raise ValueError('Unexpected bias dimensions %d, '
|
||||
'expect to be 1 or %d dimensions'
|
||||
% (ndim(bias), ndim(x) - 1))
|
||||
bias_shape = tuple(bias.shape)
|
||||
if ndim(x) == 5:
|
||||
if data_format == 'channels_first':
|
||||
x += reshape(bias, (1, bias.shape[0], 1, 1, 1))
|
||||
if ndim(bias) == 1:
|
||||
x += reshape(bias, (1, bias_shape[0], 1, 1, 1))
|
||||
else:
|
||||
x += reshape(bias, (1, bias_shape[3]) + bias_shape[:3])
|
||||
elif data_format == 'channels_last':
|
||||
x += reshape(bias, (1, 1, 1, 1, bias.shape[0]))
|
||||
if ndim(bias) == 1:
|
||||
x += reshape(bias, (1, 1, 1, 1, bias_shape[0]))
|
||||
else:
|
||||
x += reshape(bias, (1,) + bias_shape)
|
||||
elif ndim(x) == 4:
|
||||
if data_format == 'channels_first':
|
||||
x += reshape(bias, (1, bias.shape[0], 1, 1))
|
||||
if ndim(bias) == 1:
|
||||
x += reshape(bias, (1, bias_shape[0], 1, 1))
|
||||
else:
|
||||
x += reshape(bias, (1, bias_shape[2]) + bias_shape[:2])
|
||||
elif data_format == 'channels_last':
|
||||
x += reshape(bias, (1, 1, 1, bias.shape[0]))
|
||||
if ndim(bias) == 1:
|
||||
x += reshape(bias, (1, 1, 1, bias_shape[0]))
|
||||
else:
|
||||
x += reshape(bias, (1,) + bias_shape)
|
||||
elif ndim(x) == 3:
|
||||
if data_format == 'channels_first':
|
||||
x += reshape(bias, (1, bias.shape[0], 1))
|
||||
if ndim(bias) == 1:
|
||||
x += reshape(bias, (1, bias_shape[0], 1))
|
||||
else:
|
||||
x += reshape(bias, (1, bias_shape[1], bias_shape[0]))
|
||||
elif data_format == 'channels_last':
|
||||
x += reshape(bias, (1, 1, bias.shape[0]))
|
||||
if ndim(bias) == 1:
|
||||
x += reshape(bias, (1, 1, bias_shape[0]))
|
||||
else:
|
||||
x += reshape(bias, (1,) + bias_shape)
|
||||
else:
|
||||
x += bias
|
||||
return x
|
||||
@@ -2255,9 +2354,8 @@ def foldl(fn, elems, initializer=None, name=None):
|
||||
|
||||
# We need to change the order of the arguments because theano accepts x as
|
||||
# first parameter and accumulator as second
|
||||
fn2 = lambda x, acc: fn(acc, x)
|
||||
|
||||
return theano.foldl(fn2, elems, initializer, name=name)[0]
|
||||
return theano.foldl(lambda x, acc: fn(acc, x),
|
||||
elems, initializer, name=name)[0]
|
||||
|
||||
|
||||
def foldr(fn, elems, initializer=None, name=None):
|
||||
@@ -2279,6 +2377,73 @@ def foldr(fn, elems, initializer=None, name=None):
|
||||
|
||||
# We need to change the order of the arguments because theano accepts x as
|
||||
# first parameter and accumulator as second
|
||||
fn2 = lambda x, acc: fn(acc, x)
|
||||
return theano.foldr(lambda x, acc: fn(acc, x),
|
||||
elems, initializer, name=name)[0]
|
||||
|
||||
return theano.foldr(fn2, elems, initializer, name=name)[0]
|
||||
|
||||
def local_conv1d(inputs, kernel, kernel_size, strides, data_format=None):
|
||||
if data_format is None:
|
||||
data_format = image_data_format()
|
||||
if data_format not in {'channels_first', 'channels_last'}:
|
||||
raise ValueError('Unknown data_format ' + str(data_format))
|
||||
|
||||
stride = strides[0]
|
||||
kernel_shape = int_shape(kernel)
|
||||
output_length, feature_dim, filters = kernel_shape
|
||||
|
||||
xs = []
|
||||
for i in range(output_length):
|
||||
slice_length = slice(i * stride,
|
||||
i * stride + kernel_size[0])
|
||||
xs.append(reshape(inputs[:, slice_length, :],
|
||||
(1, -1, feature_dim)))
|
||||
x_aggregate = concatenate(xs, axis=0)
|
||||
# Shape: `(output_length, batch_size, filters)`.
|
||||
output = batch_dot(x_aggregate, kernel)
|
||||
return permute_dimensions(output, (1, 0, 2))
|
||||
|
||||
|
||||
def local_conv2d(inputs, kernel, kernel_size, strides, output_shape, data_format=None):
|
||||
if data_format is None:
|
||||
data_format = image_data_format()
|
||||
if data_format not in {'channels_first', 'channels_last'}:
|
||||
raise ValueError('Unknown data_format ' + str(data_format))
|
||||
|
||||
stride_row, stride_col = strides
|
||||
output_row, output_col = output_shape
|
||||
kernel_shape = int_shape(kernel)
|
||||
_, feature_dim, filters = kernel_shape
|
||||
|
||||
if data_format == 'channels_first':
|
||||
output = []
|
||||
for i in range(output_row):
|
||||
for j in range(output_col):
|
||||
slice_row = slice(i * stride_row,
|
||||
i * stride_row + kernel_size[0])
|
||||
slice_col = slice(j * stride_col,
|
||||
j * stride_col + kernel_size[1])
|
||||
x_flatten = reshape(inputs[:, :, slice_row, slice_col],
|
||||
(1, -1, feature_dim))
|
||||
output.append(dot(x_flatten,
|
||||
kernel[i * output_col + j, :, :]))
|
||||
output = concatenate(output, axis=0)
|
||||
output = reshape(output,
|
||||
(output_row, output_col, -1, filters))
|
||||
output = permute_dimensions(output, (2, 3, 0, 1))
|
||||
else:
|
||||
xs = []
|
||||
for i in range(output_row):
|
||||
for j in range(output_col):
|
||||
slice_row = slice(i * stride_row,
|
||||
i * stride_row + kernel_size[0])
|
||||
slice_col = slice(j * stride_col,
|
||||
j * stride_col + kernel_size[1])
|
||||
xs.append(reshape(inputs[:, slice_row, slice_col, :],
|
||||
(1, -1, feature_dim)))
|
||||
|
||||
x_aggregate = concatenate(xs, axis=0)
|
||||
output = batch_dot(x_aggregate, kernel)
|
||||
output = reshape(output,
|
||||
(output_row, output_col, -1, filters))
|
||||
output = permute_dimensions(output, (2, 0, 1, 3))
|
||||
return output
|
||||
|
||||
+30
-24
@@ -490,8 +490,11 @@ class EarlyStopping(Callback):
|
||||
def on_epoch_end(self, epoch, logs=None):
|
||||
current = logs.get(self.monitor)
|
||||
if current is None:
|
||||
warnings.warn('Early stopping requires %s available!' %
|
||||
(self.monitor), RuntimeWarning)
|
||||
warnings.warn(
|
||||
'Early stopping conditioned on metric `%s` '
|
||||
'which is not available. Available metrics are: %s' %
|
||||
(self.monitor, ','.join(list(logs.keys()))), RuntimeWarning
|
||||
)
|
||||
|
||||
if self.monitor_op(current - self.min_delta, self.best):
|
||||
self.best = current
|
||||
@@ -520,8 +523,6 @@ class RemoteMonitor(Callback):
|
||||
path: String; path relative to `root` to which the events will be sent.
|
||||
field: String; JSON field under which the data will be stored.
|
||||
headers: Dictionary; optional custom HTTP headers.
|
||||
Defaults to:
|
||||
`{'Accept': 'application/json', 'Content-Type': 'application/json'}`
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
@@ -530,9 +531,7 @@ class RemoteMonitor(Callback):
|
||||
field='data',
|
||||
headers=None):
|
||||
super(RemoteMonitor, self).__init__()
|
||||
if headers is None:
|
||||
headers = {'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'}
|
||||
|
||||
self.root = root
|
||||
self.path = path
|
||||
self.field = field
|
||||
@@ -582,20 +581,19 @@ class LearningRateScheduler(Callback):
|
||||
class TensorBoard(Callback):
|
||||
"""Tensorboard basic visualizations.
|
||||
|
||||
[TensorBoard](https://www.tensorflow.org/get_started/summaries_and_tensorboard)
|
||||
is a visualization tool provided with TensorFlow.
|
||||
|
||||
This callback writes a log for TensorBoard, which allows
|
||||
you to visualize dynamic graphs of your training and test
|
||||
metrics, as well as activation histograms for the different
|
||||
layers in your model.
|
||||
|
||||
TensorBoard is a visualization tool provided with TensorFlow.
|
||||
|
||||
If you have installed TensorFlow with pip, you should be able
|
||||
to launch TensorBoard from the command line:
|
||||
```
|
||||
tensorboard --logdir=/full_path_to_your_logs
|
||||
```
|
||||
You can find more information about TensorBoard
|
||||
[here](https://www.tensorflow.org/get_started/summaries_and_tensorboard).
|
||||
|
||||
# Arguments
|
||||
log_dir: the path of the directory where to save the log
|
||||
@@ -655,11 +653,12 @@ class TensorBoard(Callback):
|
||||
for layer in self.model.layers:
|
||||
|
||||
for weight in layer.weights:
|
||||
tf.summary.histogram(weight.name, weight)
|
||||
mapped_weight_name = weight.name.replace(':', '_')
|
||||
tf.summary.histogram(mapped_weight_name, weight)
|
||||
if self.write_grads:
|
||||
grads = model.optimizer.get_gradients(model.total_loss,
|
||||
weight)
|
||||
tf.summary.histogram('{}_grad'.format(weight.name), grads)
|
||||
tf.summary.histogram('{}_grad'.format(mapped_weight_name), grads)
|
||||
if self.write_images:
|
||||
w_img = tf.squeeze(weight)
|
||||
shape = K.int_shape(w_img)
|
||||
@@ -692,7 +691,7 @@ class TensorBoard(Callback):
|
||||
|
||||
shape = K.int_shape(w_img)
|
||||
assert len(shape) == 4 and shape[-1] in [1, 3, 4]
|
||||
tf.summary.image(weight.name, w_img)
|
||||
tf.summary.image(mapped_weight_name, w_img)
|
||||
|
||||
if hasattr(layer, 'output'):
|
||||
tf.summary.histogram('{}_out'.format(layer.name),
|
||||
@@ -874,8 +873,12 @@ class ReduceLROnPlateau(Callback):
|
||||
logs['lr'] = K.get_value(self.model.optimizer.lr)
|
||||
current = logs.get(self.monitor)
|
||||
if current is None:
|
||||
warnings.warn('Learning Rate Plateau Reducing requires %s available!' %
|
||||
self.monitor, RuntimeWarning)
|
||||
warnings.warn(
|
||||
'Reduce LR on plateau conditioned on metric `%s` '
|
||||
'which is not available. Available metrics are: %s' %
|
||||
(self.monitor, ','.join(list(logs.keys()))), RuntimeWarning
|
||||
)
|
||||
|
||||
else:
|
||||
if self.in_cooldown():
|
||||
self.cooldown_counter -= 1
|
||||
@@ -973,7 +976,7 @@ class CSVLogger(Callback):
|
||||
|
||||
|
||||
class LambdaCallback(Callback):
|
||||
"""Callback for creating simple, custom callbacks on-the-fly.
|
||||
r"""Callback for creating simple, custom callbacks on-the-fly.
|
||||
|
||||
This callback is constructed with anonymous functions that will be called
|
||||
at the appropriate time. Note that the callbacks expects positional
|
||||
@@ -1000,12 +1003,15 @@ class LambdaCallback(Callback):
|
||||
batch_print_callback = LambdaCallback(
|
||||
on_batch_begin=lambda batch,logs: print(batch))
|
||||
|
||||
# Plot the loss after every epoch.
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
plot_loss_callback = LambdaCallback(
|
||||
on_epoch_end=lambda epoch, logs: plt.plot(np.arange(epoch),
|
||||
logs['loss']))
|
||||
# Stream the epoch loss to a file in JSON format. The file content
|
||||
# is not well-formed JSON but rather has a JSON object per line.
|
||||
import json
|
||||
json_log = open('loss_log.json', mode='wt', buffering=1)
|
||||
json_logging_callback = LambdaCallback(
|
||||
on_epoch_end=lambda epoch, logs: json_log.write(
|
||||
json.dumps({'epoch': epoch, 'loss': logs['loss']}) + '\n'),
|
||||
on_train_end=lambda logs: json_log.close()
|
||||
)
|
||||
|
||||
# Terminate some processes after having finished model training.
|
||||
processes = ...
|
||||
@@ -1015,7 +1021,7 @@ class LambdaCallback(Callback):
|
||||
|
||||
model.fit(...,
|
||||
callbacks=[batch_print_callback,
|
||||
plot_loss_callback,
|
||||
json_logging_callback,
|
||||
cleanup_callback])
|
||||
```
|
||||
"""
|
||||
|
||||
@@ -27,7 +27,7 @@ class MaxNorm(Constraint):
|
||||
has shape `(input_dim, output_dim)`,
|
||||
set `axis` to `0` to constrain each weight vector
|
||||
of length `(input_dim,)`.
|
||||
In a `Convolution2D` layer with `data_format="channels_last"`,
|
||||
In a `Conv2D` layer with `data_format="channels_last"`,
|
||||
the weight tensor has shape
|
||||
`(rows, cols, input_depth, output_depth)`,
|
||||
set `axis` to `[0, 1, 2]`
|
||||
@@ -58,7 +58,7 @@ class NonNeg(Constraint):
|
||||
"""
|
||||
|
||||
def __call__(self, w):
|
||||
w *= K.cast(w >= 0., K.floatx())
|
||||
w *= K.cast(K.greater_equal(w, 0.), K.floatx())
|
||||
return w
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ class UnitNorm(Constraint):
|
||||
has shape `(input_dim, output_dim)`,
|
||||
set `axis` to `0` to constrain each weight vector
|
||||
of length `(input_dim,)`.
|
||||
In a `Convolution2D` layer with `data_format="channels_last"`,
|
||||
In a `Conv2D` layer with `data_format="channels_last"`,
|
||||
the weight tensor has shape
|
||||
`(rows, cols, input_depth, output_depth)`,
|
||||
set `axis` to `[0, 1, 2]`
|
||||
@@ -112,7 +112,7 @@ class MinMaxNorm(Constraint):
|
||||
has shape `(input_dim, output_dim)`,
|
||||
set `axis` to `0` to constrain each weight vector
|
||||
of length `(input_dim,)`.
|
||||
In a `Convolution2D` layer with `dim_ordering="tf"`,
|
||||
In a `Conv2D` layer with `data_format="channels_last"`,
|
||||
the weight tensor has shape
|
||||
`(rows, cols, input_depth, output_depth)`,
|
||||
set `axis` to `[0, 1, 2]`
|
||||
|
||||
@@ -16,7 +16,9 @@ def load_data(path='boston_housing.npz', seed=113, test_split=0.2):
|
||||
Tuple of Numpy arrays: `(x_train, y_train), (x_test, y_test)`.
|
||||
"""
|
||||
assert 0 <= test_split < 1
|
||||
path = get_file(path, origin='https://s3.amazonaws.com/keras-datasets/boston_housing.npz')
|
||||
path = get_file(path,
|
||||
origin='https://s3.amazonaws.com/keras-datasets/boston_housing.npz',
|
||||
file_hash='f553886a1f8d56431e820c5b82552d9d95cfcb96d1e678153f8839538947dff5')
|
||||
f = np.load(path)
|
||||
x = f['x']
|
||||
y = f['y']
|
||||
|
||||
@@ -19,7 +19,7 @@ def load_data(label_mode='fine'):
|
||||
ValueError: in case of invalid `label_mode`.
|
||||
"""
|
||||
if label_mode not in ['fine', 'coarse']:
|
||||
raise ValueError('label_mode must be one of "fine" "coarse".')
|
||||
raise ValueError('`label_mode` must be one of `"fine"`, `"coarse"`.')
|
||||
|
||||
dirname = 'cifar-100-python'
|
||||
origin = 'http://www.cs.toronto.edu/~kriz/cifar-100-python.tar.gz'
|
||||
|
||||
+12
-31
@@ -1,5 +1,6 @@
|
||||
from __future__ import absolute_import
|
||||
from ..utils.data_utils import get_file
|
||||
from ..preprocessing.sequence import _remove_long_seq
|
||||
from six.moves import zip
|
||||
import numpy as np
|
||||
import json
|
||||
@@ -16,7 +17,7 @@ def load_data(path='imdb.npz', num_words=None, skip_top=0,
|
||||
num_words: max number of words to include. Words are ranked
|
||||
by how often they occur (in the training set) and only
|
||||
the most frequent words are kept
|
||||
skip_top: skip the top N most frequently occuring words
|
||||
skip_top: skip the top N most frequently occurring words
|
||||
(which may not be informative).
|
||||
maxlen: truncate sequences after this length.
|
||||
seed: random seed for sample shuffling.
|
||||
@@ -47,14 +48,10 @@ def load_data(path='imdb.npz', num_words=None, skip_top=0,
|
||||
if kwargs:
|
||||
raise TypeError('Unrecognized keyword arguments: ' + str(kwargs))
|
||||
|
||||
path = get_file(path,
|
||||
origin='https://s3.amazonaws.com/text-datasets/imdb.npz')
|
||||
f = np.load(path)
|
||||
x_train = f['x_train']
|
||||
labels_train = f['y_train']
|
||||
x_test = f['x_test']
|
||||
labels_test = f['y_test']
|
||||
f.close()
|
||||
path = get_file(path, origin='https://s3.amazonaws.com/text-datasets/imdb.npz')
|
||||
with np.load(path) as f:
|
||||
x_train, labels_train = f['x_train'], f['y_train']
|
||||
x_test, labels_test = f['x_test'], f['y_test']
|
||||
|
||||
np.random.seed(seed)
|
||||
np.random.shuffle(x_train)
|
||||
@@ -75,14 +72,7 @@ def load_data(path='imdb.npz', num_words=None, skip_top=0,
|
||||
xs = [[w + index_from for w in x] for x in xs]
|
||||
|
||||
if maxlen:
|
||||
new_xs = []
|
||||
new_labels = []
|
||||
for x, y in zip(xs, labels):
|
||||
if len(x) < maxlen:
|
||||
new_xs.append(x)
|
||||
new_labels.append(y)
|
||||
xs = new_xs
|
||||
labels = new_labels
|
||||
xs, labels = _remove_long_seq(maxlen, xs, labels)
|
||||
if not xs:
|
||||
raise ValueError('After filtering for sequences shorter than maxlen=' +
|
||||
str(maxlen) + ', no sequence was kept. '
|
||||
@@ -94,22 +84,13 @@ def load_data(path='imdb.npz', num_words=None, skip_top=0,
|
||||
# reserve 'index_from' (=3 by default) characters:
|
||||
# 0 (padding), 1 (start), 2 (OOV)
|
||||
if oov_char is not None:
|
||||
xs = [[oov_char if (w >= num_words or w < skip_top) else w for w in x] for x in xs]
|
||||
xs = [[w if (skip_top <= w < num_words) else oov_char for w in x] for x in xs]
|
||||
else:
|
||||
new_xs = []
|
||||
for x in xs:
|
||||
nx = []
|
||||
for w in x:
|
||||
if skip_top <= w < num_words:
|
||||
nx.append(w)
|
||||
new_xs.append(nx)
|
||||
xs = new_xs
|
||||
xs = [[w for w in x if (skip_top <= w < num_words)] for x in xs]
|
||||
|
||||
x_train = np.array(xs[:len(x_train)])
|
||||
y_train = np.array(labels[:len(x_train)])
|
||||
|
||||
x_test = np.array(xs[len(x_train):])
|
||||
y_test = np.array(labels[len(x_train):])
|
||||
idx = len(x_train)
|
||||
x_train, y_train = np.array(xs[:idx]), np.array(labels[:idx])
|
||||
x_test, y_test = np.array(xs[idx:]), np.array(labels[idx:])
|
||||
|
||||
return (x_train, y_train), (x_test, y_test)
|
||||
|
||||
|
||||
@@ -14,9 +14,7 @@ def load_data(path='mnist.npz'):
|
||||
"""
|
||||
path = get_file(path, origin='https://s3.amazonaws.com/img-datasets/mnist.npz')
|
||||
f = np.load(path)
|
||||
x_train = f['x_train']
|
||||
y_train = f['y_train']
|
||||
x_test = f['x_test']
|
||||
y_test = f['y_test']
|
||||
x_train, y_train = f['x_train'], f['y_train']
|
||||
x_test, y_test = f['x_test'], f['y_test']
|
||||
f.close()
|
||||
return (x_train, y_train), (x_test, y_test)
|
||||
|
||||
+10
-27
@@ -1,6 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import
|
||||
from ..utils.data_utils import get_file
|
||||
from ..preprocessing.sequence import _remove_long_seq
|
||||
from six.moves import zip
|
||||
import numpy as np
|
||||
import json
|
||||
@@ -17,7 +18,7 @@ def load_data(path='reuters.npz', num_words=None, skip_top=0,
|
||||
num_words: max number of words to include. Words are ranked
|
||||
by how often they occur (in the training set) and only
|
||||
the most frequent words are kept
|
||||
skip_top: skip the top N most frequently occuring words
|
||||
skip_top: skip the top N most frequently occurring words
|
||||
(which may not be informative).
|
||||
maxlen: truncate sequences after this length.
|
||||
test_split: Fraction of the dataset to be used as test data.
|
||||
@@ -46,10 +47,8 @@ def load_data(path='reuters.npz', num_words=None, skip_top=0,
|
||||
raise TypeError('Unrecognized keyword arguments: ' + str(kwargs))
|
||||
|
||||
path = get_file(path, origin='https://s3.amazonaws.com/text-datasets/reuters.npz')
|
||||
npzfile = np.load(path)
|
||||
xs = npzfile['x']
|
||||
labels = npzfile['y']
|
||||
npzfile.close()
|
||||
with np.load(path) as f:
|
||||
xs, labels = f['x'], f['y']
|
||||
|
||||
np.random.seed(seed)
|
||||
np.random.shuffle(xs)
|
||||
@@ -62,14 +61,7 @@ def load_data(path='reuters.npz', num_words=None, skip_top=0,
|
||||
xs = [[w + index_from for w in x] for x in xs]
|
||||
|
||||
if maxlen:
|
||||
new_xs = []
|
||||
new_labels = []
|
||||
for x, y in zip(xs, labels):
|
||||
if len(x) < maxlen:
|
||||
new_xs.append(x)
|
||||
new_labels.append(y)
|
||||
xs = new_xs
|
||||
labels = new_labels
|
||||
xs, labels = _remove_long_seq(maxlen, xs, labels)
|
||||
|
||||
if not num_words:
|
||||
num_words = max([max(x) for x in xs])
|
||||
@@ -78,22 +70,13 @@ def load_data(path='reuters.npz', num_words=None, skip_top=0,
|
||||
# reserve 'index_from' (=3 by default) characters:
|
||||
# 0 (padding), 1 (start), 2 (OOV)
|
||||
if oov_char is not None:
|
||||
xs = [[oov_char if (w >= num_words or w < skip_top) else w for w in x] for x in xs]
|
||||
xs = [[w if (skip_top <= w < num_words) else oov_char for w in x] for x in xs]
|
||||
else:
|
||||
new_xs = []
|
||||
for x in xs:
|
||||
nx = []
|
||||
for w in x:
|
||||
if skip_top <= w < num_words:
|
||||
nx.append(w)
|
||||
new_xs.append(nx)
|
||||
xs = new_xs
|
||||
xs = [[w for w in x if (skip_top <= w < num_words)] for x in xs]
|
||||
|
||||
x_train = np.array(xs[:int(len(xs) * (1 - test_split))])
|
||||
y_train = np.array(labels[:int(len(xs) * (1 - test_split))])
|
||||
|
||||
x_test = np.array(xs[int(len(xs) * (1 - test_split)):])
|
||||
y_test = np.array(labels[int(len(xs) * (1 - test_split)):])
|
||||
idx = int(len(xs) * (1 - test_split))
|
||||
x_train, y_train = np.array(xs[:idx]), np.array(labels[:idx])
|
||||
x_test, y_test = np.array(xs[idx:]), np.array(labels[idx:])
|
||||
|
||||
return (x_train, y_train), (x_test, y_test)
|
||||
|
||||
|
||||
+74
-15
@@ -10,13 +10,13 @@ import warnings
|
||||
import copy
|
||||
import os
|
||||
import re
|
||||
import inspect
|
||||
from six.moves import zip
|
||||
|
||||
from .. import backend as K
|
||||
from .. import initializers
|
||||
from ..utils.io_utils import ask_to_proceed_with_overwrite
|
||||
from ..utils.layer_utils import print_summary as print_layer_summary
|
||||
from ..utils.generic_utils import has_arg
|
||||
from ..utils import conv_utils
|
||||
from ..legacy import interfaces
|
||||
|
||||
@@ -584,7 +584,7 @@ class Layer(object):
|
||||
user_kwargs = copy.copy(kwargs)
|
||||
if not _is_all_none(previous_mask):
|
||||
# The previous layer generated a mask.
|
||||
if 'mask' in inspect.getargspec(self.call).args:
|
||||
if has_arg(self.call, 'mask'):
|
||||
if 'mask' not in kwargs:
|
||||
# If mask is explicitly passed to __call__,
|
||||
# we should override the default mask.
|
||||
@@ -744,7 +744,7 @@ class Layer(object):
|
||||
str(mask))
|
||||
# masking not explicitly supported: return None as mask
|
||||
return None
|
||||
# if masking is explictly supported, by default
|
||||
# if masking is explicitly supported, by default
|
||||
# carry over the input mask
|
||||
return mask
|
||||
|
||||
@@ -1088,7 +1088,7 @@ class Layer(object):
|
||||
if hasattr(self, '_losses'):
|
||||
self._losses += losses
|
||||
# Update self._per_input_updates
|
||||
if inputs == []:
|
||||
if isinstance(input, list) and inputs == []:
|
||||
inputs = None
|
||||
if inputs is not None:
|
||||
inputs_hash = _object_list_uid(inputs)
|
||||
@@ -1120,7 +1120,7 @@ class Layer(object):
|
||||
if hasattr(self, '_updates'):
|
||||
self._updates += updates
|
||||
# Update self._per_input_updates
|
||||
if inputs == []:
|
||||
if isinstance(inputs, list) and inputs == []:
|
||||
inputs = None
|
||||
if inputs is not None:
|
||||
inputs_hash = _object_list_uid(inputs)
|
||||
@@ -1298,7 +1298,8 @@ class InputLayer(Layer):
|
||||
raise ValueError('Only provide the input_shape OR '
|
||||
'batch_input_shape argument to '
|
||||
'InputLayer, not both at the same time.')
|
||||
if input_tensor is not None:
|
||||
if input_tensor is not None and batch_input_shape is None:
|
||||
# If input_tensor is set, and batch_input_shape is not set:
|
||||
# Attempt automatic input shape inference.
|
||||
try:
|
||||
batch_input_shape = K.int_shape(input_tensor)
|
||||
@@ -1467,6 +1468,9 @@ class Container(Layer):
|
||||
|
||||
# Class Methods
|
||||
from_config
|
||||
|
||||
# Raises
|
||||
TypeError: if input tensors are not Keras tensors from InputLayer objects
|
||||
"""
|
||||
|
||||
@interfaces.legacy_model_constructor_support
|
||||
@@ -1493,13 +1497,19 @@ class Container(Layer):
|
||||
self.outputs = [outputs]
|
||||
|
||||
# Check for redundancy in inputs.
|
||||
inputs_set = set(self.inputs)
|
||||
if len(inputs_set) != len(self.inputs):
|
||||
if len(set(self.inputs)) != len(self.inputs):
|
||||
raise ValueError('The list of inputs passed to the model '
|
||||
'is redundant. '
|
||||
'All inputs should only appear once.'
|
||||
' Found: ' + str(self.inputs))
|
||||
|
||||
# Check for redundancy in outputs.
|
||||
if len(set(self.outputs)) != len(self.outputs):
|
||||
warnings.warn('The list of outputs passed to the model '
|
||||
'is redundant. '
|
||||
'All outputs should only appear once.'
|
||||
' Found: ' + str(self.outputs))
|
||||
|
||||
# List of initial layers (1 to 1 mapping with self.inputs,
|
||||
# hence the same layer might appear twice)
|
||||
self.input_layers = []
|
||||
@@ -1519,7 +1529,7 @@ class Container(Layer):
|
||||
# every time the Container is called on a set on input tensors,
|
||||
# we compute the output tensors,
|
||||
# output masks and output shapes in one pass,
|
||||
# then cache them here. When of of these output is queried later,
|
||||
# then cache them here. When one of these output is queried later,
|
||||
# we retrieve it from there instead of recomputing it.
|
||||
self._output_mask_cache = {}
|
||||
self._output_tensor_cache = {}
|
||||
@@ -1601,6 +1611,15 @@ class Container(Layer):
|
||||
self._feed_inputs = []
|
||||
self._feed_input_shapes = []
|
||||
for i, layer in enumerate(self.input_layers):
|
||||
# Check that layer is an InputLayer.
|
||||
if not isinstance(layer, InputLayer):
|
||||
raise TypeError(
|
||||
'Input layers to a `Model` must be `InputLayer` objects. '
|
||||
'Received inputs: {}. '
|
||||
'Input {} (0-based) originates '
|
||||
'from layer type `{}`.'.format(inputs,
|
||||
i,
|
||||
layer.__class__.__name__))
|
||||
self.input_names.append(layer.name)
|
||||
if layer.is_placeholder:
|
||||
self._feed_input_names.append(layer.name)
|
||||
@@ -2187,7 +2206,7 @@ class Container(Layer):
|
||||
kwargs = {}
|
||||
if len(computed_data) == 1:
|
||||
computed_tensor, computed_mask = computed_data[0]
|
||||
if 'mask' in inspect.getargspec(layer.call).args:
|
||||
if has_arg(layer.call, 'mask'):
|
||||
if 'mask' not in kwargs:
|
||||
kwargs['mask'] = computed_mask
|
||||
output_tensors = _to_list(layer.call(computed_tensor, **kwargs))
|
||||
@@ -2198,7 +2217,7 @@ class Container(Layer):
|
||||
else:
|
||||
computed_tensors = [x[0] for x in computed_data]
|
||||
computed_masks = [x[1] for x in computed_data]
|
||||
if 'mask' in inspect.getargspec(layer.call).args:
|
||||
if has_arg(layer.call, 'mask'):
|
||||
if 'mask' not in kwargs:
|
||||
kwargs['mask'] = computed_masks
|
||||
output_tensors = _to_list(layer.call(computed_tensors, **kwargs))
|
||||
@@ -2618,10 +2637,25 @@ class Container(Layer):
|
||||
"""
|
||||
return yaml.dump(self._updated_config(), **kwargs)
|
||||
|
||||
def summary(self, line_length=None, positions=None):
|
||||
print_layer_summary(self,
|
||||
line_length=line_length,
|
||||
positions=positions)
|
||||
def summary(self, line_length=None, positions=None, print_fn=print):
|
||||
"""Prints a string summary of the network.
|
||||
|
||||
# Arguments
|
||||
line_length: Total length of printed lines
|
||||
(e.g. set this to adapt the display to different
|
||||
terminal window sizes).
|
||||
positions: Relative or absolute positions of log elements
|
||||
in each line. If not provided,
|
||||
defaults to `[.33, .55, .67, 1.]`.
|
||||
print_fn: Print function to use.
|
||||
It will be called on each line of the summary.
|
||||
You can set it to a custom function
|
||||
in order to capture the string summary.
|
||||
"""
|
||||
return print_layer_summary(self,
|
||||
line_length=line_length,
|
||||
positions=positions,
|
||||
print_fn=print_fn)
|
||||
|
||||
|
||||
def get_source_inputs(tensor, layer=None, node_index=None):
|
||||
@@ -2904,6 +2938,31 @@ def preprocess_weights_for_loading(layer, weights,
|
||||
(2, 3, 1, 0))
|
||||
weights = [kernel, recurrent_kernel, bias]
|
||||
|
||||
if layer.__class__.__name__ in ['Model', 'Sequential']:
|
||||
new_weights = []
|
||||
# trainable weights
|
||||
for sublayer in layer.layers:
|
||||
num_weights = len(sublayer.trainable_weights)
|
||||
if num_weights > 0:
|
||||
new_weights.extend(preprocess_weights_for_loading(
|
||||
layer=sublayer,
|
||||
weights=weights[:num_weights],
|
||||
original_keras_version=original_keras_version,
|
||||
original_backend=original_backend))
|
||||
weights = weights[num_weights:]
|
||||
|
||||
# non-trainable weights
|
||||
for sublayer in layer.layers:
|
||||
num_weights = len([l for l in sublayer.weights if l not in sublayer.trainable_weights])
|
||||
if num_weights > 0:
|
||||
new_weights.extend(preprocess_weights_for_loading(
|
||||
layer=sublayer,
|
||||
weights=weights[:num_weights],
|
||||
original_keras_version=original_keras_version,
|
||||
original_backend=original_backend))
|
||||
weights = weights[num_weights:]
|
||||
weights = new_weights
|
||||
|
||||
conv_layers = ['Conv1D',
|
||||
'Conv2D',
|
||||
'Conv3D',
|
||||
|
||||
+134
-177
@@ -4,12 +4,13 @@ from __future__ import absolute_import
|
||||
|
||||
import warnings
|
||||
import copy
|
||||
import time
|
||||
import numpy as np
|
||||
import multiprocessing
|
||||
import threading
|
||||
import six
|
||||
|
||||
from keras.utils import Sequence
|
||||
from keras.utils import GeneratorEnqueuer
|
||||
from keras.utils import OrderedEnqueuer
|
||||
|
||||
try:
|
||||
import queue
|
||||
except ImportError:
|
||||
@@ -100,7 +101,7 @@ def _standardize_input_data(data, names, shapes=None,
|
||||
if len(names) > 1:
|
||||
# Case: model expects multiple inputs but only received
|
||||
# a single Numpy array.
|
||||
raise ValueError('The model expects ' + str(len(names)) +
|
||||
raise ValueError('The model expects ' + str(len(names)) + ' ' +
|
||||
exception_prefix +
|
||||
' arrays, but only received one array. '
|
||||
'Found: array with shape ' + str(data.shape))
|
||||
@@ -199,7 +200,7 @@ def _standardize_sample_weights(sample_weight, output_names):
|
||||
'sample_weight')
|
||||
|
||||
|
||||
def _check_array_lengths(inputs, targets, weights):
|
||||
def _check_array_lengths(inputs, targets, weights=None):
|
||||
"""Does user input validation for numpy arrays.
|
||||
|
||||
# Arguments
|
||||
@@ -210,29 +211,34 @@ def _check_array_lengths(inputs, targets, weights):
|
||||
# Raises
|
||||
ValueError: in case of incorrectly formatted data.
|
||||
"""
|
||||
x_lengths = [x.shape[0] for x in inputs]
|
||||
y_lengths = [y.shape[0] for y in targets]
|
||||
w_lengths = [w.shape[0] for w in weights]
|
||||
set_x = set(x_lengths)
|
||||
def set_of_lengths(x):
|
||||
# return a set with the variation between
|
||||
# different shapes, with None => 0
|
||||
if x is None:
|
||||
return {0}
|
||||
else:
|
||||
return set([0 if y is None else y.shape[0] for y in x])
|
||||
|
||||
set_x = set_of_lengths(inputs)
|
||||
set_y = set_of_lengths(targets)
|
||||
set_w = set_of_lengths(weights)
|
||||
if len(set_x) > 1:
|
||||
raise ValueError('All input arrays (x) should have '
|
||||
'the same number of samples. Got array shapes: ' +
|
||||
str([x.shape for x in inputs]))
|
||||
set_y = set(y_lengths)
|
||||
if len(set_y) > 1:
|
||||
raise ValueError('All target arrays (y) should have '
|
||||
'the same number of samples. Got array shapes: ' +
|
||||
str([y.shape for y in targets]))
|
||||
set_w = set(w_lengths)
|
||||
if len(set_w) > 1:
|
||||
raise ValueError('All sample_weight arrays should have '
|
||||
'the same number of samples. Got array shapes: ' +
|
||||
str([w.shape for w in weights]))
|
||||
if set_x and set_y and list(set_x)[0] != list(set_y)[0]:
|
||||
raise ValueError('Input arrays should have '
|
||||
'the same number of samples as target arrays. '
|
||||
'Found ' + str(list(set_x)[0]) + ' input samples '
|
||||
'and ' + str(list(set_y)[0]) + ' target samples.')
|
||||
if len(set_w) > 1:
|
||||
raise ValueError('All sample_weight arrays should have '
|
||||
'the same number of samples. Got array shapes: ' +
|
||||
str([w.shape for w in weights]))
|
||||
if set_y and set_w and list(set_y)[0] != list(set_w)[0]:
|
||||
raise ValueError('Sample_weight arrays should have '
|
||||
'the same number of samples as target arrays. Got ' +
|
||||
@@ -241,7 +247,7 @@ def _check_array_lengths(inputs, targets, weights):
|
||||
|
||||
|
||||
def _check_loss_and_target_compatibility(targets, loss_fns, output_shapes):
|
||||
"""Does validation on the compatiblity of targets and loss functions.
|
||||
"""Does validation on the compatibility of targets and loss functions.
|
||||
|
||||
This helps prevent users from using loss functions incorrectly.
|
||||
|
||||
@@ -254,7 +260,7 @@ def _check_loss_and_target_compatibility(targets, loss_fns, output_shapes):
|
||||
ValueError: if a loss function or target array
|
||||
is incompatible with an output.
|
||||
"""
|
||||
key_losses = {'mean_square_error',
|
||||
key_losses = {'mean_squared_error',
|
||||
'binary_crossentropy',
|
||||
'categorical_crossentropy'}
|
||||
for y, loss, shape in zip(targets, loss_fns, output_shapes):
|
||||
@@ -386,21 +392,25 @@ def _slice_arrays(arrays, start=None, stop=None):
|
||||
# Returns
|
||||
A slice of the array(s).
|
||||
"""
|
||||
if isinstance(arrays, list):
|
||||
if arrays is None:
|
||||
return [None]
|
||||
elif isinstance(arrays, list):
|
||||
if hasattr(start, '__len__'):
|
||||
# hdf5 datasets only support list objects as indices
|
||||
if hasattr(start, 'shape'):
|
||||
start = start.tolist()
|
||||
return [x[start] for x in arrays]
|
||||
return [None if x is None else x[start] for x in arrays]
|
||||
else:
|
||||
return [x[start:stop] for x in arrays]
|
||||
return [None if x is None else x[start:stop] for x in arrays]
|
||||
else:
|
||||
if hasattr(start, '__len__'):
|
||||
if hasattr(start, 'shape'):
|
||||
start = start.tolist()
|
||||
return arrays[start]
|
||||
else:
|
||||
elif hasattr(start, '__getitem__'):
|
||||
return arrays[start:stop]
|
||||
else:
|
||||
return [None]
|
||||
|
||||
|
||||
def _weighted_masked_objective(fn):
|
||||
@@ -443,13 +453,12 @@ def _weighted_masked_objective(fn):
|
||||
# to the number of unmasked samples.
|
||||
score_array /= K.mean(mask)
|
||||
|
||||
# reduce score_array to same ndim as weight array
|
||||
ndim = K.ndim(score_array)
|
||||
weight_ndim = K.ndim(weights)
|
||||
score_array = K.mean(score_array, axis=list(range(weight_ndim, ndim)))
|
||||
|
||||
# apply sample weighting
|
||||
if weights is not None:
|
||||
# reduce score_array to same ndim as weight array
|
||||
ndim = K.ndim(score_array)
|
||||
weight_ndim = K.ndim(weights)
|
||||
score_array = K.mean(score_array, axis=list(range(weight_ndim, ndim)))
|
||||
score_array *= weights
|
||||
score_array /= K.mean(K.cast(K.not_equal(weights, 0), K.floatx()))
|
||||
return K.mean(score_array)
|
||||
@@ -562,7 +571,7 @@ def _standardize_weights(y, sample_weight=None, class_weight=None,
|
||||
return sample_weight
|
||||
elif isinstance(class_weight, dict):
|
||||
if len(y.shape) > 2:
|
||||
raise ValueError('class_weight not supported for '
|
||||
raise ValueError('`class_weight` not supported for '
|
||||
'3+ dimensional targets.')
|
||||
if y.shape[1] > 1:
|
||||
y_classes = y.argmax(axis=1)
|
||||
@@ -579,97 +588,6 @@ def _standardize_weights(y, sample_weight=None, class_weight=None,
|
||||
return np.ones((y.shape[0], y.shape[1]), dtype=K.floatx())
|
||||
|
||||
|
||||
class GeneratorEnqueuer(object):
|
||||
"""Builds a queue out of a data generator.
|
||||
|
||||
Used in `fit_generator`, `evaluate_generator`, `predict_generator`.
|
||||
|
||||
# Arguments
|
||||
generator: a generator function which endlessly yields data
|
||||
pickle_safe: use multiprocessing if True, otherwise threading
|
||||
"""
|
||||
|
||||
def __init__(self, generator, pickle_safe=False):
|
||||
self._generator = generator
|
||||
self._pickle_safe = pickle_safe
|
||||
self._threads = []
|
||||
self._stop_event = None
|
||||
self.queue = None
|
||||
|
||||
def start(self, workers=1, max_q_size=10, wait_time=0.05):
|
||||
"""Kicks off threads which add data from the generator into the queue.
|
||||
|
||||
# Arguments
|
||||
workers: number of worker threads
|
||||
max_q_size: queue size (when full, threads could block on put())
|
||||
wait_time: time to sleep in-between calls to put()
|
||||
"""
|
||||
|
||||
def data_generator_task():
|
||||
while not self._stop_event.is_set():
|
||||
try:
|
||||
if self._pickle_safe or self.queue.qsize() < max_q_size:
|
||||
generator_output = next(self._generator)
|
||||
self.queue.put(generator_output)
|
||||
else:
|
||||
time.sleep(wait_time)
|
||||
except Exception:
|
||||
self._stop_event.set()
|
||||
raise
|
||||
|
||||
try:
|
||||
if self._pickle_safe:
|
||||
self.queue = multiprocessing.Queue(maxsize=max_q_size)
|
||||
self._stop_event = multiprocessing.Event()
|
||||
else:
|
||||
self.queue = queue.Queue()
|
||||
self._stop_event = threading.Event()
|
||||
|
||||
for _ in range(workers):
|
||||
if self._pickle_safe:
|
||||
# Reset random seed else all children processes
|
||||
# share the same seed
|
||||
np.random.seed()
|
||||
thread = multiprocessing.Process(target=data_generator_task)
|
||||
thread.daemon = True
|
||||
else:
|
||||
thread = threading.Thread(target=data_generator_task)
|
||||
self._threads.append(thread)
|
||||
thread.start()
|
||||
except:
|
||||
self.stop()
|
||||
raise
|
||||
|
||||
def is_running(self):
|
||||
return self._stop_event is not None and not self._stop_event.is_set()
|
||||
|
||||
def stop(self, timeout=None):
|
||||
"""Stop running threads and wait for them to exit, if necessary.
|
||||
|
||||
Should be called by the same thread which called start().
|
||||
|
||||
# Arguments
|
||||
timeout: maximum time to wait on thread.join()
|
||||
"""
|
||||
if self.is_running():
|
||||
self._stop_event.set()
|
||||
|
||||
for thread in self._threads:
|
||||
if thread.is_alive():
|
||||
if self._pickle_safe:
|
||||
thread.terminate()
|
||||
else:
|
||||
thread.join(timeout)
|
||||
|
||||
if self._pickle_safe:
|
||||
if self.queue is not None:
|
||||
self.queue.close()
|
||||
|
||||
self._threads = []
|
||||
self._stop_event = None
|
||||
self.queue = None
|
||||
|
||||
|
||||
class Model(Container):
|
||||
"""The `Model` class adds training & evaluation routines to a `Container`.
|
||||
"""
|
||||
@@ -990,14 +908,8 @@ class Model(Container):
|
||||
self.test_function = None
|
||||
self.predict_function = None
|
||||
|
||||
# Collected trainable weights and sort them deterministically.
|
||||
# Collected trainable weights, sorted in topological order.
|
||||
trainable_weights = self.trainable_weights
|
||||
# Sort weights by name.
|
||||
if trainable_weights:
|
||||
if K.backend() == 'theano':
|
||||
trainable_weights.sort(key=lambda x: x.name if x.name else x.auto_name)
|
||||
else:
|
||||
trainable_weights.sort(key=lambda x: x.name)
|
||||
self._collected_trainable_weights = trainable_weights
|
||||
|
||||
def _make_train_function(self):
|
||||
@@ -1462,7 +1374,10 @@ class Model(Container):
|
||||
|
||||
elif validation_split and 0. < validation_split < 1.:
|
||||
do_validation = True
|
||||
split_at = int(len(x[0]) * (1. - validation_split))
|
||||
if hasattr(x[0], 'shape'):
|
||||
split_at = int(x[0].shape[0] * (1. - validation_split))
|
||||
else:
|
||||
split_at = int(len(x[0]) * (1. - validation_split))
|
||||
x, val_x = (_slice_arrays(x, 0, split_at), _slice_arrays(x, split_at))
|
||||
y, val_y = (_slice_arrays(y, 0, split_at), _slice_arrays(y, split_at))
|
||||
sample_weights, val_sample_weights = (
|
||||
@@ -1713,9 +1628,9 @@ class Model(Container):
|
||||
validation_data=None,
|
||||
validation_steps=None,
|
||||
class_weight=None,
|
||||
max_q_size=10,
|
||||
max_queue_size=10,
|
||||
workers=1,
|
||||
pickle_safe=False,
|
||||
use_multiprocessing=False,
|
||||
initial_epoch=0):
|
||||
"""Fits the model on data yielded batch-by-batch by a Python generator.
|
||||
|
||||
@@ -1723,8 +1638,14 @@ class Model(Container):
|
||||
For instance, this allows you to do real-time data augmentation
|
||||
on images on CPU in parallel to training your model on GPU.
|
||||
|
||||
The use of `keras.utils.Sequence` guarantees the ordering
|
||||
and guarantees the single use of every input per epoch when
|
||||
using `use_multiprocessing=True`.
|
||||
|
||||
# Arguments
|
||||
generator: a generator.
|
||||
generator: a generator or an instance of Sequence (keras.utils.Sequence)
|
||||
object in order to avoid duplicate data
|
||||
when using multiprocessing.
|
||||
The output of the generator must be either
|
||||
- a tuple (inputs, targets)
|
||||
- a tuple (inputs, targets, sample_weights).
|
||||
@@ -1749,10 +1670,10 @@ class Model(Container):
|
||||
to yield from `generator` before stopping.
|
||||
class_weight: dictionary mapping class indices to a weight
|
||||
for the class.
|
||||
max_q_size: maximum size for the generator queue
|
||||
max_queue_size: maximum size for the generator queue
|
||||
workers: maximum number of processes to spin up
|
||||
when using process based threading
|
||||
pickle_safe: if True, use process based threading.
|
||||
use_multiprocessing: if True, use process based threading.
|
||||
Note that because
|
||||
this implementation relies on multiprocessing,
|
||||
you should not pass
|
||||
@@ -1797,7 +1718,8 @@ class Model(Container):
|
||||
# python 2 has 'next', 3 has '__next__'
|
||||
# avoid any explicit version checks
|
||||
val_gen = (hasattr(validation_data, 'next') or
|
||||
hasattr(validation_data, '__next__'))
|
||||
hasattr(validation_data, '__next__') or
|
||||
isinstance(validation_data, Sequence))
|
||||
if val_gen and not validation_steps:
|
||||
raise ValueError('When using a generator for validation data, '
|
||||
'you must specify a value for '
|
||||
@@ -1836,7 +1758,7 @@ class Model(Container):
|
||||
elif len(validation_data) == 3:
|
||||
val_x, val_y, val_sample_weight = validation_data
|
||||
else:
|
||||
raise ValueError('validation_data should be a tuple '
|
||||
raise ValueError('`validation_data` should be a tuple '
|
||||
'`(val_x, val_y, val_sample_weight)` '
|
||||
'or `(val_x, val_y)`. Found: ' +
|
||||
str(validation_data))
|
||||
@@ -1847,11 +1769,25 @@ class Model(Container):
|
||||
val_data += [0.]
|
||||
for cbk in callbacks:
|
||||
cbk.validation_data = val_data
|
||||
is_sequence = isinstance(generator, Sequence)
|
||||
if not is_sequence and use_multiprocessing and workers > 1:
|
||||
warnings.warn(
|
||||
UserWarning('Using a generator with `use_multiprocessing=True`'
|
||||
' and multiple workers may duplicate your data.'
|
||||
' Please consider using the`keras.utils.Sequence'
|
||||
' class.'))
|
||||
enqueuer = None
|
||||
|
||||
try:
|
||||
enqueuer = GeneratorEnqueuer(generator, pickle_safe=pickle_safe)
|
||||
enqueuer.start(max_q_size=max_q_size, workers=workers)
|
||||
if is_sequence:
|
||||
enqueuer = OrderedEnqueuer(generator,
|
||||
use_multiprocessing=use_multiprocessing)
|
||||
else:
|
||||
enqueuer = GeneratorEnqueuer(generator,
|
||||
use_multiprocessing=use_multiprocessing,
|
||||
wait_time=wait_time)
|
||||
enqueuer.start(workers=workers, max_queue_size=max_queue_size)
|
||||
output_generator = enqueuer.get()
|
||||
|
||||
callback_model.stop_training = False
|
||||
while epoch < epochs:
|
||||
@@ -1859,16 +1795,10 @@ class Model(Container):
|
||||
steps_done = 0
|
||||
batch_index = 0
|
||||
while steps_done < steps_per_epoch:
|
||||
generator_output = None
|
||||
while enqueuer.is_running():
|
||||
if not enqueuer.queue.empty():
|
||||
generator_output = enqueuer.queue.get()
|
||||
break
|
||||
else:
|
||||
time.sleep(wait_time)
|
||||
generator_output = next(output_generator)
|
||||
|
||||
if not hasattr(generator_output, '__len__'):
|
||||
raise ValueError('output of generator should be '
|
||||
raise ValueError('Output of generator should be '
|
||||
'a tuple `(x, y, sample_weight)` '
|
||||
'or `(x, y)`. Found: ' +
|
||||
str(generator_output))
|
||||
@@ -1878,7 +1808,7 @@ class Model(Container):
|
||||
elif len(generator_output) == 3:
|
||||
x, y, sample_weight = generator_output
|
||||
else:
|
||||
raise ValueError('output of generator should be '
|
||||
raise ValueError('Output of generator should be '
|
||||
'a tuple `(x, y, sample_weight)` '
|
||||
'or `(x, y)`. Found: ' +
|
||||
str(generator_output))
|
||||
@@ -1916,9 +1846,9 @@ class Model(Container):
|
||||
val_outs = self.evaluate_generator(
|
||||
validation_data,
|
||||
validation_steps,
|
||||
max_q_size=max_q_size,
|
||||
max_queue_size=max_queue_size,
|
||||
workers=workers,
|
||||
pickle_safe=pickle_safe)
|
||||
use_multiprocessing=use_multiprocessing)
|
||||
else:
|
||||
# No need for try/except because
|
||||
# data has already been validated.
|
||||
@@ -1947,7 +1877,9 @@ class Model(Container):
|
||||
|
||||
@interfaces.legacy_generator_methods_support
|
||||
def evaluate_generator(self, generator, steps,
|
||||
max_q_size=10, workers=1, pickle_safe=False):
|
||||
max_queue_size=10,
|
||||
workers=1,
|
||||
use_multiprocessing=False):
|
||||
"""Evaluates the model on a data generator.
|
||||
|
||||
The generator should return the same kind of data
|
||||
@@ -1956,12 +1888,15 @@ class Model(Container):
|
||||
# Arguments
|
||||
generator: Generator yielding tuples (inputs, targets)
|
||||
or (inputs, targets, sample_weights)
|
||||
or an instance of Sequence (keras.utils.Sequence)
|
||||
object in order to avoid duplicate data
|
||||
when using multiprocessing.
|
||||
steps: Total number of steps (batches of samples)
|
||||
to yield from `generator` before stopping.
|
||||
max_q_size: maximum size for the generator queue
|
||||
max_queue_size: maximum size for the generator queue
|
||||
workers: maximum number of processes to spin up
|
||||
when using process based threading
|
||||
pickle_safe: if True, use process based threading.
|
||||
use_multiprocessing: if True, use process based threading.
|
||||
Note that because
|
||||
this implementation relies on multiprocessing,
|
||||
you should not pass
|
||||
@@ -1985,23 +1920,30 @@ class Model(Container):
|
||||
wait_time = 0.01
|
||||
all_outs = []
|
||||
batch_sizes = []
|
||||
is_sequence = isinstance(generator, Sequence)
|
||||
if not is_sequence and use_multiprocessing and workers > 1:
|
||||
warnings.warn(
|
||||
UserWarning('Using a generator with `use_multiprocessing=True`'
|
||||
' and multiple workers may duplicate your data.'
|
||||
' Please consider using the`keras.utils.Sequence'
|
||||
' class.'))
|
||||
enqueuer = None
|
||||
|
||||
try:
|
||||
enqueuer = GeneratorEnqueuer(generator, pickle_safe=pickle_safe)
|
||||
enqueuer.start(workers=workers, max_q_size=max_q_size)
|
||||
if is_sequence:
|
||||
enqueuer = OrderedEnqueuer(generator,
|
||||
use_multiprocessing=use_multiprocessing)
|
||||
else:
|
||||
enqueuer = GeneratorEnqueuer(generator,
|
||||
use_multiprocessing=use_multiprocessing,
|
||||
wait_time=wait_time)
|
||||
enqueuer.start(workers=workers, max_queue_size=max_queue_size)
|
||||
output_generator = enqueuer.get()
|
||||
|
||||
while steps_done < steps:
|
||||
generator_output = None
|
||||
while enqueuer.is_running():
|
||||
if not enqueuer.queue.empty():
|
||||
generator_output = enqueuer.queue.get()
|
||||
break
|
||||
else:
|
||||
time.sleep(wait_time)
|
||||
|
||||
generator_output = next(output_generator)
|
||||
if not hasattr(generator_output, '__len__'):
|
||||
raise ValueError('output of generator should be a tuple '
|
||||
raise ValueError('Output of generator should be a tuple '
|
||||
'(x, y, sample_weight) '
|
||||
'or (x, y). Found: ' +
|
||||
str(generator_output))
|
||||
@@ -2011,7 +1953,7 @@ class Model(Container):
|
||||
elif len(generator_output) == 3:
|
||||
x, y, sample_weight = generator_output
|
||||
else:
|
||||
raise ValueError('output of generator should be a tuple '
|
||||
raise ValueError('Output of generator should be a tuple '
|
||||
'(x, y, sample_weight) '
|
||||
'or (x, y). Found: ' +
|
||||
str(generator_output))
|
||||
@@ -2023,6 +1965,9 @@ class Model(Container):
|
||||
batch_size = len(list(x.values())[0])
|
||||
else:
|
||||
batch_size = len(x)
|
||||
if batch_size == 0:
|
||||
raise ValueError('Received an empty batch. '
|
||||
'Batches should at least contain one item.')
|
||||
all_outs.append(outs)
|
||||
|
||||
steps_done += 1
|
||||
@@ -2044,21 +1989,26 @@ class Model(Container):
|
||||
|
||||
@interfaces.legacy_generator_methods_support
|
||||
def predict_generator(self, generator, steps,
|
||||
max_q_size=10, workers=1,
|
||||
pickle_safe=False, verbose=0):
|
||||
max_queue_size=10,
|
||||
workers=1,
|
||||
use_multiprocessing=False,
|
||||
verbose=0):
|
||||
"""Generates predictions for the input samples from a data generator.
|
||||
|
||||
The generator should return the same kind of data as accepted by
|
||||
`predict_on_batch`.
|
||||
|
||||
# Arguments
|
||||
generator: Generator yielding batches of input samples.
|
||||
generator: Generator yielding batches of input samples
|
||||
or an instance of Sequence (keras.utils.Sequence)
|
||||
object in order to avoid duplicate data
|
||||
when using multiprocessing.
|
||||
steps: Total number of steps (batches of samples)
|
||||
to yield from `generator` before stopping.
|
||||
max_q_size: Maximum size for the generator queue.
|
||||
max_queue_size: Maximum size for the generator queue.
|
||||
workers: Maximum number of processes to spin up
|
||||
when using process based threading
|
||||
pickle_safe: If `True`, use process based threading.
|
||||
use_multiprocessing: If `True`, use process based threading.
|
||||
Note that because
|
||||
this implementation relies on multiprocessing,
|
||||
you should not pass
|
||||
@@ -2079,24 +2029,31 @@ class Model(Container):
|
||||
steps_done = 0
|
||||
wait_time = 0.01
|
||||
all_outs = []
|
||||
is_sequence = isinstance(generator, Sequence)
|
||||
if not is_sequence and use_multiprocessing and workers > 1:
|
||||
warnings.warn(
|
||||
UserWarning('Using a generator with `use_multiprocessing=True`'
|
||||
' and multiple workers may duplicate your data.'
|
||||
' Please consider using the`keras.utils.Sequence'
|
||||
' class.'))
|
||||
enqueuer = None
|
||||
|
||||
try:
|
||||
enqueuer = GeneratorEnqueuer(generator, pickle_safe=pickle_safe)
|
||||
enqueuer.start(workers=workers, max_q_size=max_q_size)
|
||||
if is_sequence:
|
||||
enqueuer = OrderedEnqueuer(generator,
|
||||
use_multiprocessing=use_multiprocessing)
|
||||
else:
|
||||
enqueuer = GeneratorEnqueuer(generator,
|
||||
use_multiprocessing=use_multiprocessing,
|
||||
wait_time=wait_time)
|
||||
enqueuer.start(workers=workers, max_queue_size=max_queue_size)
|
||||
output_generator = enqueuer.get()
|
||||
|
||||
if verbose == 1:
|
||||
progbar = Progbar(target=steps)
|
||||
|
||||
while steps_done < steps:
|
||||
generator_output = None
|
||||
while enqueuer.is_running():
|
||||
if not enqueuer.queue.empty():
|
||||
generator_output = enqueuer.queue.get()
|
||||
break
|
||||
else:
|
||||
time.sleep(wait_time)
|
||||
|
||||
generator_output = next(output_generator)
|
||||
if isinstance(generator_output, tuple):
|
||||
# Compatibility with the generators
|
||||
# used for training.
|
||||
@@ -2105,7 +2062,7 @@ class Model(Container):
|
||||
elif len(generator_output) == 3:
|
||||
x, _, _ = generator_output
|
||||
else:
|
||||
raise ValueError('output of generator should be '
|
||||
raise ValueError('Output of generator should be '
|
||||
'a tuple `(x, y, sample_weight)` '
|
||||
'or `(x, y)`. Found: ' +
|
||||
str(generator_output))
|
||||
|
||||
@@ -371,6 +371,29 @@ def he_normal(seed=None):
|
||||
seed=seed)
|
||||
|
||||
|
||||
def lecun_normal(seed=None):
|
||||
"""LeCun normal initializer.
|
||||
|
||||
It draws samples from a truncated normal distribution centered on 0
|
||||
with `stddev = sqrt(1 / fan_in)`
|
||||
where `fan_in` is the number of input units in the weight tensor.
|
||||
|
||||
# Arguments
|
||||
seed: A Python integer. Used to seed the random generator.
|
||||
|
||||
# Returns
|
||||
An initializer.
|
||||
|
||||
# References
|
||||
- [Self-Normalizing Neural Networks](https://arxiv.org/abs/1706.02515)
|
||||
- [Efficient Backprop](http://yann.lecun.com/exdb/publis/pdf/lecun-98b.pdf)
|
||||
"""
|
||||
return VarianceScaling(scale=1.,
|
||||
mode='fan_in',
|
||||
distribution='normal',
|
||||
seed=seed)
|
||||
|
||||
|
||||
def he_uniform(seed=None):
|
||||
"""He uniform variance scaling initializer.
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ class LeakyReLU(Layer):
|
||||
return K.relu(inputs, alpha=self.alpha)
|
||||
|
||||
def get_config(self):
|
||||
config = {'alpha': self.alpha}
|
||||
config = {'alpha': float(self.alpha)}
|
||||
base_config = super(LeakyReLU, self).get_config()
|
||||
return dict(list(base_config.items()) + list(config.items()))
|
||||
|
||||
@@ -202,7 +202,7 @@ class ThresholdedReLU(Layer):
|
||||
self.theta = K.cast_to_floatx(theta)
|
||||
|
||||
def call(self, inputs, mask=None):
|
||||
return inputs * K.cast(inputs > self.theta, K.floatx())
|
||||
return inputs * K.cast(K.greater(inputs, self.theta), K.floatx())
|
||||
|
||||
def get_config(self):
|
||||
config = {'theta': float(self.theta)}
|
||||
|
||||
@@ -256,6 +256,9 @@ class Conv1D(_Conv):
|
||||
Specifying any stride value != 1 is incompatible with specifying
|
||||
any `dilation_rate` value != 1.
|
||||
padding: One of `"valid"`, `"causal"` or `"same"` (case-insensitive).
|
||||
`"valid"` means "no padding".
|
||||
`"same"` results in padding the input such that
|
||||
the output has the same length as the original input.
|
||||
`"causal"` results in causal (dilated) convolutions, e.g. output[t]
|
||||
does not depend on input[t+1:]. Useful when modeling temporal data
|
||||
where the model should not violate the temporal order.
|
||||
@@ -473,7 +476,7 @@ class Conv3D(_Conv):
|
||||
When using this layer as the first layer in a model,
|
||||
provide the keyword argument `input_shape`
|
||||
(tuple of integers, does not include the sample axis),
|
||||
e.g. `input_shape=(128, 128, 128, 3)` for 128x128x128 volumes
|
||||
e.g. `input_shape=(128, 128, 128, 1)` for 128x128x128 volumes
|
||||
with a single channel,
|
||||
in `data_format="channels_last"`.
|
||||
|
||||
@@ -481,7 +484,7 @@ class Conv3D(_Conv):
|
||||
filters: Integer, the dimensionality of the output space
|
||||
(i.e. the number output of filters in the convolution).
|
||||
kernel_size: An integer or tuple/list of 3 integers, specifying the
|
||||
width and height of the 3D convolution window.
|
||||
depth, height and width of the 3D convolution window.
|
||||
Can be a single integer to specify the same value for
|
||||
all spatial dimensions.
|
||||
strides: An integer or tuple/list of 3 integers,
|
||||
@@ -803,6 +806,237 @@ class Conv2DTranspose(Conv2D):
|
||||
return config
|
||||
|
||||
|
||||
class Conv3DTranspose(Conv3D):
|
||||
"""Transposed convolution layer (sometimes called Deconvolution).
|
||||
|
||||
The need for transposed convolutions generally arises
|
||||
from the desire to use a transformation going in the opposite direction
|
||||
of a normal convolution, i.e., from something that has the shape of the
|
||||
output of some convolution to something that has the shape of its input
|
||||
while maintaining a connectivity pattern that is compatible with
|
||||
said convolution.
|
||||
|
||||
When using this layer as the first layer in a model,
|
||||
provide the keyword argument `input_shape`
|
||||
(tuple of integers, does not include the sample axis),
|
||||
e.g. `input_shape=(128, 128, 128, 3)` for a 128x128x128 volume with 3 channels
|
||||
if `data_format="channels_last"`.
|
||||
|
||||
# Arguments
|
||||
filters: Integer, the dimensionality of the output space
|
||||
(i.e. the number of output filters in the convolution).
|
||||
kernel_size: An integer or tuple/list of 3 integers, specifying the
|
||||
width and height of the 3D convolution window.
|
||||
Can be a single integer to specify the same value for
|
||||
all spatial dimensions.
|
||||
strides: An integer or tuple/list of 3 integers,
|
||||
specifying the strides of the convolution along the width and height.
|
||||
Can be a single integer to specify the same value for
|
||||
all spatial dimensions.
|
||||
Specifying any stride value != 1 is incompatible with specifying
|
||||
any `dilation_rate` value != 1.
|
||||
padding: one of `"valid"` or `"same"` (case-insensitive).
|
||||
data_format: A string,
|
||||
one of `channels_last` (default) or `channels_first`.
|
||||
The ordering of the dimensions in the inputs.
|
||||
`channels_last` corresponds to inputs with shape
|
||||
`(batch, depth, height, width, channels)` while `channels_first`
|
||||
corresponds to inputs with shape
|
||||
`(batch, channels, depth, height, width)`.
|
||||
It defaults to the `image_data_format` value found in your
|
||||
Keras config file at `~/.keras/keras.json`.
|
||||
If you never set it, then it will be "channels_last".
|
||||
dilation_rate: an integer or tuple/list of 3 integers, specifying
|
||||
the dilation rate to use for dilated convolution.
|
||||
Can be a single integer to specify the same value for
|
||||
all spatial dimensions.
|
||||
Currently, specifying any `dilation_rate` value != 1 is
|
||||
incompatible with specifying any stride value != 1.
|
||||
activation: Activation function to use
|
||||
(see [activations](../activations.md)).
|
||||
If you don't specify anything, no activation is applied
|
||||
(ie. "linear" activation: `a(x) = x`).
|
||||
use_bias: Boolean, whether the layer uses a bias vector.
|
||||
kernel_initializer: Initializer for the `kernel` weights matrix
|
||||
(see [initializers](../initializers.md)).
|
||||
bias_initializer: Initializer for the bias vector
|
||||
(see [initializers](../initializers.md)).
|
||||
kernel_regularizer: Regularizer function applied to
|
||||
the `kernel` weights matrix
|
||||
(see [regularizer](../regularizers.md)).
|
||||
bias_regularizer: Regularizer function applied to the bias vector
|
||||
(see [regularizer](../regularizers.md)).
|
||||
activity_regularizer: Regularizer function applied to
|
||||
the output of the layer (its "activation").
|
||||
(see [regularizer](../regularizers.md)).
|
||||
kernel_constraint: Constraint function applied to the kernel matrix
|
||||
(see [constraints](../constraints.md)).
|
||||
bias_constraint: Constraint function applied to the bias vector
|
||||
(see [constraints](../constraints.md)).
|
||||
|
||||
# Input shape
|
||||
5D tensor with shape:
|
||||
`(batch, channels, depth, rows, cols)` if data_format='channels_first'
|
||||
or 5D tensor with shape:
|
||||
`(batch, depth, rows, cols, channels)` if data_format='channels_last'.
|
||||
|
||||
# Output shape
|
||||
5D tensor with shape:
|
||||
`(batch, filters, new_depth, new_rows, new_cols)` if data_format='channels_first'
|
||||
or 5D tensor with shape:
|
||||
`(batch, new_depth, new_rows, new_cols, filters)` if data_format='channels_last'.
|
||||
`depth` and `rows` and `cols` values might have changed due to padding.
|
||||
|
||||
# References
|
||||
- [A guide to convolution arithmetic for deep learning](https://arxiv.org/abs/1603.07285v1)
|
||||
- [Deconvolutional Networks](http://www.matthewzeiler.com/pubs/cvpr2010/cvpr2010.pdf)
|
||||
"""
|
||||
|
||||
def __init__(self, filters,
|
||||
kernel_size,
|
||||
strides=(1, 1, 1),
|
||||
padding='valid',
|
||||
data_format=None,
|
||||
activation=None,
|
||||
use_bias=True,
|
||||
kernel_initializer='glorot_uniform',
|
||||
bias_initializer='zeros',
|
||||
kernel_regularizer=None,
|
||||
bias_regularizer=None,
|
||||
activity_regularizer=None,
|
||||
kernel_constraint=None,
|
||||
bias_constraint=None,
|
||||
**kwargs):
|
||||
super(Conv3DTranspose, self).__init__(
|
||||
filters,
|
||||
kernel_size,
|
||||
strides=strides,
|
||||
padding=padding,
|
||||
data_format=data_format,
|
||||
activation=activation,
|
||||
use_bias=use_bias,
|
||||
kernel_initializer=kernel_initializer,
|
||||
bias_initializer=bias_initializer,
|
||||
kernel_regularizer=kernel_regularizer,
|
||||
bias_regularizer=bias_regularizer,
|
||||
activity_regularizer=activity_regularizer,
|
||||
kernel_constraint=kernel_constraint,
|
||||
bias_constraint=bias_constraint,
|
||||
**kwargs)
|
||||
self.input_spec = InputSpec(ndim=5)
|
||||
|
||||
def build(self, input_shape):
|
||||
if len(input_shape) != 5:
|
||||
raise ValueError('Inputs should have rank ' +
|
||||
str(5) +
|
||||
'; Received input shape:', str(input_shape))
|
||||
if self.data_format == 'channels_first':
|
||||
channel_axis = 1
|
||||
else:
|
||||
channel_axis = -1
|
||||
if input_shape[channel_axis] is None:
|
||||
raise ValueError('The channel dimension of the inputs '
|
||||
'should be defined. Found `None`.')
|
||||
input_dim = input_shape[channel_axis]
|
||||
kernel_shape = self.kernel_size + (self.filters, input_dim)
|
||||
|
||||
self.kernel = self.add_weight(shape=kernel_shape,
|
||||
initializer=self.kernel_initializer,
|
||||
name='kernel',
|
||||
regularizer=self.kernel_regularizer,
|
||||
constraint=self.kernel_constraint)
|
||||
if self.use_bias:
|
||||
self.bias = self.add_weight(shape=(self.filters,),
|
||||
initializer=self.bias_initializer,
|
||||
name='bias',
|
||||
regularizer=self.bias_regularizer,
|
||||
constraint=self.bias_constraint)
|
||||
else:
|
||||
self.bias = None
|
||||
# Set input spec.
|
||||
self.input_spec = InputSpec(ndim=5, axes={channel_axis: input_dim})
|
||||
self.built = True
|
||||
|
||||
def call(self, inputs):
|
||||
input_shape = K.shape(inputs)
|
||||
batch_size = input_shape[0]
|
||||
if self.data_format == 'channels_first':
|
||||
d_axis, h_axis, w_axis = 2, 3, 4
|
||||
else:
|
||||
d_axis, h_axis, w_axis = 1, 2, 3
|
||||
|
||||
depth = input_shape[d_axis]
|
||||
height = input_shape[h_axis]
|
||||
width = input_shape[w_axis]
|
||||
|
||||
kernel_d, kernel_h, kernel_w = self.kernel_size
|
||||
stride_d, stride_h, stride_w = self.strides
|
||||
|
||||
# Infer the dynamic output shape:
|
||||
out_depth = conv_utils.deconv_length(depth,
|
||||
stride_d, kernel_d,
|
||||
self.padding)
|
||||
out_height = conv_utils.deconv_length(height,
|
||||
stride_h, kernel_h,
|
||||
self.padding)
|
||||
out_width = conv_utils.deconv_length(width,
|
||||
stride_w, kernel_w,
|
||||
self.padding)
|
||||
|
||||
if self.data_format == 'channels_first':
|
||||
output_shape = (batch_size, self.filters, out_depth, out_height, out_width)
|
||||
else:
|
||||
output_shape = (batch_size, out_depth, out_height, out_width, self.filters)
|
||||
|
||||
outputs = K.conv3d_transpose(inputs,
|
||||
self.kernel,
|
||||
output_shape,
|
||||
self.strides,
|
||||
padding=self.padding,
|
||||
data_format=self.data_format)
|
||||
|
||||
if self.bias:
|
||||
outputs = K.bias_add(
|
||||
outputs,
|
||||
self.bias,
|
||||
data_format=self.data_format)
|
||||
|
||||
if self.activation is not None:
|
||||
return self.activation(outputs)
|
||||
return outputs
|
||||
|
||||
def compute_output_shape(self, input_shape):
|
||||
output_shape = list(input_shape)
|
||||
if self.data_format == 'channels_first':
|
||||
c_axis, d_axis, h_axis, w_axis = 1, 2, 3, 4
|
||||
else:
|
||||
c_axis, d_axis, h_axis, w_axis = 4, 1, 2, 3
|
||||
|
||||
kernel_d, kernel_h, kernel_w = self.kernel_size
|
||||
stride_d, stride_h, stride_w = self.strides
|
||||
|
||||
output_shape[c_axis] = self.filters
|
||||
output_shape[d_axis] = conv_utils.deconv_length(output_shape[d_axis],
|
||||
stride_d,
|
||||
kernel_d,
|
||||
self.padding)
|
||||
output_shape[h_axis] = conv_utils.deconv_length(output_shape[h_axis],
|
||||
stride_h,
|
||||
kernel_h,
|
||||
self.padding)
|
||||
output_shape[w_axis] = conv_utils.deconv_length(output_shape[w_axis],
|
||||
stride_w,
|
||||
kernel_w,
|
||||
self.padding)
|
||||
|
||||
return tuple(output_shape)
|
||||
|
||||
def get_config(self):
|
||||
config = super(Conv3DTranspose, self).get_config()
|
||||
config.pop('dilation_rate')
|
||||
return config
|
||||
|
||||
|
||||
class SeparableConv2D(Conv2D):
|
||||
"""Depthwise separable 2D convolution.
|
||||
|
||||
@@ -1252,7 +1486,7 @@ class ZeroPadding1D(Layer):
|
||||
class ZeroPadding2D(Layer):
|
||||
"""Zero-padding layer for 2D input (e.g. picture).
|
||||
|
||||
This layer can add rows and columns or zeros
|
||||
This layer can add rows and columns of zeros
|
||||
at the top, bottom, left and right side of an image tensor.
|
||||
|
||||
# Arguments
|
||||
@@ -1888,6 +2122,7 @@ Convolution3D = Conv3D
|
||||
SeparableConvolution2D = SeparableConv2D
|
||||
Convolution2DTranspose = Conv2DTranspose
|
||||
Deconvolution2D = Deconv2D = Conv2DTranspose
|
||||
Deconvolution3D = Deconv3D = Conv3DTranspose
|
||||
|
||||
# Legacy aliases
|
||||
AtrousConv1D = AtrousConvolution1D
|
||||
|
||||
@@ -109,7 +109,7 @@ class ConvRecurrent2D(Recurrent):
|
||||
self.state_spec = None
|
||||
|
||||
def compute_output_shape(self, input_shape):
|
||||
if type(input_shape) is list:
|
||||
if isinstance(input_shape, list):
|
||||
input_shape = input_shape[0]
|
||||
if self.data_format == 'channels_first':
|
||||
rows = input_shape[3]
|
||||
|
||||
+31
-33
@@ -5,7 +5,6 @@ from __future__ import division
|
||||
import numpy as np
|
||||
|
||||
import copy
|
||||
import inspect
|
||||
import types as python_types
|
||||
import warnings
|
||||
|
||||
@@ -19,6 +18,7 @@ from ..engine import Layer
|
||||
from ..utils.generic_utils import func_dump
|
||||
from ..utils.generic_utils import func_load
|
||||
from ..utils.generic_utils import deserialize_keras_object
|
||||
from ..utils.generic_utils import has_arg
|
||||
from ..legacy import interfaces
|
||||
|
||||
|
||||
@@ -193,8 +193,8 @@ class SpatialDropout2D(Dropout):
|
||||
if data_format is None:
|
||||
data_format = K.image_data_format()
|
||||
if data_format not in {'channels_last', 'channels_first'}:
|
||||
raise ValueError('data_format must be in '
|
||||
'{"channels_last", "channels_first"}')
|
||||
raise ValueError('`data_format` must be in '
|
||||
'{`"channels_last"`, `"channels_first"`}')
|
||||
self.data_format = data_format
|
||||
self.input_spec = InputSpec(ndim=4)
|
||||
|
||||
@@ -202,10 +202,8 @@ class SpatialDropout2D(Dropout):
|
||||
input_shape = K.shape(inputs)
|
||||
if self.data_format == 'channels_first':
|
||||
noise_shape = (input_shape[0], input_shape[1], 1, 1)
|
||||
elif self.data_format == 'channels_last':
|
||||
noise_shape = (input_shape[0], 1, 1, input_shape[3])
|
||||
else:
|
||||
raise ValueError('Invalid data_format:', self.data_format)
|
||||
noise_shape = (input_shape[0], 1, 1, input_shape[3])
|
||||
return noise_shape
|
||||
|
||||
|
||||
@@ -248,8 +246,8 @@ class SpatialDropout3D(Dropout):
|
||||
if data_format is None:
|
||||
data_format = K.image_data_format()
|
||||
if data_format not in {'channels_last', 'channels_first'}:
|
||||
raise ValueError('data_format must be in '
|
||||
'{"channels_last", "channels_first"}')
|
||||
raise ValueError('`data_format` must be in '
|
||||
'{`"channels_last"`, `"channels_first"`}')
|
||||
self.data_format = data_format
|
||||
self.input_spec = InputSpec(ndim=5)
|
||||
|
||||
@@ -257,10 +255,8 @@ class SpatialDropout3D(Dropout):
|
||||
input_shape = K.shape(inputs)
|
||||
if self.data_format == 'channels_first':
|
||||
noise_shape = (input_shape[0], input_shape[1], 1, 1, 1)
|
||||
elif self.data_format == 'channels_last':
|
||||
noise_shape = (input_shape[0], 1, 1, 1, input_shape[4])
|
||||
else:
|
||||
raise ValueError('Invalid data_format:', self.data_format)
|
||||
noise_shape = (input_shape[0], 1, 1, 1, input_shape[4])
|
||||
return noise_shape
|
||||
|
||||
|
||||
@@ -299,13 +295,13 @@ class Reshape(Layer):
|
||||
"""Reshapes an output to a certain shape.
|
||||
|
||||
# Arguments
|
||||
target_shape: target shape. Tuple of integers,
|
||||
does not include the samples dimension (batch size).
|
||||
target_shape: target shape. Tuple of integers.
|
||||
Does not include the batch axis.
|
||||
|
||||
# Input shape
|
||||
Arbitrary, although all dimensions in the input shaped must be fixed.
|
||||
Use the keyword argument `input_shape`
|
||||
(tuple of integers, does not include the samples axis)
|
||||
(tuple of integers, does not include the batch axis)
|
||||
when using this layer as the first layer in a model.
|
||||
|
||||
# Output shape
|
||||
@@ -335,27 +331,22 @@ class Reshape(Layer):
|
||||
self.target_shape = tuple(target_shape)
|
||||
|
||||
def _fix_unknown_dimension(self, input_shape, output_shape):
|
||||
"""Find and replace a missing dimension in an output shape.
|
||||
"""Finds and replaces a missing dimension in an output shape.
|
||||
|
||||
This is a near direct port of the internal Numpy function
|
||||
`_fix_unknown_dimension` in `numpy/core/src/multiarray/shape.c`
|
||||
|
||||
# Arguments
|
||||
input_shape: shape of array being reshaped
|
||||
output_shape: desired shape of the array with at most
|
||||
input_shape: original shape of array being reshaped
|
||||
output_shape: target shape of the array, with at most
|
||||
a single -1 which indicates a dimension that should be
|
||||
derived from the input shape.
|
||||
|
||||
# Returns
|
||||
The new output shape with a -1 replaced with its computed value.
|
||||
|
||||
Raises a ValueError if the total array size of the output_shape is
|
||||
different then the input_shape, or more then one unknown dimension
|
||||
is specified.
|
||||
The new output shape with a `-1` replaced with its computed value.
|
||||
|
||||
# Raises
|
||||
ValueError: in case of invalid values
|
||||
for `input_shape` or `input_shape`.
|
||||
ValueError: if `input_shape` and `output_shape` do not match.
|
||||
"""
|
||||
output_shape = list(output_shape)
|
||||
msg = 'total size of new array must be unchanged'
|
||||
@@ -386,13 +377,11 @@ class Reshape(Layer):
|
||||
|
||||
def call(self, inputs):
|
||||
# In case the target shape is not fully defined,
|
||||
# we need access to the shape of x.
|
||||
# solution:
|
||||
# 1) rely on x._keras_shape
|
||||
# 2) fallback: K.int_shape
|
||||
# we need access to the shape of `inputs`.
|
||||
# solution: rely on `K.int_shape`.
|
||||
target_shape = self.target_shape
|
||||
if -1 in target_shape:
|
||||
# target shape not fully defined
|
||||
# Target shape not fully defined.
|
||||
input_shape = None
|
||||
try:
|
||||
input_shape = K.int_shape(inputs)
|
||||
@@ -467,7 +456,7 @@ class Flatten(Layer):
|
||||
|
||||
```python
|
||||
model = Sequential()
|
||||
model.add(Convolution2D(64, 3, 3,
|
||||
model.add(Conv2D(64, 3, 3,
|
||||
border_mode='same',
|
||||
input_shape=(3, 32, 32)))
|
||||
# now: model.output_shape == (None, 64, 32, 32)
|
||||
@@ -648,13 +637,12 @@ class Lambda(Layer):
|
||||
else:
|
||||
shape = self._output_shape(input_shape)
|
||||
if not isinstance(shape, (list, tuple)):
|
||||
raise ValueError('output_shape function must return a tuple')
|
||||
raise ValueError('`output_shape` function must return a tuple.')
|
||||
return tuple(shape)
|
||||
|
||||
def call(self, inputs, mask=None):
|
||||
arguments = self.arguments
|
||||
arg_spec = inspect.getargspec(self.function)
|
||||
if 'mask' in arg_spec.args:
|
||||
if has_arg(self.function, 'mask'):
|
||||
arguments['mask'] = mask
|
||||
return self.function(inputs, **arguments)
|
||||
|
||||
@@ -720,6 +708,16 @@ class Lambda(Layer):
|
||||
else:
|
||||
output_shape = config['output_shape']
|
||||
|
||||
# If arguments were numpy array, they have been saved as
|
||||
# list. We need to recover the ndarray
|
||||
if 'arguments' in config:
|
||||
for key in config['arguments']:
|
||||
if isinstance(config['arguments'][key], dict):
|
||||
arg_dict = config['arguments'][key]
|
||||
if 'type' in arg_dict and arg_dict['type'] == 'ndarray':
|
||||
# Overwrite the argument with its numpy translation
|
||||
config['arguments'][key] = np.array(arg_dict['value'])
|
||||
|
||||
config['function'] = function
|
||||
config['output_shape'] = output_shape
|
||||
return cls(**config)
|
||||
|
||||
@@ -75,7 +75,6 @@ class Embedding(Layer):
|
||||
mask_zero=False,
|
||||
input_length=None,
|
||||
**kwargs):
|
||||
kwargs['dtype'] = 'int32'
|
||||
if 'input_shape' not in kwargs:
|
||||
if input_length:
|
||||
kwargs['input_shape'] = (input_length,)
|
||||
@@ -98,7 +97,8 @@ class Embedding(Layer):
|
||||
initializer=self.embeddings_initializer,
|
||||
name='embeddings',
|
||||
regularizer=self.embeddings_regularizer,
|
||||
constraint=self.embeddings_constraint)
|
||||
constraint=self.embeddings_constraint,
|
||||
dtype=self.dtype)
|
||||
self.built = True
|
||||
|
||||
def compute_mask(self, inputs, mask=None):
|
||||
@@ -108,11 +108,25 @@ class Embedding(Layer):
|
||||
return K.not_equal(inputs, 0)
|
||||
|
||||
def compute_output_shape(self, input_shape):
|
||||
if not self.input_length:
|
||||
input_length = input_shape[1]
|
||||
if self.input_length is None:
|
||||
return input_shape + (self.output_dim,)
|
||||
else:
|
||||
input_length = self.input_length
|
||||
return (input_shape[0], input_length, self.output_dim)
|
||||
# input_length can be tuple if input is 3D or higher
|
||||
if isinstance(self.input_length, (list, tuple)):
|
||||
in_lens = list(self.input_length)
|
||||
else:
|
||||
in_lens = [self.input_length]
|
||||
if len(in_lens) != len(input_shape) - 1:
|
||||
ValueError('"input_length" is %s, but received input has shape %s' %
|
||||
(str(self.input_length), str(input_shape)))
|
||||
else:
|
||||
for i, (s1, s2) in enumerate(zip(in_lens, input_shape[1:])):
|
||||
if s1 is not None and s2 is not None and s1 != s2:
|
||||
ValueError('"input_length" is %s, but received input has shape %s' %
|
||||
(str(self.input_length), str(input_shape)))
|
||||
elif s1 is None:
|
||||
in_lens[i] = s2
|
||||
return (input_shape[0],) + tuple(in_lens) + (self.output_dim,)
|
||||
|
||||
def call(self, inputs):
|
||||
if K.dtype(inputs) != 'int32':
|
||||
|
||||
+13
-67
@@ -147,22 +147,11 @@ class LocallyConnected1D(Layer):
|
||||
return (input_shape[0], length, self.filters)
|
||||
|
||||
def call(self, inputs):
|
||||
stride = self.strides[0]
|
||||
output_length, feature_dim, filters = self.kernel_shape
|
||||
|
||||
xs = []
|
||||
for i in range(output_length):
|
||||
slice_length = slice(i * stride,
|
||||
i * stride + self.kernel_size[0])
|
||||
xs.append(K.reshape(inputs[:, slice_length, :],
|
||||
(1, -1, feature_dim)))
|
||||
x_aggregate = K.concatenate(xs, axis=0)
|
||||
# Shape: `(output_length, batch_size, filters)`.
|
||||
output = K.batch_dot(x_aggregate, self.kernel)
|
||||
output = K.permute_dimensions(output, (1, 0, 2))
|
||||
output_length, _, filters = self.kernel_shape
|
||||
|
||||
output = K.local_conv1d(inputs, self.kernel, self.kernel_size, self.strides)
|
||||
if self.use_bias:
|
||||
output += K.reshape(self.bias, (1, output_length, filters))
|
||||
output = K.bias_add(output, self.bias)
|
||||
if self.activation is not None:
|
||||
output = self.activation(output)
|
||||
return output
|
||||
@@ -363,62 +352,19 @@ class LocallyConnected2D(Layer):
|
||||
return (input_shape[0], rows, cols, self.filters)
|
||||
|
||||
def call(self, inputs):
|
||||
stride_row, stride_col = self.strides
|
||||
_, feature_dim, filters = self.kernel_shape
|
||||
_, _, filters = self.kernel_shape
|
||||
|
||||
if self.data_format == 'channels_first':
|
||||
if K.backend() == 'theano':
|
||||
output = []
|
||||
for i in range(self.output_row):
|
||||
for j in range(self.output_col):
|
||||
slice_row = slice(i * stride_row,
|
||||
i * stride_row + self.kernel_size[0])
|
||||
slice_col = slice(j * stride_col,
|
||||
j * stride_col + self.kernel_size[1])
|
||||
x_flatten = K.reshape(inputs[:, :, slice_row, slice_col],
|
||||
(1, -1, feature_dim))
|
||||
output.append(K.dot(x_flatten,
|
||||
self.kernel[i * self.output_col + j, :, :]))
|
||||
output = K.concatenate(output, axis=0)
|
||||
else:
|
||||
xs = []
|
||||
for i in range(self.output_row):
|
||||
for j in range(self.output_col):
|
||||
slice_row = slice(i * stride_row,
|
||||
i * stride_row + self.kernel_size[0])
|
||||
slice_col = slice(j * stride_col,
|
||||
j * stride_col + self.kernel_size[1])
|
||||
xs.append(K.reshape(inputs[:, :, slice_row, slice_col],
|
||||
(1, -1, feature_dim)))
|
||||
x_aggregate = K.concatenate(xs, axis=0)
|
||||
output = K.batch_dot(x_aggregate, self.kernel)
|
||||
output = K.reshape(output,
|
||||
(self.output_row, self.output_col, -1, filters))
|
||||
output = K.permute_dimensions(output, (2, 3, 0, 1))
|
||||
|
||||
elif self.data_format == 'channels_last':
|
||||
xs = []
|
||||
for i in range(self.output_row):
|
||||
for j in range(self.output_col):
|
||||
slice_row = slice(i * stride_row,
|
||||
i * stride_row + self.kernel_size[0])
|
||||
slice_col = slice(j * stride_col,
|
||||
j * stride_col + self.kernel_size[1])
|
||||
xs.append(K.reshape(inputs[:, slice_row, slice_col, :],
|
||||
(1, -1, feature_dim)))
|
||||
x_aggregate = K.concatenate(xs, axis=0)
|
||||
output = K.batch_dot(x_aggregate, self.kernel)
|
||||
output = K.reshape(output,
|
||||
(self.output_row, self.output_col, -1, filters))
|
||||
output = K.permute_dimensions(output, (2, 0, 1, 3))
|
||||
output = K.local_conv2d(inputs,
|
||||
self.kernel,
|
||||
self.kernel_size,
|
||||
self.strides,
|
||||
(self.output_row, self.output_col),
|
||||
self.data_format)
|
||||
|
||||
if self.use_bias:
|
||||
if self.data_format == 'channels_first':
|
||||
output += K.reshape(self.bias,
|
||||
(1, filters, self.output_row, self.output_col))
|
||||
elif self.data_format == 'channels_last':
|
||||
output += K.reshape(self.bias,
|
||||
(1, self.output_row, self.output_col, filters))
|
||||
if self.data_format == 'channels_first' or self.data_format == 'channels_last':
|
||||
output = K.bias_add(output, self.bias, data_format=self.data_format)
|
||||
|
||||
output = self.activation(output)
|
||||
return output
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ from ..engine import Layer
|
||||
from .. import backend as K
|
||||
import numpy as np
|
||||
from ..legacy import interfaces
|
||||
from ..engine import InputSpec
|
||||
|
||||
|
||||
class GaussianNoise(Layer):
|
||||
@@ -90,3 +91,70 @@ class GaussianDropout(Layer):
|
||||
config = {'rate': self.rate}
|
||||
base_config = super(GaussianDropout, self).get_config()
|
||||
return dict(list(base_config.items()) + list(config.items()))
|
||||
|
||||
|
||||
class AlphaDropout(Layer):
|
||||
"""Applies Alpha Dropout to the input.
|
||||
|
||||
Alpha Dropout is a `Dropout` that keeps mean and variance of inputs
|
||||
to their original values, in order to ensure the self-normalizing property
|
||||
even after this dropout.
|
||||
Alpha Dropout fits well to Scaled Exponential Linear Units
|
||||
by randomly setting activations to the negative saturation value.
|
||||
|
||||
# Arguments
|
||||
rate: float, drop probability (as with `Dropout`).
|
||||
The multiplicative noise will have
|
||||
standard deviation `sqrt(rate / (1 - rate))`.
|
||||
seed: A Python integer to use as random seed.
|
||||
|
||||
# Input shape
|
||||
Arbitrary. Use the keyword argument `input_shape`
|
||||
(tuple of integers, does not include the samples axis)
|
||||
when using this layer as the first layer in a model.
|
||||
|
||||
# Output shape
|
||||
Same shape as input.
|
||||
|
||||
# References
|
||||
- [Self-Normalizing Neural Networks](https://arxiv.org/abs/1706.02515)
|
||||
"""
|
||||
def __init__(self, rate, noise_shape=None, seed=None, **kwargs):
|
||||
super(AlphaDropout, self).__init__(**kwargs)
|
||||
self.rate = rate
|
||||
self.noise_shape = noise_shape
|
||||
self.seed = seed
|
||||
self.supports_masking = True
|
||||
|
||||
def _get_noise_shape(self, inputs):
|
||||
return self.noise_shape if self.noise_shape else K.shape(inputs)
|
||||
|
||||
def call(self, inputs, training=None):
|
||||
if 0. < self.rate < 1.:
|
||||
noise_shape = self._get_noise_shape(inputs)
|
||||
|
||||
def dropped_inputs(inputs=inputs, rate=self.rate, seed=self.seed):
|
||||
alpha = 1.6732632423543772848170429916717
|
||||
scale = 1.0507009873554804934193349852946
|
||||
alpha_p = -alpha * scale
|
||||
|
||||
kept_idx = K.greater_equal(K.random_uniform(noise_shape, seed=seed), rate)
|
||||
kept_idx = K.cast(kept_idx, K.floatx())
|
||||
|
||||
# Get affine transformation params
|
||||
a = ((1 - rate) * (1 + rate * alpha_p ** 2)) ** -0.5
|
||||
b = -a * alpha_p * rate
|
||||
|
||||
# Apply mask
|
||||
x = inputs * kept_idx + alpha_p * (1 - kept_idx)
|
||||
|
||||
# Do affine transformation
|
||||
return a * x + b
|
||||
|
||||
return K.in_train_phase(dropped_inputs, inputs, training=training)
|
||||
return inputs
|
||||
|
||||
def get_config(self):
|
||||
config = {'rate': self.rate}
|
||||
base_config = super(AlphaDropout, self).get_config()
|
||||
return dict(list(base_config.items()) + list(config.items()))
|
||||
|
||||
@@ -137,7 +137,7 @@ class BatchNormalization(Layer):
|
||||
|
||||
def normalize_inference():
|
||||
if needs_broadcasting:
|
||||
# In this case we must explictly broadcast all parameters.
|
||||
# In this case we must explicitly broadcast all parameters.
|
||||
broadcast_moving_mean = K.reshape(self.moving_mean,
|
||||
broadcast_shape)
|
||||
broadcast_moving_variance = K.reshape(self.moving_variance,
|
||||
|
||||
+39
-10
@@ -96,6 +96,8 @@ class Recurrent(Layer):
|
||||
`[(input_dim, output_dim), (output_dim, output_dim), (output_dim,)]`.
|
||||
return_sequences: Boolean. Whether to return the last output
|
||||
in the output sequence, or the full sequence.
|
||||
return_state: Boolean. Whether to return the last state
|
||||
in addition to the output.
|
||||
go_backwards: Boolean (default False).
|
||||
If True, process the input sequence backwards and return the
|
||||
reversed sequence.
|
||||
@@ -139,6 +141,9 @@ class Recurrent(Layer):
|
||||
(Optional) 2D tensors with shape `(batch_size, output_dim)`.
|
||||
|
||||
# Output shape
|
||||
- if `return_state`: a list of tensors. The first tensor is
|
||||
the output. The remaining tensors are the last states,
|
||||
each with shape `(batch_size, units)`.
|
||||
- if `return_sequences`: 3D tensor with shape
|
||||
`(batch_size, timesteps, units)`.
|
||||
- else, 2D tensor with shape `(batch_size, units)`.
|
||||
@@ -183,6 +188,7 @@ class Recurrent(Layer):
|
||||
"""
|
||||
|
||||
def __init__(self, return_sequences=False,
|
||||
return_state=False,
|
||||
go_backwards=False,
|
||||
stateful=False,
|
||||
unroll=False,
|
||||
@@ -190,7 +196,11 @@ class Recurrent(Layer):
|
||||
**kwargs):
|
||||
super(Recurrent, self).__init__(**kwargs)
|
||||
self.return_sequences = return_sequences
|
||||
self.return_state = return_state
|
||||
self.go_backwards = go_backwards
|
||||
if K.backend() == 'cntk' and stateful:
|
||||
raise ValueError('Stateful RNN is not currently supported with CNTK.')
|
||||
|
||||
self.stateful = stateful
|
||||
self.unroll = unroll
|
||||
self.implementation = implementation
|
||||
@@ -203,18 +213,27 @@ class Recurrent(Layer):
|
||||
def compute_output_shape(self, input_shape):
|
||||
if isinstance(input_shape, list):
|
||||
input_shape = input_shape[0]
|
||||
|
||||
if self.return_sequences:
|
||||
return (input_shape[0], input_shape[1], self.units)
|
||||
output_shape = (input_shape[0], input_shape[1], self.units)
|
||||
else:
|
||||
return (input_shape[0], self.units)
|
||||
output_shape = (input_shape[0], self.units)
|
||||
|
||||
if self.return_state:
|
||||
state_shape = [(input_shape[0], self.units) for _ in self.states]
|
||||
return [output_shape] + state_shape
|
||||
else:
|
||||
return output_shape
|
||||
|
||||
def compute_mask(self, inputs, mask):
|
||||
if self.return_sequences:
|
||||
if isinstance(mask, list):
|
||||
return mask[0]
|
||||
return mask
|
||||
if isinstance(mask, list):
|
||||
mask = mask[0]
|
||||
output_mask = mask if self.return_sequences else None
|
||||
if self.return_state:
|
||||
state_mask = [None for _ in self.states]
|
||||
return [output_mask] + state_mask
|
||||
else:
|
||||
return None
|
||||
return output_mask
|
||||
|
||||
def step(self, inputs, states):
|
||||
raise NotImplementedError
|
||||
@@ -332,9 +351,18 @@ class Recurrent(Layer):
|
||||
outputs._uses_learning_phase = True
|
||||
|
||||
if self.return_sequences:
|
||||
return outputs
|
||||
output = outputs
|
||||
else:
|
||||
return last_output
|
||||
output = last_output
|
||||
|
||||
if self.return_state:
|
||||
if not isinstance(states, (list, tuple)):
|
||||
states = [states]
|
||||
else:
|
||||
states = list(states)
|
||||
return [output] + states
|
||||
else:
|
||||
return output
|
||||
|
||||
def reset_states(self, states=None):
|
||||
if not self.stateful:
|
||||
@@ -378,6 +406,7 @@ class Recurrent(Layer):
|
||||
|
||||
def get_config(self):
|
||||
config = {'return_sequences': self.return_sequences,
|
||||
'return_state': self.return_state,
|
||||
'go_backwards': self.go_backwards,
|
||||
'stateful': self.stateful,
|
||||
'unroll': self.unroll,
|
||||
@@ -924,7 +953,7 @@ class LSTM(Recurrent):
|
||||
the linear transformation of the recurrent state.
|
||||
|
||||
# References
|
||||
- [Long short-term memory](http://deeplearning.cs.cmu.edu/pdfs/Hochreiter97_lstm.pdf) (original 1997 paper)
|
||||
- [Long short-term memory](http://www.bioinf.jku.at/publications/older/2604.pdf) (original 1997 paper)
|
||||
- [Learning to forget: Continual prediction with LSTM](http://www.mitpressjournals.org/doi/pdf/10.1162/089976600300015015)
|
||||
- [Supervised sequence labeling with recurrent neural networks](http://www.cs.toronto.edu/~graves/preprint.pdf)
|
||||
- [A Theoretically Grounded Application of Dropout in Recurrent Neural Networks](http://arxiv.org/abs/1512.05287)
|
||||
|
||||
+26
-10
@@ -2,9 +2,9 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import copy
|
||||
import inspect
|
||||
from ..engine import Layer
|
||||
from ..engine import InputSpec
|
||||
from ..utils.generic_utils import has_arg
|
||||
from .. import backend as K
|
||||
|
||||
|
||||
@@ -84,12 +84,13 @@ class Wrapper(Layer):
|
||||
@classmethod
|
||||
def from_config(cls, config, custom_objects=None):
|
||||
from . import deserialize as deserialize_layer
|
||||
layer = deserialize_layer(config.pop('layer'), custom_objects=custom_objects)
|
||||
layer = deserialize_layer(config.pop('layer'),
|
||||
custom_objects=custom_objects)
|
||||
return cls(layer, **config)
|
||||
|
||||
|
||||
class TimeDistributed(Wrapper):
|
||||
"""This wrapper allows to apply a layer to every temporal slice of an input.
|
||||
"""This wrapper applies a layer to every temporal slice of an input.
|
||||
|
||||
The input should be at least 3D, and the dimension of index one
|
||||
will be considered to be the temporal dimension.
|
||||
@@ -152,12 +153,21 @@ class TimeDistributed(Wrapper):
|
||||
timesteps = input_shape[1]
|
||||
return (child_output_shape[0], timesteps) + child_output_shape[1:]
|
||||
|
||||
def call(self, inputs, mask=None):
|
||||
def call(self, inputs, training=None, mask=None):
|
||||
kwargs = {}
|
||||
if has_arg(self.layer.call, 'training'):
|
||||
kwargs['training'] = training
|
||||
uses_learning_phase = False
|
||||
|
||||
input_shape = K.int_shape(inputs)
|
||||
if input_shape[0]:
|
||||
# batch size matters, use rnn-based implementation
|
||||
def step(x, _):
|
||||
output = self.layer.call(x)
|
||||
global uses_learning_phase
|
||||
output = self.layer.call(x, **kwargs)
|
||||
if hasattr(output, '_uses_learning_phase'):
|
||||
uses_learning_phase = (output._uses_learning_phase or
|
||||
uses_learning_phase)
|
||||
return output, []
|
||||
|
||||
_, outputs, _ = K.rnn(step, inputs,
|
||||
@@ -174,7 +184,10 @@ class TimeDistributed(Wrapper):
|
||||
input_length = K.shape(inputs)[1]
|
||||
# Shape: (num_samples * timesteps, ...)
|
||||
inputs = K.reshape(inputs, (-1,) + input_shape[2:])
|
||||
y = self.layer.call(inputs) # (num_samples * timesteps, ...)
|
||||
# (num_samples * timesteps, ...)
|
||||
y = self.layer.call(inputs, **kwargs)
|
||||
if hasattr(y, '_uses_learning_phase'):
|
||||
uses_learning_phase = y._uses_learning_phase
|
||||
# Shape: (num_samples, timesteps, ...)
|
||||
output_shape = self.compute_output_shape(input_shape)
|
||||
y = K.reshape(y, (-1, input_length) + output_shape[2:])
|
||||
@@ -184,6 +197,9 @@ class TimeDistributed(Wrapper):
|
||||
self.layer.activity_regularizer is not None):
|
||||
regularization_loss = self.layer.activity_regularizer(y)
|
||||
self.add_loss(regularization_loss, inputs)
|
||||
|
||||
if uses_learning_phase:
|
||||
y._uses_learning_phase = True
|
||||
return y
|
||||
|
||||
|
||||
@@ -205,7 +221,8 @@ class Bidirectional(Wrapper):
|
||||
|
||||
```python
|
||||
model = Sequential()
|
||||
model.add(Bidirectional(LSTM(10, return_sequences=True), input_shape=(5, 10)))
|
||||
model.add(Bidirectional(LSTM(10, return_sequences=True),
|
||||
input_shape=(5, 10)))
|
||||
model.add(Bidirectional(LSTM(10)))
|
||||
model.add(Dense(5))
|
||||
model.add(Activation('softmax'))
|
||||
@@ -254,10 +271,9 @@ class Bidirectional(Wrapper):
|
||||
|
||||
def call(self, inputs, training=None, mask=None):
|
||||
kwargs = {}
|
||||
func_args = inspect.getargspec(self.layer.call).args
|
||||
if 'training' in func_args:
|
||||
if has_arg(self.layer.call, 'training'):
|
||||
kwargs['training'] = training
|
||||
if 'mask' in func_args:
|
||||
if has_arg(self.layer.call, 'mask'):
|
||||
kwargs['mask'] = mask
|
||||
|
||||
y = self.forward_layer.call(inputs, **kwargs)
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import six
|
||||
import warnings
|
||||
import functools
|
||||
import inspect
|
||||
import numpy as np
|
||||
|
||||
|
||||
@@ -86,7 +85,7 @@ def generate_legacy_interface(allowed_positional_args=None,
|
||||
warnings.warn('Update your `' + object_name +
|
||||
'` call to the Keras 2 API: ' + signature, stacklevel=2)
|
||||
return func(*args, **kwargs)
|
||||
wrapper._legacy_support_signature = inspect.getargspec(func)
|
||||
wrapper._original_function = func
|
||||
return wrapper
|
||||
return legacy_support
|
||||
|
||||
@@ -161,7 +160,7 @@ def recurrent_args_preprocessor(args, kwargs):
|
||||
kwargs.pop('forget_bias_init')
|
||||
warnings.warn('The `forget_bias_init` argument '
|
||||
'has been ignored. Use `unit_forget_bias=True` '
|
||||
'instead to intialize with ones.', stacklevel=3)
|
||||
'instead to initialize with ones.', stacklevel=3)
|
||||
if 'input_dim' in kwargs:
|
||||
input_length = kwargs.pop('input_length', None)
|
||||
input_dim = kwargs.pop('input_dim')
|
||||
@@ -461,7 +460,7 @@ def convlstm2d_args_preprocessor(args, kwargs):
|
||||
else:
|
||||
warnings.warn('The `forget_bias_init` argument '
|
||||
'has been ignored. Use `unit_forget_bias=True` '
|
||||
'instead to intialize with ones.', stacklevel=3)
|
||||
'instead to initialize with ones.', stacklevel=3)
|
||||
args, kwargs, _converted = conv2d_args_preprocessor(args, kwargs)
|
||||
return args, kwargs, converted + _converted
|
||||
|
||||
@@ -577,14 +576,21 @@ def generator_methods_args_preprocessor(args, kwargs):
|
||||
if hasattr(generator, 'batch_size'):
|
||||
kwargs['steps_per_epoch'] = samples_per_epoch // generator.batch_size
|
||||
else:
|
||||
warnings.warn('The semantics of the Keras 2 argument '
|
||||
' `steps_per_epoch` is not the same as the '
|
||||
'Keras 1 argument `samples_per_epoch`. '
|
||||
'`steps_per_epoch` is the number of batches '
|
||||
'to draw from the generator at each epoch. '
|
||||
'Update your method calls accordingly.', stacklevel=3)
|
||||
kwargs['steps_per_epoch'] = samples_per_epoch
|
||||
converted.append(('samples_per_epoch', 'steps_per_epoch'))
|
||||
|
||||
keras1_args = {'samples_per_epoch', 'val_samples', 'nb_epoch', 'nb_val_samples', 'nb_worker'}
|
||||
if keras1_args.intersection(kwargs.keys()):
|
||||
warnings.warn('The semantics of the Keras 2 argument '
|
||||
'`steps_per_epoch` is not the same as the '
|
||||
'Keras 1 argument `samples_per_epoch`. '
|
||||
'`steps_per_epoch` is the number of batches '
|
||||
'to draw from the generator at each epoch. '
|
||||
'Basically steps_per_epoch = samples_per_epoch/batch_size. '
|
||||
'Similarly `nb_val_samples`->`validation_steps` and '
|
||||
'`val_samples`->`steps` arguments have changed. '
|
||||
'Update your method calls accordingly.', stacklevel=3)
|
||||
|
||||
return args, kwargs, converted
|
||||
|
||||
|
||||
@@ -594,7 +600,9 @@ legacy_generator_methods_support = generate_legacy_method_interface(
|
||||
('val_samples', 'steps'),
|
||||
('nb_epoch', 'epochs'),
|
||||
('nb_val_samples', 'validation_steps'),
|
||||
('nb_worker', 'workers')],
|
||||
('nb_worker', 'workers'),
|
||||
('pickle_safe', 'use_multiprocessing'),
|
||||
('max_q_size', 'max_queue_size')],
|
||||
preprocessor=generator_methods_args_preprocessor)
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import inspect
|
||||
import types as python_types
|
||||
import warnings
|
||||
|
||||
from ..engine.topology import Layer, InputSpec
|
||||
from .. import backend as K
|
||||
from ..utils.generic_utils import func_dump, func_load
|
||||
from ..utils.generic_utils import func_dump, func_load, has_arg
|
||||
from .. import regularizers
|
||||
from .. import constraints
|
||||
from .. import activations
|
||||
@@ -197,8 +196,7 @@ class Merge(Layer):
|
||||
# Case: "mode" is a lambda or function.
|
||||
if callable(self.mode):
|
||||
arguments = self.arguments
|
||||
arg_spec = inspect.getargspec(self.mode)
|
||||
if 'mask' in arg_spec.args:
|
||||
if has_arg(self.mode, 'mask'):
|
||||
arguments['mask'] = mask
|
||||
return self.mode(inputs, **arguments)
|
||||
|
||||
|
||||
+5
-5
@@ -35,8 +35,8 @@ def hinge(y_true, y_pred):
|
||||
|
||||
def categorical_hinge(y_true, y_pred):
|
||||
pos = K.sum(y_true * y_pred, axis=-1)
|
||||
neg = K.max((1.0 - y_true) * y_pred, axis=-1)
|
||||
return K.mean(K.maximum(0.0, neg - pos + 1), axis=-1)
|
||||
neg = K.max((1. - y_true) * y_pred, axis=-1)
|
||||
return K.maximum(0., neg - pos + 1.)
|
||||
|
||||
|
||||
def logcosh(y_true, y_pred):
|
||||
@@ -46,15 +46,15 @@ def logcosh(y_true, y_pred):
|
||||
|
||||
|
||||
def categorical_crossentropy(y_true, y_pred):
|
||||
return K.categorical_crossentropy(y_pred, y_true)
|
||||
return K.categorical_crossentropy(y_true, y_pred)
|
||||
|
||||
|
||||
def sparse_categorical_crossentropy(y_true, y_pred):
|
||||
return K.sparse_categorical_crossentropy(y_pred, y_true)
|
||||
return K.sparse_categorical_crossentropy(y_true, y_pred)
|
||||
|
||||
|
||||
def binary_crossentropy(y_true, y_pred):
|
||||
return K.mean(K.binary_crossentropy(y_pred, y_true), axis=-1)
|
||||
return K.mean(K.binary_crossentropy(y_true, y_pred), axis=-1)
|
||||
|
||||
|
||||
def kullback_leibler_divergence(y_true, y_pred):
|
||||
|
||||
@@ -36,6 +36,11 @@ def sparse_categorical_accuracy(y_true, y_pred):
|
||||
def top_k_categorical_accuracy(y_true, y_pred, k=5):
|
||||
return K.mean(K.in_top_k(y_pred, K.argmax(y_true, axis=-1), k), axis=-1)
|
||||
|
||||
|
||||
def sparse_top_k_categorical_accuracy(y_true, y_pred, k=5):
|
||||
return K.mean(K.in_top_k(y_pred, K.cast(K.max(y_true, axis=-1), 'int32'), k), axis=-1)
|
||||
|
||||
|
||||
# Aliases
|
||||
|
||||
mse = MSE = mean_squared_error
|
||||
|
||||
+81
-87
@@ -74,7 +74,11 @@ def save_model(model, filepath, overwrite=True, include_optimizer=True):
|
||||
|
||||
# if obj is any numpy type
|
||||
if type(obj).__module__ == np.__name__:
|
||||
return obj.item()
|
||||
if isinstance(obj, np.ndarray):
|
||||
return {'type': type(obj),
|
||||
'value': obj.tolist()}
|
||||
else:
|
||||
return obj.item()
|
||||
|
||||
# misc functions (e.g. loss function)
|
||||
if callable(obj):
|
||||
@@ -140,8 +144,8 @@ def save_model(model, filepath, overwrite=True, include_optimizer=True):
|
||||
weight_values = K.batch_get_value(symbolic_weights)
|
||||
weight_names = []
|
||||
for i, (w, val) in enumerate(zip(symbolic_weights, weight_values)):
|
||||
# Default values of symbolic_weights is /variable for theano
|
||||
if K.backend() == 'theano':
|
||||
# Default values of symbolic_weights is /variable for theano and cntk
|
||||
if K.backend() == 'theano' or K.backend() == 'cntk':
|
||||
if hasattr(w, 'name') and w.name != "/variable":
|
||||
name = str(w.name)
|
||||
else:
|
||||
@@ -203,91 +207,81 @@ def load_model(filepath, custom_objects=None, compile=True):
|
||||
obj: object, dict, or list.
|
||||
|
||||
# Returns
|
||||
The same structure, where occurences
|
||||
The same structure, where occurrences
|
||||
of a custom object name have been replaced
|
||||
with the custom object.
|
||||
"""
|
||||
if isinstance(obj, list):
|
||||
deserialized = []
|
||||
for value in obj:
|
||||
if value in custom_objects:
|
||||
deserialized.append(custom_objects[value])
|
||||
else:
|
||||
deserialized.append(value)
|
||||
deserialized.append(convert_custom_objects(value))
|
||||
return deserialized
|
||||
if isinstance(obj, dict):
|
||||
deserialized = {}
|
||||
for key, value in obj.items():
|
||||
deserialized[key] = []
|
||||
if isinstance(value, list):
|
||||
for element in value:
|
||||
if element in custom_objects:
|
||||
deserialized[key].append(custom_objects[element])
|
||||
else:
|
||||
deserialized[key].append(element)
|
||||
elif value in custom_objects:
|
||||
deserialized[key] = custom_objects[value]
|
||||
else:
|
||||
deserialized[key] = value
|
||||
deserialized[key] = convert_custom_objects(value)
|
||||
return deserialized
|
||||
if obj in custom_objects:
|
||||
return custom_objects[obj]
|
||||
return obj
|
||||
with h5py.File(filepath, mode='r') as f:
|
||||
# instantiate model
|
||||
model_config = f.attrs.get('model_config')
|
||||
if model_config is None:
|
||||
raise ValueError('No model found in config file.')
|
||||
model_config = json.loads(model_config.decode('utf-8'))
|
||||
model = model_from_config(model_config, custom_objects=custom_objects)
|
||||
|
||||
f = h5py.File(filepath, mode='r')
|
||||
# set weights
|
||||
topology.load_weights_from_hdf5_group(f['model_weights'], model.layers)
|
||||
|
||||
# instantiate model
|
||||
model_config = f.attrs.get('model_config')
|
||||
if model_config is None:
|
||||
raise ValueError('No model found in config file.')
|
||||
model_config = json.loads(model_config.decode('utf-8'))
|
||||
model = model_from_config(model_config, custom_objects=custom_objects)
|
||||
# Early return if compilation is not required.
|
||||
if not compile:
|
||||
return model
|
||||
|
||||
# set weights
|
||||
topology.load_weights_from_hdf5_group(f['model_weights'], model.layers)
|
||||
# instantiate optimizer
|
||||
training_config = f.attrs.get('training_config')
|
||||
if training_config is None:
|
||||
warnings.warn('No training configuration found in save file: '
|
||||
'the model was *not* compiled. Compile it manually.')
|
||||
return model
|
||||
training_config = json.loads(training_config.decode('utf-8'))
|
||||
optimizer_config = training_config['optimizer_config']
|
||||
optimizer = optimizers.deserialize(optimizer_config,
|
||||
custom_objects=custom_objects)
|
||||
|
||||
# Early return if compilation is not required.
|
||||
if not compile:
|
||||
f.close()
|
||||
return model
|
||||
# Recover loss functions and metrics.
|
||||
loss = convert_custom_objects(training_config['loss'])
|
||||
metrics = convert_custom_objects(training_config['metrics'])
|
||||
sample_weight_mode = training_config['sample_weight_mode']
|
||||
loss_weights = training_config['loss_weights']
|
||||
|
||||
# instantiate optimizer
|
||||
training_config = f.attrs.get('training_config')
|
||||
if training_config is None:
|
||||
warnings.warn('No training configuration found in save file: '
|
||||
'the model was *not* compiled. Compile it manually.')
|
||||
f.close()
|
||||
return model
|
||||
training_config = json.loads(training_config.decode('utf-8'))
|
||||
optimizer_config = training_config['optimizer_config']
|
||||
optimizer = optimizers.deserialize(optimizer_config,
|
||||
custom_objects=custom_objects)
|
||||
# Compile model.
|
||||
model.compile(optimizer=optimizer,
|
||||
loss=loss,
|
||||
metrics=metrics,
|
||||
loss_weights=loss_weights,
|
||||
sample_weight_mode=sample_weight_mode)
|
||||
|
||||
# Recover loss functions and metrics.
|
||||
loss = convert_custom_objects(training_config['loss'])
|
||||
metrics = convert_custom_objects(training_config['metrics'])
|
||||
sample_weight_mode = training_config['sample_weight_mode']
|
||||
loss_weights = training_config['loss_weights']
|
||||
|
||||
# Compile model.
|
||||
model.compile(optimizer=optimizer,
|
||||
loss=loss,
|
||||
metrics=metrics,
|
||||
loss_weights=loss_weights,
|
||||
sample_weight_mode=sample_weight_mode)
|
||||
|
||||
# Set optimizer weights.
|
||||
if 'optimizer_weights' in f:
|
||||
# Build train function (to get weight updates).
|
||||
if isinstance(model, Sequential):
|
||||
model.model._make_train_function()
|
||||
else:
|
||||
model._make_train_function()
|
||||
optimizer_weights_group = f['optimizer_weights']
|
||||
optimizer_weight_names = [n.decode('utf8') for n in optimizer_weights_group.attrs['weight_names']]
|
||||
optimizer_weight_values = [optimizer_weights_group[n] for n in optimizer_weight_names]
|
||||
model.optimizer.set_weights(optimizer_weight_values)
|
||||
f.close()
|
||||
# Set optimizer weights.
|
||||
if 'optimizer_weights' in f:
|
||||
# Build train function (to get weight updates).
|
||||
if isinstance(model, Sequential):
|
||||
model.model._make_train_function()
|
||||
else:
|
||||
model._make_train_function()
|
||||
optimizer_weights_group = f['optimizer_weights']
|
||||
optimizer_weight_names = [n.decode('utf8') for n in
|
||||
optimizer_weights_group.attrs['weight_names']]
|
||||
optimizer_weight_values = [optimizer_weights_group[n] for n in
|
||||
optimizer_weight_names]
|
||||
try:
|
||||
model.optimizer.set_weights(optimizer_weight_values)
|
||||
except ValueError:
|
||||
warnings.warn('Error in loading the saved optimizer '
|
||||
'state. As a result, your model is '
|
||||
'starting with a freshly initialized '
|
||||
'optimizer.')
|
||||
return model
|
||||
|
||||
|
||||
@@ -304,7 +298,7 @@ def model_from_config(config, custom_objects=None):
|
||||
A Keras model instance (uncompiled).
|
||||
|
||||
# Raises
|
||||
TypeError if `config` is not a dictionary
|
||||
TypeError: if `config` is not a dictionary.
|
||||
"""
|
||||
if isinstance(config, list):
|
||||
raise TypeError('`model_from_config` expects a dictionary, not a list. '
|
||||
@@ -1032,9 +1026,9 @@ class Sequential(Model):
|
||||
validation_data=None,
|
||||
validation_steps=None,
|
||||
class_weight=None,
|
||||
max_q_size=10,
|
||||
max_queue_size=10,
|
||||
workers=1,
|
||||
pickle_safe=False,
|
||||
use_multiprocessing=False,
|
||||
initial_epoch=0):
|
||||
"""Fits the model on data generated batch-by-batch by a Python generator.
|
||||
|
||||
@@ -1071,9 +1065,9 @@ class Sequential(Model):
|
||||
validation dataset divided by the batch size.
|
||||
class_weight: Dictionary mapping class indices to a weight
|
||||
for the class.
|
||||
max_q_size: Maximum size for the generator queue
|
||||
max_queue_size: Maximum size for the generator queue
|
||||
workers: Maximum number of processes to spin up
|
||||
pickle_safe: Ff True, use process based threading.
|
||||
use_multiprocessing: if True, use process based threading.
|
||||
Note that because
|
||||
this implementation relies on multiprocessing,
|
||||
you should not pass
|
||||
@@ -1117,15 +1111,15 @@ class Sequential(Model):
|
||||
validation_data=validation_data,
|
||||
validation_steps=validation_steps,
|
||||
class_weight=class_weight,
|
||||
max_q_size=max_q_size,
|
||||
max_queue_size=max_queue_size,
|
||||
workers=workers,
|
||||
pickle_safe=pickle_safe,
|
||||
use_multiprocessing=use_multiprocessing,
|
||||
initial_epoch=initial_epoch)
|
||||
|
||||
@interfaces.legacy_generator_methods_support
|
||||
def evaluate_generator(self, generator, steps,
|
||||
max_q_size=10, workers=1,
|
||||
pickle_safe=False):
|
||||
max_queue_size=10, workers=1,
|
||||
use_multiprocessing=False):
|
||||
"""Evaluates the model on a data generator.
|
||||
|
||||
The generator should return the same kind of data
|
||||
@@ -1136,9 +1130,9 @@ class Sequential(Model):
|
||||
or (inputs, targets, sample_weights)
|
||||
steps: Total number of steps (batches of samples)
|
||||
to yield from `generator` before stopping.
|
||||
max_q_size: maximum size for the generator queue
|
||||
max_queue_size: maximum size for the generator queue
|
||||
workers: maximum number of processes to spin up
|
||||
pickle_safe: if True, use process based threading.
|
||||
use_multiprocessing: if True, use process based threading.
|
||||
Note that because this implementation
|
||||
relies on multiprocessing, you should not pass
|
||||
non picklable arguments to the generator
|
||||
@@ -1158,14 +1152,14 @@ class Sequential(Model):
|
||||
'before being used.')
|
||||
return self.model.evaluate_generator(generator,
|
||||
steps,
|
||||
max_q_size=max_q_size,
|
||||
max_queue_size=max_queue_size,
|
||||
workers=workers,
|
||||
pickle_safe=pickle_safe)
|
||||
use_multiprocessing=use_multiprocessing)
|
||||
|
||||
@interfaces.legacy_generator_methods_support
|
||||
def predict_generator(self, generator, steps,
|
||||
max_q_size=10, workers=1,
|
||||
pickle_safe=False, verbose=0):
|
||||
max_queue_size=10, workers=1,
|
||||
use_multiprocessing=False, verbose=0):
|
||||
"""Generates predictions for the input samples from a data generator.
|
||||
|
||||
The generator should return the same kind of data as accepted by
|
||||
@@ -1175,9 +1169,9 @@ class Sequential(Model):
|
||||
generator: generator yielding batches of input samples.
|
||||
steps: Total number of steps (batches of samples)
|
||||
to yield from `generator` before stopping.
|
||||
max_q_size: maximum size for the generator queue
|
||||
max_queue_size: maximum size for the generator queue
|
||||
workers: maximum number of processes to spin up
|
||||
pickle_safe: if True, use process based threading.
|
||||
use_multiprocessing: if True, use process based threading.
|
||||
Note that because this implementation
|
||||
relies on multiprocessing, you should not pass
|
||||
non picklable arguments to the generator
|
||||
@@ -1190,9 +1184,9 @@ class Sequential(Model):
|
||||
if self.model is None:
|
||||
self.build()
|
||||
return self.model.predict_generator(generator, steps,
|
||||
max_q_size=max_q_size,
|
||||
max_queue_size=max_queue_size,
|
||||
workers=workers,
|
||||
pickle_safe=pickle_safe,
|
||||
use_multiprocessing=use_multiprocessing,
|
||||
verbose=verbose)
|
||||
|
||||
def get_config(self):
|
||||
|
||||
+29
-7
@@ -1,5 +1,6 @@
|
||||
from __future__ import absolute_import
|
||||
import six
|
||||
import copy
|
||||
from six.moves import zip
|
||||
|
||||
from . import backend as K
|
||||
@@ -11,8 +12,31 @@ if K.backend() == 'tensorflow':
|
||||
|
||||
|
||||
def clip_norm(g, c, n):
|
||||
if c > 0:
|
||||
g = K.switch(n >= c, g * c / n, g)
|
||||
if c <= 0: # if clipnorm == 0 no need to add ops to the graph
|
||||
return g
|
||||
|
||||
# tf require using a special op to multiply IndexedSliced by scalar
|
||||
if K.backend() == 'tensorflow':
|
||||
condition = n >= c
|
||||
then_expression = tf.scalar_mul(c / n, g)
|
||||
else_expression = g
|
||||
|
||||
# saving the shape to avoid converting sparse tensor to dense
|
||||
if isinstance(then_expression, tf.Tensor):
|
||||
g_shape = copy.copy(then_expression.get_shape())
|
||||
elif isinstance(then_expression, tf.IndexedSlices):
|
||||
g_shape = copy.copy(then_expression.dense_shape)
|
||||
if condition.dtype != tf.bool:
|
||||
condition = tf.cast(condition, 'bool')
|
||||
g = tf.cond(condition,
|
||||
lambda: then_expression,
|
||||
lambda: else_expression)
|
||||
if isinstance(then_expression, tf.Tensor):
|
||||
g.set_shape(g_shape)
|
||||
elif isinstance(then_expression, tf.IndexedSlices):
|
||||
g._dense_shape = g_shape
|
||||
else:
|
||||
g = K.switch(K.greater_equal(n, c), g * c / n, g)
|
||||
return g
|
||||
|
||||
|
||||
@@ -195,8 +219,7 @@ class RMSprop(Optimizer):
|
||||
|
||||
def get_updates(self, params, constraints, loss):
|
||||
grads = self.get_gradients(loss, params)
|
||||
shapes = [K.get_variable_shape(p) for p in params]
|
||||
accumulators = [K.zeros(shape) for shape in shapes]
|
||||
accumulators = [K.zeros(K.get_variable_shape(p), dtype=K.dtype(p)) for p in params]
|
||||
self.weights = accumulators
|
||||
self.updates = []
|
||||
|
||||
@@ -389,9 +412,8 @@ class Adam(Optimizer):
|
||||
lr_t = lr * (K.sqrt(1. - K.pow(self.beta_2, t)) /
|
||||
(1. - K.pow(self.beta_1, t)))
|
||||
|
||||
shapes = [K.get_variable_shape(p) for p in params]
|
||||
ms = [K.zeros(shape) for shape in shapes]
|
||||
vs = [K.zeros(shape) for shape in shapes]
|
||||
ms = [K.zeros(K.get_variable_shape(p), dtype=K.dtype(p)) for p in params]
|
||||
vs = [K.zeros(K.get_variable_shape(p), dtype=K.dtype(p)) for p in params]
|
||||
self.weights = [self.iterations] + ms + vs
|
||||
|
||||
for p, g, m, v in zip(params, grads, ms, vs):
|
||||
|
||||
@@ -13,6 +13,8 @@ from six.moves import range
|
||||
import os
|
||||
import threading
|
||||
import warnings
|
||||
import multiprocessing.pool
|
||||
from functools import partial
|
||||
|
||||
from .. import backend as K
|
||||
|
||||
@@ -137,7 +139,7 @@ def random_zoom(x, zoom_range, row_axis=1, col_axis=2, channel_axis=0,
|
||||
ValueError: if `zoom_range` isn't a tuple.
|
||||
"""
|
||||
if len(zoom_range) != 2:
|
||||
raise ValueError('zoom_range should be a tuple or list of two floats. '
|
||||
raise ValueError('`zoom_range` should be a tuple or list of two floats. '
|
||||
'Received arg: ', zoom_range)
|
||||
|
||||
if zoom_range[0] == 1 and zoom_range[1] == 1:
|
||||
@@ -419,8 +421,8 @@ class ImageDataGenerator(object):
|
||||
self.preprocessing_function = preprocessing_function
|
||||
|
||||
if data_format not in {'channels_last', 'channels_first'}:
|
||||
raise ValueError('data_format should be "channels_last" (channel after row and '
|
||||
'column) or "channels_first" (channel before row and column). '
|
||||
raise ValueError('`data_format` should be `"channels_last"` (channel after row and '
|
||||
'column) or `"channels_first"` (channel before row and column). '
|
||||
'Received arg: ', data_format)
|
||||
self.data_format = data_format
|
||||
if data_format == 'channels_first':
|
||||
@@ -441,7 +443,7 @@ class ImageDataGenerator(object):
|
||||
elif len(zoom_range) == 2:
|
||||
self.zoom_range = [zoom_range[0], zoom_range[1]]
|
||||
else:
|
||||
raise ValueError('zoom_range should be a float or '
|
||||
raise ValueError('`zoom_range` should be a float or '
|
||||
'a tuple or list of two floats. '
|
||||
'Received arg: ', zoom_range)
|
||||
|
||||
@@ -636,8 +638,8 @@ class ImageDataGenerator(object):
|
||||
if x.ndim != 4:
|
||||
raise ValueError('Input to `.fit()` should have rank 4. '
|
||||
'Got array with shape: ' + str(x.shape))
|
||||
if x.shape[self.channel_axis] not in {1, 3, 4}:
|
||||
raise ValueError(
|
||||
if x.shape[self.channel_axis] not in {3, 4}:
|
||||
warnings.warn(
|
||||
'Expected input to be images (as Numpy array) '
|
||||
'following the data format convention "' + self.data_format + '" '
|
||||
'(channels on axis ' + str(self.channel_axis) + '), i.e. expected '
|
||||
@@ -821,6 +823,73 @@ class NumpyArrayIterator(Iterator):
|
||||
return batch_x, batch_y
|
||||
|
||||
|
||||
def _count_valid_files_in_directory(directory, white_list_formats, follow_links):
|
||||
"""Count files with extension in `white_list_formats` contained in a directory.
|
||||
|
||||
# Arguments
|
||||
directory: absolute path to the directory containing files to be counted
|
||||
white_list_formats: set of strings containing allowed extensions for
|
||||
the files to be counted.
|
||||
|
||||
# Returns
|
||||
the count of files with extension in `white_list_formats` contained in
|
||||
the directory.
|
||||
"""
|
||||
def _recursive_list(subpath):
|
||||
return sorted(os.walk(subpath, followlinks=follow_links), key=lambda tpl: tpl[0])
|
||||
|
||||
samples = 0
|
||||
for root, _, files in _recursive_list(directory):
|
||||
for fname in files:
|
||||
is_valid = False
|
||||
for extension in white_list_formats:
|
||||
if fname.lower().endswith('.' + extension):
|
||||
is_valid = True
|
||||
break
|
||||
if is_valid:
|
||||
samples += 1
|
||||
return samples
|
||||
|
||||
|
||||
def _list_valid_filenames_in_directory(directory, white_list_formats,
|
||||
class_indices, follow_links):
|
||||
"""List paths of files in `subdir` relative from `directory` whose extensions are in `white_list_formats`.
|
||||
|
||||
# Arguments
|
||||
directory: absolute path to a directory containing the files to list.
|
||||
The directory name is used as class label and must be a key of `class_indices`.
|
||||
white_list_formats: set of strings containing allowed extensions for
|
||||
the files to be counted.
|
||||
class_indices: dictionary mapping a class name to its index.
|
||||
|
||||
# Returns
|
||||
classes: a list of class indices
|
||||
filenames: the path of valid files in `directory`, relative from
|
||||
`directory`'s parent (e.g., if `directory` is "dataset/class1",
|
||||
the filenames will be ["class1/file1.jpg", "class1/file2.jpg", ...]).
|
||||
"""
|
||||
def _recursive_list(subpath):
|
||||
return sorted(os.walk(subpath, followlinks=follow_links), key=lambda tpl: tpl[0])
|
||||
|
||||
classes = []
|
||||
filenames = []
|
||||
subdir = os.path.basename(directory)
|
||||
basedir = os.path.dirname(directory)
|
||||
for root, _, files in _recursive_list(directory):
|
||||
for fname in files:
|
||||
is_valid = False
|
||||
for extension in white_list_formats:
|
||||
if fname.lower().endswith('.' + extension):
|
||||
is_valid = True
|
||||
break
|
||||
if is_valid:
|
||||
classes.append(class_indices[subdir])
|
||||
# add filename relative to directory
|
||||
absolute_path = os.path.join(root, fname)
|
||||
filenames.append(os.path.relpath(absolute_path, basedir))
|
||||
return classes, filenames
|
||||
|
||||
|
||||
class DirectoryIterator(Iterator):
|
||||
"""Iterator capable of reading images from a directory on disk.
|
||||
|
||||
@@ -913,38 +982,33 @@ class DirectoryIterator(Iterator):
|
||||
def _recursive_list(subpath):
|
||||
return sorted(os.walk(subpath, followlinks=follow_links), key=lambda tpl: tpl[0])
|
||||
|
||||
for subdir in classes:
|
||||
subpath = os.path.join(directory, subdir)
|
||||
for root, _, files in _recursive_list(subpath):
|
||||
for fname in files:
|
||||
is_valid = False
|
||||
for extension in white_list_formats:
|
||||
if fname.lower().endswith('.' + extension):
|
||||
is_valid = True
|
||||
break
|
||||
if is_valid:
|
||||
self.samples += 1
|
||||
pool = multiprocessing.pool.ThreadPool()
|
||||
function_partial = partial(_count_valid_files_in_directory,
|
||||
white_list_formats=white_list_formats,
|
||||
follow_links=follow_links)
|
||||
self.samples = sum(pool.map(function_partial,
|
||||
(os.path.join(directory, subdir)
|
||||
for subdir in classes)))
|
||||
|
||||
print('Found %d images belonging to %d classes.' % (self.samples, self.num_class))
|
||||
|
||||
# second, build an index of the images in the different class subfolders
|
||||
results = []
|
||||
|
||||
self.filenames = []
|
||||
self.classes = np.zeros((self.samples,), dtype='int32')
|
||||
i = 0
|
||||
for subdir in classes:
|
||||
subpath = os.path.join(directory, subdir)
|
||||
for root, _, files in _recursive_list(subpath):
|
||||
for fname in files:
|
||||
is_valid = False
|
||||
for extension in white_list_formats:
|
||||
if fname.lower().endswith('.' + extension):
|
||||
is_valid = True
|
||||
break
|
||||
if is_valid:
|
||||
self.classes[i] = self.class_indices[subdir]
|
||||
i += 1
|
||||
# add filename relative to directory
|
||||
absolute_path = os.path.join(root, fname)
|
||||
self.filenames.append(os.path.relpath(absolute_path, directory))
|
||||
for dirpath in (os.path.join(directory, subdir) for subdir in classes):
|
||||
results.append(pool.apply_async(_list_valid_filenames_in_directory,
|
||||
(dirpath, white_list_formats,
|
||||
self.class_indices, follow_links)))
|
||||
for res in results:
|
||||
classes, filenames = res.get()
|
||||
self.classes[i:i + len(classes)] = classes
|
||||
self.filenames += filenames
|
||||
i += len(classes)
|
||||
pool.close()
|
||||
pool.join()
|
||||
super(DirectoryIterator, self).__init__(self.samples, batch_size, shuffle, seed)
|
||||
|
||||
def next(self):
|
||||
|
||||
@@ -104,7 +104,7 @@ def make_sampling_table(size, sampling_factor=1e-5):
|
||||
is the probability that a word of rank i should be sampled.
|
||||
"""
|
||||
gamma = 0.577
|
||||
rank = np.array(list(range(size)))
|
||||
rank = np.arange(size)
|
||||
rank[0] = 1
|
||||
inv_fq = rank * (np.log(rank) + gamma) + 0.5 - 1. / (12. * rank)
|
||||
f = sampling_factor * inv_fq
|
||||
@@ -127,7 +127,7 @@ def skipgrams(sequence, vocabulary_size,
|
||||
of word indices (integers). If using a `sampling_table`,
|
||||
word indices are expected to match the rank
|
||||
of the words in a reference dataset (e.g. 10 would encode
|
||||
the 10-th most frequently occuring token).
|
||||
the 10-th most frequently occurring token).
|
||||
Note that index 0 is expected to be a non-word and will be skipped.
|
||||
vocabulary_size: int. maximum possible word index + 1
|
||||
window_size: int. actually half-window.
|
||||
@@ -191,3 +191,22 @@ def skipgrams(sequence, vocabulary_size,
|
||||
random.shuffle(labels)
|
||||
|
||||
return couples, labels
|
||||
|
||||
|
||||
def _remove_long_seq(maxlen, seq, label):
|
||||
"""Removes sequences that exceed the maximum length.
|
||||
|
||||
# Arguments
|
||||
maxlen: int, maximum length
|
||||
seq: list of lists where each sublist is a sequence
|
||||
label: list where each element is an integer
|
||||
|
||||
# Returns
|
||||
new_seq, new_label: shortened lists for `seq` and `label`.
|
||||
"""
|
||||
new_seq, new_label = [], []
|
||||
for x, y in zip(seq, label):
|
||||
if len(x) < maxlen:
|
||||
new_seq.append(x)
|
||||
new_label.append(y)
|
||||
return new_seq, new_label
|
||||
|
||||
@@ -8,11 +8,13 @@ from __future__ import division
|
||||
|
||||
import string
|
||||
import sys
|
||||
import warnings
|
||||
from collections import OrderedDict
|
||||
from hashlib import md5
|
||||
|
||||
import numpy as np
|
||||
from six.moves import range
|
||||
from six.moves import zip
|
||||
from collections import OrderedDict
|
||||
import warnings
|
||||
|
||||
if sys.version_info < (3,):
|
||||
maketrans = string.maketrans
|
||||
@@ -45,11 +47,58 @@ def one_hot(text, n,
|
||||
filters='!"#$%&()*+,-./:;<=>?@[\\]^_`{|}~\t\n',
|
||||
lower=True,
|
||||
split=' '):
|
||||
"""One-hot encodes a text into a list of word indexes of size n.
|
||||
|
||||
This is a wrapper to the `hashing_trick` function using `hash` as the
|
||||
hashing function, unicity of word to index mapping non-guaranteed.
|
||||
"""
|
||||
return hashing_trick(text, n,
|
||||
hash_function=hash,
|
||||
filters=filters,
|
||||
lower=lower,
|
||||
split=split)
|
||||
|
||||
|
||||
def hashing_trick(text, n,
|
||||
hash_function=None,
|
||||
filters='!"#$%&()*+,-./:;<=>?@[\\]^_`{|}~\t\n',
|
||||
lower=True,
|
||||
split=' '):
|
||||
"""Converts a text to a sequence of indexes in a fixed-size hashing space.
|
||||
|
||||
# Arguments
|
||||
text: Input text (string).
|
||||
n: Dimension of the hashing space.
|
||||
hash_function: if `None` uses python `hash` function, can be 'md5' or
|
||||
any function that takes in input a string and returns a int.
|
||||
Note that `hash` is not a stable hashing function, so
|
||||
it is not consistent across different runs, while 'md5'
|
||||
is a stable hashing function.
|
||||
filters: Sequence of characters to filter out.
|
||||
lower: Whether to convert the input to lowercase.
|
||||
split: Sentence split marker (string).
|
||||
|
||||
# Returns
|
||||
A list of integer word indices (unicity non-guaranteed).
|
||||
|
||||
`0` is a reserved index that won't be assigned to any word.
|
||||
|
||||
Two or more words may be assigned to the same index, due to possible
|
||||
collisions by the hashing function.
|
||||
The [probability](https://en.wikipedia.org/wiki/Birthday_problem#Probability_table)
|
||||
of a collision is in relation to the dimension of the hashing space and
|
||||
the number of distinct objects.
|
||||
"""
|
||||
if hash_function is None:
|
||||
hash_function = hash
|
||||
elif hash_function == 'md5':
|
||||
hash_function = lambda w: int(md5(w.encode()).hexdigest(), 16)
|
||||
|
||||
seq = text_to_word_sequence(text,
|
||||
filters=filters,
|
||||
lower=lower,
|
||||
split=split)
|
||||
return [(abs(hash(w)) % (n - 1) + 1) for w in seq]
|
||||
return [(hash_function(w) % (n - 1) + 1) for w in seq]
|
||||
|
||||
|
||||
class Tokenizer(object):
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
from __future__ import absolute_import
|
||||
from . import np_utils
|
||||
from . import conv_utils
|
||||
from . import data_utils
|
||||
from . import generic_utils
|
||||
from . import data_utils
|
||||
from . import io_utils
|
||||
from . import conv_utils
|
||||
|
||||
# Globally-importable utils.
|
||||
from .io_utils import HDF5Matrix
|
||||
from .data_utils import get_file
|
||||
from .data_utils import Sequence
|
||||
from .data_utils import GeneratorEnqueuer
|
||||
from .data_utils import OrderedEnqueuer
|
||||
from .generic_utils import CustomObjectScope
|
||||
from .generic_utils import custom_object_scope
|
||||
from .generic_utils import get_custom_objects
|
||||
|
||||
+360
-8
@@ -2,20 +2,32 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import print_function
|
||||
|
||||
import tarfile
|
||||
import zipfile
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import hashlib
|
||||
import multiprocessing
|
||||
import os
|
||||
import random
|
||||
import shutil
|
||||
import sys
|
||||
import tarfile
|
||||
import threading
|
||||
import time
|
||||
import zipfile
|
||||
from abc import abstractmethod
|
||||
from multiprocessing.pool import ThreadPool
|
||||
|
||||
import numpy as np
|
||||
import six
|
||||
from six.moves.urllib.request import urlopen
|
||||
from six.moves.urllib.error import URLError
|
||||
from six.moves.urllib.error import HTTPError
|
||||
from six.moves.urllib.error import URLError
|
||||
from six.moves.urllib.request import urlopen
|
||||
|
||||
try:
|
||||
import queue
|
||||
except ImportError:
|
||||
import Queue as queue
|
||||
|
||||
from ..utils.generic_utils import Progbar
|
||||
|
||||
|
||||
if sys.version_info[0] == 2:
|
||||
def urlretrieve(url, filename, reporthook=None, data=None):
|
||||
"""Replacement for `urlretrive` for Python 2.
|
||||
@@ -34,6 +46,7 @@ if sys.version_info[0] == 2:
|
||||
a block size in bytes, and the total size of the file.
|
||||
data: `data` argument passed to `urlopen`.
|
||||
"""
|
||||
|
||||
def chunk_read(response, chunk_size=8192, reporthook=None):
|
||||
content_type = response.info().get('Content-Length')
|
||||
total_size = -1
|
||||
@@ -282,3 +295,342 @@ def validate_file(fpath, file_hash, algorithm='auto', chunk_size=65535):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
class Sequence(object):
|
||||
"""Base object for fitting to a sequence of data, such as a dataset.
|
||||
|
||||
Every `Sequence` must implements the `__getitem__` and the `__len__` methods.
|
||||
|
||||
# Examples
|
||||
|
||||
```python
|
||||
from skimage.io import imread
|
||||
from skimage.transform import resize
|
||||
import numpy as np
|
||||
|
||||
# Here, `x_set` is list of path to the images
|
||||
# and `y_set` are the associated classes.
|
||||
|
||||
class CIFAR10Sequence(Sequence):
|
||||
def __init__(self, x_set, y_set, batch_size):
|
||||
self.X,self.y = x_set,y_set
|
||||
self.batch_size = batch_size
|
||||
|
||||
def __len__(self):
|
||||
return len(self.X) // self.batch_size
|
||||
|
||||
def __getitem__(self,idx):
|
||||
batch_x = self.X[idx*self.batch_size:(idx+1)*self.batch_size]
|
||||
batch_y = self.y[idx*self.batch_size:(idx+1)*self.batch_size]
|
||||
|
||||
return np.array([
|
||||
resize(imread(file_name), (200,200))
|
||||
for file_name in batch_x]), np.array(batch_y)
|
||||
```
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def __getitem__(self, index):
|
||||
"""Gets batch at position `index`.
|
||||
|
||||
# Arguments
|
||||
index: position of the batch in the Sequence.
|
||||
|
||||
# Returns
|
||||
A batch
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def __len__(self):
|
||||
"""Number of batch in the Sequence.
|
||||
|
||||
# Returns
|
||||
The number of batches in the Sequence.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def get_index(ds, i):
|
||||
"""Quick fix for Python2, otherwise, it cannot be pickled.
|
||||
|
||||
# Arguments
|
||||
ds: a Sequence object
|
||||
i: index
|
||||
|
||||
# Returns
|
||||
The value at index `i`.
|
||||
"""
|
||||
return ds[i]
|
||||
|
||||
|
||||
class SequenceEnqueuer(object):
|
||||
"""Base class to enqueue inputs.
|
||||
|
||||
The task of an Enqueuer is to use parallelism to speed up preprocessing.
|
||||
This is done with processes or threads.
|
||||
|
||||
# Examples
|
||||
|
||||
```python
|
||||
enqueuer = SequenceEnqueuer(...)
|
||||
enqueuer.start()
|
||||
datas = enqueuer.get()
|
||||
for data in datas:
|
||||
# Use the inputs; training, evaluating, predicting.
|
||||
# ... stop sometime.
|
||||
enqueuer.close()
|
||||
```
|
||||
|
||||
The `enqueuer.get()` should be an infinite stream of datas.
|
||||
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def is_running(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def start(self, workers=1, max_queue_size=10):
|
||||
"""Starts the handler's workers.
|
||||
|
||||
# Arguments
|
||||
workers: number of worker threads
|
||||
max_queue_size: queue size
|
||||
(when full, threads could block on `put()`).
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def stop(self, timeout=None):
|
||||
"""Stop running threads and wait for them to exit, if necessary.
|
||||
|
||||
Should be called by the same thread which called start().
|
||||
|
||||
# Arguments
|
||||
timeout: maximum time to wait on thread.join()
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def get(self):
|
||||
"""Creates a generator to extract data from the queue.
|
||||
|
||||
Skip the data if it is `None`.
|
||||
|
||||
# Returns
|
||||
Generator yielding tuples `(inputs, targets)`
|
||||
or `(inputs, targets, sample_weights)`.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class OrderedEnqueuer(SequenceEnqueuer):
|
||||
"""Builds a Enqueuer from a Sequence.
|
||||
|
||||
Used in `fit_generator`, `evaluate_generator`, `predict_generator`.
|
||||
|
||||
# Arguments
|
||||
sequence: A `keras.utils.data_utils.Sequence` object.
|
||||
use_multiprocessing: use multiprocessing if True, otherwise threading
|
||||
scheduling: Sequential querying of datas if 'sequential', random otherwise.
|
||||
"""
|
||||
|
||||
def __init__(self, sequence,
|
||||
use_multiprocessing=False,
|
||||
scheduling='sequential'):
|
||||
self.sequence = sequence
|
||||
self.use_multiprocessing = use_multiprocessing
|
||||
self.scheduling = scheduling
|
||||
self.workers = 0
|
||||
self.executor = None
|
||||
self.queue = None
|
||||
self.run_thread = None
|
||||
self.stop_signal = None
|
||||
|
||||
def is_running(self):
|
||||
return self.stop_signal is not None and not self.stop_signal.is_set()
|
||||
|
||||
def start(self, workers=1, max_queue_size=10):
|
||||
"""Start the handler's workers.
|
||||
|
||||
# Arguments
|
||||
workers: number of worker threads
|
||||
max_queue_size: queue size
|
||||
(when full, workers could block on `put()`)
|
||||
"""
|
||||
if self.use_multiprocessing:
|
||||
self.executor = multiprocessing.Pool(workers)
|
||||
else:
|
||||
self.executor = ThreadPool(workers)
|
||||
self.queue = queue.Queue(max_queue_size)
|
||||
self.stop_signal = threading.Event()
|
||||
self.run_thread = threading.Thread(target=self._run)
|
||||
self.run_thread.daemon = True
|
||||
self.run_thread.start()
|
||||
|
||||
def _run(self):
|
||||
"""Function to submit request to the executor and queue the `Future` objects."""
|
||||
sequence = list(range(len(self.sequence)))
|
||||
while True:
|
||||
if self.scheduling is not 'sequential':
|
||||
random.shuffle(sequence)
|
||||
for i in sequence:
|
||||
if self.stop_signal.is_set():
|
||||
return
|
||||
self.queue.put(
|
||||
self.executor.apply_async(get_index,
|
||||
(self.sequence, i)), block=True)
|
||||
|
||||
def get(self):
|
||||
"""Creates a generator to extract data from the queue.
|
||||
|
||||
Skip the data if it is `None`.
|
||||
|
||||
# Returns
|
||||
Generator yielding tuples (inputs, targets)
|
||||
or (inputs, targets, sample_weights)
|
||||
"""
|
||||
try:
|
||||
while self.is_running():
|
||||
inputs = self.queue.get(block=True).get()
|
||||
if inputs is not None:
|
||||
yield inputs
|
||||
except Exception as e:
|
||||
self.stop()
|
||||
raise StopIteration(e)
|
||||
|
||||
def stop(self, timeout=None):
|
||||
"""Stops running threads and wait for them to exit, if necessary.
|
||||
|
||||
Should be called by the same thread which called `start()`.
|
||||
|
||||
# Arguments
|
||||
timeout: maximum time to wait on `thread.join()`
|
||||
"""
|
||||
self.stop_signal.set()
|
||||
with self.queue.mutex:
|
||||
self.queue.queue.clear()
|
||||
self.queue.unfinished_tasks = 0
|
||||
self.queue.not_full.notify()
|
||||
self.executor.close()
|
||||
self.executor.join()
|
||||
self.run_thread.join(timeout)
|
||||
|
||||
|
||||
class GeneratorEnqueuer(SequenceEnqueuer):
|
||||
"""Builds a queue out of a data generator.
|
||||
|
||||
Used in `fit_generator`, `evaluate_generator`, `predict_generator`.
|
||||
|
||||
# Arguments
|
||||
generator: a generator function which endlessly yields data
|
||||
use_multiprocessing: use multiprocessing if True, otherwise threading
|
||||
wait_time: time to sleep in-between calls to `put()`
|
||||
random_seed: Initial seed for workers,
|
||||
will be incremented by one for each workers.
|
||||
"""
|
||||
|
||||
def __init__(self, generator,
|
||||
use_multiprocessing=False,
|
||||
wait_time=0.05,
|
||||
random_seed=None):
|
||||
self.wait_time = wait_time
|
||||
self._generator = generator
|
||||
self._use_multiprocessing = use_multiprocessing
|
||||
self._threads = []
|
||||
self._stop_event = None
|
||||
self.queue = None
|
||||
self.random_seed = random_seed
|
||||
|
||||
def start(self, workers=1, max_queue_size=10):
|
||||
"""Kicks off threads which add data from the generator into the queue.
|
||||
|
||||
# Arguments
|
||||
workers: number of worker threads
|
||||
max_queue_size: queue size
|
||||
(when full, threads could block on `put()`)
|
||||
"""
|
||||
|
||||
def data_generator_task():
|
||||
while not self._stop_event.is_set():
|
||||
try:
|
||||
if self._use_multiprocessing or self.queue.qsize() < max_queue_size:
|
||||
generator_output = next(self._generator)
|
||||
self.queue.put(generator_output)
|
||||
else:
|
||||
time.sleep(self.wait_time)
|
||||
except Exception:
|
||||
self._stop_event.set()
|
||||
raise
|
||||
|
||||
try:
|
||||
if self._use_multiprocessing:
|
||||
self.queue = multiprocessing.Queue(maxsize=max_queue_size)
|
||||
self._stop_event = multiprocessing.Event()
|
||||
else:
|
||||
self.queue = queue.Queue()
|
||||
self._stop_event = threading.Event()
|
||||
|
||||
for _ in range(workers):
|
||||
if self._use_multiprocessing:
|
||||
# Reset random seed else all children processes
|
||||
# share the same seed
|
||||
np.random.seed(self.random_seed)
|
||||
thread = multiprocessing.Process(target=data_generator_task)
|
||||
thread.daemon = True
|
||||
if self.random_seed is not None:
|
||||
self.random_seed += 1
|
||||
else:
|
||||
thread = threading.Thread(target=data_generator_task)
|
||||
self._threads.append(thread)
|
||||
thread.start()
|
||||
except:
|
||||
self.stop()
|
||||
raise
|
||||
|
||||
def is_running(self):
|
||||
return self._stop_event is not None and not self._stop_event.is_set()
|
||||
|
||||
def stop(self, timeout=None):
|
||||
"""Stops running threads and wait for them to exit, if necessary.
|
||||
|
||||
Should be called by the same thread which called `start()`.
|
||||
|
||||
# Arguments
|
||||
timeout: maximum time to wait on `thread.join()`.
|
||||
"""
|
||||
if self.is_running():
|
||||
self._stop_event.set()
|
||||
|
||||
for thread in self._threads:
|
||||
if thread.is_alive():
|
||||
if self._use_multiprocessing:
|
||||
thread.terminate()
|
||||
else:
|
||||
thread.join(timeout)
|
||||
|
||||
if self._use_multiprocessing:
|
||||
if self.queue is not None:
|
||||
self.queue.close()
|
||||
|
||||
self._threads = []
|
||||
self._stop_event = None
|
||||
self.queue = None
|
||||
|
||||
def get(self):
|
||||
"""Creates a generator to extract data from the queue.
|
||||
|
||||
Skip the data if it is `None`.
|
||||
|
||||
# Returns
|
||||
A generator
|
||||
"""
|
||||
while self.is_running():
|
||||
if not self.queue.empty():
|
||||
inputs = self.queue.get()
|
||||
if inputs is not None:
|
||||
yield inputs
|
||||
else:
|
||||
time.sleep(self.wait_time)
|
||||
|
||||
@@ -132,9 +132,8 @@ def deserialize_keras_object(identifier, module_objects=None,
|
||||
raise ValueError('Unknown ' + printable_module_name +
|
||||
': ' + class_name)
|
||||
if hasattr(cls, 'from_config'):
|
||||
arg_spec = inspect.getargspec(cls.from_config)
|
||||
custom_objects = custom_objects or {}
|
||||
if 'custom_objects' in arg_spec.args:
|
||||
if has_arg(cls.from_config, 'custom_objects'):
|
||||
return cls.from_config(config['config'],
|
||||
custom_objects=dict(list(_GLOBAL_CUSTOM_OBJECTS.items()) +
|
||||
list(custom_objects.items())))
|
||||
@@ -207,6 +206,48 @@ def func_load(code, defaults=None, closure=None, globs=None):
|
||||
closure=closure)
|
||||
|
||||
|
||||
def has_arg(fn, name, accept_all=False):
|
||||
"""Checks if a callable accepts a given keyword argument.
|
||||
|
||||
For Python 2, checks if there is an argument with the given name.
|
||||
|
||||
For Python 3, checks if there is an argument with the given name, and
|
||||
also whether this argument can be called with a keyword (i.e. if it is
|
||||
not a positional-only argument).
|
||||
|
||||
# Arguments
|
||||
fn: Callable to inspect.
|
||||
name: Check if `fn` can be called with `name` as a keyword argument.
|
||||
accept_all: What to return if there is no parameter called `name`
|
||||
but the function accepts a `**kwargs` argument.
|
||||
|
||||
# Returns
|
||||
bool, whether `fn` accepts a `name` keyword argument.
|
||||
"""
|
||||
if sys.version_info < (3,):
|
||||
arg_spec = inspect.getargspec(fn)
|
||||
if accept_all and arg_spec.keywords is not None:
|
||||
return True
|
||||
return (name in arg_spec.args)
|
||||
elif sys.version_info < (3, 3):
|
||||
arg_spec = inspect.getfullargspec(fn)
|
||||
if accept_all and arg_spec.varkw is not None:
|
||||
return True
|
||||
return (name in arg_spec.args or
|
||||
name in arg_spec.kwonlyargs)
|
||||
else:
|
||||
signature = inspect.signature(fn)
|
||||
parameter = signature.parameters.get(name)
|
||||
if parameter is None:
|
||||
if accept_all:
|
||||
for param in signature.parameters.values():
|
||||
if param.kind == inspect.Parameter.VAR_KEYWORD:
|
||||
return True
|
||||
return False
|
||||
return (parameter.kind in (inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
||||
inspect.Parameter.KEYWORD_ONLY))
|
||||
|
||||
|
||||
class Progbar(object):
|
||||
"""Displays a progress bar.
|
||||
|
||||
|
||||
@@ -96,8 +96,40 @@ class HDF5Matrix(object):
|
||||
|
||||
@property
|
||||
def shape(self):
|
||||
"""Gets a numpy-style shape tuple giving the dataset dimensions.
|
||||
|
||||
# Returns
|
||||
A numpy-style shape tuple.
|
||||
"""
|
||||
return (self.end - self.start,) + self.data.shape[1:]
|
||||
|
||||
@property
|
||||
def dtype(self):
|
||||
"""Gets the datatype of the dataset.
|
||||
|
||||
# Returns
|
||||
A numpy dtype string.
|
||||
"""
|
||||
return self.data.dtype
|
||||
|
||||
@property
|
||||
def ndim(self):
|
||||
"""Gets the number of dimensions (rank) of the dataset.
|
||||
|
||||
# Returns
|
||||
An integer denoting the number of dimensions (rank) of the dataset.
|
||||
"""
|
||||
return self.data.ndim
|
||||
|
||||
@property
|
||||
def size(self):
|
||||
"""Gets the total dataset size (number of elements).
|
||||
|
||||
# Returns
|
||||
An integer denoting the number of elements in the dataset.
|
||||
"""
|
||||
return np.prod(self.shape)
|
||||
|
||||
|
||||
def ask_to_proceed_with_overwrite(filepath):
|
||||
"""Produces a prompt asking about overwriting a file.
|
||||
|
||||
@@ -5,14 +5,20 @@ from .. import backend as K
|
||||
import numpy as np
|
||||
|
||||
|
||||
def print_summary(model, line_length=None, positions=None):
|
||||
def print_summary(model, line_length=None, positions=None, print_fn=print):
|
||||
"""Prints a summary of a model.
|
||||
|
||||
# Arguments
|
||||
model: Keras model instance.
|
||||
line_length: total length of printed lines
|
||||
positions: relative or absolute positions of log elements in each line.
|
||||
line_length: Total length of printed lines
|
||||
(e.g. set this to adapt the display to different
|
||||
terminal window sizes).
|
||||
positions: Relative or absolute positions of log elements in each line.
|
||||
If not provided, defaults to `[.33, .55, .67, 1.]`.
|
||||
print_fn: Print function to use.
|
||||
It will be called on each line of the summary.
|
||||
You can set it to a custom function
|
||||
in order to capture the string summary.
|
||||
"""
|
||||
if model.__class__.__name__ == 'Sequential':
|
||||
sequential_like = True
|
||||
@@ -51,11 +57,11 @@ def print_summary(model, line_length=None, positions=None):
|
||||
line += str(fields[i])
|
||||
line = line[:positions[i]]
|
||||
line += ' ' * (positions[i] - len(line))
|
||||
print(line)
|
||||
print_fn(line)
|
||||
|
||||
print('_' * line_length)
|
||||
print_fn('_' * line_length)
|
||||
print_row(to_display, positions)
|
||||
print('=' * line_length)
|
||||
print_fn('=' * line_length)
|
||||
|
||||
def print_layer_summary(layer):
|
||||
try:
|
||||
@@ -108,19 +114,19 @@ def print_summary(model, line_length=None, positions=None):
|
||||
else:
|
||||
print_layer_summary_with_connections(layers[i])
|
||||
if i == len(layers) - 1:
|
||||
print('=' * line_length)
|
||||
print_fn('=' * line_length)
|
||||
else:
|
||||
print('_' * line_length)
|
||||
print_fn('_' * line_length)
|
||||
|
||||
trainable_count = int(
|
||||
np.sum([K.count_params(p) for p in set(model.trainable_weights)]))
|
||||
non_trainable_count = int(
|
||||
np.sum([K.count_params(p) for p in set(model.non_trainable_weights)]))
|
||||
|
||||
print('Total params: {:,}'.format(trainable_count + non_trainable_count))
|
||||
print('Trainable params: {:,}'.format(trainable_count))
|
||||
print('Non-trainable params: {:,}'.format(non_trainable_count))
|
||||
print('_' * line_length)
|
||||
print_fn('Total params: {:,}'.format(trainable_count + non_trainable_count))
|
||||
print_fn('Trainable params: {:,}'.format(trainable_count))
|
||||
print_fn('Non-trainable params: {:,}'.format(non_trainable_count))
|
||||
print_fn('_' * line_length)
|
||||
|
||||
|
||||
def convert_all_kernels_in_model(model):
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"""Utilities related to Keras unit tests."""
|
||||
import numpy as np
|
||||
from numpy.testing import assert_allclose
|
||||
import inspect
|
||||
import six
|
||||
|
||||
from .generic_utils import has_arg
|
||||
from ..engine import Model, Input
|
||||
from ..models import Sequential
|
||||
from ..models import model_from_json
|
||||
@@ -71,7 +71,9 @@ def layer_test(layer_cls, kwargs={}, input_shape=None, input_dtype=None,
|
||||
layer.set_weights(weights)
|
||||
|
||||
# test and instantiation from weights
|
||||
if 'weights' in inspect.getargspec(layer_cls.__init__):
|
||||
# Checking for empty weights array to avoid a problem where some
|
||||
# legacy layers return bad values from get_weights()
|
||||
if has_arg(layer_cls.__init__, 'weights') and len(weights):
|
||||
kwargs['weights'] = weights
|
||||
layer = layer_cls(**kwargs)
|
||||
|
||||
|
||||
@@ -5,11 +5,15 @@ try:
|
||||
# pydot-ng is a fork of pydot that is better maintained.
|
||||
import pydot_ng as pydot
|
||||
except ImportError:
|
||||
# Fall back on pydot if necessary.
|
||||
# pydotplus is an improved version of pydot
|
||||
try:
|
||||
import pydot
|
||||
import pydotplus as pydot
|
||||
except ImportError:
|
||||
pydot = None
|
||||
# Fall back on pydot if necessary.
|
||||
try:
|
||||
import pydot
|
||||
except ImportError:
|
||||
pydot = None
|
||||
|
||||
|
||||
def _check_pydot():
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import copy
|
||||
import inspect
|
||||
import types
|
||||
|
||||
import numpy as np
|
||||
|
||||
from ..utils.np_utils import to_categorical
|
||||
from ..utils.generic_utils import has_arg
|
||||
from ..models import Sequential
|
||||
|
||||
|
||||
@@ -75,13 +75,11 @@ class BaseWrapper(object):
|
||||
else:
|
||||
legal_params_fns.append(self.build_fn)
|
||||
|
||||
legal_params = []
|
||||
for fn in legal_params_fns:
|
||||
legal_params += inspect.getargspec(fn)[0]
|
||||
legal_params = set(legal_params)
|
||||
|
||||
for params_name in params:
|
||||
if params_name not in legal_params:
|
||||
for fn in legal_params_fns:
|
||||
if has_arg(fn, params_name):
|
||||
break
|
||||
else:
|
||||
if params_name != 'nb_epoch':
|
||||
raise ValueError(
|
||||
'{} is not a legal parameter'.format(params_name))
|
||||
@@ -90,7 +88,7 @@ class BaseWrapper(object):
|
||||
"""Gets parameters for this estimator.
|
||||
|
||||
# Arguments
|
||||
**params: ignored (exists for API compatiblity).
|
||||
**params: ignored (exists for API compatibility).
|
||||
|
||||
# Returns
|
||||
Dictionary of parameter names mapped to their values.
|
||||
@@ -163,9 +161,8 @@ class BaseWrapper(object):
|
||||
"""
|
||||
override = override or {}
|
||||
res = {}
|
||||
fn_args = inspect.getargspec(fn)[0]
|
||||
for name, value in self.sk_params.items():
|
||||
if name in fn_args:
|
||||
if has_arg(fn, name):
|
||||
res.update({name: value})
|
||||
res.update(override)
|
||||
return res
|
||||
|
||||
+2
-2
@@ -3,12 +3,12 @@ from setuptools import find_packages
|
||||
|
||||
|
||||
setup(name='Keras',
|
||||
version='2.0.4',
|
||||
version='2.0.5',
|
||||
description='Deep Learning for Python',
|
||||
author='Francois Chollet',
|
||||
author_email='francois.chollet@gmail.com',
|
||||
url='https://github.com/fchollet/keras',
|
||||
download_url='https://github.com/fchollet/keras/tarball/2.0.4',
|
||||
download_url='https://github.com/fchollet/keras/tarball/2.0.5',
|
||||
license='MIT',
|
||||
install_requires=['theano', 'pyyaml', 'six'],
|
||||
extras_require={
|
||||
|
||||
@@ -6,7 +6,8 @@ import string
|
||||
from keras.utils.test_utils import get_test_data, keras_test
|
||||
from keras.utils.np_utils import to_categorical
|
||||
from keras.models import Sequential
|
||||
from keras import layers
|
||||
from keras import layers, optimizers
|
||||
import keras.backend as K
|
||||
import keras
|
||||
|
||||
|
||||
@@ -204,5 +205,14 @@ def test_masked_temporal():
|
||||
ground_truth = -np.log(0.5)
|
||||
assert(np.abs(history.history['loss'][-1] - ground_truth) < 0.06)
|
||||
|
||||
|
||||
@pytest.mark.skipif(K.backend() != 'tensorflow', reason='Requires TF backend')
|
||||
@keras_test
|
||||
def test_embedding_with_clipnorm():
|
||||
model = Sequential()
|
||||
model.add(layers.Embedding(input_dim=1, output_dim=1))
|
||||
model.compile(optimizer=optimizers.SGD(clipnorm=0.1), loss='mse')
|
||||
model.fit(np.array([[0]]), np.array([[[0.5]]]), epochs=1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__])
|
||||
|
||||
@@ -15,7 +15,7 @@ def get_standard_values():
|
||||
def test_serialization():
|
||||
all_activations = ['softmax', 'relu', 'elu', 'tanh',
|
||||
'sigmoid', 'hard_sigmoid', 'linear',
|
||||
'softplus', 'softsign']
|
||||
'softplus', 'softsign', 'selu']
|
||||
for name in all_activations:
|
||||
fn = activations.get(name)
|
||||
ref_fn = getattr(activations, name)
|
||||
@@ -136,12 +136,39 @@ def test_elu():
|
||||
assert_allclose(result, test_values, rtol=1e-05)
|
||||
|
||||
negative_values = np.array([[-1, -2]], dtype=K.floatx())
|
||||
# cntk can't rebind the input shape, so create the model again to test different batch size
|
||||
if (K.backend() == 'cntk'):
|
||||
x2 = K.placeholder(ndim=2)
|
||||
f = K.function([x2], [activations.elu(x2, 0.5)])
|
||||
result = f([negative_values])[0]
|
||||
true_result = (np.exp(negative_values) - 1) / 2
|
||||
|
||||
assert_allclose(result, true_result)
|
||||
|
||||
|
||||
def test_selu():
|
||||
x = K.placeholder(ndim=2)
|
||||
f = K.function([x], [activations.selu(x)])
|
||||
alpha = 1.6732632423543772848170429916717
|
||||
scale = 1.0507009873554804934193349852946
|
||||
|
||||
positive_values = get_standard_values()
|
||||
result = f([positive_values])[0]
|
||||
assert_allclose(result, positive_values * scale, rtol=1e-05)
|
||||
|
||||
negative_values = np.array([[-1, -2]], dtype=K.floatx())
|
||||
|
||||
# cntk can't rebind the input shape, so create the model again to test different batch size
|
||||
if (K.backend() == 'cntk'):
|
||||
x2 = K.placeholder(ndim=2)
|
||||
f = K.function([x2], [activations.selu(x2)])
|
||||
|
||||
result = f([negative_values])[0]
|
||||
true_result = (np.exp(negative_values) - 1) * scale * alpha
|
||||
|
||||
assert_allclose(result, true_result)
|
||||
|
||||
|
||||
def test_tanh():
|
||||
test_values = get_standard_values()
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import pytest
|
||||
from keras.utils.test_utils import keras_test
|
||||
from keras.utils.test_utils import layer_test
|
||||
from keras.utils.generic_utils import CustomObjectScope
|
||||
from keras.models import Sequential
|
||||
from keras import applications
|
||||
from keras import backend as K
|
||||
|
||||
@@ -11,12 +14,24 @@ def test_resnet50():
|
||||
|
||||
|
||||
@keras_test
|
||||
@pytest.mark.skipif((K.backend() == 'cntk'),
|
||||
reason="cntk does not support padding with non-concrete dimension")
|
||||
def test_resnet50_notop():
|
||||
model = applications.ResNet50(weights=None, include_top=False)
|
||||
assert model.output_shape == (None, None, None, 2048)
|
||||
|
||||
|
||||
@keras_test
|
||||
def test_resnet50_notop_specified_input_shape():
|
||||
input_shape = (3, 300, 300) if K.image_data_format() == 'channels_first' else (300, 300, 3)
|
||||
model = applications.ResNet50(weights=None, include_top=False, input_shape=input_shape)
|
||||
output_shape = (None, 2048, 1, 1) if K.image_data_format() == 'channels_first' else (None, 1, 1, 2048)
|
||||
assert model.output_shape == output_shape
|
||||
|
||||
|
||||
@keras_test
|
||||
@pytest.mark.skipif((K.backend() == 'cntk'),
|
||||
reason="cntk does not support padding with non-concrete dimension")
|
||||
def test_resnet50_pooling():
|
||||
model = applications.ResNet50(weights=None,
|
||||
include_top=False,
|
||||
@@ -24,6 +39,16 @@ def test_resnet50_pooling():
|
||||
assert model.output_shape == (None, 2048)
|
||||
|
||||
|
||||
@keras_test
|
||||
def test_resnet50_pooling_specified_input_shape():
|
||||
input_shape = (3, 300, 300) if K.image_data_format() == 'channels_first' else (300, 300, 3)
|
||||
model = applications.ResNet50(weights=None,
|
||||
include_top=False,
|
||||
pooling='avg',
|
||||
input_shape=input_shape)
|
||||
assert model.output_shape == (None, 2048)
|
||||
|
||||
|
||||
@keras_test
|
||||
def test_vgg16():
|
||||
model = applications.VGG16(weights=None)
|
||||
@@ -31,17 +56,36 @@ def test_vgg16():
|
||||
|
||||
|
||||
@keras_test
|
||||
@pytest.mark.skipif((K.backend() == 'cntk'),
|
||||
reason="cntk does not support padding with non-concrete dimension")
|
||||
def test_vgg16_notop():
|
||||
model = applications.VGG16(weights=None, include_top=False)
|
||||
assert model.output_shape == (None, None, None, 512)
|
||||
|
||||
|
||||
@keras_test
|
||||
def test_vgg16_notop_specified_input_shape():
|
||||
input_shape = (3, 300, 300) if K.image_data_format() == 'channels_first' else (300, 300, 3)
|
||||
model = applications.VGG16(weights=None, include_top=False, input_shape=input_shape)
|
||||
output_shape = (None, 512, 9, 9) if K.image_data_format() == 'channels_first' else (None, 9, 9, 512)
|
||||
assert model.output_shape == output_shape
|
||||
|
||||
|
||||
@keras_test
|
||||
@pytest.mark.skipif((K.backend() == 'cntk'),
|
||||
reason="cntk does not support padding with non-concrete dimension")
|
||||
def test_vgg16_pooling():
|
||||
model = applications.VGG16(weights=None, include_top=False, pooling='avg')
|
||||
assert model.output_shape == (None, 512)
|
||||
|
||||
|
||||
@keras_test
|
||||
def test_vgg16_pooling_specified_input_shape():
|
||||
input_shape = (3, 300, 300) if K.image_data_format() == 'channels_first' else (300, 300, 3)
|
||||
model = applications.VGG16(weights=None, include_top=False, pooling='avg', input_shape=input_shape)
|
||||
assert model.output_shape == (None, 512)
|
||||
|
||||
|
||||
@keras_test
|
||||
def test_vgg19():
|
||||
model = applications.VGG19(weights=None)
|
||||
@@ -49,17 +93,36 @@ def test_vgg19():
|
||||
|
||||
|
||||
@keras_test
|
||||
@pytest.mark.skipif((K.backend() == 'cntk'),
|
||||
reason="cntk does not support padding with non-concrete dimension")
|
||||
def test_vgg19_notop():
|
||||
model = applications.VGG16(weights=None, include_top=False)
|
||||
model = applications.VGG19(weights=None, include_top=False)
|
||||
assert model.output_shape == (None, None, None, 512)
|
||||
|
||||
|
||||
@keras_test
|
||||
def test_vgg19_notop_specified_input_shape():
|
||||
input_shape = (3, 300, 300) if K.image_data_format() == 'channels_first' else (300, 300, 3)
|
||||
model = applications.VGG19(weights=None, include_top=False, input_shape=input_shape)
|
||||
output_shape = (None, 512, 9, 9) if K.image_data_format() == 'channels_first' else (None, 9, 9, 512)
|
||||
assert model.output_shape == output_shape
|
||||
|
||||
|
||||
@keras_test
|
||||
@pytest.mark.skipif((K.backend() == 'cntk'),
|
||||
reason="cntk does not support padding with non-concrete dimension")
|
||||
def test_vgg19_pooling():
|
||||
model = applications.VGG16(weights=None, include_top=False, pooling='avg')
|
||||
assert model.output_shape == (None, 512)
|
||||
|
||||
|
||||
@keras_test
|
||||
def test_vgg19_pooling_specified_input_shape():
|
||||
input_shape = (3, 300, 300) if K.image_data_format() == 'channels_first' else (300, 300, 3)
|
||||
model = applications.VGG16(weights=None, include_top=False, pooling='avg', input_shape=input_shape)
|
||||
assert model.output_shape == (None, 512)
|
||||
|
||||
|
||||
@keras_test
|
||||
@pytest.mark.skipif((K.backend() != 'tensorflow'),
|
||||
reason='Requires tensorflow backend')
|
||||
@@ -91,16 +154,88 @@ def test_inceptionv3():
|
||||
|
||||
|
||||
@keras_test
|
||||
@pytest.mark.skipif((K.backend() == 'cntk'),
|
||||
reason="cntk does not support padding with non-concrete dimension")
|
||||
def test_inceptionv3_notop():
|
||||
model = applications.InceptionV3(weights=None, include_top=False)
|
||||
assert model.output_shape == (None, None, None, 2048)
|
||||
|
||||
|
||||
@keras_test
|
||||
@pytest.mark.skipif((K.backend() == 'cntk'),
|
||||
reason="cntk does not support padding with non-concrete dimension")
|
||||
def test_inceptionv3_pooling():
|
||||
model = applications.InceptionV3(weights=None, include_top=False, pooling='avg')
|
||||
assert model.output_shape == (None, 2048)
|
||||
|
||||
|
||||
@keras_test
|
||||
@pytest.mark.skipif((K.backend() != 'tensorflow'),
|
||||
reason="MobileNets are supported only on Tensorflow")
|
||||
def test_mobilenet():
|
||||
model = applications.MobileNet(weights=None)
|
||||
assert model.output_shape == (None, 1000)
|
||||
|
||||
|
||||
@keras_test
|
||||
@pytest.mark.skipif((K.backend() != 'tensorflow'),
|
||||
reason="MobileNets are supported only on Tensorflow")
|
||||
def test_mobilenet_no_top():
|
||||
model = applications.MobileNet(weights=None, include_top=False)
|
||||
assert model.output_shape == (None, None, None, 1024)
|
||||
|
||||
|
||||
@keras_test
|
||||
@pytest.mark.skipif((K.backend() != 'tensorflow'),
|
||||
reason="MobileNets are supported only on Tensorflow")
|
||||
def test_mobilenet_pooling():
|
||||
model = applications.MobileNet(weights=None, include_top=False, pooling='avg')
|
||||
assert model.output_shape == (None, 1024)
|
||||
|
||||
|
||||
@pytest.mark.skipif(K.backend() != 'tensorflow', reason='Requires TF backend')
|
||||
@keras_test
|
||||
def test_depthwise_conv_2d():
|
||||
_convolution_paddings = ['valid', 'same']
|
||||
num_samples = 2
|
||||
stack_size = 3
|
||||
num_row = 7
|
||||
num_col = 6
|
||||
|
||||
with CustomObjectScope({'relu6': applications.mobilenet.relu6,
|
||||
'DepthwiseConv2D': applications.mobilenet.DepthwiseConv2D}):
|
||||
for padding in _convolution_paddings:
|
||||
for strides in [(1, 1), (2, 2)]:
|
||||
for multiplier in [1, 2]:
|
||||
if padding == 'same' and strides != (1, 1):
|
||||
continue
|
||||
|
||||
layer_test(applications.mobilenet.DepthwiseConv2D,
|
||||
kwargs={'kernel_size': (3, 3),
|
||||
'padding': padding,
|
||||
'strides': strides,
|
||||
'depth_multiplier': multiplier},
|
||||
input_shape=(num_samples, num_row, num_col, stack_size))
|
||||
|
||||
layer_test(applications.mobilenet.DepthwiseConv2D,
|
||||
kwargs={'kernel_size': 3,
|
||||
'padding': padding,
|
||||
'data_format': 'channels_first',
|
||||
'activation': None,
|
||||
'depthwise_regularizer': 'l2',
|
||||
'bias_regularizer': 'l2',
|
||||
'activity_regularizer': 'l2',
|
||||
'depthwise_constraint': 'unit_norm',
|
||||
'strides': strides,
|
||||
'depth_multiplier': multiplier},
|
||||
input_shape=(num_samples, stack_size, num_row, num_col))
|
||||
|
||||
# Test invalid use case
|
||||
with pytest.raises(ValueError):
|
||||
model = Sequential([applications.mobilenet.DepthwiseConv2D(kernel_size=3,
|
||||
padding=padding,
|
||||
batch_input_shape=(None, None, 5, None))])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__])
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
import pytest
|
||||
import numpy as np
|
||||
from numpy.testing import assert_allclose
|
||||
|
||||
from keras.applications import imagenet_utils as utils
|
||||
|
||||
|
||||
def test_preprocess_input():
|
||||
x = np.random.uniform(0, 255, (2, 3, 2, 3))
|
||||
assert utils.preprocess_input(x).shape == x.shape
|
||||
|
||||
out1 = utils.preprocess_input(x, 'channels_last')
|
||||
out2 = utils.preprocess_input(np.transpose(x, (0, 3, 1, 2)), 'channels_first')
|
||||
assert_allclose(out1, out2.transpose(0, 2, 3, 1))
|
||||
|
||||
|
||||
def test_decode_predictions():
|
||||
x = np.zeros((2, 1000))
|
||||
x[0, 372] = 1.0
|
||||
x[1, 549] = 1.0
|
||||
outs = utils.decode_predictions(x, top=1)
|
||||
scores = [out[0][2] for out in outs]
|
||||
assert scores[0] == scores[1]
|
||||
|
||||
# the numbers of columns and ImageNet classes are not identical.
|
||||
with pytest.raises(ValueError):
|
||||
utils.decode_predictions(np.ones((2, 100)))
|
||||
|
||||
|
||||
def test_obtain_input_shape():
|
||||
# input_shape and default_size are not identical.
|
||||
with pytest.raises(ValueError):
|
||||
utils._obtain_input_shape(
|
||||
input_shape=(224, 224, 3),
|
||||
default_size=299,
|
||||
min_size=139,
|
||||
data_format='channels_last',
|
||||
include_top=True)
|
||||
|
||||
# Test invalid use cases
|
||||
for data_format in ['channels_last', 'channels_first']:
|
||||
# input_shape is smaller than min_size.
|
||||
shape = (100, 100)
|
||||
input_shape = shape + (3,) if data_format == 'channels_last' else (3,) + shape
|
||||
with pytest.raises(ValueError):
|
||||
utils._obtain_input_shape(
|
||||
input_shape=input_shape,
|
||||
default_size=None,
|
||||
min_size=139,
|
||||
data_format=data_format,
|
||||
include_top=False)
|
||||
|
||||
# shape is 1D.
|
||||
shape = (100,)
|
||||
input_shape = shape + (3,) if data_format == 'channels_last' else (3,) + shape
|
||||
with pytest.raises(ValueError):
|
||||
utils._obtain_input_shape(
|
||||
input_shape=input_shape,
|
||||
default_size=None,
|
||||
min_size=139,
|
||||
data_format=data_format,
|
||||
include_top=False)
|
||||
|
||||
# the number of channels is 5 not 3.
|
||||
shape = (100, 100)
|
||||
input_shape = shape + (5,) if data_format == 'channels_last' else (5,) + shape
|
||||
with pytest.raises(ValueError):
|
||||
utils._obtain_input_shape(
|
||||
input_shape=input_shape,
|
||||
default_size=None,
|
||||
min_size=139,
|
||||
data_format=data_format,
|
||||
include_top=False)
|
||||
|
||||
assert utils._obtain_input_shape(
|
||||
input_shape=None,
|
||||
default_size=None,
|
||||
min_size=139,
|
||||
data_format='channels_last',
|
||||
include_top=False) == (None, None, 3)
|
||||
|
||||
assert utils._obtain_input_shape(
|
||||
input_shape=None,
|
||||
default_size=None,
|
||||
min_size=139,
|
||||
data_format='channels_first',
|
||||
include_top=False) == (3, None, None)
|
||||
|
||||
assert utils._obtain_input_shape(
|
||||
input_shape=None,
|
||||
default_size=None,
|
||||
min_size=139,
|
||||
data_format='channels_last',
|
||||
include_top=False) == (None, None, 3)
|
||||
|
||||
assert utils._obtain_input_shape(
|
||||
input_shape=(150, 150, 3),
|
||||
default_size=None,
|
||||
min_size=139,
|
||||
data_format='channels_last',
|
||||
include_top=False) == (150, 150, 3)
|
||||
|
||||
assert utils._obtain_input_shape(
|
||||
input_shape=(3, None, None),
|
||||
default_size=None,
|
||||
min_size=139,
|
||||
data_format='channels_first',
|
||||
include_top=False) == (3, None, None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__])
|
||||
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -77,6 +77,8 @@ def test_trainable_weights():
|
||||
|
||||
|
||||
@keras_test
|
||||
@pytest.mark.skipif((K.backend() == 'cntk'),
|
||||
reason="cntk does not support add learning_phase() as input")
|
||||
def test_learning_phase():
|
||||
a = Input(shape=(32,), name='input_a')
|
||||
b = Input(shape=(32,), name='input_b')
|
||||
@@ -193,13 +195,13 @@ def test_node_construction():
|
||||
assert test_layer.input_shape == (None, 32)
|
||||
assert test_layer.output_shape == (None, 16)
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(AttributeError):
|
||||
dense.input
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(AttributeError):
|
||||
dense.output
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(AttributeError):
|
||||
dense.input_mask
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(AttributeError):
|
||||
dense.output_mask
|
||||
|
||||
assert dense.get_input_at(0) == a
|
||||
@@ -429,36 +431,35 @@ def test_recursion():
|
||||
k = Input(shape=(32,), name='input_k')
|
||||
m, n = model([j, k])
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(TypeError):
|
||||
Model([j, k], [m, n])
|
||||
|
||||
# disconnected graph
|
||||
j = Input(shape=(32,), name='input_j')
|
||||
k = Input(shape=(32,), name='input_k')
|
||||
m, n = model([j, k])
|
||||
with pytest.raises(Exception) as e:
|
||||
with pytest.raises(RuntimeError):
|
||||
Model([j], [m, n])
|
||||
|
||||
# redudant outputs
|
||||
# redundant outputs
|
||||
j = Input(shape=(32,), name='input_j')
|
||||
k = Input(shape=(32,), name='input_k')
|
||||
m, n = model([j, k])
|
||||
# this should work lol
|
||||
# TODO: raise a warning
|
||||
# this should work with a warning
|
||||
Model([j, k], [m, n, n])
|
||||
|
||||
# redundant inputs
|
||||
j = Input(shape=(32,), name='input_j')
|
||||
k = Input(shape=(32,), name='input_k')
|
||||
m, n = model([j, k])
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(ValueError):
|
||||
Model([j, k, j], [m, n])
|
||||
|
||||
# i have not idea what I'm doing: garbage as inputs/outputs
|
||||
j = Input(shape=(32,), name='input_j')
|
||||
k = Input(shape=(32,), name='input_k')
|
||||
m, n = model([j, k])
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(TypeError):
|
||||
Model([j, k], [m, n, 0])
|
||||
|
||||
####################################################
|
||||
@@ -495,7 +496,7 @@ def test_load_layers():
|
||||
from keras.models import Model
|
||||
from keras.engine.topology import preprocess_weights_for_loading
|
||||
|
||||
if K.backend() == 'tensorflow':
|
||||
if K.backend() == 'tensorflow' or K.backend() == 'cntk':
|
||||
inputs = Input(shape=(10, 20, 20, 1))
|
||||
else:
|
||||
inputs = Input(shape=(10, 1, 20, 20))
|
||||
@@ -551,6 +552,7 @@ def test_load_layers():
|
||||
assert np.all(K.eval(model.layers[2].weights[5]) == weight_tensor_bi_convlstm_new[5])
|
||||
|
||||
|
||||
@keras_test
|
||||
def test_recursion_with_bn_and_loss():
|
||||
model1 = Sequential([
|
||||
layers.Dense(5, input_dim=5, activity_regularizer='l1'),
|
||||
|
||||
@@ -1,16 +1,91 @@
|
||||
import pytest
|
||||
import numpy as np
|
||||
from numpy.testing import assert_allclose
|
||||
import sys
|
||||
import scipy.sparse as sparse
|
||||
|
||||
from keras.layers import Dense, Dropout
|
||||
from keras.engine.topology import Input
|
||||
from keras.engine.training import Model, _check_loss_and_target_compatibility
|
||||
from keras.engine.training import Model
|
||||
from keras.engine.training import Model
|
||||
from keras.engine.training import _check_loss_and_target_compatibility
|
||||
from keras.engine.training import _weighted_masked_objective
|
||||
from keras.engine.training import _check_array_lengths
|
||||
from keras.engine.training import _slice_arrays
|
||||
from keras.models import Sequential
|
||||
from keras import backend as K
|
||||
from keras.utils import Sequence
|
||||
from keras.utils.test_utils import keras_test
|
||||
from keras.callbacks import LambdaCallback
|
||||
|
||||
|
||||
class RandomSequence(Sequence):
|
||||
def __init__(self, batch_size):
|
||||
self.batch_size = batch_size
|
||||
|
||||
def __len__(self):
|
||||
return 12
|
||||
|
||||
def __getitem__(self, idx):
|
||||
return [np.random.random((self.batch_size, 3)), np.random.random((self.batch_size, 3))], [
|
||||
np.random.random((self.batch_size, 4)),
|
||||
np.random.random((self.batch_size, 3))]
|
||||
|
||||
|
||||
@keras_test
|
||||
def test_check_array_lengths():
|
||||
_check_array_lengths(None, None, None)
|
||||
a_np = np.random.random((4, 3, 3))
|
||||
_check_array_lengths(a_np, a_np, a_np)
|
||||
_check_array_lengths([a_np, a_np], [a_np, a_np], [a_np, a_np])
|
||||
_check_array_lengths([None], [None], [None])
|
||||
|
||||
b_np = np.random.random((3, 4))
|
||||
with pytest.raises(ValueError):
|
||||
_check_array_lengths(a_np, None, None)
|
||||
with pytest.raises(ValueError):
|
||||
_check_array_lengths(a_np, a_np, None)
|
||||
with pytest.raises(ValueError):
|
||||
_check_array_lengths([a_np], [None], None)
|
||||
with pytest.raises(ValueError):
|
||||
_check_array_lengths([a_np], [b_np], None)
|
||||
with pytest.raises(ValueError):
|
||||
_check_array_lengths([a_np], None, [b_np])
|
||||
|
||||
|
||||
@keras_test
|
||||
def test_slice_arrays():
|
||||
input_a = np.random.random((10, 3))
|
||||
_slice_arrays(None)
|
||||
_slice_arrays(input_a, 0)
|
||||
_slice_arrays(input_a, 0, 1)
|
||||
_slice_arrays(input_a, stop=2)
|
||||
input_a = [None, [1, 1], None, [1, 1]]
|
||||
_slice_arrays(input_a, 0)
|
||||
_slice_arrays(input_a, 0, 1)
|
||||
_slice_arrays(input_a, stop=2)
|
||||
input_a = [None]
|
||||
_slice_arrays(input_a, 0)
|
||||
_slice_arrays(input_a, 0, 1)
|
||||
_slice_arrays(input_a, stop=2)
|
||||
input_a = None
|
||||
_slice_arrays(input_a, 0)
|
||||
_slice_arrays(input_a, 0, 1)
|
||||
_slice_arrays(input_a, stop=2)
|
||||
|
||||
|
||||
@keras_test
|
||||
def test_weighted_masked_objective():
|
||||
a = Input(shape=(3,), name='input_a')
|
||||
|
||||
# weighted_masked_objective
|
||||
def mask_dummy(y_true=None, y_pred=None, weight=None):
|
||||
return K.placeholder(y_true.shape)
|
||||
|
||||
weighted_function = _weighted_masked_objective(K.categorical_crossentropy)
|
||||
weighted_function(a, a, None)
|
||||
|
||||
|
||||
@keras_test
|
||||
def test_model_methods():
|
||||
a = Input(shape=(3,), name='input_a')
|
||||
@@ -25,8 +100,6 @@ def test_model_methods():
|
||||
optimizer = 'rmsprop'
|
||||
loss = 'mse'
|
||||
loss_weights = [1., 0.5]
|
||||
model.compile(optimizer, loss, metrics=[], loss_weights=loss_weights,
|
||||
sample_weight_mode=None)
|
||||
|
||||
input_a_np = np.random.random((10, 3))
|
||||
input_b_np = np.random.random((10, 3))
|
||||
@@ -34,6 +107,13 @@ def test_model_methods():
|
||||
output_a_np = np.random.random((10, 4))
|
||||
output_b_np = np.random.random((10, 3))
|
||||
|
||||
# training/testing doesn't work before compiling.
|
||||
with pytest.raises(RuntimeError):
|
||||
model.train_on_batch([input_a_np, input_b_np], [output_a_np, output_b_np])
|
||||
|
||||
model.compile(optimizer, loss, metrics=[], loss_weights=loss_weights,
|
||||
sample_weight_mode=None)
|
||||
|
||||
# test train_on_batch
|
||||
out = model.train_on_batch([input_a_np, input_b_np],
|
||||
[output_a_np, output_b_np])
|
||||
@@ -74,7 +154,9 @@ def test_model_methods():
|
||||
out = model.fit({'input_a': input_a_np, 'input_b': input_b_np},
|
||||
{'dense_1': output_a_np, 'dropout': output_b_np},
|
||||
epochs=1, batch_size=4, validation_split=0.5,
|
||||
validation_data=({'input_a': input_a_np, 'input_b': input_b_np}, {'dense_1': output_a_np, 'dropout': output_b_np}))
|
||||
validation_data=(
|
||||
{'input_a': input_a_np, 'input_b': input_b_np},
|
||||
{'dense_1': output_a_np, 'dropout': output_b_np}))
|
||||
|
||||
# test_on_batch
|
||||
out = model.test_on_batch([input_a_np, input_b_np],
|
||||
@@ -168,6 +250,7 @@ def test_model_methods():
|
||||
while True:
|
||||
yield ([np.random.random((batch_sz, 3)), np.random.random((batch_sz, 3))],
|
||||
[np.random.random((batch_sz, 4)), np.random.random((batch_sz, 3))])
|
||||
|
||||
out = model.fit_generator(gen_data(4), steps_per_epoch=3, epochs=5,
|
||||
initial_epoch=2, callbacks=[tracker_cb])
|
||||
assert trained_epochs == [2, 3, 4]
|
||||
@@ -197,6 +280,154 @@ def test_model_methods():
|
||||
out = model.evaluate([input_a_np, input_b_np], [output_a_np, output_b_np], batch_size=4)
|
||||
out = model.predict([input_a_np, input_b_np], batch_size=4)
|
||||
|
||||
# empty batch
|
||||
with pytest.raises(ValueError):
|
||||
def gen_data():
|
||||
while True:
|
||||
yield (np.asarray([]), np.asarray([]))
|
||||
out = model.evaluate_generator(gen_data(), steps=1)
|
||||
|
||||
# x is not a list of numpy arrays.
|
||||
with pytest.raises(ValueError):
|
||||
out = model.predict([None])
|
||||
|
||||
# x does not match _feed_input_names.
|
||||
with pytest.raises(ValueError):
|
||||
out = model.predict([input_a_np, None, input_b_np])
|
||||
with pytest.raises(ValueError):
|
||||
out = model.predict([None, input_a_np, input_b_np])
|
||||
|
||||
# all input/output/weight arrays should have the same number of samples.
|
||||
with pytest.raises(ValueError):
|
||||
out = model.train_on_batch([input_a_np, input_b_np[:2]],
|
||||
[output_a_np, output_b_np],
|
||||
sample_weight=sample_weight)
|
||||
with pytest.raises(ValueError):
|
||||
out = model.train_on_batch([input_a_np, input_b_np],
|
||||
[output_a_np, output_b_np[:2]],
|
||||
sample_weight=sample_weight)
|
||||
with pytest.raises(ValueError):
|
||||
out = model.train_on_batch([input_a_np, input_b_np],
|
||||
[output_a_np, output_b_np],
|
||||
sample_weight=[sample_weight[1], sample_weight[1][:2]])
|
||||
|
||||
# `sample_weight` is neither a dict nor a list.
|
||||
with pytest.raises(TypeError):
|
||||
out = model.train_on_batch([input_a_np, input_b_np],
|
||||
[output_a_np, output_b_np],
|
||||
sample_weight=tuple(sample_weight))
|
||||
|
||||
# `validation_data` is neither a tuple nor a triple.
|
||||
with pytest.raises(ValueError):
|
||||
out = model.fit([input_a_np, input_b_np],
|
||||
[output_a_np, output_b_np],
|
||||
epochs=1, batch_size=4,
|
||||
validation_data=([input_a_np, input_b_np],))
|
||||
|
||||
# `loss` does not match outputs.
|
||||
with pytest.raises(ValueError):
|
||||
model.compile(optimizer, loss=['mse', 'mae', 'mape'])
|
||||
|
||||
# `loss_weights` does not match output_names.
|
||||
with pytest.raises(ValueError):
|
||||
model.compile(optimizer, loss='mse', loss_weights={'lstm': 0.5})
|
||||
|
||||
# `loss_weights` does not match outputs.
|
||||
with pytest.raises(ValueError):
|
||||
model.compile(optimizer, loss='mse', loss_weights=[0.5])
|
||||
|
||||
# `loss_weights` is invalid type.
|
||||
with pytest.raises(TypeError):
|
||||
model.compile(optimizer, loss='mse', loss_weights=(0.5, 0.5))
|
||||
|
||||
# `sample_weight_mode` does not match output_names.
|
||||
with pytest.raises(ValueError):
|
||||
model.compile(optimizer, loss='mse', sample_weight_mode={'lstm': 'temporal'})
|
||||
|
||||
# `sample_weight_mode` does not match output_names.
|
||||
with pytest.raises(ValueError):
|
||||
model.compile(optimizer, loss='mse', sample_weight_mode=['temporal'])
|
||||
|
||||
# `sample_weight_mode` matches output_names partially.
|
||||
with pytest.raises(ValueError):
|
||||
model.compile(optimizer, loss='mse', sample_weight_mode={'dense_1': 'temporal'})
|
||||
|
||||
# `loss` does not exist.
|
||||
with pytest.raises(RuntimeError):
|
||||
model.compile(optimizer, loss=[])
|
||||
|
||||
model.compile(optimizer, loss=['mse', 'mae'])
|
||||
model.compile(optimizer, loss='mse', loss_weights={'dense_1': 0.2, 'dropout': 0.8})
|
||||
model.compile(optimizer, loss='mse', loss_weights=[0.2, 0.8])
|
||||
|
||||
# the rank of weight arrays should be 1.
|
||||
with pytest.raises(ValueError):
|
||||
out = model.train_on_batch([input_a_np, input_b_np],
|
||||
[output_a_np, output_b_np],
|
||||
sample_weight=[None, np.random.random((10, 20, 30))])
|
||||
|
||||
model.compile(optimizer, loss='mse', sample_weight_mode={'dense_1': None, 'dropout': 'temporal'})
|
||||
model.compile(optimizer, loss='mse', sample_weight_mode=[None, 'temporal'])
|
||||
|
||||
# the rank of output arrays should be at least 3D.
|
||||
with pytest.raises(ValueError):
|
||||
out = model.train_on_batch([input_a_np, input_b_np],
|
||||
[output_a_np, output_b_np],
|
||||
sample_weight=sample_weight)
|
||||
|
||||
model.compile(optimizer, loss, metrics=[], loss_weights=loss_weights,
|
||||
sample_weight_mode=None)
|
||||
trained_epochs = []
|
||||
out = model.fit_generator(generator=RandomSequence(3), steps_per_epoch=4, epochs=5,
|
||||
initial_epoch=0, validation_data=RandomSequence(4),
|
||||
validation_steps=3, callbacks=[tracker_cb])
|
||||
assert trained_epochs == [0, 1, 2, 3, 4]
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3,), reason='Cannot catch warnings in python 2')
|
||||
@keras_test
|
||||
def test_warnings():
|
||||
a = Input(shape=(3,), name='input_a')
|
||||
b = Input(shape=(3,), name='input_b')
|
||||
|
||||
a_2 = Dense(4, name='dense_1')(a)
|
||||
dp = Dropout(0.5, name='dropout')
|
||||
b_2 = dp(b)
|
||||
|
||||
model = Model([a, b], [a_2, b_2])
|
||||
|
||||
optimizer = 'rmsprop'
|
||||
loss = 'mse'
|
||||
loss_weights = [1., 0.5]
|
||||
model.compile(optimizer, loss, metrics=[], loss_weights=loss_weights,
|
||||
sample_weight_mode=None)
|
||||
|
||||
def gen_data(batch_sz):
|
||||
while True:
|
||||
yield ([np.random.random((batch_sz, 3)), np.random.random((batch_sz, 3))],
|
||||
[np.random.random((batch_sz, 4)), np.random.random((batch_sz, 3))])
|
||||
|
||||
with pytest.warns(Warning) as w:
|
||||
out = model.fit_generator(gen_data(4), steps_per_epoch=10, use_multiprocessing=True, workers=2)
|
||||
warning_raised = any(['Sequence' in str(w_.message) for w_ in w])
|
||||
assert warning_raised, 'No warning raised when using generator with processes.'
|
||||
|
||||
with pytest.warns(None) as w:
|
||||
out = model.fit_generator(RandomSequence(3), steps_per_epoch=4, use_multiprocessing=True, workers=2)
|
||||
assert all(['Sequence' not in str(w_.message) for w_ in w]), 'A warning was raised for Sequence.'
|
||||
|
||||
|
||||
@pytest.mark.skipif(K.backend() != 'tensorflow', reason='sparse operations supported only by TF')
|
||||
@keras_test
|
||||
def test_sparse_input_validation_split():
|
||||
test_input = sparse.random(6, 3, density=0.25).tocsr()
|
||||
in1 = Input(shape=(3,), sparse=True)
|
||||
out1 = Dense(4)(in1)
|
||||
test_output = np.random.random((6, 4))
|
||||
model = Model(in1, out1)
|
||||
model.compile('rmsprop', 'mse')
|
||||
model.fit(test_input, test_output, epochs=1, batch_size=2, validation_split=0.2)
|
||||
|
||||
|
||||
@keras_test
|
||||
def test_trainable_argument():
|
||||
@@ -212,9 +443,9 @@ def test_trainable_argument():
|
||||
assert_allclose(out, out_2)
|
||||
|
||||
# test with nesting
|
||||
input = Input(shape=(3,))
|
||||
output = model(input)
|
||||
model = Model(input, output)
|
||||
inputs = Input(shape=(3,))
|
||||
outputs = model(inputs)
|
||||
model = Model(inputs, outputs)
|
||||
model.compile('rmsprop', 'mse')
|
||||
out = model.predict(x)
|
||||
model.train_on_batch(x, y)
|
||||
@@ -232,7 +463,7 @@ def test_check_not_failing():
|
||||
@keras_test
|
||||
def test_check_last_is_one():
|
||||
a = np.random.random((2, 3, 1))
|
||||
with pytest.raises(Exception) as exc:
|
||||
with pytest.raises(ValueError) as exc:
|
||||
_check_loss_and_target_compatibility([a], [K.categorical_crossentropy], [a.shape])
|
||||
|
||||
assert 'You are passing a target array' in str(exc)
|
||||
@@ -241,7 +472,7 @@ def test_check_last_is_one():
|
||||
@keras_test
|
||||
def test_check_bad_shape():
|
||||
a = np.random.random((2, 3, 5))
|
||||
with pytest.raises(Exception) as exc:
|
||||
with pytest.raises(ValueError) as exc:
|
||||
_check_loss_and_target_compatibility([a], [K.categorical_crossentropy], [(2, 3, 6)])
|
||||
|
||||
assert 'targets to have the same shape' in str(exc)
|
||||
@@ -433,6 +664,8 @@ def test_model_with_partial_loss():
|
||||
|
||||
|
||||
@keras_test
|
||||
@pytest.mark.skipif((K.backend() == 'cntk'),
|
||||
reason="cntk does not support external loss yet")
|
||||
def test_model_with_external_loss():
|
||||
# None loss, only regularization loss.
|
||||
a = Input(shape=(3,), name='input_a')
|
||||
|
||||
@@ -90,6 +90,14 @@ def test_he_normal(tensor_shape):
|
||||
target_mean=0., target_std=None, target_max=2 * scale)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('tensor_shape', [FC_SHAPE, CONV_SHAPE], ids=['FC', 'CONV'])
|
||||
def test_lecun_normal(tensor_shape):
|
||||
fan_in, _ = initializers._compute_fans(tensor_shape)
|
||||
scale = np.sqrt(1. / fan_in)
|
||||
_runner(initializers.lecun_normal(), tensor_shape,
|
||||
target_mean=0., target_std=scale)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('tensor_shape', [FC_SHAPE, CONV_SHAPE], ids=['FC', 'CONV'])
|
||||
def test_orthogonal(tensor_shape):
|
||||
_runner(initializers.orthogonal(), tensor_shape,
|
||||
@@ -99,7 +107,7 @@ def test_orthogonal(tensor_shape):
|
||||
@pytest.mark.parametrize('tensor_shape', [(100, 100), (1, 2, 3, 4)], ids=['FC', 'CONV'])
|
||||
def test_identity(tensor_shape):
|
||||
if len(tensor_shape) > 2:
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(ValueError):
|
||||
_runner(initializers.identity(), tensor_shape,
|
||||
target_mean=1. / tensor_shape[0], target_max=1.)
|
||||
else:
|
||||
|
||||
@@ -43,67 +43,69 @@ def test_convolutional_recurrent():
|
||||
if data_format == 'channels_first' or return_sequences:
|
||||
continue
|
||||
|
||||
# Tests for statefulness
|
||||
model = Sequential()
|
||||
kwargs = {'data_format': data_format,
|
||||
'return_sequences': return_sequences,
|
||||
'filters': filters,
|
||||
'kernel_size': (num_row, num_col),
|
||||
'stateful': True,
|
||||
'batch_input_shape': inputs.shape,
|
||||
'padding': 'same'}
|
||||
layer = convolutional_recurrent.ConvLSTM2D(**kwargs)
|
||||
# cntk doesn't support statefulness on LSTM yet, will enable it on cntk later
|
||||
if K.backend() != 'cntk':
|
||||
# Tests for statefulness
|
||||
model = Sequential()
|
||||
kwargs = {'data_format': data_format,
|
||||
'return_sequences': return_sequences,
|
||||
'filters': filters,
|
||||
'kernel_size': (num_row, num_col),
|
||||
'stateful': True,
|
||||
'batch_input_shape': inputs.shape,
|
||||
'padding': 'same'}
|
||||
layer = convolutional_recurrent.ConvLSTM2D(**kwargs)
|
||||
|
||||
model.add(layer)
|
||||
model.compile(optimizer='sgd', loss='mse')
|
||||
out1 = model.predict(np.ones_like(inputs))
|
||||
model.add(layer)
|
||||
model.compile(optimizer='sgd', loss='mse')
|
||||
out1 = model.predict(np.ones_like(inputs))
|
||||
|
||||
# train once so that the states change
|
||||
model.train_on_batch(np.ones_like(inputs),
|
||||
np.random.random(out1.shape))
|
||||
out2 = model.predict(np.ones_like(inputs))
|
||||
# train once so that the states change
|
||||
model.train_on_batch(np.ones_like(inputs),
|
||||
np.random.random(out1.shape))
|
||||
out2 = model.predict(np.ones_like(inputs))
|
||||
|
||||
# if the state is not reset, output should be different
|
||||
assert(out1.max() != out2.max())
|
||||
# if the state is not reset, output should be different
|
||||
assert(out1.max() != out2.max())
|
||||
|
||||
# check that output changes after states are reset
|
||||
# (even though the model itself didn't change)
|
||||
layer.reset_states()
|
||||
out3 = model.predict(np.ones_like(inputs))
|
||||
assert(out2.max() != out3.max())
|
||||
# check that output changes after states are reset
|
||||
# (even though the model itself didn't change)
|
||||
layer.reset_states()
|
||||
out3 = model.predict(np.ones_like(inputs))
|
||||
assert(out2.max() != out3.max())
|
||||
|
||||
# check that container-level reset_states() works
|
||||
model.reset_states()
|
||||
out4 = model.predict(np.ones_like(inputs))
|
||||
assert_allclose(out3, out4, atol=1e-5)
|
||||
# check that container-level reset_states() works
|
||||
model.reset_states()
|
||||
out4 = model.predict(np.ones_like(inputs))
|
||||
assert_allclose(out3, out4, atol=1e-5)
|
||||
|
||||
# check that the call to `predict` updated the states
|
||||
out5 = model.predict(np.ones_like(inputs))
|
||||
assert(out4.max() != out5.max())
|
||||
# check that the call to `predict` updated the states
|
||||
out5 = model.predict(np.ones_like(inputs))
|
||||
assert(out4.max() != out5.max())
|
||||
|
||||
# check regularizers
|
||||
kwargs = {'data_format': data_format,
|
||||
'return_sequences': return_sequences,
|
||||
'kernel_size': (num_row, num_col),
|
||||
'stateful': True,
|
||||
'filters': filters,
|
||||
'batch_input_shape': inputs.shape,
|
||||
'kernel_regularizer': regularizers.L1L2(l1=0.01),
|
||||
'recurrent_regularizer': regularizers.L1L2(l1=0.01),
|
||||
'bias_regularizer': 'l2',
|
||||
'activity_regularizer': 'l2',
|
||||
'kernel_constraint': 'max_norm',
|
||||
'recurrent_constraint': 'max_norm',
|
||||
'bias_constraint': 'max_norm',
|
||||
'padding': 'same'}
|
||||
# check regularizers
|
||||
kwargs = {'data_format': data_format,
|
||||
'return_sequences': return_sequences,
|
||||
'kernel_size': (num_row, num_col),
|
||||
'stateful': True,
|
||||
'filters': filters,
|
||||
'batch_input_shape': inputs.shape,
|
||||
'kernel_regularizer': regularizers.L1L2(l1=0.01),
|
||||
'recurrent_regularizer': regularizers.L1L2(l1=0.01),
|
||||
'bias_regularizer': 'l2',
|
||||
'activity_regularizer': 'l2',
|
||||
'kernel_constraint': 'max_norm',
|
||||
'recurrent_constraint': 'max_norm',
|
||||
'bias_constraint': 'max_norm',
|
||||
'padding': 'same'}
|
||||
|
||||
layer = convolutional_recurrent.ConvLSTM2D(**kwargs)
|
||||
layer.build(inputs.shape)
|
||||
assert len(layer.losses) == 3
|
||||
assert layer.activity_regularizer
|
||||
output = layer(K.variable(np.ones(inputs.shape)))
|
||||
assert len(layer.losses) == 4
|
||||
K.eval(output)
|
||||
layer = convolutional_recurrent.ConvLSTM2D(**kwargs)
|
||||
layer.build(inputs.shape)
|
||||
assert len(layer.losses) == 3
|
||||
assert layer.activity_regularizer
|
||||
output = layer(K.variable(np.ones(inputs.shape)))
|
||||
assert len(layer.losses) == 4
|
||||
K.eval(output)
|
||||
|
||||
# check dropout
|
||||
layer_test(convolutional_recurrent.ConvLSTM2D,
|
||||
|
||||
@@ -5,8 +5,10 @@ from numpy.testing import assert_allclose
|
||||
from keras.utils.test_utils import layer_test
|
||||
from keras.utils.test_utils import keras_test
|
||||
from keras import backend as K
|
||||
from keras.engine.topology import InputLayer
|
||||
from keras.layers import convolutional
|
||||
from keras.layers import pooling
|
||||
from keras.models import Sequential
|
||||
|
||||
|
||||
# TensorFlow does not support full convolution.
|
||||
@@ -17,6 +19,8 @@ else:
|
||||
|
||||
|
||||
@keras_test
|
||||
@pytest.mark.skipif((K.backend() == 'cntk'),
|
||||
reason="cntk does not support dilated conv")
|
||||
def test_causal_dilated_conv():
|
||||
# Causal:
|
||||
layer_test(convolutional.Conv1D,
|
||||
@@ -97,9 +101,15 @@ def test_conv_1d():
|
||||
kwargs={'filters': filters,
|
||||
'kernel_size': kernel_size,
|
||||
'padding': padding,
|
||||
'dilation_rate': 2},
|
||||
'dilation_rate': 2,
|
||||
'activation': None},
|
||||
input_shape=(batch_size, steps, input_dim))
|
||||
|
||||
convolutional.Conv1D(filters=filters,
|
||||
kernel_size=kernel_size,
|
||||
padding=padding,
|
||||
input_shape=(input_dim,))
|
||||
|
||||
|
||||
@keras_test
|
||||
def test_maxpooling_1d():
|
||||
@@ -122,6 +132,8 @@ def test_averagepooling_1d():
|
||||
|
||||
|
||||
@keras_test
|
||||
@pytest.mark.skipif((K.backend() == 'cntk'),
|
||||
reason="cntk does not support dilated conv")
|
||||
def test_convolution_2d():
|
||||
num_samples = 2
|
||||
filters = 2
|
||||
@@ -147,6 +159,8 @@ def test_convolution_2d():
|
||||
kwargs={'filters': filters,
|
||||
'kernel_size': 3,
|
||||
'padding': padding,
|
||||
'data_format': 'channels_last',
|
||||
'activation': None,
|
||||
'kernel_regularizer': 'l2',
|
||||
'bias_regularizer': 'l2',
|
||||
'activity_regularizer': 'l2',
|
||||
@@ -163,6 +177,13 @@ def test_convolution_2d():
|
||||
'dilation_rate': (2, 2)},
|
||||
input_shape=(num_samples, num_row, num_col, stack_size))
|
||||
|
||||
# Test invalid use case
|
||||
with pytest.raises(ValueError):
|
||||
model = Sequential([convolutional.Conv2D(filters=filters,
|
||||
kernel_size=kernel_size,
|
||||
padding=padding,
|
||||
batch_input_shape=(None, None, 5, None))])
|
||||
|
||||
|
||||
@keras_test
|
||||
def test_conv2d_transpose():
|
||||
@@ -190,6 +211,7 @@ def test_conv2d_transpose():
|
||||
'kernel_size': 3,
|
||||
'padding': padding,
|
||||
'data_format': 'channels_first',
|
||||
'activation': None,
|
||||
'kernel_regularizer': 'l2',
|
||||
'bias_regularizer': 'l2',
|
||||
'activity_regularizer': 'l2',
|
||||
@@ -199,6 +221,13 @@ def test_conv2d_transpose():
|
||||
input_shape=(num_samples, stack_size, num_row, num_col),
|
||||
fixed_batch_size=True)
|
||||
|
||||
# Test invalid use case
|
||||
with pytest.raises(ValueError):
|
||||
model = Sequential([convolutional.Conv2DTranspose(filters=filters,
|
||||
kernel_size=3,
|
||||
padding=padding,
|
||||
batch_input_shape=(None, None, 5, None))])
|
||||
|
||||
|
||||
@pytest.mark.skipif(K.backend() != 'tensorflow', reason='Requires TF backend')
|
||||
@keras_test
|
||||
@@ -227,6 +256,8 @@ def test_separable_conv_2d():
|
||||
kwargs={'filters': filters,
|
||||
'kernel_size': 3,
|
||||
'padding': padding,
|
||||
'data_format': 'channels_first',
|
||||
'activation': None,
|
||||
'depthwise_regularizer': 'l2',
|
||||
'pointwise_regularizer': 'l2',
|
||||
'bias_regularizer': 'l2',
|
||||
@@ -235,7 +266,14 @@ def test_separable_conv_2d():
|
||||
'depthwise_constraint': 'unit_norm',
|
||||
'strides': strides,
|
||||
'depth_multiplier': multiplier},
|
||||
input_shape=(num_samples, num_row, num_col, stack_size))
|
||||
input_shape=(num_samples, stack_size, num_row, num_col))
|
||||
|
||||
# Test invalid use case
|
||||
with pytest.raises(ValueError):
|
||||
model = Sequential([convolutional.SeparableConv2D(filters=filters,
|
||||
kernel_size=3,
|
||||
padding=padding,
|
||||
batch_input_shape=(None, None, 5, None))])
|
||||
|
||||
|
||||
@keras_test
|
||||
@@ -338,6 +376,7 @@ def test_convolution_3d():
|
||||
kwargs={'filters': filters,
|
||||
'kernel_size': (1, 2, 3),
|
||||
'padding': padding,
|
||||
'activation': None,
|
||||
'kernel_regularizer': 'l2',
|
||||
'bias_regularizer': 'l2',
|
||||
'activity_regularizer': 'l2',
|
||||
@@ -349,6 +388,51 @@ def test_convolution_3d():
|
||||
stack_size))
|
||||
|
||||
|
||||
@keras_test
|
||||
def test_conv3d_transpose():
|
||||
filters = 2
|
||||
stack_size = 3
|
||||
num_depth = 7
|
||||
num_row = 5
|
||||
num_col = 6
|
||||
|
||||
for padding in _convolution_paddings:
|
||||
for strides in [(1, 1, 1), (2, 2, 2)]:
|
||||
for data_format in ['channels_first', 'channels_last']:
|
||||
if padding == 'same' and strides != (1, 1, 1):
|
||||
continue
|
||||
layer_test(convolutional.Conv3DTranspose,
|
||||
kwargs={'filters': filters,
|
||||
'kernel_size': 3,
|
||||
'padding': padding,
|
||||
'strides': strides,
|
||||
'data_format': data_format},
|
||||
input_shape=(None, num_depth, num_row, num_col, stack_size),
|
||||
fixed_batch_size=True)
|
||||
|
||||
layer_test(convolutional.Conv3DTranspose,
|
||||
kwargs={'filters': filters,
|
||||
'kernel_size': 3,
|
||||
'padding': padding,
|
||||
'data_format': 'channels_first',
|
||||
'activation': None,
|
||||
'kernel_regularizer': 'l2',
|
||||
'bias_regularizer': 'l2',
|
||||
'activity_regularizer': 'l2',
|
||||
'kernel_constraint': 'max_norm',
|
||||
'bias_constraint': 'max_norm',
|
||||
'strides': strides},
|
||||
input_shape=(None, stack_size, num_depth, num_row, num_col),
|
||||
fixed_batch_size=True)
|
||||
|
||||
# Test invalid use case
|
||||
with pytest.raises(ValueError):
|
||||
model = Sequential([convolutional.Conv3DTranspose(filters=filters,
|
||||
kernel_size=3,
|
||||
padding=padding,
|
||||
batch_input_shape=(None, None, 5, None, None))])
|
||||
|
||||
|
||||
@keras_test
|
||||
def test_maxpooling_3d():
|
||||
pool_size = (3, 3, 3)
|
||||
@@ -389,29 +473,29 @@ def test_zero_padding_1d():
|
||||
input_dim = 2
|
||||
num_steps = 5
|
||||
shape = (num_samples, num_steps, input_dim)
|
||||
input = np.ones(shape)
|
||||
inputs = np.ones(shape)
|
||||
|
||||
# basic test
|
||||
layer_test(convolutional.ZeroPadding1D,
|
||||
kwargs={'padding': 2},
|
||||
input_shape=input.shape)
|
||||
input_shape=inputs.shape)
|
||||
layer_test(convolutional.ZeroPadding1D,
|
||||
kwargs={'padding': (1, 2)},
|
||||
input_shape=input.shape)
|
||||
input_shape=inputs.shape)
|
||||
|
||||
# correctness test
|
||||
layer = convolutional.ZeroPadding1D(padding=2)
|
||||
layer.build(shape)
|
||||
output = layer(K.variable(input))
|
||||
np_output = K.eval(output)
|
||||
outputs = layer(K.variable(inputs))
|
||||
np_output = K.eval(outputs)
|
||||
for offset in [0, 1, -1, -2]:
|
||||
assert_allclose(np_output[:, offset, :], 0.)
|
||||
assert_allclose(np_output[:, 2:-2, :], 1.)
|
||||
|
||||
layer = convolutional.ZeroPadding1D(padding=(1, 2))
|
||||
layer.build(shape)
|
||||
output = layer(K.variable(input))
|
||||
np_output = K.eval(output)
|
||||
outputs = layer(K.variable(inputs))
|
||||
np_output = K.eval(outputs)
|
||||
for left_offset in [0]:
|
||||
assert_allclose(np_output[:, left_offset, :], 0.)
|
||||
for right_offset in [-1, -2]:
|
||||
@@ -442,8 +526,8 @@ def test_zero_padding_2d():
|
||||
layer = convolutional.ZeroPadding2D(padding=(2, 2),
|
||||
data_format=data_format)
|
||||
layer.build(inputs.shape)
|
||||
output = layer(K.variable(inputs))
|
||||
np_output = K.eval(output)
|
||||
outputs = layer(K.variable(inputs))
|
||||
np_output = K.eval(outputs)
|
||||
if data_format == 'channels_last':
|
||||
for offset in [0, 1, -1, -2]:
|
||||
assert_allclose(np_output[:, offset, :, :], 0.)
|
||||
@@ -458,8 +542,8 @@ def test_zero_padding_2d():
|
||||
layer = convolutional.ZeroPadding2D(padding=((1, 2), (3, 4)),
|
||||
data_format=data_format)
|
||||
layer.build(inputs.shape)
|
||||
output = layer(K.variable(inputs))
|
||||
np_output = K.eval(output)
|
||||
outputs = layer(K.variable(inputs))
|
||||
np_output = K.eval(outputs)
|
||||
if data_format == 'channels_last':
|
||||
for top_offset in [0]:
|
||||
assert_allclose(np_output[:, top_offset, :, :], 0.)
|
||||
@@ -506,8 +590,8 @@ def test_zero_padding_3d():
|
||||
layer = convolutional.ZeroPadding3D(padding=(2, 2, 2),
|
||||
data_format=data_format)
|
||||
layer.build(inputs.shape)
|
||||
output = layer(K.variable(inputs))
|
||||
np_output = K.eval(output)
|
||||
outputs = layer(K.variable(inputs))
|
||||
np_output = K.eval(outputs)
|
||||
if data_format == 'channels_last':
|
||||
for offset in [0, 1, -1, -2]:
|
||||
assert_allclose(np_output[:, offset, :, :, :], 0.)
|
||||
@@ -524,8 +608,8 @@ def test_zero_padding_3d():
|
||||
layer = convolutional.ZeroPadding3D(padding=((1, 2), (3, 4), (0, 2)),
|
||||
data_format=data_format)
|
||||
layer.build(inputs.shape)
|
||||
output = layer(K.variable(inputs))
|
||||
np_output = K.eval(output)
|
||||
outputs = layer(K.variable(inputs))
|
||||
np_output = K.eval(outputs)
|
||||
if data_format == 'channels_last':
|
||||
for dim1_offset in [0, -1, -2]:
|
||||
assert_allclose(np_output[:, dim1_offset, :, :, :], 0.)
|
||||
@@ -577,8 +661,8 @@ def test_upsampling_2d():
|
||||
size=(length_row, length_col),
|
||||
data_format=data_format)
|
||||
layer.build(inputs.shape)
|
||||
output = layer(K.variable(inputs))
|
||||
np_output = K.eval(output)
|
||||
outputs = layer(K.variable(inputs))
|
||||
np_output = K.eval(outputs)
|
||||
if data_format == 'channels_first':
|
||||
assert np_output.shape[2] == length_row * input_num_row
|
||||
assert np_output.shape[3] == length_col * input_num_col
|
||||
@@ -597,6 +681,8 @@ def test_upsampling_2d():
|
||||
assert_allclose(np_output, expected_out)
|
||||
|
||||
|
||||
@pytest.mark.skipif((K.backend() == 'cntk'),
|
||||
reason="cntk does not support it yet")
|
||||
def test_upsampling_3d():
|
||||
num_samples = 2
|
||||
stack_size = 2
|
||||
@@ -626,8 +712,8 @@ def test_upsampling_3d():
|
||||
size=(length_dim1, length_dim2, length_dim3),
|
||||
data_format=data_format)
|
||||
layer.build(inputs.shape)
|
||||
output = layer(K.variable(inputs))
|
||||
np_output = K.eval(output)
|
||||
outputs = layer(K.variable(inputs))
|
||||
np_output = K.eval(outputs)
|
||||
if data_format == 'channels_first':
|
||||
assert np_output.shape[2] == length_dim1 * input_len_dim1
|
||||
assert np_output.shape[3] == length_dim2 * input_len_dim2
|
||||
@@ -651,6 +737,8 @@ def test_upsampling_3d():
|
||||
|
||||
|
||||
@keras_test
|
||||
@pytest.mark.skipif((K.backend() == 'cntk'),
|
||||
reason="cntk does not support slice to 0 dimension")
|
||||
def test_cropping_1d():
|
||||
num_samples = 2
|
||||
time_length = 4
|
||||
@@ -686,8 +774,8 @@ def test_cropping_2d():
|
||||
layer = convolutional.Cropping2D(cropping=cropping,
|
||||
data_format=data_format)
|
||||
layer.build(inputs.shape)
|
||||
output = layer(K.variable(inputs))
|
||||
np_output = K.eval(output)
|
||||
outputs = layer(K.variable(inputs))
|
||||
np_output = K.eval(outputs)
|
||||
# compare with numpy
|
||||
if data_format == 'channels_first':
|
||||
expected_out = inputs[:,
|
||||
@@ -714,11 +802,17 @@ def test_cropping_2d():
|
||||
layer = convolutional.Cropping2D(cropping=cropping,
|
||||
data_format=data_format)
|
||||
layer.build(inputs.shape)
|
||||
output = layer(K.variable(inputs))
|
||||
np_output = K.eval(output)
|
||||
outputs = layer(K.variable(inputs))
|
||||
np_output = K.eval(outputs)
|
||||
# compare with input
|
||||
assert_allclose(np_output, inputs)
|
||||
|
||||
# Test invalid use cases
|
||||
with pytest.raises(ValueError):
|
||||
layer = convolutional.Cropping2D(cropping=((1, 1),))
|
||||
with pytest.raises(ValueError):
|
||||
layer = convolutional.Cropping2D(cropping=lambda x: x)
|
||||
|
||||
|
||||
def test_cropping_3d():
|
||||
num_samples = 2
|
||||
@@ -745,8 +839,8 @@ def test_cropping_3d():
|
||||
layer = convolutional.Cropping3D(cropping=cropping,
|
||||
data_format=data_format)
|
||||
layer.build(inputs.shape)
|
||||
output = layer(K.variable(inputs))
|
||||
np_output = K.eval(output)
|
||||
outputs = layer(K.variable(inputs))
|
||||
np_output = K.eval(outputs)
|
||||
# compare with numpy
|
||||
if data_format == 'channels_first':
|
||||
expected_out = inputs[:,
|
||||
@@ -775,10 +869,16 @@ def test_cropping_3d():
|
||||
layer = convolutional.Cropping3D(cropping=cropping,
|
||||
data_format=data_format)
|
||||
layer.build(inputs.shape)
|
||||
output = layer(K.variable(inputs))
|
||||
np_output = K.eval(output)
|
||||
outputs = layer(K.variable(inputs))
|
||||
np_output = K.eval(outputs)
|
||||
# compare with input
|
||||
assert_allclose(np_output, inputs)
|
||||
|
||||
# Test invalid use cases
|
||||
with pytest.raises(ValueError):
|
||||
layer = convolutional.Cropping3D(cropping=((1, 1),))
|
||||
with pytest.raises(ValueError):
|
||||
layer = convolutional.Cropping3D(cropping=lambda x: x)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__])
|
||||
|
||||
@@ -32,9 +32,23 @@ def test_dropout():
|
||||
kwargs={'rate': 0.5},
|
||||
input_shape=(2, 3, 4))
|
||||
|
||||
layer_test(layers.SpatialDropout2D,
|
||||
kwargs={'rate': 0.5},
|
||||
input_shape=(2, 3, 4, 5))
|
||||
for data_format in ['channels_last', 'channels_first']:
|
||||
for shape in [(4, 5), (4, 5, 6)]:
|
||||
if data_format == 'channels_last':
|
||||
input_shape = (2,) + shape + (3,)
|
||||
else:
|
||||
input_shape = (2, 3) + shape
|
||||
layer_test(layers.SpatialDropout2D if len(shape) == 2 else layers.SpatialDropout3D,
|
||||
kwargs={'rate': 0.5,
|
||||
'data_format': data_format},
|
||||
input_shape=input_shape)
|
||||
|
||||
# Test invalid use cases
|
||||
with pytest.raises(ValueError):
|
||||
layer_test(layers.SpatialDropout2D if len(shape) == 2 else layers.SpatialDropout3D,
|
||||
kwargs={'rate': 0.5,
|
||||
'data_format': 'channels_middle'},
|
||||
input_shape=input_shape)
|
||||
|
||||
|
||||
@keras_test
|
||||
@@ -97,6 +111,24 @@ def test_lambda():
|
||||
'arguments': {'a': 0.6, 'b': 0.4}},
|
||||
input_shape=(3, 2))
|
||||
|
||||
def antirectifier(x):
|
||||
x -= K.mean(x, axis=1, keepdims=True)
|
||||
x = K.l2_normalize(x, axis=1)
|
||||
pos = K.relu(x)
|
||||
neg = K.relu(-x)
|
||||
return K.concatenate([pos, neg], axis=1)
|
||||
|
||||
def antirectifier_output_shape(input_shape):
|
||||
shape = list(input_shape)
|
||||
assert len(shape) == 2 # only valid for 2D tensors
|
||||
shape[-1] *= 2
|
||||
return tuple(shape)
|
||||
|
||||
layer_test(layers.Lambda,
|
||||
kwargs={'function': antirectifier,
|
||||
'output_shape': antirectifier_output_shape},
|
||||
input_shape=(3, 2))
|
||||
|
||||
# test serialization with function
|
||||
def f(x):
|
||||
return x + 1
|
||||
|
||||
@@ -16,6 +16,16 @@ def test_embedding():
|
||||
input_shape=(3, 2),
|
||||
input_dtype='int32',
|
||||
expected_output_dtype=K.floatx())
|
||||
layer_test(Embedding,
|
||||
kwargs={'output_dim': 4, 'input_dim': 10, 'mask_zero': True},
|
||||
input_shape=(3, 2, 5),
|
||||
input_dtype='int32',
|
||||
expected_output_dtype=K.floatx())
|
||||
layer_test(Embedding,
|
||||
kwargs={'output_dim': 4, 'input_dim': 10, 'mask_zero': True, 'input_length': (None, 5)},
|
||||
input_shape=(3, 2, 5),
|
||||
input_dtype='int32',
|
||||
expected_output_dtype=K.floatx())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -2,9 +2,12 @@ import pytest
|
||||
from keras.utils.test_utils import layer_test
|
||||
from keras.utils.test_utils import keras_test
|
||||
from keras.layers import noise
|
||||
from keras import backend as K
|
||||
|
||||
|
||||
@keras_test
|
||||
@pytest.mark.skipif((K.backend() == 'cntk'),
|
||||
reason="cntk does not support it yet")
|
||||
def test_GaussianNoise():
|
||||
layer_test(noise.GaussianNoise,
|
||||
kwargs={'stddev': 1.},
|
||||
@@ -12,11 +15,22 @@ def test_GaussianNoise():
|
||||
|
||||
|
||||
@keras_test
|
||||
@pytest.mark.skipif((K.backend() == 'cntk'),
|
||||
reason="cntk does not support it yet")
|
||||
def test_GaussianDropout():
|
||||
layer_test(noise.GaussianDropout,
|
||||
kwargs={'rate': 0.5},
|
||||
input_shape=(3, 2, 3))
|
||||
|
||||
|
||||
@keras_test
|
||||
@pytest.mark.skipif((K.backend() == 'cntk'),
|
||||
reason="cntk does not support it yet")
|
||||
def test_AlphaDropout():
|
||||
layer_test(noise.AlphaDropout,
|
||||
kwargs={'rate': 0.1},
|
||||
input_shape=(3, 2, 3))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__])
|
||||
|
||||
@@ -15,7 +15,7 @@ input_shapes = [np.ones((10, 10)), np.ones((10, 10, 10))]
|
||||
|
||||
|
||||
@keras_test
|
||||
def basic_batchnorm_test():
|
||||
def test_basic_batchnorm():
|
||||
from keras import regularizers
|
||||
layer_test(normalization.BatchNormalization,
|
||||
kwargs={'momentum': 0.9,
|
||||
@@ -29,9 +29,6 @@ def basic_batchnorm_test():
|
||||
'moving_mean_initializer': 'zeros',
|
||||
'moving_variance_initializer': 'ones'},
|
||||
input_shape=(3, 4, 2))
|
||||
layer_test(normalization.BatchNormalization,
|
||||
kwargs={'scale': False, 'center': False},
|
||||
input_shape=(3, 3))
|
||||
|
||||
|
||||
@keras_test
|
||||
|
||||
@@ -50,6 +50,23 @@ def test_dynamic_behavior(layer_class):
|
||||
model.train_on_batch(x, y)
|
||||
|
||||
|
||||
@rnn_test
|
||||
@pytest.mark.skipif(K.backend() == 'cntk', reason='Stateful is not supported with CNTK')
|
||||
def test_stateful_invalid_use(layer_class):
|
||||
layer = layer_class(units,
|
||||
stateful=True,
|
||||
batch_input_shape=(num_samples, timesteps, embedding_dim))
|
||||
model = Sequential()
|
||||
model.add(layer)
|
||||
model.compile('sgd', 'mse')
|
||||
x = np.random.random((num_samples * 2, timesteps, embedding_dim))
|
||||
y = np.random.random((num_samples * 2, units))
|
||||
with pytest.raises(ValueError):
|
||||
model.fit(x, y)
|
||||
with pytest.raises(ValueError):
|
||||
model.predict(x, batch_size=num_samples + 1)
|
||||
|
||||
|
||||
@rnn_test
|
||||
def test_dropout(layer_class):
|
||||
layer_test(layer_class,
|
||||
@@ -74,9 +91,17 @@ def test_implementation_mode(layer_class):
|
||||
kwargs={'units': units,
|
||||
'implementation': mode},
|
||||
input_shape=(num_samples, timesteps, embedding_dim))
|
||||
layer_test(layer_class,
|
||||
kwargs={'units': units,
|
||||
'implementation': mode,
|
||||
'dropout': 0.1,
|
||||
'recurrent_dropout': 0.1},
|
||||
input_shape=(num_samples, timesteps, embedding_dim))
|
||||
|
||||
|
||||
@rnn_test
|
||||
@pytest.mark.skipif((K.backend() == 'cntk'),
|
||||
reason="cntk does not support stateful RNN yet")
|
||||
def test_statefulness(layer_class):
|
||||
model = Sequential()
|
||||
model.add(embeddings.Embedding(embedding_num, embedding_dim,
|
||||
@@ -147,6 +172,8 @@ def test_regularizer(layer_class):
|
||||
|
||||
|
||||
@keras_test
|
||||
@pytest.mark.skipif((K.backend() == 'cntk'),
|
||||
reason="cntk does not support mask on RNN yet")
|
||||
def test_masking_layer():
|
||||
''' This test based on a previously failing issue here:
|
||||
https://github.com/fchollet/keras/issues/1567
|
||||
@@ -170,7 +197,9 @@ def test_masking_layer():
|
||||
|
||||
@rnn_test
|
||||
def test_from_config(layer_class):
|
||||
for stateful in (False, True):
|
||||
# cntk does not support stateful yet.
|
||||
stateful_flags = (False, True) if K.backend() != 'cntk' else (False,)
|
||||
for stateful in stateful_flags:
|
||||
l1 = layer_class(units=1, stateful=stateful)
|
||||
l2 = layer_class.from_config(l1.get_config())
|
||||
assert l1.get_config() == l2.get_config()
|
||||
@@ -220,6 +249,8 @@ def test_specify_initial_state_non_keras_tensor(layer_class):
|
||||
|
||||
|
||||
@rnn_test
|
||||
@pytest.mark.skipif((K.backend() == 'cntk'),
|
||||
reason="cntk does not support stateful RNN yet")
|
||||
def test_reset_states_with_values(layer_class):
|
||||
num_states = 2 if layer_class is recurrent.LSTM else 1
|
||||
|
||||
@@ -253,7 +284,7 @@ def test_specify_state_with_masking(layer_class):
|
||||
num_states = 2 if layer_class is recurrent.LSTM else 1
|
||||
|
||||
inputs = Input((timesteps, embedding_dim))
|
||||
masked_inputs = Masking()(inputs)
|
||||
_ = Masking()(inputs)
|
||||
initial_state = [Input((units,)) for _ in range(num_states)]
|
||||
output = layer_class(units)(inputs, initial_state=initial_state)
|
||||
|
||||
@@ -266,5 +297,37 @@ def test_specify_state_with_masking(layer_class):
|
||||
targets = np.random.random((num_samples, units))
|
||||
model.fit([inputs] + initial_state, targets)
|
||||
|
||||
|
||||
@rnn_test
|
||||
@pytest.mark.skipif((K.backend() == 'cntk'),
|
||||
reason="cntk does not support stateful RNN yet")
|
||||
def test_return_state(layer_class):
|
||||
num_states = 2 if layer_class is recurrent.LSTM else 1
|
||||
|
||||
inputs = Input(batch_shape=(num_samples, timesteps, embedding_dim))
|
||||
layer = layer_class(units, return_state=True, stateful=True)
|
||||
outputs = layer(inputs)
|
||||
output, state = outputs[0], outputs[1:]
|
||||
assert len(state) == num_states
|
||||
model = Model(inputs, state[0])
|
||||
|
||||
inputs = np.random.random((num_samples, timesteps, embedding_dim))
|
||||
state = model.predict(inputs)
|
||||
np.testing.assert_allclose(K.eval(layer.states[0]), state, atol=1e-4)
|
||||
|
||||
|
||||
@rnn_test
|
||||
def test_state_reuse(layer_class):
|
||||
inputs = Input(batch_shape=(num_samples, timesteps, embedding_dim))
|
||||
layer = layer_class(units, return_state=True, return_sequences=True)
|
||||
outputs = layer(inputs)
|
||||
output, state = outputs[0], outputs[1:]
|
||||
output = layer_class(units)(output, initial_state=state)
|
||||
model = Model(inputs, output)
|
||||
|
||||
inputs = np.random.random((num_samples, timesteps, embedding_dim))
|
||||
outputs = model.predict(inputs)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__])
|
||||
|
||||
@@ -5,6 +5,7 @@ from keras.utils.test_utils import keras_test
|
||||
from keras.layers import wrappers, Input
|
||||
from keras.layers import core, convolutional, recurrent, embeddings
|
||||
from keras.models import Sequential, Model, model_from_json
|
||||
from keras import backend as K
|
||||
|
||||
|
||||
@keras_test
|
||||
@@ -87,6 +88,19 @@ def test_TimeDistributed():
|
||||
outer_model.fit(np.random.random((10, 3, 2)), np.random.random((10, 3, 3)), epochs=1, batch_size=10)
|
||||
|
||||
|
||||
@keras_test
|
||||
@pytest.mark.skipif((K.backend() == 'cntk'),
|
||||
reason='cntk does not support dropout yet')
|
||||
def test_TimeDistributed_learning_phase():
|
||||
# test layers that need learning_phase to be set
|
||||
np.random.seed(1234)
|
||||
x = Input(shape=(3, 2))
|
||||
y = wrappers.TimeDistributed(core.Dropout(.999))(x, training=True)
|
||||
model = Model(x, y)
|
||||
y = model.predict(np.random.random((10, 3, 2)))
|
||||
assert_allclose(np.mean(y), 0., atol=1e-1, rtol=1e-1)
|
||||
|
||||
|
||||
@keras_test
|
||||
def test_regularizers():
|
||||
model = Sequential()
|
||||
@@ -108,6 +122,8 @@ def test_regularizers():
|
||||
|
||||
|
||||
@keras_test
|
||||
@pytest.mark.skipif((K.backend() == 'cntk'),
|
||||
reason='cntk does not support reverse yet')
|
||||
def test_Bidirectional():
|
||||
rnn = recurrent.SimpleRNN
|
||||
samples = 2
|
||||
@@ -142,18 +158,18 @@ def test_Bidirectional():
|
||||
model.fit(x, y, epochs=1, batch_size=1)
|
||||
|
||||
# test with functional API
|
||||
input = Input((timesteps, dim))
|
||||
output = wrappers.Bidirectional(rnn(output_dim, dropout=dropout_rate,
|
||||
recurrent_dropout=dropout_rate),
|
||||
merge_mode=mode)(input)
|
||||
model = Model(input, output)
|
||||
inputs = Input((timesteps, dim))
|
||||
outputs = wrappers.Bidirectional(rnn(output_dim, dropout=dropout_rate,
|
||||
recurrent_dropout=dropout_rate),
|
||||
merge_mode=mode)(inputs)
|
||||
model = Model(inputs, outputs)
|
||||
model.compile(loss='mse', optimizer='sgd')
|
||||
model.fit(x, y, epochs=1, batch_size=1)
|
||||
|
||||
# Bidirectional and stateful
|
||||
input = Input(batch_shape=(1, timesteps, dim))
|
||||
output = wrappers.Bidirectional(rnn(output_dim, stateful=True), merge_mode=mode)(input)
|
||||
model = Model(input, output)
|
||||
inputs = Input(batch_shape=(1, timesteps, dim))
|
||||
outputs = wrappers.Bidirectional(rnn(output_dim, stateful=True), merge_mode=mode)(inputs)
|
||||
model = Model(inputs, outputs)
|
||||
model.compile(loss='mse', optimizer='sgd')
|
||||
model.fit(x, y, epochs=1, batch_size=1)
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import pytest
|
||||
import json
|
||||
from keras.utils.test_utils import keras_test
|
||||
from keras.engine.topology import preprocess_weights_for_loading
|
||||
import keras
|
||||
import numpy as np
|
||||
|
||||
@@ -109,6 +110,10 @@ def test_lstm_legacy_interface():
|
||||
new_layer = keras.layers.LSTM(2, input_shape=[3, 5], name='d')
|
||||
assert json.dumps(old_layer.get_config()) == json.dumps(new_layer.get_config())
|
||||
|
||||
preprocess_weights_for_loading(new_layer,
|
||||
[np.random.random(x) for x in [(5, 2), (2, 2), (2,)] * 4],
|
||||
original_keras_version='1')
|
||||
|
||||
old_layer = keras.layers.LSTM(input_shape=[3, 5], output_dim=2, name='d', consume_less='mem')
|
||||
new_layer = keras.layers.LSTM(2, input_shape=[3, 5], name='d', implementation=1)
|
||||
assert json.dumps(old_layer.get_config()) == json.dumps(new_layer.get_config())
|
||||
@@ -207,6 +212,10 @@ def test_gru_legacy_interface():
|
||||
new_layer = keras.layers.GRU(2, input_shape=[3, 5], name='d')
|
||||
assert json.dumps(old_layer.get_config()) == json.dumps(new_layer.get_config())
|
||||
|
||||
preprocess_weights_for_loading(new_layer,
|
||||
[np.random.random(x) for x in [(5, 2), (2, 2), (2,)] * 3],
|
||||
original_keras_version='1')
|
||||
|
||||
old_layer = keras.layers.GRU(2, init='normal',
|
||||
inner_init='glorot_uniform',
|
||||
inner_activation='hard_sigmoid',
|
||||
@@ -798,13 +807,14 @@ def test_generator_methods_interface():
|
||||
samples_per_epoch=1,
|
||||
validation_data=val_generator(),
|
||||
nb_val_samples=1,
|
||||
nb_worker=1)
|
||||
nb_worker=1, pickle_safe=True, max_q_size=3)
|
||||
|
||||
model.evaluate_generator(generator=train_generator(),
|
||||
val_samples=2,
|
||||
nb_worker=1)
|
||||
nb_worker=1, pickle_safe=False, max_q_size=3)
|
||||
model.predict_generator(generator=pred_generator(),
|
||||
val_samples=2,
|
||||
nb_worker=1)
|
||||
nb_worker=1, pickle_safe=False, max_q_size=3)
|
||||
|
||||
|
||||
def test_spatialdropout1d_legacy_interface():
|
||||
|
||||
@@ -184,6 +184,8 @@ def test_merge():
|
||||
|
||||
|
||||
@keras_test
|
||||
@pytest.mark.skipif((K.backend() == 'cntk'),
|
||||
reason="cntk does not support stateful RNN yet")
|
||||
def test_merge_mask_2d():
|
||||
rand = lambda *shape: np.asarray(np.random.random(shape) > 0.5, dtype='int32')
|
||||
|
||||
@@ -217,6 +219,8 @@ def test_merge_mask_2d():
|
||||
|
||||
|
||||
@keras_test
|
||||
@pytest.mark.skipif((K.backend() == 'cntk'),
|
||||
reason="cntk does not support stateful RNN yet")
|
||||
def test_merge_mask_3d():
|
||||
rand = lambda *shape: np.asarray(np.random.random(shape) > 0.5, dtype='int32')
|
||||
|
||||
|
||||
@@ -19,6 +19,17 @@ batch_size = 32
|
||||
epochs = 1
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def in_tmpdir(tmpdir):
|
||||
"""Runs a function in a temporary directory.
|
||||
|
||||
Checks that the directory is empty afterwards.
|
||||
"""
|
||||
with tmpdir.as_cwd():
|
||||
yield None
|
||||
assert not tmpdir.listdir()
|
||||
|
||||
|
||||
def _get_test_data():
|
||||
np.random.seed(1234)
|
||||
|
||||
@@ -36,7 +47,7 @@ def _get_test_data():
|
||||
|
||||
|
||||
@keras_test
|
||||
def test_merge_sum():
|
||||
def test_merge_sum(in_tmpdir):
|
||||
(x_train, y_train), (x_test, y_test) = _get_test_data()
|
||||
left = Sequential()
|
||||
left.add(Dense(num_hidden, input_shape=(input_dim,)))
|
||||
@@ -131,7 +142,7 @@ def test_merge_dot():
|
||||
|
||||
|
||||
@keras_test
|
||||
def test_merge_concat():
|
||||
def test_merge_concat(in_tmpdir):
|
||||
(x_train, y_train), (x_test, y_test) = _get_test_data()
|
||||
|
||||
left = Sequential(name='branch_1')
|
||||
@@ -171,7 +182,7 @@ def test_merge_concat():
|
||||
|
||||
|
||||
@keras_test
|
||||
def test_merge_recursivity():
|
||||
def test_merge_recursivity(in_tmpdir):
|
||||
(x_train, y_train), (x_test, y_test) = _get_test_data()
|
||||
left = Sequential()
|
||||
left.add(Dense(num_hidden, input_shape=(input_dim,)))
|
||||
@@ -228,7 +239,7 @@ def test_merge_recursivity():
|
||||
|
||||
|
||||
@keras_test
|
||||
def test_merge_overlap():
|
||||
def test_merge_overlap(in_tmpdir):
|
||||
(x_train, y_train), (x_test, y_test) = _get_test_data()
|
||||
left = Sequential()
|
||||
left.add(Dense(num_hidden, input_shape=(input_dim,)))
|
||||
|
||||
@@ -16,7 +16,8 @@ allobj = [losses.mean_squared_error,
|
||||
losses.kullback_leibler_divergence,
|
||||
losses.poisson,
|
||||
losses.cosine_proximity,
|
||||
losses.logcosh]
|
||||
losses.logcosh,
|
||||
losses.categorical_hinge]
|
||||
|
||||
|
||||
def test_objective_shapes_3d():
|
||||
@@ -47,11 +48,13 @@ def test_cce_one_hot():
|
||||
|
||||
|
||||
def test_categorical_hinge():
|
||||
y_pred = K.variable(np.array([[0.3, 0.2, 0.1], [0.1, 0.2, 0.7]]))
|
||||
y_true = K.variable(np.array([[0, 1, 0], [1, 0, 0]]))
|
||||
y_pred = K.variable(np.array([[0.3, 0.2, 0.1],
|
||||
[0.1, 0.2, 0.7]]))
|
||||
y_true = K.variable(np.array([[0, 1, 0],
|
||||
[1, 0, 0]]))
|
||||
expected_loss = ((0.3 - 0.2 + 1) + (0.7 - 0.1 + 1)) / 2.0
|
||||
loss = K.eval(losses.categorical_hinge(y_true, y_pred))
|
||||
assert np.isclose(expected_loss, loss)
|
||||
assert np.isclose(expected_loss, np.mean(loss))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -42,6 +42,8 @@ def test_sparse_metrics():
|
||||
assert K.eval(metric(y_a, y_b)).shape == (6,)
|
||||
|
||||
|
||||
@pytest.mark.skipif((K.backend() == 'cntk'),
|
||||
reason="keras cntk backend does not support top_k yet")
|
||||
def test_top_k_categorical_accuracy():
|
||||
y_pred = K.variable(np.array([[0.3, 0.2, 0.1], [0.1, 0.2, 0.7]]))
|
||||
y_true = K.variable(np.array([[0, 1, 0], [1, 0, 0]]))
|
||||
@@ -56,5 +58,21 @@ def test_top_k_categorical_accuracy():
|
||||
assert failure_result == 0
|
||||
|
||||
|
||||
@pytest.mark.skipif((K.backend() == 'cntk'),
|
||||
reason="keras cntk backend does not support top_k yet")
|
||||
def test_sparse_top_k_categorical_accuracy():
|
||||
y_pred = K.variable(np.array([[0.3, 0.2, 0.1], [0.1, 0.2, 0.7]]))
|
||||
y_true = K.variable(np.array([[1], [0]]))
|
||||
success_result = K.eval(metrics.sparse_top_k_categorical_accuracy(y_true, y_pred,
|
||||
k=3))
|
||||
assert success_result == 1
|
||||
partial_result = K.eval(metrics.sparse_top_k_categorical_accuracy(y_true, y_pred,
|
||||
k=2))
|
||||
assert partial_result == 0.5
|
||||
failure_result = K.eval(metrics.sparse_top_k_categorical_accuracy(y_true, y_pred,
|
||||
k=1))
|
||||
assert failure_result == 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__])
|
||||
|
||||
@@ -88,5 +88,24 @@ def test_clipvalue():
|
||||
_test_optimizer(sgd)
|
||||
|
||||
|
||||
def test_tfoptimizer():
|
||||
from keras import constraints
|
||||
from tensorflow import train
|
||||
optimizer = optimizers.TFOptimizer(train.AdamOptimizer)
|
||||
model = Sequential()
|
||||
model.add(Dense(2, input_shape=(3,), kernel_constraint=constraints.MaxNorm(1)))
|
||||
model.compile(loss='mean_squared_error', optimizer=optimizer)
|
||||
# TF optimizers do not support weights constraints
|
||||
with pytest.raises(ValueError):
|
||||
model.fit(np.random.random((5, 3)), np.random.random((5, 2)), epochs=1, batch_size=5, verbose=0)
|
||||
# not supported
|
||||
with pytest.raises(NotImplementedError):
|
||||
optimizer.weights
|
||||
with pytest.raises(NotImplementedError):
|
||||
optimizer.get_config()
|
||||
with pytest.raises(NotImplementedError):
|
||||
optimizer.from_config(None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__])
|
||||
|
||||
@@ -3,8 +3,6 @@ from keras.preprocessing import image
|
||||
from PIL import Image
|
||||
import numpy as np
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
|
||||
class TestImage:
|
||||
@@ -29,7 +27,7 @@ class TestImage:
|
||||
def teardown_class(cls):
|
||||
del cls.all_test_images
|
||||
|
||||
def test_image_data_generator(self):
|
||||
def test_image_data_generator(self, tmpdir):
|
||||
for test_images in self.all_test_images:
|
||||
img_list = []
|
||||
for im in test_images:
|
||||
@@ -54,12 +52,10 @@ class TestImage:
|
||||
vertical_flip=True)
|
||||
generator.fit(images, augment=True)
|
||||
|
||||
tmp_folder = tempfile.mkdtemp(prefix='test_images')
|
||||
for x, y in generator.flow(images, np.arange(images.shape[0]),
|
||||
shuffle=True, save_to_dir=tmp_folder):
|
||||
shuffle=True, save_to_dir=str(tmpdir)):
|
||||
assert x.shape[1:] == images.shape[1:]
|
||||
break
|
||||
shutil.rmtree(tmp_folder)
|
||||
|
||||
def test_image_data_generator_invalid_data(self):
|
||||
generator = image.ImageDataGenerator(
|
||||
@@ -73,22 +69,11 @@ class TestImage:
|
||||
with pytest.raises(ValueError):
|
||||
x = np.random.random((3, 10, 10))
|
||||
generator.fit(x)
|
||||
with pytest.raises(ValueError):
|
||||
x = np.random.random((32, 3, 10, 10))
|
||||
generator.fit(x)
|
||||
with pytest.raises(ValueError):
|
||||
x = np.random.random((32, 10, 10, 5))
|
||||
generator.fit(x)
|
||||
|
||||
# Test flow with invalid data
|
||||
with pytest.raises(ValueError):
|
||||
x = np.random.random((32, 10, 10, 5))
|
||||
generator.flow(np.arange(x.shape[0]))
|
||||
with pytest.raises(ValueError):
|
||||
x = np.random.random((32, 10, 10))
|
||||
generator.flow(np.arange(x.shape[0]))
|
||||
with pytest.raises(ValueError):
|
||||
x = np.random.random((32, 3, 10, 10))
|
||||
generator.flow(np.arange(x.shape[0]))
|
||||
|
||||
def test_image_data_generator_fit(self):
|
||||
generator = image.ImageDataGenerator(
|
||||
@@ -97,6 +82,7 @@ class TestImage:
|
||||
featurewise_std_normalization=True,
|
||||
samplewise_std_normalization=True,
|
||||
zca_whitening=True,
|
||||
zoom_range=(0.2, 0.2),
|
||||
data_format='channels_last')
|
||||
# Test grayscale
|
||||
x = np.random.random((32, 10, 10, 1))
|
||||
@@ -118,9 +104,8 @@ class TestImage:
|
||||
x = np.random.random((32, 3, 10, 10))
|
||||
generator.fit(x)
|
||||
|
||||
def test_directory_iterator(self):
|
||||
def test_directory_iterator(self, tmpdir):
|
||||
num_classes = 2
|
||||
tmp_folder = tempfile.mkdtemp(prefix='test_images')
|
||||
|
||||
# create folders and subfolders
|
||||
paths = []
|
||||
@@ -133,7 +118,7 @@ class TestImage:
|
||||
os.path.join(class_directory, 'subfolder-1', 'sub-subfolder')
|
||||
]
|
||||
for path in classpaths:
|
||||
os.mkdir(os.path.join(tmp_folder, path))
|
||||
tmpdir.join(path).mkdir()
|
||||
paths.append(classpaths)
|
||||
|
||||
# save the images in the paths
|
||||
@@ -147,34 +132,38 @@ class TestImage:
|
||||
classpaths = paths[im_class]
|
||||
filename = os.path.join(classpaths[count % len(classpaths)], 'image-{}.jpg'.format(count))
|
||||
filenames.append(filename)
|
||||
im.save(os.path.join(tmp_folder, filename))
|
||||
im.save(str(tmpdir / filename))
|
||||
count += 1
|
||||
|
||||
# create iterator
|
||||
generator = image.ImageDataGenerator()
|
||||
dir_iterator = generator.flow_from_directory(tmp_folder)
|
||||
dir_iterator = generator.flow_from_directory(str(tmpdir))
|
||||
|
||||
# check number of classes and images
|
||||
assert(len(dir_iterator.class_indices) == num_classes)
|
||||
assert(len(dir_iterator.classes) == count)
|
||||
assert(sorted(dir_iterator.filenames) == sorted(filenames))
|
||||
shutil.rmtree(tmp_folder)
|
||||
|
||||
def test_directory_iterator_class_mode_input(self):
|
||||
tmp_folder = tempfile.mkdtemp(prefix='test_images')
|
||||
os.mkdir(os.path.join(tmp_folder, 'class-1'))
|
||||
# Test invalid use cases
|
||||
with pytest.raises(ValueError):
|
||||
generator.flow_from_directory(str(tmpdir), color_mode='cmyk')
|
||||
with pytest.raises(ValueError):
|
||||
generator.flow_from_directory(str(tmpdir), class_mode='output')
|
||||
|
||||
def test_directory_iterator_class_mode_input(self, tmpdir):
|
||||
tmpdir.join('class-1').mkdir()
|
||||
|
||||
# save the images in the paths
|
||||
count = 0
|
||||
for test_images in self.all_test_images:
|
||||
for im in test_images:
|
||||
filename = os.path.join(tmp_folder, 'class-1', 'image-{}.jpg'.format(count))
|
||||
im.save(os.path.join(tmp_folder, filename))
|
||||
filename = str(tmpdir / 'class-1' / 'image-{}.jpg'.format(count))
|
||||
im.save(filename)
|
||||
count += 1
|
||||
|
||||
# create iterator
|
||||
generator = image.ImageDataGenerator()
|
||||
dir_iterator = generator.flow_from_directory(tmp_folder, class_mode='input')
|
||||
dir_iterator = generator.flow_from_directory(str(tmpdir), class_mode='input')
|
||||
batch = next(dir_iterator)
|
||||
|
||||
# check if input and output have the same shape
|
||||
@@ -184,7 +173,6 @@ class TestImage:
|
||||
output_img = batch[1][0]
|
||||
output_img[0][0][0] += 1
|
||||
assert(input_img[0][0][0] != output_img[0][0][0])
|
||||
shutil.rmtree(tmp_folder)
|
||||
|
||||
def test_img_utils(self):
|
||||
height, width = 10, 8
|
||||
@@ -215,6 +203,31 @@ class TestImage:
|
||||
x = image.img_to_array(img, data_format='channels_last')
|
||||
assert x.shape == (height, width, 1)
|
||||
|
||||
# Test invalid use case
|
||||
with pytest.raises(ValueError):
|
||||
x = np.random.random((height, width)) # not 3D
|
||||
img = image.array_to_img(x, data_format='channels_first')
|
||||
with pytest.raises(ValueError):
|
||||
x = np.random.random((height, width, 3))
|
||||
img = image.array_to_img(x, data_format='channels') # unknown data_format
|
||||
with pytest.raises(ValueError):
|
||||
x = np.random.random((height, width, 5)) # neither RGB nor gray-scale
|
||||
img = image.array_to_img(x, data_format='channels_last')
|
||||
with pytest.raises(ValueError):
|
||||
x = np.random.random((height, width, 3))
|
||||
img = image.img_to_array(x, data_format='channels') # unknown data_format
|
||||
with pytest.raises(ValueError):
|
||||
x = np.random.random((height, width, 5, 3)) # neither RGB nor gray-scale
|
||||
img = image.img_to_array(x, data_format='channels_last')
|
||||
|
||||
def test_random_transforms(self):
|
||||
x = np.random.random((2, 28, 28))
|
||||
assert image.random_rotation(x, 45).shape == (2, 28, 28)
|
||||
assert image.random_shift(x, 1, 1).shape == (2, 28, 28)
|
||||
assert image.random_shear(x, 20).shape == (2, 28, 28)
|
||||
assert image.random_zoom(x, (5, 5)).shape == (2, 28, 28)
|
||||
assert image.random_channel_shift(x, 20).shape == (2, 28, 28)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__])
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from keras.preprocessing.text import Tokenizer, one_hot
|
||||
import pytest
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from keras.preprocessing.text import Tokenizer, one_hot, hashing_trick
|
||||
|
||||
|
||||
def test_one_hot():
|
||||
@@ -11,6 +12,22 @@ def test_one_hot():
|
||||
assert np.min(encoded) >= 0
|
||||
|
||||
|
||||
def test_hashing_trick_hash():
|
||||
text = 'The cat sat on the mat.'
|
||||
encoded = hashing_trick(text, 5)
|
||||
assert len(encoded) == 6
|
||||
assert np.max(encoded) <= 4
|
||||
assert np.min(encoded) >= 1
|
||||
|
||||
|
||||
def test_hashing_trick_md5():
|
||||
text = 'The cat sat on the mat.'
|
||||
encoded = hashing_trick(text, 5, hash_function='md5')
|
||||
assert len(encoded) == 6
|
||||
assert np.max(encoded) <= 4
|
||||
assert np.min(encoded) >= 1
|
||||
|
||||
|
||||
def test_tokenizer():
|
||||
texts = ['The cat sat on the mat.',
|
||||
'The dog sat on the log.',
|
||||
|
||||
@@ -54,9 +54,9 @@ def test_TerminateOnNaN():
|
||||
|
||||
|
||||
@keras_test
|
||||
def test_ModelCheckpoint():
|
||||
def test_ModelCheckpoint(tmpdir):
|
||||
np.random.seed(1337)
|
||||
filepath = 'checkpoint.h5'
|
||||
filepath = str(tmpdir / 'checkpoint.h5')
|
||||
(X_train, y_train), (X_test, y_test) = get_test_data(num_train=train_samples,
|
||||
num_test=test_samples,
|
||||
input_shape=(input_dim,),
|
||||
@@ -80,7 +80,7 @@ def test_ModelCheckpoint():
|
||||
save_best_only=save_best_only, mode=mode)]
|
||||
model.fit(X_train, y_train, batch_size=batch_size,
|
||||
validation_data=(X_test, y_test), callbacks=cbks, epochs=1)
|
||||
assert os.path.exists(filepath)
|
||||
assert os.path.isfile(filepath)
|
||||
os.remove(filepath)
|
||||
|
||||
# case 2
|
||||
@@ -89,7 +89,7 @@ def test_ModelCheckpoint():
|
||||
save_best_only=save_best_only, mode=mode)]
|
||||
model.fit(X_train, y_train, batch_size=batch_size,
|
||||
validation_data=(X_test, y_test), callbacks=cbks, epochs=1)
|
||||
assert os.path.exists(filepath)
|
||||
assert os.path.isfile(filepath)
|
||||
os.remove(filepath)
|
||||
|
||||
# case 3
|
||||
@@ -99,7 +99,7 @@ def test_ModelCheckpoint():
|
||||
save_best_only=save_best_only, mode=mode)]
|
||||
model.fit(X_train, y_train, batch_size=batch_size,
|
||||
validation_data=(X_test, y_test), callbacks=cbks, epochs=1)
|
||||
assert os.path.exists(filepath)
|
||||
assert os.path.isfile(filepath)
|
||||
os.remove(filepath)
|
||||
|
||||
# case 4
|
||||
@@ -108,7 +108,7 @@ def test_ModelCheckpoint():
|
||||
save_best_only=save_best_only, mode=mode)]
|
||||
model.fit(X_train, y_train, batch_size=batch_size,
|
||||
validation_data=(X_test, y_test), callbacks=cbks, epochs=1)
|
||||
assert os.path.exists(filepath)
|
||||
assert os.path.isfile(filepath)
|
||||
os.remove(filepath)
|
||||
|
||||
# case 5
|
||||
@@ -121,12 +121,13 @@ def test_ModelCheckpoint():
|
||||
period=period)]
|
||||
model.fit(X_train, y_train, batch_size=batch_size,
|
||||
validation_data=(X_test, y_test), callbacks=cbks, epochs=4)
|
||||
assert os.path.exists(filepath.format(epoch=1))
|
||||
assert os.path.exists(filepath.format(epoch=3))
|
||||
assert os.path.isfile(filepath.format(epoch=1))
|
||||
assert os.path.isfile(filepath.format(epoch=3))
|
||||
assert not os.path.exists(filepath.format(epoch=0))
|
||||
assert not os.path.exists(filepath.format(epoch=2))
|
||||
os.remove(filepath.format(epoch=1))
|
||||
os.remove(filepath.format(epoch=3))
|
||||
assert not tmpdir.listdir()
|
||||
|
||||
|
||||
@keras_test
|
||||
@@ -244,9 +245,9 @@ def test_ReduceLROnPlateau():
|
||||
|
||||
|
||||
@keras_test
|
||||
def test_CSVLogger():
|
||||
def test_CSVLogger(tmpdir):
|
||||
np.random.seed(1337)
|
||||
filepath = 'log.tsv'
|
||||
filepath = str(tmpdir / 'log.tsv')
|
||||
sep = '\t'
|
||||
(X_train, y_train), (X_test, y_test) = get_test_data(num_train=train_samples,
|
||||
num_test=test_samples,
|
||||
@@ -273,7 +274,7 @@ def test_CSVLogger():
|
||||
model.fit(X_train, y_train, batch_size=batch_size,
|
||||
validation_data=(X_test, y_test), callbacks=cbks, epochs=1)
|
||||
|
||||
assert os.path.exists(filepath)
|
||||
assert os.path.isfile(filepath)
|
||||
with open(filepath) as csvfile:
|
||||
dialect = Sniffer().sniff(csvfile.read())
|
||||
assert dialect.delimiter == sep
|
||||
@@ -296,15 +297,16 @@ def test_CSVLogger():
|
||||
assert len(re.findall('epoch', output)) == 1
|
||||
|
||||
os.remove(filepath)
|
||||
assert not tmpdir.listdir()
|
||||
|
||||
|
||||
@keras_test
|
||||
@pytest.mark.skipif((K.backend() != 'tensorflow'),
|
||||
reason='Requires tensorflow backend')
|
||||
def test_TensorBoard():
|
||||
np.random.seed(1337)
|
||||
def test_TensorBoard(tmpdir):
|
||||
np.random.seed(np.random.randint(1, 1e7))
|
||||
filepath = str(tmpdir / 'logs')
|
||||
|
||||
filepath = './logs'
|
||||
(X_train, y_train), (X_test, y_test) = get_test_data(
|
||||
num_train=train_samples,
|
||||
num_test=test_samples,
|
||||
@@ -379,17 +381,18 @@ def test_TensorBoard():
|
||||
model.fit_generator(data_generator(True), len(X_train), epochs=2,
|
||||
callbacks=cbks)
|
||||
|
||||
assert os.path.exists(filepath)
|
||||
assert os.path.isdir(filepath)
|
||||
shutil.rmtree(filepath)
|
||||
assert not tmpdir.listdir()
|
||||
|
||||
|
||||
@keras_test
|
||||
@pytest.mark.skipif((K.backend() != 'tensorflow'),
|
||||
reason='Requires tensorflow backend')
|
||||
def test_TensorBoard_convnet():
|
||||
np.random.seed(1337)
|
||||
def test_TensorBoard_convnet(tmpdir):
|
||||
np.random.seed(np.random.randint(1, 1e7))
|
||||
filepath = str(tmpdir / 'logs')
|
||||
|
||||
filepath = './logs'
|
||||
input_shape = (16, 16, 3)
|
||||
(x_train, y_train), (x_test, y_test) = get_test_data(num_train=500,
|
||||
num_test=200,
|
||||
@@ -421,8 +424,9 @@ def test_TensorBoard_convnet():
|
||||
validation_data=(x_test, y_test),
|
||||
callbacks=cbks,
|
||||
verbose=0)
|
||||
assert os.path.exists(filepath)
|
||||
assert os.path.isdir(filepath)
|
||||
shutil.rmtree(filepath)
|
||||
assert not tmpdir.listdir()
|
||||
|
||||
|
||||
@keras_test
|
||||
@@ -510,9 +514,11 @@ def test_LambdaCallback():
|
||||
@keras_test
|
||||
@pytest.mark.skipif((K.backend() != 'tensorflow'),
|
||||
reason="Requires tensorflow backend")
|
||||
def test_TensorBoard_with_ReduceLROnPlateau():
|
||||
def test_TensorBoard_with_ReduceLROnPlateau(tmpdir):
|
||||
import shutil
|
||||
filepath = './logs'
|
||||
np.random.seed(np.random.randint(1, 1e7))
|
||||
filepath = str(tmpdir / 'logs')
|
||||
|
||||
(X_train, y_train), (X_test, y_test) = get_test_data(num_train=train_samples,
|
||||
num_test=test_samples,
|
||||
input_shape=(input_dim,),
|
||||
@@ -540,8 +546,9 @@ def test_TensorBoard_with_ReduceLROnPlateau():
|
||||
model.fit(X_train, y_train, batch_size=batch_size,
|
||||
validation_data=(X_test, y_test), callbacks=cbks, epochs=2)
|
||||
|
||||
assert os.path.exists(filepath)
|
||||
assert os.path.isdir(filepath)
|
||||
shutil.rmtree(filepath)
|
||||
assert not tmpdir.listdir()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -21,6 +21,17 @@ batch_size = 32
|
||||
epochs = 1
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def in_tmpdir(tmpdir):
|
||||
"""Runs a function in a temporary directory.
|
||||
|
||||
Checks that the directory is empty afterwards.
|
||||
"""
|
||||
with tmpdir.as_cwd():
|
||||
yield None
|
||||
assert not tmpdir.listdir()
|
||||
|
||||
|
||||
@keras_test
|
||||
def test_sequential_pop():
|
||||
model = Sequential()
|
||||
@@ -87,12 +98,12 @@ def test_sequential_fit_generator():
|
||||
model.fit_generator(data_generator(True), 5, epochs,
|
||||
validation_data=data_generator(False),
|
||||
validation_steps=3)
|
||||
model.fit_generator(data_generator(True), 5, epochs, max_q_size=2)
|
||||
model.fit_generator(data_generator(True), 5, epochs, max_queue_size=2)
|
||||
model.evaluate(x_train, y_train)
|
||||
|
||||
|
||||
@keras_test
|
||||
def test_sequential():
|
||||
def test_sequential(in_tmpdir):
|
||||
(x_train, y_train), (x_test, y_test) = _get_test_data()
|
||||
|
||||
# TODO: factor out
|
||||
@@ -122,8 +133,8 @@ def test_sequential():
|
||||
|
||||
loss = model.evaluate(x_test, y_test)
|
||||
|
||||
prediction = model.predict_generator(data_generator(x_test, y_test), 1, max_q_size=2, verbose=1)
|
||||
gen_loss = model.evaluate_generator(data_generator(x_test, y_test, 50), 1, max_q_size=2)
|
||||
prediction = model.predict_generator(data_generator(x_test, y_test), 1, max_queue_size=2, verbose=1)
|
||||
gen_loss = model.evaluate_generator(data_generator(x_test, y_test, 50), 1, max_queue_size=2)
|
||||
pred_loss = K.eval(K.mean(losses.get(model.loss)(K.variable(y_test), K.variable(prediction))))
|
||||
|
||||
assert(np.isclose(pred_loss, loss))
|
||||
@@ -160,7 +171,7 @@ def test_sequential():
|
||||
|
||||
|
||||
@keras_test
|
||||
def test_nested_sequential():
|
||||
def test_nested_sequential(in_tmpdir):
|
||||
(x_train, y_train), (x_test, y_test) = _get_test_data()
|
||||
|
||||
inner = Sequential()
|
||||
|
||||
@@ -1,17 +1,41 @@
|
||||
"""Tests for functions in data_utils.py.
|
||||
"""
|
||||
import os
|
||||
import pytest
|
||||
import sys
|
||||
import tarfile
|
||||
import threading
|
||||
import zipfile
|
||||
from six.moves.urllib.request import pathname2url
|
||||
from itertools import cycle
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
from six.moves.urllib.parse import urljoin
|
||||
from six.moves.urllib.request import pathname2url
|
||||
|
||||
from keras.utils import Sequence
|
||||
from keras.utils import GeneratorEnqueuer
|
||||
from keras.utils import OrderedEnqueuer
|
||||
from keras.utils.data_utils import _hash_file
|
||||
from keras.utils.data_utils import get_file
|
||||
from keras.utils.data_utils import validate_file
|
||||
from keras.utils.data_utils import _hash_file
|
||||
|
||||
if sys.version_info < (3,):
|
||||
def next(x):
|
||||
return x.next()
|
||||
|
||||
|
||||
def test_data_utils():
|
||||
@pytest.fixture
|
||||
def in_tmpdir(tmpdir):
|
||||
"""Runs a function in a temporary directory.
|
||||
|
||||
Checks that the directory is empty afterwards.
|
||||
"""
|
||||
with tmpdir.as_cwd():
|
||||
yield None
|
||||
assert not tmpdir.listdir()
|
||||
|
||||
|
||||
def test_data_utils(in_tmpdir):
|
||||
"""Tests get_file from a url, plus extraction and validation.
|
||||
"""
|
||||
dirname = 'data_utils'
|
||||
@@ -53,5 +77,154 @@ def test_data_utils():
|
||||
os.remove('test.txt')
|
||||
os.remove('test.zip')
|
||||
|
||||
|
||||
"""Enqueuers Tests"""
|
||||
|
||||
|
||||
class threadsafe_iter:
|
||||
"""Takes an iterator/generator and makes it thread-safe by
|
||||
serializing call to the `next` method of given iterator/generator.
|
||||
"""
|
||||
|
||||
def __init__(self, it):
|
||||
self.it = it
|
||||
self.lock = threading.Lock()
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
return self.next()
|
||||
|
||||
def next(self):
|
||||
with self.lock:
|
||||
return next(self.it)
|
||||
|
||||
|
||||
def threadsafe_generator(f):
|
||||
"""A decorator that takes a generator function and makes it thread-safe.
|
||||
"""
|
||||
|
||||
def g(*a, **kw):
|
||||
return threadsafe_iter(f(*a, **kw))
|
||||
|
||||
return g
|
||||
|
||||
|
||||
class TestSequence(Sequence):
|
||||
def __init__(self, shape):
|
||||
self.shape = shape
|
||||
|
||||
def __getitem__(self, item):
|
||||
return np.ones(self.shape, dtype=np.uint8) * item
|
||||
|
||||
def __len__(self):
|
||||
return 100
|
||||
|
||||
|
||||
class FaultSequence(Sequence):
|
||||
def __getitem__(self, item):
|
||||
raise IndexError(item, 'is not present')
|
||||
|
||||
def __len__(self):
|
||||
return 100
|
||||
|
||||
|
||||
@threadsafe_generator
|
||||
def create_generator_from_sequence_threads(ds):
|
||||
for i in cycle(range(len(ds))):
|
||||
yield ds[i]
|
||||
|
||||
|
||||
def create_generator_from_sequence_pcs(ds):
|
||||
for i in cycle(range(len(ds))):
|
||||
yield ds[i]
|
||||
|
||||
|
||||
def test_generator_enqueuer_threads():
|
||||
enqueuer = GeneratorEnqueuer(create_generator_from_sequence_threads(
|
||||
TestSequence([3, 200, 200, 3])), use_multiprocessing=False)
|
||||
enqueuer.start(3, 10)
|
||||
gen_output = enqueuer.get()
|
||||
acc = []
|
||||
for i in range(100):
|
||||
acc.append(int(next(gen_output)[0, 0, 0, 0]))
|
||||
|
||||
"""
|
||||
Not comparing the order since it is not guarantee.
|
||||
It may get ordered, but not a lot, one thread can take the GIL before he was supposed to.
|
||||
"""
|
||||
assert len(set(acc) - set(range(100))) == 0, "Output is not the same"
|
||||
enqueuer.stop()
|
||||
|
||||
|
||||
def test_generator_enqueuer_processes():
|
||||
enqueuer = GeneratorEnqueuer(create_generator_from_sequence_pcs(
|
||||
TestSequence([3, 200, 200, 3])), use_multiprocessing=True)
|
||||
enqueuer.start(3, 10)
|
||||
gen_output = enqueuer.get()
|
||||
acc = []
|
||||
for i in range(100):
|
||||
acc.append(int(next(gen_output)[0, 0, 0, 0]))
|
||||
assert acc != list(range(100)), "Order was keep in GeneratorEnqueuer with processes"
|
||||
enqueuer.stop()
|
||||
|
||||
|
||||
def test_generator_enqueuer_fail_threads():
|
||||
enqueuer = GeneratorEnqueuer(create_generator_from_sequence_threads(
|
||||
FaultSequence()), use_multiprocessing=False)
|
||||
enqueuer.start(3, 10)
|
||||
gen_output = enqueuer.get()
|
||||
with pytest.raises(StopIteration):
|
||||
next(gen_output)
|
||||
|
||||
|
||||
def test_generator_enqueuer_fail_processes():
|
||||
enqueuer = GeneratorEnqueuer(create_generator_from_sequence_pcs(
|
||||
FaultSequence()), use_multiprocessing=True)
|
||||
enqueuer.start(3, 10)
|
||||
gen_output = enqueuer.get()
|
||||
with pytest.raises(StopIteration):
|
||||
next(gen_output)
|
||||
|
||||
|
||||
def test_ordered_enqueuer_threads():
|
||||
enqueuer = OrderedEnqueuer(TestSequence([3, 200, 200, 3]), use_multiprocessing=False)
|
||||
enqueuer.start(3, 10)
|
||||
gen_output = enqueuer.get()
|
||||
acc = []
|
||||
for i in range(100):
|
||||
acc.append(next(gen_output)[0, 0, 0, 0])
|
||||
assert acc == list(range(100)), "Order was not keep in GeneratorEnqueuer with threads"
|
||||
enqueuer.stop()
|
||||
|
||||
|
||||
def test_ordered_enqueuer_processes():
|
||||
enqueuer = OrderedEnqueuer(TestSequence([3, 200, 200, 3]), use_multiprocessing=True)
|
||||
enqueuer.start(3, 10)
|
||||
gen_output = enqueuer.get()
|
||||
acc = []
|
||||
for i in range(100):
|
||||
acc.append(next(gen_output)[0, 0, 0, 0])
|
||||
assert acc == list(range(100)), "Order was not keep in GeneratorEnqueuer with processes"
|
||||
enqueuer.stop()
|
||||
|
||||
|
||||
def test_ordered_enqueuer_fail_threads():
|
||||
enqueuer = OrderedEnqueuer(FaultSequence(), use_multiprocessing=False)
|
||||
enqueuer.start(3, 10)
|
||||
gen_output = enqueuer.get()
|
||||
with pytest.raises(StopIteration):
|
||||
next(gen_output)
|
||||
|
||||
|
||||
def test_ordered_enqueuer_fail_processes():
|
||||
enqueuer = OrderedEnqueuer(FaultSequence(), use_multiprocessing=True)
|
||||
enqueuer.start(3, 10)
|
||||
gen_output = enqueuer.get()
|
||||
with pytest.raises(StopIteration):
|
||||
next(gen_output)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__])
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import sys
|
||||
import pytest
|
||||
from keras.utils.generic_utils import custom_object_scope
|
||||
from keras.utils.generic_utils import custom_object_scope, has_arg
|
||||
from keras import activations
|
||||
from keras import regularizers
|
||||
|
||||
@@ -20,5 +21,46 @@ def test_custom_objects_scope():
|
||||
assert cl.__class__ == CustomClass
|
||||
|
||||
|
||||
@pytest.mark.parametrize('fn, name, accept_all, expected', [
|
||||
('f(x)', 'x', False, True),
|
||||
('f(x)', 'y', False, False),
|
||||
('f(x)', 'y', True, False),
|
||||
('f(x, y)', 'y', False, True),
|
||||
('f(x, y=1)', 'y', False, True),
|
||||
('f(x, **kwargs)', 'x', False, True),
|
||||
('f(x, **kwargs)', 'y', False, False),
|
||||
('f(x, **kwargs)', 'y', True, True),
|
||||
('f(x, y=1, **kwargs)', 'y', False, True),
|
||||
# Keyword-only arguments (Python 3 only)
|
||||
('f(x, *args, y=1)', 'y', False, True),
|
||||
('f(x, *args, y=1)', 'z', True, False),
|
||||
('f(x, *, y=1)', 'x', False, True),
|
||||
('f(x, *, y=1)', 'y', False, True),
|
||||
# lambda
|
||||
(lambda x: x, 'x', False, True),
|
||||
(lambda x: x, 'y', False, False),
|
||||
(lambda x: x, 'y', True, False),
|
||||
])
|
||||
def test_has_arg(fn, name, accept_all, expected):
|
||||
if isinstance(fn, str):
|
||||
context = dict()
|
||||
try:
|
||||
exec('def {}: pass'.format(fn), context)
|
||||
except SyntaxError:
|
||||
if sys.version_info >= (3,):
|
||||
raise
|
||||
pytest.skip('Function is not compatible with Python 2')
|
||||
context.pop('__builtins__', None) # Sometimes exec adds builtins to the context
|
||||
fn, = context.values()
|
||||
|
||||
assert has_arg(fn, name, accept_all) is expected
|
||||
|
||||
|
||||
@pytest.mark.xfail(sys.version_info < (3, 3),
|
||||
reason='inspect API does not reveal positional-only arguments')
|
||||
def test_has_arg_positional_only():
|
||||
assert has_arg(pow, 'x') is False
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__])
|
||||
|
||||
@@ -10,6 +10,17 @@ import warnings
|
||||
import h5py
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def in_tmpdir(tmpdir):
|
||||
"""Runs a function in a temporary directory.
|
||||
|
||||
Checks that the directory is empty afterwards.
|
||||
"""
|
||||
with tmpdir.as_cwd():
|
||||
yield None
|
||||
assert not tmpdir.listdir()
|
||||
|
||||
|
||||
def create_dataset(h5_path='test.h5'):
|
||||
X = np.random.randn(200, 10).astype('float32')
|
||||
y = np.random.randint(0, 2, size=(200, 1))
|
||||
@@ -23,7 +34,7 @@ def create_dataset(h5_path='test.h5'):
|
||||
f.close()
|
||||
|
||||
|
||||
def test_io_utils():
|
||||
def test_io_utils(in_tmpdir):
|
||||
'''Tests the HDF5Matrix code using the sample from @jfsantos at
|
||||
https://gist.github.com/jfsantos/e2ef822c744357a4ed16ec0c885100a3
|
||||
'''
|
||||
@@ -42,6 +53,10 @@ def test_io_utils():
|
||||
assert y_train.shape == (150, 1), 'HDF5Matrix shape should match input array'
|
||||
# But they do not support negative indices, so don't try print(X_train[-1])
|
||||
|
||||
assert y_train.dtype == np.dtype('i'), 'HDF5Matrix dtype should match input array'
|
||||
assert y_train.ndim == 2, 'HDF5Matrix ndim should match input array'
|
||||
assert y_train.size == 150, 'HDF5Matrix ndim should match input array'
|
||||
|
||||
model = Sequential()
|
||||
model.add(Dense(64, input_shape=(10,), activation='relu'))
|
||||
model.add(Dense(1, activation='sigmoid'))
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import pytest
|
||||
import numpy as np
|
||||
from numpy.testing import assert_allclose
|
||||
from keras import backend as K
|
||||
from keras.layers import Conv2D
|
||||
from keras.layers import Dense
|
||||
from keras.layers import Flatten
|
||||
from keras.models import Sequential
|
||||
from keras.utils import layer_utils
|
||||
from keras.utils.test_utils import keras_test
|
||||
|
||||
|
||||
@keras_test
|
||||
def test_convert_weights():
|
||||
def get_model(shape, data_format):
|
||||
model = Sequential()
|
||||
model.add(Conv2D(filters=2,
|
||||
kernel_size=(4, 3),
|
||||
input_shape=shape,
|
||||
data_format=data_format))
|
||||
model.add(Flatten())
|
||||
model.add(Dense(5))
|
||||
return model
|
||||
|
||||
for data_format in ['channels_first', 'channels_last']:
|
||||
if data_format == 'channels_first':
|
||||
shape = (3, 5, 5)
|
||||
target_shape = (5, 5, 3)
|
||||
prev_shape = (2, 3, 2)
|
||||
flip = lambda x: np.flip(np.flip(x, axis=2), axis=3)
|
||||
transpose = lambda x: np.transpose(x, (0, 2, 3, 1))
|
||||
target_data_format = 'channels_last'
|
||||
elif data_format == 'channels_last':
|
||||
shape = (5, 5, 3)
|
||||
target_shape = (3, 5, 5)
|
||||
prev_shape = (2, 2, 3)
|
||||
flip = lambda x: np.flip(np.flip(x, axis=1), axis=2)
|
||||
transpose = lambda x: np.transpose(x, (0, 3, 1, 2))
|
||||
target_data_format = 'channels_first'
|
||||
|
||||
model1 = get_model(shape, data_format)
|
||||
model2 = get_model(target_shape, target_data_format)
|
||||
conv = K.function([model1.input], [model1.layers[0].output])
|
||||
|
||||
x = np.random.random((1,) + shape)
|
||||
|
||||
# Test equivalence of convert_all_kernels_in_model
|
||||
convout1 = conv([x])[0]
|
||||
layer_utils.convert_all_kernels_in_model(model1)
|
||||
convout2 = flip(conv([flip(x)])[0])
|
||||
|
||||
assert_allclose(convout1, convout2, atol=1e-5)
|
||||
|
||||
# Test equivalence of convert_dense_weights_data_format
|
||||
out1 = model1.predict(x)
|
||||
layer_utils.convert_dense_weights_data_format(model1.layers[2], prev_shape, target_data_format)
|
||||
for (src, dst) in zip(model1.layers, model2.layers):
|
||||
dst.set_weights(src.get_weights())
|
||||
out2 = model2.predict(transpose(x))
|
||||
|
||||
assert_allclose(out1, out2, atol=1e-5)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__])
|
||||
@@ -78,11 +78,11 @@ def test_sequential_model_saving_2():
|
||||
|
||||
@keras_test
|
||||
def test_functional_model_saving():
|
||||
input = Input(shape=(3,))
|
||||
x = Dense(2)(input)
|
||||
output = Dense(3)(x)
|
||||
inputs = Input(shape=(3,))
|
||||
x = Dense(2)(inputs)
|
||||
outputs = Dense(3)(x)
|
||||
|
||||
model = Model(input, output)
|
||||
model = Model(inputs, outputs)
|
||||
model.compile(loss=losses.MSE,
|
||||
optimizer=optimizers.RMSprop(lr=0.0001),
|
||||
metrics=[metrics.categorical_accuracy])
|
||||
@@ -103,12 +103,12 @@ def test_functional_model_saving():
|
||||
|
||||
@keras_test
|
||||
def test_saving_multiple_metrics_outputs():
|
||||
input = Input(shape=(5,))
|
||||
x = Dense(5)(input)
|
||||
inputs = Input(shape=(5,))
|
||||
x = Dense(5)(inputs)
|
||||
output1 = Dense(1, name='output1')(x)
|
||||
output2 = Dense(1, name='output2')(x)
|
||||
|
||||
model = Model(inputs=input, outputs=[output1, output2])
|
||||
model = Model(inputs=inputs, outputs=[output1, output2])
|
||||
|
||||
metrics = {'output1': ['mse', 'binary_accuracy'],
|
||||
'output2': ['mse', 'binary_accuracy']
|
||||
@@ -270,11 +270,11 @@ def square_fn(x):
|
||||
|
||||
@keras_test
|
||||
def test_saving_lambda_custom_objects():
|
||||
input = Input(shape=(3,))
|
||||
x = Lambda(lambda x: square_fn(x), output_shape=(3,))(input)
|
||||
output = Dense(3)(x)
|
||||
inputs = Input(shape=(3,))
|
||||
x = Lambda(lambda x: square_fn(x), output_shape=(3,))(inputs)
|
||||
outputs = Dense(3)(x)
|
||||
|
||||
model = Model(input, output)
|
||||
model = Model(inputs, outputs)
|
||||
model.compile(loss=losses.MSE,
|
||||
optimizer=optimizers.RMSprop(lr=0.0001),
|
||||
metrics=[metrics.categorical_accuracy])
|
||||
@@ -293,6 +293,26 @@ def test_saving_lambda_custom_objects():
|
||||
assert_allclose(out, out2, atol=1e-05)
|
||||
|
||||
|
||||
@keras_test
|
||||
def test_saving_lambda_numpy_array_arguments():
|
||||
mean = np.random.random((4, 2, 3))
|
||||
std = np.abs(np.random.random((4, 2, 3))) + 1e-5
|
||||
inputs = Input(shape=(4, 2, 3))
|
||||
outputs = Lambda(lambda image, mu, std: (image - mu) / std,
|
||||
arguments={'mu': mean, 'std': std})(inputs)
|
||||
model = Model(inputs, outputs)
|
||||
model.compile(loss='mse', optimizer='sgd', metrics=['acc'])
|
||||
|
||||
_, fname = tempfile.mkstemp('.h5')
|
||||
save_model(model, fname)
|
||||
|
||||
model = load_model(fname)
|
||||
os.remove(fname)
|
||||
|
||||
assert_allclose(mean, model.layers[1].arguments['mu'])
|
||||
assert_allclose(std, model.layers[1].arguments['std'])
|
||||
|
||||
|
||||
@keras_test
|
||||
def test_saving_custom_activation_function():
|
||||
x = Input(shape=(3,))
|
||||
@@ -316,5 +336,6 @@ def test_saving_custom_activation_function():
|
||||
out2 = model.predict(x)
|
||||
assert_allclose(out, out2, atol=1e-05)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__])
|
||||
|
||||
Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais
Referência em uma Nova Issue
Bloquear um usuário