Comparar commits
62 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 |
@@ -19,3 +19,4 @@ examples/img/*
|
||||
|
||||
# developer environments
|
||||
.idea
|
||||
.vscode
|
||||
|
||||
+3
-3
@@ -14,9 +14,9 @@ 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
|
||||
@@ -86,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
|
||||
|
||||
+10
-3
@@ -46,13 +46,20 @@ You can also use Github issues to request features you would like to see in Kera
|
||||
**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.
|
||||
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
|
||||
## 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), [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.*
|
||||
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:
|
||||
|
||||
|
||||
+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
+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
|
||||
|
||||
+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
|
||||
|
||||
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -77,7 +77,7 @@ 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
|
||||
|
||||
+123
-36
@@ -1,7 +1,7 @@
|
||||
from __future__ import print_function
|
||||
import cntk as C
|
||||
import numpy as np
|
||||
from .common import _FLOATX, _EPSILON, image_dim_ordering, image_data_format
|
||||
from .common import floatx, epsilon, image_dim_ordering, image_data_format
|
||||
from collections import defaultdict
|
||||
from contextlib import contextmanager
|
||||
import warnings
|
||||
@@ -130,7 +130,10 @@ def _convert_dtype_string(dtype):
|
||||
'float64.' % dtype)
|
||||
|
||||
|
||||
def variable(value, dtype=_FLOATX, name=None):
|
||||
def variable(value, dtype=None, name=None):
|
||||
if dtype is None:
|
||||
dtype = floatx()
|
||||
|
||||
if name is None:
|
||||
name = ''
|
||||
|
||||
@@ -227,10 +230,12 @@ def eval(x):
|
||||
def placeholder(
|
||||
shape=None,
|
||||
ndim=None,
|
||||
dtype=_FLOATX,
|
||||
dtype=None,
|
||||
sparse=False,
|
||||
name=None,
|
||||
dynamic_axis_num=1):
|
||||
if dtype is None:
|
||||
dtype = floatx()
|
||||
if not shape:
|
||||
if ndim:
|
||||
shape = tuple([None for _ in range(ndim)])
|
||||
@@ -304,7 +309,7 @@ def _prepare_name(name, default):
|
||||
|
||||
def constant(value, dtype=None, shape=None, name=None):
|
||||
if dtype is None:
|
||||
dtype = _FLOATX
|
||||
dtype = floatx()
|
||||
if shape is None:
|
||||
shape = ()
|
||||
np_value = value * np.ones(shape)
|
||||
@@ -351,8 +356,10 @@ def random_uniform(shape, minval=0.0, maxval=1.0, dtype=None, seed=None):
|
||||
return random_uniform_variable(shape, minval, maxval, dtype, seed)
|
||||
|
||||
|
||||
def random_uniform_variable(shape, low, high, dtype=_FLOATX,
|
||||
name=None, seed=None):
|
||||
def random_uniform_variable(shape, low, high,
|
||||
dtype=None, name=None, seed=None):
|
||||
if dtype is None:
|
||||
dtype = floatx()
|
||||
if seed is None:
|
||||
# ensure that randomness is conditioned by the Numpy RNG
|
||||
seed = np.random.randint(10e3)
|
||||
@@ -380,9 +387,11 @@ def random_normal_variable(
|
||||
shape,
|
||||
mean,
|
||||
scale,
|
||||
dtype=_FLOATX,
|
||||
dtype=None,
|
||||
name=None,
|
||||
seed=None):
|
||||
if dtype is None:
|
||||
dtype = floatx()
|
||||
if seed is None:
|
||||
# ensure that randomness is conditioned by the Numpy RNG
|
||||
seed = np.random.randint(10e7)
|
||||
@@ -403,7 +412,9 @@ def random_normal_variable(
|
||||
name=name)
|
||||
|
||||
|
||||
def random_normal(shape, mean=0.0, stddev=1.0, dtype=_FLOATX, seed=None):
|
||||
def random_normal(shape, mean=0.0, stddev=1.0, dtype=None, seed=None):
|
||||
if dtype is None:
|
||||
dtype = floatx()
|
||||
for _ in shape:
|
||||
if _ is None:
|
||||
raise ValueError('CNTK Backend: randomness op with '
|
||||
@@ -435,17 +446,23 @@ def dtype(x):
|
||||
return _convert_dtype_string(x.dtype)
|
||||
|
||||
|
||||
def zeros(shape, dtype=_FLOATX, name=None):
|
||||
def zeros(shape, dtype=None, name=None):
|
||||
if dtype is None:
|
||||
dtype = floatx()
|
||||
ctype = _convert_string_dtype(dtype)
|
||||
return variable(value=np.zeros(shape, ctype), dtype=dtype, name=name)
|
||||
|
||||
|
||||
def ones(shape, dtype=_FLOATX, name=None):
|
||||
def ones(shape, dtype=None, name=None):
|
||||
if dtype is None:
|
||||
dtype = floatx()
|
||||
ctype = _convert_string_dtype(dtype)
|
||||
return variable(value=np.ones(shape, ctype), dtype=dtype, name=name)
|
||||
|
||||
|
||||
def eye(size, dtype=_FLOATX, name=None):
|
||||
def eye(size, dtype=None, name=None):
|
||||
if dtype is None:
|
||||
dtype = floatx()
|
||||
return variable(np.eye(size), dtype, name)
|
||||
|
||||
|
||||
@@ -652,16 +669,16 @@ def _normalize_axis(axis, x):
|
||||
|
||||
nones = _get_dynamic_axis_num(x)
|
||||
|
||||
if type(axis) is tuple:
|
||||
if isinstance(axis, tuple):
|
||||
_axis = list(axis)
|
||||
elif type(axis) is int:
|
||||
elif isinstance(axis, int):
|
||||
_axis = [axis]
|
||||
elif type(axis) is list:
|
||||
elif isinstance(axis, list):
|
||||
_axis = list(axis)
|
||||
else:
|
||||
_axis = axis
|
||||
|
||||
if type(_axis) is list:
|
||||
if isinstance(_axis, list):
|
||||
for i, a in enumerate(_axis):
|
||||
if a is not None and a < 0:
|
||||
_axis[i] = (a % ndim)
|
||||
@@ -729,7 +746,7 @@ def all(x, axis=None, keepdims=False):
|
||||
return all_matrix
|
||||
|
||||
|
||||
def classification_error(output, target, axis=-1):
|
||||
def classification_error(target, output, axis=-1):
|
||||
return C.ops.reduce_mean(
|
||||
C.equal(
|
||||
argmax(
|
||||
@@ -801,10 +818,10 @@ def clip(x, min_value, max_value):
|
||||
return C.clip(x, min_value, max_value)
|
||||
|
||||
|
||||
def binary_crossentropy(output, target, from_logits=False):
|
||||
def binary_crossentropy(target, output, from_logits=False):
|
||||
if from_logits:
|
||||
output = C.sigmoid(output)
|
||||
output = C.clip(output, _EPSILON, 1.0 - _EPSILON)
|
||||
output = C.clip(output, epsilon(), 1.0 - epsilon())
|
||||
output = -target * C.log(output) - (1.0 - target) * C.log(1.0 - output)
|
||||
return output
|
||||
|
||||
@@ -1352,6 +1369,16 @@ def conv2d(x, kernel, strides=(1, 1), padding='valid',
|
||||
return _postprocess_conv2d_output(x, data_format)
|
||||
|
||||
|
||||
def separable_conv2d(x, depthwise_kernel, pointwise_kernel, strides=(1, 1),
|
||||
padding='valid', data_format=None, dilation_rate=(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)):
|
||||
if data_format is None:
|
||||
@@ -1376,6 +1403,41 @@ 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):
|
||||
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_conv3d_input(x, data_format)
|
||||
kernel = _preprocess_conv3d_kernel(kernel, data_format)
|
||||
padding = _preprocess_border_mode(padding)
|
||||
strides = (1,) + strides
|
||||
# cntk output_shape does not include batch axis
|
||||
output_shape = output_shape[1:]
|
||||
# in keras2, need handle output shape in different format
|
||||
if data_format == 'channels_last':
|
||||
shape = list(output_shape)
|
||||
shape[0] = output_shape[3]
|
||||
shape[1] = output_shape[0]
|
||||
shape[2] = output_shape[1]
|
||||
shape[3] = output_shape[2]
|
||||
output_shape = tuple(shape)
|
||||
|
||||
x = C.convolution_transpose(
|
||||
kernel,
|
||||
x,
|
||||
strides,
|
||||
auto_padding=[
|
||||
False,
|
||||
padding,
|
||||
padding,
|
||||
padding],
|
||||
output_shape=output_shape)
|
||||
return _postprocess_conv3d_output(x, data_format)
|
||||
|
||||
|
||||
def pool2d(x, pool_size, strides=(1, 1),
|
||||
padding='valid', data_format=None,
|
||||
pool_mode='max'):
|
||||
@@ -1477,7 +1539,7 @@ def softsign(x):
|
||||
return x / (1 + C.abs(x))
|
||||
|
||||
|
||||
def categorical_crossentropy(output, target, from_logits=False):
|
||||
def categorical_crossentropy(target, output, from_logits=False):
|
||||
if from_logits:
|
||||
result = C.cross_entropy_with_softmax(output, target)
|
||||
# cntk's result shape is (batch, 1), while keras expect (batch, )
|
||||
@@ -1485,18 +1547,19 @@ def categorical_crossentropy(output, target, from_logits=False):
|
||||
else:
|
||||
# scale preds so that the class probas of each sample sum to 1
|
||||
output /= C.reduce_sum(output, axis=-1)
|
||||
# avoid numerical instability with _EPSILON clipping
|
||||
output = C.clip(output, _EPSILON, 1.0 - _EPSILON)
|
||||
# avoid numerical instability with epsilon clipping
|
||||
output = C.clip(output, epsilon(), 1.0 - epsilon())
|
||||
return -sum(target * C.log(output), axis=-1)
|
||||
|
||||
|
||||
def sparse_categorical_crossentropy(output, target, from_logits=False):
|
||||
def sparse_categorical_crossentropy(target, output, from_logits=False):
|
||||
target = C.one_hot(target, output.shape[-1])
|
||||
target = C.reshape(target, output.shape)
|
||||
return categorical_crossentropy(output, target, from_logits)
|
||||
|
||||
|
||||
class Function(object):
|
||||
|
||||
def __init__(self, inputs, outputs, updates=[], **kwargs):
|
||||
self.placeholders = inputs
|
||||
self.trainer = None
|
||||
@@ -1533,11 +1596,12 @@ class Function(object):
|
||||
p_list.append(grad_parameter_dict[g])
|
||||
u_list.append(g)
|
||||
else:
|
||||
raise ValueError('CNTK backend: when constructing trainer, '
|
||||
'found gradient node `%s` which is not '
|
||||
'related to any parameters in the model. '
|
||||
'Please double check how the gradient node '
|
||||
'is constructed.' % g)
|
||||
raise ValueError(
|
||||
'CNTK backend: when constructing trainer, '
|
||||
'found gradient node `%s` which is not '
|
||||
'related to any parameters in the model. '
|
||||
'Please double check how the gradient node '
|
||||
'is constructed.' % g)
|
||||
|
||||
if len(u_list) > 0:
|
||||
learner = C.cntk_py.universal_learner(p_list, u_list, update_func)
|
||||
@@ -1567,6 +1631,17 @@ class Function(object):
|
||||
else:
|
||||
self.metrics_func = None
|
||||
|
||||
@staticmethod
|
||||
def _is_input_shape_compatible(input, placeholder):
|
||||
if hasattr(input, 'shape') and hasattr(placeholder, 'shape'):
|
||||
num_dynamic = get_num_dynamic_axis(placeholder)
|
||||
input_shape = input.shape[num_dynamic:]
|
||||
placeholder_shape = placeholder.shape
|
||||
for i, p in zip(input_shape, placeholder_shape):
|
||||
if i != p and p != C.InferredDimension:
|
||||
return False
|
||||
return True
|
||||
|
||||
def __call__(self, inputs):
|
||||
assert type(inputs) in {list, tuple}
|
||||
feed_dict = {}
|
||||
@@ -1576,6 +1651,15 @@ class Function(object):
|
||||
value.dtype != np.float32 and
|
||||
value.dtype != np.float64):
|
||||
value = value.astype(np.float32)
|
||||
# in current version cntk can't support input with variable
|
||||
# length. Will support it in next release.
|
||||
if not self._is_input_shape_compatible(value, tensor):
|
||||
raise ValueError(
|
||||
'CNTK backend: The placeholder has been resolved '
|
||||
'to shape `%s`, but input shape is `%s`. Currently '
|
||||
'CNTK can not take variable length inputs. Please '
|
||||
'pass inputs that have a static shape.'
|
||||
% (tensor.shape, value.shape))
|
||||
feed_dict[tensor] = value
|
||||
|
||||
updated = []
|
||||
@@ -1585,9 +1669,10 @@ class Function(object):
|
||||
if argument in feed_dict:
|
||||
input_dict[argument] = feed_dict[argument]
|
||||
else:
|
||||
raise ValueError('CNTK backend: argument %s is not found in inputs. '
|
||||
'Please double check the model and inputs in '
|
||||
'`train_function`.' % argument.name)
|
||||
raise ValueError(
|
||||
'CNTK backend: argument %s is not found in inputs. '
|
||||
'Please double check the model and inputs in '
|
||||
'`train_function`.' % argument.name)
|
||||
|
||||
result = self.trainer.train_minibatch(
|
||||
input_dict, self.trainer_output)
|
||||
@@ -1603,9 +1688,10 @@ class Function(object):
|
||||
if argument in feed_dict:
|
||||
input_dict[argument] = feed_dict[argument]
|
||||
else:
|
||||
raise ValueError('CNTK backend: metrics argument %s '
|
||||
'is not found in inputs. Please double '
|
||||
'check the model and inputs.' % argument.name)
|
||||
raise ValueError(
|
||||
'CNTK backend: metrics argument %s '
|
||||
'is not found in inputs. Please double '
|
||||
'check the model and inputs.' % argument.name)
|
||||
output_values = self.metrics_func.eval(input_dict, as_numpy=False)
|
||||
if isinstance(output_values, dict):
|
||||
for o in self.metrics_outputs:
|
||||
@@ -1623,9 +1709,10 @@ class Function(object):
|
||||
if argument in feed_dict:
|
||||
input_dict[argument] = feed_dict[argument]
|
||||
else:
|
||||
raise ValueError('CNTK backend: assign ops argument %s '
|
||||
'is not found in inputs. Please double '
|
||||
'check the model and inputs.' % argument.name)
|
||||
raise ValueError(
|
||||
'CNTK backend: assign ops argument %s '
|
||||
'is not found in inputs. Please double '
|
||||
'check the model and inputs.' % argument.name)
|
||||
self.unrelated_updates.eval(input_dict, as_numpy=False)
|
||||
return updated
|
||||
|
||||
|
||||
@@ -451,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)
|
||||
```
|
||||
"""
|
||||
@@ -478,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)
|
||||
@@ -508,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
|
||||
@@ -550,7 +550,7 @@ def dtype(x):
|
||||
'float32_ref'
|
||||
```
|
||||
"""
|
||||
return x.dtype.name
|
||||
return x.dtype.base_dtype.name
|
||||
|
||||
|
||||
def eval(x):
|
||||
@@ -1089,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>
|
||||
|
||||
@@ -1153,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):
|
||||
@@ -1171,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):
|
||||
@@ -1189,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):
|
||||
@@ -1207,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):
|
||||
@@ -1255,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)
|
||||
|
||||
|
||||
@@ -1296,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):
|
||||
@@ -1312,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):
|
||||
@@ -1328,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):
|
||||
@@ -2327,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
|
||||
@@ -2722,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.
|
||||
|
||||
@@ -2741,26 +2741,26 @@ 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.
|
||||
|
||||
@@ -2787,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.
|
||||
@@ -2911,6 +2911,26 @@ 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.
|
||||
|
||||
@@ -3211,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.
|
||||
@@ -3251,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'):
|
||||
|
||||
@@ -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
|
||||
@@ -446,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'):
|
||||
@@ -930,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,)
|
||||
@@ -1138,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()
|
||||
|
||||
|
||||
@@ -1225,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),
|
||||
@@ -1311,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,
|
||||
@@ -1344,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 = []
|
||||
@@ -1353,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:
|
||||
@@ -1386,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()
|
||||
@@ -1487,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:
|
||||
@@ -1498,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
|
||||
@@ -1904,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)):
|
||||
@@ -1948,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:
|
||||
@@ -2278,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):
|
||||
@@ -2302,9 +2377,8 @@ 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(fn2, elems, initializer, name=name)[0]
|
||||
return theano.foldr(lambda x, acc: fn(acc, x),
|
||||
elems, initializer, name=name)[0]
|
||||
|
||||
|
||||
def local_conv1d(inputs, kernel, kernel_size, strides, data_format=None):
|
||||
@@ -2372,5 +2446,4 @@ def local_conv2d(inputs, kernel, kernel_size, strides, output_shape, data_format
|
||||
output = reshape(output,
|
||||
(output_row, output_col, -1, filters))
|
||||
output = permute_dimensions(output, (2, 0, 1, 3))
|
||||
|
||||
return output
|
||||
|
||||
+22
-18
@@ -523,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,
|
||||
@@ -533,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
|
||||
@@ -657,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)
|
||||
@@ -694,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),
|
||||
@@ -876,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
|
||||
@@ -975,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
|
||||
@@ -1002,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 = ...
|
||||
@@ -1017,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]`
|
||||
@@ -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]`
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -17,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.
|
||||
|
||||
@@ -18,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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1529,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 = {}
|
||||
@@ -2637,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):
|
||||
@@ -2923,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',
|
||||
|
||||
+127
-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:
|
||||
@@ -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 ' +
|
||||
@@ -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,101 +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()
|
||||
if hasattr(data_generator_task, 'lock'):
|
||||
# We should replace the threading lock of the iterator
|
||||
# with a process-safe lock.
|
||||
data_generator_task.lock = multiprocessing.Lock()
|
||||
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`.
|
||||
"""
|
||||
@@ -994,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):
|
||||
@@ -1720,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.
|
||||
|
||||
@@ -1730,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).
|
||||
@@ -1756,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
|
||||
@@ -1804,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 '
|
||||
@@ -1843,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))
|
||||
@@ -1854,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:
|
||||
@@ -1866,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))
|
||||
@@ -1885,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))
|
||||
@@ -1923,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.
|
||||
@@ -1954,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
|
||||
@@ -1963,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
|
||||
@@ -1992,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))
|
||||
@@ -2018,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))
|
||||
@@ -2030,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
|
||||
@@ -2051,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
|
||||
@@ -2086,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.
|
||||
@@ -2112,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))
|
||||
|
||||
@@ -476,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"`.
|
||||
|
||||
@@ -484,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,
|
||||
@@ -806,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.
|
||||
|
||||
@@ -1891,6 +2122,7 @@ Convolution3D = Conv3D
|
||||
SeparableConvolution2D = SeparableConv2D
|
||||
Convolution2DTranspose = Conv2DTranspose
|
||||
Deconvolution2D = Deconv2D = Conv2DTranspose
|
||||
Deconvolution3D = Deconv3D = Conv3DTranspose
|
||||
|
||||
# Legacy aliases
|
||||
AtrousConv1D = AtrousConvolution1D
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -246,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)
|
||||
|
||||
@@ -456,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)
|
||||
@@ -637,7 +637,7 @@ 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):
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -953,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)
|
||||
|
||||
@@ -155,8 +155,7 @@ class TimeDistributed(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
|
||||
uses_learning_phase = False
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -601,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)
|
||||
|
||||
|
||||
|
||||
+3
-3
@@ -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):
|
||||
|
||||
+28
-35
@@ -207,32 +207,19 @@ 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]
|
||||
@@ -288,7 +275,13 @@ def load_model(filepath, custom_objects=None, compile=True):
|
||||
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)
|
||||
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
|
||||
|
||||
|
||||
@@ -1033,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.
|
||||
|
||||
@@ -1072,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
|
||||
@@ -1118,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
|
||||
@@ -1137,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
|
||||
@@ -1159,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
|
||||
@@ -1176,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
|
||||
@@ -1191,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):
|
||||
|
||||
@@ -219,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 = []
|
||||
|
||||
@@ -413,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):
|
||||
|
||||
@@ -139,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:
|
||||
@@ -421,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':
|
||||
@@ -443,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)
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -8,6 +8,9 @@ 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)
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -166,5 +169,73 @@ def test_inceptionv3_pooling():
|
||||
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__])
|
||||
|
||||
@@ -131,11 +131,11 @@ def check_cross_entropy_with_valid_probability_distribution():
|
||||
yth = KTH.variable(yval)
|
||||
yc = KC.placeholder((4, 2))
|
||||
|
||||
ztf = KTF.eval(KTF.categorical_crossentropy(xtf, ytf, from_logits=True))
|
||||
zth = KTH.eval(KTH.categorical_crossentropy(xth, yth, from_logits=True))
|
||||
ztf = KTF.eval(KTF.categorical_crossentropy(ytf, xtf, from_logits=True))
|
||||
zth = KTH.eval(KTH.categorical_crossentropy(yth, xth, from_logits=True))
|
||||
|
||||
func_cntk = KC.function([xc, yc],
|
||||
[KC.categorical_crossentropy(xc, yc, from_logits=True), ])
|
||||
[KC.categorical_crossentropy(yc, xc, from_logits=True)])
|
||||
zc = func_cntk([xval, yval])
|
||||
# Keras function return a list, take the first output
|
||||
assert len(zc) == 1
|
||||
@@ -839,7 +839,7 @@ class TestBackend(object):
|
||||
np.log(np.sum(np.exp(x_np), axis=axis, keepdims=keepdims)),
|
||||
rtol=1e-5)
|
||||
|
||||
@pytest.mark.parametrize('K', [KTH, KTF], ids=["KTH", "KTF"])
|
||||
@pytest.mark.parametrize('K', [KTF], ids=["KTF"])
|
||||
def test_logsumexp_optim(self, K):
|
||||
'''
|
||||
Check if optimization works.
|
||||
@@ -886,7 +886,7 @@ class TestBackend(object):
|
||||
check_two_tensor_operation('binary_crossentropy', (4, 2), (4, 2), BACKENDS, from_logits=True)
|
||||
# cross_entropy call require the label is a valid probability distribution,
|
||||
# otherwise it is garbage in garbage out...
|
||||
# due to the algo difference, we can't guranteen CNTK has the same result on the garbage input.
|
||||
# due to the algo difference, we can't guarantee CNTK has the same result on the garbage input.
|
||||
# so create a seperate test case for valid lable input
|
||||
check_two_tensor_operation('categorical_crossentropy', (4, 2), (4, 2), [KTH, KTF], from_logits=True)
|
||||
check_cross_entropy_with_valid_probability_distribution()
|
||||
@@ -1180,6 +1180,9 @@ class TestBackend(object):
|
||||
cntk_check_single_tensor_operation('pool3d', (5, 9, 11, 5, 3), pool_size=(2, 3, 2),
|
||||
strides=(1, 1, 1), pool_mode='avg')
|
||||
|
||||
check_single_tensor_operation('pool3d', (2, 6, 6, 6, 3), [KTH, KTF], pool_size=(3, 3, 3),
|
||||
strides=(1, 1, 1), padding='same', pool_mode='avg')
|
||||
|
||||
def test_random_normal(self):
|
||||
mean = 0.
|
||||
std = 1.
|
||||
@@ -1387,12 +1390,6 @@ class TestBackend(object):
|
||||
ztf = KTF.eval(ztf)
|
||||
assert zth.shape == ztf.shape
|
||||
|
||||
# TODO remove this if statement when Theano without
|
||||
# T.nnet.bn.batch_normalization_train is deprecated
|
||||
zth2, _, _ = KTH._old_normalize_batch_in_training(xth, None, None,
|
||||
reduction_axes=[0, 1, 2, 3])
|
||||
assert zth.shape == tuple(KTH.eval(zth2.shape))
|
||||
|
||||
def test_ctc(self):
|
||||
# simplified version of TensorFlow's test
|
||||
|
||||
|
||||
@@ -1,17 +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')
|
||||
@@ -80,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],
|
||||
@@ -174,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]
|
||||
@@ -203,6 +280,13 @@ 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])
|
||||
@@ -291,6 +375,47 @@ def test_model_methods():
|
||||
[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
|
||||
@@ -318,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)
|
||||
|
||||
@@ -388,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)
|
||||
@@ -428,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]:
|
||||
@@ -481,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.)
|
||||
@@ -497,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.)
|
||||
@@ -545,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.)
|
||||
@@ -563,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.)
|
||||
@@ -616,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
|
||||
@@ -667,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
|
||||
@@ -729,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[:,
|
||||
@@ -757,8 +802,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 input
|
||||
assert_allclose(np_output, inputs)
|
||||
|
||||
@@ -794,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[:,
|
||||
@@ -824,8 +869,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 input
|
||||
assert_allclose(np_output, inputs)
|
||||
|
||||
|
||||
@@ -93,11 +93,12 @@ def test_TimeDistributed():
|
||||
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(0., y, atol=1e-2)
|
||||
assert_allclose(np.mean(y), 0., atol=1e-1, rtol=1e-1)
|
||||
|
||||
|
||||
@keras_test
|
||||
@@ -157,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)
|
||||
|
||||
|
||||
@@ -807,18 +807,14 @@ def test_generator_methods_interface():
|
||||
samples_per_epoch=1,
|
||||
validation_data=val_generator(),
|
||||
nb_val_samples=1,
|
||||
nb_worker=1)
|
||||
model.fit_generator(train_generator(),
|
||||
10,
|
||||
1,
|
||||
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():
|
||||
|
||||
@@ -144,6 +144,12 @@ class TestImage:
|
||||
assert(len(dir_iterator.classes) == count)
|
||||
assert(sorted(dir_iterator.filenames) == sorted(filenames))
|
||||
|
||||
# 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()
|
||||
|
||||
|
||||
@@ -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.',
|
||||
|
||||
@@ -98,7 +98,7 @@ 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)
|
||||
|
||||
|
||||
@@ -133,8 +133,8 @@ def test_sequential(in_tmpdir):
|
||||
|
||||
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))
|
||||
|
||||
@@ -1,14 +1,27 @@
|
||||
"""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()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -64,5 +77,154 @@ def test_data_utils(in_tmpdir):
|
||||
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__])
|
||||
|
||||
@@ -53,6 +53,10 @@ def test_io_utils(in_tmpdir):
|
||||
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'))
|
||||
|
||||
@@ -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])
|
||||
@@ -297,10 +297,10 @@ def test_saving_lambda_custom_objects():
|
||||
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
|
||||
input = Input(shape=(4, 2, 3))
|
||||
output = Lambda(lambda image, mu, std: (image - mu) / std,
|
||||
arguments={'mu': mean, 'std': std})(input)
|
||||
model = Model(input, output)
|
||||
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')
|
||||
|
||||
@@ -49,16 +49,16 @@ def test_multiprocessing_training():
|
||||
steps_per_epoch=5,
|
||||
epochs=1,
|
||||
verbose=1,
|
||||
max_q_size=10,
|
||||
max_queue_size=10,
|
||||
workers=4,
|
||||
pickle_safe=True)
|
||||
use_multiprocessing=True)
|
||||
|
||||
model.fit_generator(custom_generator(),
|
||||
steps_per_epoch=5,
|
||||
epochs=1,
|
||||
verbose=1,
|
||||
max_q_size=10,
|
||||
pickle_safe=False)
|
||||
max_queue_size=10,
|
||||
use_multiprocessing=False)
|
||||
|
||||
model.fit_generator(custom_generator(True),
|
||||
steps_per_epoch=5,
|
||||
@@ -131,16 +131,16 @@ def test_multiprocessing_training_fromfile(in_tmpdir):
|
||||
steps_per_epoch=5,
|
||||
epochs=1,
|
||||
verbose=1,
|
||||
max_q_size=10,
|
||||
max_queue_size=10,
|
||||
workers=2,
|
||||
pickle_safe=True)
|
||||
use_multiprocessing=True)
|
||||
|
||||
model.fit_generator(custom_generator(),
|
||||
steps_per_epoch=5,
|
||||
epochs=1,
|
||||
verbose=1,
|
||||
max_q_size=10,
|
||||
pickle_safe=False)
|
||||
max_queue_size=10,
|
||||
use_multiprocessing=False)
|
||||
|
||||
os.remove('data.npz')
|
||||
|
||||
@@ -166,13 +166,13 @@ def test_multiprocessing_predicting():
|
||||
model.compile(loss='mse', optimizer='adadelta')
|
||||
model.predict_generator(custom_generator(),
|
||||
steps=5,
|
||||
max_q_size=10,
|
||||
max_queue_size=10,
|
||||
workers=2,
|
||||
pickle_safe=True)
|
||||
use_multiprocessing=True)
|
||||
model.predict_generator(custom_generator(),
|
||||
steps=5,
|
||||
max_q_size=10,
|
||||
pickle_safe=False)
|
||||
max_queue_size=10,
|
||||
use_multiprocessing=False)
|
||||
|
||||
|
||||
@keras_test
|
||||
@@ -199,13 +199,13 @@ def test_multiprocessing_evaluating():
|
||||
|
||||
model.evaluate_generator(custom_generator(),
|
||||
steps=5,
|
||||
max_q_size=10,
|
||||
max_queue_size=10,
|
||||
workers=2,
|
||||
pickle_safe=True)
|
||||
use_multiprocessing=True)
|
||||
model.evaluate_generator(custom_generator(),
|
||||
steps=5,
|
||||
max_q_size=10,
|
||||
pickle_safe=False)
|
||||
max_queue_size=10,
|
||||
use_multiprocessing=False)
|
||||
|
||||
|
||||
@keras_test
|
||||
@@ -226,16 +226,16 @@ def test_multiprocessing_fit_error():
|
||||
|
||||
samples = batch_size * (good_batches + 1)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(StopIteration):
|
||||
model.fit_generator(
|
||||
custom_generator(), samples, 1,
|
||||
workers=4, pickle_safe=True,
|
||||
workers=4, use_multiprocessing=True,
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(StopIteration):
|
||||
model.fit_generator(
|
||||
custom_generator(), samples, 1,
|
||||
pickle_safe=False,
|
||||
use_multiprocessing=False,
|
||||
)
|
||||
|
||||
|
||||
@@ -255,45 +255,45 @@ def test_multiprocessing_evaluate_error():
|
||||
model.add(Dense(1, input_shape=(2, )))
|
||||
model.compile(loss='mse', optimizer='adadelta')
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(StopIteration):
|
||||
model.evaluate_generator(
|
||||
custom_generator(), good_batches + 1, 1,
|
||||
workers=4, pickle_safe=True,
|
||||
workers=4, use_multiprocessing=True,
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(StopIteration):
|
||||
model.evaluate_generator(
|
||||
custom_generator(), good_batches + 1, 1,
|
||||
pickle_safe=False,
|
||||
use_multiprocessing=False,
|
||||
)
|
||||
|
||||
|
||||
@keras_test
|
||||
def test_multiprocessing_predict_error():
|
||||
batch_size = 10
|
||||
good_batches = 3
|
||||
workers = 4
|
||||
|
||||
def custom_generator():
|
||||
"""Raises an exception after a few good batches"""
|
||||
for i in range(good_batches):
|
||||
yield (np.random.randint(batch_size, 256, (50, 2)),
|
||||
np.random.randint(batch_size, 2, 50))
|
||||
yield (np.random.randint(1, 256, size=(2, 5)),
|
||||
np.random.randint(1, 256, size=(2, 5)))
|
||||
raise RuntimeError
|
||||
|
||||
model = Sequential()
|
||||
model.add(Dense(1, input_shape=(2, )))
|
||||
model.add(Dense(1, input_shape=(5,)))
|
||||
model.compile(loss='mse', optimizer='adadelta')
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(StopIteration):
|
||||
model.predict_generator(
|
||||
custom_generator(), good_batches + 1, 1,
|
||||
workers=4, pickle_safe=True,
|
||||
custom_generator(), good_batches * workers + 1, 1,
|
||||
workers=workers, use_multiprocessing=True,
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(StopIteration):
|
||||
model.predict_generator(
|
||||
custom_generator(), good_batches + 1, 1,
|
||||
pickle_safe=False,
|
||||
use_multiprocessing=False,
|
||||
)
|
||||
|
||||
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário