Comparar commits
267 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| 141e05e3a7 | |||
| cadd3e4e2c | |||
| 09034d9e17 | |||
| 5706d1d688 | |||
| 41b9777746 | |||
| d31fe1ac34 | |||
| c0eedfeca0 | |||
| dc8b5509f3 | |||
| ddec052dab | |||
| 2e45022c95 | |||
| cec7f73bca | |||
| 30989dc997 | |||
| 50a0d1cad4 | |||
| 3db1f132a7 | |||
| 3b196feda5 | |||
| dd766c68d9 | |||
| 599e070824 | |||
| 04c998a742 | |||
| cc985c3a9c | |||
| 36cc508030 | |||
| 444cd56740 | |||
| 88a86f7e45 | |||
| d6f94c0bc9 | |||
| 2157aa6172 | |||
| 2013527840 | |||
| 8c73c6f218 | |||
| 63059f6063 | |||
| e179198410 | |||
| ed4a95bdad | |||
| 1baddb9094 | |||
| 3160a445a8 | |||
| d627fd8781 | |||
| b4bdc5a0fa | |||
| 6911fa2cba | |||
| 0d7c8711bd | |||
| d8b0fe0957 | |||
| 263de77a5a | |||
| d3615e682e | |||
| 35da9d6ef2 | |||
| dbe7662e72 | |||
| 30118bbab0 | |||
| 2902149f77 | |||
| eb8b40cccd | |||
| 76da13dff6 | |||
| f7cbdff79c | |||
| 81233b3cd3 | |||
| af88e051fa | |||
| 6ad6b19bd6 | |||
| 0242ca59ac | |||
| c064963ef8 | |||
| 4318718769 | |||
| f981bdb551 | |||
| 3860e078a5 | |||
| d09e2a67bb | |||
| 56ba6b9c7e | |||
| a9c6f26412 | |||
| 73817b8b77 | |||
| 3f128b9838 | |||
| 9621bb5b8e | |||
| 836fb03aa0 | |||
| c3aaf50b64 | |||
| fe00f5ff64 | |||
| 8b3543fca9 | |||
| a6fe2ae341 | |||
| 3ca7751445 | |||
| ebfde534c0 | |||
| f8c7dbb758 | |||
| 62f9053330 | |||
| c429e651c1 | |||
| dacf017d38 | |||
| dc3c1488bb | |||
| 4d7ff76cfb | |||
| 8ad6865952 | |||
| 2a4f6b942d | |||
| 91a819fb34 | |||
| 52dbeb1f26 | |||
| 0836e47dfc | |||
| 75bef59016 | |||
| 337c0c66cf | |||
| f8e2df16f1 | |||
| 10deb8f267 | |||
| efe5916109 | |||
| 64449c196e | |||
| f57128bd3d | |||
| 8740791d5c | |||
| 6ddb5a0452 | |||
| 133699c2f3 | |||
| b0ea92bc12 | |||
| e754581ecb | |||
| fcb6ae8eed | |||
| bf4dab3501 | |||
| a066cf8680 | |||
| 7448dcea65 | |||
| cf3b3dff32 | |||
| ca96737b20 | |||
| 61ade48343 | |||
| 295bfe4e3a | |||
| 1145fec39f | |||
| ecd414d716 | |||
| 80a831de1a | |||
| 37fd456a5c | |||
| 0c1af0901d | |||
| 70431a5336 | |||
| cf3ab771d3 | |||
| 524090e600 | |||
| 9c9318ff6b | |||
| f75f70a60d | |||
| e3c31aa762 | |||
| ef1e959505 | |||
| c5b8a1df80 | |||
| e73cf505a7 | |||
| 8606edf3bf | |||
| 71d46b7153 | |||
| 7f3b2067bc | |||
| ed882f4064 | |||
| 126b820561 | |||
| b50624debd | |||
| 709390dfdb | |||
| 56c492cbcc | |||
| bde45eff87 | |||
| ce7276bc55 | |||
| fc476840fa | |||
| cfcb1e8703 | |||
| 8ba647c196 | |||
| a86057d91c | |||
| 1ebeff8ee3 | |||
| be4a86f6dc | |||
| e5ccf53531 | |||
| ef93e2cffd | |||
| b8c59acd77 | |||
| 3cc242615d | |||
| 6596cc79d6 | |||
| cd28c6d07e | |||
| b883761820 | |||
| b2b04b0fff | |||
| 9e2628e811 | |||
| 537fb1cc01 | |||
| 99891c0cc8 | |||
| d748db43ae | |||
| 4ec84541e3 | |||
| ca05efc76f | |||
| 7768ae04a2 | |||
| 0daec53acb | |||
| d9ca798c60 | |||
| 990ef92a60 | |||
| becc5f3a2c | |||
| e5d0dc65e0 | |||
| 48ce23086b | |||
| a3c9d2d7c9 | |||
| 9efe17aeea | |||
| 763a2a9536 | |||
| 4a43567cea | |||
| be24159959 | |||
| 943d2d4cf8 | |||
| c4361d2246 | |||
| 80927fa958 | |||
| f3f19146f9 | |||
| 3799660504 | |||
| 9b4f973d57 | |||
| 567fdccd0b | |||
| cf755a9c7c | |||
| ff2f8ac69b | |||
| 428f4bfde6 | |||
| 7552f2c26d | |||
| 0eea5f8867 | |||
| 47c67ac19a | |||
| 55d9374961 | |||
| 045e47174f | |||
| 22c091ae3f | |||
| d20fe64a69 | |||
| c6c150b042 | |||
| ababd95210 | |||
| ff676f10f6 | |||
| 73e563ecaf | |||
| 3f905e4a35 | |||
| 98e2789db9 | |||
| 8a6cf4c13e | |||
| f4af11c730 | |||
| 9f2aa1b6ae | |||
| abca83373d | |||
| 3aa807a0c8 | |||
| 089fa11752 | |||
| 06a1545645 | |||
| 461573a8d9 | |||
| db8f43128b | |||
| 80ddb5b3b8 | |||
| 3ffba42466 | |||
| 2c49115cd3 | |||
| bbaa66c530 | |||
| 784d81d2c8 | |||
| 523e9845d7 | |||
| 47bd0af702 | |||
| 82d3489764 | |||
| 44d558ad7f | |||
| cbd11315b7 | |||
| 1588998ee8 | |||
| 58ca064f93 | |||
| 3ecf201aea | |||
| 654404c2ed | |||
| 7896ef7143 | |||
| 0c75006d12 | |||
| c7f7ffe7c4 | |||
| f10c430731 | |||
| ea5cb74414 | |||
| 606a9b6810 | |||
| 35c5fa911d | |||
| d4e9696447 | |||
| 4cff0623de | |||
| bc82613eae | |||
| 0ec57f28bc | |||
| 860e4e9177 | |||
| a2fdc32381 | |||
| d1a3842b3d | |||
| 5406bd3ad2 | |||
| 941c3f6ae8 | |||
| bec2701214 | |||
| bc60832dcf | |||
| 96483326d8 | |||
| fed7cc257e | |||
| d03f7768b8 | |||
| ce79e0a8ef | |||
| 5b3809394c | |||
| e501cd664e | |||
| 47aafaaca0 | |||
| b8a9f84fad | |||
| 0f3f56327b | |||
| a154495a2a | |||
| 25e5f7531a | |||
| ab179fab89 | |||
| 59a714abe8 | |||
| e432d10be5 | |||
| eeb576b12f | |||
| 1019e50e7f | |||
| 1a6cb71732 | |||
| 9048b5cbba | |||
| c9642571c2 | |||
| cda80c790b | |||
| 46a5b3cb36 | |||
| 5b23dd8a2f | |||
| 2b26389188 | |||
| 44e0a7bbf9 | |||
| 87cc39d99f | |||
| 55aacd1905 | |||
| 3df101cc77 | |||
| c23579e059 | |||
| 6a41ac1c36 | |||
| b78ade7e36 | |||
| 61800be9a0 | |||
| 3925eabaaf | |||
| 34296ec961 | |||
| 52f48e1f46 | |||
| 20728c95fa | |||
| 6181ca8aae | |||
| 68115cc25f | |||
| c192beaf43 | |||
| 27dd1e939c | |||
| 1e46a5d3ec | |||
| 0bf2b1b075 | |||
| ab3ef3efe5 | |||
| 4d3ee897da | |||
| 8e591d228c | |||
| 6429a57a3c | |||
| 9ad5ed8103 | |||
| 209b42c5ee | |||
| 23147de72b | |||
| 7b72163073 | |||
| 6279544dc3 |
+2
-2
@@ -49,9 +49,9 @@ install:
|
||||
|
||||
# install TensorFlow
|
||||
- if [[ "$TRAVIS_PYTHON_VERSION" == "2.7" ]]; then
|
||||
pip install https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-0.6.0-cp27-none-linux_x86_64.whl;
|
||||
pip install https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-0.7.1-cp27-none-linux_x86_64.whl;
|
||||
elif [[ "$TRAVIS_PYTHON_VERSION" == "3.4" ]]; then
|
||||
pip install https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-0.6.0-cp34-none-linux_x86_64.whl;
|
||||
pip install https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-0.7.1-cp34-none-linux_x86_64.whl;
|
||||
fi
|
||||
# command to run tests
|
||||
script:
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
Please make sure that the boxes below are checked before you submit your issue. Thank you!
|
||||
|
||||
- [ ] Check that you are up-to-date with the master branch of Keras. You can update with:
|
||||
pip install git+git://github.com/fchollet/keras.git --upgrade --no-deps
|
||||
|
||||
- [ ] If running on Theano, check that you are up-to-date with the master branch of Theano. You can update with:
|
||||
pip install git+git://github.com/Theano/Theano.git --upgrade --no-deps
|
||||
|
||||
- [ ] Provide a link to a GitHub Gist of a Python script that can reproduce your issue (or just copy the script here if it is short).
|
||||
+14
-15
@@ -1,6 +1,7 @@
|
||||
# Keras: Deep Learning library for Theano and TensorFlow
|
||||
|
||||

|
||||
[](https://travis-ci.org/fchollet/keras)
|
||||
[](https://badge.fury.io/py/keras)
|
||||
|
||||
## You have just found Keras.
|
||||
|
||||
@@ -37,9 +38,9 @@ Keras is compatible with: __Python 2.7-3.5__.
|
||||
|
||||
## Getting started: 30 seconds to Keras
|
||||
|
||||
The core data structure of Keras is a __model__, a way to organize layers. There are two types of models: [`Sequential`](http://keras.io/models/#sequential) and [`Graph`](http://keras.io/models/#graph).
|
||||
The core data structure of Keras is a __model__, a way to organize layers. The main type of model is the [`Sequential`](http://keras.io/getting-started/sequential-model-guide) model, a linear stack of layers. For more complex architectures, you should use the [Keras function API](http://keras.io/getting-started/functional-api-guide).
|
||||
|
||||
Here's the `Sequential` model (a linear pile of layers):
|
||||
Here's the `Sequential` model:
|
||||
|
||||
```python
|
||||
from keras.models import Sequential
|
||||
@@ -52,15 +53,15 @@ Stacking layers is as easy as `.add()`:
|
||||
```python
|
||||
from keras.layers.core import Dense, Activation
|
||||
|
||||
model.add(Dense(output_dim=64, input_dim=100, init="glorot_uniform"))
|
||||
model.add(Dense(output_dim=64, input_dim=100))
|
||||
model.add(Activation("relu"))
|
||||
model.add(Dense(output_dim=10, init="glorot_uniform"))
|
||||
model.add(Dense(output_dim=10))
|
||||
model.add(Activation("softmax"))
|
||||
```
|
||||
|
||||
Once your model looks good, configure its learning process with `.compile()`:
|
||||
```python
|
||||
model.compile(loss='categorical_crossentropy', optimizer='sgd')
|
||||
model.compile(loss='categorical_crossentropy', optimizer='sgd', metrics=['accuracy'])
|
||||
```
|
||||
|
||||
If you need to, you can further configure your optimizer. A core principle of Keras is to make things reasonably simple, while allowing the user to be fully in control when they need to (the ultimate control being the easy extensibility of the source code).
|
||||
@@ -81,7 +82,7 @@ model.train_on_batch(X_batch, Y_batch)
|
||||
|
||||
Evaluate your performance in one line:
|
||||
```python
|
||||
objective_score = model.evaluate(X_test, Y_test, batch_size=32)
|
||||
loss_and_metrics = model.evaluate(X_test, Y_test, batch_size=32)
|
||||
```
|
||||
|
||||
Or generate predictions on new data:
|
||||
@@ -90,11 +91,14 @@ classes = model.predict_classes(X_test, batch_size=32)
|
||||
proba = model.predict_proba(X_test, batch_size=32)
|
||||
```
|
||||
|
||||
Building a network of LSTMs, a deep CNN, a Neural Turing Machine, a word2vec embedder or any other model is just as fast. The ideas behind deep learning are simple, so why should their implementation be painful?
|
||||
Building a question answering system, an image classification model, a Neural Turing Machine, a word2vec embedder or any other model is just as fast. The ideas behind deep learning are simple, so why should their implementation be painful?
|
||||
|
||||
Have a look at these [starter examples](http://keras.io/examples/).
|
||||
For a more in-depth tutorial about Keras, you can check out:
|
||||
|
||||
In the [examples folder](https://github.com/fchollet/keras/tree/master/examples) of the repo, you will find more advanced models: question-answering with memory networks, text generation with stacked LSTMs, neural turing machines, etc.
|
||||
- [Getting started with the Sequential model](http://keras.io/getting-started/sequential-model-guide)
|
||||
- [Getting started with the functional API](http://keras.io/getting-started/functional-api-guide)
|
||||
|
||||
In the [examples folder](https://github.com/fchollet/keras/tree/master/examples) of the repository, you will find more advanced models: question-answering with memory networks, text generation with stacked LSTMs, etc.
|
||||
|
||||
|
||||
------------------
|
||||
@@ -114,11 +118,6 @@ Keras uses the following dependencies:
|
||||
- Theano
|
||||
- [See installation instructions](http://deeplearning.net/software/theano/install.html#install).
|
||||
|
||||
**Note**: You should use the latest version of Theano, not the PyPI version. Install it with:
|
||||
```
|
||||
sudo pip install git+git://github.com/Theano/Theano.git
|
||||
```
|
||||
|
||||
*When using the TensorFlow backend:*
|
||||
|
||||
- TensorFlow
|
||||
|
||||
+369
-109
@@ -1,4 +1,57 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
General documentation architecture:
|
||||
|
||||
Home
|
||||
Index
|
||||
|
||||
- Getting started
|
||||
Getting started with the sequential model
|
||||
Getting started with the functional api
|
||||
Examples
|
||||
FAQ
|
||||
Installation guide
|
||||
|
||||
- Models
|
||||
About Keras models
|
||||
explain when one should use Sequential or functional API
|
||||
explain compilation step
|
||||
explain weight saving, weight loading
|
||||
explain serialization, deserialization
|
||||
Sequential
|
||||
Model (functional API)
|
||||
|
||||
- Layers
|
||||
About Keras layers
|
||||
explain common layer functions: get_weights, set_weights, get_config
|
||||
explain input_shape
|
||||
explain usage on non-Keras tensors
|
||||
Core layers
|
||||
Convolutional
|
||||
Recurrent
|
||||
Embeddings
|
||||
Normalization
|
||||
Advanced activations
|
||||
Noise
|
||||
|
||||
- Preprocessing
|
||||
Image preprocessing
|
||||
Text preprocessing
|
||||
Sequence preprocessing
|
||||
|
||||
Objectives
|
||||
Optimizers
|
||||
Activations
|
||||
Callbacks
|
||||
Datasets
|
||||
Backend
|
||||
Initializations
|
||||
Regularizers
|
||||
Constraints
|
||||
Visualization
|
||||
Scikit-learn API
|
||||
|
||||
'''
|
||||
from __future__ import print_function
|
||||
import re
|
||||
import inspect
|
||||
@@ -11,35 +64,147 @@ from keras.layers import core
|
||||
from keras.layers import noise
|
||||
from keras.layers import normalization
|
||||
from keras.layers import advanced_activations
|
||||
from keras.layers import containers
|
||||
from keras.layers import embeddings
|
||||
from keras.layers import wrappers
|
||||
from keras import optimizers
|
||||
from keras import callbacks
|
||||
from keras import models
|
||||
from keras.engine import topology
|
||||
from keras import objectives
|
||||
from keras import backend
|
||||
from keras import constraints
|
||||
from keras import activations
|
||||
from keras import regularizers
|
||||
|
||||
MODULES = [(convolutional, 'keras.layers.convolutional'),
|
||||
(recurrent, 'keras.layers.recurrent'),
|
||||
(noise, 'keras.layers.noise'),
|
||||
(normalization, 'keras.layers.normalization'),
|
||||
(advanced_activations, 'keras.layers.advanced_activations'),
|
||||
(containers, 'keras.layers.containers'),
|
||||
(core, 'keras.layers.core'),
|
||||
(embeddings, 'keras.layers.embeddings'),
|
||||
(optimizers, 'keras.optimizers'),
|
||||
(callbacks, 'keras.callbacks'),
|
||||
(models, 'keras.models')]
|
||||
EXCLUDE = {
|
||||
'Optimizer',
|
||||
'Wrapper',
|
||||
'get_session',
|
||||
'set_session',
|
||||
}
|
||||
|
||||
SKIP = ['build', 'get_params', 'MaskedLayer',
|
||||
'SiameseHead', 'MaskedLambda',
|
||||
'CallbackList']
|
||||
ROOT = 'http://keras.io/'
|
||||
INCLUDE_METHODS_FOR = [
|
||||
'Layer',
|
||||
'Graph',
|
||||
'Sequential',
|
||||
'Callback',
|
||||
PAGES = [
|
||||
{
|
||||
'page': 'models/sequential.md',
|
||||
'functions': [
|
||||
models.Sequential.compile,
|
||||
models.Sequential.fit,
|
||||
models.Sequential.evaluate,
|
||||
models.Sequential.predict,
|
||||
models.Sequential.predict_classes,
|
||||
models.Sequential.predict_proba,
|
||||
models.Sequential.train_on_batch,
|
||||
models.Sequential.test_on_batch,
|
||||
models.Sequential.predict_on_batch,
|
||||
models.Sequential.fit_generator,
|
||||
models.Sequential.evaluate_generator,
|
||||
],
|
||||
},
|
||||
{
|
||||
'page': 'models/model.md',
|
||||
'functions': [
|
||||
models.Model.compile,
|
||||
models.Model.fit,
|
||||
models.Model.evaluate,
|
||||
models.Model.predict,
|
||||
models.Model.train_on_batch,
|
||||
models.Model.test_on_batch,
|
||||
models.Model.predict_on_batch,
|
||||
models.Model.fit_generator,
|
||||
models.Model.evaluate_generator,
|
||||
models.Model.get_layer,
|
||||
]
|
||||
},
|
||||
{
|
||||
'page': 'layers/core.md',
|
||||
'classes': [
|
||||
core.Dense,
|
||||
core.Activation,
|
||||
core.Dropout,
|
||||
core.Flatten,
|
||||
core.Reshape,
|
||||
core.Permute,
|
||||
core.RepeatVector,
|
||||
topology.Merge,
|
||||
core.Lambda,
|
||||
core.ActivityRegularization,
|
||||
core.Masking,
|
||||
core.Highway,
|
||||
core.MaxoutDense,
|
||||
core.TimeDistributedDense,
|
||||
],
|
||||
},
|
||||
{
|
||||
'page': 'layers/convolutional.md',
|
||||
'classes': [
|
||||
convolutional.Convolution1D,
|
||||
convolutional.Convolution2D,
|
||||
convolutional.Convolution3D,
|
||||
convolutional.MaxPooling1D,
|
||||
convolutional.MaxPooling2D,
|
||||
convolutional.MaxPooling3D,
|
||||
convolutional.AveragePooling1D,
|
||||
convolutional.AveragePooling2D,
|
||||
convolutional.AveragePooling3D,
|
||||
convolutional.UpSampling1D,
|
||||
convolutional.UpSampling2D,
|
||||
convolutional.UpSampling3D,
|
||||
convolutional.ZeroPadding1D,
|
||||
convolutional.ZeroPadding2D,
|
||||
convolutional.ZeroPadding3D,
|
||||
],
|
||||
},
|
||||
{
|
||||
'page': 'layers/recurrent.md',
|
||||
'classes': [
|
||||
recurrent.Recurrent,
|
||||
recurrent.SimpleRNN,
|
||||
recurrent.GRU,
|
||||
recurrent.LSTM,
|
||||
],
|
||||
},
|
||||
{
|
||||
'page': 'layers/embeddings.md',
|
||||
'classes': [
|
||||
embeddings.Embedding,
|
||||
],
|
||||
},
|
||||
{
|
||||
'page': 'layers/normalization.md',
|
||||
'classes': [
|
||||
normalization.BatchNormalization,
|
||||
],
|
||||
},
|
||||
{
|
||||
'page': 'layers/advanced-activations.md',
|
||||
'all_module_classes': [advanced_activations],
|
||||
},
|
||||
{
|
||||
'page': 'layers/noise.md',
|
||||
'all_module_classes': [noise],
|
||||
},
|
||||
{
|
||||
'page': 'layers/wrappers.md',
|
||||
'all_module_classes': [wrappers],
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
'page': 'optimizers.md',
|
||||
'all_module_classes': [optimizers],
|
||||
},
|
||||
{
|
||||
'page': 'callbacks.md',
|
||||
'all_module_classes': [callbacks],
|
||||
},
|
||||
{
|
||||
'page': 'backend.md',
|
||||
'all_module_functions': [backend],
|
||||
},
|
||||
]
|
||||
|
||||
ROOT = 'http://keras.io/'
|
||||
|
||||
|
||||
def get_earliest_class_that_defined_member(member, cls):
|
||||
ancestors = get_classes_ancestors([cls])
|
||||
@@ -67,16 +232,19 @@ def get_classes_ancestors(classes):
|
||||
return filtered_ancestors
|
||||
|
||||
|
||||
def get_method_signature(method):
|
||||
signature = inspect.getargspec(method)
|
||||
def get_function_signature(function, method=True):
|
||||
signature = inspect.getargspec(function)
|
||||
defaults = signature.defaults
|
||||
args = signature.args[1:]
|
||||
if method:
|
||||
args = signature.args[1:]
|
||||
else:
|
||||
args = signature.args
|
||||
if defaults:
|
||||
kwargs = zip(args[-len(defaults):], defaults)
|
||||
args = args[:-len(defaults)]
|
||||
else:
|
||||
kwargs = []
|
||||
st = '%s.%s(' % (method.__module__, method.__name__)
|
||||
st = '%s.%s(' % (function.__module__, function.__name__)
|
||||
for a in args:
|
||||
st += str(a) + ', '
|
||||
for a, v in kwargs:
|
||||
@@ -91,6 +259,17 @@ def get_method_signature(method):
|
||||
return st + ')'
|
||||
|
||||
|
||||
def get_class_signature(cls):
|
||||
try:
|
||||
class_signature = get_function_signature(cls.__init__)
|
||||
class_signature = class_signature.replace('__init__', cls.__name__)
|
||||
except:
|
||||
# in case the class inherits from object and does not
|
||||
# define __init__
|
||||
class_signature = cls.__module__ + '.' + cls.__name__ + '()'
|
||||
return class_signature
|
||||
|
||||
|
||||
def class_to_docs_link(cls):
|
||||
module_name = cls.__module__
|
||||
assert module_name[:6] == 'keras.'
|
||||
@@ -124,19 +303,26 @@ def process_class_docstring(docstring):
|
||||
docstring = re.sub(r' ([^\s\\]+):(.*)\n',
|
||||
r' - __\1__:\2\n',
|
||||
docstring)
|
||||
|
||||
docstring = docstring.replace(' ' * 5, '\t\t')
|
||||
docstring = docstring.replace(' ' * 3, '\t')
|
||||
docstring = docstring.replace(' ', '')
|
||||
return docstring
|
||||
|
||||
|
||||
def process_method_docstring(docstring):
|
||||
docstring = re.sub(r' # (.*)\n',
|
||||
r' __\1__\n\n',
|
||||
def process_function_docstring(docstring):
|
||||
docstring = re.sub(r'\n # (.*)\n',
|
||||
r'\n __\1__\n\n',
|
||||
docstring)
|
||||
docstring = re.sub(r'\n # (.*)\n',
|
||||
r'\n __\1__\n\n',
|
||||
docstring)
|
||||
|
||||
docstring = re.sub(r' ([^\s\\]+):(.*)\n',
|
||||
r' - __\1__:\2\n',
|
||||
docstring)
|
||||
|
||||
docstring = docstring.replace(' ' * 6, '\t\t')
|
||||
docstring = docstring.replace(' ' * 4, '\t')
|
||||
docstring = docstring.replace(' ', '')
|
||||
return docstring
|
||||
@@ -156,101 +342,175 @@ for subdir, dirs, fnames in os.walk('templates'):
|
||||
shutil.copy(fpath, new_fpath)
|
||||
|
||||
print('Starting autogeneration.')
|
||||
covered_so_far = set()
|
||||
for module, module_name in MODULES:
|
||||
class_pages = []
|
||||
for name in dir(module):
|
||||
if name in SKIP:
|
||||
continue
|
||||
if name[0] == '_':
|
||||
continue
|
||||
module_member = getattr(module, name)
|
||||
if module_member in covered_so_far:
|
||||
continue
|
||||
if inspect.isclass(module_member):
|
||||
cls = module_member
|
||||
if cls.__module__ == module_name:
|
||||
for page_data in PAGES:
|
||||
blocks = []
|
||||
classes = page_data.get('classes', [])
|
||||
for module in page_data.get('all_module_classes', []):
|
||||
module_classes = []
|
||||
for name in dir(module):
|
||||
if name[0] == '_' or name in EXCLUDE:
|
||||
continue
|
||||
module_member = getattr(module, name)
|
||||
if inspect.isclass(module_member):
|
||||
cls = module_member
|
||||
if cls.__module__ == module.__name__:
|
||||
if cls not in module_classes:
|
||||
module_classes.append(cls)
|
||||
module_classes.sort(key=lambda x: id(x))
|
||||
classes += module_classes
|
||||
|
||||
try:
|
||||
class_signature = get_method_signature(cls.__init__)
|
||||
class_signature = class_signature.replace('__init__', cls.__name__)
|
||||
except:
|
||||
# in case the class inherits from object and does not
|
||||
# define __init__
|
||||
class_signature = module_name + '.' + cls.__name__ + '()'
|
||||
for cls in classes:
|
||||
subblocks = []
|
||||
signature = get_class_signature(cls)
|
||||
subblocks.append('<span style="float:right;">' + class_to_source_link(cls) + '</span>')
|
||||
subblocks.append('### ' + cls.__name__ + '\n')
|
||||
subblocks.append(code_snippet(signature))
|
||||
docstring = cls.__doc__
|
||||
if docstring:
|
||||
subblocks.append(process_class_docstring(docstring))
|
||||
blocks.append('\n'.join(subblocks))
|
||||
|
||||
methods = []
|
||||
methods_not_defined_here = []
|
||||
for name in dir(cls):
|
||||
if name in SKIP:
|
||||
continue
|
||||
if name[0] == '_':
|
||||
continue
|
||||
cls_member = getattr(cls, name)
|
||||
if inspect.ismethod(cls_member):
|
||||
method = cls_member
|
||||
signature = inspect.getargspec(method)
|
||||
defaults = signature.defaults
|
||||
args = signature.args[1:]
|
||||
if defaults:
|
||||
kwargs = zip(args[-len(defaults):], defaults)
|
||||
args = args[:-len(defaults)]
|
||||
else:
|
||||
kwargs = []
|
||||
functions = page_data.get('functions', [])
|
||||
for module in page_data.get('all_module_functions', []):
|
||||
module_functions = []
|
||||
for name in dir(module):
|
||||
if name[0] == '_' or name in EXCLUDE:
|
||||
continue
|
||||
module_member = getattr(module, name)
|
||||
if inspect.isfunction(module_member):
|
||||
function = module_member
|
||||
if module.__name__ in function.__module__:
|
||||
if function not in module_functions:
|
||||
module_functions.append(function)
|
||||
module_functions.sort(key=lambda x: id(x))
|
||||
functions += module_functions
|
||||
|
||||
defined_by = get_earliest_class_that_defined_member(method.__name__, cls)
|
||||
if cls == defined_by:
|
||||
methods.append(method)
|
||||
else:
|
||||
methods_not_defined_here.append((method, defined_by))
|
||||
|
||||
blocks = []
|
||||
blocks.append('<span style="float:right;">' + class_to_source_link(cls) + '</span>')
|
||||
blocks.append('# ' + cls.__name__ + '\n')
|
||||
blocks.append(code_snippet(class_signature))
|
||||
docstring = cls.__doc__
|
||||
if docstring:
|
||||
blocks.append(process_class_docstring(docstring))
|
||||
|
||||
if cls.__name__ in INCLUDE_METHODS_FOR:
|
||||
if methods or methods_not_defined_here:
|
||||
blocks.append('### Methods\n')
|
||||
for method in methods:
|
||||
signature = get_method_signature(method)
|
||||
signature = signature.replace(module_name + '.', '')
|
||||
blocks.append(code_snippet(signature))
|
||||
docstring = method.__doc__
|
||||
if docstring:
|
||||
blocks.append(process_method_docstring(docstring))
|
||||
for method, defined_by in methods_not_defined_here:
|
||||
signature = get_method_signature(method)
|
||||
method_module_name = method.__module__
|
||||
signature = signature.replace(method_module_name + '.', '')
|
||||
link = '[' + defined_by.__name__ + '](' + class_to_docs_link(defined_by) + ')'
|
||||
blocks.append(code_snippet(signature))
|
||||
blocks.append('Defined by ' + link + '.\n')
|
||||
|
||||
mkdown = '\n'.join(blocks)
|
||||
class_pages.append((id(cls), mkdown))
|
||||
covered_so_far.add(module_member)
|
||||
|
||||
class_pages.sort(key=lambda x: x[0])
|
||||
class_pages = [x[1] for x in class_pages]
|
||||
module_page = '\n----\n\n'.join(class_pages)
|
||||
for function in functions:
|
||||
subblocks = []
|
||||
signature = get_function_signature(function, method=False)
|
||||
signature = signature.replace(function.__module__ + '.', '')
|
||||
subblocks.append('### ' + function.__name__ + '\n')
|
||||
subblocks.append(code_snippet(signature))
|
||||
docstring = function.__doc__
|
||||
if docstring:
|
||||
subblocks.append(process_function_docstring(docstring))
|
||||
blocks.append('\n\n'.join(subblocks))
|
||||
|
||||
mkdown = '\n----\n\n'.join(blocks)
|
||||
# save module page.
|
||||
# Either insert content into existing page,
|
||||
# or create page otherwise
|
||||
path = 'sources/' + module_name.replace('.', '/')[6:] + '.md'
|
||||
page_name = page_data['page']
|
||||
path = os.path.join('sources', page_name)
|
||||
if os.path.exists(path):
|
||||
template = open(path).read()
|
||||
assert '{{autogenerated}}' in template, ('Template found for ' + path +
|
||||
' but missing {{autogenerated}} tag.')
|
||||
module_page = template.replace('{{autogenerated}}', module_page)
|
||||
mkdown = template.replace('{{autogenerated}}', mkdown)
|
||||
print('...inserting autogenerated content into template:', path)
|
||||
else:
|
||||
print('...creating new page with autogenerated content:', path)
|
||||
subdir = os.path.dirname(path)
|
||||
if not os.path.exists(subdir):
|
||||
os.makedirs(subdir)
|
||||
open(path, 'w').write(module_page)
|
||||
open(path, 'w').write(mkdown)
|
||||
|
||||
|
||||
# covered_so_far = set()
|
||||
# for module, module_name in MODULES:
|
||||
# class_pages = []
|
||||
# for name in dir(module):
|
||||
# if name in SKIP:
|
||||
# continue
|
||||
# if name[0] == '_':
|
||||
# continue
|
||||
# module_member = getattr(module, name)
|
||||
# if module_member in covered_so_far:
|
||||
# continue
|
||||
# if inspect.isclass(module_member):
|
||||
# cls = module_member
|
||||
# if cls.__module__ == module_name:
|
||||
|
||||
# try:
|
||||
# class_signature = get_function_signature(cls.__init__)
|
||||
# class_signature = class_signature.replace('__init__', cls.__name__)
|
||||
# except:
|
||||
# # in case the class inherits from object and does not
|
||||
# # define __init__
|
||||
# class_signature = module_name + '.' + cls.__name__ + '()'
|
||||
|
||||
# functions = []
|
||||
# functions_not_defined_here = []
|
||||
# for name in dir(cls):
|
||||
# if name in SKIP:
|
||||
# continue
|
||||
# if name[0] == '_':
|
||||
# continue
|
||||
# cls_member = getattr(cls, name)
|
||||
# if inspect.isfunction(cls_member):
|
||||
# function = cls_member
|
||||
# signature = inspect.getargspec(function)
|
||||
# defaults = signature.defaults
|
||||
# args = signature.args[1:]
|
||||
# if defaults:
|
||||
# kwargs = zip(args[-len(defaults):], defaults)
|
||||
# args = args[:-len(defaults)]
|
||||
# else:
|
||||
# kwargs = []
|
||||
|
||||
# defined_by = get_earliest_class_that_defined_member(function.__name__, cls)
|
||||
# if cls == defined_by:
|
||||
# functions.append(function)
|
||||
# else:
|
||||
# functions_not_defined_here.append((function, defined_by))
|
||||
|
||||
# blocks = []
|
||||
# blocks.append('<span style="float:right;">' + class_to_source_link(cls) + '</span>')
|
||||
# blocks.append('# ' + cls.__name__ + '\n')
|
||||
# blocks.append(code_snippet(class_signature))
|
||||
# docstring = cls.__doc__
|
||||
# if docstring:
|
||||
# blocks.append(process_class_docstring(docstring))
|
||||
|
||||
# if cls.__name__ in INCLUDE_functionS_FOR:
|
||||
# if functions or functions_not_defined_here:
|
||||
# blocks.append('### functions\n')
|
||||
# for function in functions:
|
||||
# signature = get_function_signature(function)
|
||||
# signature = signature.replace(module_name + '.', '')
|
||||
# blocks.append(code_snippet(signature))
|
||||
# docstring = function.__doc__
|
||||
# if docstring:
|
||||
# blocks.append(process_function_docstring(docstring))
|
||||
# for function, defined_by in functions_not_defined_here:
|
||||
# signature = get_function_signature(function)
|
||||
# function_module_name = function.__module__
|
||||
# signature = signature.replace(function_module_name + '.', '')
|
||||
# link = '[' + defined_by.__name__ + '](' + class_to_docs_link(defined_by) + ')'
|
||||
# blocks.append(code_snippet(signature))
|
||||
# blocks.append('Defined by ' + link + '.\n')
|
||||
|
||||
# mkdown = '\n'.join(blocks)
|
||||
# class_pages.append((id(cls), mkdown))
|
||||
# covered_so_far.add(module_member)
|
||||
|
||||
# class_pages.sort(key=lambda x: x[0])
|
||||
# class_pages = [x[1] for x in class_pages]
|
||||
# module_page = '\n----\n\n'.join(class_pages)
|
||||
|
||||
# # save module page.
|
||||
# # Either insert content into existing page,
|
||||
# # or create page otherwise
|
||||
# path = 'sources/' + module_name.replace('.', '/')[6:] + '.md'
|
||||
# if os.path.exists(path):
|
||||
# template = open(path).read()
|
||||
# assert '{{autogenerated}}' in template, ('Template found for ' + path +
|
||||
# ' but missing {{autogenerated}} tag.')
|
||||
# module_page = template.replace('{{autogenerated}}', module_page)
|
||||
# print('...inserting autogenerated content into template:', path)
|
||||
# else:
|
||||
# print('...creating new page with autogenerated content:', path)
|
||||
# subdir = os.path.dirname(path)
|
||||
# if not os.path.exists(subdir):
|
||||
# os.makedirs(subdir)
|
||||
# open(path, 'w').write(module_page)
|
||||
|
||||
+29
-19
@@ -3,8 +3,8 @@ theme: readthedocs
|
||||
docs_dir: sources
|
||||
repo_url: http://github.com/fchollet/keras
|
||||
site_url: http://keras.io/
|
||||
#theme_dir: theme
|
||||
site_description: Documentation for fast and lightweight Keras Deep Learning library.
|
||||
# theme_dir: theme
|
||||
site_description: 'Documentation for Keras, the Python Deep Learning library.'
|
||||
|
||||
dev_addr: '0.0.0.0:8000'
|
||||
google_analytics: ['UA-61785484-1', 'keras.io']
|
||||
@@ -12,31 +12,41 @@ google_analytics: ['UA-61785484-1', 'keras.io']
|
||||
|
||||
pages:
|
||||
- Home: index.md
|
||||
- Index: documentation.md
|
||||
- Examples: examples.md
|
||||
- FAQ: faq.md
|
||||
- Backends: backend.md
|
||||
- Optimizers: optimizers.md
|
||||
- Objectives: objectives.md
|
||||
- Models: models.md
|
||||
- Activations: activations.md
|
||||
- Initializations: initializations.md
|
||||
- Regularizers: regularizers.md
|
||||
- Constraints: constraints.md
|
||||
- Callbacks: callbacks.md
|
||||
- Datasets: datasets.md
|
||||
- Visualization: visualization.md
|
||||
- Getting started:
|
||||
- Guide to the Sequential model: getting-started/sequential-model-guide.md
|
||||
- Guide to the Functional API: getting-started/functional-api-guide.md
|
||||
- FAQ: getting-started/faq.md
|
||||
- Models:
|
||||
- About Keras models: models/about-keras-models.md
|
||||
- Sequential: models/sequential.md
|
||||
- Model (functional API): models/model.md
|
||||
- Layers:
|
||||
- About Keras layers: layers/about-keras-layers.md
|
||||
- Core Layers: layers/core.md
|
||||
- Convolutional Layers: layers/convolutional.md
|
||||
- Recurrent Layers: layers/recurrent.md
|
||||
- Advanced Activations Layers: layers/advanced_activations.md
|
||||
- Normalization Layers: layers/normalization.md
|
||||
- Embedding Layers: layers/embeddings.md
|
||||
- Advanced Activations Layers: layers/advanced-activations.md
|
||||
- Normalization Layers: layers/normalization.md
|
||||
- Noise layers: layers/noise.md
|
||||
- Containers: layers/containers.md
|
||||
- Layer wrappers: layers/wrappers.md
|
||||
- Writing your own Keras layers: layers/writing-your-own-keras-layers.md
|
||||
- Preprocessing:
|
||||
- Sequence Preprocessing: preprocessing/sequence.md
|
||||
- Text Preprocessing: preprocessing/text.md
|
||||
- Image Preprocessing: preprocessing/image.md
|
||||
- Objectives: objectives.md
|
||||
- Optimizers: optimizers.md
|
||||
- Activations: activations.md
|
||||
- Callbacks: callbacks.md
|
||||
- Datasets: datasets.md
|
||||
- Backend: backend.md
|
||||
- Initializations: initializations.md
|
||||
- Regularizers: regularizers.md
|
||||
- Constraints: constraints.md
|
||||
- Visualization: visualization.md
|
||||
- Scikit-learn API: scikit-learn-api.md
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
externo
+5
-3
@@ -14,11 +14,13 @@ is equivalent to:
|
||||
model.add(Dense(64, activation='tanh'))
|
||||
```
|
||||
|
||||
You can also pass an element-wise Theano function as an activation:
|
||||
You can also pass an element-wise Theano/TensorFlow function as an activation:
|
||||
|
||||
```python
|
||||
from keras import backend as K
|
||||
|
||||
def tanh(x):
|
||||
return theano.tensor.tanh(x)
|
||||
return K.tanh(x)
|
||||
|
||||
model.add(Dense(64, activation=tanh))
|
||||
model.add(Activation(tanh))
|
||||
@@ -36,4 +38,4 @@ model.add(Activation(tanh))
|
||||
|
||||
## On Advanced Activations
|
||||
|
||||
Activations that are more complex than a simple Theano function (eg. learnable activations, configurable activations, etc.) are available as [Advanced Activation layers](layers/advanced_activations.md), and can be found in the module `keras.layers.advanced_activations`. These include PReLU and LeakyReLU.
|
||||
Activations that are more complex than a simple Theano/TensorFlow function (eg. learnable activations, configurable activations, etc.) are available as [Advanced Activation layers](layers/advanced-activations.md), and can be found in the module `keras.layers.advanced_activations`. These include PReLU and LeakyReLU.
|
||||
|
||||
externo
+10
-2
@@ -9,6 +9,8 @@ At this time, Keras has two backend implementations available: the **Theano** ba
|
||||
- [Theano](http://deeplearning.net/software/theano/) is an open-source symbolic tensor manipulation framework developed by LISA/MILA Lab at Université de Montréal.
|
||||
- [TensorFlow](http://www.tensorflow.org/) is an open-source symbolic tensor manipulation framework developed by Google, Inc.
|
||||
|
||||
----
|
||||
|
||||
## Switching from one backend to another
|
||||
|
||||
If you have run Keras at least once, you will find the Keras configuration file at:
|
||||
@@ -32,6 +34,8 @@ Using TensorFlow backend.
|
||||
tensorflow
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
## Using the abstract Keras backend to write new code
|
||||
|
||||
If you want the Keras modules you write to be compatible with both Theano and TensorFlow, you have to write them via the abstract Keras backend API. Here's an intro.
|
||||
@@ -74,8 +78,12 @@ a = concatenate([b, c], axis=-1)
|
||||
# etc...
|
||||
```
|
||||
|
||||
For more information, see the code at `keras/backend/theano_backend.py` and `keras/backend/tensorflow_backend.py`.
|
||||
|
||||
----
|
||||
|
||||
## Backend functions
|
||||
|
||||
|
||||
{{autogenerated}}
|
||||
|
||||
|
||||
|
||||
|
||||
externo
+20
-14
@@ -2,13 +2,13 @@
|
||||
|
||||
## CIFAR10 small image classification
|
||||
|
||||
`keras.datasets.cifar10`
|
||||
|
||||
Dataset of 50,000 32x32 color training images, labeled over 10 categories, and 10,000 test images.
|
||||
|
||||
### Usage:
|
||||
|
||||
```python
|
||||
from keras.datasets import cifar10
|
||||
|
||||
(X_train, y_train), (X_test, y_test) = cifar10.load_data()
|
||||
```
|
||||
|
||||
@@ -21,13 +21,13 @@ Dataset of 50,000 32x32 color training images, labeled over 10 categories, and 1
|
||||
|
||||
## CIFAR100 small image classification
|
||||
|
||||
`keras.datasets.cifar100`
|
||||
|
||||
Dataset of 50,000 32x32 color training images, labeled over 100 categories, and 10,000 test images.
|
||||
|
||||
### Usage:
|
||||
|
||||
```python
|
||||
from keras.datasets import cifar100
|
||||
|
||||
(X_train, y_train), (X_test, y_test) = cifar100.load_data(label_mode='fine')
|
||||
```
|
||||
|
||||
@@ -44,8 +44,6 @@ Dataset of 50,000 32x32 color training images, labeled over 100 categories, and
|
||||
|
||||
## IMDB Movie reviews sentiment classification
|
||||
|
||||
`keras.datasets.imdb`
|
||||
|
||||
Dataset of 25,000 movies reviews from IMDB, labeled by sentiment (positive/negative). Reviews have been preprocessed, and each review is encoded as a [sequence](preprocessing/sequence.md) of word indexes (integers). For convenience, words are indexed by overall frequency in the dataset, so that for instance the integer "3" encodes the 3rd most frequent word in the data. This allows for quick filtering operations such as: "only consider the top 10,000 most common words, but eliminate the top 20 most common words".
|
||||
|
||||
As a convention, "0" does not stand for a specific word, but instead is used to encode any unknown word.
|
||||
@@ -53,8 +51,13 @@ As a convention, "0" does not stand for a specific word, but instead is used to
|
||||
### Usage:
|
||||
|
||||
```python
|
||||
(X_train, y_train), (X_test, y_test) = imdb.load_data(path="imdb.pkl", \
|
||||
nb_words=None, skip_top=0, maxlen=None, test_split=0.1, seed=113)
|
||||
from keras.datasets import imdb
|
||||
|
||||
(X_train, y_train), (X_test, y_test) = imdb.load_data(path="imdb.pkl",
|
||||
nb_words=None,
|
||||
skip_top=0,
|
||||
maxlen=None,
|
||||
test_split=0.1)
|
||||
```
|
||||
- __Return:__
|
||||
- 2 tuples:
|
||||
@@ -74,15 +77,18 @@ nb_words=None, skip_top=0, maxlen=None, test_split=0.1, seed=113)
|
||||
|
||||
## Reuters newswire topics classification
|
||||
|
||||
`keras.datasets.reuters`
|
||||
|
||||
Dataset of 11,228 newswires from Reuters, labeled over 46 topics. As with the IMDB dataset, each wire is encoded as a sequence of word indexes (same conventions).
|
||||
|
||||
### Usage:
|
||||
|
||||
```python
|
||||
(X_train, y_train), (X_test, y_test) = reuters.load_data(path="reuters.pkl", \
|
||||
nb_words=None, skip_top=0, maxlen=None, test_split=0.1, seed=113)
|
||||
from keras.datasets import reuters
|
||||
|
||||
(X_train, y_train), (X_test, y_test) = reuters.load_data(path="reuters.pkl",
|
||||
nb_words=None,
|
||||
skip_top=0,
|
||||
maxlen=None,
|
||||
test_split=0.1)
|
||||
```
|
||||
|
||||
The specifications are the same as that of the IMDB dataset.
|
||||
@@ -101,13 +107,13 @@ word_index = reuters.get_word_index(path="reuters_word_index.pkl")
|
||||
|
||||
## MNIST database of handwritten digits
|
||||
|
||||
`keras.datasets.mnist`
|
||||
|
||||
Dataset of 60,000 28x28 grayscale images of the 10 digits, along with a test set of 10,000 images.
|
||||
|
||||
### Usage:
|
||||
|
||||
```python
|
||||
from keras.datasets import mnist
|
||||
|
||||
(X_train, y_train), (X_test, y_test) = mnist.load_data()
|
||||
```
|
||||
|
||||
|
||||
externo
-40
@@ -1,40 +0,0 @@
|
||||
# Keras Documentation Index
|
||||
|
||||
## Introduction
|
||||
|
||||
- [Home](index.md)
|
||||
- [Index](documentation.md)
|
||||
- [Examples](examples.md)
|
||||
- [FAQ](faq.md)
|
||||
- [Backend](backend.md)
|
||||
|
||||
---
|
||||
|
||||
## Base functionality
|
||||
|
||||
- [Optimizers](optimizers.md)
|
||||
- [Objectives](objectives.md)
|
||||
- [Models](models.md)
|
||||
- [Activations](activations.md)
|
||||
- [Initializations](initializations.md)
|
||||
- [Regularizers](regularizers.md)
|
||||
- [Constraints](constraints.md)
|
||||
- [Callbacks](callbacks.md)
|
||||
- [Datasets](datasets.md)
|
||||
|
||||
---
|
||||
|
||||
## Layers
|
||||
- [Core](layers/core.md)
|
||||
- [Convolutional](layers/convolutional.md)
|
||||
- [Recurrent](layers/recurrent.md)
|
||||
- [Advanced Activations](layers/advanced_activations.md)
|
||||
- [Normalization](layers/normalization.md)
|
||||
- [Embeddings](layers/embeddings.md)
|
||||
|
||||
---
|
||||
|
||||
## Preprocessing
|
||||
- [Sequence](preprocessing/sequence.md)
|
||||
- [Text](preprocessing/text.md)
|
||||
- [Image](preprocessing/image.md)
|
||||
externo
-197
@@ -1,197 +0,0 @@
|
||||
|
||||
Here are a few examples to get you started!
|
||||
|
||||
### Multilayer Perceptron (MLP) for multi-class softmax classification:
|
||||
|
||||
```python
|
||||
from keras.models import Sequential
|
||||
from keras.layers import Dense, Dropout, Activation
|
||||
from keras.optimizers import SGD
|
||||
|
||||
model = Sequential()
|
||||
# Dense(64) is a fully-connected layer with 64 hidden units.
|
||||
# in the first layer, you must specify the expected input data shape:
|
||||
# here, 20-dimensional vectors.
|
||||
model.add(Dense(64, input_dim=20, init='uniform'))
|
||||
model.add(Activation('tanh'))
|
||||
model.add(Dropout(0.5))
|
||||
model.add(Dense(64, init='uniform'))
|
||||
model.add(Activation('tanh'))
|
||||
model.add(Dropout(0.5))
|
||||
model.add(Dense(10, init='uniform'))
|
||||
model.add(Activation('softmax'))
|
||||
|
||||
sgd = SGD(lr=0.1, decay=1e-6, momentum=0.9, nesterov=True)
|
||||
model.compile(loss='categorical_crossentropy',
|
||||
optimizer=sgd)
|
||||
|
||||
model.fit(X_train, y_train,
|
||||
nb_epoch=20,
|
||||
batch_size=16,
|
||||
show_accuracy=True)
|
||||
score = model.evaluate(X_test, y_test, batch_size=16)
|
||||
```
|
||||
|
||||
### Alternative implementation of a similar MLP:
|
||||
|
||||
```python
|
||||
model = Sequential()
|
||||
model.add(Dense(64, input_dim=20, activation='relu'))
|
||||
model.add(Dropout(0.5))
|
||||
model.add(Dense(64, activation='relu'))
|
||||
model.add(Dropout(0.5))
|
||||
model.add(Dense(10, activation='softmax'))
|
||||
|
||||
model.compile(loss='categorical_crossentropy', optimizer='adadelta')
|
||||
```
|
||||
|
||||
|
||||
### MLP for binary classification:
|
||||
```python
|
||||
model = Sequential()
|
||||
model.add(Dense(64, input_dim=20, init='uniform', activation='relu'))
|
||||
model.add(Dropout(0.5))
|
||||
model.add(Dense(64, activation='relu'))
|
||||
model.add(Dropout(0.5))
|
||||
model.add(Dense(1, activation='sigmoid'))
|
||||
|
||||
# "class_mode" defaults to "categorical". For correctly displaying accuracy
|
||||
# in a binary classification problem, it should be set to "binary".
|
||||
model.compile(loss='binary_crossentropy',
|
||||
optimizer='rmsprop',
|
||||
class_mode='binary')
|
||||
```
|
||||
|
||||
### VGG-like convnet:
|
||||
|
||||
```python
|
||||
from keras.models import Sequential
|
||||
from keras.layers import Dense, Dropout, Activation, Flatten
|
||||
from keras.layers import Convolution2D, MaxPooling2D
|
||||
from keras.optimizers import SGD
|
||||
|
||||
model = Sequential()
|
||||
# input: 100x100 images with 3 channels -> (3, 100, 100) tensors.
|
||||
# this applies 32 convolution filters of size 3x3 each.
|
||||
model.add(Convolution2D(32, 3, 3, border_mode='valid', input_shape=(3, 100, 100)))
|
||||
model.add(Activation('relu'))
|
||||
model.add(Convolution2D(32, 3, 3))
|
||||
model.add(Activation('relu'))
|
||||
model.add(MaxPooling2D(pool_size=(2, 2)))
|
||||
model.add(Dropout(0.25))
|
||||
|
||||
model.add(Convolution2D(64, 3, 3, border_mode='valid'))
|
||||
model.add(Activation('relu'))
|
||||
model.add(Convolution2D(64, 3, 3))
|
||||
model.add(Activation('relu'))
|
||||
model.add(MaxPooling2D(pool_size=(2, 2)))
|
||||
model.add(Dropout(0.25))
|
||||
|
||||
model.add(Flatten())
|
||||
# Note: Keras does automatic shape inference.
|
||||
model.add(Dense(256))
|
||||
model.add(Activation('relu'))
|
||||
model.add(Dropout(0.5))
|
||||
|
||||
model.add(Dense(10))
|
||||
model.add(Activation('softmax'))
|
||||
|
||||
sgd = SGD(lr=0.1, decay=1e-6, momentum=0.9, nesterov=True)
|
||||
model.compile(loss='categorical_crossentropy', optimizer=sgd)
|
||||
|
||||
model.fit(X_train, Y_train, batch_size=32, nb_epoch=1)
|
||||
|
||||
```
|
||||
|
||||
### Sequence classification with LSTM:
|
||||
|
||||
```python
|
||||
from keras.models import Sequential
|
||||
from keras.layers import Dense, Dropout, Activation
|
||||
from keras.layers import Embedding
|
||||
from keras.layers import LSTM
|
||||
|
||||
model = Sequential()
|
||||
model.add(Embedding(max_features, 256, input_length=maxlen))
|
||||
model.add(LSTM(output_dim=128, activation='sigmoid', inner_activation='hard_sigmoid'))
|
||||
model.add(Dropout(0.5))
|
||||
model.add(Dense(1))
|
||||
model.add(Activation('sigmoid'))
|
||||
|
||||
model.compile(loss='binary_crossentropy', optimizer='rmsprop')
|
||||
|
||||
model.fit(X_train, Y_train, batch_size=16, nb_epoch=10)
|
||||
score = model.evaluate(X_test, Y_test, batch_size=16)
|
||||
```
|
||||
|
||||
### Architecture for learning image captions with a convnet and a Gated Recurrent Unit:
|
||||
(word-level embedding, caption of maximum length 16 words).
|
||||
|
||||
Note that getting this to work well will require using a bigger convnet, initialized with pre-trained weights.
|
||||
|
||||
```python
|
||||
max_caption_len = 16
|
||||
vocab_size = 10000
|
||||
|
||||
# first, let's define an image model that
|
||||
# will encode pictures into 128-dimensional vectors.
|
||||
# it should be initialized with pre-trained weights.
|
||||
image_model = Sequential()
|
||||
image_model.add(Convolution2D(32, 3, 3, border_mode='valid', input_shape=(3, 100, 100)))
|
||||
image_model.add(Activation('relu'))
|
||||
image_model.add(Convolution2D(32, 3, 3))
|
||||
image_model.add(Activation('relu'))
|
||||
image_model.add(MaxPooling2D(pool_size=(2, 2)))
|
||||
|
||||
image_model.add(Convolution2D(64, 3, 3, border_mode='valid'))
|
||||
image_model.add(Activation('relu'))
|
||||
image_model.add(Convolution2D(64, 3, 3))
|
||||
image_model.add(Activation('relu'))
|
||||
image_model.add(MaxPooling2D(pool_size=(2, 2)))
|
||||
|
||||
image_model.add(Flatten())
|
||||
image_model.add(Dense(128))
|
||||
|
||||
# let's load the weights from a save file.
|
||||
image_model.load_weights('weight_file.h5')
|
||||
|
||||
# next, let's define a RNN model that encodes sequences of words
|
||||
# into sequences of 128-dimensional word vectors.
|
||||
language_model = Sequential()
|
||||
language_model.add(Embedding(vocab_size, 256, input_length=max_caption_len))
|
||||
language_model.add(GRU(output_dim=128, return_sequences=True))
|
||||
language_model.add(TimeDistributedDense(128))
|
||||
|
||||
# let's repeat the image vector to turn it into a sequence.
|
||||
image_model.add(RepeatVector(max_caption_len))
|
||||
|
||||
# the output of both models will be tensors of shape (samples, max_caption_len, 128).
|
||||
# let's concatenate these 2 vector sequences.
|
||||
model = Merge([image_model, language_model], mode='concat', concat_axis=-1)
|
||||
# let's encode this vector sequence into a single vector
|
||||
model.add(GRU(256, 256, return_sequences=False))
|
||||
# which will be used to compute a probability
|
||||
# distribution over what the next word in the caption should be!
|
||||
model.add(Dense(vocab_size))
|
||||
model.add(Activation('softmax'))
|
||||
|
||||
model.compile(loss='categorical_crossentropy', optimizer='rmsprop')
|
||||
|
||||
# "images" is a numpy float array of shape (nb_samples, nb_channels=3, width, height).
|
||||
# "captions" is a numpy integer array of shape (nb_samples, max_caption_len)
|
||||
# containing word index sequences representing partial captions.
|
||||
# "next_words" is a numpy float array of shape (nb_samples, vocab_size)
|
||||
# containing a categorical encoding (0s and 1s) of the next word in the corresponding
|
||||
# partial caption.
|
||||
model.fit([images, partial_captions], next_words, batch_size=16, nb_epoch=100)
|
||||
```
|
||||
|
||||
In the examples folder, you will find example models for real datasets:
|
||||
|
||||
- CIFAR10 small images classification: Convolutional Neural Network (CNN) with realtime data augmentation
|
||||
- IMDB movie review sentiment classification: LSTM over sequences of words
|
||||
- Reuters newswires topic classification: Multilayer Perceptron (MLP)
|
||||
- MNIST handwritten digits classification: MLP & CNN
|
||||
- Character-level text generation with LSTM
|
||||
|
||||
...and more.
|
||||
@@ -1,29 +1,39 @@
|
||||
# Keras FAQ: Frequently Asked Keras Questions
|
||||
|
||||
[How can I run Keras on GPU?](#how-can-i-run-keras-on-gpu)
|
||||
|
||||
[How can I save a Keras model?](#how-can-i-save-a-keras-model)
|
||||
|
||||
[Why is the training loss much higher than the testing loss?](#why-is-the-training-loss-much-higher-than-the-testing-loss)
|
||||
|
||||
[How can I visualize the output of an intermediate layer?](#how-can-i-visualize-the-output-of-an-intermediate-layer)
|
||||
|
||||
[How can I use Keras with datasets that don't fit in memory?](#how-can-i-use-keras-with-datasets-that-dont-fit-in-memory)
|
||||
|
||||
[How can I interrupt training when the validation loss isn't decreasing anymore?](#how-can-i-interrupt-training-when-the-validation-loss-isnt-decreasing-anymore)
|
||||
|
||||
[How is the validation split computed?](#how-is-the-validation-split-computed)
|
||||
|
||||
[Is the data shuffled during training?](#is-the-data-shuffled-during-training)
|
||||
|
||||
[How can I record the training / validation loss / accuracy at each epoch?](#how-can-i-record-the-training-validation-loss-accuracy-at-each-epoch)
|
||||
|
||||
[How can I use stateful RNNs?](#how-can-i-use-stateful-rnns)
|
||||
- [How should I cite Keras?](#how-should-i-cite-keras)
|
||||
- [How can I run Keras on GPU?](#how-can-i-run-keras-on-gpu)
|
||||
- [How can I save a Keras model?](#how-can-i-save-a-keras-model)
|
||||
- [Why is the training loss much higher than the testing loss?](#why-is-the-training-loss-much-higher-than-the-testing-loss)
|
||||
- [How can I visualize the output of an intermediate layer?](#how-can-i-visualize-the-output-of-an-intermediate-layer)
|
||||
- [How can I use Keras with datasets that don't fit in memory?](#how-can-i-use-keras-with-datasets-that-dont-fit-in-memory)
|
||||
- [How can I interrupt training when the validation loss isn't decreasing anymore?](#how-can-i-interrupt-training-when-the-validation-loss-isnt-decreasing-anymore)
|
||||
- [How is the validation split computed?](#how-is-the-validation-split-computed)
|
||||
- [Is the data shuffled during training?](#is-the-data-shuffled-during-training)
|
||||
- [How can I record the training / validation loss / accuracy at each epoch?](#how-can-i-record-the-training-validation-loss-accuracy-at-each-epoch)
|
||||
- [How can I use stateful RNNs?](#how-can-i-use-stateful-rnns)
|
||||
|
||||
---
|
||||
|
||||
### How should I cite Keras?
|
||||
|
||||
Please cite Keras in your publications if it helps your research. Here is an example BibTeX entry:
|
||||
|
||||
```
|
||||
@misc{chollet2015keras,
|
||||
author = {Chollet, François},
|
||||
title = {Keras},
|
||||
year = {2015},
|
||||
publisher = {GitHub},
|
||||
journal = {GitHub repository},
|
||||
howpublished = {\url{https://github.com/fchollet/keras}}
|
||||
}
|
||||
```
|
||||
|
||||
### How can I run Keras on GPU?
|
||||
|
||||
If you are running on the TensorFlow backend, your code will automatically run on GPU if any available GPU is detected.
|
||||
If you are running on the Theano backend, you can use one of the following methods:
|
||||
|
||||
Method 1: use Theano flags.
|
||||
```bash
|
||||
THEANO_FLAGS=device=gpu,floatX=float32 python my_keras_script.py
|
||||
@@ -67,7 +77,10 @@ model = model_from_json(json_string)
|
||||
model = model_from_yaml(yaml_string)
|
||||
```
|
||||
|
||||
If you need to save the weights of a model, you can do so in HDF5:
|
||||
If you need to save the weights of a model, you can do so in HDF5 with the code below.
|
||||
|
||||
Note that you will first need to install HDF5 and the Python library h5py, which do not come bundled with Keras.
|
||||
|
||||
```python
|
||||
model.save_weights('my_model_weights.h5')
|
||||
```
|
||||
@@ -108,22 +121,31 @@ from keras import backend as K
|
||||
|
||||
# with a Sequential model
|
||||
get_3rd_layer_output = K.function([model.layers[0].input],
|
||||
[model.layers[3].get_output(train=False)])
|
||||
[model.layers[3].output])
|
||||
layer_output = get_3rd_layer_output([X])[0]
|
||||
|
||||
# with a Graph model
|
||||
get_conv_layer_output = K.function([model.inputs[i].input for i in model.input_order],
|
||||
[model.nodes['conv'].get_output(train=False)])
|
||||
conv_output = get_conv_layer_output([input_data_dict[i] for i in model.input_order])[0]
|
||||
```
|
||||
|
||||
Similarly, you could build a Theano and TensorFlow function directly.
|
||||
|
||||
Note that if your model has a different behavior in training and testing phase (e.g. if it uses `Dropout`, `BatchNormalization`, etc.), you will need
|
||||
to pass the learning phase flag to your function:
|
||||
|
||||
```python
|
||||
get_3rd_layer_output = K.function([model.layers[0].input, K.learning_phase()],
|
||||
[model.layers[3].output])
|
||||
|
||||
# output in train mode
|
||||
layer_output = get_3rd_layer_output([X, 1])[0]
|
||||
|
||||
# output in test mode
|
||||
layer_output = get_3rd_layer_output([X, 1])[0]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### How can I use Keras with datasets that don't fit in memory?
|
||||
|
||||
You can do batch training using `model.train_on_batch(X, y)` and `model.test_on_batch(X, y)`. See the [models documentation](models.md).
|
||||
You can do batch training using `model.train_on_batch(X, y)` and `model.test_on_batch(X, y)`. See the [models documentation](/models/sequential).
|
||||
|
||||
Alternatively, you can write a generator that yields batches of training data and use the method `model.fit_generator(data_generator, samples_per_epoch, nb_epoch)`.
|
||||
|
||||
@@ -141,7 +163,7 @@ early_stopping = EarlyStopping(monitor='val_loss', patience=2)
|
||||
model.fit(X, y, validation_split=0.2, callbacks=[early_stopping])
|
||||
```
|
||||
|
||||
Find out more in the [callbacks documentation](callbacks.md).
|
||||
Find out more in the [callbacks documentation](/callbacks).
|
||||
|
||||
---
|
||||
|
||||
@@ -156,14 +178,14 @@ If you set the `validation_split` argument in `model.fit` to e.g. 0.1, then the
|
||||
|
||||
Yes, if the `shuffle` argument in `model.fit` is set to `True` (which is the default), the training data will be randomly shuffled at each epoch.
|
||||
|
||||
Validation data isn't shuffled.
|
||||
Validation data is never shuffled.
|
||||
|
||||
---
|
||||
|
||||
|
||||
### How can I record the training / validation loss / accuracy at each epoch?
|
||||
|
||||
The `model.fit` method returns an `History` callback, which has a `history` attribute containing the lists of successive losses / accuracies.
|
||||
The `model.fit` method returns an `History` callback, which has a `history` attribute containing the lists of successive losses and other metrics.
|
||||
|
||||
```python
|
||||
hist = model.fit(X, y, validation_split=0.2)
|
||||
@@ -0,0 +1,421 @@
|
||||
# Getting started with the Keras functional API
|
||||
|
||||
The Keras functional API is the way to go for defining complex models, such as multi-output models, directed acyclic graphs, or models with shared layers.
|
||||
|
||||
This guide assumes that you are already familiar with the `Sequential` model.
|
||||
|
||||
Let's start with something simple.
|
||||
|
||||
-----
|
||||
|
||||
## First example: fully connected network
|
||||
|
||||
The `Sequential` model is probably a better choice to implement such a network, but it helps to start with something really simple.
|
||||
|
||||
- A layer instance is callable (on a tensor), and it returns a tensor
|
||||
- Input tensor(s) and output tensor(s) can then be used to define a `Model`
|
||||
- Such a model can be trained just like Keras `Sequential` models.
|
||||
|
||||
```python
|
||||
from keras.layers import Input, Dense
|
||||
from keras.models import Model
|
||||
|
||||
# this returns a tensor
|
||||
inputs = Input(shape=(784,))
|
||||
|
||||
# a layer instance is callable on a tensor, and returns a tensor
|
||||
x = Dense(64, activation='relu')(inputs)
|
||||
x = Dense(64, activation='relu')(x)
|
||||
predictions = Dense(10, activation='softmax')(x)
|
||||
|
||||
# this creates a model that includes
|
||||
# the Input layer and three Dense layers
|
||||
model = Model(input=inputs, output=predictions)
|
||||
model.compile(optimizer='rmsprop',
|
||||
loss='categorical_crossentropy',
|
||||
metrics=['accuracy'])
|
||||
model.fit(data, labels) # starts training
|
||||
```
|
||||
|
||||
-----
|
||||
|
||||
## All models are callable, just like layers
|
||||
|
||||
With the functional API, it is easy to re-use trained models: you can treat any model as if it were a layer, by calling it on a tensor. Note that by calling a model you aren't just re-using the *architecture* of the model, you are also re-using its weights.
|
||||
|
||||
```python
|
||||
x = Input(shape=(784,))
|
||||
# this works, and returns the 10-way softmax we defined above.
|
||||
y = model(x)
|
||||
```
|
||||
|
||||
This can allow, for instance, to quickly create models that can process *sequences* of inputs. You could turn an image classification model into a video classification model, in just one line.
|
||||
|
||||
```python
|
||||
from keras.layers import TimeDistributed
|
||||
|
||||
# input tensor for sequences of 20 timesteps,
|
||||
# each containing a 784-dimensional vector
|
||||
input_sequences = Input(shape=(20, 784))
|
||||
|
||||
# this applies our previous model to every timestep in the input sequences.
|
||||
# the output of the previous model was a 10-way softmax,
|
||||
# so the output of the layer below will be a sequence of 20 vectors of size 10.
|
||||
processed_sequences = TimeDistributed(model)(input_sequences)
|
||||
```
|
||||
|
||||
-----
|
||||
|
||||
## Multi-input and multi-output models
|
||||
|
||||
Here's a good use case for the functional API: models with multiple inputs and outputs. The functional API makes it easy to manipulate a large number of intertwined datastreams.
|
||||
|
||||
Let's consider the following model. We seek to predict how many retweets and likes a news headline will receive on Twitter. The main input to the model will be the headline itself, as a sequence of words, but to spice things up, our model will also have an auxiliary input, receiving extra data such as the time of day when the headline was posted, etc.
|
||||
The model will also be supervised via two loss functions. Using the main loss function earlier in a model is a good regularization mechanism for deep models.
|
||||
|
||||
Here's what our model looks like:
|
||||
|
||||
<img src="http://s3.amazonaws.com/keras.io/img/multi-input-multi-output-graph.png" alt="multi-input-multi-output-graph" style="width: 400px;"/>
|
||||
|
||||
Let's implement it with the functional API.
|
||||
|
||||
The main input will receive the headline, as a sequence of integers (each integer encodes a word).
|
||||
The integers will be between 1 and 10,000 (a vocabulary of 10,000 words) and the sequences will be 100 words long.
|
||||
|
||||
```python
|
||||
from keras.layers import Input, Embedding, LSTM, Dense, merge
|
||||
from keras.models import Model
|
||||
|
||||
# headline input: meant to receive sequences of 100 integers, between 1 and 10000.
|
||||
# note that we can name any layer by passing it a "name" argument.
|
||||
main_input = Input(shape=(100,), dtype='int32', name='main_input')
|
||||
|
||||
# this embedding layer will encode the input sequence
|
||||
# into a sequence of dense 512-dimensional vectors.
|
||||
x = Embedding(output_dim=512, input_dim=10000, input_length=100)(main_input)
|
||||
|
||||
# a LSTM will transform the vector sequence into a single vector,
|
||||
# containing information about the entire sequence
|
||||
lstm_out = LSTM(32)(x)
|
||||
```
|
||||
|
||||
Here we insert the auxiliary loss, allowing the LSTM and Embedding layer to be trained smoothly even though the main loss will be much higher in the model.
|
||||
|
||||
```python
|
||||
auxiliary_loss = Dense(1, activation='sigmoid', name='aux_output')(lstm_out)
|
||||
```
|
||||
|
||||
At this point, we feed into the model our auxiliary input data by concatenating it with the LSTM output:
|
||||
|
||||
```python
|
||||
auxiliary_input = Input(shape=(5,), name='aux_input')
|
||||
x = merge([lstm_out, auxiliary_input], mode='concat')
|
||||
|
||||
# we stack a deep fully-connected network on top
|
||||
x = Dense(64, activation='relu')(x)
|
||||
x = Dense(64, activation='relu')(x)
|
||||
x = Dense(64, activation='relu')(x)
|
||||
|
||||
# and finally we add the main logistic regression layer
|
||||
main_loss = Dense(1, activation='sigmoid', name='main_output')(x)
|
||||
```
|
||||
|
||||
This defines a model with two inputs and two outputs:
|
||||
|
||||
```python
|
||||
model = Model(input=[main_input, auxiliary_input], output=[main_loss, auxiliary_loss])
|
||||
```
|
||||
|
||||
We compile the model and assign a weight of 0.2 to the auxiliary loss.
|
||||
To specify different `loss_weight` or `loss` for each different output, you can use a list or a dictionary.
|
||||
Here we pass a single loss as the `loss` argument, so the same loss will be used on all outputs.
|
||||
|
||||
```python
|
||||
model.compile(optimizer='rmsprop', loss='binary_crossentropy',
|
||||
loss_weight=[1., 0.2])
|
||||
```
|
||||
|
||||
We can train the model by passing it lists of input arrays and target arrays:
|
||||
|
||||
```python
|
||||
model.fit([headline_data, additional_data], [labels, labels],
|
||||
nb_epoch=50, batch_size=32)
|
||||
```
|
||||
|
||||
Since our inputs and outputs are named (we passed them a "name" argument),
|
||||
We could also have compiled the model via:
|
||||
|
||||
```python
|
||||
model.compile(optimizer='rmsprop',
|
||||
loss={'main_output': 'binary_crossentropy', 'aux_output': 'binary_crossentropy'},
|
||||
loss_weight={'main_output': 1., 'aux_output': 0.2})
|
||||
|
||||
# and trained it via:
|
||||
model.fit({'main_input': headline_data, 'aux_input': additional_data},
|
||||
{'main_output': labels, 'aux_output': labels},
|
||||
nb_epoch=50, batch_size=32)
|
||||
```
|
||||
|
||||
-----
|
||||
|
||||
## Shared layers
|
||||
|
||||
Another good use for the functional API are models that use shared layers. Let's take a look at shared layers.
|
||||
|
||||
Let's consider a dataset of tweets. We want to build a model that can tell whether two tweets are from the same person or not (this can allow us to compare users by the similarity of their tweets, for instance).
|
||||
|
||||
One way to achieve this is to build a model that encodes two tweets into two vectors, concatenates the vectors and adds a logistic regression of top, outputting a probability that the two tweets share the same author. The model would then be trained on positive tweet pairs and negative tweet pairs.
|
||||
|
||||
Because the problem is symetric, the mechanism that encodes the first tweet should be reused (weights and all) to encode the second tweet. Here we use a shared LSTM layer to encode the tweets.
|
||||
|
||||
Let's build this with the functional API. We will take as input for a tweet a binary matrix of shape `(140, 256)`, i.e. a sequence of 140 vectors of size 256, where each dimension in the 256-dimensional vector encodes the presence/absence of a character (out of an alphabet of 256 frequent characters).
|
||||
|
||||
```python
|
||||
from keras.layers import Input, LSTM, Dense, merge
|
||||
from keras.models import Model
|
||||
|
||||
tweet_a = Input(shape=(140, 256))
|
||||
tweet_b = Input(shape=(140, 256))
|
||||
```
|
||||
|
||||
To share a layer across different inputs, simply instantiate the layer once, then call it on as many inputs as you want:
|
||||
|
||||
```python
|
||||
# this layer can take as input a matrix
|
||||
# and will return a vector of size 64
|
||||
shared_lstm = LSTM(64)
|
||||
|
||||
# when we reuse the same layer instance
|
||||
# multiple times, the weights of the layer
|
||||
# are also being reused
|
||||
# (it is effectively *the same* layer)
|
||||
encoded_a = shared_lstm(tweet_a)
|
||||
encoded_b = shared_lstm(tweet_b)
|
||||
|
||||
# we can then concatenate the two vectors:
|
||||
merged_vector = merge([encoded_a, encoded_b], mode='concat', concat_axis=-1)
|
||||
|
||||
# and add a logistic regression on top
|
||||
predictions = Dense(1, activation='sigmoid')(merged_vector)
|
||||
|
||||
# we define a trainable model linking the
|
||||
# tweet inputs to the predictions
|
||||
model = Model(input=[tweet_a, tweet_b], output=predictions)
|
||||
|
||||
model.compile(optimizer='rmsprop',
|
||||
loss='binary_crossentropy',
|
||||
metrics=['accuracy'])
|
||||
model.fit([data_a, data_b], labels, nb_epoch=10)
|
||||
```
|
||||
|
||||
Let's pause to take a look at how to read the shared layer's output or output shape.
|
||||
|
||||
-----
|
||||
|
||||
## The concept of layer "node"
|
||||
|
||||
Whenever you are calling a layer on some input, you are creating a new tensor (the output of the layer), and you are adding a "node" to the layer, linking the input tensor to the output tensor. When you are calling the same layer multiple times, that layer owns multiple nodes indexed as 0, 1, 2...
|
||||
|
||||
In previous versions of Keras, you could obtain the output tensor of a layer instance via `layer.get_output()`, or its output shape via `layer.output_shape`. You still can (except `get_output()` has been replaced by the property `output`). But what if a layer is connected to multiple inputs?
|
||||
|
||||
As long as a layer is only connected to one input, there is no confusion, and `.output` will return the one output of the layer:
|
||||
|
||||
```python
|
||||
a = Input(shape=(140, 256))
|
||||
|
||||
lstm = LSTM(32)
|
||||
encoded_a = lstm(a)
|
||||
|
||||
assert lstm.output == encoded_a
|
||||
```
|
||||
|
||||
Not so if the layer has multiple inputs:
|
||||
```python
|
||||
a = Input(shape=(140, 256))
|
||||
b = Input(shape=(140, 256))
|
||||
|
||||
lstm = LSTM(32)
|
||||
encoded_a = lstm(a)
|
||||
encoded_b = lstm(b)
|
||||
|
||||
lstm.output
|
||||
```
|
||||
```
|
||||
>> AssertionError: Layer lstm_1 has multiple inbound nodes,
|
||||
hence the notion of "layer output" is ill-defined.
|
||||
Use `get_output_at(node_index)` instead.
|
||||
```
|
||||
|
||||
Okay then. The following works:
|
||||
|
||||
```python
|
||||
assert lstm.get_output_at(0) == encoded_a
|
||||
assert lstm.get_output_at(1) == encoded_b
|
||||
```
|
||||
|
||||
Simple enough, right?
|
||||
|
||||
The same is true for the properties `input_shape` and `output_shape`: as long as the layer has only one node, or as long as all nodes have the same input/output shape, then the notion of "layer output/input shape" is well defined, and that one shape will be returned by `layer.output_shape`/`layer.input_shape`. But if, for instance, you apply a same `Convolution2D` layer to an input of shape `(3, 32, 32)`, and then to an input of shape `(3, 64, 64)`, the layer will have multiple input/output shapes, and you will have to fetch them by specifying the index of the node they belong to:
|
||||
|
||||
```python
|
||||
a = Input(shape=(3, 32, 32))
|
||||
b = Input(shape=(3, 64, 64))
|
||||
|
||||
conv = Convolution2D(16, 3, 3, border_mode='same')
|
||||
conved_a = conv(a)
|
||||
|
||||
# only one input so far, the following will work:
|
||||
assert conv.input_shape == (None, 3, 32, 32)
|
||||
|
||||
conved_b = conv(b)
|
||||
# now the `.input_shape` property wouldn't work, but this does:
|
||||
assert conv.get_input_shape_at(0) == (None, 3, 32, 32)
|
||||
assert conv.get_input_shape_at(1) == (None, 3, 64, 64)
|
||||
```
|
||||
|
||||
-----
|
||||
|
||||
## More examples
|
||||
|
||||
Code examples are still the best way to get started, so here are a few more.
|
||||
|
||||
### Inception module
|
||||
|
||||
For more information about the Inception architecture, see [Going Deeper with Convolutions](http://arxiv.org/abs/1409.4842).
|
||||
|
||||
```python
|
||||
from keras.layers import merge, Convolution2D, MaxPooling2D, Input
|
||||
|
||||
input_img = Input(shape=(3, 256, 256))
|
||||
|
||||
tower_1 = Convolution2D(64, 1, 1, border_mode='same', activation='relu')(input_img)
|
||||
tower_1 = Convolution2D(64, 3, 3, border_mode='same', activation='relu')(tower_1)
|
||||
|
||||
tower_2 = Convolution2D(64, 1, 1, border_mode='same', activation='relu')(input_img)
|
||||
tower_2 = Convolution2D(64, 5, 5, border_mode='same', activation='relu')(tower_2)
|
||||
|
||||
tower_3 = MaxPooling2D((3, 3), strides=(1, 1), border_mode='same')(input_img)
|
||||
tower_3 = Convolution2D(64, 1, 1, border_mode='same', activation='relu')(tower_3)
|
||||
|
||||
output = merge([tower_1, tower_2, tower_3], mode='concat', concat_axis=1)
|
||||
```
|
||||
|
||||
### Residual connection on a convolution layer
|
||||
|
||||
For more information about residual networks, see [Deep Residual Learning for Image Recognition](http://arxiv.org/abs/1512.03385).
|
||||
|
||||
```python
|
||||
from keras.layers import merge, Convolution2D, Input
|
||||
|
||||
# input tensor for a 3-channel 256x256 image
|
||||
x = Input(shape=(3, 256, 256))
|
||||
# 3x3 conv with 16 output channels
|
||||
y = Convolution2D(16, 3, 3, border_mode='same')
|
||||
# this returns x + y.
|
||||
z = merge([x, y], mode='sum')
|
||||
```
|
||||
|
||||
### Shared vision model
|
||||
|
||||
This model re-uses the same image-processing module on two inputs, to classify whether two MNIST digits are the same digit or different digits.
|
||||
|
||||
```python
|
||||
from keras.layers import merge, Convolution2D, MaxPooling2D, Input, Dense, Flatten
|
||||
from keras.models import Model
|
||||
|
||||
# first, define the vision modules
|
||||
digit_input = Input(shape=(1, 27, 27))
|
||||
x = Convolution2D(64, 3, 3)(digit_input)
|
||||
x = Convolution2D(64, 3, 3)(x)
|
||||
x = MaxPooling2D((2, 2))(x)
|
||||
out = Flatten()(x)
|
||||
|
||||
vision_model = Model(digit_input, out)
|
||||
|
||||
# then define the tell-digits-apart model
|
||||
digit_a = Input(shape=(1, 27, 27))
|
||||
digit_b = Input(shape=(1, 27, 27))
|
||||
|
||||
# the vision model will be shared, weights and all
|
||||
out_a = vision_model(digit_a)
|
||||
out_b = vision_model(digit_b)
|
||||
|
||||
concatenated = merge([out_a, out_b], mode='concat')
|
||||
out = Dense(1, activation='sigmoid')(concatenated)
|
||||
|
||||
classification_model = Model([digit_a, digit_b], out)
|
||||
```
|
||||
|
||||
### Visual question answering model
|
||||
|
||||
This model can select the correct one-word answer when asked a natural-language question about a picture.
|
||||
|
||||
It works by encoding the question into a vector, encoding the image into a vector, concatenating the two, and training on top a logistic regression over some vocabulary of potential answers.
|
||||
|
||||
```python
|
||||
from keras.layers import Convolution2D, MaxPooling2D, Flatten
|
||||
from keras.layers import Input, LSTM, Embedding, Dense, merge
|
||||
from keras.models import Model, Sequential
|
||||
|
||||
# first, let's define a vision model using a Sequential model.
|
||||
# this model will encode an image into a vector.
|
||||
vision_model = Sequential()
|
||||
vision_model.add(Convolution2D(64, 3, 3, activation='relu', border_mode='same', input_shape=(3, 224, 224)))
|
||||
vision_model.add(Convolution2D(64, 3, 3, activation='relu'))
|
||||
vision_model.add(MaxPooling2D((2, 2)))
|
||||
vision_model.add(Convolution2D(128, 3, 3, activation='relu', border_mode='same'))
|
||||
vision_model.add(Convolution2D(128, 3, 3, activation='relu'))
|
||||
vision_model.add(MaxPooling2D((2, 2)))
|
||||
vision_model.add(Convolution2D(256, 3, 3, activation='relu', border_mode='same'))
|
||||
vision_model.add(Convolution2D(256, 3, 3, activation='relu'))
|
||||
vision_model.add(Convolution2D(256, 3, 3, activation='relu'))
|
||||
vision_model.add(MaxPooling2D((2, 2)))
|
||||
vision_model.add(Flatten())
|
||||
|
||||
# now let's get a tensor with the output of our vision model:
|
||||
image_input = Input(shape=(3, 224, 224))
|
||||
encoded_image = vision_model(image_input)
|
||||
|
||||
# next, let's define a language model to encode the question into a vector.
|
||||
# each question will be at most 100 word long,
|
||||
# and we will index words as integers from 1 to 9999.
|
||||
question_input = Input(shape=(100,), dtype='int32')
|
||||
embedded_question = Embedding(input_dim=10000, output_dim=256, input_length=100)(question_input)
|
||||
encoded_question = LSTM(256)(embedded_question)
|
||||
|
||||
# let's concatenate the question vector and the image vector:
|
||||
merged = merge([encoded_question, encoded_image], mode='concat')
|
||||
|
||||
# and let's train a logistic regression over 1000 words on top:
|
||||
output = Dense(1000, activation='softmax')(merged)
|
||||
|
||||
# this is our final model:
|
||||
vqa_model = Model(input=[image_input, question_input], output=output)
|
||||
|
||||
# the next stage would be training this model on actual data.
|
||||
```
|
||||
|
||||
### Video question answering model
|
||||
|
||||
Now that we have trained our image QA model, we can quickly turn it into a video QA model. With appropriate training, you will be able to show it a short video (e.g. 100-frame human action) and ask a natural language question about the video (e.g. "what sport is the boy playing?" -> "football").
|
||||
|
||||
```python
|
||||
from keras.layers import TimeDistributed
|
||||
|
||||
video_input = Input(shape=(100, 3, 224, 224))
|
||||
# this is our video encoded via the previously trained vision_model (weights are reused)
|
||||
encoded_frame_sequence = TimeDistributed(vision_model)(video_input) # the output will be a sequence of vectors
|
||||
encoded_video = LSTM(256)(encoded_frame_sequence) # the output will be a vector
|
||||
|
||||
# this is a model-level representation of the question encoder, reusing the same weights as before:
|
||||
question_encoder = Model(input=question_input, output=encoded_question)
|
||||
|
||||
# let's use it to encode the question:
|
||||
video_question_input = Input(shape=(100,), dtype='int32')
|
||||
encoded_video_question = question_encoder(video_question_input)
|
||||
|
||||
# and this is our video question answering model:
|
||||
merged = merge([encoded_video, encoded_video_question], mode='concat')
|
||||
output = Dense(1000, activation='softmax')(merged)
|
||||
video_qa_model = Model(input=[video_input, video_question_input], output=output)
|
||||
```
|
||||
@@ -0,0 +1,541 @@
|
||||
# Getting started with the Keras Sequential model
|
||||
|
||||
The `Sequential` model is a linear stack of layers.
|
||||
|
||||
You can create a `Sequential` model by passing a list of layer instances to the constructor:
|
||||
|
||||
```python
|
||||
from keras.models import Sequential
|
||||
|
||||
model = Sequential([
|
||||
Dense(32, input_dim=784),
|
||||
Activation('relu'),
|
||||
Dense(10),
|
||||
Activation('softmax'),
|
||||
])
|
||||
```
|
||||
|
||||
You can also simply add layers via the `.add()` method:
|
||||
|
||||
```python
|
||||
model = Sequential()
|
||||
model.add(Dense(32, input_dim=784))
|
||||
model.add(Activation('relu'))
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
## Specifying the input shape
|
||||
|
||||
The model needs to know what input shape it should expect. For this reason, the first layer in a `Sequential` model (and only the first, because following layers can do automatic shape inference) needs to receive information about its input shape. There are several possible ways to do this:
|
||||
|
||||
- pass an `input_shape` argument to the first layer. This is a shape tuple (a tuple of integers or `None` entries, where `None` indicates that any positive integer may be expected). In `input_shape`, the batch dimension is not included.
|
||||
- pass instead a `batch_input_shape` argument, where the batch dimension is included. This is useful for specifying a fixed batch size (e.g. with stateful RNNs).
|
||||
- some 2D layers, such as `Dense`, support the specification of their input shape via the argument `input_dim`, and some 3D temporal layers support the arguments `input_dim` and `input_length`.
|
||||
|
||||
As such, the following three snippets are strictly equivalent:
|
||||
```python
|
||||
model = Sequential()
|
||||
model.add(Dense(32, input_shape=(784,)))
|
||||
```
|
||||
```python
|
||||
model = Sequential()
|
||||
model.add(Dense(32, batch_input_shape=(None, 784)))
|
||||
# note that batch dimension is "None" here,
|
||||
# so the model will be able to process batches of any size.
|
||||
```
|
||||
```python
|
||||
model = Sequential()
|
||||
model.add(Dense(32, input_dim=784))
|
||||
```
|
||||
|
||||
And so are the following three snippets:
|
||||
```python
|
||||
model = Sequential()
|
||||
model.add(LSTM(32, input_shape=(10, 64)))
|
||||
```
|
||||
```python
|
||||
model = Sequential()
|
||||
model.add(LSTM(32, batch_input_shape=(None, 10, 64)))
|
||||
```
|
||||
```python
|
||||
model = Sequential()
|
||||
model.add(LSTM(32, input_length=10, input_dim=64))
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
## The Merge layer
|
||||
|
||||
Multiple `Sequential` instances can be merged into a single output via a `Merge` layer. The output is a layer that can be added as first layer in a new `Sequential` model. For instance, here's a model with two separate input branches getting merged:
|
||||
|
||||
```python
|
||||
from keras.layers import Merge
|
||||
|
||||
left_branch = Sequential()
|
||||
left_branch.add(Dense(32, input_dim=784))
|
||||
|
||||
right_branch = Sequential()
|
||||
right_branch.add(Dense(32, input_dim=784))
|
||||
|
||||
merged = Merge([left_branch, right_branch], mode='concat')
|
||||
|
||||
final_model = Sequential()
|
||||
final_model.add(merged)
|
||||
final_model.add(Dense(10, activation='softmax'))
|
||||
```
|
||||
|
||||
<img src="http://s3.amazonaws.com/keras.io/img/two_branches_sequential_model.png" alt="two branch Sequential" style="width: 400px;"/>
|
||||
|
||||
The `Merge` layer supports a number of pre-defined modes:
|
||||
|
||||
- `sum` (default): element-wise sum
|
||||
- `concat`: tensor concatenation. You can specify the concatenation axis via the argument `concat_axis`.
|
||||
- `mul`: element-wise multiplication
|
||||
- `ave`: tensor average
|
||||
- `dot`: dot product. You can specify which axes to reduce along via the argument `dot_axes`.
|
||||
- `cos`: cosine proximity between vectors in 2D tensors.
|
||||
|
||||
You can also pass a function as the `mode` argument, allowing for arbitrary transformations:
|
||||
|
||||
```python
|
||||
merged = Merge([left_branch, right_branch], mode=lambda x, y: x - y)
|
||||
```
|
||||
|
||||
Now you know enough to be able to define *almost* any model with Keras. For complex models that cannot be expressed via `Sequential` and `Merge`, you can use [the functional API](/getting-started/functional-api-guide).
|
||||
|
||||
|
||||
----
|
||||
|
||||
## Compilation
|
||||
|
||||
Before training a model, you need to configure the learning process, which is done via the `compile` method. It receives three arguments:
|
||||
|
||||
- an optimizer. This could be the string identifier of an existing optimizer (such as `rmsprop` or `adagrad`), or an instance of the `Optimizer` class. See: [optimizers](/optimizers).
|
||||
- a loss function. This is the objective that the model will try to minimize. If can be the string identifier of an existing loss function (such as `categorical_crossentropy` or `mse`), or it can be an objective function. See: [objectives](/objectives).
|
||||
- a list of metrics. For any classification problem you will want to set this to `metrics=['accuracy']`. A metric could be the string identifier of an existing metric (only `accuracy` is supported at this point), or a custom metric function.
|
||||
|
||||
```python
|
||||
# for a multi-class classification problem
|
||||
model.compile(optimizer='rmsprop',
|
||||
loss='categorical_crossentropy',
|
||||
metrics=['accuracy'])
|
||||
|
||||
# for a binary classification problem
|
||||
model.compile(optimizer='rmsprop',
|
||||
loss='binary_crossentropy',
|
||||
metrics=['accuracy'])
|
||||
|
||||
# for a mean squared error regression problem
|
||||
model.compile(optimizer='rmsprop',
|
||||
loss='mse')
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
## Training
|
||||
|
||||
Keras models are trained on Numpy arrays of input data and labels. For training a model, you will typically use the `fit` function. [Read its documentation here](/models/sequential).
|
||||
|
||||
```python
|
||||
# for a single-input model with 2 classes (binary):
|
||||
|
||||
model = Sequential()
|
||||
model.add(Dense(1, input_dim=784, activation='softmax'))
|
||||
model.compile(optimizer='rmsprop',
|
||||
loss='binary_crossentropy',
|
||||
metrics=['accuracy'])
|
||||
|
||||
# generate dummy data
|
||||
import numpy as np
|
||||
data = np.random.random((1000, 784))
|
||||
labels = np.random.randint(2, size=(1000, 1))
|
||||
|
||||
# train the model, iterating on the data in batches
|
||||
# of 32 samples
|
||||
model.fit(data, labels, nb_epoch=10, batch_size=32)
|
||||
```
|
||||
```python
|
||||
# for a multi-input model with 10 classes:
|
||||
|
||||
left_branch = Sequential()
|
||||
left_branch.add(Dense(32, input_dim=784))
|
||||
|
||||
right_branch = Sequential()
|
||||
right_branch.add(Dense(32, input_dim=784))
|
||||
|
||||
merged = Merge([left_branch, right_branch], mode='concat')
|
||||
|
||||
model = Sequential()
|
||||
model.add(merged)
|
||||
model.add(Dense(10, activation='softmax'))
|
||||
|
||||
model.compile(optimizer='rmsprop',
|
||||
loss='categorical_crossentropy',
|
||||
metrics=['accuracy'])
|
||||
|
||||
# generate dummy data
|
||||
import numpy as np
|
||||
from keras.utils.np_utils import to_categorical
|
||||
data_1 = np.random.random((1000, 784))
|
||||
data_2 = np.random.random((1000, 784))
|
||||
|
||||
# these are integers between 0 and 9
|
||||
labels = np.random.randint(10, size=(1000, 1))
|
||||
# we convert the labels to a binary matrix of size (1000, 10)
|
||||
# for use with categorical_crossentropy
|
||||
labels = to_categorical(labels, 10)
|
||||
|
||||
# train the model
|
||||
# note that we are passing a list of Numpy arrays as training data
|
||||
# since the model has 2 inputs
|
||||
model.fit([data_1, data_2], labels, nb_epoch=10, batch_size=32)
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
Here are a few examples to get you started!
|
||||
|
||||
In the examples folder, you will also find example models for real datasets:
|
||||
|
||||
- CIFAR10 small images classification: Convolutional Neural Network (CNN) with realtime data augmentation
|
||||
- IMDB movie review sentiment classification: LSTM over sequences of words
|
||||
- Reuters newswires topic classification: Multilayer Perceptron (MLP)
|
||||
- MNIST handwritten digits classification: MLP & CNN
|
||||
- Character-level text generation with LSTM
|
||||
|
||||
...and more.
|
||||
|
||||
|
||||
### Multilayer Perceptron (MLP) for multi-class softmax classification:
|
||||
|
||||
```python
|
||||
from keras.models import Sequential
|
||||
from keras.layers import Dense, Dropout, Activation
|
||||
from keras.optimizers import SGD
|
||||
|
||||
model = Sequential()
|
||||
# Dense(64) is a fully-connected layer with 64 hidden units.
|
||||
# in the first layer, you must specify the expected input data shape:
|
||||
# here, 20-dimensional vectors.
|
||||
model.add(Dense(64, input_dim=20, init='uniform'))
|
||||
model.add(Activation('tanh'))
|
||||
model.add(Dropout(0.5))
|
||||
model.add(Dense(64, init='uniform'))
|
||||
model.add(Activation('tanh'))
|
||||
model.add(Dropout(0.5))
|
||||
model.add(Dense(10, init='uniform'))
|
||||
model.add(Activation('softmax'))
|
||||
|
||||
sgd = SGD(lr=0.1, decay=1e-6, momentum=0.9, nesterov=True)
|
||||
model.compile(loss='categorical_crossentropy',
|
||||
optimizer=sgd,
|
||||
metrics=['accuracy'])
|
||||
|
||||
model.fit(X_train, y_train,
|
||||
nb_epoch=20,
|
||||
batch_size=16)
|
||||
score = model.evaluate(X_test, y_test, batch_size=16)
|
||||
```
|
||||
|
||||
|
||||
### Alternative implementation of a similar MLP:
|
||||
|
||||
```python
|
||||
model = Sequential()
|
||||
model.add(Dense(64, input_dim=20, activation='relu'))
|
||||
model.add(Dropout(0.5))
|
||||
model.add(Dense(64, activation='relu'))
|
||||
model.add(Dropout(0.5))
|
||||
model.add(Dense(10, activation='softmax'))
|
||||
|
||||
model.compile(loss='categorical_crossentropy',
|
||||
optimizer='adadelta',
|
||||
metrics=['accuracy'])
|
||||
```
|
||||
|
||||
|
||||
### MLP for binary classification:
|
||||
```python
|
||||
model = Sequential()
|
||||
model.add(Dense(64, input_dim=20, init='uniform', activation='relu'))
|
||||
model.add(Dropout(0.5))
|
||||
model.add(Dense(64, activation='relu'))
|
||||
model.add(Dropout(0.5))
|
||||
model.add(Dense(1, activation='sigmoid'))
|
||||
|
||||
model.compile(loss='binary_crossentropy',
|
||||
optimizer='rmsprop',
|
||||
metrics=['accuracy'])
|
||||
```
|
||||
|
||||
|
||||
### VGG-like convnet:
|
||||
|
||||
```python
|
||||
from keras.models import Sequential
|
||||
from keras.layers import Dense, Dropout, Activation, Flatten
|
||||
from keras.layers import Convolution2D, MaxPooling2D
|
||||
from keras.optimizers import SGD
|
||||
|
||||
model = Sequential()
|
||||
# input: 100x100 images with 3 channels -> (3, 100, 100) tensors.
|
||||
# this applies 32 convolution filters of size 3x3 each.
|
||||
model.add(Convolution2D(32, 3, 3, border_mode='valid', input_shape=(3, 100, 100)))
|
||||
model.add(Activation('relu'))
|
||||
model.add(Convolution2D(32, 3, 3))
|
||||
model.add(Activation('relu'))
|
||||
model.add(MaxPooling2D(pool_size=(2, 2)))
|
||||
model.add(Dropout(0.25))
|
||||
|
||||
model.add(Convolution2D(64, 3, 3, border_mode='valid'))
|
||||
model.add(Activation('relu'))
|
||||
model.add(Convolution2D(64, 3, 3))
|
||||
model.add(Activation('relu'))
|
||||
model.add(MaxPooling2D(pool_size=(2, 2)))
|
||||
model.add(Dropout(0.25))
|
||||
|
||||
model.add(Flatten())
|
||||
# Note: Keras does automatic shape inference.
|
||||
model.add(Dense(256))
|
||||
model.add(Activation('relu'))
|
||||
model.add(Dropout(0.5))
|
||||
|
||||
model.add(Dense(10))
|
||||
model.add(Activation('softmax'))
|
||||
|
||||
sgd = SGD(lr=0.1, decay=1e-6, momentum=0.9, nesterov=True)
|
||||
model.compile(loss='categorical_crossentropy', optimizer=sgd)
|
||||
|
||||
model.fit(X_train, Y_train, batch_size=32, nb_epoch=1)
|
||||
```
|
||||
|
||||
|
||||
### Sequence classification with LSTM:
|
||||
|
||||
```python
|
||||
from keras.models import Sequential
|
||||
from keras.layers import Dense, Dropout, Activation
|
||||
from keras.layers import Embedding
|
||||
from keras.layers import LSTM
|
||||
|
||||
model = Sequential()
|
||||
model.add(Embedding(max_features, 256, input_length=maxlen))
|
||||
model.add(LSTM(output_dim=128, activation='sigmoid', inner_activation='hard_sigmoid'))
|
||||
model.add(Dropout(0.5))
|
||||
model.add(Dense(1))
|
||||
model.add(Activation('sigmoid'))
|
||||
|
||||
model.compile(loss='binary_crossentropy',
|
||||
optimizer='rmsprop',
|
||||
metrics=['accuracy'])
|
||||
|
||||
model.fit(X_train, Y_train, batch_size=16, nb_epoch=10)
|
||||
score = model.evaluate(X_test, Y_test, batch_size=16)
|
||||
```
|
||||
|
||||
### Architecture for learning image captions with a convnet and a Gated Recurrent Unit:
|
||||
(word-level embedding, caption of maximum length 16 words).
|
||||
|
||||
Note that getting this to work well will require using a bigger convnet, initialized with pre-trained weights.
|
||||
|
||||
```python
|
||||
max_caption_len = 16
|
||||
vocab_size = 10000
|
||||
|
||||
# first, let's define an image model that
|
||||
# will encode pictures into 128-dimensional vectors.
|
||||
# it should be initialized with pre-trained weights.
|
||||
image_model = Sequential()
|
||||
image_model.add(Convolution2D(32, 3, 3, border_mode='valid', input_shape=(3, 100, 100)))
|
||||
image_model.add(Activation('relu'))
|
||||
image_model.add(Convolution2D(32, 3, 3))
|
||||
image_model.add(Activation('relu'))
|
||||
image_model.add(MaxPooling2D(pool_size=(2, 2)))
|
||||
|
||||
image_model.add(Convolution2D(64, 3, 3, border_mode='valid'))
|
||||
image_model.add(Activation('relu'))
|
||||
image_model.add(Convolution2D(64, 3, 3))
|
||||
image_model.add(Activation('relu'))
|
||||
image_model.add(MaxPooling2D(pool_size=(2, 2)))
|
||||
|
||||
image_model.add(Flatten())
|
||||
image_model.add(Dense(128))
|
||||
|
||||
# let's load the weights from a save file.
|
||||
image_model.load_weights('weight_file.h5')
|
||||
|
||||
# next, let's define a RNN model that encodes sequences of words
|
||||
# into sequences of 128-dimensional word vectors.
|
||||
language_model = Sequential()
|
||||
language_model.add(Embedding(vocab_size, 256, input_length=max_caption_len))
|
||||
language_model.add(GRU(output_dim=128, return_sequences=True))
|
||||
language_model.add(TimeDistributedDense(128))
|
||||
|
||||
# let's repeat the image vector to turn it into a sequence.
|
||||
image_model.add(RepeatVector(max_caption_len))
|
||||
|
||||
# the output of both models will be tensors of shape (samples, max_caption_len, 128).
|
||||
# let's concatenate these 2 vector sequences.
|
||||
model = Sequential()
|
||||
model.add(Merge([image_model, language_model], mode='concat', concat_axis=-1))
|
||||
# let's encode this vector sequence into a single vector
|
||||
model.add(GRU(256, return_sequences=False))
|
||||
# which will be used to compute a probability
|
||||
# distribution over what the next word in the caption should be!
|
||||
model.add(Dense(vocab_size))
|
||||
model.add(Activation('softmax'))
|
||||
|
||||
model.compile(loss='categorical_crossentropy', optimizer='rmsprop')
|
||||
|
||||
# "images" is a numpy float array of shape (nb_samples, nb_channels=3, width, height).
|
||||
# "captions" is a numpy integer array of shape (nb_samples, max_caption_len)
|
||||
# containing word index sequences representing partial captions.
|
||||
# "next_words" is a numpy float array of shape (nb_samples, vocab_size)
|
||||
# containing a categorical encoding (0s and 1s) of the next word in the corresponding
|
||||
# partial caption.
|
||||
model.fit([images, partial_captions], next_words, batch_size=16, nb_epoch=100)
|
||||
```
|
||||
|
||||
|
||||
### Stacked LSTM for sequence classification
|
||||
|
||||
In this model, we stack 3 LSTM layers on top of each other,
|
||||
making the model capable of learning higher-level temporal representations.
|
||||
|
||||
The first two LSTMs return their full output sequences, but the last one only returns
|
||||
the last step in its output sequence, thus dropping the temporal dimension
|
||||
(i.e. converting the input sequence into a single vector).
|
||||
|
||||
<img src="http://keras.io/img/regular_stacked_lstm.png" alt="stacked LSTM" style="width: 300px;"/>
|
||||
|
||||
```python
|
||||
from keras.models import Sequential
|
||||
from keras.layers import LSTM, Dense
|
||||
import numpy as np
|
||||
|
||||
data_dim = 16
|
||||
timesteps = 8
|
||||
nb_classes = 10
|
||||
|
||||
# expected input data shape: (batch_size, timesteps, data_dim)
|
||||
model = Sequential()
|
||||
model.add(LSTM(32, return_sequences=True,
|
||||
input_shape=(timesteps, data_dim))) # returns a sequence of vectors of dimension 32
|
||||
model.add(LSTM(32, return_sequences=True)) # returns a sequence of vectors of dimension 32
|
||||
model.add(LSTM(32)) # return a single vector of dimension 32
|
||||
model.add(Dense(10, activation='softmax'))
|
||||
|
||||
model.compile(loss='categorical_crossentropy',
|
||||
optimizer='rmsprop',
|
||||
metrics=['accuracy'])
|
||||
|
||||
# generate dummy training data
|
||||
x_train = np.random.random((1000, timesteps, data_dim))
|
||||
y_train = np.random.random((1000, nb_classes))
|
||||
|
||||
# generate dummy validation data
|
||||
x_val = np.random.random((100, timesteps, data_dim))
|
||||
y_val = np.random.random((100, nb_classes))
|
||||
|
||||
model.fit(x_train, y_train,
|
||||
batch_size=64, nb_epoch=5,
|
||||
validation_data=(x_val, y_val))
|
||||
```
|
||||
|
||||
|
||||
### Same stacked LSTM model, rendered "stateful"
|
||||
|
||||
A stateful recurrent model is one for which the internal states (memories) obtained after processing a batch
|
||||
of samples are reused as initial states for the samples of the next batch. This allows to process longer sequences
|
||||
while keeping computational complexity manageable.
|
||||
|
||||
[You can read more about stateful RNNs in the FAQ.](/faq/#how-can-i-use-stateful-rnns)
|
||||
|
||||
```python
|
||||
from keras.models import Sequential
|
||||
from keras.layers import LSTM, Dense
|
||||
import numpy as np
|
||||
|
||||
data_dim = 16
|
||||
timesteps = 8
|
||||
nb_classes = 10
|
||||
batch_size = 32
|
||||
|
||||
# expected input batch shape: (batch_size, timesteps, data_dim)
|
||||
# note that we have to provide the full batch_input_shape since the network is stateful.
|
||||
# the sample of index i in batch k is the follow-up for the sample i in batch k-1.
|
||||
model = Sequential()
|
||||
model.add(LSTM(32, return_sequences=True, stateful=True,
|
||||
batch_input_shape=(batch_size, timesteps, data_dim)))
|
||||
model.add(LSTM(32, return_sequences=True, stateful=True))
|
||||
model.add(LSTM(32, stateful=True))
|
||||
model.add(Dense(10, activation='softmax'))
|
||||
|
||||
model.compile(loss='categorical_crossentropy',
|
||||
optimizer='rmsprop',
|
||||
metrics=['accuracy'])
|
||||
|
||||
# generate dummy training data
|
||||
x_train = np.random.random((batch_size * 10, timesteps, data_dim))
|
||||
y_train = np.random.random((batch_size * 10, nb_classes))
|
||||
|
||||
# generate dummy validation data
|
||||
x_val = np.random.random((batch_size * 3, timesteps, data_dim))
|
||||
y_val = np.random.random((batch_size * 3, nb_classes))
|
||||
|
||||
model.fit(x_train, y_train,
|
||||
batch_size=batch_size, nb_epoch=5,
|
||||
validation_data=(x_val, y_val))
|
||||
```
|
||||
|
||||
|
||||
### Two merged LSTM encoders for classification over two parallel sequences
|
||||
|
||||
In this model, two input sequences are encoded into vectors by two separate LSTM modules.
|
||||
|
||||
These two vectors are then concatenated, and a fully connected network is trained on top of the concatenated representations.
|
||||
|
||||
<img src="http://keras.io/img/dual_lstm.png" alt="Dual LSTM" style="width: 600px;"/>
|
||||
|
||||
```python
|
||||
from keras.models import Sequential
|
||||
from keras.layers import Merge, LSTM, Dense
|
||||
import numpy as np
|
||||
|
||||
data_dim = 16
|
||||
timesteps = 8
|
||||
nb_classes = 10
|
||||
|
||||
encoder_a = Sequential()
|
||||
encoder_a.add(LSTM(32, input_shape=(timesteps, data_dim)))
|
||||
|
||||
encoder_b = Sequential()
|
||||
encoder_b.add(LSTM(32, input_shape=(timesteps, data_dim)))
|
||||
|
||||
decoder = Sequential()
|
||||
decoder.add(Merge([encoder_a, encoder_b], mode='concat'))
|
||||
decoder.add(Dense(32, activation='relu'))
|
||||
decoder.add(Dense(nb_classes, activation='softmax'))
|
||||
|
||||
decoder.compile(loss='categorical_crossentropy',
|
||||
optimizer='rmsprop',
|
||||
metrics=['accuracy'])
|
||||
|
||||
# generate dummy training data
|
||||
x_train_a = np.random.random((1000, timesteps, data_dim))
|
||||
x_train_b = np.random.random((1000, timesteps, data_dim))
|
||||
y_train = np.random.random((1000, nb_classes))
|
||||
|
||||
# generate dummy validation data
|
||||
x_val_a = np.random.random((100, timesteps, data_dim))
|
||||
x_val_b = np.random.random((100, timesteps, data_dim))
|
||||
y_val = np.random.random((100, nb_classes))
|
||||
|
||||
decoder.fit([x_train_a, x_train_b], y_train,
|
||||
batch_size=64, nb_epoch=5,
|
||||
validation_data=([x_val_a, x_val_b], y_val))
|
||||
```
|
||||
externo
+12
-14
@@ -33,11 +33,12 @@ Keras is compatible with: __Python 2.7-3.5__.
|
||||
------------------
|
||||
|
||||
|
||||
|
||||
## Getting started: 30 seconds to Keras
|
||||
|
||||
The core datastructure of Keras is a __model__, a way to organize layers. There are two types of models: [`Sequential`](http://keras.io/models/#sequential) and [`Graph`](http://keras.io/models/#graph).
|
||||
The core data structure of Keras is a __model__, a way to organize layers. The main type of model is the [`Sequential`](http://keras.io/getting-started/sequential-model-guide) model, a linear stack of layers. For more complex architectures, you should use the [Keras function API](http://keras.io/getting-started/functional-api-guide).
|
||||
|
||||
Here's the `Sequential` model (a linear pile of layers):
|
||||
Here's the `Sequential` model:
|
||||
|
||||
```python
|
||||
from keras.models import Sequential
|
||||
@@ -50,15 +51,15 @@ Stacking layers is as easy as `.add()`:
|
||||
```python
|
||||
from keras.layers.core import Dense, Activation
|
||||
|
||||
model.add(Dense(output_dim=64, input_dim=100, init="glorot_uniform"))
|
||||
model.add(Dense(output_dim=64, input_dim=100))
|
||||
model.add(Activation("relu"))
|
||||
model.add(Dense(output_dim=10, init="glorot_uniform"))
|
||||
model.add(Dense(output_dim=10))
|
||||
model.add(Activation("softmax"))
|
||||
```
|
||||
|
||||
Once your model looks good, configure its learning process with `.compile()`:
|
||||
```python
|
||||
model.compile(loss='categorical_crossentropy', optimizer='sgd')
|
||||
model.compile(loss='categorical_crossentropy', optimizer='sgd', metrics=['accuracy'])
|
||||
```
|
||||
|
||||
If you need to, you can further configure your optimizer. A core principle of Keras is to make things reasonably simple, while allowing the user to be fully in control when they need to (the ultimate control being the easy extensibility of the source code).
|
||||
@@ -79,7 +80,7 @@ model.train_on_batch(X_batch, Y_batch)
|
||||
|
||||
Evaluate your performance in one line:
|
||||
```python
|
||||
objective_score = model.evaluate(X_test, Y_test, batch_size=32)
|
||||
loss_and_metrics = model.evaluate(X_test, Y_test, batch_size=32)
|
||||
```
|
||||
|
||||
Or generate predictions on new data:
|
||||
@@ -88,12 +89,14 @@ classes = model.predict_classes(X_test, batch_size=32)
|
||||
proba = model.predict_proba(X_test, batch_size=32)
|
||||
```
|
||||
|
||||
Building a network of LSTMs, a deep CNN, a Neural Turing Machine, a word2vec embedder or any other model is just as fast. The ideas behind deep learning are simple, so why should their implementation be painful?
|
||||
Building a question answering system, an image classification model, a Neural Turing Machine, a word2vec embedder or any other model is just as fast. The ideas behind deep learning are simple, so why should their implementation be painful?
|
||||
|
||||
Have a look at these [starter examples](http://keras.io/examples/).
|
||||
For a more in-depth tutorial about Keras, you can check out:
|
||||
|
||||
In the [examples folder](https://github.com/fchollet/keras/tree/master/examples) of the repo, you will find more advanced models: question-answering with memory networks, text generation with stacked LSTMs, neural turing machines, etc.
|
||||
- [Getting started with the Sequential model](http://keras.io/getting-started/sequential-model-guide)
|
||||
- [Getting started with the functional API](http://keras.io/getting-started/functional-api-guide)
|
||||
|
||||
In the [examples folder](https://github.com/fchollet/keras/tree/master/examples) of the repository, you will find more advanced models: question-answering with memory networks, text generation with stacked LSTMs, etc.
|
||||
|
||||
------------------
|
||||
|
||||
@@ -112,11 +115,6 @@ Keras uses the following dependencies:
|
||||
- Theano
|
||||
- [See installation instructions](http://deeplearning.net/software/theano/install.html#install).
|
||||
|
||||
**Note**: You should use the latest version of Theano, not the PyPI version. Install it with:
|
||||
```
|
||||
sudo pip install git+git://github.com/Theano/Theano.git
|
||||
```
|
||||
|
||||
*When using the TensorFlow backend:*
|
||||
|
||||
- TensorFlow
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# About Keras layers
|
||||
|
||||
All Keras layers have a number of methods in common:
|
||||
|
||||
- `layer.get_weights()`: returns the weights of the layer as a list of Numpy arrays.
|
||||
- `layer.set_weights(weights)`: sets the weights of the layer from a list of Numpy arrays (with the same shapes as the output of `get_weights`).
|
||||
- `layer.get_config()`: returns a dictionary containing the configuration of the layer. The layer can be reinstantiated from its config via:
|
||||
```python
|
||||
from keras.utils.layer_utils import layer_from_config
|
||||
|
||||
config = layer.get_config()
|
||||
layer = layer_from_config(config)
|
||||
```
|
||||
|
||||
If a layer has a single node (i.e. if it isn't a shared layer), you can get its input tensor, output tensor, input shape and output shape via:
|
||||
|
||||
- `layer.input`
|
||||
- `layer.output`
|
||||
- `layer.input_shape`
|
||||
- `layer.output_shape`
|
||||
|
||||
If the layer has multiple nodes (see: [the concept of layer node and shared layers](/getting-started/functional-api-guide/#the-concept-of-layer-node)), you can use the following methods:
|
||||
|
||||
- `layer.get_input_at(node_index)`
|
||||
- `layer.get_output_at(node_index)`
|
||||
- `layer.get_input_shape_at(node_index)`
|
||||
- `layer.get_output_shape_at(node_index)`
|
||||
externo
-114
@@ -1,114 +0,0 @@
|
||||
Keras has two models: __Sequential__, a linear stack of layers, and __Graph__, a directed acyclic graph of layers.
|
||||
|
||||
# Using the Sequential model
|
||||
|
||||
```python
|
||||
from keras.models import Sequential
|
||||
from keras.layers.core import Dense, Dropout, Activation
|
||||
from keras.optimizers import SGD
|
||||
|
||||
model = Sequential()
|
||||
model.add(Dense(2, init='uniform', input_dim=64))
|
||||
model.add(Activation('softmax'))
|
||||
|
||||
model.compile(optimizer='sgd', loss='mse')
|
||||
|
||||
'''
|
||||
Train the model for 3 epochs, in batches of 16 samples,
|
||||
on data stored in the Numpy array X_train,
|
||||
and labels stored in the Numpy array y_train:
|
||||
'''
|
||||
model.fit(X_train, y_train, nb_epoch=3, batch_size=16, verbose=1)
|
||||
'''
|
||||
What you will see with mode verbose=1:
|
||||
Train on 37800 samples, validate on 4200 samples
|
||||
Epoch 0
|
||||
37800/37800 [==============================] - 7s - loss: 0.0385
|
||||
Epoch 1
|
||||
37800/37800 [==============================] - 8s - loss: 0.0140
|
||||
Epoch 2
|
||||
10960/37800 [=======>......................] - ETA: 4s - loss: 0.0109
|
||||
'''
|
||||
|
||||
model.fit(X_train, y_train, nb_epoch=3, batch_size=16, verbose=2)
|
||||
'''
|
||||
What you will see with mode verbose=2:
|
||||
Train on 37800 samples, validate on 4200 samples
|
||||
Epoch 0
|
||||
loss: 0.0190
|
||||
Epoch 1
|
||||
loss: 0.0146
|
||||
Epoch 2
|
||||
loss: 0.0049
|
||||
'''
|
||||
|
||||
'''
|
||||
Demonstration of the show_accuracy argument
|
||||
'''
|
||||
model.fit(X_train, y_train, nb_epoch=3, batch_size=16, verbose=2, show_accuracy=True)
|
||||
'''
|
||||
Train on 37800 samples, validate on 4200 samples
|
||||
Epoch 0
|
||||
loss: 0.0190 - acc.: 0.8750
|
||||
Epoch 1
|
||||
loss: 0.0146 - acc.: 0.8750
|
||||
Epoch 2
|
||||
loss: 0.0049 - acc.: 1.0000
|
||||
'''
|
||||
|
||||
'''
|
||||
Demonstration of the validation_split argument
|
||||
'''
|
||||
model.fit(X_train, y_train, nb_epoch=3, batch_size=16,
|
||||
validation_split=0.1, show_accuracy=True, verbose=1)
|
||||
'''
|
||||
Train on 37800 samples, validate on 4200 samples
|
||||
Epoch 0
|
||||
37800/37800 [==============================] - 7s - loss: 0.0385 - acc.: 0.7258 - val. loss: 0.0160 - val. acc.: 0.9136
|
||||
Epoch 1
|
||||
37800/37800 [==============================] - 8s - loss: 0.0140 - acc.: 0.9265 - val. loss: 0.0109 - val. acc.: 0.9383
|
||||
Epoch 2
|
||||
10960/37800 [=======>......................] - ETA: 4s - loss: 0.0109 - acc.: 0.9420
|
||||
'''
|
||||
```
|
||||
|
||||
# Using the Graph model
|
||||
|
||||
```python
|
||||
# graph model with one input and two outputs
|
||||
graph = Graph()
|
||||
graph.add_input(name='input', input_shape=(32,))
|
||||
graph.add_node(Dense(16), name='dense1', input='input')
|
||||
graph.add_node(Dense(4), name='dense2', input='input')
|
||||
graph.add_node(Dense(4), name='dense3', input='dense1')
|
||||
graph.add_output(name='output1', input='dense2')
|
||||
graph.add_output(name='output2', input='dense3')
|
||||
|
||||
graph.compile(optimizer='rmsprop', loss={'output1':'mse', 'output2':'mse'})
|
||||
history = graph.fit({'input':X_train, 'output1':y_train, 'output2':y2_train}, nb_epoch=10)
|
||||
|
||||
```
|
||||
|
||||
```python
|
||||
# graph model with two inputs and one output
|
||||
graph = Graph()
|
||||
graph.add_input(name='input1', input_shape=(32,))
|
||||
graph.add_input(name='input2', input_shape=(32,))
|
||||
graph.add_node(Dense(16), name='dense1', input='input1')
|
||||
graph.add_node(Dense(4), name='dense2', input='input2')
|
||||
graph.add_node(Dense(4), name='dense3', input='dense1')
|
||||
graph.add_output(name='output', inputs=['dense2', 'dense3'], merge_mode='sum')
|
||||
graph.compile(optimizer='rmsprop', loss={'output':'mse'})
|
||||
|
||||
history = graph.fit({'input1':X_train, 'input2':X2_train, 'output':y_train}, nb_epoch=10)
|
||||
predictions = graph.predict({'input1':X_test, 'input2':X2_test}) # {'output':...}
|
||||
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
# Model API documentation
|
||||
|
||||
|
||||
|
||||
{{autogenerated}}
|
||||
@@ -0,0 +1,33 @@
|
||||
# About Keras models
|
||||
|
||||
There are two types of models available in Keras: [the Sequential model](/models/sequential) and [the Model class used with functional API](/models/model).
|
||||
|
||||
These models have a number of methods in common:
|
||||
|
||||
- `model.summary()`: prints a summary representation of your model.
|
||||
- `model.get_config()`: returns a dictionary containing the configuration of the model. The model can be reinstantiated from its config via:
|
||||
```python
|
||||
config = model.get_config()
|
||||
model = Model.from_config(config)
|
||||
# or, for Sequential:
|
||||
model = Sequential.from_config(config)
|
||||
```
|
||||
|
||||
- `model.get_weights()`: returns a list of all weight tensors in the model, as Numpy arrays.
|
||||
- `model.set_weights(weights)`: sets the values of the weights of the model, from a list of Numpy arrays. The arrays in the list should have the same shape as those returned by `get_weights()`.
|
||||
- `model.to_json()`: returns a representation of the model as a JSON string. Note that the representation does not include the weights, only the architecture. You can reinstantiate the same model (with reinitialized weights) from the JSON string via:
|
||||
```python
|
||||
from models import model_from_json
|
||||
|
||||
json_string = model.to_json()
|
||||
model = model_from_json(json_string)
|
||||
```
|
||||
- `model.to_yaml()`: returns a representation of the model as a YAML string. Note that the representation does not include the weights, only the architecture. You can reinstantiate the same model (with reinitialized weights) from the YAML string via:
|
||||
```python
|
||||
from models import model_from_yaml
|
||||
|
||||
yaml_string = model.to_yaml()
|
||||
model = model_from_yaml(yaml_string)
|
||||
```
|
||||
- `model.save_weights(filepath)`: saves the weights of the model as a HDF5 file.
|
||||
- `model.load_weights(filepath)`: loads the weights of the model from a HDF5 file (created by `save_weights`).
|
||||
externo
+32
@@ -0,0 +1,32 @@
|
||||
# Model class API
|
||||
|
||||
In the functional API, given an input tensor and output tensor, you can instantiate a `Model` via:
|
||||
|
||||
```python
|
||||
from keras.models import Model
|
||||
from keras.layers import Input, Dense
|
||||
|
||||
a = Input(shape=(32,))
|
||||
b = Dense(32)(a)
|
||||
model = Model(input=a, output=b)
|
||||
```
|
||||
|
||||
This model will include all layers required in the computation of `a` given `b`.
|
||||
|
||||
In the case of multi-input or multi-output models, you can use lists as well:
|
||||
|
||||
```python
|
||||
model = Model(input=[a1, a2], output=[b1, b3, b3])
|
||||
```
|
||||
|
||||
For a detailed introduction of what `Model` can do, read [this guide to the Keras functional API](/getting-started/functional-api-guide).
|
||||
|
||||
## Useful attributes of Model
|
||||
|
||||
- `model.layers` is a flattened list of the layers comprising the model graph.
|
||||
- `model.inputs` is the list of input tensors.
|
||||
- `model.outputs` is the list of output tensors.
|
||||
|
||||
## Methods
|
||||
|
||||
{{autogenerated}}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
# The Sequential model API
|
||||
|
||||
To get started, read [this guide to the Keras Sequential model](/getting-started/sequential-model-guide).
|
||||
|
||||
## Useful attributes of Model
|
||||
|
||||
- `model.layers` is a list of the layers added to the model.
|
||||
|
||||
|
||||
----
|
||||
|
||||
## Sequential model methods
|
||||
|
||||
{{autogenerated}}
|
||||
externo
+3
-3
@@ -7,10 +7,10 @@ An objective function (or loss function, or optimization score function) is one
|
||||
model.compile(loss='mean_squared_error', optimizer='sgd')
|
||||
```
|
||||
|
||||
You can either pass the name of an existing objective, or pass a Theano symbolic function that returns a scalar for each data-point and takes the following two arguments:
|
||||
You can either pass the name of an existing objective, or pass a Theano/TensorFlow symbolic function that returns a scalar for each data-point and takes the following two arguments:
|
||||
|
||||
- __y_true__: True labels. Theano tensor.
|
||||
- __y_pred__: Predictions. Theano tensor of the same shape as y_true.
|
||||
- __y_true__: True labels. Theano/TensorFlow tensor.
|
||||
- __y_pred__: Predictions. Theano/TensorFlow tensor of the same shape as y_true.
|
||||
|
||||
The actual optimized objective is the mean of the output array across all datapoints.
|
||||
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
# Wrappers for the Sciki-Learn API
|
||||
|
||||
You can use `Sequential` Keras models (single-input only) as part of your Scikit-Learn workflow via the wrappers found at `keras.wrappers.sklearn.py`.
|
||||
|
||||
There are two wrappers available:
|
||||
|
||||
`keras.wrappers.sklearn.KerasClassifier(build_fn=None, **sk_params)`, which implements the sklearn classifier interface,
|
||||
|
||||
`keras.wrappers.sklearn.KerasRegressor(build_fn=None, **sk_params)`, which implements the sklearn regressor interface.
|
||||
|
||||
### Arguments
|
||||
|
||||
- __build_fn__: callable function or class instance
|
||||
- __sk_params__: model parameters & fitting parameters
|
||||
|
||||
`build_fn` should construct, compile and return a Keras model, which
|
||||
will then be used to fit/predict. One of the following
|
||||
three values could be passed to build_fn:
|
||||
|
||||
1. A function
|
||||
2. An instance of a class that implements the __call__ method
|
||||
3. None. This means you implement a class that inherits from either
|
||||
`KerasClassifier` or `KerasRegressor`. The __call__ method of the
|
||||
present class will then be treated as the default build_fn.
|
||||
|
||||
`sk_params` takes both model parameters and fitting parameters. Legal model
|
||||
parameters are the arguments of `build_fn`. Note that like all other
|
||||
estimators in scikit-learn, 'build_fn' should provide defalult values for
|
||||
its arguments, so that you could create the estimator without passing any
|
||||
values to `sk_params`.
|
||||
|
||||
`sk_params` could also accept parameters for calling `fit`, `predict`,
|
||||
`predict_proba`, and `score` methods (e.g., `nb_epoch`, `batch_size`).
|
||||
fitting (predicting) parameters are selected in the following order:
|
||||
|
||||
1. Values passed to the dictionary arguments of
|
||||
`fit`, `predict`, `predict_proba`, and `score` methods
|
||||
2. Values passed to `sk_params`
|
||||
3. The default values of the `keras.models.Sequential`
|
||||
`fit`, `predict`, `predict_proba` and `score` methods
|
||||
|
||||
When using scikit-learn's `grid_search` API, legal tunable parameters are
|
||||
those you could pass to `sk_params`, including fitting parameters.
|
||||
In other words, you could use `grid_search` to search for the best
|
||||
`batch_size` or `nb_epoch` as well as the model parameters.
|
||||
externo
+6
-2
@@ -10,11 +10,15 @@ from keras.utils.visualize_util import plot
|
||||
plot(model, to_file='model.png')
|
||||
```
|
||||
|
||||
`plot` takes one optional arguments:
|
||||
|
||||
- `show_shapes` (defaults to False) controls whether output shapes are shown in the graph.
|
||||
|
||||
You can also directly obtain the `pydot.Graph` object and render it yourself,
|
||||
for example to show it in an ipython notebook :
|
||||
```python
|
||||
from IPython.display import SVG
|
||||
from keras.utils.visualize_util import to_graph
|
||||
from keras.utils.visualize_util import model_to_dot
|
||||
|
||||
SVG(to_graph(model).create(prog='dot', format='svg'))
|
||||
SVG(model_to_dot(model).create(prog='dot', format='svg'))
|
||||
```
|
||||
|
||||
@@ -27,7 +27,8 @@ Five digits inverted:
|
||||
'''
|
||||
|
||||
from __future__ import print_function
|
||||
from keras.models import Sequential, slice_X
|
||||
from keras.models import Sequential
|
||||
from keras.engine.training import slice_X
|
||||
from keras.layers.core import Activation, TimeDistributedDense, RepeatVector
|
||||
from keras.layers import recurrent
|
||||
import numpy as np
|
||||
@@ -142,7 +143,9 @@ for _ in range(LAYERS):
|
||||
model.add(TimeDistributedDense(len(chars)))
|
||||
model.add(Activation('softmax'))
|
||||
|
||||
model.compile(loss='categorical_crossentropy', optimizer='adam')
|
||||
model.compile(loss='categorical_crossentropy',
|
||||
optimizer='adam',
|
||||
metrics=['accuracy'])
|
||||
|
||||
# Train the model each generation and show predictions against the validation dataset
|
||||
for iteration in range(1, 200):
|
||||
@@ -150,7 +153,7 @@ for iteration in range(1, 200):
|
||||
print('-' * 50)
|
||||
print('Iteration', iteration)
|
||||
model.fit(X_train, y_train, batch_size=BATCH_SIZE, nb_epoch=1,
|
||||
validation_data=(X_val, y_val), show_accuracy=True)
|
||||
validation_data=(X_val, y_val))
|
||||
###
|
||||
# Select 10 samples from the validation set at random so we can visualize errors
|
||||
for i in range(10):
|
||||
|
||||
@@ -11,7 +11,6 @@ backend (`K`), our code can run both on TensorFlow and Theano.
|
||||
'''
|
||||
|
||||
from __future__ import print_function
|
||||
import numpy as np
|
||||
from keras.models import Sequential
|
||||
from keras.layers.core import Dense, Dropout, Layer, Activation
|
||||
from keras.datasets import mnist
|
||||
@@ -46,15 +45,13 @@ class Antirectifier(Layer):
|
||||
with twice less parameters yet with comparable
|
||||
classification accuracy as an equivalent ReLU-based network.
|
||||
'''
|
||||
@property
|
||||
def output_shape(self):
|
||||
shape = list(self.input_shape)
|
||||
def get_output_shape_for(self, input_shape):
|
||||
shape = list(input_shape)
|
||||
assert len(shape) == 2 # only valid for 2D tensors
|
||||
shape[-1] *= 2
|
||||
return tuple(shape)
|
||||
|
||||
def get_output(self, train):
|
||||
x = self.get_input(train)
|
||||
def call(self, x, mask=None):
|
||||
x -= K.mean(x, axis=1, keepdims=True)
|
||||
x = K.l2_normalize(x, axis=1)
|
||||
pos = K.relu(x)
|
||||
@@ -66,7 +63,7 @@ batch_size = 128
|
||||
nb_classes = 10
|
||||
nb_epoch = 40
|
||||
|
||||
# the data, shuffled and split between tran and test sets
|
||||
# the data, shuffled and split between train and test sets
|
||||
(X_train, y_train), (X_test, y_test) = mnist.load_data()
|
||||
|
||||
X_train = X_train.reshape(60000, 784)
|
||||
@@ -94,13 +91,14 @@ model.add(Dense(10))
|
||||
model.add(Activation('softmax'))
|
||||
|
||||
# compile the model
|
||||
model.compile(loss='categorical_crossentropy', optimizer='rmsprop')
|
||||
model.compile(loss='categorical_crossentropy',
|
||||
optimizer='rmsprop',
|
||||
metrics=['accuracy'])
|
||||
|
||||
# train the model
|
||||
model.fit(X_train, Y_train,
|
||||
batch_size=batch_size, nb_epoch=nb_epoch,
|
||||
show_accuracy=True, verbose=1,
|
||||
validation_data=(X_test, Y_test))
|
||||
verbose=1, validation_data=(X_test, Y_test))
|
||||
|
||||
# next, compare with an equivalent network
|
||||
# with2x bigger Dense layers and ReLU
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
'''Train a memory network on the bAbI dataset.
|
||||
'''Trains a memory network on the bAbI dataset.
|
||||
|
||||
References:
|
||||
- Jason Weston, Antoine Bordes, Sumit Chopra, Tomas Mikolov, Alexander M. Rush,
|
||||
@@ -18,7 +18,7 @@ from keras.models import Sequential
|
||||
from keras.layers.embeddings import Embedding
|
||||
from keras.layers.core import Activation, Dense, Merge, Permute, Dropout
|
||||
from keras.layers.recurrent import LSTM
|
||||
from keras.datasets.data_utils import get_file
|
||||
from keras.utils.data_utils import get_file
|
||||
from keras.preprocessing.sequence import pad_sequences
|
||||
from functools import reduce
|
||||
import tarfile
|
||||
@@ -167,7 +167,7 @@ question_encoder.add(Dropout(0.3))
|
||||
match = Sequential()
|
||||
match.add(Merge([input_encoder_m, question_encoder],
|
||||
mode='dot',
|
||||
dot_axes=[(2,), (2,)]))
|
||||
dot_axes=[2, 2]))
|
||||
# output: (samples, story_maxlen, query_maxlen)
|
||||
# embed the input into a single vector with size = story_maxlen:
|
||||
input_encoder_c = Sequential()
|
||||
@@ -195,10 +195,10 @@ answer.add(Dense(vocab_size))
|
||||
# we output a probability distribution over the vocabulary
|
||||
answer.add(Activation('softmax'))
|
||||
|
||||
answer.compile(optimizer='rmsprop', loss='categorical_crossentropy')
|
||||
answer.compile(optimizer='rmsprop', loss='categorical_crossentropy',
|
||||
metrics=['accuracy'])
|
||||
# Note: you could use a Graph model to avoid repeat the input twice
|
||||
answer.fit([inputs_train, queries_train, inputs_train], answers_train,
|
||||
batch_size=32,
|
||||
nb_epoch=120,
|
||||
show_accuracy=True,
|
||||
validation_data=([inputs_test, queries_test, inputs_test], answers_test))
|
||||
|
||||
+24
-16
@@ -7,8 +7,8 @@ http://arxiv.org/abs/1502.05698
|
||||
|
||||
Task Number | FB LSTM Baseline | Keras QA
|
||||
--- | --- | ---
|
||||
QA1 - Single Supporting Fact | 50 | 52.1
|
||||
QA2 - Two Supporting Facts | 20 | 37.0
|
||||
QA1 - Single Supporting Fact | 50 | 100.0
|
||||
QA2 - Two Supporting Facts | 20 | 50.0
|
||||
QA3 - Three Supporting Facts | 20 | 20.5
|
||||
QA4 - Two Arg. Relations | 61 | 62.9
|
||||
QA5 - Three Arg. Relations | 70 | 61.9
|
||||
@@ -34,8 +34,8 @@ https://research.facebook.com/researchers/1543934539189348
|
||||
Notes:
|
||||
|
||||
- With default word, sentence, and query vector sizes, the GRU model achieves:
|
||||
- 52.1% test accuracy on QA1 in 20 epochs (2 seconds per epoch on CPU)
|
||||
- 37.0% test accuracy on QA2 in 20 epochs (16 seconds per epoch on CPU)
|
||||
- 100% test accuracy on QA1 in 20 epochs (2 seconds per epoch on CPU)
|
||||
- 50% test accuracy on QA2 in 20 epochs (16 seconds per epoch on CPU)
|
||||
In comparison, the Facebook paper achieves 50% and 20% for the LSTM baseline.
|
||||
|
||||
- The task does not traditionally parse the question separately. This likely
|
||||
@@ -64,9 +64,9 @@ import tarfile
|
||||
import numpy as np
|
||||
np.random.seed(1337) # for reproducibility
|
||||
|
||||
from keras.datasets.data_utils import get_file
|
||||
from keras.utils.data_utils import get_file
|
||||
from keras.layers.embeddings import Embedding
|
||||
from keras.layers.core import Dense, Merge
|
||||
from keras.layers.core import Dense, Merge, Dropout, RepeatVector
|
||||
from keras.layers import recurrent
|
||||
from keras.models import Sequential
|
||||
from keras.preprocessing.sequence import pad_sequences
|
||||
@@ -138,12 +138,12 @@ def vectorize_stories(data, word_idx, story_maxlen, query_maxlen):
|
||||
Y.append(y)
|
||||
return pad_sequences(X, maxlen=story_maxlen), pad_sequences(Xq, maxlen=query_maxlen), np.array(Y)
|
||||
|
||||
RNN = recurrent.GRU
|
||||
RNN = recurrent.LSTM
|
||||
EMBED_HIDDEN_SIZE = 50
|
||||
SENT_HIDDEN_SIZE = 100
|
||||
QUERY_HIDDEN_SIZE = 100
|
||||
BATCH_SIZE = 32
|
||||
EPOCHS = 20
|
||||
EPOCHS = 40
|
||||
print('RNN / Embed / Sent / Query = {}, {}, {}, {}'.format(RNN, EMBED_HIDDEN_SIZE, SENT_HIDDEN_SIZE, QUERY_HIDDEN_SIZE))
|
||||
|
||||
path = get_file('babi-tasks-v1-2.tar.gz', origin='http://www.thespermwhale.com/jaseweston/babi/tasks_1-20_v1-2.tar.gz')
|
||||
@@ -178,20 +178,28 @@ print('story_maxlen, query_maxlen = {}, {}'.format(story_maxlen, query_maxlen))
|
||||
print('Build model...')
|
||||
|
||||
sentrnn = Sequential()
|
||||
sentrnn.add(Embedding(vocab_size, EMBED_HIDDEN_SIZE, mask_zero=True))
|
||||
sentrnn.add(RNN(SENT_HIDDEN_SIZE, return_sequences=False))
|
||||
sentrnn.add(Embedding(vocab_size, EMBED_HIDDEN_SIZE,
|
||||
input_length=story_maxlen))
|
||||
sentrnn.add(Dropout(0.3))
|
||||
|
||||
qrnn = Sequential()
|
||||
qrnn.add(Embedding(vocab_size, EMBED_HIDDEN_SIZE))
|
||||
qrnn.add(RNN(QUERY_HIDDEN_SIZE, return_sequences=False))
|
||||
qrnn.add(Embedding(vocab_size, EMBED_HIDDEN_SIZE,
|
||||
input_length=query_maxlen))
|
||||
qrnn.add(Dropout(0.3))
|
||||
qrnn.add(RNN(EMBED_HIDDEN_SIZE, return_sequences=False))
|
||||
qrnn.add(RepeatVector(story_maxlen))
|
||||
|
||||
model = Sequential()
|
||||
model.add(Merge([sentrnn, qrnn], mode='concat'))
|
||||
model.add(Merge([sentrnn, qrnn], mode='sum'))
|
||||
model.add(RNN(EMBED_HIDDEN_SIZE, return_sequences=False))
|
||||
model.add(Dropout(0.3))
|
||||
model.add(Dense(vocab_size, activation='softmax'))
|
||||
|
||||
model.compile(optimizer='adam', loss='categorical_crossentropy', class_mode='categorical')
|
||||
model.compile(optimizer='adam',
|
||||
loss='categorical_crossentropy',
|
||||
metrics=['accuracy'])
|
||||
|
||||
print('Training')
|
||||
model.fit([X, Xq], Y, batch_size=BATCH_SIZE, nb_epoch=EPOCHS, validation_split=0.05, show_accuracy=True)
|
||||
loss, acc = model.evaluate([tX, tXq], tY, batch_size=BATCH_SIZE, show_accuracy=True)
|
||||
model.fit([X, Xq], Y, batch_size=BATCH_SIZE, nb_epoch=EPOCHS, validation_split=0.05)
|
||||
loss, acc = model.evaluate([tX, tXq], tY, batch_size=BATCH_SIZE)
|
||||
print('Test loss / test accuracy = {:.4f} / {:.4f}'.format(loss, acc))
|
||||
|
||||
@@ -66,7 +66,9 @@ model.add(Activation('softmax'))
|
||||
|
||||
# let's train the model using SGD + momentum (how original).
|
||||
sgd = SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True)
|
||||
model.compile(loss='categorical_crossentropy', optimizer=sgd)
|
||||
model.compile(loss='categorical_crossentropy',
|
||||
optimizer=sgd,
|
||||
metrics=['accuracy'])
|
||||
|
||||
X_train = X_train.astype('float32')
|
||||
X_test = X_test.astype('float32')
|
||||
@@ -75,9 +77,11 @@ X_test /= 255
|
||||
|
||||
if not data_augmentation:
|
||||
print('Not using data augmentation.')
|
||||
model.fit(X_train, Y_train, batch_size=batch_size,
|
||||
nb_epoch=nb_epoch, show_accuracy=True,
|
||||
validation_data=(X_test, Y_test), shuffle=True)
|
||||
model.fit(X_train, Y_train,
|
||||
batch_size=batch_size,
|
||||
nb_epoch=nb_epoch,
|
||||
validation_data=(X_test, Y_test),
|
||||
shuffle=True)
|
||||
else:
|
||||
print('Using real-time data augmentation.')
|
||||
|
||||
@@ -99,8 +103,8 @@ else:
|
||||
datagen.fit(X_train)
|
||||
|
||||
# fit the model on the batches generated by datagen.flow()
|
||||
model.fit_generator(datagen.flow(X_train, Y_train, batch_size=batch_size),
|
||||
model.fit_generator(datagen.flow(X_train, Y_train,
|
||||
batch_size=batch_size),
|
||||
samples_per_epoch=X_train.shape[0],
|
||||
nb_epoch=nb_epoch, show_accuracy=True,
|
||||
validation_data=(X_test, Y_test),
|
||||
nb_worker=1)
|
||||
nb_epoch=nb_epoch,
|
||||
validation_data=(X_test, Y_test))
|
||||
|
||||
@@ -47,15 +47,13 @@ def deprocess_image(x):
|
||||
x = np.clip(x, 0, 255).astype('uint8')
|
||||
return x
|
||||
|
||||
# this will contain our generated image
|
||||
input_img = K.placeholder((1, 3, img_width, img_height))
|
||||
|
||||
# build the VGG16 network with our input_img as input
|
||||
first_layer = ZeroPadding2D((1, 1), input_shape=(3, img_width, img_height))
|
||||
first_layer.input = input_img
|
||||
|
||||
# build the VGG16 network
|
||||
model = Sequential()
|
||||
model.add(first_layer)
|
||||
model.add(ZeroPadding2D((1, 1), batch_input_shape=(1, 3, img_width, img_height)))
|
||||
first_layer = model.layers[-1]
|
||||
# this is a placeholder tensor that will contain our generated images
|
||||
input_img = first_layer.input
|
||||
|
||||
model.add(Convolution2D(64, 3, 3, activation='relu', name='conv1_1'))
|
||||
model.add(ZeroPadding2D((1, 1)))
|
||||
model.add(Convolution2D(64, 3, 3, activation='relu', name='conv1_2'))
|
||||
@@ -125,7 +123,7 @@ for filter_index in range(0, 200):
|
||||
|
||||
# we build a loss function that maximizes the activation
|
||||
# of the nth filter of the layer considered
|
||||
layer_output = layer_dict[layer_name].get_output()
|
||||
layer_output = layer_dict[layer_name].output
|
||||
loss = K.mean(layer_output[:, filter_index, :, :])
|
||||
|
||||
# we compute the gradient of the input picture wrt this loss
|
||||
|
||||
@@ -21,6 +21,7 @@ from scipy.optimize import fmin_l_bfgs_b
|
||||
import time
|
||||
import argparse
|
||||
import h5py
|
||||
import os
|
||||
|
||||
from keras.models import Sequential
|
||||
from keras.layers.convolutional import Convolution2D, ZeroPadding2D, MaxPooling2D
|
||||
@@ -73,15 +74,13 @@ def deprocess_image(x):
|
||||
x = np.clip(x, 0, 255).astype('uint8')
|
||||
return x
|
||||
|
||||
# this will contain our generated image
|
||||
dream = K.placeholder((1, 3, img_width, img_height))
|
||||
|
||||
# build the VGG16 network with our dream as input
|
||||
first_layer = ZeroPadding2D((1, 1), input_shape=(3, img_width, img_height))
|
||||
first_layer.input = dream
|
||||
|
||||
# build the VGG16 network
|
||||
model = Sequential()
|
||||
model.add(first_layer)
|
||||
model.add(ZeroPadding2D((1, 1), batch_input_shape=(1, 3, img_width, img_height)))
|
||||
first_layer = model.layers[-1]
|
||||
# this is a placeholder tensor that will contain our generated images
|
||||
dream = first_layer.input
|
||||
|
||||
model.add(Convolution2D(64, 3, 3, activation='relu', name='conv1_1'))
|
||||
model.add(ZeroPadding2D((1, 1)))
|
||||
model.add(Convolution2D(64, 3, 3, activation='relu', name='conv1_2'))
|
||||
@@ -149,7 +148,7 @@ for layer_name in settings['features']:
|
||||
# add the L2 norm of the features of a layer to the loss
|
||||
assert layer_name in layer_dict.keys(), 'Layer ' + layer_name + ' not found in model.'
|
||||
coeff = settings['features'][layer_name]
|
||||
x = layer_dict[layer_name].get_output()
|
||||
x = layer_dict[layer_name].output
|
||||
shape = layer_dict[layer_name].output_shape
|
||||
# we avoid border artifacts by only involving non-border pixels in the loss
|
||||
loss -= coeff * K.sum(K.square(x[:, :, 2: shape[2]-2, 2: shape[3]-2])) / np.prod(shape[1:])
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
'''Train a Bidirectional LSTM on the IMDB sentiment classification task.
|
||||
|
||||
GPU command:
|
||||
THEANO_FLAGS=mode=FAST_RUN,device=gpu,floatX=float32 python imdb_bidirectional_lstm.py
|
||||
|
||||
Output after 4 epochs on CPU: ~0.8146
|
||||
Time per epoch on CPU (Core i7): ~150s.
|
||||
'''
|
||||
@@ -12,11 +9,8 @@ import numpy as np
|
||||
np.random.seed(1337) # for reproducibility
|
||||
|
||||
from keras.preprocessing import sequence
|
||||
from keras.utils.np_utils import accuracy
|
||||
from keras.models import Graph
|
||||
from keras.layers.core import Dense, Dropout
|
||||
from keras.layers.embeddings import Embedding
|
||||
from keras.layers.recurrent import LSTM
|
||||
from keras.models import Model
|
||||
from keras.layers import Dense, Dropout, Embedding, LSTM, Input, merge
|
||||
from keras.datasets import imdb
|
||||
|
||||
|
||||
@@ -38,25 +32,30 @@ print('X_test shape:', X_test.shape)
|
||||
y_train = np.array(y_train)
|
||||
y_test = np.array(y_test)
|
||||
|
||||
print('Build model...')
|
||||
model = Graph()
|
||||
model.add_input(name='input', input_shape=(maxlen,), dtype=int)
|
||||
model.add_node(Embedding(max_features, 128, input_length=maxlen),
|
||||
name='embedding', input='input')
|
||||
model.add_node(LSTM(64), name='forward', input='embedding')
|
||||
model.add_node(LSTM(64, go_backwards=True), name='backward', input='embedding')
|
||||
model.add_node(Dropout(0.5), name='dropout', inputs=['forward', 'backward'])
|
||||
model.add_node(Dense(1, activation='sigmoid'), name='sigmoid', input='dropout')
|
||||
model.add_output(name='output', input='sigmoid')
|
||||
|
||||
# this is the placeholder tensor for the input sequences
|
||||
sequence = Input(shape=(maxlen,), dtype='int32')
|
||||
# this embedding layer will transform the sequences of integers
|
||||
# into vectors of size 128
|
||||
embedded = Embedding(max_features, 128, input_length=maxlen)(sequence)
|
||||
|
||||
# apply forwards LSTM
|
||||
forwards = LSTM(64)(embedded)
|
||||
# apply backwards LSTM
|
||||
backwards = LSTM(64, go_backwards=True)(embedded)
|
||||
|
||||
# concatenate the outputs of the 2 LSTMs
|
||||
merged = merge([forwards, backwards], mode='concat', concat_axis=-1)
|
||||
after_dp = Dropout(0.5)(merged)
|
||||
output = Dense(1, activation='sigmoid')(after_dp)
|
||||
|
||||
model = Model(input=sequence, output=output)
|
||||
|
||||
# try using different optimizers and different optimizer configs
|
||||
model.compile('adam', {'output': 'binary_crossentropy'})
|
||||
model.compile('adam', 'binary_crossentropy', metrics=['accuracy'])
|
||||
|
||||
print('Train...')
|
||||
model.fit({'input': X_train, 'output': y_train},
|
||||
model.fit(X_train, y_train,
|
||||
batch_size=batch_size,
|
||||
nb_epoch=4)
|
||||
acc = accuracy(y_test,
|
||||
np.round(np.array(model.predict({'input': X_test},
|
||||
batch_size=batch_size)['output'])))
|
||||
print('Test accuracy:', acc)
|
||||
nb_epoch=4,
|
||||
validation_data=[X_test, y_test])
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
'''This example demonstrates the use of Convolution1D for text classification.
|
||||
|
||||
Run on GPU: THEANO_FLAGS=mode=FAST_RUN,device=gpu,floatX=float32 python imdb_cnn.py
|
||||
|
||||
Get to 0.835 test accuracy after 2 epochs. 100s/epoch on K520 GPU.
|
||||
Gets to 0.835 test accuracy after 2 epochs. 100s/epoch on K520 GPU.
|
||||
'''
|
||||
|
||||
from __future__ import print_function
|
||||
@@ -72,7 +70,8 @@ model.add(Activation('sigmoid'))
|
||||
|
||||
model.compile(loss='binary_crossentropy',
|
||||
optimizer='rmsprop',
|
||||
class_mode='binary')
|
||||
model.fit(X_train, y_train, batch_size=batch_size,
|
||||
nb_epoch=nb_epoch, show_accuracy=True,
|
||||
metrics=['accuracy'])
|
||||
model.fit(X_train, y_train,
|
||||
batch_size=batch_size,
|
||||
nb_epoch=nb_epoch,
|
||||
validation_data=(X_test, y_test))
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
'''Train a recurrent convolutional network on the IMDB sentiment
|
||||
classification task.
|
||||
|
||||
GPU command:
|
||||
THEANO_FLAGS=mode=FAST_RUN,device=gpu,floatX=float32 python imdb_cnn_lstm.py
|
||||
|
||||
Get to 0.8498 test accuracy after 2 epochs. 41s/epoch on K520 GPU.
|
||||
Gets to 0.8498 test accuracy after 2 epochs. 41s/epoch on K520 GPU.
|
||||
'''
|
||||
|
||||
from __future__ import print_function
|
||||
import numpy as np
|
||||
np.random.seed(1337) # for reproducibility
|
||||
@@ -71,12 +67,11 @@ model.add(Activation('sigmoid'))
|
||||
|
||||
model.compile(loss='binary_crossentropy',
|
||||
optimizer='adam',
|
||||
class_mode='binary')
|
||||
metrics=['accuracy'])
|
||||
|
||||
print('Train...')
|
||||
model.fit(X_train, y_train, batch_size=batch_size, nb_epoch=nb_epoch,
|
||||
validation_data=(X_test, y_test), show_accuracy=True)
|
||||
score, acc = model.evaluate(X_test, y_test, batch_size=batch_size,
|
||||
show_accuracy=True)
|
||||
validation_data=(X_test, y_test))
|
||||
score, acc = model.evaluate(X_test, y_test, batch_size=batch_size)
|
||||
print('Test score:', score)
|
||||
print('Test accuracy:', acc)
|
||||
|
||||
+13
-17
@@ -1,4 +1,4 @@
|
||||
'''Train a LSTM on the IMDB sentiment classification task.
|
||||
'''Trains a LSTM on the IMDB sentiment classification task.
|
||||
|
||||
The dataset is actually too small for LSTM to be of any advantage
|
||||
compared to simpler, much faster methods such as TF-IDF+LogReg.
|
||||
@@ -11,11 +11,7 @@ Some configurations won't converge.
|
||||
|
||||
- LSTM loss decrease patterns during training can be quite different
|
||||
from what you see with CNNs/MLPs/etc.
|
||||
|
||||
GPU command:
|
||||
THEANO_FLAGS=mode=FAST_RUN,device=gpu,floatX=float32 python imdb_lstm.py
|
||||
'''
|
||||
|
||||
from __future__ import print_function
|
||||
import numpy as np
|
||||
np.random.seed(1337) # for reproducibility
|
||||
@@ -25,11 +21,11 @@ from keras.utils import np_utils
|
||||
from keras.models import Sequential
|
||||
from keras.layers.core import Dense, Dropout, Activation
|
||||
from keras.layers.embeddings import Embedding
|
||||
from keras.layers.recurrent import LSTM
|
||||
from keras.layers.recurrent import LSTM, SimpleRNN, GRU
|
||||
from keras.datasets import imdb
|
||||
|
||||
max_features = 20000
|
||||
maxlen = 100 # cut texts after this number of words (among top max_features most common words)
|
||||
maxlen = 80 # cut texts after this number of words (among top max_features most common words)
|
||||
batch_size = 32
|
||||
|
||||
print('Loading data...')
|
||||
@@ -38,7 +34,7 @@ print('Loading data...')
|
||||
print(len(X_train), 'train sequences')
|
||||
print(len(X_test), 'test sequences')
|
||||
|
||||
print("Pad sequences (samples x time)")
|
||||
print('Pad sequences (samples x time)')
|
||||
X_train = sequence.pad_sequences(X_train, maxlen=maxlen)
|
||||
X_test = sequence.pad_sequences(X_test, maxlen=maxlen)
|
||||
print('X_train shape:', X_train.shape)
|
||||
@@ -46,22 +42,22 @@ print('X_test shape:', X_test.shape)
|
||||
|
||||
print('Build model...')
|
||||
model = Sequential()
|
||||
model.add(Embedding(max_features, 128, input_length=maxlen))
|
||||
model.add(LSTM(128)) # try using a GRU instead, for fun
|
||||
model.add(Dropout(0.5))
|
||||
model.add(Embedding(max_features, 128, input_length=maxlen, dropout=0.2))
|
||||
model.add(LSTM(128, dropout_W=0.2, dropout_U=0.2)) # try using a GRU instead, for fun
|
||||
model.add(Dense(1))
|
||||
model.add(Activation('sigmoid'))
|
||||
|
||||
# try using different optimizers and different optimizer configs
|
||||
model.compile(loss='binary_crossentropy',
|
||||
optimizer='adam',
|
||||
class_mode="binary")
|
||||
metrics=['accuracy'])
|
||||
|
||||
print("Train...")
|
||||
model.fit(X_train, y_train, batch_size=batch_size, nb_epoch=3,
|
||||
validation_data=(X_test, y_test), show_accuracy=True)
|
||||
print('Train...')
|
||||
print(X_train.shape)
|
||||
print(y_train.shape)
|
||||
model.fit(X_train, y_train, batch_size=batch_size, nb_epoch=15,
|
||||
validation_data=(X_test, y_test))
|
||||
score, acc = model.evaluate(X_test, y_test,
|
||||
batch_size=batch_size,
|
||||
show_accuracy=True)
|
||||
batch_size=batch_size)
|
||||
print('Test score:', score)
|
||||
print('Test accuracy:', acc)
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
'''This demonstrates how to reach a score of 0.4890 (local validation)
|
||||
on the Kaggle Otto challenge, with a deep net using Keras.
|
||||
|
||||
Requires Scikit-Learn and Pandas.
|
||||
|
||||
Recommended to run on GPU:
|
||||
Command: THEANO_FLAGS=mode=FAST_RUN,device=gpu,floatX=float32 python kaggle_otto_nn.py
|
||||
On EC2 g2.2xlarge instance: 19s/epoch. 6-7 minutes total training time.
|
||||
|
||||
Best validation score at epoch 21: 0.4881
|
||||
|
||||
Try it at home:
|
||||
- with/without BatchNormalization (BatchNormalization helps!)
|
||||
- with ReLU or with PReLU (PReLU helps!)
|
||||
- with smaller layers, largers layers
|
||||
- with more layers, less layers
|
||||
- with different optimizers (SGD+momentum+decay is probably better than Adam!)
|
||||
|
||||
Get the data from Kaggle:
|
||||
https://www.kaggle.com/c/otto-group-product-classification-challenge/data
|
||||
'''
|
||||
|
||||
from __future__ import print_function
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
np.random.seed(1337) # for reproducibility
|
||||
|
||||
from keras.models import Sequential
|
||||
from keras.layers.core import Dense, Dropout, Activation
|
||||
from keras.layers.normalization import BatchNormalization
|
||||
from keras.layers.advanced_activations import PReLU
|
||||
from keras.utils import np_utils, generic_utils
|
||||
|
||||
from sklearn.preprocessing import LabelEncoder
|
||||
from sklearn.preprocessing import StandardScaler
|
||||
|
||||
|
||||
def load_data(path, train=True):
|
||||
df = pd.read_csv(path)
|
||||
X = df.values.copy()
|
||||
if train:
|
||||
np.random.shuffle(X) # https://youtu.be/uyUXoap67N8
|
||||
X, labels = X[:, 1:-1].astype(np.float32), X[:, -1]
|
||||
return X, labels
|
||||
else:
|
||||
X, ids = X[:, 1:].astype(np.float32), X[:, 0].astype(str)
|
||||
return X, ids
|
||||
|
||||
|
||||
def preprocess_data(X, scaler=None):
|
||||
if not scaler:
|
||||
scaler = StandardScaler()
|
||||
scaler.fit(X)
|
||||
X = scaler.transform(X)
|
||||
return X, scaler
|
||||
|
||||
|
||||
def preprocess_labels(labels, encoder=None, categorical=True):
|
||||
if not encoder:
|
||||
encoder = LabelEncoder()
|
||||
encoder.fit(labels)
|
||||
y = encoder.transform(labels).astype(np.int32)
|
||||
if categorical:
|
||||
y = np_utils.to_categorical(y)
|
||||
return y, encoder
|
||||
|
||||
|
||||
def make_submission(y_prob, ids, encoder, fname):
|
||||
with open(fname, 'w') as f:
|
||||
f.write('id,')
|
||||
f.write(','.join([str(i) for i in encoder.classes_]))
|
||||
f.write('\n')
|
||||
for i, probs in zip(ids, y_prob):
|
||||
probas = ','.join([i] + [str(p) for p in probs.tolist()])
|
||||
f.write(probas)
|
||||
f.write('\n')
|
||||
print('Wrote submission to file {}.'.format(fname))
|
||||
|
||||
print('Loading data...')
|
||||
X, labels = load_data('train.csv', train=True)
|
||||
X, scaler = preprocess_data(X)
|
||||
y, encoder = preprocess_labels(labels)
|
||||
|
||||
X_test, ids = load_data('test.csv', train=False)
|
||||
X_test, _ = preprocess_data(X_test, scaler)
|
||||
|
||||
nb_classes = y.shape[1]
|
||||
print(nb_classes, 'classes')
|
||||
|
||||
dims = X.shape[1]
|
||||
print(dims, 'dims')
|
||||
|
||||
print('Building model...')
|
||||
|
||||
model = Sequential()
|
||||
model.add(Dense(512, input_shape=(dims,)))
|
||||
model.add(PReLU())
|
||||
model.add(BatchNormalization())
|
||||
model.add(Dropout(0.5))
|
||||
|
||||
model.add(Dense(512))
|
||||
model.add(PReLU())
|
||||
model.add(BatchNormalization())
|
||||
model.add(Dropout(0.5))
|
||||
|
||||
model.add(Dense(512))
|
||||
model.add(PReLU())
|
||||
model.add(BatchNormalization())
|
||||
model.add(Dropout(0.5))
|
||||
|
||||
model.add(Dense(nb_classes))
|
||||
model.add(Activation('softmax'))
|
||||
|
||||
model.compile(loss='categorical_crossentropy', optimizer='adam')
|
||||
|
||||
print('Training model...')
|
||||
model.fit(X, y, nb_epoch=20, batch_size=128, validation_split=0.15)
|
||||
|
||||
print('Generating submission...')
|
||||
proba = model.predict_proba(X_test)
|
||||
make_submission(proba, ids, encoder, fname='keras-otto.csv')
|
||||
@@ -14,7 +14,7 @@ from __future__ import print_function
|
||||
from keras.models import Sequential
|
||||
from keras.layers.core import Dense, Activation, Dropout
|
||||
from keras.layers.recurrent import LSTM
|
||||
from keras.datasets.data_utils import get_file
|
||||
from keras.utils.data_utils import get_file
|
||||
import numpy as np
|
||||
import random
|
||||
import sys
|
||||
@@ -29,7 +29,7 @@ char_indices = dict((c, i) for i, c in enumerate(chars))
|
||||
indices_char = dict((i, c) for i, c in enumerate(chars))
|
||||
|
||||
# cut the text in semi-redundant sequences of maxlen characters
|
||||
maxlen = 20
|
||||
maxlen = 40
|
||||
step = 3
|
||||
sentences = []
|
||||
next_chars = []
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
'''Train a simple convnet on the MNIST dataset.
|
||||
'''Trains a simple convnet on the MNIST dataset.
|
||||
|
||||
Run on GPU: THEANO_FLAGS=mode=FAST_RUN,device=gpu,floatX=float32 python mnist_cnn.py
|
||||
|
||||
Get to 99.25% test accuracy after 12 epochs (there is still a lot of margin for parameter tuning).
|
||||
Gets to 99.25% test accuracy after 12 epochs
|
||||
(there is still a lot of margin for parameter tuning).
|
||||
16 seconds per epoch on a GRID K520 GPU.
|
||||
'''
|
||||
|
||||
@@ -29,7 +28,7 @@ nb_pool = 2
|
||||
# convolution kernel size
|
||||
nb_conv = 3
|
||||
|
||||
# the data, shuffled and split between tran and test sets
|
||||
# the data, shuffled and split between train and test sets
|
||||
(X_train, y_train), (X_test, y_test) = mnist.load_data()
|
||||
|
||||
X_train = X_train.reshape(X_train.shape[0], 1, img_rows, img_cols)
|
||||
@@ -64,10 +63,12 @@ model.add(Dropout(0.5))
|
||||
model.add(Dense(nb_classes))
|
||||
model.add(Activation('softmax'))
|
||||
|
||||
model.compile(loss='categorical_crossentropy', optimizer='adadelta')
|
||||
model.compile(loss='categorical_crossentropy',
|
||||
optimizer='adadelta',
|
||||
metrics=['accuracy'])
|
||||
|
||||
model.fit(X_train, Y_train, batch_size=batch_size, nb_epoch=nb_epoch,
|
||||
show_accuracy=True, verbose=1, validation_data=(X_test, Y_test))
|
||||
score = model.evaluate(X_test, Y_test, show_accuracy=True, verbose=0)
|
||||
verbose=1, validation_data=(X_test, Y_test))
|
||||
score = model.evaluate(X_test, Y_test, verbose=0)
|
||||
print('Test score:', score[0])
|
||||
print('Test accuracy:', score[1])
|
||||
|
||||
+10
-25
@@ -14,18 +14,15 @@ Reaches 0.93 train/test accuracy after 900 epochs
|
||||
'''
|
||||
|
||||
from __future__ import print_function
|
||||
import numpy as np
|
||||
np.random.seed(1337) # for reproducibility
|
||||
|
||||
from keras.datasets import mnist
|
||||
from keras.models import Sequential
|
||||
from keras.layers.core import Dense, Activation
|
||||
from keras.initializations import normal, identity
|
||||
from keras.layers.recurrent import SimpleRNN, LSTM
|
||||
from keras.layers.recurrent import SimpleRNN
|
||||
from keras.optimizers import RMSprop
|
||||
from keras.utils import np_utils
|
||||
|
||||
|
||||
batch_size = 32
|
||||
nb_classes = 10
|
||||
nb_epochs = 200
|
||||
@@ -54,32 +51,20 @@ Y_test = np_utils.to_categorical(y_test, nb_classes)
|
||||
print('Evaluate IRNN...')
|
||||
model = Sequential()
|
||||
model.add(SimpleRNN(output_dim=hidden_units,
|
||||
init=lambda shape: normal(shape, scale=0.001),
|
||||
inner_init=lambda shape: identity(shape, scale=1.0),
|
||||
activation='relu', input_shape=X_train.shape[1:]))
|
||||
init=lambda shape, name: normal(shape, scale=0.001, name=name),
|
||||
inner_init=lambda shape, name: identity(shape, scale=1.0, name=name),
|
||||
activation='relu',
|
||||
input_shape=X_train.shape[1:]))
|
||||
model.add(Dense(nb_classes))
|
||||
model.add(Activation('softmax'))
|
||||
rmsprop = RMSprop(lr=learning_rate)
|
||||
model.compile(loss='categorical_crossentropy', optimizer=rmsprop)
|
||||
model.compile(loss='categorical_crossentropy',
|
||||
optimizer=rmsprop,
|
||||
metrics=['accuracy'])
|
||||
|
||||
model.fit(X_train, Y_train, batch_size=batch_size, nb_epoch=nb_epochs,
|
||||
show_accuracy=True, verbose=1, validation_data=(X_test, Y_test))
|
||||
verbose=1, validation_data=(X_test, Y_test))
|
||||
|
||||
scores = model.evaluate(X_test, Y_test, show_accuracy=True, verbose=0)
|
||||
scores = model.evaluate(X_test, Y_test, verbose=0)
|
||||
print('IRNN test score:', scores[0])
|
||||
print('IRNN test accuracy:', scores[1])
|
||||
|
||||
print('Compare to LSTM...')
|
||||
model = Sequential()
|
||||
model.add(LSTM(hidden_units, input_shape=X_train.shape[1:]))
|
||||
model.add(Dense(nb_classes))
|
||||
model.add(Activation('softmax'))
|
||||
rmsprop = RMSprop(lr=learning_rate)
|
||||
model.compile(loss='categorical_crossentropy', optimizer=rmsprop)
|
||||
|
||||
model.fit(X_train, Y_train, batch_size=batch_size, nb_epoch=nb_epochs,
|
||||
show_accuracy=True, verbose=1, validation_data=(X_test, Y_test))
|
||||
|
||||
scores = model.evaluate(X_test, Y_test, show_accuracy=True, verbose=0)
|
||||
print('LSTM test score:', scores[0])
|
||||
print('LSTM test accuracy:', scores[1])
|
||||
|
||||
+12
-11
@@ -1,6 +1,6 @@
|
||||
'''Train a simple deep NN on the MNIST dataset.
|
||||
'''Trains a simple deep NN on the MNIST dataset.
|
||||
|
||||
Get to 98.40% test accuracy after 20 epochs
|
||||
Gets to 98.40% test accuracy after 20 epochs
|
||||
(there is *a lot* of margin for parameter tuning).
|
||||
2 seconds per epoch on a K520 GPU.
|
||||
'''
|
||||
@@ -20,7 +20,7 @@ batch_size = 128
|
||||
nb_classes = 10
|
||||
nb_epoch = 20
|
||||
|
||||
# the data, shuffled and split between tran and test sets
|
||||
# the data, shuffled and split between train and test sets
|
||||
(X_train, y_train), (X_test, y_test) = mnist.load_data()
|
||||
|
||||
X_train = X_train.reshape(60000, 784)
|
||||
@@ -46,14 +46,15 @@ model.add(Dropout(0.2))
|
||||
model.add(Dense(10))
|
||||
model.add(Activation('softmax'))
|
||||
|
||||
rms = RMSprop()
|
||||
model.compile(loss='categorical_crossentropy', optimizer=rms)
|
||||
model.summary()
|
||||
|
||||
model.fit(X_train, Y_train,
|
||||
batch_size=batch_size, nb_epoch=nb_epoch,
|
||||
show_accuracy=True, verbose=2,
|
||||
validation_data=(X_test, Y_test))
|
||||
score = model.evaluate(X_test, Y_test,
|
||||
show_accuracy=True, verbose=0)
|
||||
model.compile(loss='categorical_crossentropy',
|
||||
optimizer=RMSprop(),
|
||||
metrics=['accuracy'])
|
||||
|
||||
history = model.fit(X_train, Y_train,
|
||||
batch_size=batch_size, nb_epoch=nb_epoch,
|
||||
verbose=1, validation_data=(X_test, Y_test))
|
||||
score = model.evaluate(X_test, Y_test, verbose=0)
|
||||
print('Test score:', score[0])
|
||||
print('Test accuracy:', score[1])
|
||||
|
||||
@@ -7,8 +7,6 @@ for mode details).
|
||||
[1] "Dimensionality Reduction by Learning an Invariant Mapping"
|
||||
http://yann.lecun.com/exdb/publis/pdf/hadsell-chopra-lecun-06.pdf
|
||||
|
||||
Run on GPU: THEANO_FLAGS=mode=FAST_RUN,device=gpu,floatX=float32 python mnist_siamese_graph.py
|
||||
|
||||
Gets to 99.5% test accuracy after 20 epochs.
|
||||
3 seconds per epoch on a Titan X GPU
|
||||
'''
|
||||
@@ -19,25 +17,23 @@ np.random.seed(1337) # for reproducibility
|
||||
|
||||
import random
|
||||
from keras.datasets import mnist
|
||||
from keras.models import Sequential, Graph
|
||||
from keras.layers.core import Dense, Dropout, Lambda
|
||||
from keras.models import Sequential, Model
|
||||
from keras.layers import Dense, Dropout, Input, Lambda
|
||||
from keras.optimizers import SGD, RMSprop
|
||||
from keras import backend as K
|
||||
|
||||
|
||||
def euclidean_distance(inputs):
|
||||
assert len(inputs) == 2, ('Euclidean distance needs '
|
||||
'2 inputs, %d given' % len(inputs))
|
||||
u, v = inputs.values()
|
||||
return K.sqrt(K.sum(K.square(u - v), axis=1, keepdims=True))
|
||||
def euclidean_distance(vects):
|
||||
x, y = vects
|
||||
return K.sqrt(K.sum(K.square(x - y), axis=1, keepdims=True))
|
||||
|
||||
|
||||
def contrastive_loss(y, d):
|
||||
def contrastive_loss(y_true, y_pred):
|
||||
'''Contrastive loss from Hadsell-et-al.'06
|
||||
http://yann.lecun.com/exdb/publis/pdf/hadsell-chopra-lecun-06.pdf
|
||||
'''
|
||||
margin = 1
|
||||
return K.mean(y * K.square(d) + (1 - y) * K.square(K.maximum(margin - d, 0)))
|
||||
return K.mean(y_true * K.square(y_pred) + (1 - y_true) * K.square(K.maximum(margin - y_pred, 0)))
|
||||
|
||||
|
||||
def create_pairs(x, digit_indices):
|
||||
@@ -77,7 +73,7 @@ def compute_accuracy(predictions, labels):
|
||||
return labels[predictions.ravel() < 0.5].mean()
|
||||
|
||||
|
||||
# the data, shuffled and split between tran and test sets
|
||||
# the data, shuffled and split between train and test sets
|
||||
(X_train, y_train), (X_test, y_test) = mnist.load_data()
|
||||
X_train = X_train.reshape(60000, 784)
|
||||
X_test = X_test.reshape(10000, 784)
|
||||
@@ -98,26 +94,31 @@ te_pairs, te_y = create_pairs(X_test, digit_indices)
|
||||
# network definition
|
||||
base_network = create_base_network(input_dim)
|
||||
|
||||
g = Graph()
|
||||
g.add_input(name='input_a', input_shape=(input_dim,))
|
||||
g.add_input(name='input_b', input_shape=(input_dim,))
|
||||
g.add_shared_node(base_network, name='shared', inputs=['input_a', 'input_b'],
|
||||
merge_mode='join')
|
||||
g.add_node(Lambda(euclidean_distance), name='d', input='shared')
|
||||
g.add_output(name='output', input='d')
|
||||
input_a = Input(shape=(input_dim,))
|
||||
input_b = Input(shape=(input_dim,))
|
||||
|
||||
# because we re-use the same instance `base_network`,
|
||||
# the weights of the network
|
||||
# will be shared across the two branches
|
||||
processed_a = base_network(input_a)
|
||||
processed_b = base_network(input_b)
|
||||
|
||||
distance = Lambda(euclidean_distance)([processed_a, processed_b])
|
||||
|
||||
model = Model(input=[input_a, input_b], output=distance)
|
||||
|
||||
# train
|
||||
rms = RMSprop()
|
||||
g.compile(loss={'output': contrastive_loss}, optimizer=rms)
|
||||
g.fit({'input_a': tr_pairs[:, 0], 'input_b': tr_pairs[:, 1], 'output': tr_y},
|
||||
validation_data={'input_a': te_pairs[:, 0], 'input_b': te_pairs[:, 1], 'output': te_y},
|
||||
batch_size=128,
|
||||
nb_epoch=nb_epoch)
|
||||
model.compile(loss=contrastive_loss, optimizer=rms)
|
||||
model.fit([tr_pairs[:, 0], tr_pairs[:, 1]], tr_y,
|
||||
validation_data=([te_pairs[:, 0], te_pairs[:, 1]], te_y),
|
||||
batch_size=128,
|
||||
nb_epoch=nb_epoch)
|
||||
|
||||
# compute final accuracy on training and test sets
|
||||
pred = g.predict({'input_a': tr_pairs[:, 0], 'input_b': tr_pairs[:, 1]})['output']
|
||||
pred = model.predict([tr_pairs[:, 0], tr_pairs[:, 1]])
|
||||
tr_acc = compute_accuracy(pred, tr_y)
|
||||
pred = g.predict({'input_a': te_pairs[:, 0], 'input_b': te_pairs[:, 1]})['output']
|
||||
pred = model.predict([te_pairs[:, 0], te_pairs[:, 1]])
|
||||
te_acc = compute_accuracy(pred, te_y)
|
||||
|
||||
print('* Accuracy on training set: %0.2f%%' % (100 * tr_acc))
|
||||
|
||||
@@ -55,15 +55,17 @@ def train_model(model, train, test, nb_classes):
|
||||
Y_train = np_utils.to_categorical(train[1], nb_classes)
|
||||
Y_test = np_utils.to_categorical(test[1], nb_classes)
|
||||
|
||||
model.compile(loss='categorical_crossentropy', optimizer='adadelta')
|
||||
model.compile(loss='categorical_crossentropy',
|
||||
optimizer='adadelta',
|
||||
metrics=['accuracy'])
|
||||
|
||||
t = now()
|
||||
model.fit(X_train, Y_train,
|
||||
batch_size=batch_size, nb_epoch=nb_epoch,
|
||||
show_accuracy=True, verbose=1,
|
||||
verbose=1,
|
||||
validation_data=(X_test, Y_test))
|
||||
print('Training time: %s' % (now() - t))
|
||||
score = model.evaluate(X_test, Y_test, show_accuracy=True, verbose=0)
|
||||
score = model.evaluate(X_test, Y_test, verbose=0)
|
||||
print('Test score:', score[0])
|
||||
print('Test accuracy:', score[1])
|
||||
|
||||
|
||||
@@ -7,11 +7,11 @@ and make sure the variable `weights_path` in this script matches the location of
|
||||
|
||||
Run the script with:
|
||||
```
|
||||
python neural_style.py path_to_your_base_image.jpg path_to_your_reference.jpg prefix_for_results
|
||||
python neural_style_transfer.py path_to_your_base_image.jpg path_to_your_reference.jpg prefix_for_results
|
||||
```
|
||||
e.g.:
|
||||
```
|
||||
python neural_style.py img/tuebingen.jpg img/starry_night.jpg results/my_result
|
||||
python neural_style_transfer.py img/tuebingen.jpg img/starry_night.jpg results/my_result
|
||||
```
|
||||
|
||||
It is preferrable to run this script on GPU, for speed.
|
||||
@@ -89,17 +89,11 @@ assert img_height == img_width, 'Due to the use of the Gram matrix, width and he
|
||||
def preprocess_image(image_path):
|
||||
img = imresize(imread(image_path), (img_width, img_height))
|
||||
img = img.transpose((2, 0, 1)).astype('float64')
|
||||
img[:, :, 0] -= 103.939
|
||||
img[:, :, 1] -= 116.779
|
||||
img[:, :, 2] -= 123.68
|
||||
img = np.expand_dims(img, axis=0)
|
||||
return img
|
||||
|
||||
# util function to convert a tensor into a valid image
|
||||
def deprocess_image(x):
|
||||
x[:, :, 0] += 103.939
|
||||
x[:, :, 1] += 116.779
|
||||
x[:, :, 2] += 123.68
|
||||
x = x.transpose((1, 2, 0))
|
||||
x = np.clip(x, 0, 255).astype('uint8')
|
||||
return x
|
||||
@@ -117,8 +111,8 @@ input_tensor = K.concatenate([base_image,
|
||||
combination_image], axis=0)
|
||||
|
||||
# build the VGG16 network with our 3 images as input
|
||||
first_layer = ZeroPadding2D((1, 1), input_shape=(3, img_width, img_height))
|
||||
first_layer.input = input_tensor
|
||||
first_layer = ZeroPadding2D((1, 1))
|
||||
first_layer.set_input(input_tensor, shape=(3, 3, img_width, img_height))
|
||||
|
||||
model = Sequential()
|
||||
model.add(first_layer)
|
||||
@@ -174,7 +168,7 @@ f.close()
|
||||
print('Model loaded.')
|
||||
|
||||
# get the symbolic outputs of each "key" layer (we gave them unique names).
|
||||
outputs_dict = dict([(layer.name, layer.get_output()) for layer in model.layers])
|
||||
outputs_dict = dict([(layer.name, layer.output) for layer in model.layers])
|
||||
|
||||
# compute the neural style loss
|
||||
# first we need to define 4 util functions
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
'''Train and evaluate a simple MLP on the Reuters newswire topic classification task.
|
||||
GPU run command:
|
||||
THEANO_FLAGS=mode=FAST_RUN,device=gpu,floatX=float32 python examples/reuters_mlp.py
|
||||
CPU run command:
|
||||
python examples/reuters_mlp.py
|
||||
'''Trains and evaluate a simple MLP
|
||||
on the Reuters newswire topic classification task.
|
||||
'''
|
||||
|
||||
from __future__ import print_function
|
||||
@@ -49,9 +46,14 @@ model.add(Dropout(0.5))
|
||||
model.add(Dense(nb_classes))
|
||||
model.add(Activation('softmax'))
|
||||
|
||||
model.compile(loss='categorical_crossentropy', optimizer='adam')
|
||||
model.compile(loss='categorical_crossentropy',
|
||||
optimizer='adam',
|
||||
metrics=['accuracy'])
|
||||
|
||||
history = model.fit(X_train, Y_train, nb_epoch=nb_epoch, batch_size=batch_size, verbose=1, show_accuracy=True, validation_split=0.1)
|
||||
score = model.evaluate(X_test, Y_test, batch_size=batch_size, verbose=1, show_accuracy=True)
|
||||
history = model.fit(X_train, Y_train,
|
||||
nb_epoch=nb_epoch, batch_size=batch_size,
|
||||
verbose=1, validation_split=0.1)
|
||||
score = model.evaluate(X_test, Y_test,
|
||||
batch_size=batch_size, verbose=1)
|
||||
print('Test score:', score[0])
|
||||
print('Test accuracy:', score[1])
|
||||
|
||||
@@ -59,7 +59,7 @@ model.add(LSTM(50,
|
||||
return_sequences=False,
|
||||
stateful=True))
|
||||
model.add(Dense(1))
|
||||
model.compile(loss='rmse', optimizer='rmsprop')
|
||||
model.compile(loss='mse', optimizer='rmsprop')
|
||||
|
||||
print('Training')
|
||||
for i in range(epochs):
|
||||
@@ -68,7 +68,8 @@ for i in range(epochs):
|
||||
expected_output,
|
||||
batch_size=batch_size,
|
||||
verbose=1,
|
||||
nb_epoch=1)
|
||||
nb_epoch=1,
|
||||
shuffle=False)
|
||||
model.reset_states()
|
||||
|
||||
print('Predicting')
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
__version__ = '0.3.1'
|
||||
__version__ = '1.0.0'
|
||||
|
||||
@@ -7,13 +7,9 @@ def softmax(x):
|
||||
if ndim == 2:
|
||||
return K.softmax(x)
|
||||
elif ndim == 3:
|
||||
# apply softmax to each timestep
|
||||
def step(x, states):
|
||||
return K.softmax(x), []
|
||||
last_output, outputs, states = K.rnn(step, x,
|
||||
[],
|
||||
mask=None)
|
||||
return outputs
|
||||
e = K.exp(x - K.max(x, axis=-1, keepdims=True))
|
||||
s = K.sum(e, axis=-1, keepdims=True)
|
||||
return e / s
|
||||
else:
|
||||
raise Exception('Cannot apply softmax to a tensor that is not 2D or 3D. ' +
|
||||
'Here, ndim=' + str(ndim))
|
||||
|
||||
@@ -3,7 +3,12 @@ from __future__ import print_function
|
||||
import os
|
||||
import json
|
||||
import sys
|
||||
from .common import epsilon, floatx, set_epsilon, set_floatx
|
||||
from .common import epsilon
|
||||
from .common import floatx
|
||||
from .common import set_epsilon
|
||||
from .common import set_floatx
|
||||
from .common import get_uid
|
||||
from .common import cast_to_floatx
|
||||
|
||||
_keras_base_dir = os.path.expanduser('~')
|
||||
if not os.access(_keras_base_dir, os.W_OK):
|
||||
@@ -18,7 +23,7 @@ _config_path = os.path.expanduser(os.path.join(_keras_dir, 'keras.json'))
|
||||
if os.path.exists(_config_path):
|
||||
_config = json.load(open(_config_path))
|
||||
_floatx = _config.get('floatx', floatx())
|
||||
assert _floatx in {'float32', 'float64'}
|
||||
assert _floatx in {'float16', 'float32', 'float64'}
|
||||
_epsilon = _config.get('epsilon', epsilon())
|
||||
assert type(_epsilon) == float
|
||||
_backend = _config.get('backend', _BACKEND)
|
||||
|
||||
@@ -3,6 +3,7 @@ import numpy as np
|
||||
# the type of float to use throughout the session.
|
||||
_FLOATX = 'float32'
|
||||
_EPSILON = 10e-8
|
||||
_UID_PREFIXES = {}
|
||||
|
||||
|
||||
def epsilon():
|
||||
@@ -15,12 +16,15 @@ def set_epsilon(e):
|
||||
|
||||
|
||||
def floatx():
|
||||
'''Returns the default float type, as a string
|
||||
(e.g. 'float16', 'float32', 'float64').
|
||||
'''
|
||||
return _FLOATX
|
||||
|
||||
|
||||
def set_floatx(floatx):
|
||||
global _FLOATX
|
||||
if floatx not in {'float32', 'float64'}:
|
||||
if floatx not in {'float16', 'float32', 'float64'}:
|
||||
raise Exception('Unknown floatx type: ' + str(floatx))
|
||||
floatx = str(floatx)
|
||||
_FLOATX = floatx
|
||||
@@ -30,3 +34,12 @@ def cast_to_floatx(x):
|
||||
'''Cast a Numpy array to floatx.
|
||||
'''
|
||||
return np.asarray(x, dtype=_FLOATX)
|
||||
|
||||
|
||||
def get_uid(prefix=''):
|
||||
if prefix not in _UID_PREFIXES:
|
||||
_UID_PREFIXES[prefix] = 1
|
||||
return 1
|
||||
else:
|
||||
_UID_PREFIXES[prefix] += 1
|
||||
return _UID_PREFIXES[prefix]
|
||||
|
||||
@@ -1,20 +1,42 @@
|
||||
import tensorflow as tf
|
||||
import numpy as np
|
||||
import os
|
||||
import copy
|
||||
import warnings
|
||||
from .common import _FLOATX, _EPSILON
|
||||
|
||||
# INTERNAL UTILS
|
||||
|
||||
_SESSION = None
|
||||
_LEARNING_PHASE = tf.placeholder(dtype='uint8', name='keras_learning_phase') # 0 = test, 1 = train
|
||||
|
||||
|
||||
def _get_session():
|
||||
def learning_phase():
|
||||
'''Returns the learning phase flag.
|
||||
|
||||
The learning phase flag is an integer tensor (0 = test, 1 = train)
|
||||
to be passed as input to any Keras function
|
||||
that uses a different behavior at train time and test time.
|
||||
'''
|
||||
return _LEARNING_PHASE
|
||||
|
||||
|
||||
def get_session():
|
||||
'''Returns the TF session in use by the backend.
|
||||
'''
|
||||
global _SESSION
|
||||
if _SESSION is None:
|
||||
_SESSION = tf.Session('')
|
||||
if not os.environ.get('OMP_NUM_THREADS'):
|
||||
_SESSION = tf.Session(config=tf.ConfigProto(allow_soft_placement=True))
|
||||
else:
|
||||
nb_thread = int(os.environ.get('OMP_NUM_THREADS'))
|
||||
_SESSION = tf.Session(config=tf.ConfigProto(intra_op_parallelism_threads=nb_thread, allow_soft_placement=True))
|
||||
return _SESSION
|
||||
|
||||
|
||||
def _set_session(session):
|
||||
def set_session(session):
|
||||
'''Sets the TF session.
|
||||
'''
|
||||
global _SESSION
|
||||
_SESSION = session
|
||||
|
||||
@@ -22,81 +44,199 @@ def _set_session(session):
|
||||
# VARIABLE MANIPULATION
|
||||
|
||||
def variable(value, dtype=_FLOATX, name=None):
|
||||
'''Instantiates a tensor.
|
||||
|
||||
# Arguments
|
||||
value: numpy array, initial value of the tensor.
|
||||
dtype: tensor type.
|
||||
name: optional name string for the tensor.
|
||||
|
||||
# Returns
|
||||
Tensor variable instance.
|
||||
'''
|
||||
v = tf.Variable(np.asarray(value, dtype=dtype), name=name)
|
||||
_get_session().run(v.initializer)
|
||||
get_session().run(v.initializer)
|
||||
return v
|
||||
|
||||
|
||||
def placeholder(shape=None, ndim=None, dtype=_FLOATX, name=None):
|
||||
'''Instantiates a placeholder.
|
||||
|
||||
# Arguments
|
||||
shape: shape of the placeholder
|
||||
(integer tuple, may include None entries).
|
||||
ndim: number of axes of the tensor.
|
||||
At least one of {`shape`, `ndim`} must be specified.
|
||||
If both are specified, `shape` is used.
|
||||
dtype: placeholder type.
|
||||
name: optional name string for the placeholder.
|
||||
|
||||
# Returns
|
||||
Placeholder tensor instance.
|
||||
'''
|
||||
if not shape:
|
||||
if ndim:
|
||||
shape = [None for _ in range(ndim)]
|
||||
return tf.placeholder(dtype, shape=shape, name=name)
|
||||
shape = tuple([None for _ in range(ndim)])
|
||||
x = tf.placeholder(dtype, shape=shape, name=name)
|
||||
x._keras_shape = shape
|
||||
x._uses_learning_phase = False
|
||||
return x
|
||||
|
||||
|
||||
def shape(x):
|
||||
return x.get_shape()
|
||||
'''Returns the symbolic shape of a tensor.
|
||||
'''
|
||||
return tf.shape(x)
|
||||
|
||||
|
||||
def int_shape(x):
|
||||
'''Returns the shape of a tensor as a tuple of
|
||||
integers or None entries.
|
||||
'''
|
||||
shape = x.get_shape()
|
||||
return tuple([i.__int__() for i in shape])
|
||||
|
||||
|
||||
def ndim(x):
|
||||
return len(x.get_shape())
|
||||
'''Returns the number of axes in a tensor, as an integer.
|
||||
'''
|
||||
dims = x.get_shape()._dims
|
||||
if dims is not None:
|
||||
return len(dims)
|
||||
return None
|
||||
|
||||
|
||||
def dtype(x):
|
||||
'''Returns the dtype of a tensor, as a string.
|
||||
'''
|
||||
return x.dtype.name
|
||||
|
||||
|
||||
def eval(x):
|
||||
'''Run a graph.
|
||||
'''Evaluates the value of a tensor.
|
||||
Returns a Numpy array.
|
||||
'''
|
||||
return x.eval(session=_get_session())
|
||||
return x.eval(session=get_session())
|
||||
|
||||
|
||||
def zeros(shape, dtype=_FLOATX, name=None):
|
||||
'''Instantiates an all-zeros tensor variable.
|
||||
'''
|
||||
return variable(np.zeros(shape), dtype, name)
|
||||
|
||||
|
||||
def ones(shape, dtype=_FLOATX, name=None):
|
||||
'''Instantiates an all-ones tensor variable.
|
||||
'''
|
||||
return variable(np.ones(shape), dtype, name)
|
||||
|
||||
|
||||
def ones_like(x, name=None):
|
||||
return tf.ones_like(x, name=name)
|
||||
|
||||
|
||||
def zeros_like(x, name=None):
|
||||
'''Instantiates an all-zeros tensor
|
||||
of the same shape as another tensor.
|
||||
'''
|
||||
return tf.zeros_like(x, name=name)
|
||||
|
||||
|
||||
def ones_like(x, name=None):
|
||||
'''Instantiates an all-ones tensor
|
||||
of the same shape as another tensor.
|
||||
'''
|
||||
return tf.ones_like(x, name=name)
|
||||
|
||||
|
||||
def count_params(x):
|
||||
'''Return number of scalars in a tensor.
|
||||
'''Returns the number of scalars in a tensor.
|
||||
'''
|
||||
shape = x.get_shape()
|
||||
return np.prod([shape[i]._value for i in range(len(shape))])
|
||||
|
||||
|
||||
def cast(x, dtype):
|
||||
'''Casts a tensor to a different dtype.
|
||||
'''
|
||||
return tf.cast(x, dtype)
|
||||
|
||||
|
||||
# LINEAR ALGEBRA
|
||||
|
||||
def dot(x, y):
|
||||
return tf.matmul(x, y)
|
||||
'''Multiplies 2 tensors.
|
||||
When attempting to multiply a ND tensor
|
||||
with a ND tensor, reproduces the Theano behavior
|
||||
(e.g. (2, 3).(4, 3, 5) = (2, 4, 5))
|
||||
'''
|
||||
if ndim(x) is not None and (ndim(x) > 2 or ndim(y) > 2):
|
||||
x_shape = (-1,) + int_shape(x)[1:]
|
||||
y_shape = int_shape(y)
|
||||
y_permute_dim = list(range(ndim(y)))
|
||||
y_permute_dim = [y_permute_dim.pop(-2)] + y_permute_dim
|
||||
xt = tf.reshape(x, [-1, x_shape[-1]])
|
||||
yt = tf.reshape(tf.transpose(y, perm=y_permute_dim), [y_shape[-2], -1])
|
||||
return tf.reshape(tf.matmul(xt, yt), x_shape[:-1] + y_shape[:-2] + y_shape[-1:])
|
||||
out = tf.matmul(x, y)
|
||||
return out
|
||||
|
||||
|
||||
def batch_dot(x, y, axes=None):
|
||||
'''Batchwise dot product.
|
||||
|
||||
batch_dot results in a tensor with less dimensions than the input.
|
||||
If the number of dimensions is reduced to 1, we use `expand_dims` to
|
||||
make sure that ndim is at least 2.
|
||||
|
||||
# Example
|
||||
Assume x = [[1, 2] and y = [[5, 6]
|
||||
[3, 4]] [7, 8]]
|
||||
batch_dot(x, y, axes=1) = [[17, 53]] which is the main diagonal
|
||||
of x.dot(y.T), although we never have to calculate the off-diagonal
|
||||
elements.
|
||||
|
||||
|
||||
# Arguments
|
||||
x, y: tensors with ndim >= 2
|
||||
axes: list (or single) int with target dimensions
|
||||
|
||||
# Returns
|
||||
Tensor with ndim >= 2
|
||||
'''
|
||||
if type(axes) == int:
|
||||
axes = (axes, axes)
|
||||
if axes is not None:
|
||||
adj_x = None if axes[0] == ndim(x) - 1 else True
|
||||
adj_y = True if axes[1] == ndim(y) - 1 else None
|
||||
else:
|
||||
adj_x = None
|
||||
adj_y = None
|
||||
out = tf.batch_matmul(x, y, adj_x=adj_x, adj_y=adj_y)
|
||||
if ndim(out) == 1:
|
||||
out = expand_dims(out, 1)
|
||||
return out
|
||||
|
||||
|
||||
def transpose(x):
|
||||
'''Transposes a matrix.
|
||||
'''
|
||||
return tf.transpose(x)
|
||||
|
||||
|
||||
def gather(reference, indices):
|
||||
'''reference: a tensor.
|
||||
indices: an int tensor of indices.
|
||||
'''Retrieves the vectors of indices `indices`
|
||||
in the 2D tensor `reference`.
|
||||
|
||||
Return: a tensor of same type as reference.
|
||||
# Arguments
|
||||
reference: a 2D tensor.
|
||||
indices: an int tensor of indices.
|
||||
|
||||
# Returns
|
||||
A 3D tensor of same type as `reference`.
|
||||
'''
|
||||
return tf.gather(reference, indices)
|
||||
|
||||
|
||||
# ELEMENT-WISE OPERATIONS
|
||||
|
||||
def normalize_axis(axis, ndim):
|
||||
def _normalize_axis(axis, ndim):
|
||||
if type(axis) is tuple:
|
||||
axis = list(axis)
|
||||
if type(axis) is list:
|
||||
@@ -110,31 +250,37 @@ def normalize_axis(axis, ndim):
|
||||
|
||||
|
||||
def max(x, axis=None, keepdims=False):
|
||||
axis = normalize_axis(axis, ndim(x))
|
||||
'''Maximum value in a tensor.
|
||||
'''
|
||||
axis = _normalize_axis(axis, ndim(x))
|
||||
return tf.reduce_max(x, reduction_indices=axis, keep_dims=keepdims)
|
||||
|
||||
|
||||
def min(x, axis=None, keepdims=False):
|
||||
axis = normalize_axis(axis, ndim(x))
|
||||
'''Minimum value in a tensor.
|
||||
'''
|
||||
axis = _normalize_axis(axis, ndim(x))
|
||||
return tf.reduce_min(x, reduction_indices=axis, keep_dims=keepdims)
|
||||
|
||||
|
||||
def sum(x, axis=None, keepdims=False):
|
||||
'''Sum of the values in a tensor, alongside the specified axis.
|
||||
'''
|
||||
axis = normalize_axis(axis, ndim(x))
|
||||
axis = _normalize_axis(axis, ndim(x))
|
||||
return tf.reduce_sum(x, reduction_indices=axis, keep_dims=keepdims)
|
||||
|
||||
|
||||
def prod(x, axis=None, keepdims=False):
|
||||
'''Multiply the values in a tensor, alongside the specified axis.
|
||||
'''Multiplies the values in a tensor, alongside the specified axis.
|
||||
'''
|
||||
axis = normalize_axis(axis, ndim(x))
|
||||
axis = _normalize_axis(axis, ndim(x))
|
||||
return tf.reduce_prod(x, reduction_indices=axis, keep_dims=keepdims)
|
||||
|
||||
|
||||
def std(x, axis=None, keepdims=False):
|
||||
axis = normalize_axis(axis, ndim(x))
|
||||
'''Standard deviation of a tensor, alongside the specificied axis.
|
||||
'''
|
||||
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)
|
||||
@@ -145,7 +291,9 @@ def std(x, axis=None, keepdims=False):
|
||||
|
||||
|
||||
def mean(x, axis=None, keepdims=False):
|
||||
axis = normalize_axis(axis, ndim(x))
|
||||
'''Mean of a tensor, alongside the specificied axis.
|
||||
'''
|
||||
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)
|
||||
@@ -154,57 +302,85 @@ def mean(x, axis=None, keepdims=False):
|
||||
def any(x, axis=None, keepdims=False):
|
||||
'''Bitwise reduction (logical OR).
|
||||
|
||||
Return array of uint8 (0s and 1s).
|
||||
Returns an uint8 tensor (0s and 1s).
|
||||
'''
|
||||
axis = normalize_axis(axis, ndim(x))
|
||||
axis = _normalize_axis(axis, ndim(x))
|
||||
x = tf.cast(x, tf.bool)
|
||||
x = tf.reduce_any(x, reduction_indices=axis, keep_dims=keepdims)
|
||||
return tf.cast(x, tf.uint8)
|
||||
|
||||
|
||||
def argmax(x, axis=-1):
|
||||
'''Returns the index of the maximum value
|
||||
along a tensor axis.
|
||||
'''
|
||||
if axis < 0:
|
||||
axis = axis % len(x.get_shape())
|
||||
return tf.argmax(x, axis)
|
||||
|
||||
|
||||
def argmin(x, axis=-1):
|
||||
'''Returns the index of the minimum value
|
||||
along a tensor axis.
|
||||
'''
|
||||
if axis < 0:
|
||||
axis = axis % len(x.get_shape())
|
||||
return tf.argmin(x, axis)
|
||||
|
||||
|
||||
def square(x):
|
||||
'''Element-wise square.
|
||||
'''
|
||||
return tf.square(x)
|
||||
|
||||
|
||||
def abs(x):
|
||||
'''Element-wise absolute value.
|
||||
'''
|
||||
return tf.abs(x)
|
||||
|
||||
|
||||
def sqrt(x):
|
||||
'''Element-wise square root.
|
||||
'''
|
||||
x = tf.clip_by_value(x, tf.cast(0., dtype=_FLOATX),
|
||||
tf.cast(np.inf, dtype=_FLOATX))
|
||||
return tf.sqrt(x)
|
||||
|
||||
|
||||
def exp(x):
|
||||
'''Element-wise exponential.
|
||||
'''
|
||||
return tf.exp(x)
|
||||
|
||||
|
||||
def log(x):
|
||||
'''Element-wise log.
|
||||
'''
|
||||
return tf.log(x)
|
||||
|
||||
|
||||
def round(x):
|
||||
'''Element-wise rounding to the closest integer.
|
||||
'''
|
||||
return tf.round(x)
|
||||
|
||||
|
||||
def sign(x):
|
||||
'''Element-wise sign.
|
||||
'''
|
||||
return tf.sign(x)
|
||||
|
||||
|
||||
def pow(x, a):
|
||||
'''Element-wise exponentiation.
|
||||
'''
|
||||
return tf.pow(x, a)
|
||||
|
||||
|
||||
def clip(x, min_value, max_value):
|
||||
'''Element-wise value clipping.
|
||||
'''
|
||||
if max_value < min_value:
|
||||
max_value = min_value
|
||||
return tf.clip_by_value(x, tf.cast(min_value, dtype=_FLOATX),
|
||||
@@ -212,59 +388,77 @@ def clip(x, min_value, max_value):
|
||||
|
||||
|
||||
def equal(x, y):
|
||||
'''Element-wise equality between two tensors.
|
||||
Returns a bool tensor.
|
||||
'''
|
||||
return tf.equal(x, y)
|
||||
|
||||
|
||||
def not_equal(x, y):
|
||||
'''Element-wise inequality between two tensors.
|
||||
Returns a bool tensor.
|
||||
'''
|
||||
return tf.not_equal(x, y)
|
||||
|
||||
|
||||
def maximum(x, y):
|
||||
'''Element-wise maximum of two tensors.
|
||||
'''
|
||||
return tf.maximum(x, y)
|
||||
|
||||
|
||||
def minimum(x, y):
|
||||
'''Element-wise minimum of two tensors.
|
||||
'''
|
||||
return tf.minimum(x, y)
|
||||
|
||||
|
||||
# SHAPE OPERATIONS
|
||||
|
||||
def concatenate(tensors, axis=-1):
|
||||
'''Concantes a list of tensors alongside the specified axis.
|
||||
'''
|
||||
if axis < 0:
|
||||
axis = axis % len(tensors[0].get_shape())
|
||||
if len(tensors[0].get_shape()):
|
||||
axis = axis % len(tensors[0].get_shape())
|
||||
else:
|
||||
axis = 0
|
||||
return tf.concat(axis, tensors)
|
||||
|
||||
|
||||
def reshape(x, shape):
|
||||
'''Reshapes a tensor to the specified shape.
|
||||
'''
|
||||
return tf.reshape(x, shape)
|
||||
|
||||
|
||||
def permute_dimensions(x, pattern):
|
||||
'''Transpose dimensions.
|
||||
'''Permutes axes in a tensor.
|
||||
|
||||
pattern should be a tuple or list of
|
||||
dimension indices, e.g. [0, 2, 1].
|
||||
# Arguments
|
||||
pattern: should be a tuple of
|
||||
dimension indices, e.g. (0, 2, 1).
|
||||
'''
|
||||
return tf.transpose(x, perm=pattern)
|
||||
|
||||
|
||||
def resize_images(X, height_factor, width_factor, dim_ordering):
|
||||
'''Resize the images contained in a 4D tensor of shape
|
||||
'''Resizes the images contained in a 4D tensor of shape
|
||||
- [batch, channels, height, width] (for 'th' dim_ordering)
|
||||
- [batch, height, width, channels] (for 'tf' dim_ordering)
|
||||
by a factor of (height_factor, width_factor). Both factors should be
|
||||
positive integers.
|
||||
'''
|
||||
if dim_ordering == 'th':
|
||||
new_height = shape(X)[2].value * height_factor
|
||||
new_width = shape(X)[3].value * width_factor
|
||||
new_shape = tf.shape(X)[2:]
|
||||
new_shape *= tf.constant(np.array([height_factor, width_factor]).astype('int32'))
|
||||
X = permute_dimensions(X, [0, 2, 3, 1])
|
||||
X = tf.image.resize_nearest_neighbor(X, (new_height, new_width))
|
||||
X = tf.image.resize_nearest_neighbor(X, new_shape)
|
||||
return permute_dimensions(X, [0, 3, 1, 2])
|
||||
elif dim_ordering == 'tf':
|
||||
new_height = shape(X)[1].value * height_factor
|
||||
new_width = shape(X)[2].value * width_factor
|
||||
return tf.image.resize_nearest_neighbor(X, (new_height, new_width))
|
||||
new_shape = tf.shape(X)[1:3]
|
||||
new_shape *= tf.constant(np.array([height_factor, width_factor]).astype('int32'))
|
||||
return tf.image.resize_nearest_neighbor(X, new_shape)
|
||||
else:
|
||||
raise Exception('Invalid dim_ordering: ' + dim_ordering)
|
||||
|
||||
@@ -284,7 +478,7 @@ def repeat_elements(x, rep, axis):
|
||||
|
||||
|
||||
def repeat(x, n):
|
||||
'''Repeat a 2D tensor:
|
||||
'''Repeats a 2D tensor:
|
||||
|
||||
if x has shape (samples, dim) and n=2,
|
||||
the output will have shape (samples, 2, dim)
|
||||
@@ -312,19 +506,19 @@ def batch_flatten(x):
|
||||
|
||||
|
||||
def expand_dims(x, dim=-1):
|
||||
'''Add a 1-sized dimension at index "dim".
|
||||
'''Adds a 1-sized dimension at index "dim".
|
||||
'''
|
||||
return tf.expand_dims(x, dim)
|
||||
|
||||
|
||||
def squeeze(x, axis):
|
||||
'''Remove a 1-dimension from the tensor at index "axis".
|
||||
'''Removes a 1-dimension from the tensor at index "axis".
|
||||
'''
|
||||
return tf.squeeze(x, [axis])
|
||||
|
||||
|
||||
def temporal_padding(x, padding=1):
|
||||
'''Pad the middle dimension of a 3D tensor
|
||||
'''Pads the middle dimension of a 3D tensor
|
||||
with "padding" zeros left and right.
|
||||
'''
|
||||
pattern = [[0, 0], [padding, padding], [0, 0]]
|
||||
@@ -332,7 +526,7 @@ def temporal_padding(x, padding=1):
|
||||
|
||||
|
||||
def spatial_2d_padding(x, padding=(1, 1), dim_ordering='th'):
|
||||
'''Pad the 2nd and 3rd dimensions of a 4D tensor
|
||||
'''Pads the 2nd and 3rd dimensions of a 4D tensor
|
||||
with "padding[0]" and "padding[1]" (resp.) zeros left and right.
|
||||
'''
|
||||
if dim_ordering == 'th':
|
||||
@@ -345,16 +539,25 @@ def spatial_2d_padding(x, padding=(1, 1), dim_ordering='th'):
|
||||
return tf.pad(x, pattern)
|
||||
|
||||
|
||||
def pack(x):
|
||||
return tf.pack(x)
|
||||
|
||||
|
||||
# VALUE MANIPULATION
|
||||
|
||||
|
||||
def get_value(x):
|
||||
'''Technically the same as eval() for TF.
|
||||
'''Returns the value of a tensor variable,
|
||||
as a Numpy array.
|
||||
'''
|
||||
return x.eval(session=_get_session())
|
||||
return x.eval(session=get_session())
|
||||
|
||||
|
||||
def set_value(x, value):
|
||||
tf.assign(x, np.asarray(value)).op.run(session=_get_session())
|
||||
'''Sets the value of a tensor variable,
|
||||
from a Numpy array.
|
||||
'''
|
||||
tf.assign(x, np.asarray(value)).op.run(session=get_session())
|
||||
|
||||
|
||||
# GRAPH MANIPULATION
|
||||
@@ -362,9 +565,9 @@ def set_value(x, value):
|
||||
class Function(object):
|
||||
|
||||
def __init__(self, inputs, outputs, updates=[]):
|
||||
assert type(inputs) in {list, tuple}
|
||||
assert type(outputs) in {list, tuple}
|
||||
assert type(updates) in {list, tuple}
|
||||
assert type(inputs) in {list, tuple}, 'Input to a TensorFlow backend function should be a list or tuple.'
|
||||
assert type(outputs) in {list, tuple}, 'Output to a TensorFlow backend function should be a list or tuple.'
|
||||
assert type(updates) in {list, tuple}, 'Updates in a TensorFlow backend function should be a list or tuple.'
|
||||
self.inputs = list(inputs)
|
||||
self.outputs = list(outputs)
|
||||
with tf.control_dependencies(self.outputs):
|
||||
@@ -374,50 +577,71 @@ class Function(object):
|
||||
assert type(inputs) in {list, tuple}
|
||||
names = [v.name for v in self.inputs]
|
||||
feed_dict = dict(zip(names, inputs))
|
||||
session = _get_session()
|
||||
session = get_session()
|
||||
updated = session.run(self.outputs + self.updates, feed_dict=feed_dict)
|
||||
return updated[:len(self.outputs)]
|
||||
|
||||
|
||||
def function(inputs, outputs, updates=[]):
|
||||
def function(inputs, outputs, updates=[], **kwargs):
|
||||
'''Instantiates a Keras function.
|
||||
|
||||
# Arguments
|
||||
inputs: list of placeholder/variable tensors.
|
||||
outputs: list of output tensors.
|
||||
updates: list of update tuples (old_tensor, new_tensor).
|
||||
'''
|
||||
if len(kwargs) > 0:
|
||||
msg = [
|
||||
"Expected no kwargs, you passed %s" % len(kwargs),
|
||||
"kwargs passed to function are ignored with Tensorflow backend"
|
||||
]
|
||||
warnings.warn('\n'.join(msg))
|
||||
return Function(inputs, outputs, updates=updates)
|
||||
|
||||
|
||||
def gradients(loss, variables):
|
||||
'''Returns the gradients of `variables` (list of tensor variables)
|
||||
with regard to `loss`.
|
||||
'''
|
||||
return tf.gradients(loss, variables)
|
||||
|
||||
|
||||
# CONTROL FLOW
|
||||
|
||||
def rnn(step_function, inputs, initial_states,
|
||||
go_backwards=False, mask=None):
|
||||
go_backwards=False, mask=None, constants=None,
|
||||
unroll=False, input_length=None):
|
||||
'''Iterates over the time dimension of a tensor.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
inputs: tensor of temporal data of shape (samples, time, ...)
|
||||
(at least 3D).
|
||||
step_function:
|
||||
Parameters:
|
||||
input: 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),
|
||||
new_states: list of tensors, same length and shapes
|
||||
as 'states'.
|
||||
initial_states: tensor with shape (samples, ...) (no time dimension),
|
||||
containing the initial values for the states used in
|
||||
the step function.
|
||||
go_backwards: boolean. If True, do the iteration over
|
||||
the time dimension in reverse order.
|
||||
mask: binary tensor with shape (samples, time, 1),
|
||||
with a zero for every element that is masked.
|
||||
# Arguments
|
||||
inputs: tensor of temporal data of shape (samples, time, ...)
|
||||
(at least 3D).
|
||||
step_function:
|
||||
Parameters:
|
||||
input: 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),
|
||||
new_states: list of tensors, same length and shapes
|
||||
as 'states'.
|
||||
initial_states: tensor with shape (samples, ...) (no time dimension),
|
||||
containing the initial values for the states used in
|
||||
the step function.
|
||||
go_backwards: boolean. If True, do the iteration over
|
||||
the time dimension in reverse order.
|
||||
mask: binary tensor with shape (samples, time, 1),
|
||||
with a zero for every element that is masked.
|
||||
constants: a list of constant values passed at each step.
|
||||
unroll: with TensorFlow the RNN is always unrolled, but with Theano you
|
||||
can use this boolean flag to unroll the RNN.
|
||||
input_length: not relevant in the TensorFlow implementation.
|
||||
Must be specified if using unrolling with Theano.
|
||||
|
||||
# Returns
|
||||
A tuple (last_output, outputs, new_states).
|
||||
|
||||
Returns
|
||||
-------
|
||||
A tuple (last_output, outputs, new_states).
|
||||
last_output: the latest output of the rnn, of shape (samples, ...)
|
||||
outputs: tensor with shape (samples, time, ...) where each
|
||||
entry outputs[s, t] is the output of the step function
|
||||
@@ -430,6 +654,8 @@ def rnn(step_function, inputs, initial_states,
|
||||
axes = [1, 0] + list(range(2, ndim))
|
||||
inputs = tf.transpose(inputs, (axes))
|
||||
input_list = tf.unpack(inputs)
|
||||
if constants is None:
|
||||
constants = []
|
||||
|
||||
states = initial_states
|
||||
successive_states = []
|
||||
@@ -445,8 +671,11 @@ def rnn(step_function, inputs, initial_states,
|
||||
mask = tf.cast(tf.transpose(mask, axes), tf.bool)
|
||||
mask_list = tf.unpack(mask)
|
||||
|
||||
if go_backwards:
|
||||
mask_list.reverse()
|
||||
|
||||
for input, mask_t in zip(input_list, mask_list):
|
||||
output, new_states = step_function(input, states)
|
||||
output, new_states = step_function(input, states + constants)
|
||||
|
||||
# tf.select needs its condition tensor to be the same shape as its two
|
||||
# result tensors, but in our case the condition (mask) tensor is
|
||||
@@ -474,7 +703,7 @@ def rnn(step_function, inputs, initial_states,
|
||||
successive_states.append(states)
|
||||
else:
|
||||
for input in input_list:
|
||||
output, states = step_function(input, states)
|
||||
output, states = step_function(input, states + constants)
|
||||
successive_outputs.append(output)
|
||||
successive_states.append(states)
|
||||
|
||||
@@ -488,59 +717,129 @@ 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 (int or bool).
|
||||
Note that both `then_expression` and `else_expression`
|
||||
should be symbolic tensors of the *same shape*.
|
||||
|
||||
# Arguments
|
||||
condition: scalar tensor.
|
||||
then_expression: TensorFlow operation.
|
||||
else_expression: TensorFlow operation.
|
||||
'''
|
||||
return tf.python.control_flow_ops.cond(condition,
|
||||
lambda: then_expression,
|
||||
lambda: else_expression)
|
||||
x_shape = copy.copy(then_expression.get_shape())
|
||||
x = tf.python.control_flow_ops.cond(tf.cast(condition, 'bool'),
|
||||
lambda: then_expression,
|
||||
lambda: else_expression)
|
||||
x.set_shape(x_shape)
|
||||
return x
|
||||
|
||||
|
||||
def in_train_phase(x, alt):
|
||||
'''Selects `x` in train phase, and `alt` otherwise.
|
||||
Note that `alt` should have the *same shape* as `x`.
|
||||
'''
|
||||
x_shape = copy.copy(x.get_shape())
|
||||
x = tf.python.control_flow_ops.cond(tf.cast(_LEARNING_PHASE, 'bool'),
|
||||
lambda: x,
|
||||
lambda: alt)
|
||||
x._uses_learning_phase = True
|
||||
x.set_shape(x_shape)
|
||||
return x
|
||||
|
||||
|
||||
def in_test_phase(x, alt):
|
||||
'''Selects `x` in test phase, and `alt` otherwise.
|
||||
Note that `alt` should have the *same shape* as `x`.
|
||||
'''
|
||||
x_shape = copy.copy(x.get_shape())
|
||||
x = tf.python.control_flow_ops.cond(tf.cast(_LEARNING_PHASE, 'bool'),
|
||||
lambda: alt,
|
||||
lambda: x)
|
||||
x._uses_learning_phase = True
|
||||
x.set_shape(x_shape)
|
||||
return x
|
||||
|
||||
|
||||
# NN OPERATIONS
|
||||
|
||||
def relu(x, alpha=0., max_value=None):
|
||||
'''ReLU.
|
||||
'''Rectified linear unit
|
||||
|
||||
alpha: slope of negative section.
|
||||
# Arguments
|
||||
alpha: slope of negative section.
|
||||
max_value: saturation threshold.
|
||||
'''
|
||||
negative_part = tf.nn.relu(-x)
|
||||
x = tf.nn.relu(x)
|
||||
if max_value is not None:
|
||||
x = tf.clip_by_value(x, tf.cast(0., dtype=_FLOATX),
|
||||
tf.cast(max_value, dtype=_FLOATX))
|
||||
x -= tf.constant(alpha, dtype=_FLOATX) * negative_part
|
||||
if isinstance(alpha, (tuple, list, np.ndarray)) or np.isscalar(alpha):
|
||||
alpha = tf.constant(alpha, dtype=_FLOATX)
|
||||
x -= alpha * negative_part
|
||||
return x
|
||||
|
||||
|
||||
def softmax(x):
|
||||
'''Softmax of a tensor.
|
||||
'''
|
||||
return tf.nn.softmax(x)
|
||||
|
||||
|
||||
def softplus(x):
|
||||
'''Softplus of a tensor.
|
||||
'''
|
||||
return tf.nn.softplus(x)
|
||||
|
||||
|
||||
def categorical_crossentropy(output, target, from_logits=False):
|
||||
'''Note: tf.nn.softmax_cross_entropy_with_logits
|
||||
expects logits, Keras expects probabilities.
|
||||
'''Categorical crossentropy between an output tensor
|
||||
and a target tensor, where the target is a tensor of the same
|
||||
shape as the output.
|
||||
'''
|
||||
# Note: tf.nn.softmax_cross_entropy_with_logits
|
||||
# expects logits, Keras expects probabilities.
|
||||
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,
|
||||
reduction_indices=len(output.get_shape()) - 1,
|
||||
keep_dims=True)
|
||||
# manual computation of crossentropy
|
||||
output = tf.clip_by_value(output, tf.cast(_EPSILON, dtype=_FLOATX),
|
||||
tf.cast(1.-_EPSILON, dtype=_FLOATX))
|
||||
tf.cast(1. - _EPSILON, dtype=_FLOATX))
|
||||
return - tf.reduce_sum(target * tf.log(output),
|
||||
reduction_indices=len(output.get_shape())-1)
|
||||
reduction_indices=len(output.get_shape()) - 1)
|
||||
else:
|
||||
return tf.nn.softmax_cross_entropy_with_logits(output, target)
|
||||
|
||||
|
||||
def binary_crossentropy(output, target, from_logits=False):
|
||||
'''Note: tf.nn.sigmoid_cross_entropy_with_logits
|
||||
expects logits, Keras expects probabilities.
|
||||
def sparse_categorical_crossentropy(output, target, from_logits=False):
|
||||
'''Categorical crossentropy between an output tensor
|
||||
and a target tensor, where the target is an integer tensor.
|
||||
'''
|
||||
# Note: tf.nn.softmax_cross_entropy_with_logits
|
||||
# expects logits, Keras expects probabilities.
|
||||
if not from_logits:
|
||||
output = tf.clip_by_value(output, tf.cast(_EPSILON, dtype=_FLOATX),
|
||||
tf.cast(1.-_EPSILON, dtype=_FLOATX))
|
||||
output = tf.log(output)
|
||||
|
||||
output_shape = output.get_shape()
|
||||
res = tf.nn.sparse_softmax_cross_entropy_with_logits(
|
||||
tf.reshape(output, [-1, int(output_shape[-1])]),
|
||||
cast(flatten(target), 'int64'))
|
||||
if len(output_shape) == 3:
|
||||
# if our output includes timesteps we need to reshape
|
||||
return tf.reshape(res, [-1, int(output_shape[-2])])
|
||||
else:
|
||||
return res
|
||||
|
||||
|
||||
def binary_crossentropy(output, target, from_logits=False):
|
||||
'''Binary crossentropy between an output tensor and a target tensor.
|
||||
'''
|
||||
# Note: tf.nn.softmax_cross_entropy_with_logits
|
||||
# expects logits, Keras expects probabilities.
|
||||
if not from_logits:
|
||||
# transform back to logits
|
||||
output = tf.clip_by_value(output, tf.cast(_EPSILON, dtype=_FLOATX),
|
||||
@@ -550,10 +849,15 @@ def binary_crossentropy(output, target, from_logits=False):
|
||||
|
||||
|
||||
def sigmoid(x):
|
||||
'''Element-wise sigmoid.
|
||||
'''
|
||||
return tf.nn.sigmoid(x)
|
||||
|
||||
|
||||
def hard_sigmoid(x):
|
||||
'''Segment-wise linear approximation of sigmoid.
|
||||
Faster than sigmoid.
|
||||
'''
|
||||
x = (0.2 * x) + 0.5
|
||||
x = tf.clip_by_value(x, tf.cast(0., dtype=_FLOATX),
|
||||
tf.cast(1., dtype=_FLOATX))
|
||||
@@ -561,10 +865,21 @@ def hard_sigmoid(x):
|
||||
|
||||
|
||||
def tanh(x):
|
||||
'''Element-wise tanh.
|
||||
'''
|
||||
return tf.nn.tanh(x)
|
||||
|
||||
|
||||
def dropout(x, level, seed=None):
|
||||
'''Sets entries in `x` to zero at random,
|
||||
while scaling the entire tensor.
|
||||
|
||||
# Arguments
|
||||
x: tensor
|
||||
level: fraction of the entries in the tensor
|
||||
that will be set to 0
|
||||
seed: random seed to ensure determinism.
|
||||
'''
|
||||
retain_prob = 1. - level
|
||||
if seed is None:
|
||||
seed = np.random.randint(10e6)
|
||||
@@ -574,6 +889,8 @@ def dropout(x, level, seed=None):
|
||||
|
||||
|
||||
def l2_normalize(x, axis):
|
||||
'''Normalizes a tensor wrt the L2 norm alonside the specified axis.
|
||||
'''
|
||||
if axis < 0:
|
||||
axis = axis % len(x.get_shape())
|
||||
return tf.nn.l2_normalize(x, dim=axis)
|
||||
@@ -584,11 +901,14 @@ def l2_normalize(x, axis):
|
||||
|
||||
def conv2d(x, kernel, strides=(1, 1), border_mode='valid', dim_ordering='th',
|
||||
image_shape=None, filter_shape=None):
|
||||
'''
|
||||
Run on cuDNN if available.
|
||||
border_mode: string, "same" or "valid".
|
||||
dim_ordering: whether to use Theano or TensorFlow dimension ordering
|
||||
in inputs/kernels/ouputs.
|
||||
'''2D convolution.
|
||||
|
||||
# Arguments
|
||||
kernel: kernel tensor.
|
||||
strides: strides tuple.
|
||||
border_mode: string, "same" or "valid".
|
||||
dim_ordering: "tf" or "th". Whether to use Theano or TensorFlow dimension ordering
|
||||
in inputs/kernels/ouputs.
|
||||
'''
|
||||
if border_mode == 'same':
|
||||
padding = 'SAME'
|
||||
@@ -627,11 +947,14 @@ def conv2d(x, kernel, strides=(1, 1), border_mode='valid', dim_ordering='th',
|
||||
|
||||
def pool2d(x, pool_size, strides=(1, 1),
|
||||
border_mode='valid', dim_ordering='th', pool_mode='max'):
|
||||
'''
|
||||
pool_size: tuple of 2 integers.
|
||||
strides: tuple of 2 integers.
|
||||
border_mode: one of "valid", "same".
|
||||
dim_ordering: one of "th", "tf".
|
||||
'''2D Pooling.
|
||||
|
||||
# Arguments
|
||||
pool_size: tuple of 2 integers.
|
||||
strides: tuple of 2 integers.
|
||||
border_mode: one of "valid", "same".
|
||||
dim_ordering: one of "th", "tf".
|
||||
pool_mode: one of "max", "avg".
|
||||
'''
|
||||
if border_mode == 'same':
|
||||
padding = 'SAME'
|
||||
@@ -686,3 +1009,10 @@ def random_uniform(shape, low=0.0, high=1.0, dtype=_FLOATX, seed=None):
|
||||
seed = np.random.randint(10e6)
|
||||
return tf.random_uniform(shape, minval=low, maxval=high,
|
||||
dtype=dtype, seed=seed)
|
||||
|
||||
|
||||
def random_binomial(shape, p=0.0, dtype=_FLOATX, seed=None):
|
||||
if seed is None:
|
||||
seed = np.random.randint(10e6)
|
||||
return tf.select(tf.random_uniform(shape, dtype=dtype, seed=seed) <= p,
|
||||
tf.ones(shape), tf.zeros(shape))
|
||||
|
||||
+455
-129
@@ -1,28 +1,21 @@
|
||||
import theano
|
||||
from theano import tensor as T
|
||||
from theano.sandbox.rng_mrg import MRG_RandomStreams as RandomStreams
|
||||
from theano.tensor.signal import downsample
|
||||
from theano.tensor.signal import pool
|
||||
from theano.tensor.nnet import conv3d2d
|
||||
import inspect
|
||||
import numpy as np
|
||||
from .common import _FLOATX, _EPSILON
|
||||
|
||||
|
||||
# INTERNAL UTILS
|
||||
theano.config.floatX = _FLOATX
|
||||
_LEARNING_PHASE = T.scalar(dtype='uint8', name='keras_learning_phase') # 0 = test, 1 = train
|
||||
|
||||
|
||||
def _on_gpu():
|
||||
'''Return whether the session is set to
|
||||
run on GPU or not (i.e. on CPU).
|
||||
'''
|
||||
return theano.config.device[:3] == 'gpu' or theano.sandbox.cuda.cuda_enabled
|
||||
|
||||
|
||||
if _on_gpu():
|
||||
'''Import cuDNN only if running on GPU:
|
||||
not having Cuda installed should not
|
||||
prevent from running the present code.
|
||||
'''
|
||||
from theano.sandbox.cuda import dnn
|
||||
def learning_phase():
|
||||
# False = test, True = train
|
||||
return _LEARNING_PHASE
|
||||
|
||||
|
||||
# VARIABLE MANIPULATION
|
||||
@@ -41,8 +34,14 @@ def placeholder(shape=None, ndim=None, dtype=_FLOATX, name=None):
|
||||
raise Exception('Specify either a shape or ndim value.')
|
||||
if shape is not None:
|
||||
ndim = len(shape)
|
||||
else:
|
||||
shape = tuple([None for _ in range(ndim)])
|
||||
|
||||
broadcast = (False,) * ndim
|
||||
return T.TensorType(dtype, broadcast)(name)
|
||||
x = T.TensorType(dtype, broadcast)(name)
|
||||
x._keras_shape = shape
|
||||
x._uses_learning_phase = False
|
||||
return x
|
||||
|
||||
|
||||
def shape(x):
|
||||
@@ -58,6 +57,10 @@ def ndim(x):
|
||||
return x.ndim
|
||||
|
||||
|
||||
def dtype(x):
|
||||
return x.dtype
|
||||
|
||||
|
||||
def eval(x):
|
||||
'''Run a graph.
|
||||
'''
|
||||
@@ -108,6 +111,38 @@ def dot(x, y):
|
||||
return T.dot(x, y)
|
||||
|
||||
|
||||
def batch_dot(x, y, axes=None):
|
||||
'''batchwise dot product
|
||||
batch_dot results in a tensor with less dimensions than the input.
|
||||
If the number of dimensions is reduced to 1, we use `expand_dims` to
|
||||
make sure that ndim is at least 2.
|
||||
|
||||
# Example
|
||||
Assume x = [[1, 2] and y = [[5, 6]
|
||||
[3, 4]] [7, 8]]
|
||||
batch_dot(x, y, axes=1) = [[17, 53]] which is the main diagonal
|
||||
of x.dot(y.T), although we never have to calculate the off-diagonal
|
||||
elements.
|
||||
|
||||
|
||||
# Arguments
|
||||
x, y: tensors with ndim >= 2
|
||||
axes: list (or single) int with target dimensions
|
||||
|
||||
# Returns
|
||||
Tensor with ndim >= 2
|
||||
'''
|
||||
if type(axes) == int:
|
||||
axes = (axes, axes)
|
||||
if axes is None:
|
||||
# behaves like tf.batch_matmul as default
|
||||
axes = [x.ndim - 1, y.ndim - 2]
|
||||
out = T.batched_tensordot(x, y, axes=axes)
|
||||
if ndim(out) == 1:
|
||||
out = expand_dims(out, 1)
|
||||
return out
|
||||
|
||||
|
||||
def transpose(x):
|
||||
return T.transpose(x)
|
||||
|
||||
@@ -145,7 +180,10 @@ def prod(x, axis=None, keepdims=False):
|
||||
|
||||
|
||||
def mean(x, axis=None, keepdims=False):
|
||||
return T.mean(x, axis=axis, keepdims=keepdims)
|
||||
dtype = None
|
||||
if 'int' in x.dtype:
|
||||
dtype = _FLOATX
|
||||
return T.mean(x, axis=axis, keepdims=keepdims, dtype=dtype)
|
||||
|
||||
|
||||
def std(x, axis=None, keepdims=False):
|
||||
@@ -191,6 +229,10 @@ def round(x):
|
||||
return T.round(x)
|
||||
|
||||
|
||||
def sign(x):
|
||||
return T.sgn(x)
|
||||
|
||||
|
||||
def pow(x, a):
|
||||
return T.pow(x, a)
|
||||
|
||||
@@ -265,6 +307,27 @@ def resize_images(X, height_factor, width_factor, dim_ordering):
|
||||
raise Exception('Invalid dim_ordering: ' + dim_ordering)
|
||||
|
||||
|
||||
def resize_volumes(X, depth_factor, height_factor, width_factor, dim_ordering):
|
||||
'''Resize the volume contained in a 5D tensor of shape
|
||||
- [batch, channels, depth, height, width] (for 'th' dim_ordering)
|
||||
- [batch, depth, height, width, channels] (for 'tf' dim_ordering)
|
||||
by a factor of (depth_factor, height_factor, width_factor).
|
||||
Both factors should be positive integers.
|
||||
'''
|
||||
if dim_ordering == 'th':
|
||||
output = repeat_elements(X, depth_factor, axis=2)
|
||||
output = repeat_elements(output, height_factor, axis=3)
|
||||
output = repeat_elements(output, width_factor, axis=4)
|
||||
return output
|
||||
elif dim_ordering == 'tf':
|
||||
output = repeat_elements(X, depth_factor, axis=1)
|
||||
output = repeat_elements(output, height_factor, axis=2)
|
||||
output = repeat_elements(output, width_factor, axis=3)
|
||||
return output
|
||||
else:
|
||||
raise Exception('Invalid dim_ordering: ' + dim_ordering)
|
||||
|
||||
|
||||
def repeat(x, n):
|
||||
'''Repeat a 2D tensor.
|
||||
|
||||
@@ -357,6 +420,45 @@ def spatial_2d_padding(x, padding=(1, 1), dim_ordering='th'):
|
||||
raise Exception('Invalid dim_ordering: ' + dim_ordering)
|
||||
return T.set_subtensor(output[indices], x)
|
||||
|
||||
|
||||
def spatial_3d_padding(x, padding=(1, 1, 1), dim_ordering='th'):
|
||||
'''Pad the 2nd, 3rd and 4th dimensions of a 5D tensor
|
||||
with "padding[0]", "padding[1]" and "padding[2]" (resp.) zeros left and right.
|
||||
'''
|
||||
input_shape = x.shape
|
||||
if dim_ordering == 'th':
|
||||
output_shape = (input_shape[0],
|
||||
input_shape[1],
|
||||
input_shape[2] + 2 * padding[0],
|
||||
input_shape[3] + 2 * padding[1],
|
||||
input_shape[4] + 2 * padding[2])
|
||||
output = T.zeros(output_shape)
|
||||
indices = (slice(None),
|
||||
slice(None),
|
||||
slice(padding[0], input_shape[2] + padding[0]),
|
||||
slice(padding[1], input_shape[3] + padding[1]),
|
||||
slice(padding[2], input_shape[4] + padding[2]))
|
||||
|
||||
elif dim_ordering == 'tf':
|
||||
output_shape = (input_shape[0],
|
||||
input_shape[1] + 2 * padding[0],
|
||||
input_shape[2] + 2 * padding[1],
|
||||
input_shape[3] + 2 * padding[2],
|
||||
input_shape[4])
|
||||
output = T.zeros(output_shape)
|
||||
indices = (slice(None),
|
||||
slice(padding[0], input_shape[1] + padding[0]),
|
||||
slice(padding[1], input_shape[2] + padding[1]),
|
||||
slice(padding[2], input_shape[3] + padding[2]),
|
||||
slice(None))
|
||||
else:
|
||||
raise Exception('Invalid dim_ordering: ' + dim_ordering)
|
||||
return T.set_subtensor(output[indices], x)
|
||||
|
||||
|
||||
def pack(x):
|
||||
return T.stack(*x)
|
||||
|
||||
# VALUE MANIPULATION
|
||||
|
||||
|
||||
@@ -377,15 +479,23 @@ class Function(object):
|
||||
|
||||
def __init__(self, inputs, outputs, updates=[], **kwargs):
|
||||
self.function = theano.function(inputs, outputs, updates=updates,
|
||||
allow_input_downcast=True, **kwargs)
|
||||
allow_input_downcast=True,
|
||||
on_unused_input='warn',
|
||||
**kwargs)
|
||||
|
||||
def __call__(self, inputs):
|
||||
assert type(inputs) in {list, tuple}
|
||||
return self.function(*inputs)
|
||||
|
||||
|
||||
def function(inputs, outputs, updates=[]):
|
||||
return Function(inputs, outputs, updates=updates)
|
||||
def function(inputs, outputs, updates=[], **kwargs):
|
||||
if len(kwargs) > 0:
|
||||
function_args = inspect.getargspec(theano.function)[0]
|
||||
for key in kwargs.keys():
|
||||
if key not in function_args:
|
||||
msg = "Invalid argument '%s' passed to K.function" % key
|
||||
raise ValueError(msg)
|
||||
return Function(inputs, outputs, updates=updates, **kwargs)
|
||||
|
||||
|
||||
def gradients(loss, variables):
|
||||
@@ -395,44 +505,51 @@ def gradients(loss, variables):
|
||||
# CONTROL FLOW
|
||||
|
||||
def rnn(step_function, inputs, initial_states,
|
||||
go_backwards=False, mask=None):
|
||||
go_backwards=False, mask=None, constants=None,
|
||||
unroll=False, input_length=None):
|
||||
'''Iterates over the time dimension of a tensor.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
inputs: tensor of temporal data of shape (samples, time, ...)
|
||||
(at least 3D).
|
||||
step_function:
|
||||
Parameters:
|
||||
input: 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),
|
||||
new_states: list of tensors, same length and shapes
|
||||
as 'states'.
|
||||
initial_states: tensor with shape (samples, ...) (no time dimension),
|
||||
containing the initial values for the states used in
|
||||
the step function.
|
||||
go_backwards: boolean. If True, do the iteration over
|
||||
the time dimension in reverse order.
|
||||
mask: binary tensor with shape (samples, time),
|
||||
with a zero for every element that is masked.
|
||||
# Arguments
|
||||
inputs: tensor of temporal data of shape (samples, time, ...)
|
||||
(at least 3D).
|
||||
step_function:
|
||||
Parameters:
|
||||
input: 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),
|
||||
new_states: list of tensors, same length and shapes
|
||||
as 'states'.
|
||||
initial_states: tensor with shape (samples, ...) (no time dimension),
|
||||
containing the initial values for the states used in
|
||||
the step function.
|
||||
go_backwards: boolean. If True, do the iteration over
|
||||
the time dimension in reverse order.
|
||||
mask: binary tensor with shape (samples, time),
|
||||
with a zero for every element that is masked.
|
||||
constants: a list of constant values passed at each step.
|
||||
unroll: whether to unroll the RNN or to use a symbolic loop (`scan`).
|
||||
input_length: must be specified if using `unroll`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
A tuple (last_output, outputs, new_states).
|
||||
last_output: the latest output of the rnn, of shape (samples, ...)
|
||||
outputs: tensor with shape (samples, time, ...) where each
|
||||
entry outputs[s, t] is the output of the step function
|
||||
at time t for sample s.
|
||||
new_states: list of tensors, latest states returned by
|
||||
the step function, of shape (samples, ...).
|
||||
# Returns
|
||||
A tuple (last_output, outputs, new_states).
|
||||
last_output: the latest output of the rnn, of shape (samples, ...)
|
||||
outputs: tensor with shape (samples, time, ...) where each
|
||||
entry outputs[s, t] is the output of the step function
|
||||
at time t for sample s.
|
||||
new_states: list of tensors, latest states returned by
|
||||
the step function, of shape (samples, ...).
|
||||
'''
|
||||
ndim = inputs.ndim
|
||||
assert ndim >= 3, 'Input should be at least 3D.'
|
||||
|
||||
if unroll:
|
||||
if input_length is None:
|
||||
raise Exception('When specifying `unroll=True`, an `input_length` '
|
||||
'must be provided to `rnn`.')
|
||||
|
||||
axes = [1, 0] + list(range(2, ndim))
|
||||
inputs = inputs.dimshuffle(axes)
|
||||
|
||||
@@ -442,43 +559,104 @@ def rnn(step_function, inputs, initial_states,
|
||||
assert mask.ndim == ndim
|
||||
mask = mask.dimshuffle(axes)
|
||||
|
||||
# build an all-zero tensor of shape (samples, output_dim)
|
||||
initial_output = step_function(inputs[0], initial_states)[0] * 0
|
||||
# Theano gets confused by broadcasting patterns in the scan op
|
||||
initial_output = T.unbroadcast(initial_output, 0, 1)
|
||||
if constants is None:
|
||||
constants = []
|
||||
|
||||
def _step(input, mask, output_tm1, *states):
|
||||
output, new_states = step_function(input, states)
|
||||
# output previous output if masked.
|
||||
output = T.switch(mask, output, 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
|
||||
if unroll:
|
||||
indices = list(range(input_length))
|
||||
if go_backwards:
|
||||
indices = indices[::-1]
|
||||
|
||||
results, _ = theano.scan(
|
||||
_step,
|
||||
sequences=[inputs, mask],
|
||||
outputs_info=[initial_output] + initial_states,
|
||||
go_backwards=go_backwards)
|
||||
successive_outputs = []
|
||||
successive_states = []
|
||||
states = initial_states
|
||||
for i in indices:
|
||||
output, new_states = step_function(inputs[i], states)
|
||||
|
||||
if len(successive_outputs) == 0:
|
||||
prev_output = zeros_like(output)
|
||||
else:
|
||||
prev_output = successive_outputs[-1]
|
||||
|
||||
output = T.switch(mask[i], output, prev_output)
|
||||
kept_states = []
|
||||
for state, new_state in zip(states, new_states):
|
||||
kept_states.append(T.switch(mask[i], new_state, state))
|
||||
states = kept_states
|
||||
|
||||
successive_outputs.append(output)
|
||||
successive_states.append(states)
|
||||
|
||||
outputs = T.stack(*successive_outputs)
|
||||
states = []
|
||||
for i in range(len(successive_states[-1])):
|
||||
states.append(T.stack(*[states_at_step[i] for states_at_step in successive_states]))
|
||||
else:
|
||||
# build an all-zero tensor of shape (samples, output_dim)
|
||||
initial_output = step_function(inputs[0], initial_states + constants)[0] * 0
|
||||
# Theano gets confused by broadcasting patterns in the scan op
|
||||
initial_output = T.unbroadcast(initial_output, 0, 1)
|
||||
|
||||
def _step(input, mask, output_tm1, *states):
|
||||
output, new_states = step_function(input, states)
|
||||
# output previous output if masked.
|
||||
output = T.switch(mask, output, 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
|
||||
|
||||
results, _ = theano.scan(
|
||||
_step,
|
||||
sequences=[inputs, mask],
|
||||
outputs_info=[initial_output] + initial_states,
|
||||
non_sequences=constants,
|
||||
go_backwards=go_backwards)
|
||||
|
||||
# deal with Theano API inconsistency
|
||||
if type(results) is list:
|
||||
outputs = results[0]
|
||||
states = results[1:]
|
||||
else:
|
||||
outputs = results
|
||||
states = []
|
||||
else:
|
||||
def _step(input, *states):
|
||||
output, new_states = step_function(input, states)
|
||||
return [output] + new_states
|
||||
if unroll:
|
||||
indices = list(range(input_length))
|
||||
if go_backwards:
|
||||
indices = indices[::-1]
|
||||
|
||||
results, _ = theano.scan(
|
||||
_step,
|
||||
sequences=inputs,
|
||||
outputs_info=[None] + initial_states,
|
||||
go_backwards=go_backwards)
|
||||
successive_outputs = []
|
||||
successive_states = []
|
||||
states = initial_states
|
||||
for i in indices:
|
||||
output, states = step_function(inputs[i], states)
|
||||
successive_outputs.append(output)
|
||||
successive_states.append(states)
|
||||
outputs = T.stack(*successive_outputs)
|
||||
states = []
|
||||
for i in range(len(successive_states[-1])):
|
||||
states.append(T.stack(*[states_at_step[i] for states_at_step in successive_states]))
|
||||
|
||||
# deal with Theano API inconsistency
|
||||
if type(results) is list:
|
||||
outputs = results[0]
|
||||
states = results[1:]
|
||||
else:
|
||||
outputs = results
|
||||
states = []
|
||||
else:
|
||||
def _step(input, *states):
|
||||
output, new_states = step_function(input, states)
|
||||
return [output] + new_states
|
||||
|
||||
results, _ = theano.scan(
|
||||
_step,
|
||||
sequences=inputs,
|
||||
outputs_info=[None] + initial_states,
|
||||
non_sequences=constants,
|
||||
go_backwards=go_backwards)
|
||||
|
||||
# deal with Theano API inconsistency
|
||||
if type(results) is list:
|
||||
outputs = results[0]
|
||||
states = results[1:]
|
||||
else:
|
||||
outputs = results
|
||||
states = []
|
||||
|
||||
outputs = T.squeeze(outputs)
|
||||
last_output = outputs[-1]
|
||||
@@ -495,6 +673,18 @@ def switch(condition, then_expression, else_expression):
|
||||
return T.switch(condition, then_expression, else_expression)
|
||||
|
||||
|
||||
def in_train_phase(x, alt):
|
||||
x = T.switch(_LEARNING_PHASE, x, alt)
|
||||
x._uses_learning_phase = True
|
||||
return x
|
||||
|
||||
|
||||
def in_test_phase(x, alt):
|
||||
x = T.switch(_LEARNING_PHASE, alt, x)
|
||||
x._uses_learning_phase = True
|
||||
return x
|
||||
|
||||
|
||||
# NN OPERATIONS
|
||||
|
||||
def relu(x, alpha=0., max_value=None):
|
||||
@@ -527,6 +717,13 @@ def categorical_crossentropy(output, target, from_logits=False):
|
||||
return T.nnet.categorical_crossentropy(output, target)
|
||||
|
||||
|
||||
def sparse_categorical_crossentropy(output, target, 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):
|
||||
if from_logits:
|
||||
output = T.nnet.sigmoid(output)
|
||||
@@ -569,7 +766,6 @@ def l2_normalize(x, axis):
|
||||
def conv2d(x, kernel, strides=(1, 1), border_mode='valid', dim_ordering='th',
|
||||
image_shape=None, filter_shape=None):
|
||||
'''
|
||||
Run on cuDNN if available.
|
||||
border_mode: string, "same" or "valid".
|
||||
'''
|
||||
if dim_ordering not in {'th', 'tf'}:
|
||||
@@ -591,51 +787,151 @@ def conv2d(x, kernel, strides=(1, 1), border_mode='valid', dim_ordering='th',
|
||||
filter_shape = (filter_shape[3], filter_shape[2],
|
||||
filter_shape[0], filter_shape[1])
|
||||
|
||||
if _on_gpu() and dnn.dnn_available():
|
||||
if border_mode == 'same':
|
||||
assert(strides == (1, 1))
|
||||
conv_out = dnn.dnn_conv(img=x,
|
||||
kerns=kernel,
|
||||
border_mode='full')
|
||||
np_kernel = kernel.eval()
|
||||
shift_x = (np_kernel.shape[2] - 1) // 2
|
||||
shift_y = (np_kernel.shape[3] - 1) // 2
|
||||
conv_out = conv_out[:, :,
|
||||
shift_x:x.shape[2] + shift_x,
|
||||
shift_y:x.shape[3] + shift_y]
|
||||
else:
|
||||
conv_out = dnn.dnn_conv(img=x,
|
||||
kerns=kernel,
|
||||
border_mode=border_mode,
|
||||
subsample=strides)
|
||||
if border_mode == 'same':
|
||||
th_border_mode = 'half'
|
||||
np_kernel = kernel.eval()
|
||||
elif border_mode == 'valid':
|
||||
th_border_mode = 'valid'
|
||||
else:
|
||||
if border_mode == 'same':
|
||||
th_border_mode = 'full'
|
||||
assert(strides == (1, 1))
|
||||
elif border_mode == 'valid':
|
||||
th_border_mode = 'valid'
|
||||
else:
|
||||
raise Exception('Border mode not supported: ' + str(border_mode))
|
||||
raise Exception('Border mode not supported: ' + str(border_mode))
|
||||
|
||||
# Theano might not accept long type
|
||||
def int_or_none(value):
|
||||
try:
|
||||
return int(value)
|
||||
except TypeError:
|
||||
return None
|
||||
|
||||
if image_shape is not None:
|
||||
image_shape = tuple(int_or_none(v) for v in image_shape)
|
||||
|
||||
if filter_shape is not None:
|
||||
filter_shape = tuple(int_or_none(v) for v in filter_shape)
|
||||
|
||||
conv_out = T.nnet.conv2d(x, kernel,
|
||||
border_mode=th_border_mode,
|
||||
subsample=strides,
|
||||
input_shape=image_shape,
|
||||
filter_shape=filter_shape)
|
||||
|
||||
if border_mode == 'same':
|
||||
if np_kernel.shape[2] % 2 == 0:
|
||||
conv_out = conv_out[:, :, :(x.shape[2] + strides[0] - 1) // strides[0], :]
|
||||
if np_kernel.shape[3] % 2 == 0:
|
||||
conv_out = conv_out[:, :, :, :(x.shape[3] + strides[1] - 1) // strides[1]]
|
||||
|
||||
conv_out = T.nnet.conv.conv2d(x, kernel,
|
||||
border_mode=th_border_mode,
|
||||
subsample=strides,
|
||||
image_shape=image_shape,
|
||||
filter_shape=filter_shape)
|
||||
if border_mode == 'same':
|
||||
np_kernel = kernel.eval()
|
||||
shift_x = (np_kernel.shape[2] - 1) // 2
|
||||
shift_y = (np_kernel.shape[3] - 1) // 2
|
||||
conv_out = conv_out[:, :,
|
||||
shift_x:x.shape[2] + shift_x,
|
||||
shift_y:x.shape[3] + shift_y]
|
||||
if dim_ordering == 'tf':
|
||||
conv_out = conv_out.dimshuffle((0, 2, 3, 1))
|
||||
return conv_out
|
||||
|
||||
|
||||
def conv3d(x, kernel, strides=(1, 1, 1),
|
||||
border_mode='valid', dim_ordering='th',
|
||||
volume_shape=None, filter_shape=None):
|
||||
'''
|
||||
Run on cuDNN if available.
|
||||
border_mode: string, "same" or "valid".
|
||||
'''
|
||||
if dim_ordering not in {'th', 'tf'}:
|
||||
raise Exception('Unknown dim_ordering ' + str(dim_ordering))
|
||||
|
||||
if border_mode not in {'same', 'valid'}:
|
||||
raise Exception('Invalid border mode: ' + str(border_mode))
|
||||
|
||||
if dim_ordering == 'tf':
|
||||
# TF uses the last dimension as channel dimension,
|
||||
# instead of the 2nd one.
|
||||
# TH input shape: (samples, input_depth, conv_dim1, conv_dim2, conv_dim3)
|
||||
# TF input shape: (samples, conv_dim1, conv_dim2, conv_dim3, input_depth)
|
||||
# TH kernel shape: (out_depth, input_depth, kernel_dim1, kernel_dim2, kernel_dim3)
|
||||
# TF kernel shape: (kernel_dim1, kernel_dim2, kernel_dim3, input_depth, out_depth)
|
||||
x = x.dimshuffle((0, 4, 1, 2, 3))
|
||||
kernel = kernel.dimshuffle((4, 3, 0, 1, 2))
|
||||
if volume_shape:
|
||||
volume_shape = (volume_shape[0], volume_shape[4],
|
||||
volume_shape[1], volume_shape[2], volume_shape[3])
|
||||
if filter_shape:
|
||||
filter_shape = (filter_shape[4], filter_shape[3],
|
||||
filter_shape[0], filter_shape[1], filter_shape[2])
|
||||
|
||||
if border_mode == 'same':
|
||||
assert(strides == (1, 1, 1))
|
||||
pad_dim1 = (kernel.shape[2] - 1)
|
||||
pad_dim2 = (kernel.shape[3] - 1)
|
||||
pad_dim3 = (kernel.shape[4] - 1)
|
||||
output_shape = (x.shape[0], x.shape[1],
|
||||
x.shape[2] + pad_dim1,
|
||||
x.shape[3] + pad_dim2,
|
||||
x.shape[4] + pad_dim3)
|
||||
output = T.zeros(output_shape)
|
||||
indices = (slice(None), slice(None),
|
||||
slice(pad_dim1 // 2, x.shape[2] + pad_dim1 // 2),
|
||||
slice(pad_dim2 // 2, x.shape[3] + pad_dim2 // 2),
|
||||
slice(pad_dim3 // 2, x.shape[4] + pad_dim3 // 2))
|
||||
x = T.set_subtensor(output[indices], x)
|
||||
border_mode = 'valid'
|
||||
|
||||
border_mode_3d = (border_mode, border_mode, border_mode)
|
||||
conv_out = conv3d2d.conv3d(signals=x.dimshuffle(0, 2, 1, 3, 4),
|
||||
filters=kernel.dimshuffle(0, 2, 1, 3, 4),
|
||||
border_mode=border_mode_3d)
|
||||
conv_out = conv_out.dimshuffle(0, 2, 1, 3, 4)
|
||||
|
||||
# support strides by manually slicing the output
|
||||
if strides != (1, 1, 1):
|
||||
conv_out = conv_out[:, :, ::strides[0], ::strides[1], ::strides[2]]
|
||||
|
||||
if dim_ordering == 'tf':
|
||||
conv_out = conv_out.dimshuffle((0, 2, 3, 4, 1))
|
||||
|
||||
return conv_out
|
||||
|
||||
|
||||
def pool2d(x, pool_size, strides=(1, 1), border_mode='valid',
|
||||
dim_ordering='th', pool_mode='max'):
|
||||
if border_mode == 'same':
|
||||
w_pad = pool_size[0] - 2 if pool_size[0] % 2 == 1 else pool_size[0] - 1
|
||||
h_pad = pool_size[1] - 2 if pool_size[1] % 2 == 1 else pool_size[1] - 1
|
||||
padding = (w_pad, h_pad)
|
||||
elif border_mode == 'valid':
|
||||
padding = (0, 0)
|
||||
else:
|
||||
raise Exception('Invalid border mode: ' + str(border_mode))
|
||||
|
||||
if dim_ordering not in {'th', 'tf'}:
|
||||
raise Exception('Unknown dim_ordering ' + str(dim_ordering))
|
||||
|
||||
if dim_ordering == 'tf':
|
||||
x = x.dimshuffle((0, 3, 1, 2))
|
||||
|
||||
if pool_mode == 'max':
|
||||
pool_out = pool.pool_2d(x, ds=pool_size, st=strides,
|
||||
ignore_border=True,
|
||||
padding=padding,
|
||||
mode='max')
|
||||
elif pool_mode == 'avg':
|
||||
pool_out = pool.pool_2d(x, ds=pool_size, st=strides,
|
||||
ignore_border=True,
|
||||
padding=padding,
|
||||
mode='average_exc_pad')
|
||||
else:
|
||||
raise Exception('Invalid pooling mode: ' + str(pool_mode))
|
||||
|
||||
if border_mode == 'same':
|
||||
expected_width = (x.shape[2] + strides[0] - 1) // strides[0]
|
||||
expected_height = (x.shape[3] + strides[1] - 1) // strides[1]
|
||||
|
||||
pool_out = pool_out[:, :,
|
||||
: expected_width,
|
||||
: expected_height]
|
||||
|
||||
if dim_ordering == 'tf':
|
||||
pool_out = pool_out.dimshuffle((0, 2, 3, 1))
|
||||
return pool_out
|
||||
|
||||
|
||||
def pool3d(x, pool_size, strides=(1, 1, 1), border_mode='valid',
|
||||
dim_ordering='th', pool_mode='max'):
|
||||
if border_mode == 'same':
|
||||
# TODO: add implementation for border_mode="same"
|
||||
raise Exception('border_mode="same" not supported with Theano.')
|
||||
@@ -649,23 +945,46 @@ def pool2d(x, pool_size, strides=(1, 1), border_mode='valid',
|
||||
raise Exception('Unknown dim_ordering ' + str(dim_ordering))
|
||||
|
||||
if dim_ordering == 'tf':
|
||||
x = x.dimshuffle((0, 3, 1, 2))
|
||||
x = x.dimshuffle((0, 4, 1, 2, 3))
|
||||
|
||||
if pool_mode == 'max':
|
||||
pool_out = downsample.max_pool_2d(x, ds=pool_size, st=strides,
|
||||
ignore_border=ignore_border,
|
||||
padding=padding,
|
||||
mode='max')
|
||||
# pooling over conv_dim2, conv_dim1 (last two channels)
|
||||
output = pool.pool_2d(input=x.dimshuffle(0, 1, 4, 3, 2),
|
||||
ds=(pool_size[1], pool_size[0]),
|
||||
st=(strides[1], strides[0]),
|
||||
ignore_border=ignore_border,
|
||||
padding=padding,
|
||||
mode='max')
|
||||
|
||||
# pooling over conv_dim3
|
||||
pool_out = pool.pool_2d(input=output.dimshuffle(0, 1, 4, 3, 2),
|
||||
ds=(1, pool_size[2]),
|
||||
st=(1, strides[2]),
|
||||
ignore_border=ignore_border,
|
||||
padding=padding,
|
||||
mode='max')
|
||||
|
||||
elif pool_mode == 'avg':
|
||||
pool_out = downsample.max_pool_2d(x, ds=pool_size, st=strides,
|
||||
ignore_border=ignore_border,
|
||||
padding=padding,
|
||||
mode='average_exc_pad')
|
||||
# pooling over conv_dim2, conv_dim1 (last two channels)
|
||||
output = pool.pool_2d(input=x.dimshuffle(0, 1, 4, 3, 2),
|
||||
ds=(pool_size[1], pool_size[0]),
|
||||
st=(strides[1], strides[0]),
|
||||
ignore_border=ignore_border,
|
||||
padding=padding,
|
||||
mode='average_exc_pad')
|
||||
|
||||
# pooling over conv_dim3
|
||||
pool_out = pool.pool_2d(input=output.dimshuffle(0, 1, 4, 3, 2),
|
||||
ds=(1, pool_size[2]),
|
||||
st=(1, strides[2]),
|
||||
ignore_border=ignore_border,
|
||||
padding=padding,
|
||||
mode='average_exc_pad')
|
||||
else:
|
||||
raise Exception('Invalid pooling mode: ' + str(pool_mode))
|
||||
|
||||
if dim_ordering == 'tf':
|
||||
pool_out = pool_out.dimshuffle((0, 2, 3, 1))
|
||||
pool_out = pool_out.dimshuffle((0, 2, 3, 4, 1))
|
||||
return pool_out
|
||||
|
||||
|
||||
@@ -685,6 +1004,13 @@ def random_uniform(shape, low=0.0, high=1.0, dtype=_FLOATX, seed=None):
|
||||
rng = RandomStreams(seed=seed)
|
||||
return rng.uniform(shape, low=low, high=high, dtype=dtype)
|
||||
|
||||
|
||||
def random_binomial(shape, p=0.0, dtype=_FLOATX, seed=None):
|
||||
if seed is None:
|
||||
seed = np.random.randint(10e6)
|
||||
rng = RandomStreams(seed=seed)
|
||||
return rng.binomial(shape, p=p, dtype=dtype)
|
||||
|
||||
'''
|
||||
more TODO:
|
||||
|
||||
|
||||
+45
-88
@@ -92,7 +92,8 @@ class Callback(object):
|
||||
will include the following quantities in the `logs` that
|
||||
it passes to its callbacks:
|
||||
|
||||
on_epoch_end: logs optionally include `val_loss`
|
||||
on_epoch_end: logs include `acc` and `loss`, and
|
||||
optionally include `val_loss`
|
||||
(if validation is enabled in `fit`), and `val_acc`
|
||||
(if validation and accuracy monitoring are enabled).
|
||||
on_batch_begin: logs include `size`,
|
||||
@@ -129,11 +130,35 @@ class Callback(object):
|
||||
|
||||
|
||||
class BaseLogger(Callback):
|
||||
'''Callback that prints events to the standard output.
|
||||
'''Callback that accumulates epoch averages of
|
||||
the metrics being monitored.
|
||||
|
||||
This callback is automatically applied to
|
||||
every Keras model (it is the basis of the verbosity modes
|
||||
in models).
|
||||
every Keras model.
|
||||
'''
|
||||
def on_epoch_begin(self, epoch, logs={}):
|
||||
self.seen = 0
|
||||
self.totals = {}
|
||||
|
||||
def on_batch_end(self, batch, logs={}):
|
||||
batch_size = logs.get('size', 0)
|
||||
self.seen += batch_size
|
||||
|
||||
for k, v in logs.items():
|
||||
if k in self.totals:
|
||||
self.totals[k] += v * batch_size
|
||||
else:
|
||||
self.totals[k] = v * batch_size
|
||||
|
||||
def on_epoch_end(self, epoch, logs={}):
|
||||
for k in self.params['metrics']:
|
||||
if k in self.totals:
|
||||
# make value available to next callbacks
|
||||
logs[k] = self.totals[k] / self.seen
|
||||
|
||||
|
||||
class ProgbarLogger(Callback):
|
||||
'''Callback that prints metrics to stdout.
|
||||
'''
|
||||
def on_train_begin(self, logs={}):
|
||||
self.verbose = self.params['verbose']
|
||||
@@ -145,7 +170,6 @@ class BaseLogger(Callback):
|
||||
self.progbar = Progbar(target=self.params['nb_sample'],
|
||||
verbose=self.verbose)
|
||||
self.seen = 0
|
||||
self.totals = {}
|
||||
|
||||
def on_batch_begin(self, batch, logs={}):
|
||||
if self.seen < self.params['nb_sample']:
|
||||
@@ -155,11 +179,6 @@ class BaseLogger(Callback):
|
||||
batch_size = logs.get('size', 0)
|
||||
self.seen += batch_size
|
||||
|
||||
for k, v in logs.items():
|
||||
if k in self.totals:
|
||||
self.totals[k] += v * batch_size
|
||||
else:
|
||||
self.totals[k] = v * batch_size
|
||||
for k in self.params['metrics']:
|
||||
if k in logs:
|
||||
self.log_values.append((k, logs[k]))
|
||||
@@ -171,8 +190,6 @@ class BaseLogger(Callback):
|
||||
|
||||
def on_epoch_end(self, epoch, logs={}):
|
||||
for k in self.params['metrics']:
|
||||
if k in self.totals:
|
||||
self.log_values.append((k, self.totals[k] / self.seen))
|
||||
if k in logs:
|
||||
self.log_values.append((k, logs[k]))
|
||||
if self.verbose:
|
||||
@@ -191,26 +208,8 @@ class History(Callback):
|
||||
self.epoch = []
|
||||
self.history = {}
|
||||
|
||||
def on_epoch_begin(self, epoch, logs={}):
|
||||
self.seen = 0
|
||||
self.totals = {}
|
||||
|
||||
def on_batch_end(self, batch, logs={}):
|
||||
batch_size = logs.get('size', 0)
|
||||
self.seen += batch_size
|
||||
for k, v in logs.items():
|
||||
if k in self.totals:
|
||||
self.totals[k] += v * batch_size
|
||||
else:
|
||||
self.totals[k] = v * batch_size
|
||||
|
||||
def on_epoch_end(self, epoch, logs={}):
|
||||
self.epoch.append(epoch)
|
||||
for k, v in self.totals.items():
|
||||
if k not in self.history:
|
||||
self.history[k] = []
|
||||
self.history[k].append(v / self.seen)
|
||||
|
||||
for k, v in logs.items():
|
||||
if k not in self.history:
|
||||
self.history[k] = []
|
||||
@@ -256,7 +255,7 @@ class ModelCheckpoint(Callback):
|
||||
|
||||
if mode not in ['auto', 'min', 'max']:
|
||||
warnings.warn('ModelCheckpoint mode %s is unknown, '
|
||||
'fallback to auto mode.' % (self.mode),
|
||||
'fallback to auto mode.' % (mode),
|
||||
RuntimeWarning)
|
||||
mode = 'auto'
|
||||
|
||||
@@ -373,26 +372,10 @@ class RemoteMonitor(Callback):
|
||||
def __init__(self, root='http://localhost:9000'):
|
||||
self.root = root
|
||||
|
||||
def on_epoch_begin(self, epoch, logs={}):
|
||||
self.seen = 0
|
||||
self.totals = {}
|
||||
|
||||
def on_batch_end(self, batch, logs={}):
|
||||
batch_size = logs.get('size', 0)
|
||||
self.seen += batch_size
|
||||
for k, v in logs.items():
|
||||
if k in self.totals:
|
||||
self.totals[k] += v * batch_size
|
||||
else:
|
||||
self.totals[k] = v * batch_size
|
||||
|
||||
def on_epoch_end(self, epoch, logs={}):
|
||||
import requests
|
||||
send = {}
|
||||
send['epoch'] = epoch
|
||||
|
||||
for k, v in self.totals.items():
|
||||
send[k] = v / self.seen
|
||||
for k, v in logs.items():
|
||||
send[k] = v
|
||||
|
||||
@@ -463,61 +446,35 @@ class TensorBoard(Callback):
|
||||
import keras.backend.tensorflow_backend as KTF
|
||||
|
||||
self.model = model
|
||||
self.sess = KTF._get_session()
|
||||
self.sess = KTF.get_session()
|
||||
if self.histogram_freq and not self.merged:
|
||||
mod_type = self.model.get_config()['name']
|
||||
if mod_type == 'Sequential':
|
||||
layers = {l.get_config()['name']: l for l in self.model.layers}
|
||||
elif mod_type == 'Graph':
|
||||
layers = self.model.nodes
|
||||
else:
|
||||
raise Exception('Unrecognized model:',
|
||||
self.model.get_config()['name'])
|
||||
for l in layers:
|
||||
cur_layer = layers[l]
|
||||
if hasattr(cur_layer, 'W'):
|
||||
tf.histogram_summary('{}_W'.format(l), cur_layer.W)
|
||||
if hasattr(cur_layer, 'b'):
|
||||
tf.histogram_summary('{}_b'.format(l), cur_layer.b)
|
||||
if hasattr(cur_layer, 'get_output'):
|
||||
tf.histogram_summary('{}_out'.format(l),
|
||||
cur_layer.get_output())
|
||||
layers = self.model.layers
|
||||
for layer in layers:
|
||||
if hasattr(layer, 'W'):
|
||||
tf.histogram_summary('{}_W'.format(layer), layer.W)
|
||||
if hasattr(layer, 'b'):
|
||||
tf.histogram_summary('{}_b'.format(layer), layer.b)
|
||||
if hasattr(layer, 'output'):
|
||||
tf.histogram_summary('{}_out'.format(layer),
|
||||
layer.output)
|
||||
self.merged = tf.merge_all_summaries()
|
||||
self.writer = tf.train.SummaryWriter(self.log_dir,
|
||||
self.sess.graph_def)
|
||||
|
||||
def on_epoch_begin(self, epoch, logs={}):
|
||||
self.seen = 0
|
||||
self.totals = {}
|
||||
|
||||
def on_batch_end(self, batch, logs={}):
|
||||
batch_size = logs.get('size', 0)
|
||||
self.seen += batch_size
|
||||
for k, v in logs.items():
|
||||
if k in self.totals:
|
||||
self.totals[k] += v * batch_size
|
||||
else:
|
||||
self.totals[k] = v * batch_size
|
||||
|
||||
def on_epoch_end(self, epoch, logs={}):
|
||||
import tensorflow as tf
|
||||
|
||||
if self.model.validation_data and self.histogram_freq:
|
||||
if epoch % self.histogram_freq == 0:
|
||||
if self.params.get('show_accuracy'):
|
||||
test_function = self.model._test_with_acc
|
||||
else:
|
||||
test_function = self.model._test
|
||||
names = [v.name for v in test_function.inputs]
|
||||
feed_dict = dict(zip(names, self.model.validation_data))
|
||||
# TODO: implement batched calls to sess.run
|
||||
# (current call will likely go OOM on GPU)
|
||||
feed_dict = dict(zip(self.model.inputs,
|
||||
self.model.validation_data))
|
||||
result = self.sess.run([self.merged], feed_dict=feed_dict)
|
||||
summary_str = result[0]
|
||||
self.writer.add_summary(summary_str, epoch)
|
||||
|
||||
all_values = self.totals.copy()
|
||||
all_values.update(logs)
|
||||
|
||||
for name, value in all_values.items():
|
||||
for name, value in logs.items():
|
||||
if name in ['batch', 'size']:
|
||||
continue
|
||||
summary = tf.Summary()
|
||||
|
||||
+8
-10
@@ -7,7 +7,7 @@ class Constraint(object):
|
||||
return p
|
||||
|
||||
def get_config(self):
|
||||
return {"name": self.__class__.__name__}
|
||||
return {'name': self.__class__.__name__}
|
||||
|
||||
|
||||
class MaxNorm(Constraint):
|
||||
@@ -42,9 +42,9 @@ class MaxNorm(Constraint):
|
||||
return p
|
||||
|
||||
def get_config(self):
|
||||
return {"name": self.__class__.__name__,
|
||||
"m": self.m,
|
||||
"axis": self.axis}
|
||||
return {'name': self.__class__.__name__,
|
||||
'm': self.m,
|
||||
'axis': self.axis}
|
||||
|
||||
|
||||
class NonNeg(Constraint):
|
||||
@@ -79,17 +79,15 @@ class UnitNorm(Constraint):
|
||||
return p / (K.epsilon() + K.sqrt(K.sum(K.square(p), axis=self.axis, keepdims=True)))
|
||||
|
||||
def get_config(self):
|
||||
return {"name": self.__class__.__name__,
|
||||
"axis": self.axis}
|
||||
return {'name': self.__class__.__name__,
|
||||
'axis': self.axis}
|
||||
|
||||
|
||||
identity = Constraint
|
||||
maxnorm = MaxNorm
|
||||
nonneg = NonNeg
|
||||
unitnorm = UnitNorm
|
||||
|
||||
from .utils.generic_utils import get_from_module
|
||||
|
||||
|
||||
def get(identifier, kwargs=None):
|
||||
return get_from_module(identifier, globals(), 'constraint', instantiate=True, kwargs=kwargs)
|
||||
return get_from_module(identifier, globals(), 'constraint',
|
||||
instantiate=True, kwargs=kwargs)
|
||||
|
||||
@@ -4,6 +4,7 @@ import sys
|
||||
from six.moves import cPickle
|
||||
from six.moves import range
|
||||
|
||||
|
||||
def load_batch(fpath, label_key='labels'):
|
||||
f = open(fpath, 'rb')
|
||||
if sys.version_info < (3,):
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from __future__ import absolute_import
|
||||
from .cifar import load_batch
|
||||
from .data_utils import get_file
|
||||
from ..utils.data_utils import get_file
|
||||
import numpy as np
|
||||
import os
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from __future__ import absolute_import
|
||||
from .cifar import load_batch
|
||||
from .data_utils import get_file
|
||||
from ..utils.data_utils import get_file
|
||||
import numpy as np
|
||||
import os
|
||||
|
||||
|
||||
@@ -1,53 +1,4 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import print_function
|
||||
from ..utils.data_utils import *
|
||||
import warnings
|
||||
|
||||
import tarfile
|
||||
import os
|
||||
from six.moves.urllib.request import FancyURLopener
|
||||
|
||||
from ..utils.generic_utils import Progbar
|
||||
|
||||
|
||||
class ParanoidURLopener(FancyURLopener):
|
||||
def http_error_default(self, url, fp, errcode, errmsg, headers):
|
||||
raise Exception('URL fetch failure on {}: {} -- {}'.format(url, errcode, errmsg))
|
||||
|
||||
|
||||
def get_file(fname, origin, untar=False):
|
||||
datadir_base = os.path.expanduser(os.path.join('~', '.keras'))
|
||||
if not os.access(datadir_base, os.W_OK):
|
||||
datadir_base = os.path.join('/tmp', '.keras')
|
||||
datadir = os.path.join(datadir_base, 'datasets')
|
||||
if not os.path.exists(datadir):
|
||||
os.makedirs(datadir)
|
||||
|
||||
if untar:
|
||||
untar_fpath = os.path.join(datadir, fname)
|
||||
fpath = untar_fpath + '.tar.gz'
|
||||
else:
|
||||
fpath = os.path.join(datadir, fname)
|
||||
|
||||
if not os.path.exists(fpath):
|
||||
print('Downloading data from', origin)
|
||||
global progbar
|
||||
progbar = None
|
||||
|
||||
def dl_progress(count, block_size, total_size):
|
||||
global progbar
|
||||
if progbar is None:
|
||||
progbar = Progbar(total_size)
|
||||
else:
|
||||
progbar.update(count*block_size)
|
||||
|
||||
ParanoidURLopener().retrieve(origin, fpath, dl_progress)
|
||||
progbar = None
|
||||
|
||||
if untar:
|
||||
if not os.path.exists(untar_fpath):
|
||||
print('Untaring file...')
|
||||
tfile = tarfile.open(fpath, 'r:gz')
|
||||
tfile.extractall(path=datadir)
|
||||
tfile.close()
|
||||
return untar_fpath
|
||||
|
||||
return fpath
|
||||
warnings.warn('data_utils has been moved to keras.utils.data_utils.')
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from __future__ import absolute_import
|
||||
from six.moves import cPickle
|
||||
import gzip
|
||||
from .data_utils import get_file
|
||||
from ..utils.data_utils import get_file
|
||||
from six.moves import zip
|
||||
import numpy as np
|
||||
|
||||
@@ -60,10 +60,10 @@ def load_data(path="imdb.pkl", nb_words=None, skip_top=0,
|
||||
nX.append(nx)
|
||||
X = nX
|
||||
|
||||
X_train = X[:int(len(X) * (1 - test_split))]
|
||||
y_train = labels[:int(len(X) * (1 - test_split))]
|
||||
X_train = np.array(X[:int(len(X) * (1 - test_split))])
|
||||
y_train = np.array(labels[:int(len(X) * (1 - test_split))])
|
||||
|
||||
X_test = X[int(len(X) * (1 - test_split)):]
|
||||
y_test = labels[int(len(X) * (1 - test_split)):]
|
||||
X_test = np.array(X[int(len(X) * (1 - test_split)):])
|
||||
y_test = np.array(labels[int(len(X) * (1 - test_split)):])
|
||||
|
||||
return (X_train, y_train), (X_test, y_test)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import gzip
|
||||
from .data_utils import get_file
|
||||
from ..utils.data_utils import get_file
|
||||
from six.moves import cPickle
|
||||
import sys
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import
|
||||
from .data_utils import get_file
|
||||
from ..utils.data_utils import get_file
|
||||
from six.moves import cPickle
|
||||
from six.moves import zip
|
||||
import numpy as np
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# note: topology.Node is an internal class,
|
||||
# it isn't meant to be used by Keras users.
|
||||
from .topology import InputSpec
|
||||
from .topology import Input
|
||||
from .topology import InputLayer
|
||||
from .topology import Layer
|
||||
from .topology import Merge
|
||||
from .topology import merge
|
||||
from .topology import get_source_inputs
|
||||
from .training import Model
|
||||
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+33
-15
@@ -3,9 +3,26 @@ import numpy as np
|
||||
from . import backend as K
|
||||
|
||||
|
||||
def get_fans(shape):
|
||||
fan_in = shape[0] if len(shape) == 2 else np.prod(shape[1:])
|
||||
fan_out = shape[1] if len(shape) == 2 else shape[0]
|
||||
def get_fans(shape, dim_ordering='th'):
|
||||
if len(shape) == 2:
|
||||
fan_in = shape[0]
|
||||
fan_out = shape[1]
|
||||
elif len(shape) == 4 or len(shape) == 5:
|
||||
# assuming convolution kernels (2D or 3D).
|
||||
# TH kernel shape: (depth, input_depth, ...)
|
||||
# TF kernel shape: (..., input_depth, depth)
|
||||
if dim_ordering == 'th':
|
||||
fan_in = np.prod(shape[1:])
|
||||
fan_out = shape[0]
|
||||
elif dim_ordering == 'tf':
|
||||
fan_in = np.prod(shape[:-1])
|
||||
fan_out = shape[-1]
|
||||
else:
|
||||
raise Exception('Invalid dim_ordering: ' + dim_ordering)
|
||||
else:
|
||||
# no specific assumptions
|
||||
fan_in = np.sqrt(np.prod(shape))
|
||||
fan_out = np.sqrt(np.prod(shape))
|
||||
return fan_in, fan_out
|
||||
|
||||
|
||||
@@ -19,39 +36,39 @@ def normal(shape, scale=0.05, name=None):
|
||||
name=name)
|
||||
|
||||
|
||||
def lecun_uniform(shape, name=None):
|
||||
def lecun_uniform(shape, name=None, dim_ordering='th'):
|
||||
''' Reference: LeCun 98, Efficient Backprop
|
||||
http://yann.lecun.com/exdb/publis/pdf/lecun-98b.pdf
|
||||
'''
|
||||
fan_in, fan_out = get_fans(shape)
|
||||
fan_in, fan_out = get_fans(shape, dim_ordering=dim_ordering)
|
||||
scale = np.sqrt(3. / fan_in)
|
||||
return uniform(shape, scale, name=name)
|
||||
|
||||
|
||||
def glorot_normal(shape, name=None):
|
||||
def glorot_normal(shape, name=None, dim_ordering='th'):
|
||||
''' Reference: Glorot & Bengio, AISTATS 2010
|
||||
'''
|
||||
fan_in, fan_out = get_fans(shape)
|
||||
fan_in, fan_out = get_fans(shape, dim_ordering=dim_ordering)
|
||||
s = np.sqrt(2. / (fan_in + fan_out))
|
||||
return normal(shape, s, name=name)
|
||||
|
||||
|
||||
def glorot_uniform(shape, name=None):
|
||||
fan_in, fan_out = get_fans(shape)
|
||||
def glorot_uniform(shape, name=None, dim_ordering='th'):
|
||||
fan_in, fan_out = get_fans(shape, dim_ordering=dim_ordering)
|
||||
s = np.sqrt(6. / (fan_in + fan_out))
|
||||
return uniform(shape, s, name=name)
|
||||
|
||||
|
||||
def he_normal(shape, name=None):
|
||||
def he_normal(shape, name=None, dim_ordering='th'):
|
||||
''' Reference: He et al., http://arxiv.org/abs/1502.01852
|
||||
'''
|
||||
fan_in, fan_out = get_fans(shape)
|
||||
fan_in, fan_out = get_fans(shape, dim_ordering=dim_ordering)
|
||||
s = np.sqrt(2. / fan_in)
|
||||
return normal(shape, s, name=name)
|
||||
|
||||
|
||||
def he_uniform(shape, name=None):
|
||||
fan_in, fan_out = get_fans(shape)
|
||||
def he_uniform(shape, name=None, dim_ordering='th'):
|
||||
fan_in, fan_out = get_fans(shape, dim_ordering=dim_ordering)
|
||||
s = np.sqrt(6. / fan_in)
|
||||
return uniform(shape, s, name=name)
|
||||
|
||||
@@ -85,5 +102,6 @@ def one(shape, name=None):
|
||||
|
||||
|
||||
from .utils.generic_utils import get_from_module
|
||||
def get(identifier):
|
||||
return get_from_module(identifier, globals(), 'initialization')
|
||||
def get(identifier, **kwargs):
|
||||
return get_from_module(identifier, globals(),
|
||||
'initialization', kwargs=kwargs)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from __future__ import absolute_import
|
||||
from ..engine import Layer, Input, InputLayer, Merge, merge, InputSpec
|
||||
from .core import *
|
||||
from .convolutional import *
|
||||
from .recurrent import *
|
||||
@@ -6,3 +7,4 @@ from .normalization import *
|
||||
from .embeddings import *
|
||||
from .noise import *
|
||||
from .advanced_activations import *
|
||||
from .wrappers import *
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
from .. import initializations
|
||||
from ..layers.core import MaskedLayer
|
||||
from ..engine import Layer
|
||||
from .. import backend as K
|
||||
import numpy as np
|
||||
|
||||
|
||||
class LeakyReLU(MaskedLayer):
|
||||
class LeakyReLU(Layer):
|
||||
'''Special version of a Rectified Linear Unit
|
||||
that allows a small gradient when the unit is not active:
|
||||
`f(x) = alpha*x for x < 0`.
|
||||
`f(x) = alpha * x for x < 0`,
|
||||
`f(x) = x for x >= 0`.
|
||||
|
||||
# Input shape
|
||||
Arbitrary. Use the keyword argument `input_shape`
|
||||
@@ -21,22 +22,25 @@ class LeakyReLU(MaskedLayer):
|
||||
alpha: float >= 0. Negative slope coefficient.
|
||||
'''
|
||||
def __init__(self, alpha=0.3, **kwargs):
|
||||
super(LeakyReLU, self).__init__(**kwargs)
|
||||
self.supports_masking = True
|
||||
self.alpha = alpha
|
||||
super(LeakyReLU, self).__init__(**kwargs)
|
||||
|
||||
def get_output(self, train):
|
||||
X = self.get_input(train)
|
||||
return K.relu(X, alpha=self.alpha)
|
||||
def call(self, x, mask=None):
|
||||
return K.relu(x, alpha=self.alpha)
|
||||
|
||||
def get_config(self):
|
||||
config = {"name": self.__class__.__name__,
|
||||
"alpha": self.alpha}
|
||||
config = {'alpha': self.alpha}
|
||||
base_config = super(LeakyReLU, self).get_config()
|
||||
return dict(list(base_config.items()) + list(config.items()))
|
||||
|
||||
|
||||
class PReLU(MaskedLayer):
|
||||
'''
|
||||
class PReLU(Layer):
|
||||
'''Parametric Rectified Linear Unit:
|
||||
`f(x) = alphas * x for x < 0`,
|
||||
`f(x) = x for x >= 0`,
|
||||
where `alphas` is a learned array with the same shape as x.
|
||||
|
||||
# Input shape
|
||||
Arbitrary. Use the keyword argument `input_shape`
|
||||
(tuple of integers, does not include the samples axis)
|
||||
@@ -45,42 +49,44 @@ class PReLU(MaskedLayer):
|
||||
# Output shape
|
||||
Same shape as the input.
|
||||
|
||||
# Arguments:
|
||||
# Arguments
|
||||
init: initialization function for the weights.
|
||||
weights: initial weights, as a list of a single numpy array.
|
||||
|
||||
# References:
|
||||
# References
|
||||
- [Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification](http://arxiv.org/pdf/1502.01852v1.pdf)
|
||||
'''
|
||||
def __init__(self, init='zero', weights=None, **kwargs):
|
||||
self.supports_masking = True
|
||||
self.init = initializations.get(init)
|
||||
self.initial_weights = weights
|
||||
super(PReLU, self).__init__(**kwargs)
|
||||
|
||||
def build(self):
|
||||
input_shape = self.input_shape[1:]
|
||||
self.alphas = self.init(input_shape)
|
||||
def build(self, input_shape):
|
||||
self.alphas = self.init(input_shape[1:],
|
||||
name='{}_alphas'.format(self.name))
|
||||
self.trainable_weights = [self.alphas]
|
||||
|
||||
if self.initial_weights is not None:
|
||||
self.set_weights(self.initial_weights)
|
||||
del self.initial_weights
|
||||
|
||||
def get_output(self, train):
|
||||
X = self.get_input(train)
|
||||
pos = K.relu(X)
|
||||
neg = self.alphas * (X - abs(X)) * 0.5
|
||||
def call(self, x, mask=None):
|
||||
pos = K.relu(x)
|
||||
neg = self.alphas * (x - abs(x)) * 0.5
|
||||
return pos + neg
|
||||
|
||||
def get_config(self):
|
||||
config = {"name": self.__class__.__name__,
|
||||
"init": self.init.__name__}
|
||||
config = {'init': self.init.__name__}
|
||||
base_config = super(PReLU, self).get_config()
|
||||
return dict(list(base_config.items()) + list(config.items()))
|
||||
|
||||
|
||||
class ELU(MaskedLayer):
|
||||
'''
|
||||
class ELU(Layer):
|
||||
'''Exponential Linear Unit:
|
||||
`f(x) = alpha * (exp(x) - 1.) for x < 0`,
|
||||
`f(x) = x for x >= 0`.
|
||||
|
||||
# Input shape
|
||||
Arbitrary. Use the keyword argument `input_shape`
|
||||
(tuple of integers, does not include the samples axis)
|
||||
@@ -96,24 +102,24 @@ class ELU(MaskedLayer):
|
||||
- [Fast and Accurate Deep Network Learning by Exponential Linear Units (ELUs)](http://arxiv.org/pdf/1511.07289v1.pdf)
|
||||
'''
|
||||
def __init__(self, alpha=1.0, **kwargs):
|
||||
self.supports_masking = True
|
||||
self.alpha = K.cast_to_floatx(alpha)
|
||||
super(ELU, self).__init__(**kwargs)
|
||||
self.alpha = alpha
|
||||
|
||||
def get_output(self, train):
|
||||
X = self.get_input(train)
|
||||
pos = K.relu(X)
|
||||
neg = (X - abs(X)) * 0.5
|
||||
def call(self, x, mask=None):
|
||||
pos = K.relu(x)
|
||||
neg = (x - abs(x)) * 0.5
|
||||
return pos + self.alpha * (K.exp(neg) - 1.)
|
||||
|
||||
def get_config(self):
|
||||
config = {"name": self.__class__.__name__,
|
||||
"alpha": self.alpha}
|
||||
config = {'alpha': self.alpha}
|
||||
base_config = super(ELU, self).get_config()
|
||||
return dict(list(base_config.items()) + list(config.items()))
|
||||
|
||||
|
||||
class ParametricSoftplus(MaskedLayer):
|
||||
'''Parametric Softplus of the form: alpha * log(1 + exp(beta * X))
|
||||
class ParametricSoftplus(Layer):
|
||||
'''Parametric Softplus:
|
||||
`alpha * log(1 + exp(beta * x))`
|
||||
|
||||
# Input shape
|
||||
Arbitrary. Use the keyword argument `input_shape`
|
||||
@@ -128,40 +134,43 @@ class ParametricSoftplus(MaskedLayer):
|
||||
beta_init: float. Initial values of the beta weights.
|
||||
weights: initial weights, as a list of 2 numpy arrays.
|
||||
|
||||
# References:
|
||||
# References
|
||||
- [Inferring Nonlinear Neuronal Computation Based on Physiologically Plausible Inputs](http://journals.plos.org/ploscompbiol/article?id=10.1371/journal.pcbi.1003143)
|
||||
'''
|
||||
def __init__(self, alpha_init=0.2, beta_init=5.0,
|
||||
weights=None, **kwargs):
|
||||
self.alpha_init = alpha_init
|
||||
self.beta_init = beta_init
|
||||
self.supports_masking = True
|
||||
self.alpha_init = K.cast_to_floatx(alpha_init)
|
||||
self.beta_init = K.cast_to_floatx(beta_init)
|
||||
self.initial_weights = weights
|
||||
super(ParametricSoftplus, self).__init__(**kwargs)
|
||||
|
||||
def build(self):
|
||||
input_shape = self.input_shape[1:]
|
||||
self.alphas = K.variable(self.alpha_init * np.ones(input_shape))
|
||||
self.betas = K.variable(self.beta_init * np.ones(input_shape))
|
||||
def build(self, input_shape):
|
||||
input_shape = input_shape[1:]
|
||||
self.alphas = K.variable(self.alpha_init * np.ones(input_shape),
|
||||
name='{}_alphas'.format(self.name))
|
||||
self.betas = K.variable(self.beta_init * np.ones(input_shape),
|
||||
name='{}_betas'.format(self.name))
|
||||
self.trainable_weights = [self.alphas, self.betas]
|
||||
|
||||
if self.initial_weights is not None:
|
||||
self.set_weights(self.initial_weights)
|
||||
del self.initial_weights
|
||||
|
||||
def get_output(self, train):
|
||||
X = self.get_input(train)
|
||||
return K.softplus(self.betas * X) * self.alphas
|
||||
def call(self, x, mask=None):
|
||||
return K.softplus(self.betas * x) * self.alphas
|
||||
|
||||
def get_config(self):
|
||||
config = {"name": self.__class__.__name__,
|
||||
"alpha_init": self.alpha_init,
|
||||
"beta_init": self.beta_init}
|
||||
config = {'alpha_init': self.alpha_init,
|
||||
'beta_init': self.beta_init}
|
||||
base_config = super(ParametricSoftplus, self).get_config()
|
||||
return dict(list(base_config.items()) + list(config.items()))
|
||||
|
||||
|
||||
class ThresholdedLinear(MaskedLayer):
|
||||
'''Thresholded Linear Activation.
|
||||
class ThresholdedReLU(Layer):
|
||||
'''Thresholded Rectified Linear Unit:
|
||||
`f(x) = x for x > theta`
|
||||
`f(x) = 0 otherwise`.
|
||||
|
||||
# Input shape
|
||||
Arbitrary. Use the keyword argument `input_shape`
|
||||
@@ -175,50 +184,83 @@ class ThresholdedLinear(MaskedLayer):
|
||||
theta: float >= 0. Threshold location of activation.
|
||||
|
||||
# References
|
||||
[Zero-Bias Autoencoders and the Benefits of Co-Adapting Features](http://arxiv.org/pdf/1402.3337.pdf)
|
||||
'''
|
||||
def __init__(self, theta=1.0, **kwargs):
|
||||
super(ThresholdedLinear, self).__init__(**kwargs)
|
||||
self.theta = theta
|
||||
|
||||
def get_output(self, train):
|
||||
X = self.get_input(train)
|
||||
return K.switch(K.abs(X) < self.theta, 0, X)
|
||||
|
||||
def get_config(self):
|
||||
config = {"name": self.__class__.__name__,
|
||||
"theta": self.theta}
|
||||
base_config = super(ThresholdedLinear, self).get_config()
|
||||
return dict(list(base_config.items()) + list(config.items()))
|
||||
|
||||
|
||||
class ThresholdedReLU(MaskedLayer):
|
||||
'''Thresholded Rectified Activation.
|
||||
|
||||
# Input shape
|
||||
Arbitrary. Use the keyword argument `input_shape`
|
||||
(tuple of integers, does not include the samples axis)
|
||||
when using this layer as the first layer in a model.
|
||||
|
||||
# Output shape
|
||||
Same shape as the input.
|
||||
|
||||
# Arguments
|
||||
theta: float >= 0. Threshold location of activation.
|
||||
|
||||
# References
|
||||
[Zero-Bias Autoencoders and the Benefits of Co-Adapting Features](http://arxiv.org/pdf/1402.3337.pdf)
|
||||
- [Zero-Bias Autoencoders and the Benefits of Co-Adapting Features](http://arxiv.org/pdf/1402.3337.pdf)
|
||||
'''
|
||||
def __init__(self, theta=1.0, **kwargs):
|
||||
self.supports_masking = True
|
||||
self.theta = K.cast_to_floatx(theta)
|
||||
super(ThresholdedReLU, self).__init__(**kwargs)
|
||||
self.theta = theta
|
||||
|
||||
def get_output(self, train):
|
||||
X = self.get_input(train)
|
||||
return K.switch(X > self.theta, X, 0)
|
||||
def call(self, x, mask=None):
|
||||
return x * K.cast(x > self.theta, K.floatx())
|
||||
|
||||
def get_config(self):
|
||||
config = {"name": self.__class__.__name__,
|
||||
"theta": self.theta}
|
||||
config = {'theta': self.theta}
|
||||
base_config = super(ThresholdedReLU, self).get_config()
|
||||
return dict(list(base_config.items()) + list(config.items()))
|
||||
|
||||
|
||||
class SReLU(Layer):
|
||||
'''S-shaped Rectified Linear Unit.
|
||||
|
||||
# Input shape
|
||||
Arbitrary. Use the keyword argument `input_shape`
|
||||
(tuple of integers, does not include the samples axis)
|
||||
when using this layer as the first layer in a model.
|
||||
|
||||
# Output shape
|
||||
Same shape as the input.
|
||||
|
||||
# Arguments
|
||||
t_left_init: initialization function for the left part intercept
|
||||
a_left_init: initialization function for the left part slope
|
||||
t_right_init: initialization function for the right part intercept
|
||||
a_right_init: initialization function for the right part slope
|
||||
|
||||
# References
|
||||
- [Deep Learning with S-shaped Rectified Linear Activation Units](http://arxiv.org/abs/1512.07030)
|
||||
'''
|
||||
def __init__(self, t_left_init='zero', a_left_init='glorot_uniform',
|
||||
t_right_init='glorot_uniform', a_right_init='one', **kwargs):
|
||||
self.supports_masking = True
|
||||
self.t_left_init = t_left_init
|
||||
self.a_left_init = a_left_init
|
||||
self.t_right_init = t_right_init
|
||||
self.a_right_init = a_right_init
|
||||
super(SReLU, self).__init__(**kwargs)
|
||||
|
||||
def build(self, input_shape):
|
||||
input_shape = input_shape[1:]
|
||||
|
||||
t_left_init = initializations.get(self.t_left_init)
|
||||
a_left_init = initializations.get(self.a_left_init)
|
||||
t_right_init = initializations.get(self.t_right_init)
|
||||
a_right_init = initializations.get(self.a_right_init)
|
||||
|
||||
self.t_left = t_left_init(input_shape,
|
||||
name='{}_t_left'.format(self.name))
|
||||
self.a_left = a_left_init(input_shape,
|
||||
name='{}_a_left'.format(self.name))
|
||||
self.t_right = t_right_init(input_shape,
|
||||
name='{}_t_right'.format(self.name))
|
||||
self.a_right = a_right_init(input_shape,
|
||||
name='{}_a_right'.format(self.name))
|
||||
# ensure the the right part is always to the right of the left
|
||||
self.t_right_actual = self.t_left + abs(self.t_right)
|
||||
self.trainable_weights = [self.t_left, self.a_left,
|
||||
self.t_right, self.a_right]
|
||||
|
||||
def call(self, x, mask=None):
|
||||
Y_left_and_center = self.t_left + K.relu(x - self.t_left,
|
||||
self.a_left,
|
||||
self.t_right_actual - self.t_left)
|
||||
Y_right = K.relu(x - self.t_right_actual) * self.a_right
|
||||
return Y_left_and_center + Y_right
|
||||
|
||||
def get_config(self):
|
||||
config = {'t_left_init': self.t_left_init,
|
||||
'a_left_init': self.a_left_init,
|
||||
't_right_init': self.t_right_init,
|
||||
'a_right_init': self.a_right_init}
|
||||
base_config = super(SReLU, self).get_config()
|
||||
return dict(list(base_config.items()) + list(config.items()))
|
||||
|
||||
@@ -1,533 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import
|
||||
from __future__ import print_function
|
||||
|
||||
from collections import OrderedDict
|
||||
from .. import backend as K
|
||||
from ..layers.core import Layer, Merge, Siamese, SiameseHead
|
||||
from six.moves import range
|
||||
|
||||
|
||||
class Sequential(Layer):
|
||||
'''The Sequential container is a linear stack of layers.
|
||||
Apart from the `add` methods and the `layers` constructor argument,
|
||||
the API is identical to that of the `Layer` class.
|
||||
|
||||
This class is also the basis for the `keras.models.Sequential` model.
|
||||
|
||||
# Arguments
|
||||
layers: list of layers to be added to the container.
|
||||
'''
|
||||
def __init__(self, layers=[]):
|
||||
self.layers = []
|
||||
self.layer_cache = {}
|
||||
for layer in layers:
|
||||
self.add(layer)
|
||||
self._cache_enabled = True
|
||||
|
||||
def __call__(self, X, mask=None, train=False):
|
||||
# turn off layer cache temporarily
|
||||
tmp_cache_enabled = self.cache_enabled
|
||||
self.cache_enabled = False
|
||||
# recursively search for a layer which is not a Sequential model
|
||||
layer = self
|
||||
while issubclass(layer.__class__, Sequential):
|
||||
layer = layer.layers[0]
|
||||
# set temporary input to first layer
|
||||
tmp_input = layer.get_input
|
||||
tmp_mask = None
|
||||
layer.get_input = lambda _: X
|
||||
if hasattr(layer, 'get_input_mask'):
|
||||
tmp_mask = layer.get_input_mask
|
||||
layer.get_input_mask = lambda _: mask
|
||||
Y = self.get_output(train=train)
|
||||
# return input from first layer to what it was
|
||||
layer.get_input = tmp_input
|
||||
if hasattr(layer, 'get_input_mask'):
|
||||
layer.get_input_mask = tmp_mask
|
||||
self.cache_enabled = tmp_cache_enabled
|
||||
return Y
|
||||
|
||||
@property
|
||||
def cache_enabled(self):
|
||||
return self._cache_enabled
|
||||
|
||||
@cache_enabled.setter
|
||||
def cache_enabled(self, value):
|
||||
self._cache_enabled = value
|
||||
for l in self.layers:
|
||||
l.cache_enabled = value
|
||||
|
||||
def set_previous(self, layer):
|
||||
self.layers[0].previous = layer
|
||||
|
||||
def add(self, layer):
|
||||
layer.layer_cache = self.layer_cache
|
||||
self.layers.append(layer)
|
||||
if len(self.layers) > 1:
|
||||
self.layers[-1].set_previous(self.layers[-2])
|
||||
if not hasattr(self.layers[0], 'input'):
|
||||
self.set_input()
|
||||
|
||||
@property
|
||||
def trainable_weights(self):
|
||||
weights = []
|
||||
for l in self.layers:
|
||||
if l.trainable:
|
||||
weights += l.get_params()[0]
|
||||
return weights
|
||||
|
||||
@property
|
||||
def regularizers(self):
|
||||
regularizers = []
|
||||
for l in self.layers:
|
||||
if l.trainable:
|
||||
regularizers += l.get_params()[1]
|
||||
return regularizers
|
||||
|
||||
@property
|
||||
def constraints(self):
|
||||
constraints = []
|
||||
for l in self.layers:
|
||||
if l.trainable:
|
||||
constraints += l.get_params()[2]
|
||||
return constraints
|
||||
|
||||
@property
|
||||
def updates(self):
|
||||
updates = []
|
||||
for l in self.layers:
|
||||
if l.trainable:
|
||||
updates += l.get_params()[3]
|
||||
return updates
|
||||
|
||||
@property
|
||||
def state_updates(self):
|
||||
"""
|
||||
Return the `updates` from all layers in the sequence that are
|
||||
stateful. This is useful for separating _training_ updates and
|
||||
_prediction_ updates for when we need to update a layers internal state
|
||||
during a stateful prediction.
|
||||
"""
|
||||
state_updates = []
|
||||
for l in self.layers:
|
||||
if getattr(l, 'stateful', False):
|
||||
state_updates += l.get_params()[3]
|
||||
return state_updates
|
||||
|
||||
def reset_states(self):
|
||||
for l in self.layers:
|
||||
if hasattr(l, 'reset_states') and getattr(l, 'stateful', False):
|
||||
l.reset_states()
|
||||
|
||||
@property
|
||||
def output_shape(self):
|
||||
return self.layers[-1].output_shape
|
||||
|
||||
def get_output(self, train=False):
|
||||
return self.layers[-1].get_output(train)
|
||||
|
||||
def set_input(self):
|
||||
for l in self.layers:
|
||||
if hasattr(l, 'input'):
|
||||
ndim = K.ndim(l.input)
|
||||
self.layers[0].input = K.placeholder(ndim=ndim)
|
||||
break
|
||||
|
||||
def get_input(self, train=False):
|
||||
if not hasattr(self.layers[0], 'input'):
|
||||
self.set_input()
|
||||
return self.layers[0].get_input(train)
|
||||
|
||||
@property
|
||||
def input_shape(self):
|
||||
return self.layers[0].input_shape
|
||||
|
||||
@property
|
||||
def input(self):
|
||||
return self.get_input()
|
||||
|
||||
def get_weights(self):
|
||||
weights = []
|
||||
for layer in self.layers:
|
||||
weights += layer.get_weights()
|
||||
return weights
|
||||
|
||||
def set_weights(self, weights):
|
||||
for i in range(len(self.layers)):
|
||||
nb_param = len(self.layers[i].trainable_weights) + len(self.layers[i].non_trainable_weights)
|
||||
self.layers[i].set_weights(weights[:nb_param])
|
||||
weights = weights[nb_param:]
|
||||
|
||||
def get_config(self):
|
||||
return {'name': self.__class__.__name__,
|
||||
'layers': [layer.get_config() for layer in self.layers]}
|
||||
|
||||
def count_params(self):
|
||||
return sum([layer.count_params() for layer in self.layers])
|
||||
|
||||
|
||||
class Graph(Layer):
|
||||
'''Implement a NN graph with arbitrary layer connections,
|
||||
arbitrary number of inputs and arbitrary number of outputs.
|
||||
|
||||
This class is also the basis for the `keras.models.Graph` model.
|
||||
|
||||
Note: `Graph` can only be used as a layer
|
||||
(connect, input, get_input, get_output)
|
||||
when it has exactly one input and one output.
|
||||
'''
|
||||
def __init__(self):
|
||||
self.namespace = set() # strings
|
||||
self.nodes = OrderedDict() # layer-like
|
||||
self.inputs = {} # layer-like
|
||||
self.input_order = [] # strings
|
||||
self.outputs = {} # layer-like
|
||||
self.output_order = [] # strings
|
||||
self.input_config = [] # dicts
|
||||
self.output_config = [] # dicts
|
||||
self.node_config = [] # dicts
|
||||
self.layer_cache = {}
|
||||
|
||||
@property
|
||||
def nb_input(self):
|
||||
return len(self.inputs)
|
||||
|
||||
@property
|
||||
def nb_output(self):
|
||||
return len(self.outputs)
|
||||
|
||||
@property
|
||||
def trainable_weights(self):
|
||||
weights = []
|
||||
for l in self.nodes.values():
|
||||
if l.trainable:
|
||||
weights += l.get_params()[0]
|
||||
return weights
|
||||
|
||||
@property
|
||||
def regularizers(self):
|
||||
regularizers = []
|
||||
for l in self.nodes.values():
|
||||
if l.trainable:
|
||||
regularizers += l.get_params()[1]
|
||||
return regularizers
|
||||
|
||||
@property
|
||||
def constraints(self):
|
||||
constraints = []
|
||||
for l in self.nodes.values():
|
||||
if l.trainable:
|
||||
constraints += l.get_params()[2]
|
||||
return constraints
|
||||
|
||||
@property
|
||||
def updates(self):
|
||||
updates = []
|
||||
for l in self.nodes.values():
|
||||
if l.trainable:
|
||||
updates += l.get_params()[3]
|
||||
return updates
|
||||
|
||||
@property
|
||||
def state_updates(self):
|
||||
"""
|
||||
Return the `updates` from all nodes in that graph for nodes that are
|
||||
stateful. This is useful for separating _training_ updates and
|
||||
_prediction_ updates for when we need to update a layers internal state
|
||||
during a stateful prediction.
|
||||
"""
|
||||
state_updates = []
|
||||
for l in self.nodes.values():
|
||||
if getattr(l, 'stateful', False):
|
||||
state_updates += l.get_params()[3]
|
||||
return state_updates
|
||||
|
||||
def reset_states(self):
|
||||
for l in self.nodes.values():
|
||||
if hasattr(l, 'reset_states') and getattr(l, 'stateful', False):
|
||||
l.reset_states()
|
||||
|
||||
def set_previous(self, layer, connection_map={}):
|
||||
if self.nb_input != layer.nb_output:
|
||||
raise Exception('Cannot connect layers: '
|
||||
'input count does not match output count.')
|
||||
if self.nb_input == 1:
|
||||
self.inputs[self.input_order[0]].set_previous(layer)
|
||||
else:
|
||||
if not connection_map:
|
||||
raise Exception('Cannot attach multi-input layer: '
|
||||
'no connection_map provided.')
|
||||
for k, v in connection_map.items():
|
||||
if k in self.inputs and v in layer.outputs:
|
||||
self.inputs[k].set_previous(layer.outputs[v])
|
||||
else:
|
||||
raise Exception('Invalid connection map.')
|
||||
|
||||
def get_input(self, train=False):
|
||||
if len(self.inputs) == len(self.outputs) == 1:
|
||||
return self.inputs[self.input_order[0]].get_input(train)
|
||||
else:
|
||||
return dict([(k, v.get_input(train)) for k, v in self.inputs.items()])
|
||||
|
||||
@property
|
||||
def input(self):
|
||||
return self.get_input()
|
||||
|
||||
@property
|
||||
def output_shape(self):
|
||||
if self.nb_output == 1:
|
||||
# return tuple
|
||||
return self.outputs[self.output_order[0]].output_shape
|
||||
else:
|
||||
# return dictionary mapping output names to shape tuples
|
||||
return dict([(k, v.output_shape) for k, v in self.outputs.items()])
|
||||
|
||||
def get_output(self, train=False):
|
||||
if len(self.inputs) == len(self.outputs) == 1:
|
||||
return self.outputs[self.output_order[0]].get_output(train)
|
||||
else:
|
||||
return dict([(k, v.get_output(train)) for k, v in self.outputs.items()])
|
||||
|
||||
def add_input(self, name, input_shape=None,
|
||||
batch_input_shape=None, dtype='float'):
|
||||
'''Add an input to the graph.
|
||||
|
||||
# Arguments:
|
||||
name: string. The name of the new input. Must be unique in the graph.
|
||||
input_shape: a tuple of integers, the expected shape of the input samples.
|
||||
Does not include the batch size.
|
||||
batch_input_shape: a tuple of integers, the expected shape of the
|
||||
whole input batch, including the batch size.
|
||||
dtype: 'float' or 'int'.
|
||||
'''
|
||||
if name in self.namespace:
|
||||
raise Exception('Duplicate node identifier: ' + name)
|
||||
self.namespace.add(name)
|
||||
self.input_order.append(name)
|
||||
layer = Layer(name=name) # empty layer
|
||||
if input_shape:
|
||||
layer.set_input_shape((None,) + tuple(input_shape))
|
||||
elif batch_input_shape:
|
||||
layer.set_input_shape(batch_input_shape)
|
||||
if dtype == 'float':
|
||||
layer.input = K.placeholder(shape=layer.input_shape, name=name)
|
||||
else:
|
||||
if (input_shape and len(input_shape) == 1) or (batch_input_shape and len(batch_input_shape) == 2):
|
||||
layer.input = K.placeholder(shape=layer.input_shape,
|
||||
dtype='int32',
|
||||
name=name)
|
||||
else:
|
||||
raise Exception('Type "int" can only be used with ndim==2 (Embedding).')
|
||||
self.inputs[name] = layer
|
||||
config = {'name': name, 'dtype': dtype}
|
||||
if batch_input_shape:
|
||||
config['batch_input_shape'] = batch_input_shape
|
||||
else:
|
||||
config['input_shape'] = input_shape
|
||||
self.input_config.append(config)
|
||||
|
||||
def add_node(self, layer, name, input=None, inputs=[],
|
||||
merge_mode='concat', concat_axis=-1, dot_axes=-1,
|
||||
create_output=False):
|
||||
'''Add a node in the graph. It can be connected to multiple
|
||||
inputs, which will first be merged into one tensor
|
||||
according to the mode specified.
|
||||
|
||||
# Arguments
|
||||
layer: the layer at the node.
|
||||
name: name for the node.
|
||||
input: when connecting the layer to a single input,
|
||||
this is the name of the incoming node.
|
||||
inputs: when connecting the layer to multiple inputs,
|
||||
this is a list of names of incoming nodes.
|
||||
merge_mode: one of {concat, sum, dot, ave, mul}
|
||||
concat_axis: when `merge_mode=='concat'`, this is the
|
||||
input concatenation axis.
|
||||
dot_axes: when `merge_mode='dot'`, this is the contraction axes
|
||||
specification; see the `Merge layer for details.
|
||||
create_output: boolean. Set this to `True` if you want the output
|
||||
of your node to be an output of the graph.
|
||||
'''
|
||||
if name in self.namespace:
|
||||
raise Exception('Duplicate node identifier: ' + name)
|
||||
layer.name = name
|
||||
if input:
|
||||
if input not in self.namespace:
|
||||
raise Exception('Unknown node/input identifier: ' + input)
|
||||
if input in self.nodes:
|
||||
layer.set_previous(self.nodes[input])
|
||||
elif input in self.inputs:
|
||||
layer.set_previous(self.inputs[input])
|
||||
if inputs:
|
||||
to_merge = []
|
||||
for n in inputs:
|
||||
if n in self.nodes:
|
||||
to_merge.append(self.nodes[n])
|
||||
elif n in self.inputs:
|
||||
to_merge.append(self.inputs[n])
|
||||
else:
|
||||
raise Exception('Unknown identifier: ' + n)
|
||||
merge = Merge(to_merge, mode=merge_mode,
|
||||
concat_axis=concat_axis, dot_axes=dot_axes)
|
||||
layer.set_previous(merge)
|
||||
|
||||
self.namespace.add(name)
|
||||
layer.layer_cache = self.layer_cache
|
||||
self.nodes[name] = layer
|
||||
self.node_config.append({'name': name,
|
||||
'input': input,
|
||||
'inputs': inputs,
|
||||
'merge_mode': merge_mode,
|
||||
'concat_axis': concat_axis,
|
||||
'dot_axes': dot_axes,
|
||||
'create_output': create_output})
|
||||
|
||||
if create_output:
|
||||
self.add_output(name, input=name)
|
||||
|
||||
def add_shared_node(self, layer, name, inputs=[], merge_mode=None,
|
||||
concat_axis=-1, dot_axes=-1, outputs=[],
|
||||
create_output=False):
|
||||
'''Used to share a same layer across multiple nodes.
|
||||
|
||||
Supposed, for instance, that you want to apply one same `Dense`
|
||||
layer after to the output of two different nodes.
|
||||
You can then add the `Dense` layer as a shared node.
|
||||
|
||||
# Arguments
|
||||
layer: The layer to be shared across multiple inputs
|
||||
name: Name of the shared node
|
||||
inputs: List of names of input nodes
|
||||
merge_mode: Same meaning as `merge_mode` argument of `add_node()`
|
||||
concat_axis: Same meaning as `concat_axis` argument of `add_node()`
|
||||
dot_axes: Same meaning as `dot_axes` argument of `add_node()`
|
||||
outputs: Used when `merge_mode=None`. Names for the output nodes.
|
||||
create_output: Same meaning as `create_output` argument of `add_node()`.
|
||||
'''
|
||||
if name in self.namespace:
|
||||
raise Exception('Duplicate node identifier: ' + name)
|
||||
for o in outputs:
|
||||
if o in self.namespace:
|
||||
raise Exception('Duplicate node identifier: ' + o)
|
||||
if merge_mode:
|
||||
if merge_mode not in {'sum', 'ave', 'mul', 'dot', 'cos', 'concat', 'join'}:
|
||||
raise Exception('Invalid merge mode')
|
||||
layers = []
|
||||
for i in range(len(inputs)):
|
||||
input = inputs[i]
|
||||
if input in self.nodes:
|
||||
n = self.nodes[input]
|
||||
if n.__class__.__name__ == 'Siamese':
|
||||
if n.merge_mode is None:
|
||||
for j in range(len(n.inputs)):
|
||||
sh = SiameseHead(j)
|
||||
sh.previous = n
|
||||
layers.append(sh)
|
||||
else:
|
||||
layers.append(n)
|
||||
else:
|
||||
layers.append(n)
|
||||
elif input in self.inputs:
|
||||
n = self.inputs[input]
|
||||
layers.append(n)
|
||||
else:
|
||||
raise Exception('Unknown identifier: ' + input)
|
||||
s = Siamese(layer, layers, merge_mode,
|
||||
concat_axis=concat_axis,
|
||||
dot_axes=dot_axes,
|
||||
is_graph=True)
|
||||
self.namespace.add(name)
|
||||
self.nodes[name] = s
|
||||
self.node_config.append({'name': name,
|
||||
'inputs': inputs,
|
||||
'merge_mode': merge_mode,
|
||||
'concat_axis': concat_axis,
|
||||
'dot_axes': dot_axes,
|
||||
'create_output': create_output if merge_mode else False})
|
||||
if not merge_mode:
|
||||
for i in range(len(outputs)):
|
||||
sh = SiameseHead(i)
|
||||
sh.previous = s
|
||||
sh_name = outputs[i]
|
||||
self.namespace.add(sh_name)
|
||||
self.nodes[sh_name] = sh
|
||||
self.node_config.append({'name': sh_name,
|
||||
'inputs': [name],
|
||||
'create_output': create_output})
|
||||
if create_output:
|
||||
self.add_output(sh_name, input=sh_name)
|
||||
|
||||
if create_output and merge_mode:
|
||||
if merge_mode == 'join':
|
||||
raise Exception('Output can not be of type OrderedDict')
|
||||
self.add_output(name, input=name)
|
||||
|
||||
def add_output(self, name, input=None, inputs=[],
|
||||
merge_mode='concat', concat_axis=-1, dot_axes=-1):
|
||||
'''Add an output to the graph.
|
||||
|
||||
This output can merge several node outputs into a single output.
|
||||
|
||||
# Arguments
|
||||
name: name of the output.
|
||||
input: when connecting the layer to a single input,
|
||||
this is the name of the incoming node.
|
||||
inputs: when connecting the layer to multiple inputs,
|
||||
this is a list of names of incoming nodes.
|
||||
merge_mode: one of {concat, sum, dot, ave, mul}
|
||||
concat_axis: when `merge_mode=='concat'`, this is the
|
||||
input concatenation axis.
|
||||
dot_axes: when `merge_mode='dot'`, this is the contraction axes
|
||||
specification; see the `Merge layer for details.
|
||||
'''
|
||||
if name in self.output_order:
|
||||
raise Exception('Duplicate output identifier: ' + name)
|
||||
if input:
|
||||
if input not in self.namespace:
|
||||
raise Exception('Unknown node/input identifier: ' + input)
|
||||
if input in self.nodes:
|
||||
self.outputs[name] = self.nodes[input]
|
||||
elif input in self.inputs:
|
||||
self.outputs[name] = self.inputs[input]
|
||||
if inputs:
|
||||
to_merge = []
|
||||
for n in inputs:
|
||||
if n not in self.nodes:
|
||||
raise Exception('Unknown identifier: ' + n)
|
||||
to_merge.append(self.nodes[n])
|
||||
merge = Merge(to_merge, mode=merge_mode,
|
||||
concat_axis=concat_axis, dot_axes=dot_axes)
|
||||
self.outputs[name] = merge
|
||||
|
||||
self.output_order.append(name)
|
||||
self.output_config.append({'name': name,
|
||||
'input': input,
|
||||
'inputs': inputs,
|
||||
'merge_mode': merge_mode,
|
||||
'concat_axis': concat_axis,
|
||||
'dot_axes': dot_axes})
|
||||
|
||||
def get_config(self):
|
||||
return {'name': self.__class__.__name__,
|
||||
'input_config': self.input_config,
|
||||
'node_config': self.node_config,
|
||||
'output_config': self.output_config,
|
||||
'input_order': self.input_order,
|
||||
'output_order': self.output_order,
|
||||
'nodes': dict([(c['name'], self.nodes[c['name']].get_config()) for c in self.node_config])}
|
||||
|
||||
def count_params(self):
|
||||
return sum([layer.count_params() for layer in self.nodes.values()])
|
||||
|
||||
def get_weights(self):
|
||||
weights = []
|
||||
for layer in self.nodes.values():
|
||||
weights += layer.get_weights()
|
||||
return weights
|
||||
|
||||
def set_weights(self, weights):
|
||||
for layer in self.nodes.values():
|
||||
nb_param = len(layer.get_weights())
|
||||
layer.set_weights(weights[:nb_param])
|
||||
weights = weights[nb_param:]
|
||||
+635
-170
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+597
-1483
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -1,10 +1,8 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from .. import backend as K
|
||||
|
||||
from .. import activations, initializations, regularizers, constraints
|
||||
from ..layers.core import Layer, MaskedLayer
|
||||
|
||||
from ..constraints import unitnorm
|
||||
from .. import initializations, regularizers, constraints
|
||||
from ..engine import Layer
|
||||
|
||||
|
||||
class Embedding(Layer):
|
||||
@@ -13,11 +11,21 @@ class Embedding(Layer):
|
||||
|
||||
This layer can only be used as the first layer in a model.
|
||||
|
||||
# Input shape
|
||||
2D tensor with shape: `(nb_samples, sequence_length)`.
|
||||
# Example
|
||||
|
||||
# Output shape
|
||||
3D tensor with shape: `(nb_samples, sequence_length, output_dim)`.
|
||||
```python
|
||||
model = Sequential()
|
||||
model.add(Embedding(1000, 64, input_length=10))
|
||||
# the model will take as input an integer matrix of size (batch, input_length).
|
||||
# the largest integer (i.e. word index) in the input should be no larger than 1000 (vocabulary size).
|
||||
# now model.output_shape == (None, 10, 64), where None is the batch dimension.
|
||||
|
||||
input_array = np.random.randint(1000, size=(32, 10))
|
||||
|
||||
model.compile('rmsprop', 'mse')
|
||||
output_array = model.predict(input_array)
|
||||
assert output_array.shape == (32, 10, 64)
|
||||
```
|
||||
|
||||
# Arguments
|
||||
input_dim: int >= 0. Size of the vocabulary, ie.
|
||||
@@ -42,6 +50,16 @@ class Embedding(Layer):
|
||||
This argument is required if you are going to connect
|
||||
`Flatten` then `Dense` layers upstream
|
||||
(without it, the shape of the dense outputs cannot be computed).
|
||||
dropout: float between 0 and 1. Fraction of the embeddings to drop.
|
||||
|
||||
# Input shape
|
||||
2D tensor with shape: `(nb_samples, sequence_length)`.
|
||||
|
||||
# Output shape
|
||||
3D tensor with shape: `(nb_samples, sequence_length, output_dim)`.
|
||||
|
||||
# References
|
||||
- [A Theoretically Grounded Application of Dropout in Recurrent Neural Networks](http://arxiv.org/abs/1512.05287)
|
||||
'''
|
||||
input_ndim = 2
|
||||
|
||||
@@ -50,12 +68,13 @@ class Embedding(Layer):
|
||||
W_regularizer=None, activity_regularizer=None,
|
||||
W_constraint=None,
|
||||
mask_zero=False,
|
||||
weights=None, **kwargs):
|
||||
weights=None, dropout=0., **kwargs):
|
||||
self.input_dim = input_dim
|
||||
self.output_dim = output_dim
|
||||
self.init = initializations.get(init)
|
||||
self.input_length = input_length
|
||||
self.mask_zero = mask_zero
|
||||
self.dropout = dropout
|
||||
|
||||
self.W_constraint = constraints.get(W_constraint)
|
||||
self.constraints = [self.W_constraint]
|
||||
@@ -63,14 +82,16 @@ class Embedding(Layer):
|
||||
self.W_regularizer = regularizers.get(W_regularizer)
|
||||
self.activity_regularizer = regularizers.get(activity_regularizer)
|
||||
|
||||
if 0. < self.dropout < 1.:
|
||||
self.uses_learning_phase = True
|
||||
self.initial_weights = weights
|
||||
kwargs['input_shape'] = (self.input_dim,)
|
||||
kwargs['input_shape'] = (self.input_length,)
|
||||
kwargs['input_dtype'] = 'int32'
|
||||
super(Embedding, self).__init__(**kwargs)
|
||||
|
||||
def build(self):
|
||||
self.input = K.placeholder(shape=(self.input_shape[0], self.input_length),
|
||||
dtype='int32')
|
||||
self.W = self.init((self.input_dim, self.output_dim))
|
||||
def build(self, input_shape):
|
||||
self.W = self.init((self.input_dim, self.output_dim),
|
||||
name='{}_W'.format(self.name))
|
||||
self.trainable_weights = [self.W]
|
||||
self.regularizers = []
|
||||
if self.W_regularizer:
|
||||
@@ -84,31 +105,35 @@ class Embedding(Layer):
|
||||
if self.initial_weights is not None:
|
||||
self.set_weights(self.initial_weights)
|
||||
|
||||
def get_output_mask(self, train=None):
|
||||
X = self.get_input(train)
|
||||
def compute_mask(self, x, mask=None):
|
||||
if not self.mask_zero:
|
||||
return None
|
||||
else:
|
||||
return K.not_equal(X, 0)
|
||||
return K.not_equal(x, 0)
|
||||
|
||||
@property
|
||||
def output_shape(self):
|
||||
return (self.input_shape[0], self.input_length, self.output_dim)
|
||||
def get_output_shape_for(self, input_shape):
|
||||
return (input_shape[0], self.input_length, self.output_dim)
|
||||
|
||||
def get_output(self, train=False):
|
||||
X = self.get_input(train)
|
||||
out = K.gather(self.W, X)
|
||||
def call(self, x, mask=None):
|
||||
if 0. < self.dropout < 1.:
|
||||
retain_p = 1. - self.dropout
|
||||
B = K.random_binomial((self.input_dim,), p=retain_p) * (1. / retain_p)
|
||||
B = K.expand_dims(B)
|
||||
W = K.in_train_phase(self.W * B, self.W)
|
||||
else:
|
||||
W = self.W
|
||||
out = K.gather(W, x)
|
||||
return out
|
||||
|
||||
def get_config(self):
|
||||
config = {"name": self.__class__.__name__,
|
||||
"input_dim": self.input_dim,
|
||||
"output_dim": self.output_dim,
|
||||
"init": self.init.__name__,
|
||||
"input_length": self.input_length,
|
||||
"mask_zero": self.mask_zero,
|
||||
"activity_regularizer": self.activity_regularizer.get_config() if self.activity_regularizer else None,
|
||||
"W_regularizer": self.W_regularizer.get_config() if self.W_regularizer else None,
|
||||
"W_constraint": self.W_constraint.get_config() if self.W_constraint else None}
|
||||
config = {'input_dim': self.input_dim,
|
||||
'output_dim': self.output_dim,
|
||||
'init': self.init.__name__,
|
||||
'input_length': self.input_length,
|
||||
'mask_zero': self.mask_zero,
|
||||
'activity_regularizer': self.activity_regularizer.get_config() if self.activity_regularizer else None,
|
||||
'W_regularizer': self.W_regularizer.get_config() if self.W_regularizer else None,
|
||||
'W_constraint': self.W_constraint.get_config() if self.W_constraint else None,
|
||||
'dropout': self.dropout}
|
||||
base_config = super(Embedding, self).get_config()
|
||||
return dict(list(base_config.items()) + list(config.items()))
|
||||
|
||||
+52
-46
@@ -1,9 +1,9 @@
|
||||
from __future__ import absolute_import
|
||||
from .core import MaskedLayer
|
||||
from ..engine import Layer
|
||||
from .. import backend as K
|
||||
|
||||
|
||||
class GaussianNoise(MaskedLayer):
|
||||
class GaussianNoise(Layer):
|
||||
'''Apply to the input an additive zero-centred gaussian noise with
|
||||
standard deviation `sigma`. This is useful to mitigate overfitting
|
||||
(you could see it as a kind of random data augmentation).
|
||||
@@ -12,6 +12,44 @@ class GaussianNoise(MaskedLayer):
|
||||
|
||||
As it is a regularization layer, it is only active at training time.
|
||||
|
||||
# Arguments
|
||||
sigma: float, standard deviation of the noise distribution.
|
||||
|
||||
# Input shape
|
||||
Arbitrary. Use the keyword argument `input_shape`
|
||||
(tuple of integers, does not include the samples axis)
|
||||
when using this layer as the first layer in a model.
|
||||
|
||||
# Output shape
|
||||
Same shape as input.
|
||||
'''
|
||||
def __init__(self, sigma, **kwargs):
|
||||
self.supports_masking = True
|
||||
self.sigma = sigma
|
||||
self.uses_learning_phase = True
|
||||
super(GaussianNoise, self).__init__(**kwargs)
|
||||
|
||||
def call(self, x, mask=None):
|
||||
noise_x = x + K.random_normal(shape=K.shape(x),
|
||||
mean=0.,
|
||||
std=self.sigma)
|
||||
return K.in_train_phase(noise_x, x)
|
||||
|
||||
def get_config(self):
|
||||
config = {'sigma': self.sigma}
|
||||
base_config = super(GaussianNoise, self).get_config()
|
||||
return dict(list(base_config.items()) + list(config.items()))
|
||||
|
||||
|
||||
class GaussianDropout(Layer):
|
||||
'''Apply to the input an multiplicative one-centred gaussian noise
|
||||
with standard deviation `sqrt(p/(1-p))`.
|
||||
|
||||
As it is a regularization layer, it is only active at training time.
|
||||
|
||||
# Arguments
|
||||
p: float, drop probability (as with `Dropout`).
|
||||
|
||||
# Input shape
|
||||
Arbitrary. Use the keyword argument `input_shape`
|
||||
(tuple of integers, does not include the samples axis)
|
||||
@@ -20,56 +58,24 @@ class GaussianNoise(MaskedLayer):
|
||||
# Output shape
|
||||
Same shape as input.
|
||||
|
||||
# Arguments
|
||||
sigma: float, standard deviation of the noise distribution.
|
||||
'''
|
||||
def __init__(self, sigma, **kwargs):
|
||||
super(GaussianNoise, self).__init__(**kwargs)
|
||||
self.sigma = sigma
|
||||
|
||||
def get_output(self, train=False):
|
||||
X = self.get_input(train)
|
||||
if not train or self.sigma == 0:
|
||||
return X
|
||||
else:
|
||||
return X + K.random_normal(shape=K.shape(X),
|
||||
mean=0.,
|
||||
std=self.sigma)
|
||||
|
||||
def get_config(self):
|
||||
config = {"name": self.__class__.__name__,
|
||||
"sigma": self.sigma}
|
||||
base_config = super(GaussianNoise, self).get_config()
|
||||
return dict(list(base_config.items()) + list(config.items()))
|
||||
|
||||
|
||||
class GaussianDropout(MaskedLayer):
|
||||
'''Apply to the input an multiplicative one-centred gaussian noise
|
||||
with standard deviation `sqrt(p/(1-p))`.
|
||||
|
||||
As it is a regularization layer, it is only active at training time.
|
||||
|
||||
# Arguments
|
||||
p: float, drop probability (as with `Dropout`).
|
||||
|
||||
# References:
|
||||
# References
|
||||
[Dropout: A Simple Way to Prevent Neural Networks from Overfitting Srivastava, Hinton, et al. 2014](http://www.cs.toronto.edu/~rsalakhu/papers/srivastava14a.pdf)
|
||||
'''
|
||||
def __init__(self, p, **kwargs):
|
||||
super(GaussianDropout, self).__init__(**kwargs)
|
||||
self.supports_masking = True
|
||||
self.p = p
|
||||
if 0 < p < 1:
|
||||
self.uses_learning_phase = True
|
||||
super(GaussianDropout, self).__init__(**kwargs)
|
||||
|
||||
def get_output(self, train):
|
||||
X = self.get_input(train)
|
||||
if train:
|
||||
# self.p refers to drop probability rather than
|
||||
# retain probability (as in paper), for consistency
|
||||
X *= K.random_normal(shape=K.shape(X), mean=1.0,
|
||||
std=K.sqrt(self.p / (1.0 - self.p)))
|
||||
return X
|
||||
def call(self, x, mask=None):
|
||||
if 0 < self.p < 1:
|
||||
noise_x = x * K.random_normal(shape=K.shape(x), mean=1.0,
|
||||
std=K.sqrt(self.p / (1.0 - self.p)))
|
||||
return K.in_train_phase(noise_x, x)
|
||||
return x
|
||||
|
||||
def get_config(self):
|
||||
config = {"name": self.__class__.__name__,
|
||||
"p": self.p}
|
||||
config = {'p': self.p}
|
||||
base_config = super(GaussianDropout, self).get_config()
|
||||
return dict(list(base_config.items()) + list(config.items()))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from ..layers.core import Layer
|
||||
from ..engine import Layer, InputSpec
|
||||
from .. import initializations
|
||||
from .. import backend as K
|
||||
|
||||
@@ -8,14 +8,6 @@ class BatchNormalization(Layer):
|
||||
i.e. applies a transformation that maintains the mean activation
|
||||
close to 0 and the activation standard deviation close to 1.
|
||||
|
||||
# Input shape
|
||||
Arbitrary. Use the keyword argument `input_shape`
|
||||
(tuple of integers, does not include the samples axis)
|
||||
when using this layer as the first layer in a model.
|
||||
|
||||
# Output shape
|
||||
Same shape as input.
|
||||
|
||||
# Arguments
|
||||
epsilon: small float > 0. Fuzz parameter.
|
||||
mode: integer, 0 or 1.
|
||||
@@ -37,71 +29,96 @@ class BatchNormalization(Layer):
|
||||
weights: Initialization weights.
|
||||
List of 2 numpy arrays, with shapes:
|
||||
`[(input_shape,), (input_shape,)]`
|
||||
beta_init: name of initialization function for shift parameter
|
||||
(see [initializations](../initializations.md)), or alternatively,
|
||||
Theano/TensorFlow function to use for weights initialization.
|
||||
This parameter is only relevant if you don't pass a `weights` argument.
|
||||
gamma_init: name of initialization function for scale parameter (see
|
||||
[initializations](../initializations.md)), or alternatively,
|
||||
Theano/TensorFlow function to use for weights initialization.
|
||||
This parameter is only relevant if you don't pass a `weights` argument.
|
||||
|
||||
# Input shape
|
||||
Arbitrary. Use the keyword argument `input_shape`
|
||||
(tuple of integers, does not include the samples axis)
|
||||
when using this layer as the first layer in a model.
|
||||
|
||||
# Output shape
|
||||
Same shape as input.
|
||||
|
||||
# References
|
||||
- [Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift](http://arxiv.org/pdf/1502.03167v3.pdf)
|
||||
'''
|
||||
def __init__(self, epsilon=1e-6, mode=0, axis=-1, momentum=0.9,
|
||||
weights=None, **kwargs):
|
||||
self.init = initializations.get("uniform")
|
||||
weights=None, beta_init='zero', gamma_init='one', **kwargs):
|
||||
self.beta_init = initializations.get(beta_init)
|
||||
self.gamma_init = initializations.get(gamma_init)
|
||||
self.epsilon = epsilon
|
||||
self.mode = mode
|
||||
self.axis = axis
|
||||
self.momentum = momentum
|
||||
self.initial_weights = weights
|
||||
self.uses_learning_phase = True
|
||||
super(BatchNormalization, self).__init__(**kwargs)
|
||||
|
||||
def build(self):
|
||||
input_shape = self.input_shape # starts with samples axis
|
||||
def build(self, input_shape):
|
||||
self.input_spec = [InputSpec(shape=input_shape)]
|
||||
shape = (input_shape[self.axis],)
|
||||
|
||||
self.gamma = self.init(shape)
|
||||
self.beta = K.zeros(shape)
|
||||
self.gamma = self.gamma_init(shape, name='{}_gamma'.format(self.name))
|
||||
self.beta = self.beta_init(shape, name='{}_beta'.format(self.name))
|
||||
self.trainable_weights = [self.gamma, self.beta]
|
||||
|
||||
self.running_mean = K.zeros(shape)
|
||||
self.running_std = K.ones(shape)
|
||||
self.running_mean = K.zeros(shape,
|
||||
name='{}_running_mean'.format(self.name))
|
||||
self.running_std = K.ones(shape,
|
||||
name='{}_running_std'.format(self.name))
|
||||
self.non_trainable_weights = [self.running_mean, self.running_std]
|
||||
|
||||
if self.initial_weights is not None:
|
||||
self.set_weights(self.initial_weights)
|
||||
del self.initial_weights
|
||||
|
||||
def get_output(self, train):
|
||||
X = self.get_input(train)
|
||||
def call(self, x, mask=None):
|
||||
if self.mode == 0:
|
||||
input_shape = self.input_shape
|
||||
input_shape = self.input_spec[0].shape
|
||||
|
||||
reduction_axes = list(range(len(input_shape)))
|
||||
del reduction_axes[self.axis]
|
||||
broadcast_shape = [1] * len(input_shape)
|
||||
broadcast_shape[self.axis] = input_shape[self.axis]
|
||||
if train:
|
||||
m = K.mean(X, axis=reduction_axes)
|
||||
brodcast_m = K.reshape(m, broadcast_shape)
|
||||
std = K.mean(K.square(X - brodcast_m) + self.epsilon, axis=reduction_axes)
|
||||
std = K.sqrt(std)
|
||||
brodcast_std = K.reshape(std, broadcast_shape)
|
||||
mean_update = self.momentum * self.running_mean + (1-self.momentum) * m
|
||||
std_update = self.momentum * self.running_std + (1-self.momentum) * std
|
||||
self.updates = [(self.running_mean, mean_update),
|
||||
(self.running_std, std_update)]
|
||||
X_normed = (X - brodcast_m) / (brodcast_std + self.epsilon)
|
||||
else:
|
||||
brodcast_m = K.reshape(self.running_mean, broadcast_shape)
|
||||
brodcast_std = K.reshape(self.running_std, broadcast_shape)
|
||||
X_normed = ((X - brodcast_m) /
|
||||
(brodcast_std + self.epsilon))
|
||||
out = K.reshape(self.gamma, broadcast_shape) * X_normed + K.reshape(self.beta, broadcast_shape)
|
||||
|
||||
# case: train mode (uses stats of the current batch)
|
||||
mean = K.mean(x, axis=reduction_axes)
|
||||
brodcast_mean = K.reshape(mean, broadcast_shape)
|
||||
std = K.mean(K.square(x - brodcast_mean) + self.epsilon, axis=reduction_axes)
|
||||
std = K.sqrt(std)
|
||||
brodcast_std = K.reshape(std, broadcast_shape)
|
||||
mean_update = self.momentum * self.running_mean + (1-self.momentum) * mean
|
||||
std_update = self.momentum * self.running_std + (1-self.momentum) * std
|
||||
self.updates = [(self.running_mean, mean_update),
|
||||
(self.running_std, std_update)]
|
||||
x_normed = (x - brodcast_mean) / (brodcast_std + self.epsilon)
|
||||
|
||||
# case: test mode (uses running averages)
|
||||
brodcast_running_mean = K.reshape(self.running_mean, broadcast_shape)
|
||||
brodcast_running_std = K.reshape(self.running_std, broadcast_shape)
|
||||
x_normed_running = ((x - brodcast_running_mean) / (brodcast_running_std + self.epsilon))
|
||||
|
||||
# pick the normalized form of x corresponding to the training phase
|
||||
x_normed = K.in_train_phase(x_normed, x_normed_running)
|
||||
out = K.reshape(self.gamma, broadcast_shape) * x_normed + K.reshape(self.beta, broadcast_shape)
|
||||
|
||||
elif self.mode == 1:
|
||||
m = K.mean(X, axis=-1, keepdims=True)
|
||||
std = K.std(X, axis=-1, keepdims=True)
|
||||
X_normed = (X - m) / (std + self.epsilon)
|
||||
out = self.gamma * X_normed + self.beta
|
||||
# sample-wise normalization
|
||||
m = K.mean(x, axis=-1, keepdims=True)
|
||||
std = K.std(x, axis=-1, keepdims=True)
|
||||
x_normed = (x - m) / (std + self.epsilon)
|
||||
out = self.gamma * x_normed + self.beta
|
||||
return out
|
||||
|
||||
def get_config(self):
|
||||
config = {"name": self.__class__.__name__,
|
||||
"epsilon": self.epsilon,
|
||||
config = {"epsilon": self.epsilon,
|
||||
"mode": self.mode,
|
||||
"axis": self.axis,
|
||||
"momentum": self.momentum}
|
||||
|
||||
+460
-119
@@ -3,25 +3,67 @@ from __future__ import absolute_import
|
||||
import numpy as np
|
||||
|
||||
from .. import backend as K
|
||||
from .. import activations, initializations
|
||||
from ..layers.core import MaskedLayer
|
||||
from .. import activations, initializations, regularizers
|
||||
from ..engine import Layer, InputSpec
|
||||
|
||||
|
||||
class Recurrent(MaskedLayer):
|
||||
def time_distributed_dense(x, w, b=None, dropout=None,
|
||||
input_dim=None, output_dim=None, timesteps=None):
|
||||
'''Apply y.w + b for every temporal slice y of x.
|
||||
'''
|
||||
if not input_dim:
|
||||
# won't work with TensorFlow
|
||||
input_dim = K.shape(x)[2]
|
||||
if not timesteps:
|
||||
# won't work with TensorFlow
|
||||
timesteps = K.shape(x)[1]
|
||||
if not output_dim:
|
||||
# won't work with TensorFlow
|
||||
output_dim = K.shape(w)[1]
|
||||
|
||||
if dropout is not None and 0. < dropout < 1.:
|
||||
# apply the same dropout pattern at every timestep
|
||||
ones = K.ones_like(K.reshape(x[:, 0, :], (-1, input_dim)))
|
||||
dropout_matrix = K.dropout(ones, dropout)
|
||||
expanded_dropout_matrix = K.repeat(dropout_matrix, timesteps)
|
||||
x = K.in_train_phase(x * expanded_dropout_matrix, x)
|
||||
|
||||
# collapse time dimension and batch dimension together
|
||||
x = K.reshape(x, (-1, input_dim))
|
||||
|
||||
x = K.dot(x, w)
|
||||
if b:
|
||||
x = x + b
|
||||
# reshape to 3D tensor
|
||||
x = K.reshape(x, (-1, timesteps, output_dim))
|
||||
return x
|
||||
|
||||
|
||||
class Recurrent(Layer):
|
||||
'''Abstract base class for recurrent layers.
|
||||
Do not use in a model -- it's not a functional layer!
|
||||
Do not use in a model -- it's not a valid layer!
|
||||
Use its children classes `LSTM`, `GRU` and `SimpleRNN` instead.
|
||||
|
||||
All recurrent layers (GRU, LSTM, SimpleRNN) also
|
||||
All recurrent layers (`LSTM`, `GRU`, `SimpleRNN`) also
|
||||
follow the specifications of this class and accept
|
||||
the keyword arguments listed below.
|
||||
|
||||
# Input shape
|
||||
3D tensor with shape `(nb_samples, timesteps, input_dim)`.
|
||||
# Example
|
||||
|
||||
# Output shape
|
||||
- if `return_sequences`: 3D tensor with shape
|
||||
`(nb_samples, timesteps, output_dim)`.
|
||||
- else, 2D tensor with shape `(nb_samples, output_dim)`.
|
||||
```python
|
||||
# as the first layer in a Sequential model
|
||||
model = Sequential()
|
||||
model.add(LSTM(32, input_shape=(10, 64)))
|
||||
# now model.output_shape == (None, 10, 32)
|
||||
# note: `None` is the batch dimension.
|
||||
|
||||
# the following is identical:
|
||||
model = Sequential()
|
||||
model.add(LSTM(32, input_dim=64, input_length=10))
|
||||
|
||||
# for subsequent layers, not need to specify the input size:
|
||||
model.add(LSTM(16))
|
||||
```
|
||||
|
||||
# Arguments
|
||||
weights: list of numpy arrays to set as initial weights.
|
||||
@@ -34,6 +76,17 @@ class Recurrent(MaskedLayer):
|
||||
stateful: Boolean (default False). If True, the last state
|
||||
for each sample at index i in a batch will be used as initial
|
||||
state for the sample of index i in the following batch.
|
||||
unroll: Boolean (default False). If True, the network will be unrolled,
|
||||
else a symbolic loop will be used. When using TensorFlow, the network
|
||||
is always unrolled, so this argument does not do anything.
|
||||
Unrolling can speed-up a RNN, although it tends to be more memory-intensive.
|
||||
Unrolling is only suitable for short sequences.
|
||||
consume_less: one of "cpu", "mem". If set to "cpu", the RNN will use
|
||||
an implementation that uses fewer, larger matrix products,
|
||||
thus running faster (at least on CPU) but consuming more memory.
|
||||
If set to "mem", the RNN will use more matrix products,
|
||||
but smaller ones, thus running slower (may actually be faster on GPU)
|
||||
while consuming less memory.
|
||||
input_dim: dimensionality of the input (integer).
|
||||
This argument (or alternatively, the keyword argument `input_shape`)
|
||||
is required when using this layer as the first layer in a model.
|
||||
@@ -47,6 +100,14 @@ class Recurrent(MaskedLayer):
|
||||
at the level of the first layer
|
||||
(e.g. via the `input_shape` argument)
|
||||
|
||||
# Input shape
|
||||
3D tensor with shape `(nb_samples, timesteps, input_dim)`.
|
||||
|
||||
# Output shape
|
||||
- if `return_sequences`: 3D tensor with shape
|
||||
`(nb_samples, timesteps, output_dim)`.
|
||||
- else, 2D tensor with shape `(nb_samples, output_dim)`.
|
||||
|
||||
# Masking
|
||||
This layer supports masking for input data with a variable number
|
||||
of timesteps. To introduce masks to your data,
|
||||
@@ -78,75 +139,92 @@ class Recurrent(MaskedLayer):
|
||||
|
||||
To reset the states of your model, call `.reset_states()` on either
|
||||
a specific layer, or on your entire model.
|
||||
'''
|
||||
input_ndim = 3
|
||||
|
||||
# Note on using dropout with TensorFlow
|
||||
When using the TensorFlow backend, specify a fixed batch size for your model
|
||||
following the notes on statefulness RNNs.
|
||||
'''
|
||||
def __init__(self, weights=None,
|
||||
return_sequences=False, go_backwards=False, stateful=False,
|
||||
unroll=False, consume_less='cpu',
|
||||
input_dim=None, input_length=None, **kwargs):
|
||||
self.return_sequences = return_sequences
|
||||
self.initial_weights = weights
|
||||
self.go_backwards = go_backwards
|
||||
self.stateful = stateful
|
||||
self.unroll = unroll
|
||||
self.consume_less = consume_less
|
||||
|
||||
self.supports_masking = True
|
||||
self.input_spec = [InputSpec(ndim=3)]
|
||||
self.input_dim = input_dim
|
||||
self.input_length = input_length
|
||||
if self.input_dim:
|
||||
kwargs['input_shape'] = (self.input_length, self.input_dim)
|
||||
super(Recurrent, self).__init__(**kwargs)
|
||||
|
||||
def get_output_mask(self, train=False):
|
||||
if self.return_sequences:
|
||||
return super(Recurrent, self).get_output_mask(train)
|
||||
else:
|
||||
return None
|
||||
|
||||
@property
|
||||
def output_shape(self):
|
||||
input_shape = self.input_shape
|
||||
def get_output_shape_for(self, input_shape):
|
||||
if self.return_sequences:
|
||||
return (input_shape[0], input_shape[1], self.output_dim)
|
||||
else:
|
||||
return (input_shape[0], self.output_dim)
|
||||
|
||||
def compute_mask(self, input, mask):
|
||||
if self.return_sequences:
|
||||
return mask
|
||||
else:
|
||||
return None
|
||||
|
||||
def step(self, x, states):
|
||||
raise NotImplementedError
|
||||
|
||||
def get_initial_states(self, X):
|
||||
def get_constants(self, x):
|
||||
return []
|
||||
|
||||
def get_initial_states(self, x):
|
||||
# build an all-zero tensor of shape (samples, output_dim)
|
||||
initial_state = K.zeros_like(X) # (samples, timesteps, input_dim)
|
||||
initial_state = K.zeros_like(x) # (samples, timesteps, input_dim)
|
||||
initial_state = K.sum(initial_state, axis=1) # (samples, input_dim)
|
||||
reducer = K.zeros((self.input_dim, self.output_dim))
|
||||
initial_state = K.dot(initial_state, reducer) # (samples, output_dim)
|
||||
initial_states = [initial_state for _ in range(len(self.states))]
|
||||
return initial_states
|
||||
|
||||
def get_output(self, train=False):
|
||||
# input shape: (nb_samples, time (padded with zeros), input_dim)
|
||||
X = self.get_input(train)
|
||||
mask = self.get_input_mask(train)
|
||||
def preprocess_input(self, x):
|
||||
return x
|
||||
|
||||
assert K.ndim(X) == 3
|
||||
def call(self, x, mask=None):
|
||||
# input shape: (nb_samples, time (padded with zeros), input_dim)
|
||||
# note that the .build() method of subclasses MUST define
|
||||
# self.input_sepc with a complete input shape.
|
||||
input_shape = self.input_spec[0].shape
|
||||
if K._BACKEND == 'tensorflow':
|
||||
if not self.input_shape[1]:
|
||||
raise Exception('When using TensorFlow, you should define ' +
|
||||
'explicitly the number of timesteps of ' +
|
||||
'your sequences.\n' +
|
||||
'If your first layer is an Embedding, ' +
|
||||
'make sure to pass it an "input_length" ' +
|
||||
'argument. Otherwise, make sure ' +
|
||||
'the first layer has ' +
|
||||
'an "input_shape" or "batch_input_shape" ' +
|
||||
'argument, including the time axis.')
|
||||
if not input_shape[1]:
|
||||
raise Exception('When using TensorFlow, you should define '
|
||||
'explicitly the number of timesteps of '
|
||||
'your sequences.\n'
|
||||
'If your first layer is an Embedding, '
|
||||
'make sure to pass it an "input_length" '
|
||||
'argument. Otherwise, make sure '
|
||||
'the first layer has '
|
||||
'an "input_shape" or "batch_input_shape" '
|
||||
'argument, including the time axis. '
|
||||
'Found input shape at layer ' + self.name +
|
||||
': ' + str(input_shape))
|
||||
if self.stateful:
|
||||
initial_states = self.states
|
||||
else:
|
||||
initial_states = self.get_initial_states(X)
|
||||
initial_states = self.get_initial_states(x)
|
||||
constants = self.get_constants(x)
|
||||
preprocessed_input = self.preprocess_input(x)
|
||||
|
||||
last_output, outputs, states = K.rnn(self.step, X,
|
||||
last_output, outputs, states = K.rnn(self.step, preprocessed_input,
|
||||
initial_states,
|
||||
go_backwards=self.go_backwards,
|
||||
mask=mask)
|
||||
mask=mask,
|
||||
constants=constants,
|
||||
unroll=self.unroll,
|
||||
input_length=input_shape[1])
|
||||
if self.stateful:
|
||||
self.updates = []
|
||||
for i in range(len(states)):
|
||||
@@ -158,22 +236,23 @@ class Recurrent(MaskedLayer):
|
||||
return last_output
|
||||
|
||||
def get_config(self):
|
||||
config = {"name": self.__class__.__name__,
|
||||
"return_sequences": self.return_sequences,
|
||||
"go_backwards": self.go_backwards,
|
||||
"stateful": self.stateful}
|
||||
config = {'return_sequences': self.return_sequences,
|
||||
'go_backwards': self.go_backwards,
|
||||
'stateful': self.stateful,
|
||||
'unroll': self.unroll,
|
||||
'consume_less': self.consume_less}
|
||||
if self.stateful:
|
||||
config['batch_input_shape'] = self.input_shape
|
||||
config['batch_input_shape'] = self.input_spec[0].shape
|
||||
else:
|
||||
config['input_dim'] = self.input_dim
|
||||
config['input_length'] = self.input_length
|
||||
|
||||
|
||||
base_config = super(Recurrent, self).get_config()
|
||||
return dict(list(base_config.items()) + list(config.items()))
|
||||
|
||||
|
||||
class SimpleRNN(Recurrent):
|
||||
'''Fully-connected RNN where the output is to fed back to input.
|
||||
'''Fully-connected RNN where the output is to be fed back to input.
|
||||
|
||||
# Arguments
|
||||
output_dim: dimension of the internal projections and the final output.
|
||||
@@ -184,18 +263,38 @@ class SimpleRNN(Recurrent):
|
||||
activation: activation function.
|
||||
Can be the name of an existing function (str),
|
||||
or a Theano function (see: [activations](../activations.md)).
|
||||
W_regularizer: instance of [WeightRegularizer](../regularizers.md)
|
||||
(eg. L1 or L2 regularization), applied to the input weights matrices.
|
||||
U_regularizer: instance of [WeightRegularizer](../regularizers.md)
|
||||
(eg. L1 or L2 regularization), applied to the recurrent weights matrices.
|
||||
b_regularizer: instance of [WeightRegularizer](../regularizers.md),
|
||||
applied to the bias.
|
||||
dropout_W: float between 0 and 1. Fraction of the input units to drop for input gates.
|
||||
dropout_U: float between 0 and 1. Fraction of the input units to drop for recurrent connections.
|
||||
|
||||
# References
|
||||
- [A Theoretically Grounded Application of Dropout in Recurrent Neural Networks](http://arxiv.org/abs/1512.05287)
|
||||
'''
|
||||
def __init__(self, output_dim,
|
||||
init='glorot_uniform', inner_init='orthogonal',
|
||||
activation='sigmoid', **kwargs):
|
||||
activation='tanh',
|
||||
W_regularizer=None, U_regularizer=None, b_regularizer=None,
|
||||
dropout_W=0., dropout_U=0., **kwargs):
|
||||
self.output_dim = output_dim
|
||||
self.init = initializations.get(init)
|
||||
self.inner_init = initializations.get(inner_init)
|
||||
self.activation = activations.get(activation)
|
||||
self.W_regularizer = regularizers.get(W_regularizer)
|
||||
self.U_regularizer = regularizers.get(U_regularizer)
|
||||
self.b_regularizer = regularizers.get(b_regularizer)
|
||||
self.dropout_W, self.dropout_U = dropout_W, dropout_U
|
||||
|
||||
if self.dropout_W or self.dropout_U:
|
||||
self.uses_learning_phase = True
|
||||
super(SimpleRNN, self).__init__(**kwargs)
|
||||
|
||||
def build(self):
|
||||
input_shape = self.input_shape
|
||||
def build(self, input_shape):
|
||||
self.input_spec = [InputSpec(shape=input_shape)]
|
||||
if self.stateful:
|
||||
self.reset_states()
|
||||
else:
|
||||
@@ -204,9 +303,23 @@ class SimpleRNN(Recurrent):
|
||||
input_dim = input_shape[2]
|
||||
self.input_dim = input_dim
|
||||
|
||||
self.W = self.init((input_dim, self.output_dim))
|
||||
self.U = self.inner_init((self.output_dim, self.output_dim))
|
||||
self.b = K.zeros((self.output_dim,))
|
||||
self.W = self.init((input_dim, self.output_dim),
|
||||
name='{}_W'.format(self.name))
|
||||
self.U = self.inner_init((self.output_dim, self.output_dim),
|
||||
name='{}_U'.format(self.name))
|
||||
self.b = K.zeros((self.output_dim,), name='{}_b'.format(self.name))
|
||||
|
||||
self.regularizers = []
|
||||
if self.W_regularizer:
|
||||
self.W_regularizer.set_param(self.W)
|
||||
self.regularizers.append(self.W_regularizer)
|
||||
if self.U_regularizer:
|
||||
self.U_regularizer.set_param(self.U)
|
||||
self.regularizers.append(self.U_regularizer)
|
||||
if self.b_regularizer:
|
||||
self.b_regularizer.set_param(self.b)
|
||||
self.regularizers.append(self.b_regularizer)
|
||||
|
||||
self.trainable_weights = [self.W, self.U, self.b]
|
||||
|
||||
if self.initial_weights is not None:
|
||||
@@ -215,30 +328,70 @@ class SimpleRNN(Recurrent):
|
||||
|
||||
def reset_states(self):
|
||||
assert self.stateful, 'Layer must be stateful.'
|
||||
input_shape = self.input_shape
|
||||
input_shape = self.input_spec[0].shape
|
||||
if not input_shape[0]:
|
||||
raise Exception('If a RNN is stateful, a complete ' +
|
||||
'input_shape must be provided ' +
|
||||
'(including batch size).')
|
||||
'input_shape must be provided (including batch size).')
|
||||
if hasattr(self, 'states'):
|
||||
K.set_value(self.states[0],
|
||||
np.zeros((input_shape[0], self.output_dim)))
|
||||
else:
|
||||
self.states = [K.zeros((input_shape[0], self.output_dim))]
|
||||
|
||||
def preprocess_input(self, x):
|
||||
if self.consume_less == 'cpu':
|
||||
input_shape = self.input_spec[0].shape
|
||||
input_dim = input_shape[2]
|
||||
timesteps = input_shape[1]
|
||||
return time_distributed_dense(x, self.W, self.b, self.dropout_W,
|
||||
input_dim, self.output_dim,
|
||||
timesteps)
|
||||
else:
|
||||
return x
|
||||
|
||||
def step(self, x, states):
|
||||
# states only contains the previous output.
|
||||
assert len(states) == 1
|
||||
prev_output = states[0]
|
||||
h = K.dot(x, self.W) + self.b
|
||||
output = self.activation(h + K.dot(prev_output, self.U))
|
||||
B_U = states[1]
|
||||
B_W = states[2]
|
||||
|
||||
if self.consume_less == 'cpu':
|
||||
h = x
|
||||
else:
|
||||
h = K.dot(x * B_W, self.W) + self.b
|
||||
|
||||
output = self.activation(h + K.dot(prev_output * B_U, self.U))
|
||||
return output, [output]
|
||||
|
||||
def get_constants(self, x):
|
||||
constants = []
|
||||
if 0 < self.dropout_U < 1:
|
||||
ones = K.ones_like(K.reshape(x[:, 0, 0], (-1, 1)))
|
||||
ones = K.concatenate([ones] * self.output_dim, 1)
|
||||
B_U = K.in_train_phase(K.dropout(ones, self.dropout_U), ones)
|
||||
constants.append(B_U)
|
||||
else:
|
||||
constants.append(K.cast_to_floatx(1.))
|
||||
if self.consume_less == 'cpu' and 0 < self.dropout_W < 1:
|
||||
input_shape = self.input_spec[0].shape
|
||||
input_dim = input_shape[-1]
|
||||
ones = K.ones_like(K.reshape(x[:, 0, 0], (-1, 1)))
|
||||
ones = K.concatenate([ones] * input_dim, 1)
|
||||
B_W = K.in_train_phase(K.dropout(ones, self.dropout_W), ones)
|
||||
constants.append(B_W)
|
||||
else:
|
||||
constants.append(K.cast_to_floatx(1.))
|
||||
return constants
|
||||
|
||||
def get_config(self):
|
||||
config = {"output_dim": self.output_dim,
|
||||
"init": self.init.__name__,
|
||||
"inner_init": self.inner_init.__name__,
|
||||
"activation": self.activation.__name__}
|
||||
"activation": self.activation.__name__,
|
||||
"W_regularizer": self.W_regularizer.get_config() if self.W_regularizer else None,
|
||||
"U_regularizer": self.U_regularizer.get_config() if self.U_regularizer else None,
|
||||
"b_regularizer": self.b_regularizer.get_config() if self.b_regularizer else None,
|
||||
"dropout_W": self.dropout_W,
|
||||
"dropout_U": self.dropout_U}
|
||||
base_config = super(SimpleRNN, self).get_config()
|
||||
return dict(list(base_config.items()) + list(config.items()))
|
||||
|
||||
@@ -256,39 +409,78 @@ class GRU(Recurrent):
|
||||
Can be the name of an existing function (str),
|
||||
or a Theano function (see: [activations](../activations.md)).
|
||||
inner_activation: activation function for the inner cells.
|
||||
W_regularizer: instance of [WeightRegularizer](../regularizers.md)
|
||||
(eg. L1 or L2 regularization), applied to the input weights matrices.
|
||||
U_regularizer: instance of [WeightRegularizer](../regularizers.md)
|
||||
(eg. L1 or L2 regularization), applied to the recurrent weights matrices.
|
||||
b_regularizer: instance of [WeightRegularizer](../regularizers.md),
|
||||
applied to the bias.
|
||||
dropout_W: float between 0 and 1. Fraction of the input units to drop for input gates.
|
||||
dropout_U: float between 0 and 1. Fraction of the input units to drop for recurrent connections.
|
||||
|
||||
# References
|
||||
- [On the Properties of Neural Machine Translation: Encoder–Decoder Approaches](http://www.aclweb.org/anthology/W14-4012)
|
||||
- [Empirical Evaluation of Gated Recurrent Neural Networks on Sequence Modeling](http://arxiv.org/pdf/1412.3555v1.pdf)
|
||||
- [A Theoretically Grounded Application of Dropout in Recurrent Neural Networks](http://arxiv.org/abs/1512.05287)
|
||||
'''
|
||||
def __init__(self, output_dim,
|
||||
init='glorot_uniform', inner_init='orthogonal',
|
||||
activation='sigmoid', inner_activation='hard_sigmoid',
|
||||
**kwargs):
|
||||
activation='tanh', inner_activation='hard_sigmoid',
|
||||
W_regularizer=None, U_regularizer=None, b_regularizer=None,
|
||||
dropout_W=0., dropout_U=0., **kwargs):
|
||||
self.output_dim = output_dim
|
||||
self.init = initializations.get(init)
|
||||
self.inner_init = initializations.get(inner_init)
|
||||
self.activation = activations.get(activation)
|
||||
self.inner_activation = activations.get(inner_activation)
|
||||
self.W_regularizer = regularizers.get(W_regularizer)
|
||||
self.U_regularizer = regularizers.get(U_regularizer)
|
||||
self.b_regularizer = regularizers.get(b_regularizer)
|
||||
self.dropout_W, self.dropout_U = dropout_W, dropout_U
|
||||
|
||||
if self.dropout_W or self.dropout_U:
|
||||
self.uses_learning_phase = True
|
||||
super(GRU, self).__init__(**kwargs)
|
||||
|
||||
def build(self):
|
||||
input_shape = self.input_shape
|
||||
def build(self, input_shape):
|
||||
self.input_spec = [InputSpec(shape=input_shape)]
|
||||
input_dim = input_shape[2]
|
||||
self.input_dim = input_dim
|
||||
self.input = K.placeholder(input_shape)
|
||||
|
||||
self.W_z = self.init((input_dim, self.output_dim))
|
||||
self.U_z = self.inner_init((self.output_dim, self.output_dim))
|
||||
self.b_z = K.zeros((self.output_dim,))
|
||||
self.W_z = self.init((input_dim, self.output_dim),
|
||||
name='{}_W_z'.format(self.name))
|
||||
self.U_z = self.inner_init((self.output_dim, self.output_dim),
|
||||
name='{}_U_z'.format(self.name))
|
||||
self.b_z = K.zeros((self.output_dim,), name='{}_b_z'.format(self.name))
|
||||
|
||||
self.W_r = self.init((input_dim, self.output_dim))
|
||||
self.U_r = self.inner_init((self.output_dim, self.output_dim))
|
||||
self.b_r = K.zeros((self.output_dim,))
|
||||
self.W_r = self.init((input_dim, self.output_dim),
|
||||
name='{}_W_r'.format(self.name))
|
||||
self.U_r = self.inner_init((self.output_dim, self.output_dim),
|
||||
name='{}_U_r'.format(self.name))
|
||||
self.b_r = K.zeros((self.output_dim,), name='{}_b_r'.format(self.name))
|
||||
|
||||
self.W_h = self.init((input_dim, self.output_dim))
|
||||
self.U_h = self.inner_init((self.output_dim, self.output_dim))
|
||||
self.b_h = K.zeros((self.output_dim,))
|
||||
self.W_h = self.init((input_dim, self.output_dim),
|
||||
name='{}_W_h'.format(self.name))
|
||||
self.U_h = self.inner_init((self.output_dim, self.output_dim),
|
||||
name='{}_U_h'.format(self.name))
|
||||
self.b_h = K.zeros((self.output_dim,), name='{}_b_h'.format(self.name))
|
||||
|
||||
self.regularizers = []
|
||||
if self.W_regularizer:
|
||||
self.W_regularizer.set_param(K.concatenate([self.W_z,
|
||||
self.W_r,
|
||||
self.W_h]))
|
||||
self.regularizers.append(self.W_regularizer)
|
||||
if self.U_regularizer:
|
||||
self.U_regularizer.set_param(K.concatenate([self.U_z,
|
||||
self.U_r,
|
||||
self.U_h]))
|
||||
self.regularizers.append(self.U_regularizer)
|
||||
if self.b_regularizer:
|
||||
self.b_regularizer.set_param(K.concatenate([self.b_z,
|
||||
self.b_r,
|
||||
self.b_h]))
|
||||
self.regularizers.append(self.b_regularizer)
|
||||
|
||||
self.trainable_weights = [self.W_z, self.U_z, self.b_z,
|
||||
self.W_r, self.U_r, self.b_r,
|
||||
@@ -305,37 +497,85 @@ class GRU(Recurrent):
|
||||
|
||||
def reset_states(self):
|
||||
assert self.stateful, 'Layer must be stateful.'
|
||||
input_shape = self.input_shape
|
||||
input_shape = self.input_spec[0].shape
|
||||
if not input_shape[0]:
|
||||
raise Exception('If a RNN is stateful, a complete ' +
|
||||
'input_shape must be provided ' +
|
||||
'(including batch size).')
|
||||
'input_shape must be provided (including batch size).')
|
||||
if hasattr(self, 'states'):
|
||||
K.set_value(self.states[0],
|
||||
np.zeros((input_shape[0], self.output_dim)))
|
||||
else:
|
||||
self.states = [K.zeros((input_shape[0], self.output_dim))]
|
||||
|
||||
def preprocess_input(self, x):
|
||||
if self.consume_less == 'cpu':
|
||||
input_shape = self.input_spec[0].shape
|
||||
input_dim = input_shape[2]
|
||||
timesteps = input_shape[1]
|
||||
|
||||
x_z = time_distributed_dense(x, self.W_z, self.b_z, self.dropout_W,
|
||||
input_dim, self.output_dim, timesteps)
|
||||
x_r = time_distributed_dense(x, self.W_r, self.b_r, self.dropout_W,
|
||||
input_dim, self.output_dim, timesteps)
|
||||
x_h = time_distributed_dense(x, self.W_h, self.b_h, self.dropout_W,
|
||||
input_dim, self.output_dim, timesteps)
|
||||
return K.concatenate([x_z, x_r, x_h], axis=2)
|
||||
else:
|
||||
return x
|
||||
|
||||
def step(self, x, states):
|
||||
assert len(states) == 1
|
||||
x_z = K.dot(x, self.W_z) + self.b_z
|
||||
x_r = K.dot(x, self.W_r) + self.b_r
|
||||
x_h = K.dot(x, self.W_h) + self.b_h
|
||||
h_tm1 = states[0] # previous memory
|
||||
B_U = states[1] # dropout matrices for recurrent units
|
||||
B_W = states[2]
|
||||
|
||||
h_tm1 = states[0]
|
||||
z = self.inner_activation(x_z + K.dot(h_tm1, self.U_z))
|
||||
r = self.inner_activation(x_r + K.dot(h_tm1, self.U_r))
|
||||
if self.consume_less == 'cpu':
|
||||
x_z = x[:, :self.output_dim]
|
||||
x_r = x[:, self.output_dim: 2 * self.output_dim]
|
||||
x_h = x[:, 2 * self.output_dim:]
|
||||
else:
|
||||
x_z = K.dot(x * B_W[0], self.W_z) + self.b_z
|
||||
x_r = K.dot(x * B_W[1], self.W_r) + self.b_r
|
||||
x_h = K.dot(x * B_W[2], self.W_h) + self.b_h
|
||||
|
||||
hh = self.activation(x_h + K.dot(r * h_tm1, self.U_h))
|
||||
z = self.inner_activation(x_z + K.dot(h_tm1 * B_U[0], self.U_z))
|
||||
r = self.inner_activation(x_r + K.dot(h_tm1 * B_U[1], self.U_r))
|
||||
|
||||
hh = self.activation(x_h + K.dot(r * h_tm1 * B_U[2], self.U_h))
|
||||
h = z * h_tm1 + (1 - z) * hh
|
||||
return h, [h]
|
||||
|
||||
def get_constants(self, x):
|
||||
constants = []
|
||||
if 0 < self.dropout_U < 1:
|
||||
ones = K.ones_like(K.reshape(x[:, 0, 0], (-1, 1)))
|
||||
ones = K.concatenate([ones] * self.output_dim, 1)
|
||||
B_U = [K.dropout(ones, self.dropout_U) for _ in range(3)]
|
||||
constants.append(B_U)
|
||||
else:
|
||||
constants.append([K.cast_to_floatx(1.) for _ in range(4)])
|
||||
|
||||
if self.consume_less == 'cpu' and 0 < self.dropout_W < 1:
|
||||
input_shape = self.input_spec[0].shape
|
||||
input_dim = input_shape[-1]
|
||||
ones = K.ones_like(K.reshape(x[:, 0, 0], (-1, 1)))
|
||||
ones = K.concatenate([ones] * input_dim, 1)
|
||||
B_W = [K.dropout(ones, self.dropout_W) for _ in range(3)]
|
||||
constants.append(B_W)
|
||||
else:
|
||||
constants.append([K.cast_to_floatx(1.) for _ in range(4)])
|
||||
return constants
|
||||
|
||||
def get_config(self):
|
||||
config = {"output_dim": self.output_dim,
|
||||
"init": self.init.__name__,
|
||||
"inner_init": self.inner_init.__name__,
|
||||
"activation": self.activation.__name__,
|
||||
"inner_activation": self.inner_activation.__name__}
|
||||
"inner_activation": self.inner_activation.__name__,
|
||||
"W_regularizer": self.W_regularizer.get_config() if self.W_regularizer else None,
|
||||
"U_regularizer": self.U_regularizer.get_config() if self.U_regularizer else None,
|
||||
"b_regularizer": self.b_regularizer.get_config() if self.b_regularizer else None,
|
||||
"dropout_W": self.dropout_W,
|
||||
"dropout_U": self.dropout_U}
|
||||
base_config = super(GRU, self).get_config()
|
||||
return dict(list(base_config.items()) + list(config.items()))
|
||||
|
||||
@@ -359,51 +599,97 @@ class LSTM(Recurrent):
|
||||
Can be the name of an existing function (str),
|
||||
or a Theano function (see: [activations](../activations.md)).
|
||||
inner_activation: activation function for the inner cells.
|
||||
W_regularizer: instance of [WeightRegularizer](../regularizers.md)
|
||||
(eg. L1 or L2 regularization), applied to the input weights matrices.
|
||||
U_regularizer: instance of [WeightRegularizer](../regularizers.md)
|
||||
(eg. L1 or L2 regularization), applied to the recurrent weights matrices.
|
||||
b_regularizer: instance of [WeightRegularizer](../regularizers.md),
|
||||
applied to the bias.
|
||||
dropout_W: float between 0 and 1. Fraction of the input units to drop for input gates.
|
||||
dropout_U: float between 0 and 1. Fraction of the input units to drop for recurrent connections.
|
||||
|
||||
# References
|
||||
- [Long short-term memory](http://deeplearning.cs.cmu.edu/pdfs/Hochreiter97_lstm.pdf) (original 1997 paper)
|
||||
- [Learning to forget: Continual prediction with LSTM](http://www.mitpressjournals.org/doi/pdf/10.1162/089976600300015015)
|
||||
- [Supervised sequence labelling 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)
|
||||
'''
|
||||
def __init__(self, output_dim,
|
||||
init='glorot_uniform', inner_init='orthogonal',
|
||||
forget_bias_init='one', activation='tanh',
|
||||
inner_activation='hard_sigmoid', **kwargs):
|
||||
inner_activation='hard_sigmoid',
|
||||
W_regularizer=None, U_regularizer=None, b_regularizer=None,
|
||||
dropout_W=0., dropout_U=0., **kwargs):
|
||||
self.output_dim = output_dim
|
||||
self.init = initializations.get(init)
|
||||
self.inner_init = initializations.get(inner_init)
|
||||
self.forget_bias_init = initializations.get(forget_bias_init)
|
||||
self.activation = activations.get(activation)
|
||||
self.inner_activation = activations.get(inner_activation)
|
||||
self.W_regularizer = regularizers.get(W_regularizer)
|
||||
self.U_regularizer = regularizers.get(U_regularizer)
|
||||
self.b_regularizer = regularizers.get(b_regularizer)
|
||||
self.dropout_W, self.dropout_U = dropout_W, dropout_U
|
||||
|
||||
if self.dropout_W or self.dropout_U:
|
||||
self.uses_learning_phase = True
|
||||
super(LSTM, self).__init__(**kwargs)
|
||||
|
||||
def build(self):
|
||||
input_shape = self.input_shape
|
||||
def build(self, input_shape):
|
||||
self.input_spec = [InputSpec(shape=input_shape)]
|
||||
input_dim = input_shape[2]
|
||||
self.input_dim = input_dim
|
||||
self.input = K.placeholder(input_shape)
|
||||
|
||||
if self.stateful:
|
||||
self.reset_states()
|
||||
else:
|
||||
# initial states: 2 all-zero tensor of shape (output_dim)
|
||||
# initial states: 2 all-zero tensors of shape (output_dim)
|
||||
self.states = [None, None]
|
||||
|
||||
self.W_i = self.init((input_dim, self.output_dim))
|
||||
self.U_i = self.inner_init((self.output_dim, self.output_dim))
|
||||
self.b_i = K.zeros((self.output_dim,))
|
||||
self.W_i = self.init((input_dim, self.output_dim),
|
||||
name='{}_W_i'.format(self.name))
|
||||
self.U_i = self.inner_init((self.output_dim, self.output_dim),
|
||||
name='{}_U_i'.format(self.name))
|
||||
self.b_i = K.zeros((self.output_dim,), name='{}_b_i'.format(self.name))
|
||||
|
||||
self.W_f = self.init((input_dim, self.output_dim))
|
||||
self.U_f = self.inner_init((self.output_dim, self.output_dim))
|
||||
self.b_f = self.forget_bias_init((self.output_dim,))
|
||||
self.W_f = self.init((input_dim, self.output_dim),
|
||||
name='{}_W_f'.format(self.name))
|
||||
self.U_f = self.inner_init((self.output_dim, self.output_dim),
|
||||
name='{}_U_f'.format(self.name))
|
||||
self.b_f = self.forget_bias_init((self.output_dim,),
|
||||
name='{}_b_f'.format(self.name))
|
||||
|
||||
self.W_c = self.init((input_dim, self.output_dim))
|
||||
self.U_c = self.inner_init((self.output_dim, self.output_dim))
|
||||
self.b_c = K.zeros((self.output_dim,))
|
||||
self.W_c = self.init((input_dim, self.output_dim),
|
||||
name='{}_W_c'.format(self.name))
|
||||
self.U_c = self.inner_init((self.output_dim, self.output_dim),
|
||||
name='{}_U_c'.format(self.name))
|
||||
self.b_c = K.zeros((self.output_dim,), name='{}_b_c'.format(self.name))
|
||||
|
||||
self.W_o = self.init((input_dim, self.output_dim))
|
||||
self.U_o = self.inner_init((self.output_dim, self.output_dim))
|
||||
self.b_o = K.zeros((self.output_dim,))
|
||||
self.W_o = self.init((input_dim, self.output_dim),
|
||||
name='{}_W_o'.format(self.name))
|
||||
self.U_o = self.inner_init((self.output_dim, self.output_dim),
|
||||
name='{}_U_o'.format(self.name))
|
||||
self.b_o = K.zeros((self.output_dim,), name='{}_b_o'.format(self.name))
|
||||
|
||||
self.regularizers = []
|
||||
if self.W_regularizer:
|
||||
self.W_regularizer.set_param(K.concatenate([self.W_i,
|
||||
self.W_f,
|
||||
self.W_c,
|
||||
self.W_o]))
|
||||
self.regularizers.append(self.W_regularizer)
|
||||
if self.U_regularizer:
|
||||
self.U_regularizer.set_param(K.concatenate([self.U_i,
|
||||
self.U_f,
|
||||
self.U_c,
|
||||
self.U_o]))
|
||||
self.regularizers.append(self.U_regularizer)
|
||||
if self.b_regularizer:
|
||||
self.b_regularizer.set_param(K.concatenate([self.b_i,
|
||||
self.b_f,
|
||||
self.b_c,
|
||||
self.b_o]))
|
||||
self.regularizers.append(self.b_regularizer)
|
||||
|
||||
self.trainable_weights = [self.W_i, self.U_i, self.b_i,
|
||||
self.W_c, self.U_c, self.b_c,
|
||||
@@ -416,11 +702,10 @@ class LSTM(Recurrent):
|
||||
|
||||
def reset_states(self):
|
||||
assert self.stateful, 'Layer must be stateful.'
|
||||
input_shape = self.input_shape
|
||||
input_shape = self.input_spec[0].shape
|
||||
if not input_shape[0]:
|
||||
raise Exception('If a RNN is stateful, a complete ' +
|
||||
'input_shape must be provided ' +
|
||||
'(including batch size).')
|
||||
'input_shape must be provided (including batch size).')
|
||||
if hasattr(self, 'states'):
|
||||
K.set_value(self.states[0],
|
||||
np.zeros((input_shape[0], self.output_dim)))
|
||||
@@ -430,29 +715,85 @@ class LSTM(Recurrent):
|
||||
self.states = [K.zeros((input_shape[0], self.output_dim)),
|
||||
K.zeros((input_shape[0], self.output_dim))]
|
||||
|
||||
def preprocess_input(self, x, train=False):
|
||||
if self.consume_less == 'cpu':
|
||||
if train and (0 < self.dropout_W < 1):
|
||||
dropout = self.dropout_W
|
||||
else:
|
||||
dropout = 0
|
||||
input_shape = self.input_spec[0].shape
|
||||
input_dim = input_shape[2]
|
||||
timesteps = input_shape[1]
|
||||
|
||||
x_i = time_distributed_dense(x, self.W_i, self.b_i, dropout,
|
||||
input_dim, self.output_dim, timesteps)
|
||||
x_f = time_distributed_dense(x, self.W_f, self.b_f, dropout,
|
||||
input_dim, self.output_dim, timesteps)
|
||||
x_c = time_distributed_dense(x, self.W_c, self.b_c, dropout,
|
||||
input_dim, self.output_dim, timesteps)
|
||||
x_o = time_distributed_dense(x, self.W_o, self.b_o, dropout,
|
||||
input_dim, self.output_dim, timesteps)
|
||||
return K.concatenate([x_i, x_f, x_c, x_o], axis=2)
|
||||
else:
|
||||
return x
|
||||
|
||||
def step(self, x, states):
|
||||
assert len(states) == 2
|
||||
h_tm1 = states[0]
|
||||
c_tm1 = states[1]
|
||||
B_U = states[2]
|
||||
B_W = states[3]
|
||||
|
||||
x_i = K.dot(x, self.W_i) + self.b_i
|
||||
x_f = K.dot(x, self.W_f) + self.b_f
|
||||
x_c = K.dot(x, self.W_c) + self.b_c
|
||||
x_o = K.dot(x, self.W_o) + self.b_o
|
||||
if self.consume_less == 'cpu':
|
||||
x_i = x[:, :self.output_dim]
|
||||
x_f = x[:, self.output_dim: 2 * self.output_dim]
|
||||
x_c = x[:, 2 * self.output_dim: 3 * self.output_dim]
|
||||
x_o = x[:, 3 * self.output_dim:]
|
||||
else:
|
||||
x_i = K.dot(x * B_W[0], self.W_i) + self.b_i
|
||||
x_f = K.dot(x * B_W[1], self.W_f) + self.b_f
|
||||
x_c = K.dot(x * B_W[2], self.W_c) + self.b_c
|
||||
x_o = K.dot(x * B_W[3], self.W_o) + self.b_o
|
||||
|
||||
i = self.inner_activation(x_i + K.dot(h_tm1 * B_U[0], self.U_i))
|
||||
f = self.inner_activation(x_f + K.dot(h_tm1 * B_U[1], self.U_f))
|
||||
c = f * c_tm1 + i * self.activation(x_c + K.dot(h_tm1 * B_U[2], self.U_c))
|
||||
o = self.inner_activation(x_o + K.dot(h_tm1 * B_U[3], self.U_o))
|
||||
|
||||
i = self.inner_activation(x_i + K.dot(h_tm1, self.U_i))
|
||||
f = self.inner_activation(x_f + K.dot(h_tm1, self.U_f))
|
||||
c = f * c_tm1 + i * self.activation(x_c + K.dot(h_tm1, self.U_c))
|
||||
o = self.inner_activation(x_o + K.dot(h_tm1, self.U_o))
|
||||
h = o * self.activation(c)
|
||||
return h, [h, c]
|
||||
|
||||
def get_constants(self, x):
|
||||
constants = []
|
||||
if 0 < self.dropout_U < 1:
|
||||
ones = K.ones_like(K.reshape(x[:, 0, 0], (-1, 1)))
|
||||
ones = K.concatenate([ones] * self.output_dim, 1)
|
||||
B_U = [K.dropout(ones, self.dropout_U) for _ in range(4)]
|
||||
constants.append(B_U)
|
||||
else:
|
||||
constants.append([K.cast_to_floatx(1.) for _ in range(4)])
|
||||
|
||||
if self.consume_less == 'cpu' and 0 < self.dropout_W < 1:
|
||||
input_shape = self.input_spec[0].shape
|
||||
input_dim = input_shape[-1]
|
||||
ones = K.ones_like(K.reshape(x[:, 0, 0], (-1, 1)))
|
||||
ones = K.concatenate([ones] * input_dim, 1)
|
||||
B_W = [K.dropout(ones, self.dropout_W) for _ in range(4)]
|
||||
constants.append(B_W)
|
||||
else:
|
||||
constants.append([K.cast_to_floatx(1.) for _ in range(4)])
|
||||
return constants
|
||||
|
||||
def get_config(self):
|
||||
config = {"output_dim": self.output_dim,
|
||||
"init": self.init.__name__,
|
||||
"inner_init": self.inner_init.__name__,
|
||||
"forget_bias_init": self.forget_bias_init.__name__,
|
||||
"activation": self.activation.__name__,
|
||||
"inner_activation": self.inner_activation.__name__}
|
||||
"inner_activation": self.inner_activation.__name__,
|
||||
"W_regularizer": self.W_regularizer.get_config() if self.W_regularizer else None,
|
||||
"U_regularizer": self.U_regularizer.get_config() if self.U_regularizer else None,
|
||||
"b_regularizer": self.b_regularizer.get_config() if self.b_regularizer else None,
|
||||
"dropout_W": self.dropout_W,
|
||||
"dropout_U": self.dropout_U}
|
||||
base_config = super(LSTM, self).get_config()
|
||||
return dict(list(base_config.items()) + list(config.items()))
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
from ..engine import Layer, InputSpec
|
||||
from .. import backend as K
|
||||
|
||||
|
||||
class Wrapper(Layer):
|
||||
|
||||
def __init__(self, layer, **kwargs):
|
||||
self.layer = layer
|
||||
super(Wrapper, self).__init__(**kwargs)
|
||||
|
||||
def build(self, input_shape=None):
|
||||
'''Assumes that self.layer is already set.
|
||||
Should be called at the end of .build() in the
|
||||
children classes.
|
||||
'''
|
||||
self.trainable_weights = getattr(self.layer, 'trainable_weights', [])
|
||||
self.non_trainable_weights = getattr(self.layer, 'non_trainable_weights', [])
|
||||
self.updates = getattr(self.layer, 'updates', [])
|
||||
self.regularizers = getattr(self.layer, 'regularizers', [])
|
||||
self.constraints = getattr(self.layer, 'constraints', {})
|
||||
|
||||
def get_weights(self):
|
||||
weights = self.layer.get_weights()
|
||||
return weights
|
||||
|
||||
def set_weights(self, weights):
|
||||
self.layer.set_weights(weights)
|
||||
|
||||
def get_config(self):
|
||||
config = {'layer': {'class_name': self.layer.__class__.__name__,
|
||||
'config': self.layer.get_config()}}
|
||||
base_config = super(Wrapper, self).get_config()
|
||||
return dict(list(base_config.items()) + list(config.items()))
|
||||
|
||||
@classmethod
|
||||
def from_config(cls, config):
|
||||
from keras.utils.layer_utils import layer_from_config
|
||||
layer = layer_from_config(config.pop('layer'))
|
||||
return cls(layer, **config)
|
||||
|
||||
|
||||
class TimeDistributed(Wrapper):
|
||||
"""This wrapper allows to apply a layer to every
|
||||
temporal slice of an input.
|
||||
|
||||
The input should be at least 3D,
|
||||
and the dimension of index one will be considered to be
|
||||
the temporal dimension.
|
||||
|
||||
Consider a batch of 32 samples, where each sample is a sequence of 10
|
||||
vectors of 16 dimensions. The batch input shape of the layer is then `(32, 10, 16)`
|
||||
(and the `input_shape`, not including the samples dimension, is `(10, 16)`).
|
||||
|
||||
You can then use `TimeDistributed` to apply a `Dense` layer to each of the 10 timesteps, independently:
|
||||
```python
|
||||
# as the first layer in a model
|
||||
model = Sequential()
|
||||
model.add(TimeDistributed(Dense(8), input_shape=(10, 16)))
|
||||
# now model.output_shape == (None, 10, 8)
|
||||
|
||||
# subsequent layers: no need for input_shape
|
||||
model.add(TimeDistributed(Dense(32)))
|
||||
# now model.output_shape == (None, 10, 32)
|
||||
```
|
||||
|
||||
The output will then have shape `(32, 10, 8)`.
|
||||
|
||||
Note this is strictly equivalent to using `layers.core.TimeDistributedDense`.
|
||||
However what is different about `TimeDistributed`
|
||||
is that it can be used with arbitrary layers, not just `Dense`,
|
||||
for instance with a `Convolution2D` layer:
|
||||
|
||||
```python
|
||||
model = Sequential()
|
||||
model.add(TimeDistributed(Convolution2D(64, 3, 3), input_shape=(10, 3, 299, 299)))
|
||||
```
|
||||
|
||||
# Arguments
|
||||
layer: a layer instance.
|
||||
"""
|
||||
def __init__(self, layer, **kwargs):
|
||||
self.supports_masking = True
|
||||
super(TimeDistributed, self).__init__(layer, **kwargs)
|
||||
|
||||
def build(self, input_shape):
|
||||
assert len(input_shape) >= 3
|
||||
self.input_spec = [InputSpec(shape=input_shape)]
|
||||
if K._BACKEND == 'tensorflow':
|
||||
if not input_shape[1]:
|
||||
raise Exception('When using TensorFlow, you should define '
|
||||
'explicitly the number of timesteps of '
|
||||
'your sequences.\n'
|
||||
'If your first layer is an Embedding, '
|
||||
'make sure to pass it an "input_length" '
|
||||
'argument. Otherwise, make sure '
|
||||
'the first layer has '
|
||||
'an "input_shape" or "batch_input_shape" '
|
||||
'argument, including the time axis.')
|
||||
child_input_shape = (input_shape[0],) + input_shape[2:]
|
||||
self.layer.build(child_input_shape)
|
||||
super(TimeDistributed, self).build()
|
||||
|
||||
def get_output_shape_for(self, input_shape):
|
||||
child_input_shape = (input_shape[0],) + input_shape[2:]
|
||||
child_output_shape = self.layer.get_output_shape_for(child_input_shape)
|
||||
timesteps = input_shape[1]
|
||||
return (child_output_shape[0], timesteps) + child_output_shape[1:]
|
||||
|
||||
def call(self, X, mask=None):
|
||||
input_shape = self.input_spec[0].shape
|
||||
if input_shape[0]:
|
||||
# batch size matters, use rnn-based implementation
|
||||
def step(x, states):
|
||||
output = self.layer.call(x)
|
||||
return output, []
|
||||
|
||||
last_output, outputs, states = K.rnn(step, X,
|
||||
initial_states=[])
|
||||
y = outputs
|
||||
else:
|
||||
# no batch size specified, therefore the layer will be able
|
||||
# to process batches of any size
|
||||
# we can go with reshape-based implementation for performance
|
||||
X = K.reshape(X, (-1, ) + input_shape[2:]) # (nb_samples * timesteps, ...)
|
||||
y = self.layer.call(X) # (nb_samples * timesteps, ...)
|
||||
input_length = input_shape[1]
|
||||
if not input_length:
|
||||
input_length = K.shape(X)[1]
|
||||
# (nb_samples, timesteps, ...)
|
||||
output_shape = self.get_output_shape_for(input_shape)
|
||||
y = K.reshape(y, (-1, input_length) + output_shape[2:])
|
||||
return y
|
||||
@@ -0,0 +1,768 @@
|
||||
from collections import OrderedDict
|
||||
import warnings
|
||||
import copy
|
||||
|
||||
from .. import backend as K
|
||||
from ..layers import InputLayer, Layer, Merge
|
||||
from ..engine.training import Model
|
||||
|
||||
|
||||
class Graph(Model):
|
||||
'''Arbitrary connection graph.
|
||||
|
||||
THIS IS A LEGACY MODEL AND SHOULD NOT BE USED
|
||||
except for backwards compatibility support.
|
||||
|
||||
For multi-inputs/multi-outputs models, or
|
||||
models using shared layers, use the functional API instead.
|
||||
'''
|
||||
|
||||
def __init__(self, name=None):
|
||||
# model attributes
|
||||
self.inbound_nodes = []
|
||||
self.outbound_nodes = []
|
||||
self.built = False
|
||||
self.supports_masking = False
|
||||
|
||||
# legacy attributes (we prefix them with _graph_)
|
||||
self._graph_namespace = set() # strings
|
||||
self._graph_nodes = OrderedDict() # layer-like
|
||||
self._graph_inputs = OrderedDict() # layer-like
|
||||
self._graph_outputs = OrderedDict() # layer-like
|
||||
self._graph_input_config = [] # dicts
|
||||
self._graph_output_config = [] # dicts
|
||||
self._graph_node_config = [] # dicts
|
||||
self._graph_shared_nodes_names = []
|
||||
|
||||
if not name:
|
||||
prefix = 'graph_'
|
||||
name = prefix + str(K.get_uid(prefix))
|
||||
self.name = name
|
||||
|
||||
def __call__(self, x, mask=None):
|
||||
self.build()
|
||||
return super(Graph, self).__call__(x, mask)
|
||||
|
||||
def build(self, input_shape=None):
|
||||
# this will crash if the input/output layers have multiple nodes
|
||||
# no plans to support that case since Graph is deprecated
|
||||
input_tensors = [layer.output for layer in self._graph_inputs.values()]
|
||||
output_tensors = [layer.output for layer in self._graph_outputs.values()]
|
||||
# actually create the model
|
||||
super(Graph, self).__init__(input_tensors,
|
||||
output_tensors,
|
||||
name=self.name)
|
||||
self.built = True
|
||||
|
||||
def compile(self, optimizer, loss,
|
||||
metrics=[],
|
||||
sample_weight_modes=None,
|
||||
loss_weights=None,
|
||||
**kwargs):
|
||||
'''Configures the learning process.
|
||||
|
||||
# Arguments
|
||||
optimizer: str (name of optimizer) or optimizer object.
|
||||
See [optimizers](optimizers.md).
|
||||
loss: dictionary mapping the name(s) of the output(s) to
|
||||
a loss function (string name of objective function or
|
||||
objective function. See [objectives](objectives.md)).
|
||||
metrics: list of str (name of metrics) or
|
||||
list of metrics functions. See [metrics](metrics.md).
|
||||
sample_weight_modes: optional dictionary mapping certain
|
||||
output names to a sample weight mode ("temporal" and None
|
||||
are the only supported modes). If you need to do
|
||||
timestep-wise loss weighting on one of your graph outputs,
|
||||
you will need to set the sample weight mode for this output
|
||||
to "temporal".
|
||||
loss_weights: dictionary you can pass to specify a weight
|
||||
coefficient for each loss function (in a multi-output model).
|
||||
If no loss weight is specified for an output,
|
||||
the weight for this output's loss will be considered to be 1.
|
||||
kwargs: for Theano backend, these are passed into K.function.
|
||||
Ignored for Tensorflow backend.
|
||||
'''
|
||||
# create the underlying Model
|
||||
if not self.built:
|
||||
self.build()
|
||||
super(Graph, self).compile(optimizer, loss,
|
||||
metrics=metrics,
|
||||
sample_weight_mode=sample_weight_modes,
|
||||
loss_weights=loss_weights,
|
||||
**kwargs)
|
||||
|
||||
def add_input(self, name, input_shape=None,
|
||||
batch_input_shape=None, dtype='float'):
|
||||
'''Adds an input to the graph.
|
||||
|
||||
# Arguments:
|
||||
name: string. The name of the new input.
|
||||
Must be unique in the graph.
|
||||
input_shape: a tuple of integers,
|
||||
the expected shape of the input samples.
|
||||
Does not include the batch size.
|
||||
batch_input_shape: a tuple of integers,
|
||||
the expected shape of the whole input batch,
|
||||
including the batch size.
|
||||
dtype: 'float', or 'int'.
|
||||
'''
|
||||
if name in self._graph_namespace:
|
||||
raise Exception('Duplicate node identifier: ' + name)
|
||||
self._graph_namespace.add(name)
|
||||
self.built = False
|
||||
|
||||
if dtype[:3] == 'int':
|
||||
dtype = 'int32'
|
||||
elif dtype[:5] == 'float':
|
||||
dtype = K.floatx()
|
||||
else:
|
||||
raise Exception('Uknown dtype (should be "int" or "float"): ' +
|
||||
str(dtype))
|
||||
|
||||
# create input layer
|
||||
input_layer = InputLayer(input_shape=input_shape,
|
||||
batch_input_shape=batch_input_shape,
|
||||
name=name, input_dtype=dtype)
|
||||
self._graph_inputs[name] = input_layer
|
||||
|
||||
# append input config to self._graph_input_config
|
||||
config = {'name': name, 'dtype': dtype}
|
||||
if batch_input_shape:
|
||||
config['batch_input_shape'] = batch_input_shape
|
||||
else:
|
||||
config['input_shape'] = input_shape
|
||||
self._graph_input_config.append(config)
|
||||
|
||||
def add_node(self, layer, name, input=None, inputs=[],
|
||||
merge_mode='concat', concat_axis=-1, dot_axes=-1,
|
||||
create_output=False):
|
||||
'''Adds a node in the graph. It can be connected to multiple
|
||||
inputs, which will first be merged into one tensor
|
||||
according to the mode specified.
|
||||
|
||||
# Arguments
|
||||
layer: the layer at the node.
|
||||
name: name for the node.
|
||||
input: when connecting the layer to a single input,
|
||||
this is the name of the incoming node.
|
||||
inputs: when connecting the layer to multiple inputs,
|
||||
this is a list of names of incoming nodes.
|
||||
merge_mode: one of {concat, sum, dot, ave, mul}
|
||||
concat_axis: when `merge_mode=='concat'`, this is the
|
||||
input concatenation axis.
|
||||
dot_axes: when `merge_mode='dot'`,
|
||||
this is the contraction axes specification;
|
||||
see the `Merge` layer for details.
|
||||
create_output: boolean. Set this to `True` if you want the output
|
||||
of your node to be an output of the graph.
|
||||
'''
|
||||
if name in self._graph_namespace:
|
||||
raise Exception('Duplicate node identifier: ' + name)
|
||||
self._graph_namespace.add(name)
|
||||
layer.name = name
|
||||
self.built = False
|
||||
|
||||
if input:
|
||||
if input not in self._graph_namespace:
|
||||
raise Exception('Unknown node/input identifier: ' + input)
|
||||
if input in self._graph_nodes:
|
||||
layer.add_inbound_node(self._graph_nodes[input])
|
||||
elif input in self._graph_inputs:
|
||||
layer.add_inbound_node(self._graph_inputs[input])
|
||||
if inputs:
|
||||
to_merge = []
|
||||
for n in inputs:
|
||||
if n in self._graph_nodes:
|
||||
to_merge.append(self._graph_nodes[n])
|
||||
elif n in self._graph_inputs:
|
||||
to_merge.append(self._graph_inputs[n])
|
||||
else:
|
||||
raise Exception('Unknown identifier: ' + n)
|
||||
merge = Merge(to_merge, mode=merge_mode,
|
||||
concat_axis=concat_axis, dot_axes=dot_axes,
|
||||
name='merge_inputs_for_' + name)
|
||||
layer.add_inbound_node(merge)
|
||||
self._graph_nodes[name] = layer
|
||||
self._graph_node_config.append({'name': name,
|
||||
'input': input,
|
||||
'inputs': inputs,
|
||||
'merge_mode': merge_mode,
|
||||
'concat_axis': concat_axis,
|
||||
'dot_axes': dot_axes,
|
||||
'create_output': create_output})
|
||||
if create_output:
|
||||
self.add_output(name, input=name)
|
||||
|
||||
def add_shared_node(self, layer, name, inputs=[], merge_mode=None,
|
||||
concat_axis=-1, dot_axes=-1, outputs=[],
|
||||
create_output=False):
|
||||
'''Used to share a same layer across multiple nodes.
|
||||
|
||||
Supposed, for instance, that you want to apply one same `Dense` layer
|
||||
after two different nodes ('node_a' and 'node_b').
|
||||
You can then add the dense layer as a shared node by calling:
|
||||
|
||||
```python
|
||||
model.add_shared_node(my_dense, name='shared_dense', inputs=['node_a', 'node_b'], ...)
|
||||
```
|
||||
|
||||
If you want access to the output of dense(node_a) and dense(node_b) separately,
|
||||
you can add these outputs to the Graph by passing an `outputs` argument:
|
||||
|
||||
```python
|
||||
model.add_shared_node(my_dense, name='shared_dense', inputs=['node_a', 'node_b'],
|
||||
outputs=['dense_output_a', 'dense_outputs_b'])
|
||||
```
|
||||
|
||||
Otherwise you can merge these different outputs via `merge_mode`.
|
||||
In that case you can access the merged output
|
||||
under the identifier `name`.
|
||||
|
||||
# Arguments
|
||||
layer: The layer to be shared across multiple inputs
|
||||
name: Name of the shared node
|
||||
inputs: List of names of input nodes
|
||||
merge_mode: Same meaning as `merge_mode` argument of `add_node()`
|
||||
concat_axis: Same meaning as `concat_axis` argument of `add_node()`
|
||||
dot_axes: Same meaning as `dot_axes` argument of `add_node()`
|
||||
outputs: Used when `merge_mode=None`. Names for the output nodes.
|
||||
create_output: Same meaning as `create_output` argument of `add_node()`.
|
||||
'''
|
||||
if name in self._graph_namespace:
|
||||
raise Exception('Duplicate node identifier: ' + name)
|
||||
self._graph_namespace.add(name)
|
||||
self.built = False
|
||||
|
||||
for o in outputs:
|
||||
if o in self._graph_namespace:
|
||||
raise Exception('Duplicate node identifier: ' + o)
|
||||
if merge_mode:
|
||||
if merge_mode not in {'sum', 'ave', 'mul', 'dot', 'cos', 'concat'}:
|
||||
raise Exception('Invalid merge mode:', merge_mode)
|
||||
input_layers = []
|
||||
for i in range(len(inputs)):
|
||||
input = inputs[i]
|
||||
if input in self._graph_nodes:
|
||||
n = self._graph_nodes[input]
|
||||
input_layers.append(n)
|
||||
elif input in self._graph_inputs:
|
||||
n = self._graph_inputs[input]
|
||||
input_layers.append(n)
|
||||
else:
|
||||
raise Exception('Unknown identifier: ' + input)
|
||||
|
||||
created_node_indices = []
|
||||
for input_layer in input_layers:
|
||||
created_node_indices.append(len(layer.inbound_nodes))
|
||||
layer.add_inbound_node(input_layer)
|
||||
|
||||
if merge_mode:
|
||||
layer.name = 'input_for_' + name
|
||||
# collect all output nodes of layer and merge them into a single output
|
||||
merge = Merge([layer for _ in range(len(inputs))],
|
||||
mode=merge_mode,
|
||||
concat_axis=concat_axis, dot_axes=dot_axes,
|
||||
node_indices=created_node_indices,
|
||||
name=name)
|
||||
self._graph_nodes[name] = merge
|
||||
if create_output:
|
||||
self.add_output(name, input=name)
|
||||
else:
|
||||
layer.name = name
|
||||
# create one new layer per output node of layer,
|
||||
# and add them to the Graph with their own identifiers
|
||||
if len(outputs) != len(inputs):
|
||||
raise Exception('When using merge_mode=None, '
|
||||
'you should provide a list of '
|
||||
'output names (`output` argument) '
|
||||
'the same size as `input`.')
|
||||
for i in range(len(outputs)):
|
||||
output_layer_name = outputs[i]
|
||||
output_layer = Layer(name=output_layer_name)
|
||||
output_layer.add_inbound_node(layer, created_node_indices[i])
|
||||
self._graph_namespace.add(output_layer_name)
|
||||
self._graph_nodes[output_layer_name] = output_layer
|
||||
if create_output:
|
||||
self.add_output(output_layer_name, input=output_layer_name)
|
||||
|
||||
self._graph_node_config.append({'name': name,
|
||||
'layer': {
|
||||
'config': layer.get_config(),
|
||||
'class_name': layer.__class__.__name__,
|
||||
},
|
||||
'inputs': inputs,
|
||||
'merge_mode': merge_mode,
|
||||
'concat_axis': concat_axis,
|
||||
'dot_axes': dot_axes,
|
||||
'outputs': outputs,
|
||||
'create_output': create_output if merge_mode else False})
|
||||
self._graph_shared_nodes_names.append(name)
|
||||
|
||||
def add_output(self, name, input=None, inputs=[],
|
||||
merge_mode='concat', concat_axis=-1, dot_axes=-1):
|
||||
'''Adds an output to the graph.
|
||||
|
||||
This output can merge several node outputs into a single output.
|
||||
|
||||
# Arguments
|
||||
name: name of the output.
|
||||
input: when connecting the layer to a single input,
|
||||
this is the name of the incoming node.
|
||||
inputs: when connecting the layer to multiple inputs,
|
||||
this is a list of names of incoming nodes.
|
||||
merge_mode: one of {concat, sum, dot, ave, mul}
|
||||
concat_axis: when `merge_mode=='concat'`, this is the
|
||||
input concatenation axis.
|
||||
dot_axes: when `merge_mode='dot'`,
|
||||
this is the contraction axes specification;
|
||||
see the `Merge layer for details.
|
||||
'''
|
||||
if name not in self._graph_namespace:
|
||||
self._graph_namespace.add(name)
|
||||
if name in self._graph_outputs:
|
||||
raise Exception('Duplicate output identifier:', name)
|
||||
self.built = False
|
||||
|
||||
if input:
|
||||
if input in self._graph_nodes:
|
||||
layer = self._graph_nodes[input]
|
||||
elif input in self._graph_inputs:
|
||||
layer = self._graph_inputs[input]
|
||||
else:
|
||||
raise Exception('Unknown node/input identifier: ' + input)
|
||||
if layer.name == name:
|
||||
self._graph_outputs[name] = layer
|
||||
else:
|
||||
layer.name = name
|
||||
self._graph_outputs[name] = layer
|
||||
if inputs:
|
||||
to_merge = []
|
||||
for n in inputs:
|
||||
if n not in self._graph_nodes:
|
||||
raise Exception('Unknown identifier: ' + n)
|
||||
to_merge.append(self._graph_nodes[n])
|
||||
merge = Merge(to_merge, mode=merge_mode,
|
||||
concat_axis=concat_axis, dot_axes=dot_axes,
|
||||
name=name)
|
||||
self._graph_outputs[name] = merge
|
||||
|
||||
self._graph_output_config.append({'name': name,
|
||||
'input': input,
|
||||
'inputs': inputs,
|
||||
'merge_mode': merge_mode,
|
||||
'concat_axis': concat_axis,
|
||||
'dot_axes': dot_axes})
|
||||
|
||||
def _get_x(self, data):
|
||||
x = []
|
||||
for key in self._graph_inputs.keys():
|
||||
if key not in data:
|
||||
raise Exception('Expected to be provided an array '
|
||||
'(in dict argument `data`) for input "' +
|
||||
key + '".')
|
||||
x.append(data[key])
|
||||
return x
|
||||
|
||||
def _get_y(self, data):
|
||||
y = []
|
||||
for key in self._graph_outputs.keys():
|
||||
if key not in data:
|
||||
raise Exception('Expected to be provided an array '
|
||||
'(in dict argument `data`) for output "' +
|
||||
key + '".')
|
||||
y.append(data[key])
|
||||
return y
|
||||
|
||||
def fit(self, data, batch_size=32, nb_epoch=10, verbose=1, callbacks=[],
|
||||
validation_split=0., validation_data=None, shuffle=True,
|
||||
class_weight=None, sample_weight=None, **kwargs):
|
||||
'''Trains the model for a fixed number of epochs.
|
||||
|
||||
Returns a history object. Its `history` attribute is a record of
|
||||
training loss values at successive epochs,
|
||||
as well as validation loss values (if applicable).
|
||||
|
||||
# Arguments
|
||||
data: dictionary mapping input names and outputs names to
|
||||
appropriate numpy arrays. All arrays should contain
|
||||
the same number of samples.
|
||||
batch_size: int. Number of samples per gradient update.
|
||||
nb_epoch: int.
|
||||
verbose: 0 for no logging to stdout,
|
||||
1 for progress bar logging, 2 for one log line per epoch.
|
||||
callbacks: `keras.callbacks.Callback` list. List of callbacks
|
||||
to apply during training. See [callbacks](callbacks.md).
|
||||
validation_split: float (0. < x < 1). Fraction of the data to
|
||||
use as held-out validation data.
|
||||
validation_data: dictionary mapping input names and outputs names
|
||||
to appropriate numpy arrays to be used as
|
||||
held-out validation data.
|
||||
All arrays should contain the same number of samples.
|
||||
Will override validation_split.
|
||||
shuffle: boolean. Whether to shuffle the samples at each epoch.
|
||||
class_weight: dictionary mapping output names to
|
||||
class weight dictionaries.
|
||||
sample_weight: dictionary mapping output names to
|
||||
numpy arrays of sample weights.
|
||||
'''
|
||||
if 'show_accuracy' in kwargs:
|
||||
kwargs.pop('show_accuracy')
|
||||
warnings.warn('The "show_accuracy" argument is deprecated, '
|
||||
'instead you should pass the "accuracy" metric to '
|
||||
'the model at compile time:\n'
|
||||
'`model.compile(optimizer, loss, '
|
||||
'metrics=["accuracy"])`')
|
||||
if kwargs:
|
||||
raise Exception('Received unknown keyword arguments: ' +
|
||||
str(kwargs))
|
||||
x = self._get_x(data)
|
||||
y = self._get_y(data)
|
||||
|
||||
if type(validation_data) is tuple:
|
||||
raise Exception('Cannot used sample_weight with '
|
||||
'validation data with legacy Graph model. '
|
||||
'validation_data should be a dictionary.')
|
||||
if validation_data:
|
||||
val_x = self._get_x(validation_data)
|
||||
val_y = self._get_y(validation_data)
|
||||
validation_data = (val_x, val_y)
|
||||
return super(Graph, self).fit(x, y,
|
||||
batch_size=batch_size,
|
||||
nb_epoch=nb_epoch,
|
||||
verbose=verbose,
|
||||
callbacks=callbacks,
|
||||
validation_split=validation_split,
|
||||
validation_data=validation_data,
|
||||
shuffle=shuffle,
|
||||
class_weight=class_weight,
|
||||
sample_weight=sample_weight)
|
||||
|
||||
def evaluate(self, data, batch_size=128,
|
||||
verbose=0, sample_weight={}, **kwargs):
|
||||
'''Computes the loss on some input data, batch by batch.
|
||||
|
||||
Returns the scalar test loss over the data,
|
||||
or a list of metrics values (starting with the test loss)
|
||||
if applicable.
|
||||
|
||||
Arguments: see `fit` method.
|
||||
'''
|
||||
if 'show_accuracy' in kwargs:
|
||||
kwargs.pop('show_accuracy')
|
||||
warnings.warn('The "show_accuracy" argument is deprecated, '
|
||||
'instead you should pass the "accuracy" metric to '
|
||||
'the model at compile time:\n'
|
||||
'`model.compile(optimizer, loss, '
|
||||
'metrics=["accuracy"])`')
|
||||
if kwargs:
|
||||
raise Exception('Received unknown keyword arguments: ' +
|
||||
str(kwargs))
|
||||
x = self._get_x(data)
|
||||
y = self._get_y(data)
|
||||
return super(Graph, self).evaluate(x, y,
|
||||
batch_size=batch_size,
|
||||
verbose=verbose,
|
||||
sample_weight=sample_weight)
|
||||
|
||||
def predict(self, data, batch_size=128, verbose=0):
|
||||
'''Generates output predictions for the input samples
|
||||
batch by batch.
|
||||
|
||||
Arguments: see `fit` method.
|
||||
'''
|
||||
x = self._get_x(data)
|
||||
output_list = super(Graph, self).predict(x, batch_size=batch_size,
|
||||
verbose=verbose)
|
||||
return dict(zip(self._graph_outputs, output_list))
|
||||
|
||||
def train_on_batch(self, data,
|
||||
class_weight={},
|
||||
sample_weight={}, **kwargs):
|
||||
'''Single gradient update on a batch of samples.
|
||||
|
||||
Returns the scalar train loss over the data,
|
||||
or a list of metrics values (starting with the test loss)
|
||||
if applicable.
|
||||
|
||||
Arguments: see `fit` method.
|
||||
'''
|
||||
if 'accuracy' in kwargs:
|
||||
kwargs.pop('accuracy')
|
||||
warnings.warn('The "accuracy" argument is deprecated, '
|
||||
'instead you should pass the "accuracy" metric to '
|
||||
'the model at compile time:\n'
|
||||
'`model.compile(optimizer, loss, '
|
||||
'metrics=["accuracy"])`')
|
||||
if kwargs:
|
||||
raise Exception('Received unknown keyword arguments: ' +
|
||||
str(kwargs))
|
||||
x = self._get_x(data)
|
||||
y = self._get_y(data)
|
||||
return super(Graph, self).train_on_batch(x, y,
|
||||
sample_weight=sample_weight,
|
||||
class_weight=class_weight)
|
||||
|
||||
def test_on_batch(self, data, sample_weight={}, **kwargs):
|
||||
'''Test the network on a single batch of samples.
|
||||
|
||||
Returns the scalar test loss over the data,
|
||||
or a list of metrics values (starting with the test loss)
|
||||
if applicable.
|
||||
|
||||
Arguments: see `fit` method.
|
||||
'''
|
||||
if 'accuracy' in kwargs:
|
||||
kwargs.pop('accuracy')
|
||||
warnings.warn('The "accuracy" argument is deprecated, '
|
||||
'instead you should pass the "accuracy" metric to '
|
||||
'the model at compile time:\n'
|
||||
'`model.compile(optimizer, loss, '
|
||||
'metrics=["accuracy"])`')
|
||||
if kwargs:
|
||||
raise Exception('Received unknown keyword arguments: ' +
|
||||
str(kwargs))
|
||||
x = self._get_x(data)
|
||||
y = self._get_y(data)
|
||||
return super(Graph, self).test_on_batch(x, y,
|
||||
sample_weight=sample_weight)
|
||||
|
||||
def predict_on_batch(self, data):
|
||||
output_list = super(Graph, self).predict_on_batch(data)
|
||||
return dict(zip(self._graph_outputs, output_list))
|
||||
|
||||
def fit_generator(self, generator, samples_per_epoch, nb_epoch,
|
||||
verbose=1, callbacks=[],
|
||||
validation_data=None, nb_val_samples=None,
|
||||
class_weight={}, **kwargs):
|
||||
'''Fits a model on data generated batch-by-batch by a Python generator.
|
||||
The generator is run in parallel to the model, for efficiency.
|
||||
For instance, this allows you to do real-time data augmentation
|
||||
on images on CPU in parallel to training your model on GPU.
|
||||
|
||||
# Arguments
|
||||
generator: a generator.
|
||||
The output of the generator must be either a tuple
|
||||
of dictionaries `(input_data, sample_weight)`
|
||||
or a dictionary `input_data`
|
||||
(mapping names of inputs and outputs to Numpy arrays).
|
||||
All arrays should contain the same number of samples.
|
||||
The generator is expected to loop over its data
|
||||
indefinitely. An epoch finishes when `samples_per_epoch`
|
||||
samples have been seen by the model.
|
||||
samples_per_epoch: integer, number of samples to process before
|
||||
going to the next epoch.
|
||||
nb_epoch: integer, total number of iterations on the data.
|
||||
verbose: verbosity mode, 0, 1, or 2.
|
||||
callbacks: list of callbacks to be called during training.
|
||||
validation_data: dictionary mapping input names and outputs names
|
||||
to appropriate numpy arrays to be used as
|
||||
held-out validation data, or a generator yielding such
|
||||
dictionaries. All arrays should contain the same number
|
||||
of samples. If a generator, will be called until more than
|
||||
`nb_val_samples` examples have been generated at the
|
||||
end of every epoch. These examples will then be used
|
||||
as the validation data.
|
||||
nb_val_samples: number of samples to use from validation
|
||||
generator at the end of every epoch.
|
||||
class_weight: dictionary mapping class indices to a weight
|
||||
for the class.
|
||||
|
||||
# Returns
|
||||
A `History` object.
|
||||
|
||||
# Examples
|
||||
|
||||
```python
|
||||
def generate_arrays_from_file(path):
|
||||
while 1:
|
||||
f = open(path)
|
||||
for line in f:
|
||||
# create numpy arrays of input data
|
||||
# and labels, from each line in the file
|
||||
x1, x2, y = process_line(line)
|
||||
yield ({'input_1': x1, 'input_2': x2, 'output': y})
|
||||
f.close()
|
||||
|
||||
graph.fit_generator(generate_arrays_from_file('/my_file.txt'),
|
||||
samples_per_epoch=10000, nb_epoch=10)
|
||||
```
|
||||
'''
|
||||
if 'show_accuracy' in kwargs:
|
||||
kwargs.pop('show_accuracy')
|
||||
warnings.warn('The "show_accuracy" argument is deprecated, '
|
||||
'instead you should pass the "accuracy" metric to '
|
||||
'the model at compile time:\n'
|
||||
'`model.compile(optimizer, loss, '
|
||||
'metrics=["accuracy"])`')
|
||||
if 'nb_worker' in kwargs:
|
||||
kwargs.pop('nb_worker')
|
||||
warnings.warn('The "nb_worker" argument is deprecated, '
|
||||
'please remove it from your code.')
|
||||
if 'nb_val_worker' in kwargs:
|
||||
kwargs.pop('nb_val_worker')
|
||||
warnings.warn('The "nb_val_worker" argument is deprecated, '
|
||||
'please remove it from your code.')
|
||||
if kwargs:
|
||||
raise Exception('Received unknown keyword arguments: ' +
|
||||
str(kwargs))
|
||||
|
||||
self._train_on_batch = self.train_on_batch
|
||||
self.train_on_batch = super(Graph, self).train_on_batch
|
||||
self._evaluate = self.evaluate
|
||||
self.evaluate = super(Graph, self).evaluate
|
||||
|
||||
if validation_data and type(validation_data) is tuple:
|
||||
raise Exception('Cannot use sample_weight with '
|
||||
'validation_data in legacy Graph model.')
|
||||
if validation_data and type(validation_data) is dict:
|
||||
validation_data = (self._get_x(validation_data),
|
||||
self._get_y(validation_data))
|
||||
|
||||
original_generator = generator
|
||||
|
||||
def fixed_generator():
|
||||
while 1:
|
||||
data = next(original_generator)
|
||||
if type(data) is tuple:
|
||||
data, sample_weight = data
|
||||
x = self._get_x(data)
|
||||
y = self._get_y(data)
|
||||
yield x, y, sample_weight
|
||||
else:
|
||||
x = self._get_x(data)
|
||||
y = self._get_y(data)
|
||||
yield x, y
|
||||
|
||||
generator = fixed_generator()
|
||||
history = super(Graph, self).fit_generator(generator,
|
||||
samples_per_epoch,
|
||||
nb_epoch,
|
||||
verbose=verbose,
|
||||
callbacks=callbacks,
|
||||
validation_data=validation_data,
|
||||
nb_val_samples=nb_val_samples,
|
||||
class_weight=class_weight)
|
||||
self.train_on_batch = self._train_on_batch
|
||||
self.evaluate = self._evaluate
|
||||
return history
|
||||
|
||||
def evaluate_generator(self, generator, val_samples,
|
||||
verbose=1, **kwargs):
|
||||
'''Evaluates the model on a generator. The generator should
|
||||
return the same kind of data with every yield as accepted
|
||||
by `evaluate`.
|
||||
|
||||
If `show_accuracy`, it returns a tuple `(loss, accuracy)`,
|
||||
otherwise it returns the loss value.
|
||||
|
||||
Arguments:
|
||||
generator:
|
||||
generator yielding dictionaries of the kind accepted
|
||||
by `evaluate`, or tuples of such dictionaries and
|
||||
associated dictionaries of sample weights.
|
||||
val_samples:
|
||||
total number of samples to generate from `generator`
|
||||
to use in validation.
|
||||
|
||||
Other arguments are the same as for `fit`.
|
||||
'''
|
||||
if 'show_accuracy' in kwargs:
|
||||
kwargs.pop('show_accuracy')
|
||||
warnings.warn('The "show_accuracy" argument is deprecated, '
|
||||
'instead you should pass the "accuracy" metric to '
|
||||
'the model at compile time:\n'
|
||||
'`model.compile(optimizer, loss, '
|
||||
'metrics=["accuracy"])`')
|
||||
if 'verbose' in kwargs:
|
||||
kwargs.pop('verbose')
|
||||
warnings.warn('The "verbose" argument is deprecated.')
|
||||
if kwargs:
|
||||
raise Exception('Received unknown keyword arguments: ' +
|
||||
str(kwargs))
|
||||
|
||||
self._test_on_batch = self.test_on_batch
|
||||
self.test_on_batch = super(Graph, self).test_on_batch
|
||||
|
||||
original_generator = generator
|
||||
|
||||
def fixed_generator():
|
||||
while 1:
|
||||
data = next(original_generator)
|
||||
if type(data) is tuple:
|
||||
data, sample_weight = data
|
||||
x = self._get_x(data)
|
||||
y = self._get_y(data)
|
||||
yield x, y, sample_weight
|
||||
else:
|
||||
x = self._get_x(data)
|
||||
y = self._get_y(data)
|
||||
yield x, y
|
||||
|
||||
generator = fixed_generator()
|
||||
history = super(Graph, self).evaluate_generator(generator,
|
||||
val_samples)
|
||||
self.test_on_batch = self._test_on_batch
|
||||
return history
|
||||
|
||||
# get_weights, set_weights: inherited
|
||||
def get_config(self):
|
||||
config = {'input_config': self._graph_input_config,
|
||||
'node_config': self._graph_node_config,
|
||||
'output_config': self._graph_output_config}
|
||||
nodes = {}
|
||||
for name, node in self._graph_nodes.items():
|
||||
nodes[name] = {'class_name': node.__class__.__name__,
|
||||
'config': node.get_config()}
|
||||
if name in self._graph_shared_nodes_names:
|
||||
nodes[name]['shared'] = True
|
||||
config['nodes'] = nodes
|
||||
return copy.deepcopy(config)
|
||||
|
||||
@classmethod
|
||||
def from_config(cls, config):
|
||||
# TODO: test legacy support
|
||||
from keras.utils.layer_utils import layer_from_config
|
||||
|
||||
def normalize_legacy_config(conf):
|
||||
if 'class_name' not in conf:
|
||||
class_name = conf['name']
|
||||
name = conf.get('custom_name')
|
||||
conf['name'] = name
|
||||
new_config = {
|
||||
'class_name': class_name,
|
||||
'config': conf,
|
||||
}
|
||||
return new_config
|
||||
return conf
|
||||
|
||||
graph = cls()
|
||||
inputs = config.get('input_config')
|
||||
for input in inputs:
|
||||
graph.add_input(**input)
|
||||
|
||||
nodes = config.get('node_config')
|
||||
for node in nodes:
|
||||
layer_config = config['nodes'][node['name']]
|
||||
layer_config = normalize_legacy_config(layer_config)
|
||||
if 'layer' in node:
|
||||
# for add_shared_node
|
||||
node['layer'] = layer_from_config(node['layer'])
|
||||
else:
|
||||
layer = layer_from_config(layer_config)
|
||||
node['layer'] = layer
|
||||
|
||||
node['create_output'] = False # outputs will be added below
|
||||
if layer_config.get('shared'):
|
||||
graph.add_shared_node(**node)
|
||||
else:
|
||||
graph.add_node(**node)
|
||||
|
||||
outputs = config.get('output_config')
|
||||
for output in outputs:
|
||||
graph.add_output(**output)
|
||||
return graph
|
||||
|
||||
def load_weights(self, fname):
|
||||
if not self.built:
|
||||
self.build()
|
||||
super(Graph, self).load_weights(fname)
|
||||
@@ -0,0 +1,10 @@
|
||||
from . import backend as K
|
||||
|
||||
|
||||
def binary_accuracy(y_true, y_pred):
|
||||
return K.mean(K.equal(y_true, K.round(y_pred)))
|
||||
|
||||
|
||||
def categorical_accuracy(y_true, y_pred):
|
||||
return K.mean(K.equal(K.argmax(y_true, axis=-1),
|
||||
K.argmax(y_pred, axis=-1)))
|
||||
+613
-1409
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -36,6 +36,12 @@ def categorical_crossentropy(y_true, y_pred):
|
||||
return K.categorical_crossentropy(y_pred, y_true)
|
||||
|
||||
|
||||
def sparse_categorical_crossentropy(y_true, y_pred):
|
||||
'''expects a 1-D or 2-D array of integer classes.
|
||||
'''
|
||||
return K.sparse_categorical_crossentropy(y_pred, y_true)
|
||||
|
||||
|
||||
def binary_crossentropy(y_true, y_pred):
|
||||
return K.mean(K.binary_crossentropy(y_pred, y_true), axis=-1)
|
||||
|
||||
|
||||
+107
-38
@@ -31,6 +31,7 @@ class Optimizer(object):
|
||||
def __init__(self, **kwargs):
|
||||
self.__dict__.update(kwargs)
|
||||
self.updates = []
|
||||
self.weights = []
|
||||
|
||||
def get_state(self):
|
||||
return [K.get_value(u[0]) for u in self.updates]
|
||||
@@ -52,13 +53,48 @@ class Optimizer(object):
|
||||
grads = [K.clip(g, -self.clipvalue, self.clipvalue) for g in grads]
|
||||
return grads
|
||||
|
||||
def set_weights(self, weights):
|
||||
'''Sets the weights of the optimizer, from Numpy arrays.
|
||||
|
||||
Should only be called after computing the gradients
|
||||
(otherwise the optimizer has no weights).
|
||||
|
||||
# Arguments
|
||||
weights: a list of Numpy arrays. The number
|
||||
of arrays and their shape must match
|
||||
number of the dimensions of the weights
|
||||
of the optimizer (i.e. it should match the
|
||||
output of `get_weights`).
|
||||
'''
|
||||
params = self.weights
|
||||
if len(params) != len(weights):
|
||||
raise Exception('Provided weight array does not match weights (' +
|
||||
str(len(params)) + ' optimizer params vs. ' +
|
||||
str(len(weights)) + ' provided weights)')
|
||||
for p, w in zip(params, weights):
|
||||
if K.get_value(p).shape != w.shape:
|
||||
raise Exception('Optimizer weight shape ' +
|
||||
str(K.get_value(p).shape) +
|
||||
' not compatible with '
|
||||
'provided weight shape ' + str(w.shape))
|
||||
K.set_value(p, w)
|
||||
|
||||
def get_weights(self):
|
||||
'''Returns the current weights of the optimizer,
|
||||
as a list of numpy arrays.
|
||||
'''
|
||||
weights = []
|
||||
for p in self.weights:
|
||||
weights.append(K.get_value(p))
|
||||
return weights
|
||||
|
||||
def get_config(self):
|
||||
return {"name": self.__class__.__name__}
|
||||
|
||||
|
||||
class SGD(Optimizer):
|
||||
'''Stochastic gradient descent, with support for momentum,
|
||||
decay, and Nesterov momentum.
|
||||
learning rate decay, and Nesterov momentum.
|
||||
|
||||
# Arguments
|
||||
lr: float >= 0. Learning rate.
|
||||
@@ -77,10 +113,10 @@ class SGD(Optimizer):
|
||||
|
||||
def get_updates(self, params, constraints, loss):
|
||||
grads = self.get_gradients(loss, params)
|
||||
lr = self.lr * (1.0 / (1.0 + self.decay * self.iterations))
|
||||
lr = self.lr * (1. / (1. + self.decay * self.iterations))
|
||||
self.updates = [(self.iterations, self.iterations + 1.)]
|
||||
|
||||
for p, g, c in zip(params, grads, constraints):
|
||||
for p, g in zip(params, grads):
|
||||
m = K.variable(np.zeros(K.get_value(p).shape)) # momentum
|
||||
v = self.momentum * m - lr * g # velocity
|
||||
self.updates.append((m, v))
|
||||
@@ -90,7 +126,11 @@ class SGD(Optimizer):
|
||||
else:
|
||||
new_p = p + v
|
||||
|
||||
self.updates.append((p, c(new_p))) # apply constraints
|
||||
# apply constraints
|
||||
if p in constraints:
|
||||
c = constraints[p]
|
||||
new_p = c(new_p)
|
||||
self.updates.append((p, new_p))
|
||||
return self.updates
|
||||
|
||||
def get_config(self):
|
||||
@@ -105,7 +145,8 @@ class RMSprop(Optimizer):
|
||||
'''RMSProp optimizer.
|
||||
|
||||
It is recommended to leave the parameters of this optimizer
|
||||
at their default values.
|
||||
at their default values
|
||||
(except the learning rate, which can be freely tuned).
|
||||
|
||||
This optimizer is usually a good choice for recurrent
|
||||
neural networks.
|
||||
@@ -123,16 +164,21 @@ class RMSprop(Optimizer):
|
||||
|
||||
def get_updates(self, params, constraints, loss):
|
||||
grads = self.get_gradients(loss, params)
|
||||
accumulators = [K.variable(np.zeros(K.get_value(p).shape)) for p in params]
|
||||
# accumulators
|
||||
self.weights = [K.variable(np.zeros(K.get_value(p).shape)) for p in params]
|
||||
self.updates = []
|
||||
|
||||
for p, g, a, c in zip(params, grads, accumulators, constraints):
|
||||
for p, g, a in zip(params, grads, self.weights):
|
||||
# update accumulator
|
||||
new_a = self.rho * a + (1 - self.rho) * K.square(g)
|
||||
new_a = self.rho * a + (1. - self.rho) * K.square(g)
|
||||
self.updates.append((a, new_a))
|
||||
|
||||
new_p = p - self.lr * g / K.sqrt(new_a + self.epsilon)
|
||||
self.updates.append((p, c(new_p))) # apply constraints
|
||||
|
||||
# apply constraints
|
||||
if p in constraints:
|
||||
c = constraints[p]
|
||||
new_p = c(new_p)
|
||||
self.updates.append((p, new_p))
|
||||
return self.updates
|
||||
|
||||
def get_config(self):
|
||||
@@ -159,14 +205,19 @@ class Adagrad(Optimizer):
|
||||
|
||||
def get_updates(self, params, constraints, loss):
|
||||
grads = self.get_gradients(loss, params)
|
||||
accumulators = [K.variable(np.zeros(K.get_value(p).shape)) for p in params]
|
||||
# accumulators
|
||||
self.weights = [K.variable(np.zeros(K.get_value(p).shape)) for p in params]
|
||||
self.updates = []
|
||||
|
||||
for p, g, a, c in zip(params, grads, accumulators, constraints):
|
||||
for p, g, a in zip(params, grads, self.weights):
|
||||
new_a = a + K.square(g) # update accumulator
|
||||
self.updates.append((a, new_a))
|
||||
new_p = p - self.lr * g / K.sqrt(new_a + self.epsilon)
|
||||
self.updates.append((p, c(new_p))) # apply constraints
|
||||
# apply constraints
|
||||
if p in constraints:
|
||||
c = constraints[p]
|
||||
new_p = c(new_p)
|
||||
self.updates.append((p, new_p))
|
||||
return self.updates
|
||||
|
||||
def get_config(self):
|
||||
@@ -182,7 +233,8 @@ class Adadelta(Optimizer):
|
||||
at their default values.
|
||||
|
||||
# Arguments
|
||||
lr: float >= 0. Learning rate. It is recommended to leave it at the default value.
|
||||
lr: float >= 0. Learning rate.
|
||||
It is recommended to leave it at the default value.
|
||||
rho: float >= 0.
|
||||
epsilon: float >= 0. Fuzz factor.
|
||||
|
||||
@@ -198,19 +250,23 @@ class Adadelta(Optimizer):
|
||||
grads = self.get_gradients(loss, params)
|
||||
accumulators = [K.variable(np.zeros(K.get_value(p).shape)) for p in params]
|
||||
delta_accumulators = [K.variable(np.zeros(K.get_value(p).shape)) for p in params]
|
||||
self.weights = accumulators + delta_accumulators
|
||||
self.updates = []
|
||||
|
||||
for p, g, a, d_a, c in zip(params, grads, accumulators,
|
||||
delta_accumulators, constraints):
|
||||
for p, g, a, d_a in zip(params, grads, accumulators, delta_accumulators):
|
||||
# update accumulator
|
||||
new_a = self.rho * a + (1 - self.rho) * K.square(g)
|
||||
new_a = self.rho * a + (1. - self.rho) * K.square(g)
|
||||
self.updates.append((a, new_a))
|
||||
|
||||
# use the new accumulator and the *old* delta_accumulator
|
||||
update = g * K.sqrt(d_a + self.epsilon) / K.sqrt(new_a + self.epsilon)
|
||||
|
||||
new_p = p - self.lr * update
|
||||
self.updates.append((p, c(new_p))) # apply constraints
|
||||
# apply constraints
|
||||
if p in constraints:
|
||||
c = constraints[p]
|
||||
new_p = c(new_p)
|
||||
self.updates.append((p, new_p))
|
||||
|
||||
# update delta_accumulator
|
||||
new_d_a = self.rho * d_a + (1 - self.rho) * K.square(update)
|
||||
@@ -248,24 +304,29 @@ class Adam(Optimizer):
|
||||
|
||||
def get_updates(self, params, constraints, loss):
|
||||
grads = self.get_gradients(loss, params)
|
||||
self.updates = [(self.iterations, self.iterations+1.)]
|
||||
self.updates = [(self.iterations, self.iterations + 1)]
|
||||
|
||||
t = self.iterations + 1
|
||||
lr_t = self.lr * K.sqrt(1 - K.pow(self.beta_2, t)) / (1 - K.pow(self.beta_1, t))
|
||||
lr_t = self.lr * K.sqrt(1. - K.pow(self.beta_2, t)) / (1. - K.pow(self.beta_1, t))
|
||||
|
||||
for p, g, c in zip(params, grads, constraints):
|
||||
# zero init of moment
|
||||
m = K.variable(np.zeros(K.get_value(p).shape))
|
||||
# zero init of velocity
|
||||
v = K.variable(np.zeros(K.get_value(p).shape))
|
||||
ms = [K.variable(np.zeros(K.get_value(p).shape)) for p in params]
|
||||
vs = [K.variable(np.zeros(K.get_value(p).shape)) for p in params]
|
||||
self.weights = ms + vs
|
||||
|
||||
m_t = (self.beta_1 * m) + (1 - self.beta_1) * g
|
||||
v_t = (self.beta_2 * v) + (1 - self.beta_2) * K.square(g)
|
||||
for p, g, m, v in zip(params, grads, ms, vs):
|
||||
m_t = (self.beta_1 * m) + (1. - self.beta_1) * g
|
||||
v_t = (self.beta_2 * v) + (1. - self.beta_2) * K.square(g)
|
||||
p_t = p - lr_t * m_t / (K.sqrt(v_t) + self.epsilon)
|
||||
|
||||
self.updates.append((m, m_t))
|
||||
self.updates.append((v, v_t))
|
||||
self.updates.append((p, c(p_t))) # apply constraints
|
||||
|
||||
new_p = p_t
|
||||
# apply constraints
|
||||
if p in constraints:
|
||||
c = constraints[p]
|
||||
new_p = c(new_p)
|
||||
self.updates.append((p, new_p))
|
||||
return self.updates
|
||||
|
||||
def get_config(self):
|
||||
@@ -294,31 +355,39 @@ class Adamax(Optimizer):
|
||||
*args, **kwargs):
|
||||
super(Adamax, self).__init__(**kwargs)
|
||||
self.__dict__.update(locals())
|
||||
self.iterations = K.variable(0)
|
||||
self.iterations = K.variable(0.)
|
||||
self.lr = K.variable(lr)
|
||||
self.beta_1 = K.variable(beta_1)
|
||||
self.beta_2 = K.variable(beta_2)
|
||||
|
||||
def get_updates(self, params, constraints, loss):
|
||||
grads = self.get_gradients(loss, params)
|
||||
self.updates = [(self.iterations, self.iterations+1.)]
|
||||
self.updates = [(self.iterations, self.iterations + 1)]
|
||||
|
||||
t = self.iterations + 1
|
||||
lr_t = self.lr / (1 - K.pow(self.beta_1, t))
|
||||
lr_t = self.lr / (1. - K.pow(self.beta_1, t))
|
||||
|
||||
for p, g, c in zip(params, grads, constraints):
|
||||
# zero init of 1st moment
|
||||
m = K.variable(np.zeros(K.get_value(p).shape))
|
||||
# zero init of exponentially weighted infinity norm
|
||||
u = K.variable(np.zeros(K.get_value(p).shape))
|
||||
# zero init of 1st moment
|
||||
ms = [K.variable(np.zeros(K.get_value(p).shape)) for p in params]
|
||||
# zero init of exponentially weighted infinity norm
|
||||
us = [K.variable(np.zeros(K.get_value(p).shape)) for p in params]
|
||||
self.weights = ms + us
|
||||
|
||||
m_t = (self.beta_1 * m) + (1 - self.beta_1) * g
|
||||
for p, g, m, u in zip(params, grads, ms, us):
|
||||
|
||||
m_t = (self.beta_1 * m) + (1. - self.beta_1) * g
|
||||
u_t = K.maximum(self.beta_2 * u, K.abs(g))
|
||||
p_t = p - lr_t * m_t / (u_t + self.epsilon)
|
||||
|
||||
self.updates.append((m, m_t))
|
||||
self.updates.append((u, u_t))
|
||||
self.updates.append((p, c(p_t))) # apply constraints
|
||||
|
||||
new_p = p_t
|
||||
# apply constraints
|
||||
if p in constraints:
|
||||
c = constraints[p]
|
||||
new_p = c(new_p)
|
||||
self.updates.append((p, new_p))
|
||||
return self.updates
|
||||
|
||||
def get_config(self):
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
'''Fairly basic set of tools for realtime data augmentation on image data.
|
||||
Can easily be extended to include new transformations,
|
||||
new preprocessing methods, etc...
|
||||
'''
|
||||
from __future__ import absolute_import
|
||||
|
||||
import numpy as np
|
||||
@@ -7,18 +11,13 @@ from scipy import linalg
|
||||
|
||||
from os import listdir
|
||||
from os.path import isfile, join
|
||||
import random
|
||||
import math
|
||||
from six.moves import range
|
||||
import threading
|
||||
|
||||
'''Fairly basic set of tools for realtime data augmentation on image data.
|
||||
Can easily be extended to include new transformations, new preprocessing methods, etc...
|
||||
'''
|
||||
|
||||
|
||||
def random_rotation(x, rg, fill_mode="nearest", cval=0.):
|
||||
angle = random.uniform(-rg, rg)
|
||||
def random_rotation(x, rg, fill_mode='nearest', cval=0.):
|
||||
angle = np.random.uniform(-rg, rg)
|
||||
x = ndimage.interpolation.rotate(x, angle,
|
||||
axes=(1, 2),
|
||||
reshape=False,
|
||||
@@ -27,19 +26,14 @@ def random_rotation(x, rg, fill_mode="nearest", cval=0.):
|
||||
return x
|
||||
|
||||
|
||||
def random_shift(x, wrg, hrg, fill_mode="nearest", cval=0.):
|
||||
crop_left_pixels = 0
|
||||
crop_top_pixels = 0
|
||||
def random_shift(x, wrg, hrg, fill_mode='nearest', cval=0.):
|
||||
shift_x = shift_y = 0
|
||||
|
||||
if wrg:
|
||||
crop = random.uniform(0., wrg)
|
||||
split = random.uniform(0, 1)
|
||||
crop_left_pixels = int(split*crop*x.shape[1])
|
||||
shift_x = np.random.uniform(-wrg, wrg) * x.shape[2]
|
||||
if hrg:
|
||||
crop = random.uniform(0., hrg)
|
||||
split = random.uniform(0, 1)
|
||||
crop_top_pixels = int(split*crop*x.shape[2])
|
||||
x = ndimage.interpolation.shift(x, (0, crop_left_pixels, crop_top_pixels),
|
||||
shift_y = np.random.uniform(-hrg, hrg) * x.shape[1]
|
||||
x = ndimage.interpolation.shift(x, (0, shift_y, shift_x),
|
||||
order=0,
|
||||
mode=fill_mode,
|
||||
cval=cval)
|
||||
@@ -63,8 +57,8 @@ def random_barrel_transform(x, intensity):
|
||||
pass
|
||||
|
||||
|
||||
def random_shear(x, intensity, fill_mode="nearest", cval=0.):
|
||||
shear = random.uniform(-intensity, intensity)
|
||||
def random_shear(x, intensity, fill_mode='nearest', cval=0.):
|
||||
shear = np.random.uniform(-intensity, intensity)
|
||||
shear_matrix = np.array([[1.0, -math.sin(shear), 0.0],
|
||||
[0.0, math.cos(shear), 0.0],
|
||||
[0.0, 0.0, 1.0]])
|
||||
@@ -80,9 +74,9 @@ def random_channel_shift(x, rg):
|
||||
pass
|
||||
|
||||
|
||||
def random_zoom(x, rg, fill_mode="nearest", cval=0.):
|
||||
zoom_w = random.uniform(1.-rg, 1.)
|
||||
zoom_h = random.uniform(1.-rg, 1.)
|
||||
def random_zoom(x, rg, fill_mode='nearest', cval=0.):
|
||||
zoom_w = np.random.uniform(1.-rg, 1.)
|
||||
zoom_h = np.random.uniform(1.-rg, 1.)
|
||||
x = ndimage.interpolation.zoom(x, zoom=(1., zoom_w, zoom_h),
|
||||
mode=fill_mode,
|
||||
cval=cval)
|
||||
@@ -98,10 +92,10 @@ def array_to_img(x, scale=True):
|
||||
x *= 255
|
||||
if x.shape[2] == 3:
|
||||
# RGB
|
||||
return Image.fromarray(x.astype("uint8"), "RGB")
|
||||
return Image.fromarray(x.astype('uint8'), 'RGB')
|
||||
else:
|
||||
# grayscale
|
||||
return Image.fromarray(x[:, :, 0].astype("uint8"), "L")
|
||||
return Image.fromarray(x[:, :, 0].astype('uint8'), 'L')
|
||||
|
||||
|
||||
def img_to_array(img):
|
||||
@@ -132,21 +126,33 @@ def list_pictures(directory, ext='jpg|jpeg|bmp|png'):
|
||||
|
||||
class ImageDataGenerator(object):
|
||||
'''Generate minibatches with
|
||||
realtime data augmentation.
|
||||
real-time data augmentation.
|
||||
|
||||
# Arguments
|
||||
featurewise_center: set input mean to 0 over the dataset.
|
||||
samplewise_center: set each sample mean to 0.
|
||||
featurewise_std_normalization: divide inputs by std of the dataset.
|
||||
samplewise_std_normalization: divide each input by its std.
|
||||
zca_whitening: apply ZCA whitening.
|
||||
rotation_range: degrees (0 to 180).
|
||||
width_shift_range: fraction of total width.
|
||||
height_shift_range: fraction of total height.
|
||||
shear_range: shear intensity (shear angle in radians).
|
||||
horizontal_flip: whether to randomly flip images horizontally.
|
||||
vertical_flip: whether to randomly flip images vertically.
|
||||
'''
|
||||
def __init__(self,
|
||||
featurewise_center=True, # set input mean to 0 over the dataset
|
||||
samplewise_center=False, # set each sample mean to 0
|
||||
featurewise_std_normalization=True, # divide inputs by std of the dataset
|
||||
samplewise_std_normalization=False, # divide each input by its std
|
||||
zca_whitening=False, # apply ZCA whitening
|
||||
rotation_range=0., # degrees (0 to 180)
|
||||
width_shift_range=0., # fraction of total width
|
||||
height_shift_range=0., # fraction of total height
|
||||
shear_range=0., # shear intensity (shear angle in radians)
|
||||
featurewise_center=True,
|
||||
samplewise_center=False,
|
||||
featurewise_std_normalization=True,
|
||||
samplewise_std_normalization=False,
|
||||
zca_whitening=False,
|
||||
rotation_range=0.,
|
||||
width_shift_range=0.,
|
||||
height_shift_range=0.,
|
||||
shear_range=0.,
|
||||
horizontal_flip=False,
|
||||
vertical_flip=False):
|
||||
|
||||
self.__dict__.update(locals())
|
||||
self.mean = None
|
||||
self.std = None
|
||||
@@ -177,26 +183,30 @@ class ImageDataGenerator(object):
|
||||
else:
|
||||
b = 0
|
||||
total_b += 1
|
||||
yield index_array[current_index: current_index + current_batch_size], current_index, current_batch_size
|
||||
yield (index_array[current_index: current_index + current_batch_size],
|
||||
current_index, current_batch_size)
|
||||
|
||||
def flow(self, X, y, batch_size=32, shuffle=False, seed=None,
|
||||
save_to_dir=None, save_prefix="", save_format="jpeg"):
|
||||
save_to_dir=None, save_prefix='', save_format='jpeg'):
|
||||
assert len(X) == len(y)
|
||||
self.X = X
|
||||
self.y = y
|
||||
self.save_to_dir = save_to_dir
|
||||
self.save_prefix = save_prefix
|
||||
self.save_format = save_format
|
||||
self.flow_generator = self._flow_index(X.shape[0], batch_size, shuffle, seed)
|
||||
self.flow_generator = self._flow_index(X.shape[0], batch_size,
|
||||
shuffle, seed)
|
||||
return self
|
||||
|
||||
def __iter__(self):
|
||||
# needed if we want to do something like for x,y in data_gen.flow(...):
|
||||
# needed if we want to do something like:
|
||||
# for x, y in data_gen.flow(...):
|
||||
return self
|
||||
|
||||
def next(self):
|
||||
# for python 2.x
|
||||
# Keep under lock only the mechainsem which advance the indexing of each batch
|
||||
# for python 2.x.
|
||||
# Keeps under lock only the mechanism which advances
|
||||
# the indexing of each batch
|
||||
# see # http://anandology.com/blog/using-iterators-and-generators/
|
||||
with self.lock:
|
||||
index_array, current_index, current_batch_size = next(self.flow_generator)
|
||||
@@ -204,36 +214,36 @@ class ImageDataGenerator(object):
|
||||
bX = np.zeros(tuple([current_batch_size] + list(self.X.shape)[1:]))
|
||||
for i, j in enumerate(index_array):
|
||||
x = self.X[j]
|
||||
x = self.random_transform(x.astype("float32"))
|
||||
x = self.random_transform(x.astype('float32'))
|
||||
x = self.standardize(x)
|
||||
bX[i] = x
|
||||
if self.save_to_dir:
|
||||
for i in range(current_batch_size):
|
||||
img = array_to_img(bX[i], scale=True)
|
||||
img.save(self.save_to_dir + "/" + self.save_prefix + "_" + str(current_index + i) + "." + self.save_format)
|
||||
img.save(self.save_to_dir + '/' + self.save_prefix + '_' + str(current_index + i) + '.' + self.save_format)
|
||||
bY = self.y[index_array]
|
||||
return bX, bY
|
||||
|
||||
def __next__(self):
|
||||
# for python 3.x
|
||||
# for python 3.x.
|
||||
return self.next()
|
||||
|
||||
def standardize(self, x):
|
||||
if self.samplewise_center:
|
||||
x -= np.mean(x, axis=1, keepdims=True)
|
||||
if self.samplewise_std_normalization:
|
||||
x /= (np.std(x, axis=1, keepdims=True) + 1e-7)
|
||||
|
||||
if self.featurewise_center:
|
||||
x -= self.mean
|
||||
if self.featurewise_std_normalization:
|
||||
x /= self.std
|
||||
x /= (self.std + 1e-7)
|
||||
|
||||
if self.zca_whitening:
|
||||
flatx = np.reshape(x, (x.shape[0]*x.shape[1]*x.shape[2]))
|
||||
flatx = np.reshape(x, (x.shape[0] * x.shape[1] * x.shape[2]))
|
||||
whitex = np.dot(flatx, self.principal_components)
|
||||
x = np.reshape(whitex, (x.shape[0], x.shape[1], x.shape[2]))
|
||||
|
||||
if self.samplewise_center:
|
||||
x -= np.mean(x)
|
||||
if self.samplewise_std_normalization:
|
||||
x /= np.std(x)
|
||||
|
||||
return x
|
||||
|
||||
def random_transform(self, x):
|
||||
@@ -242,34 +252,41 @@ class ImageDataGenerator(object):
|
||||
if self.width_shift_range or self.height_shift_range:
|
||||
x = random_shift(x, self.width_shift_range, self.height_shift_range)
|
||||
if self.horizontal_flip:
|
||||
if random.random() < 0.5:
|
||||
if np.random.random() < 0.5:
|
||||
x = horizontal_flip(x)
|
||||
if self.vertical_flip:
|
||||
if random.random() < 0.5:
|
||||
if np.random.random() < 0.5:
|
||||
x = vertical_flip(x)
|
||||
if self.shear_range:
|
||||
x = random_shear(x,self.shear_range)
|
||||
x = random_shear(x, self.shear_range)
|
||||
# TODO:
|
||||
# zoom
|
||||
# barrel/fisheye
|
||||
# shearing
|
||||
# channel shifting
|
||||
return x
|
||||
|
||||
def fit(self, X,
|
||||
augment=False, # fit on randomly augmented samples
|
||||
rounds=1, # if augment, how many augmentation passes over the data do we use
|
||||
augment=False,
|
||||
rounds=1,
|
||||
seed=None):
|
||||
'''Required for featurewise_center, featurewise_std_normalization and zca_whitening.
|
||||
'''Required for featurewise_center, featurewise_std_normalization
|
||||
and zca_whitening.
|
||||
|
||||
# Arguments
|
||||
X: Numpy array, the data to fit on.
|
||||
augment: whether to fit on randomly augmented samples
|
||||
rounds: if `augment`,
|
||||
how many augmentation passes to do over the data
|
||||
seed: random seed.
|
||||
'''
|
||||
X = np.copy(X)
|
||||
if augment:
|
||||
aX = np.zeros(tuple([rounds*X.shape[0]]+list(X.shape)[1:]))
|
||||
aX = np.zeros(tuple([rounds * X.shape[0]] + list(X.shape)[1:]))
|
||||
for r in range(rounds):
|
||||
for i in range(X.shape[0]):
|
||||
img = array_to_img(X[i])
|
||||
img = self.random_transform(img)
|
||||
aX[i+r*X.shape[0]] = img_to_array(img)
|
||||
aX[i + r * X.shape[0]] = img_to_array(img)
|
||||
X = aX
|
||||
|
||||
if self.featurewise_center:
|
||||
@@ -277,14 +294,13 @@ class ImageDataGenerator(object):
|
||||
X -= self.mean
|
||||
if self.featurewise_std_normalization:
|
||||
self.std = np.std(X, axis=0)
|
||||
X /= self.std
|
||||
X /= (self.std + 1e-7)
|
||||
|
||||
if self.zca_whitening:
|
||||
flatX = np.reshape(X, (X.shape[0], X.shape[1]*X.shape[2]*X.shape[3]))
|
||||
fudge = 10e-6
|
||||
flatX = np.reshape(X, (X.shape[0], X.shape[1] * X.shape[2] * X.shape[3]))
|
||||
sigma = np.dot(flatX.T, flatX) / flatX.shape[1]
|
||||
U, S, V = linalg.svd(sigma)
|
||||
self.principal_components = np.dot(np.dot(U, np.diag(1. / np.sqrt(S + fudge))), U.T)
|
||||
self.principal_components = np.dot(np.dot(U, np.diag(1. / np.sqrt(S + 10e-7))), U.T)
|
||||
|
||||
|
||||
class GraphImageDataGenerator(ImageDataGenerator):
|
||||
|
||||
@@ -4,19 +4,20 @@ import numpy as np
|
||||
import random
|
||||
from six.moves import range
|
||||
|
||||
def pad_sequences(sequences, maxlen=None, dtype='int32', padding='pre', truncating='pre', value=0.):
|
||||
"""
|
||||
Pad each sequence to the same length:
|
||||
the length of the longest sequence.
|
||||
|
||||
If maxlen is provided, any sequence longer
|
||||
than maxlen is truncated to maxlen. Truncation happens off either the beginning (default) or
|
||||
the end of the sequence.
|
||||
def pad_sequences(sequences, maxlen=None, dtype='int32',
|
||||
padding='pre', truncating='pre', value=0.):
|
||||
'''Pads each sequence to the same length:
|
||||
the length of the longest sequence.
|
||||
|
||||
Supports post-padding and pre-padding (default).
|
||||
If maxlen is provided, any sequence longer
|
||||
than maxlen is truncated to maxlen.
|
||||
Truncation happens off either the beginning (default) or
|
||||
the end of the sequence.
|
||||
|
||||
Parameters:
|
||||
-----------
|
||||
Supports post-padding and pre-padding (default).
|
||||
|
||||
# Arguments
|
||||
sequences: list of lists where each element is a sequence
|
||||
maxlen: int, maximum length
|
||||
dtype: type to cast the resulting sequence.
|
||||
@@ -25,53 +26,64 @@ def pad_sequences(sequences, maxlen=None, dtype='int32', padding='pre', truncati
|
||||
maxlen either in the beginning or in the end of the sequence
|
||||
value: float, value to pad the sequences to the desired value.
|
||||
|
||||
Returns:
|
||||
# Returns
|
||||
x: numpy array with dimensions (number_of_sequences, maxlen)
|
||||
|
||||
"""
|
||||
'''
|
||||
lengths = [len(s) for s in sequences]
|
||||
|
||||
nb_samples = len(sequences)
|
||||
if maxlen is None:
|
||||
maxlen = np.max(lengths)
|
||||
|
||||
x = (np.ones((nb_samples, maxlen)) * value).astype(dtype)
|
||||
# take the sample shape from the first non empty sequence
|
||||
# checking for consistency in the main loop below.
|
||||
sample_shape = tuple()
|
||||
for s in sequences:
|
||||
if len(s) > 0:
|
||||
sample_shape = np.asarray(s).shape[1:]
|
||||
break
|
||||
|
||||
x = (np.ones((nb_samples, maxlen) + sample_shape) * value).astype(dtype)
|
||||
for idx, s in enumerate(sequences):
|
||||
if len(s) == 0:
|
||||
continue # empty list was found
|
||||
continue # empty list was found
|
||||
if truncating == 'pre':
|
||||
trunc = s[-maxlen:]
|
||||
elif truncating == 'post':
|
||||
trunc = s[:maxlen]
|
||||
else:
|
||||
raise ValueError("Truncating type '%s' not understood" % padding)
|
||||
raise ValueError('Truncating type "%s" not understood' % truncating)
|
||||
|
||||
# check `trunc` has expected shape
|
||||
trunc = np.asarray(trunc, dtype=dtype)
|
||||
if trunc.shape[1:] != sample_shape:
|
||||
raise ValueError('Shape of sample %s of sequence at position %s is different from expected shape %s' %
|
||||
(trunc.shape[1:], idx, sample_shape))
|
||||
|
||||
if padding == 'post':
|
||||
x[idx, :len(trunc)] = trunc
|
||||
elif padding == 'pre':
|
||||
x[idx, -len(trunc):] = trunc
|
||||
else:
|
||||
raise ValueError("Padding type '%s' not understood" % padding)
|
||||
raise ValueError('Padding type "%s" not understood' % padding)
|
||||
return x
|
||||
|
||||
|
||||
def make_sampling_table(size, sampling_factor=1e-5):
|
||||
'''
|
||||
This generates an array where the ith element
|
||||
is the probability that a word of rank i would be sampled,
|
||||
according to the sampling distribution used in word2vec.
|
||||
'''This generates an array where the ith element
|
||||
is the probability that a word of rank i would be sampled,
|
||||
according to the sampling distribution used in word2vec.
|
||||
|
||||
The word2vec formula is:
|
||||
p(word) = min(1, sqrt(word.frequency/sampling_factor) / (word.frequency/sampling_factor))
|
||||
The word2vec formula is:
|
||||
p(word) = min(1, sqrt(word.frequency/sampling_factor) / (word.frequency/sampling_factor))
|
||||
|
||||
We assume that the word frequencies follow Zipf's law (s=1) to derive
|
||||
a numerical approximation of frequency(rank):
|
||||
frequency(rank) ~ 1/(rank * (log(rank) + gamma) + 1/2 - 1/(12*rank))
|
||||
We assume that the word frequencies follow Zipf's law (s=1) to derive
|
||||
a numerical approximation of frequency(rank):
|
||||
frequency(rank) ~ 1/(rank * (log(rank) + gamma) + 1/2 - 1/(12*rank))
|
||||
where gamma is the Euler-Mascheroni constant.
|
||||
|
||||
Parameters:
|
||||
-----------
|
||||
size: int, number of possible words to sample.
|
||||
# Arguments
|
||||
size: int, number of possible words to sample.
|
||||
'''
|
||||
gamma = 0.577
|
||||
rank = np.array(list(range(size)))
|
||||
@@ -85,28 +97,28 @@ def make_sampling_table(size, sampling_factor=1e-5):
|
||||
def skipgrams(sequence, vocabulary_size,
|
||||
window_size=4, negative_samples=1., shuffle=True,
|
||||
categorical=False, sampling_table=None):
|
||||
'''
|
||||
Take a sequence (list of indexes of words),
|
||||
returns couples of [word_index, other_word index] and labels (1s or 0s),
|
||||
where label = 1 if 'other_word' belongs to the context of 'word',
|
||||
and label=0 if 'other_word' is ramdomly sampled
|
||||
'''Take a sequence (list of indexes of words),
|
||||
returns couples of [word_index, other_word index] and labels (1s or 0s),
|
||||
where label = 1 if 'other_word' belongs to the context of 'word',
|
||||
and label=0 if 'other_word' is ramdomly sampled
|
||||
|
||||
Paramaters:
|
||||
-----------
|
||||
# Arguments
|
||||
vocabulary_size: int. maximum possible word index + 1
|
||||
window_size: int. actually half-window. The window of a word wi will be [i-window_size, i+window_size+1]
|
||||
negative_samples: float >= 0. 0 for no negative (=random) samples. 1 for same number as positive samples. etc.
|
||||
categorical: bool. if False, labels will be integers (eg. [0, 1, 1 .. ]),
|
||||
window_size: int. actually half-window.
|
||||
The window of a word wi will be [i-window_size, i+window_size+1]
|
||||
negative_samples: float >= 0. 0 for no negative (=random) samples.
|
||||
1 for same number as positive samples. etc.
|
||||
categorical: bool. if False, labels will be
|
||||
integers (eg. [0, 1, 1 .. ]),
|
||||
if True labels will be categorical eg. [[1,0],[0,1],[0,1] .. ]
|
||||
|
||||
Returns:
|
||||
--------
|
||||
# Returns
|
||||
couples, lables: where `couples` are int pairs and
|
||||
`labels` are either 0 or 1.
|
||||
|
||||
Notes:
|
||||
------
|
||||
By convention, index 0 in the vocabulary is a non-word and will be skipped.
|
||||
# Notes
|
||||
By convention, index 0 in the vocabulary is
|
||||
a non-word and will be skipped.
|
||||
'''
|
||||
couples = []
|
||||
labels = []
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
These preprocessing utils would greatly benefit
|
||||
from a fast Cython rewrite.
|
||||
'''These preprocessing utilities would greatly benefit
|
||||
from a fast Cython rewrite.
|
||||
'''
|
||||
from __future__ import absolute_import
|
||||
|
||||
@@ -75,8 +74,7 @@ class Tokenizer(object):
|
||||
self.char_level = char_level
|
||||
|
||||
def fit_on_texts(self, texts):
|
||||
'''
|
||||
required before using texts_to_sequences or texts_to_matrix
|
||||
'''Required before using texts_to_sequences or texts_to_matrix
|
||||
|
||||
# Arguments
|
||||
texts: can be a list of strings,
|
||||
@@ -107,9 +105,8 @@ class Tokenizer(object):
|
||||
self.index_docs[self.word_index[w]] = c
|
||||
|
||||
def fit_on_sequences(self, sequences):
|
||||
'''
|
||||
required before using sequences_to_matrix
|
||||
(if fit_on_texts was never called)
|
||||
'''Required before using sequences_to_matrix
|
||||
(if fit_on_texts was never called)
|
||||
'''
|
||||
self.document_count = len(sequences)
|
||||
self.index_docs = {}
|
||||
@@ -122,12 +119,11 @@ class Tokenizer(object):
|
||||
self.index_docs[i] += 1
|
||||
|
||||
def texts_to_sequences(self, texts):
|
||||
'''
|
||||
Transform each text in texts in a sequence of integers.
|
||||
Only top "nb_words" most frequent words will be taken into account.
|
||||
Only words known by the tokenizer will be taken into account.
|
||||
'''Transforms each text in texts in a sequence of integers.
|
||||
Only top "nb_words" most frequent words will be taken into account.
|
||||
Only words known by the tokenizer will be taken into account.
|
||||
|
||||
Returns a list of sequences.
|
||||
Returns a list of sequences.
|
||||
'''
|
||||
res = []
|
||||
for vect in self.texts_to_sequences_generator(texts):
|
||||
@@ -135,12 +131,14 @@ class Tokenizer(object):
|
||||
return res
|
||||
|
||||
def texts_to_sequences_generator(self, texts):
|
||||
'''
|
||||
Transform each text in texts in a sequence of integers.
|
||||
Only top "nb_words" most frequent words will be taken into account.
|
||||
Only words known by the tokenizer will be taken into account.
|
||||
'''Transforms each text in texts in a sequence of integers.
|
||||
Only top "nb_words" most frequent words will be taken into account.
|
||||
Only words known by the tokenizer will be taken into account.
|
||||
|
||||
Yields individual sequences.
|
||||
Yields individual sequences.
|
||||
|
||||
# Arguments:
|
||||
texts: list of strings.
|
||||
'''
|
||||
nb_words = self.nb_words
|
||||
for text in texts:
|
||||
@@ -150,56 +148,67 @@ class Tokenizer(object):
|
||||
i = self.word_index.get(w)
|
||||
if i is not None:
|
||||
if nb_words and i >= nb_words:
|
||||
pass
|
||||
continue
|
||||
else:
|
||||
vect.append(i)
|
||||
yield vect
|
||||
|
||||
def texts_to_matrix(self, texts, mode="binary"):
|
||||
'''
|
||||
modes: binary, count, tfidf, freq
|
||||
def texts_to_matrix(self, texts, mode='binary'):
|
||||
'''Convert a list of texts to a Numpy matrix,
|
||||
according to some vectorization mode.
|
||||
|
||||
# Arguments:
|
||||
texts: list of strings.
|
||||
modes: one of "binary", "count", "tfidf", "freq"
|
||||
'''
|
||||
sequences = self.texts_to_sequences(texts)
|
||||
return self.sequences_to_matrix(sequences, mode=mode)
|
||||
|
||||
def sequences_to_matrix(self, sequences, mode="binary"):
|
||||
'''
|
||||
modes: binary, count, tfidf, freq
|
||||
def sequences_to_matrix(self, sequences, mode='binary'):
|
||||
'''Converts a list of sequences into a Numpy matrix,
|
||||
according to some vectorization mode.
|
||||
|
||||
# Arguments:
|
||||
sequences: list of sequences
|
||||
(a sequence is a list of integer word indices).
|
||||
modes: one of "binary", "count", "tfidf", "freq"
|
||||
'''
|
||||
if not self.nb_words:
|
||||
if self.word_index:
|
||||
nb_words = len(self.word_index) + 1
|
||||
else:
|
||||
raise Exception("Specify a dimension (nb_words argument), or fit on some text data first.")
|
||||
raise Exception('Specify a dimension (nb_words argument), '
|
||||
'or fit on some text data first.')
|
||||
else:
|
||||
nb_words = self.nb_words
|
||||
|
||||
if mode == "tfidf" and not self.document_count:
|
||||
raise Exception("Fit the Tokenizer on some data before using tfidf mode.")
|
||||
if mode == 'tfidf' and not self.document_count:
|
||||
raise Exception('Fit the Tokenizer on some data '
|
||||
'before using tfidf mode.')
|
||||
|
||||
X = np.zeros((len(sequences), nb_words))
|
||||
for i, seq in enumerate(sequences):
|
||||
if not seq:
|
||||
pass
|
||||
continue
|
||||
counts = {}
|
||||
for j in seq:
|
||||
if j >= nb_words:
|
||||
pass
|
||||
continue
|
||||
if j not in counts:
|
||||
counts[j] = 1.
|
||||
else:
|
||||
counts[j] += 1
|
||||
for j, c in list(counts.items()):
|
||||
if mode == "count":
|
||||
if mode == 'count':
|
||||
X[i][j] = c
|
||||
elif mode == "freq":
|
||||
elif mode == 'freq':
|
||||
X[i][j] = c / len(seq)
|
||||
elif mode == "binary":
|
||||
elif mode == 'binary':
|
||||
X[i][j] = 1
|
||||
elif mode == "tfidf":
|
||||
elif mode == 'tfidf':
|
||||
tf = np.log(c / len(seq))
|
||||
df = (1 + np.log(1 + self.index_docs.get(j, 0) / (1 + self.document_count)))
|
||||
X[i][j] = tf / df
|
||||
else:
|
||||
raise Exception("Unknown vectorization mode: " + str(mode))
|
||||
raise Exception('Unknown vectorization mode: ' + str(mode))
|
||||
return X
|
||||
|
||||
+33
-19
@@ -13,46 +13,61 @@ class Regularizer(object):
|
||||
return loss
|
||||
|
||||
def get_config(self):
|
||||
return {"name": self.__class__.__name__}
|
||||
return {'name': self.__class__.__name__}
|
||||
|
||||
|
||||
class WeightRegularizer(Regularizer):
|
||||
def __init__(self, l1=0., l2=0.):
|
||||
self.l1 = l1
|
||||
self.l2 = l2
|
||||
self.l1 = K.cast_to_floatx(l1)
|
||||
self.l2 = K.cast_to_floatx(l2)
|
||||
self.uses_learning_phase = True
|
||||
|
||||
def set_param(self, p):
|
||||
self.p = p
|
||||
|
||||
def __call__(self, loss):
|
||||
loss += K.sum(K.abs(self.p)) * self.l1
|
||||
loss += K.sum(K.square(self.p)) * self.l2
|
||||
return loss
|
||||
if not hasattr(self, 'p'):
|
||||
raise Exception('Need to call `set_param` on '
|
||||
'WeightRegularizer instance '
|
||||
'before calling the instance. '
|
||||
'Check that you are not passing '
|
||||
'a WeightRegularizer instead of an '
|
||||
'ActivityRegularizer '
|
||||
'(i.e. activity_regularizer="l2" instead '
|
||||
'of activity_regularizer="activity_l2".')
|
||||
regularized_loss = loss + K.sum(K.abs(self.p)) * self.l1
|
||||
regularized_loss += K.sum(K.square(self.p)) * self.l2
|
||||
return K.in_train_phase(regularized_loss, loss)
|
||||
|
||||
def get_config(self):
|
||||
return {"name": self.__class__.__name__,
|
||||
"l1": self.l1,
|
||||
"l2": self.l2}
|
||||
return {'name': self.__class__.__name__,
|
||||
'l1': self.l1,
|
||||
'l2': self.l2}
|
||||
|
||||
|
||||
class ActivityRegularizer(Regularizer):
|
||||
def __init__(self, l1=0., l2=0.):
|
||||
self.l1 = l1
|
||||
self.l2 = l2
|
||||
self.l1 = K.cast_to_floatx(l1)
|
||||
self.l2 = K.cast_to_floatx(l2)
|
||||
self.uses_learning_phase = True
|
||||
|
||||
def set_layer(self, layer):
|
||||
self.layer = layer
|
||||
|
||||
def __call__(self, loss):
|
||||
output = self.layer.get_output(True)
|
||||
loss += self.l1 * K.sum(K.mean(K.abs(output), axis=0))
|
||||
loss += self.l2 * K.sum(K.mean(K.square(output), axis=0))
|
||||
return loss
|
||||
if not hasattr(self, 'layer'):
|
||||
raise Exception('Need to call `set_layer` on '
|
||||
'ActivityRegularizer instance '
|
||||
'before calling the instance.')
|
||||
output = self.layer.output
|
||||
regularized_loss = loss + self.l1 * K.sum(K.mean(K.abs(output), axis=0))
|
||||
regularized_loss += self.l2 * K.sum(K.mean(K.square(output), axis=0))
|
||||
return K.in_train_phase(regularized_loss, loss)
|
||||
|
||||
def get_config(self):
|
||||
return {"name": self.__class__.__name__,
|
||||
"l1": self.l1,
|
||||
"l2": self.l2}
|
||||
return {'name': self.__class__.__name__,
|
||||
'l1': self.l1,
|
||||
'l2': self.l2}
|
||||
|
||||
|
||||
def l1(l=0.01):
|
||||
@@ -78,7 +93,6 @@ def activity_l2(l=0.01):
|
||||
def activity_l1l2(l1=0.01, l2=0.01):
|
||||
return ActivityRegularizer(l1=l1, l2=l2)
|
||||
|
||||
identity = Regularizer
|
||||
|
||||
from .utils.generic_utils import get_from_module
|
||||
def get(identifier, kwargs=None):
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import print_function
|
||||
|
||||
import tarfile
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
from six.moves.urllib.request import urlopen
|
||||
from six.moves.urllib.error import URLError, HTTPError
|
||||
|
||||
from ..utils.generic_utils import Progbar
|
||||
|
||||
|
||||
# Under Python 2, 'urlretrieve' relies on FancyURLopener from legacy
|
||||
# urllib module, known to have issues with proxy management
|
||||
if sys.version_info[0] == 2:
|
||||
def urlretrieve(url, filename, reporthook=None, data=None):
|
||||
def chunk_read(response, chunk_size=8192, reporthook=None):
|
||||
total_size = response.info().get('Content-Length').strip()
|
||||
total_size = int(total_size)
|
||||
count = 0
|
||||
while 1:
|
||||
chunk = response.read(chunk_size)
|
||||
if not chunk:
|
||||
break
|
||||
count += 1
|
||||
if reporthook:
|
||||
reporthook(count, chunk_size, total_size)
|
||||
yield chunk
|
||||
|
||||
response = urlopen(url, data)
|
||||
with open(filename, 'wb') as fd:
|
||||
for chunk in chunk_read(response, reporthook=reporthook):
|
||||
fd.write(chunk)
|
||||
else:
|
||||
from six.moves.urllib.request import urlretrieve
|
||||
|
||||
|
||||
def get_file(fname, origin, untar=False):
|
||||
datadir_base = os.path.expanduser(os.path.join('~', '.keras'))
|
||||
if not os.access(datadir_base, os.W_OK):
|
||||
datadir_base = os.path.join('/tmp', '.keras')
|
||||
datadir = os.path.join(datadir_base, 'datasets')
|
||||
if not os.path.exists(datadir):
|
||||
os.makedirs(datadir)
|
||||
|
||||
if untar:
|
||||
untar_fpath = os.path.join(datadir, fname)
|
||||
fpath = untar_fpath + '.tar.gz'
|
||||
else:
|
||||
fpath = os.path.join(datadir, fname)
|
||||
|
||||
if not os.path.exists(fpath):
|
||||
print('Downloading data from', origin)
|
||||
global progbar
|
||||
progbar = None
|
||||
|
||||
def dl_progress(count, block_size, total_size):
|
||||
global progbar
|
||||
if progbar is None:
|
||||
progbar = Progbar(total_size)
|
||||
else:
|
||||
progbar.update(count*block_size)
|
||||
|
||||
error_msg = 'URL fetch failure on {}: {} -- {}'
|
||||
try:
|
||||
try:
|
||||
urlretrieve(origin, fpath, dl_progress)
|
||||
except URLError as e:
|
||||
raise Exception(error_msg.format(origin, e.errno, e.reason))
|
||||
except HTTPError as e:
|
||||
raise Exception(error_msg.format(origin, e.code, e.msg))
|
||||
except (Exception, KeyboardInterrupt) as e:
|
||||
if os.path.exists(fpath):
|
||||
os.remove(fpath)
|
||||
raise e
|
||||
progbar = None
|
||||
|
||||
if untar:
|
||||
if not os.path.exists(untar_fpath):
|
||||
print('Untaring file...')
|
||||
tfile = tarfile.open(fpath, 'r:gz')
|
||||
try:
|
||||
tfile.extractall(path=datadir)
|
||||
except (Exception, KeyboardInterrupt) as e:
|
||||
if os.path.exists(untar_fpath):
|
||||
if os.path.isfile(untar_fpath):
|
||||
os.remove(untar_fpath)
|
||||
else:
|
||||
shutil.rmtree(untar_fpath)
|
||||
raise e
|
||||
tfile.close()
|
||||
return untar_fpath
|
||||
|
||||
return fpath
|
||||
@@ -18,6 +18,14 @@ def get_from_module(identifier, module_params, module_name,
|
||||
return res(**kwargs)
|
||||
else:
|
||||
return res
|
||||
elif type(identifier) is dict:
|
||||
name = identifier.pop('name')
|
||||
res = module_params.get(name)
|
||||
if res:
|
||||
return res(**identifier)
|
||||
else:
|
||||
raise Exception('Invalid ' + str(module_name) + ': ' +
|
||||
str(identifier))
|
||||
return identifier
|
||||
|
||||
|
||||
@@ -63,15 +71,15 @@ class Progbar(object):
|
||||
numdigits = int(np.floor(np.log10(self.target))) + 1
|
||||
barstr = '%%%dd/%%%dd [' % (numdigits, numdigits)
|
||||
bar = barstr % (current, self.target)
|
||||
prog = float(current)/self.target
|
||||
prog_width = int(self.width*prog)
|
||||
prog = float(current) / self.target
|
||||
prog_width = int(self.width * prog)
|
||||
if prog_width > 0:
|
||||
bar += ('='*(prog_width-1))
|
||||
bar += ('=' * (prog_width-1))
|
||||
if current < self.target:
|
||||
bar += '>'
|
||||
else:
|
||||
bar += '='
|
||||
bar += ('.'*(self.width-prog_width))
|
||||
bar += ('.' * (self.width - prog_width))
|
||||
bar += ']'
|
||||
sys.stdout.write(bar)
|
||||
self.total_width = len(bar)
|
||||
@@ -80,7 +88,7 @@ class Progbar(object):
|
||||
time_per_unit = (now - self.start) / current
|
||||
else:
|
||||
time_per_unit = 0
|
||||
eta = time_per_unit*(self.target - current)
|
||||
eta = time_per_unit * (self.target - current)
|
||||
info = ''
|
||||
if current < self.target:
|
||||
info += ' - ETA: %ds' % eta
|
||||
@@ -99,7 +107,7 @@ class Progbar(object):
|
||||
|
||||
self.total_width += len(info)
|
||||
if prev_total_width > self.total_width:
|
||||
info += ((prev_total_width-self.total_width) * " ")
|
||||
info += ((prev_total_width - self.total_width) * " ")
|
||||
|
||||
sys.stdout.write(info)
|
||||
sys.stdout.flush()
|
||||
@@ -120,4 +128,18 @@ class Progbar(object):
|
||||
sys.stdout.write(info + "\n")
|
||||
|
||||
def add(self, n, values=[]):
|
||||
self.update(self.seen_so_far+n, values)
|
||||
self.update(self.seen_so_far + n, values)
|
||||
|
||||
|
||||
def display_table(rows, positions):
|
||||
|
||||
def display_row(objects, positions):
|
||||
line = ''
|
||||
for i in range(len(objects)):
|
||||
line += str(objects[i])
|
||||
line = line[:positions[i]]
|
||||
line += ' ' * (positions[i] - len(line))
|
||||
print(line)
|
||||
|
||||
for objects in rows:
|
||||
display_row(objects, positions)
|
||||
|
||||
+74
-135
@@ -1,158 +1,97 @@
|
||||
from __future__ import print_function
|
||||
import inspect
|
||||
import numpy as np
|
||||
import copy
|
||||
|
||||
from ..layers.advanced_activations import *
|
||||
from ..layers.core import *
|
||||
from ..layers.convolutional import *
|
||||
from ..layers.embeddings import *
|
||||
from ..layers.noise import *
|
||||
from ..layers.normalization import *
|
||||
from ..layers.recurrent import *
|
||||
from ..layers import containers
|
||||
from .. import regularizers
|
||||
from .. import constraints
|
||||
from .generic_utils import get_from_module
|
||||
from ..layers import *
|
||||
from ..models import Model, Sequential, Graph
|
||||
from .. import backend as K
|
||||
|
||||
|
||||
def container_from_config(original_layer_dict, custom_objects={}):
|
||||
layer_dict = copy.deepcopy(original_layer_dict)
|
||||
name = layer_dict.get('name')
|
||||
def layer_from_config(config, custom_objects={}):
|
||||
'''
|
||||
# Arguments
|
||||
config: dict of the form {'class_name': str, 'config': dict}
|
||||
custom_objects: dict mapping class names (or function names)
|
||||
of custom (non-Keras) objects to class/functions
|
||||
|
||||
# Returns
|
||||
Layer instance (may be Model, Sequential, Graph, Layer...)
|
||||
'''
|
||||
# Insert custom layers into globals so they can
|
||||
# be accessed by `get_from_module`.
|
||||
for cls_key in custom_objects:
|
||||
globals()[cls_key] = custom_objects[cls_key]
|
||||
|
||||
if name == 'Merge':
|
||||
mode = layer_dict.get('mode')
|
||||
concat_axis = layer_dict.get('concat_axis')
|
||||
dot_axes = layer_dict.get('dot_axes')
|
||||
layers = layer_dict.get('layers')
|
||||
layer_list = []
|
||||
for layer in layers:
|
||||
init_layer = container_from_config(layer)
|
||||
layer_list.append(init_layer)
|
||||
merge_layer = Merge(layer_list, mode, concat_axis, dot_axes)
|
||||
return merge_layer
|
||||
class_name = config['class_name']
|
||||
|
||||
elif name == 'Sequential':
|
||||
layers = layer_dict.get('layers')
|
||||
layer_list = []
|
||||
for layer in layers:
|
||||
init_layer = container_from_config(layer)
|
||||
layer_list.append(init_layer)
|
||||
seq_layer = containers.Sequential(layer_list)
|
||||
return seq_layer
|
||||
|
||||
elif name == 'Graph':
|
||||
graph_layer = containers.Graph()
|
||||
inputs = layer_dict.get('input_config')
|
||||
|
||||
for input in inputs:
|
||||
graph_layer.add_input(**input)
|
||||
|
||||
nodes = layer_dict.get('node_config')
|
||||
for node in nodes:
|
||||
layer = container_from_config(layer_dict['nodes'].get(node['name']))
|
||||
node['layer'] = layer
|
||||
graph_layer.add_node(**node)
|
||||
|
||||
outputs = layer_dict.get('output_config')
|
||||
for output in outputs:
|
||||
graph_layer.add_output(**output)
|
||||
return graph_layer
|
||||
|
||||
elif name == 'AutoEncoder':
|
||||
kwargs = {'encoder': container_from_config(layer_dict.get('encoder_config')),
|
||||
'decoder': container_from_config(layer_dict.get('decoder_config'))}
|
||||
for kwarg in ['output_reconstruction', 'weights']:
|
||||
if kwarg in layer_dict:
|
||||
kwargs[kwarg] = layer_dict[kwarg]
|
||||
return AutoEncoder(**kwargs)
|
||||
|
||||
else: # this is a non-topological layer (e.g. Dense, etc.)
|
||||
layer_dict.pop('name')
|
||||
|
||||
for k, v in layer_dict.items():
|
||||
# a dictionary argument may be a regularizer or constraint
|
||||
if isinstance(v, dict):
|
||||
vname = v.pop('name')
|
||||
if vname in [x for x, y in inspect.getmembers(constraints, predicate=inspect.isclass)]:
|
||||
layer_dict[k] = constraints.get(vname, v)
|
||||
elif vname in [x for x, y in inspect.getmembers(regularizers, predicate=inspect.isclass)]:
|
||||
layer_dict[k] = regularizers.get(vname, v)
|
||||
else:
|
||||
# not a regularizer of constraint, don't touch it
|
||||
v['name'] = vname
|
||||
|
||||
# the "name" keyword argument of layers is saved as "custom_name"
|
||||
if 'custom_name' in layer_dict:
|
||||
layer_dict['name'] = layer_dict.pop('custom_name')
|
||||
base_layer = get_layer(name, layer_dict)
|
||||
return base_layer
|
||||
if class_name == 'Sequential':
|
||||
layer_class = Sequential
|
||||
elif class_name == 'Graph':
|
||||
layer_class = Graph
|
||||
elif class_name in ['Model', 'Container']:
|
||||
layer_class = Model
|
||||
else:
|
||||
layer_class = get_from_module(class_name, globals(), 'layer',
|
||||
instantiate=False)
|
||||
return layer_class.from_config(config['config'])
|
||||
|
||||
|
||||
def model_summary(model):
|
||||
param_count = 0 # param count in the model
|
||||
def print_summary(layers, relevant_nodes=None):
|
||||
line_length = 100 # total length of printed lines
|
||||
positions = [35, 55, 67, 100] # absolute positions of log elements in each line
|
||||
# header names for the different log elements
|
||||
to_display = ['Layer (type)', 'Output Shape', 'Param #', 'Connected to']
|
||||
|
||||
def display(objects, positions):
|
||||
def print_row(fields, positions):
|
||||
line = ''
|
||||
for i in range(len(objects)):
|
||||
line += str(objects[i])
|
||||
for i in range(len(fields)):
|
||||
line += str(fields[i])
|
||||
line = line[:positions[i]]
|
||||
line += ' ' * (positions[i] - len(line))
|
||||
print(line)
|
||||
|
||||
def display_layer_info(layer, name, positions):
|
||||
layer_type = layer.__class__.__name__
|
||||
output_shape = layer.output_shape
|
||||
params = layer.count_params()
|
||||
to_display = ['%s (%s)' % (layer_type, name), output_shape, params]
|
||||
display(to_display, positions)
|
||||
print('_' * line_length)
|
||||
print_row(to_display, positions)
|
||||
print('=' * line_length)
|
||||
|
||||
line_length = 80 # total length of printed lines
|
||||
positions = [30, 60, 80] # absolute positions of log elements in each line
|
||||
# header names for the different log elements
|
||||
to_display = ['Layer (name)', 'Output Shape', 'Param #']
|
||||
def print_layer_summary(layer):
|
||||
try:
|
||||
output_shape = layer.output_shape
|
||||
except:
|
||||
output_shape = 'multiple'
|
||||
connections = []
|
||||
for node_index, node in enumerate(layer.inbound_nodes):
|
||||
if relevant_nodes:
|
||||
node_key = layer.name + '_ib-' + str(node_index)
|
||||
if node_key not in relevant_nodes:
|
||||
# node is node part of the current network
|
||||
continue
|
||||
for i in range(len(node.inbound_layers)):
|
||||
inbound_layer = node.inbound_layers[i].name
|
||||
inbound_node_index = node.node_indices[i]
|
||||
inbound_tensor_index = node.tensor_indices[i]
|
||||
connections.append(inbound_layer + '[' + str(inbound_node_index) + '][' + str(inbound_tensor_index) + ']')
|
||||
|
||||
# for sequential models, we start by printing
|
||||
# the expect input shape
|
||||
if model.__class__.__name__ == 'Sequential':
|
||||
print('-' * line_length)
|
||||
print('Initial input shape: ' + str(model.input_shape))
|
||||
name = layer.name
|
||||
cls_name = layer.__class__.__name__
|
||||
if not connections:
|
||||
first_connection = ''
|
||||
else:
|
||||
first_connection = connections[0]
|
||||
fields = [name + ' (' + cls_name + ')', output_shape, layer.count_params(), first_connection]
|
||||
print_row(fields, positions)
|
||||
if len(connections) > 1:
|
||||
for i in range(1, len(connections)):
|
||||
fields = ['', '', '', connections[i]]
|
||||
print_row(fields, positions)
|
||||
|
||||
# print header
|
||||
print('-' * line_length)
|
||||
display(to_display, positions)
|
||||
print('-' * line_length)
|
||||
total_params = 0
|
||||
for i in range(len(layers)):
|
||||
print_layer_summary(layers[i])
|
||||
if i == len(layers) - 1:
|
||||
print('=' * line_length)
|
||||
else:
|
||||
print('_' * line_length)
|
||||
total_params += layers[i].count_params()
|
||||
|
||||
if model.__class__.__name__ == 'Sequential':
|
||||
for layer in model.layers:
|
||||
name = getattr(layer, 'name', 'Unnamed')
|
||||
display_layer_info(layer, name, positions)
|
||||
param_count += layer.count_params()
|
||||
|
||||
elif model.__class__.__name__ == 'Graph':
|
||||
for name in model.input_order:
|
||||
layer = model.inputs[name]
|
||||
display_layer_info(layer, name, positions)
|
||||
|
||||
for name in model.nodes:
|
||||
layer = model.nodes[name]
|
||||
display_layer_info(layer, name, positions)
|
||||
param_count += layer.count_params()
|
||||
|
||||
for name in model.output_order:
|
||||
layer = model.outputs[name]
|
||||
display_layer_info(layer, name, positions)
|
||||
|
||||
print('-' * line_length)
|
||||
print('Total params: %s' % param_count)
|
||||
print('-' * line_length)
|
||||
|
||||
|
||||
from .generic_utils import get_from_module
|
||||
def get_layer(identifier, kwargs=None):
|
||||
return get_from_module(identifier, globals(), 'layer',
|
||||
instantiate=True, kwargs=kwargs)
|
||||
print('Total params: %s' % total_params)
|
||||
print('_' * line_length)
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
import numpy as np
|
||||
from numpy.testing import assert_allclose
|
||||
import inspect
|
||||
|
||||
from ..engine import Model, Input
|
||||
from ..models import Sequential, model_from_json
|
||||
from .. import backend as K
|
||||
|
||||
|
||||
def get_test_data(nb_train=1000, nb_test=500, input_shape=(10,), output_shape=(2,),
|
||||
def get_test_data(nb_train=1000, nb_test=500, input_shape=(10,),
|
||||
output_shape=(2,),
|
||||
classification=True, nb_class=2):
|
||||
'''
|
||||
classification=True overrides output_shape
|
||||
@@ -25,3 +32,73 @@ def get_test_data(nb_train=1000, nb_test=500, input_shape=(10,), output_shape=(2
|
||||
y[i] = np.random.normal(loc=y_loc[i], scale=0.7, size=output_shape)
|
||||
|
||||
return (X[:nb_train], y[:nb_train]), (X[nb_train:], y[nb_train:])
|
||||
|
||||
|
||||
def layer_test(layer_cls, kwargs={}, input_shape=None, input_dtype=None,
|
||||
input_data=None, expected_output=None, expected_output_dtype=None):
|
||||
'''Test routine for a layer with a single input tensor
|
||||
and single output tensor.
|
||||
'''
|
||||
if input_data is None:
|
||||
assert input_shape
|
||||
if not input_dtype:
|
||||
input_dtype = K.floatx()
|
||||
input_data = (10 * np.random.random(input_shape)).astype(input_dtype)
|
||||
elif input_shape is None:
|
||||
input_shape = input_data.shape
|
||||
|
||||
if expected_output_dtype is None:
|
||||
expected_output_dtype = input_dtype
|
||||
|
||||
# instantiation
|
||||
layer = layer_cls(**kwargs)
|
||||
|
||||
# test get_weights , set_weights
|
||||
weights = layer.get_weights()
|
||||
layer.set_weights(weights)
|
||||
|
||||
# test and instantiation from weights
|
||||
if 'weights' in inspect.getargspec(layer_cls.__init__):
|
||||
kwargs['weights'] = weights
|
||||
layer = layer_cls(**kwargs)
|
||||
|
||||
# test in functional API
|
||||
x = Input(shape=input_shape[1:], dtype=input_dtype)
|
||||
y = layer(x)
|
||||
assert K.dtype(y) == expected_output_dtype
|
||||
|
||||
model = Model(input=x, output=y)
|
||||
model.compile('rmsprop', 'mse')
|
||||
|
||||
expected_output_shape = layer.get_output_shape_for(input_shape)
|
||||
actual_output = model.predict(input_data)
|
||||
actual_output_shape = actual_output.shape
|
||||
assert expected_output_shape == actual_output_shape
|
||||
if expected_output is not None:
|
||||
assert_allclose(actual_output, expected_output, rtol=1e-3)
|
||||
|
||||
# test serialization
|
||||
model_config = model.get_config()
|
||||
model = Model.from_config(model_config)
|
||||
model.compile('rmsprop', 'mse')
|
||||
|
||||
# test as first layer in Sequential API
|
||||
layer_config = layer.get_config()
|
||||
layer_config['batch_input_shape'] = input_shape
|
||||
layer = layer.__class__.from_config(layer_config)
|
||||
|
||||
model = Sequential()
|
||||
model.add(layer)
|
||||
model.compile('rmsprop', 'mse')
|
||||
actual_output = model.predict(input_data)
|
||||
actual_output_shape = actual_output.shape
|
||||
assert expected_output_shape == actual_output_shape
|
||||
if expected_output is not None:
|
||||
assert_allclose(actual_output, expected_output, rtol=1e-3)
|
||||
|
||||
# test JSON serialization
|
||||
json_model = model.to_json()
|
||||
model = model_from_json(json_model)
|
||||
|
||||
# for further checks in the caller function
|
||||
return actual_output
|
||||
|
||||
+44
-137
@@ -1,7 +1,3 @@
|
||||
import itertools
|
||||
from keras.layers.containers import Graph, Sequential
|
||||
from keras.layers.core import Merge
|
||||
|
||||
try:
|
||||
# pydot-ng is a fork of pydot that is better maintained
|
||||
import pydot_ng as pydot
|
||||
@@ -9,146 +5,57 @@ except ImportError:
|
||||
# fall back on pydot if necessary
|
||||
import pydot
|
||||
if not pydot.find_graphviz():
|
||||
raise RuntimeError("Failed to import pydot. You must install pydot"
|
||||
" and graphviz for `pydotprint` to work.")
|
||||
raise RuntimeError('Failed to import pydot. You must install pydot'
|
||||
' and graphviz for `pydotprint` to work.')
|
||||
|
||||
|
||||
def layer_typename(layer):
|
||||
return type(layer).__module__ + "." + type(layer).__name__
|
||||
def model_to_dot(model, show_shapes=False):
|
||||
dot = pydot.Dot()
|
||||
dot.set('rankdir', 'TB')
|
||||
dot.set('concentrate', True)
|
||||
dot.set_node_defaults(shape='record')
|
||||
|
||||
if model.__class__.__name__ == 'Sequential':
|
||||
if not model.built:
|
||||
model.build()
|
||||
model = model.model
|
||||
layers = model.layers
|
||||
|
||||
def get_layer_to_name(model):
|
||||
"""Returns a dict mapping layer to their name in the model"""
|
||||
if not isinstance(model, Graph):
|
||||
return {}
|
||||
else:
|
||||
node_to_name = itertools.chain(
|
||||
model.nodes.items(), model.inputs.items(), model.outputs.items()
|
||||
)
|
||||
return {v: k for k, v in node_to_name}
|
||||
# first, populate the nodes of the graph
|
||||
for layer in layers:
|
||||
layer_id = str(id(layer))
|
||||
label = str(layer.name) + ' (' + layer.__class__.__name__ + ')'
|
||||
|
||||
|
||||
class ModelToDot(object):
|
||||
"""
|
||||
This is a helper class which visits a keras model (Sequential or Graph) and
|
||||
returns a pydot.Graph representation.
|
||||
|
||||
This is implemented as a class because we need to maintain various states.
|
||||
|
||||
Use it as ```ModelToDot()(model)```
|
||||
|
||||
Keras models can have an arbitrary number of inputs and outputs. A given
|
||||
layer can have multiple inputs but has a single output. We therefore
|
||||
explore the model by starting at its output and crawling "up" the tree.
|
||||
"""
|
||||
def _pydot_node_for_layer(self, layer, label):
|
||||
"""
|
||||
Returns the pydot.Node corresponding to the given layer.
|
||||
`label` specify the name of the layer (only used if the layer isn't yet
|
||||
associated with a pydot.Node)
|
||||
"""
|
||||
# Check if this already exists (will be the case for nodes that
|
||||
# serve as input to more than one layer)
|
||||
if layer in self.layer_to_pydotnode:
|
||||
node = self.layer_to_pydotnode[layer]
|
||||
else:
|
||||
layer_id = 'layer%d' % self.idgen
|
||||
self.idgen += 1
|
||||
|
||||
label = label + " (" + layer_typename(layer) + ")"
|
||||
|
||||
if self.show_shape:
|
||||
# Build the label that will actually contain a table with the
|
||||
# input/output
|
||||
outputlabels = str(layer.output_shape)
|
||||
if hasattr(layer, 'input_shape'):
|
||||
inputlabels = str(layer.input_shape)
|
||||
elif hasattr(layer, 'input_shapes'):
|
||||
inputlabels = ', '.join(
|
||||
[str(ishape) for ishape in layer.input_shapes])
|
||||
else:
|
||||
inputlabels = ''
|
||||
label = "%s\n|{input:|output:}|{{%s}|{%s}}" % (
|
||||
label, inputlabels, outputlabels)
|
||||
|
||||
node = pydot.Node(layer_id, label=label)
|
||||
self.g.add_node(node)
|
||||
self.layer_to_pydotnode[layer] = node
|
||||
return node
|
||||
|
||||
def _process_layer(self, layer, layer_to_name=None, connect_to=None):
|
||||
"""
|
||||
Process a layer, adding its node to the graph and creating edges to its
|
||||
outputs.
|
||||
|
||||
`connect_to` specify where the output of the current layer will be
|
||||
connected
|
||||
`layer_to_name` is a dict mapping layer to their name in the Graph
|
||||
model. Should be {} when processing a Sequential model
|
||||
"""
|
||||
# The layer can be a container layer, in which case we can recurse
|
||||
is_graph = isinstance(layer, Graph)
|
||||
is_seq = isinstance(layer, Sequential)
|
||||
if self.recursive and (is_graph or is_seq):
|
||||
# We got a container layer, recursively transform it
|
||||
if is_graph:
|
||||
child_layers = layer.outputs.values()
|
||||
if show_shapes:
|
||||
# Build the label that will actually contain a table with the
|
||||
# input/output
|
||||
outputlabels = str(layer.output_shape)
|
||||
if hasattr(layer, 'input_shape'):
|
||||
inputlabels = str(layer.input_shape)
|
||||
elif hasattr(layer, 'input_shapes'):
|
||||
inputlabels = ', '.join(
|
||||
[str(ishape) for ishape in layer.input_shapes])
|
||||
else:
|
||||
child_layers = [layer.layers[-1]]
|
||||
for l in child_layers:
|
||||
self._process_layer(l, layer_to_name=get_layer_to_name(layer),
|
||||
connect_to=connect_to)
|
||||
else:
|
||||
# This is a simple layer.
|
||||
label = layer_to_name.get(layer, '')
|
||||
layer_node = self._pydot_node_for_layer(layer, label=label)
|
||||
inputlabels = ''
|
||||
label = '%s\n|{input:|output:}|{{%s}|{%s}}' % (label, inputlabels, outputlabels)
|
||||
|
||||
if connect_to is not None:
|
||||
self.g.add_edge(pydot.Edge(layer_node, connect_to))
|
||||
node = pydot.Node(layer_id, label=label)
|
||||
dot.add_node(node)
|
||||
|
||||
# Proceed upwards to the parent(s). Only Merge layers have more
|
||||
# than one parent
|
||||
if isinstance(layer, Merge): # Merge layer
|
||||
for l in layer.layers:
|
||||
self._process_layer(l, layer_to_name,
|
||||
connect_to=layer_node)
|
||||
elif hasattr(layer, 'previous') and layer.previous is not None:
|
||||
self._process_layer(layer.previous, layer_to_name,
|
||||
connect_to=layer_node)
|
||||
|
||||
def __call__(self, model, recursive=True, show_shape=False,
|
||||
connect_to=None):
|
||||
self.idgen = 0
|
||||
# Maps keras layer to the pydot.Node representing them
|
||||
self.layer_to_pydotnode = {}
|
||||
self.recursive = recursive
|
||||
self.show_shape = show_shape
|
||||
|
||||
self.g = pydot.Dot()
|
||||
self.g.set('rankdir', 'TB')
|
||||
self.g.set('concentrate', True)
|
||||
self.g.set_node_defaults(shape='record')
|
||||
|
||||
if hasattr(model, 'outputs'):
|
||||
# Graph
|
||||
for name, l in model.outputs.items():
|
||||
self._process_layer(l, get_layer_to_name(model),
|
||||
connect_to=connect_to)
|
||||
else:
|
||||
# Sequential container
|
||||
self._process_layer(model.layers[-1], {}, connect_to=connect_to)
|
||||
|
||||
return self.g
|
||||
# second, add the edges
|
||||
for layer in layers:
|
||||
layer_id = str(id(layer))
|
||||
for i, node in enumerate(layer.inbound_nodes):
|
||||
node_key = layer.name + '_ib-' + str(i)
|
||||
if node_key in model.container_nodes:
|
||||
# add edges
|
||||
for inbound_layer in node.inbound_layers:
|
||||
inbound_layer_id = str(id(inbound_layer))
|
||||
layer_id = str(id(layer))
|
||||
dot.add_edge(pydot.Edge(inbound_layer_id, layer_id))
|
||||
return dot
|
||||
|
||||
|
||||
def to_graph(model, **kwargs):
|
||||
"""
|
||||
`recursive` controls whether we recursively explore container layers
|
||||
`show_shape` controls whether the shape is shown in the graph
|
||||
"""
|
||||
return ModelToDot()(model, **kwargs)
|
||||
|
||||
|
||||
def plot(model, to_file='model.png'):
|
||||
graph = to_graph(model)
|
||||
graph.write_png(to_file)
|
||||
def plot(model, to_file='model.png', show_shapes=False):
|
||||
dot = model_to_dot(model, show_shapes)
|
||||
dot.write_png(to_file)
|
||||
|
||||
+224
-212
@@ -1,266 +1,278 @@
|
||||
from __future__ import absolute_import
|
||||
import abc
|
||||
import copy
|
||||
import inspect
|
||||
import types
|
||||
import numpy as np
|
||||
|
||||
from ..utils.np_utils import to_categorical
|
||||
from ..models import Sequential
|
||||
|
||||
|
||||
class BaseWrapper(object):
|
||||
"""
|
||||
Base class for the Keras scikit-learn wrapper.
|
||||
'''Base class for the Keras scikit-learn wrapper.
|
||||
|
||||
Warning: This class should not be used directly. Use derived classes instead.
|
||||
Warning: This class should not be used directly.
|
||||
Use descendant classes instead.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
train_batch_size : int, optional
|
||||
Number of training samples evaluated at a time.
|
||||
test_batch_size : int, optional
|
||||
Number of test samples evaluated at a time.
|
||||
nb_epochs : int, optional
|
||||
Number of training epochs.
|
||||
shuffle : boolean, optional
|
||||
Whether to shuffle the samples at each epoch.
|
||||
show_accuracy : boolean, optional
|
||||
Whether to display class accuracy in the logs at each epoch.
|
||||
validation_split : float [0, 1], optional
|
||||
Fraction of the data to use as held-out validation data.
|
||||
validation_data : tuple (X, y), optional
|
||||
Data to be used as held-out validation data. Will override validation_split.
|
||||
callbacks : list, optional
|
||||
List of callbacks to apply during training.
|
||||
verbose : int, optional
|
||||
Verbosity level.
|
||||
"""
|
||||
__metaclass__ = abc.ABCMeta
|
||||
# Arguments
|
||||
build_fn: callable function or class instance
|
||||
sk_params: model parameters & fitting parameters
|
||||
|
||||
@abc.abstractmethod
|
||||
def __init__(self, model, optimizer, loss,
|
||||
train_batch_size=128, test_batch_size=128,
|
||||
nb_epoch=100, shuffle=True, show_accuracy=False,
|
||||
validation_split=0, validation_data=None, callbacks=None,
|
||||
verbose=0,):
|
||||
self.model = model
|
||||
self.optimizer = optimizer
|
||||
self.loss = loss
|
||||
self.compiled_model_ = None
|
||||
self.classes_ = []
|
||||
self.config_ = []
|
||||
self.weights_ = []
|
||||
The build_fn should construct, compile and return a Keras model, which
|
||||
will then be used to fit/predict. One of the following
|
||||
three values could be passed to build_fn:
|
||||
1. A function
|
||||
2. An instance of a class that implements the __call__ method
|
||||
3. None. This means you implement a class that inherits from either
|
||||
`KerasClassifier` or `KerasRegressor`. The __call__ method of the
|
||||
present class will then be treated as the default build_fn.
|
||||
|
||||
self.train_batch_size = train_batch_size
|
||||
self.test_batch_size = test_batch_size
|
||||
self.nb_epoch = nb_epoch
|
||||
self.shuffle = shuffle
|
||||
self.show_accuracy = show_accuracy
|
||||
self.validation_split = validation_split
|
||||
self.validation_data = validation_data
|
||||
self.callbacks = [] if callbacks is None else callbacks
|
||||
`sk_params` takes both model parameters and fitting parameters. Legal model
|
||||
parameters are the arguments of `build_fn`. Note that like all other
|
||||
estimators in scikit-learn, 'build_fn' should provide defalult values for
|
||||
its arguments, so that you could create the estimator without passing any
|
||||
values to `sk_params`.
|
||||
|
||||
self.verbose = verbose
|
||||
`sk_params` could also accept parameters for calling `fit`, `predict`,
|
||||
`predict_proba`, and `score` methods (e.g., `nb_epoch`, `batch_size`).
|
||||
fitting (predicting) parameters are selected in the following order:
|
||||
|
||||
1. Values passed to the dictionary arguments of
|
||||
`fit`, `predict`, `predict_proba`, and `score` methods
|
||||
2. Values passed to `sk_params`
|
||||
3. The default values of the `keras.models.Sequential`
|
||||
`fit`, `predict`, `predict_proba` and `score` methods
|
||||
|
||||
When using scikit-learn's `grid_search` API, legal tunable parameters are
|
||||
those you could pass to `sk_params`, including fitting parameters.
|
||||
In other words, you could use `grid_search` to search for the best
|
||||
`batch_size` or `nb_epoch` as well as the model parameters.
|
||||
'''
|
||||
|
||||
def __init__(self, build_fn=None, **sk_params):
|
||||
self.build_fn = build_fn
|
||||
self.sk_params = sk_params
|
||||
self.check_params(sk_params)
|
||||
|
||||
def check_params(self, params):
|
||||
'''Check for user typos in "params" keys to avoid
|
||||
unwanted usage of default values
|
||||
|
||||
# Arguments
|
||||
params: dictionary
|
||||
The parameters to be checked
|
||||
'''
|
||||
legal_params_fns = [Sequential.fit, Sequential.predict,
|
||||
Sequential.predict_classes, Sequential.evaluate]
|
||||
if self.build_fn is None:
|
||||
legal_params_fns.append(self.__call__)
|
||||
elif not isinstance(self.build_fn, types.FunctionType):
|
||||
legal_params_fns.append(self.build_fn.__call__)
|
||||
else:
|
||||
legal_params_fns.append(self.build_fn)
|
||||
|
||||
legal_params = []
|
||||
for fn in legal_params_fns:
|
||||
legal_params += inspect.getargspec(fn)[0]
|
||||
legal_params = set(legal_params)
|
||||
|
||||
for params_name in params:
|
||||
if params_name not in legal_params:
|
||||
assert False, '{} is not a legal parameter'.format(params_name)
|
||||
|
||||
def get_params(self, deep=True):
|
||||
"""
|
||||
Get parameters for this estimator.
|
||||
'''Get parameters for this estimator.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
deep: boolean, optional
|
||||
If True, will return the parameters for this estimator and
|
||||
contained subobjects that are estimators.
|
||||
# Arguments
|
||||
deep: boolean, optional
|
||||
If True, will return the parameters for this estimator and
|
||||
contained sub-objects that are estimators.
|
||||
|
||||
Returns
|
||||
-------
|
||||
params : dict
|
||||
Dictionary of parameter names mapped to their values.
|
||||
"""
|
||||
return {'model': self.model, 'optimizer': self.optimizer, 'loss': self.loss}
|
||||
# Returns
|
||||
params : dict
|
||||
Dictionary of parameter names mapped to their values.
|
||||
'''
|
||||
res = copy.deepcopy(self.sk_params)
|
||||
res.update({'build_fn': self.build_fn})
|
||||
return res
|
||||
|
||||
def set_params(self, **params):
|
||||
"""
|
||||
Set the parameters of this estimator.
|
||||
'''Set the parameters of this estimator.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
# Arguments
|
||||
params: dict
|
||||
Dictionary of parameter names mapped to their values.
|
||||
|
||||
Returns
|
||||
-------
|
||||
self
|
||||
"""
|
||||
for parameter, value in params.items():
|
||||
setattr(self, parameter, value)
|
||||
# Returns
|
||||
self
|
||||
'''
|
||||
self.check_params(params)
|
||||
self.sk_params.update(params)
|
||||
return self
|
||||
|
||||
def fit(self, X, y):
|
||||
"""
|
||||
Fit the model according to the given training data.
|
||||
def fit(self, X, y, **kwargs):
|
||||
'''Construct a new model with build_fn and fit the model according
|
||||
to the given training data.
|
||||
|
||||
Makes a copy of the un-compiled model definition to use for
|
||||
compilation and fitting, leaving the original definition
|
||||
intact.
|
||||
# Arguments
|
||||
X : array-like, shape `(n_samples, n_features)`
|
||||
Training samples where n_samples in the number of samples
|
||||
and n_features is the number of features.
|
||||
y : array-like, shape `(n_samples,)` or `(n_samples, n_outputs)`
|
||||
True labels for X.
|
||||
kwargs: dictionary arguments
|
||||
Legal arguments are the arguments of `Sequential.fit`
|
||||
|
||||
Parameters
|
||||
----------
|
||||
X : array-like, shape = (n_samples, n_features)
|
||||
Training samples where n_samples in the number of samples
|
||||
and n_features is the number of features.
|
||||
y : array-like, shape = (n_samples) or (n_samples, n_outputs)
|
||||
True labels for X.
|
||||
# Returns
|
||||
history : object
|
||||
details about the training history at each epoch.
|
||||
'''
|
||||
|
||||
Returns
|
||||
-------
|
||||
history : object
|
||||
Returns details about the training history at each epoch.
|
||||
"""
|
||||
if len(y.shape) == 1:
|
||||
self.classes_ = list(np.unique(y))
|
||||
if self.loss == 'categorical_crossentropy':
|
||||
y = to_categorical(y)
|
||||
if self.build_fn is None:
|
||||
self.model = self.__call__(**self.filter_sk_params(self.__call__))
|
||||
elif not isinstance(self.build_fn, types.FunctionType):
|
||||
self.model = self.build_fn(
|
||||
**self.filter_sk_params(self.build_fn.__call__))
|
||||
else:
|
||||
self.classes_ = np.arange(0, y.shape[1])
|
||||
self.model = self.build_fn(**self.filter_sk_params(self.build_fn))
|
||||
|
||||
self.compiled_model_ = copy.deepcopy(self.model)
|
||||
self.compiled_model_.compile(optimizer=self.optimizer, loss=self.loss)
|
||||
history = self.compiled_model_.fit(
|
||||
X, y, batch_size=self.train_batch_size, nb_epoch=self.nb_epoch, verbose=self.verbose,
|
||||
shuffle=self.shuffle, show_accuracy=self.show_accuracy,
|
||||
validation_split=self.validation_split, validation_data=self.validation_data,
|
||||
callbacks=self.callbacks)
|
||||
loss_name = self.model.loss
|
||||
if hasattr(loss_name, '__name__'):
|
||||
loss_name = loss_name.__name__
|
||||
if loss_name == 'categorical_crossentropy' and len(y.shape) != 2:
|
||||
y = to_categorical(y)
|
||||
|
||||
self.config_ = self.model.get_config()
|
||||
self.weights_ = self.model.get_weights()
|
||||
fit_args = copy.deepcopy(self.filter_sk_params(Sequential.fit))
|
||||
fit_args.update(kwargs)
|
||||
|
||||
history = self.model.fit(X, y, **fit_args)
|
||||
|
||||
return history
|
||||
|
||||
def filter_sk_params(self, fn, override={}):
|
||||
'''Filter sk_params and return those in fn's arguments
|
||||
|
||||
# Arguments
|
||||
fn : arbitrary function
|
||||
override: dictionary, values to overrid sk_params
|
||||
|
||||
# Returns
|
||||
res : dictionary dictionary containing variabls
|
||||
in both sk_params and fn's arguments.
|
||||
'''
|
||||
res = {}
|
||||
fn_args = inspect.getargspec(fn)[0]
|
||||
for name, value in self.sk_params.items():
|
||||
if name in fn_args:
|
||||
res.update({name: value})
|
||||
res.update(override)
|
||||
return res
|
||||
|
||||
|
||||
class KerasClassifier(BaseWrapper):
|
||||
"""
|
||||
Implementation of the scikit-learn classifier API for Keras.
|
||||
'''Implementation of the scikit-learn classifier API for Keras.
|
||||
'''
|
||||
|
||||
Parameters
|
||||
----------
|
||||
model : object
|
||||
An un-compiled Keras model object is required to use the scikit-learn wrapper.
|
||||
optimizer : string
|
||||
Optimization method used by the model during compilation/training.
|
||||
loss : string
|
||||
Loss function used by the model during compilation/training.
|
||||
"""
|
||||
def __init__(self, model, optimizer='adam', loss='categorical_crossentropy', **kwargs):
|
||||
super(KerasClassifier, self).__init__(model, optimizer, loss, **kwargs)
|
||||
def predict(self, X, **kwargs):
|
||||
'''Returns the class predictions for the given test data.
|
||||
|
||||
def predict(self, X):
|
||||
"""
|
||||
Returns the class predictions for the given test data.
|
||||
# Arguments
|
||||
X: array-like, shape `(n_samples, n_features)`
|
||||
Test samples where n_samples in the number of samples
|
||||
and n_features is the number of features.
|
||||
kwargs: dictionary arguments
|
||||
Legal arguments are the arguments of `Sequential.predict_classes`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
X : array-like, shape = (n_samples, n_features)
|
||||
Test samples where n_samples in the number of samples
|
||||
and n_features is the number of features.
|
||||
# Returns
|
||||
preds: array-like, shape `(n_samples,)`
|
||||
Class predictions.
|
||||
'''
|
||||
kwargs = self.filter_sk_params(Sequential.predict_classes, kwargs)
|
||||
return self.model.predict_classes(X, **kwargs)
|
||||
|
||||
Returns
|
||||
-------
|
||||
preds : array-like, shape = (n_samples)
|
||||
Class predictions.
|
||||
"""
|
||||
return self.compiled_model_.predict_classes(
|
||||
X, batch_size=self.test_batch_size, verbose=self.verbose)
|
||||
def predict_proba(self, X, **kwargs):
|
||||
'''Returns class probability estimates for the given test data.
|
||||
|
||||
def predict_proba(self, X):
|
||||
"""
|
||||
Returns class probability estimates for the given test data.
|
||||
# Arguments
|
||||
X: array-like, shape `(n_samples, n_features)`
|
||||
Test samples where n_samples in the number of samples
|
||||
and n_features is the number of features.
|
||||
kwargs: dictionary arguments
|
||||
Legal arguments are the arguments of `Sequential.predict_classes`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
X : array-like, shape = (n_samples, n_features)
|
||||
Test samples where n_samples in the number of samples
|
||||
and n_features is the number of features.
|
||||
# Returns
|
||||
proba: array-like, shape `(n_samples, n_outputs)`
|
||||
Class probability estimates.
|
||||
'''
|
||||
kwargs = self.filter_sk_params(Sequential.predict_proba, kwargs)
|
||||
return self.model.predict_proba(X, **kwargs)
|
||||
|
||||
Returns
|
||||
-------
|
||||
proba : array-like, shape = (n_samples, n_outputs)
|
||||
Class probability estimates.
|
||||
"""
|
||||
return self.compiled_model_.predict_proba(
|
||||
X, batch_size=self.test_batch_size, verbose=self.verbose)
|
||||
def score(self, X, y, **kwargs):
|
||||
'''Returns the mean accuracy on the given test data and labels.
|
||||
|
||||
def score(self, X, y):
|
||||
"""
|
||||
Returns the mean accuracy on the given test data and labels.
|
||||
# Arguments
|
||||
X: array-like, shape `(n_samples, n_features)`
|
||||
Test samples where n_samples in the number of samples
|
||||
and n_features is the number of features.
|
||||
y: array-like, shape `(n_samples,)` or `(n_samples, n_outputs)`
|
||||
True labels for X.
|
||||
kwargs: dictionary arguments
|
||||
Legal arguments are the arguments of `Sequential.evaluate`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
X : array-like, shape = (n_samples, n_features)
|
||||
Test samples where n_samples in the number of samples
|
||||
and n_features is the number of features.
|
||||
y : array-like, shape = (n_samples) or (n_samples, n_outputs)
|
||||
True labels for X.
|
||||
|
||||
Returns
|
||||
-------
|
||||
score : float
|
||||
Mean accuracy of predictions on X wrt. y.
|
||||
"""
|
||||
loss, accuracy = self.compiled_model_.evaluate(
|
||||
X, y, batch_size=self.test_batch_size, show_accuracy=True, verbose=self.verbose)
|
||||
return accuracy
|
||||
# Returns
|
||||
score: float
|
||||
Mean accuracy of predictions on X wrt. y.
|
||||
'''
|
||||
kwargs = self.filter_sk_params(Sequential.evaluate, kwargs)
|
||||
outputs = self.model.evaluate(X, y, **kwargs)
|
||||
if type(outputs) is not list:
|
||||
outputs = [outputs]
|
||||
for name, output in zip(self.model.metrics_names, outputs):
|
||||
if name == 'acc':
|
||||
return output
|
||||
raise Exception('The model is not configured to compute accuracy. '
|
||||
'You should pass `metrics=["accuracy"]` to '
|
||||
'the `model.compile()` method.')
|
||||
|
||||
|
||||
class KerasRegressor(BaseWrapper):
|
||||
"""
|
||||
Implementation of the scikit-learn regressor API for Keras.
|
||||
'''Implementation of the scikit-learn regressor API for Keras.
|
||||
'''
|
||||
|
||||
Parameters
|
||||
----------
|
||||
model : object
|
||||
An un-compiled Keras model object is required to use the scikit-learn wrapper.
|
||||
optimizer : string
|
||||
Optimization method used by the model during compilation/training.
|
||||
loss : string
|
||||
Loss function used by the model during compilation/training.
|
||||
"""
|
||||
def __init__(self, model, optimizer='adam', loss='mean_squared_error', **kwargs):
|
||||
super(KerasRegressor, self).__init__(model, optimizer, loss, **kwargs)
|
||||
def predict(self, X, **kwargs):
|
||||
'''Returns predictions for the given test data.
|
||||
|
||||
def predict(self, X):
|
||||
"""
|
||||
Returns predictions for the given test data.
|
||||
# Arguments
|
||||
X: array-like, shape `(n_samples, n_features)`
|
||||
Test samples where n_samples in the number of samples
|
||||
and n_features is the number of features.
|
||||
kwargs: dictionary arguments
|
||||
Legal arguments are the arguments of `Sequential.predict`.
|
||||
# Returns
|
||||
preds: array-like, shape `(n_samples,)`
|
||||
Predictions.
|
||||
'''
|
||||
kwargs = self.filter_sk_params(Sequential.predict, kwargs)
|
||||
return self.model.predict(X, **kwargs)
|
||||
|
||||
Parameters
|
||||
----------
|
||||
X : array-like, shape = (n_samples, n_features)
|
||||
Test samples where n_samples in the number of samples
|
||||
and n_features is the number of features.
|
||||
def score(self, X, y, **kwargs):
|
||||
'''Returns the mean loss on the given test data and labels.
|
||||
|
||||
Returns
|
||||
-------
|
||||
preds : array-like, shape = (n_samples)
|
||||
Predictions.
|
||||
"""
|
||||
return self.compiled_model_.predict(
|
||||
X, batch_size=self.test_batch_size, verbose=self.verbose).ravel()
|
||||
# Arguments
|
||||
X: array-like, shape `(n_samples, n_features)`
|
||||
Test samples where n_samples in the number of samples
|
||||
and n_features is the number of features.
|
||||
y: array-like, shape `(n_samples,)`
|
||||
True labels for X.
|
||||
kwargs: dictionary arguments
|
||||
Legal arguments are the arguments of `Sequential.evaluate`.
|
||||
|
||||
def score(self, X, y):
|
||||
"""
|
||||
Returns the mean accuracy on the given test data and labels.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
X : array-like, shape = (n_samples, n_features)
|
||||
Test samples where n_samples in the number of samples
|
||||
and n_features is the number of features.
|
||||
y : array-like, shape = (n_samples)
|
||||
True labels for X.
|
||||
|
||||
Returns
|
||||
-------
|
||||
score : float
|
||||
Loss from predictions on X wrt. y.
|
||||
"""
|
||||
loss = self.compiled_model_.evaluate(
|
||||
X, y, batch_size=self.test_batch_size, show_accuracy=False, verbose=self.verbose)
|
||||
# Returns
|
||||
score: float
|
||||
Mean accuracy of predictions on X wrt. y.
|
||||
'''
|
||||
kwargs = self.filter_sk_params(Sequential.evaluate, kwargs)
|
||||
loss = self.model.evaluate(X, y, **kwargs)
|
||||
if type(loss) is list:
|
||||
return loss[0]
|
||||
return loss
|
||||
|
||||
+3
-3
@@ -3,12 +3,12 @@ from setuptools import find_packages
|
||||
|
||||
|
||||
setup(name='Keras',
|
||||
version='0.3.1',
|
||||
description='Theano-based Deep Learning library',
|
||||
version='1.0.0',
|
||||
description='Deep Learning for Python',
|
||||
author='Francois Chollet',
|
||||
author_email='francois.chollet@gmail.com',
|
||||
url='https://github.com/fchollet/keras',
|
||||
download_url='https://github.com/fchollet/keras/tarball/0.3.1',
|
||||
download_url='https://github.com/fchollet/keras/tarball/1.0.0',
|
||||
license='MIT',
|
||||
install_requires=['theano', 'pyyaml', 'six'],
|
||||
extras_require={
|
||||
|
||||
@@ -35,10 +35,12 @@ def test_image_classification():
|
||||
Activation('relu'),
|
||||
Dense(y_test.shape[-1], activation='softmax')
|
||||
])
|
||||
model.compile(loss='categorical_crossentropy', optimizer='sgd')
|
||||
model.compile(loss='categorical_crossentropy',
|
||||
optimizer='rmsprop',
|
||||
metrics=['accuracy'])
|
||||
history = model.fit(X_train, y_train, nb_epoch=10, batch_size=16,
|
||||
validation_data=(X_test, y_test),
|
||||
show_accuracy=True, verbose=0)
|
||||
verbose=0)
|
||||
assert(history.history['val_acc'][-1] > 0.85)
|
||||
|
||||
|
||||
|
||||
@@ -4,17 +4,22 @@ import pytest
|
||||
import string
|
||||
|
||||
from keras.utils.test_utils import get_test_data
|
||||
from keras.models import Sequential
|
||||
from keras.layers.core import TimeDistributedDense, Dropout, Dense, Activation
|
||||
from keras.layers.recurrent import GRU, LSTM
|
||||
from keras.layers.embeddings import Embedding
|
||||
from keras.utils.np_utils import to_categorical
|
||||
from keras.models import Sequential
|
||||
from keras.layers import TimeDistributedDense
|
||||
from keras.layers import Dense
|
||||
from keras.layers import Activation
|
||||
from keras.layers import GRU
|
||||
from keras.layers import LSTM
|
||||
from keras.layers import Embedding
|
||||
|
||||
|
||||
def test_temporal_classification():
|
||||
'''
|
||||
Classify temporal sequences of float numbers of length 3 into 2 classes using
|
||||
single layer of GRU units and softmax applied to the last activations of the units
|
||||
Classify temporal sequences of float numbers
|
||||
of length 3 into 2 classes using
|
||||
single layer of GRU units and softmax applied
|
||||
to the last activations of the units
|
||||
'''
|
||||
np.random.seed(1337)
|
||||
(X_train, y_train), (X_test, y_test) = get_test_data(nb_train=500,
|
||||
@@ -29,10 +34,12 @@ def test_temporal_classification():
|
||||
model.add(GRU(y_train.shape[-1],
|
||||
input_shape=(X_train.shape[1], X_train.shape[2]),
|
||||
activation='softmax'))
|
||||
model.compile(loss='categorical_crossentropy', optimizer='adadelta')
|
||||
model.compile(loss='categorical_crossentropy',
|
||||
optimizer='adadelta',
|
||||
metrics=['accuracy'])
|
||||
history = model.fit(X_train, y_train, nb_epoch=5, batch_size=16,
|
||||
validation_data=(X_test, y_test),
|
||||
show_accuracy=True, verbose=0)
|
||||
verbose=0)
|
||||
assert(history.history['val_acc'][-1] > 0.9)
|
||||
|
||||
|
||||
@@ -142,12 +149,12 @@ def test_masked_temporal():
|
||||
'''
|
||||
np.random.seed(55318)
|
||||
model = Sequential()
|
||||
model.add(Embedding(10, 20, mask_zero=True))
|
||||
model.add(Embedding(10, 20, mask_zero=True, input_length=20))
|
||||
model.add(TimeDistributedDense(10))
|
||||
model.add(Activation('softmax'))
|
||||
model.compile(loss='categorical_crossentropy',
|
||||
optimizer='adam',
|
||||
sample_weight_mode="temporal")
|
||||
sample_weight_mode='temporal')
|
||||
|
||||
X = np.random.random_integers(1, 9, (50000, 20))
|
||||
for rowi in range(X.shape[0]):
|
||||
@@ -164,15 +171,15 @@ def test_masked_temporal():
|
||||
|
||||
# Mask 50% of the outputs via sample weights
|
||||
sample_weight = np.random.random_integers(0, 1, y.shape)
|
||||
print("X shape: ", X.shape)
|
||||
print("Y shape: ", Y.shape)
|
||||
print("sample_weight shape: ", Y.shape)
|
||||
print('X shape:', X.shape)
|
||||
print('Y shape:', Y.shape)
|
||||
print('sample_weight shape:', Y.shape)
|
||||
|
||||
history = model.fit(X, Y, validation_split=0.05,
|
||||
sample_weight=sample_weight,
|
||||
sample_weight=None,
|
||||
verbose=1, nb_epoch=2)
|
||||
ground_truth = -np.log(0.5)
|
||||
assert(np.abs(history.history['val_loss'][-1] - ground_truth) < 0.05)
|
||||
assert(np.abs(history.history['val_loss'][-1] - ground_truth) < 0.06)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__])
|
||||
|
||||
@@ -28,10 +28,12 @@ def test_vector_classification():
|
||||
Dense(nb_hidden, input_shape=(X_train.shape[-1],), activation='relu'),
|
||||
Dense(y_train.shape[-1], activation='softmax')
|
||||
])
|
||||
model.compile(loss='categorical_crossentropy', optimizer='rmsprop')
|
||||
model.compile(loss='categorical_crossentropy',
|
||||
optimizer='rmsprop',
|
||||
metrics=['accuracy'])
|
||||
history = model.fit(X_train, y_train, nb_epoch=15, batch_size=16,
|
||||
validation_data=(X_test, y_test),
|
||||
show_accuracy=True, verbose=0)
|
||||
verbose=0)
|
||||
assert(history.history['val_acc'][-1] > 0.8)
|
||||
|
||||
|
||||
|
||||
@@ -40,6 +40,10 @@ class TestBackend(object):
|
||||
|
||||
def test_linear_operations(self):
|
||||
check_two_tensor_operation('dot', (4, 2), (2, 4))
|
||||
check_two_tensor_operation('dot', (4, 2), (5, 2, 3))
|
||||
|
||||
check_two_tensor_operation('batch_dot', (4, 2, 3), (4, 5, 3),
|
||||
axes=(2, 2))
|
||||
check_single_tensor_operation('transpose', (4, 2))
|
||||
|
||||
def test_shape_operations(self):
|
||||
@@ -145,6 +149,7 @@ class TestBackend(object):
|
||||
check_single_tensor_operation('exp', (4, 2))
|
||||
check_single_tensor_operation('log', (4, 2))
|
||||
check_single_tensor_operation('round', (4, 2))
|
||||
check_single_tensor_operation('sign', (4, 2))
|
||||
check_single_tensor_operation('pow', (4, 2), a=3)
|
||||
check_single_tensor_operation('clip', (4, 2), min_value=0.4,
|
||||
max_value=0.6)
|
||||
@@ -222,10 +227,10 @@ class TestBackend(object):
|
||||
return step_function
|
||||
|
||||
th_rnn_step_fn = rnn_step_fn(input_dim, output_dim, KTH)
|
||||
inputs = KTH.variable(input_val)
|
||||
initial_states = [KTH.variable(init_state_val)]
|
||||
last_output, outputs, new_states = KTH.rnn(th_rnn_step_fn, inputs,
|
||||
initial_states,
|
||||
th_inputs = KTH.variable(input_val)
|
||||
th_initial_states = [KTH.variable(init_state_val)]
|
||||
last_output, outputs, new_states = KTH.rnn(th_rnn_step_fn, th_inputs,
|
||||
th_initial_states,
|
||||
go_backwards=False,
|
||||
mask=None)
|
||||
th_last_output = KTH.eval(last_output)
|
||||
@@ -234,10 +239,10 @@ class TestBackend(object):
|
||||
th_state = KTH.eval(new_states[0])
|
||||
|
||||
tf_rnn_step_fn = rnn_step_fn(input_dim, output_dim, KTF)
|
||||
inputs = KTF.variable(input_val)
|
||||
initial_states = [KTF.variable(init_state_val)]
|
||||
last_output, outputs, new_states = KTF.rnn(tf_rnn_step_fn, inputs,
|
||||
initial_states,
|
||||
tf_inputs = KTF.variable(input_val)
|
||||
tf_initial_states = [KTF.variable(init_state_val)]
|
||||
last_output, outputs, new_states = KTF.rnn(tf_rnn_step_fn, tf_inputs,
|
||||
tf_initial_states,
|
||||
go_backwards=False,
|
||||
mask=None)
|
||||
tf_last_output = KTF.eval(last_output)
|
||||
@@ -249,6 +254,80 @@ class TestBackend(object):
|
||||
assert_allclose(tf_outputs, th_outputs, atol=1e-04)
|
||||
assert_allclose(tf_state, th_state, atol=1e-04)
|
||||
|
||||
# test unroll
|
||||
unrolled_last_output, unrolled_outputs, unrolled_new_states = KTH.rnn(
|
||||
th_rnn_step_fn, th_inputs,
|
||||
th_initial_states,
|
||||
go_backwards=False,
|
||||
mask=None,
|
||||
unroll=True,
|
||||
input_length=timesteps)
|
||||
|
||||
unrolled_th_last_output = KTH.eval(unrolled_last_output)
|
||||
unrolled_th_outputs = KTH.eval(unrolled_outputs)
|
||||
assert len(unrolled_new_states) == 1
|
||||
unrolled_th_state = KTH.eval(unrolled_new_states[0])
|
||||
assert_allclose(th_last_output, unrolled_th_last_output, atol=1e-04)
|
||||
assert_allclose(th_outputs, unrolled_th_outputs, atol=1e-04)
|
||||
assert_allclose(th_state, unrolled_th_state, atol=1e-04)
|
||||
|
||||
# test unroll with backwards = True
|
||||
bwd_last_output, bwd_outputs, bwd_new_states = KTH.rnn(
|
||||
th_rnn_step_fn, th_inputs,
|
||||
th_initial_states,
|
||||
go_backwards=True,
|
||||
mask=None)
|
||||
bwd_th_last_output = KTH.eval(bwd_last_output)
|
||||
bwd_th_outputs = KTH.eval(bwd_outputs)
|
||||
assert len(bwd_new_states) == 1
|
||||
bwd_th_state = KTH.eval(bwd_new_states[0])
|
||||
|
||||
bwd_unrolled_last_output, bwd_unrolled_outputs, bwd_unrolled_new_states = KTH.rnn(
|
||||
th_rnn_step_fn, th_inputs,
|
||||
th_initial_states,
|
||||
go_backwards=True,
|
||||
mask=None,
|
||||
unroll=True,
|
||||
input_length=timesteps)
|
||||
|
||||
bwd_unrolled_th_last_output = KTH.eval(bwd_unrolled_last_output)
|
||||
bwd_unrolled_th_outputs = KTH.eval(bwd_unrolled_outputs)
|
||||
assert len(bwd_unrolled_new_states) == 1
|
||||
bwd_unrolled_th_state = KTH.eval(bwd_unrolled_new_states[0])
|
||||
assert_allclose(bwd_th_last_output, bwd_unrolled_th_last_output, atol=1e-04)
|
||||
assert_allclose(bwd_th_outputs, bwd_unrolled_th_outputs, atol=1e-04)
|
||||
assert_allclose(bwd_th_state, bwd_unrolled_th_state, atol=1e-04)
|
||||
|
||||
# test unroll with masking
|
||||
np_mask = np.random.randint(2, size=(32, timesteps))
|
||||
th_mask = KTH.variable(np_mask)
|
||||
|
||||
masked_last_output, masked_outputs, masked_new_states = KTH.rnn(
|
||||
th_rnn_step_fn, th_inputs,
|
||||
th_initial_states,
|
||||
go_backwards=False,
|
||||
mask=th_mask)
|
||||
masked_th_last_output = KTH.eval(masked_last_output)
|
||||
masked_th_outputs = KTH.eval(masked_outputs)
|
||||
assert len(masked_new_states) == 1
|
||||
masked_th_state = KTH.eval(masked_new_states[0])
|
||||
|
||||
unrolled_masked_last_output, unrolled_masked_outputs, unrolled_masked_new_states = KTH.rnn(
|
||||
th_rnn_step_fn, th_inputs,
|
||||
th_initial_states,
|
||||
go_backwards=False,
|
||||
mask=th_mask,
|
||||
unroll=True,
|
||||
input_length=timesteps)
|
||||
unrolled_masked_th_last_output = KTH.eval(unrolled_masked_last_output)
|
||||
unrolled_masked_th_outputs = KTH.eval(unrolled_masked_outputs)
|
||||
assert len(unrolled_masked_new_states) == 1
|
||||
unrolled_masked_th_state = KTH.eval(unrolled_masked_new_states[0])
|
||||
|
||||
assert_allclose(unrolled_masked_th_last_output, masked_th_last_output, atol=1e-04)
|
||||
assert_allclose(unrolled_masked_th_outputs, masked_th_outputs, atol=1e-04)
|
||||
assert_allclose(unrolled_masked_th_state, masked_th_state, atol=1e-04)
|
||||
|
||||
def test_switch(self):
|
||||
val = np.random.random()
|
||||
xth = KTH.variable(val)
|
||||
@@ -273,7 +352,7 @@ class TestBackend(object):
|
||||
check_single_tensor_operation('tanh', (4, 2))
|
||||
|
||||
# dropout
|
||||
val = np.random.random((20, 20))
|
||||
val = np.random.random((100, 100))
|
||||
xth = KTH.variable(val)
|
||||
xtf = KTF.variable(val)
|
||||
zth = KTH.eval(KTH.dropout(xth, level=0.2))
|
||||
@@ -355,6 +434,20 @@ class TestBackend(object):
|
||||
assert(np.max(rand) <= max)
|
||||
assert(np.min(rand) >= min)
|
||||
|
||||
def test_random_binomial(self):
|
||||
p = 0.5
|
||||
rand = KTF.eval(KTF.random_binomial((1000, 1000), p))
|
||||
assert(rand.shape == (1000, 1000))
|
||||
assert(np.abs(np.mean(rand) - p) < 0.01)
|
||||
assert(np.max(rand) == 1)
|
||||
assert(np.min(rand) == 0)
|
||||
|
||||
rand = KTH.eval(KTH.random_binomial((1000, 1000), p))
|
||||
assert(rand.shape == (1000, 1000))
|
||||
assert(np.abs(np.mean(rand) - p) < 0.01)
|
||||
assert(np.max(rand) == 1)
|
||||
assert(np.min(rand) == 0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__])
|
||||
|
||||
@@ -0,0 +1,565 @@
|
||||
import pytest
|
||||
import json
|
||||
import numpy as np
|
||||
|
||||
from keras.layers import Dense, Dropout
|
||||
from keras.engine import merge, Input, get_source_inputs
|
||||
from keras.models import Model
|
||||
from keras import backend as K
|
||||
from keras.models import model_from_json, model_from_yaml
|
||||
|
||||
|
||||
def test_learning_phase():
|
||||
a = Input(shape=(32,), name='input_a')
|
||||
b = Input(shape=(32,), name='input_b')
|
||||
|
||||
a_2 = Dense(16, name='dense_1')(a)
|
||||
dp = Dropout(0.5, name='dropout')
|
||||
b_2 = dp(b)
|
||||
|
||||
assert dp.uses_learning_phase
|
||||
|
||||
assert not a_2._uses_learning_phase
|
||||
assert b_2._uses_learning_phase
|
||||
|
||||
# test merge
|
||||
m = merge([a_2, b_2], mode='concat')
|
||||
assert m._uses_learning_phase
|
||||
|
||||
# Test recursion
|
||||
model = Model([a, b], [a_2, b_2])
|
||||
print(model.input_spec)
|
||||
assert model.uses_learning_phase
|
||||
|
||||
c = Input(shape=(32,), name='input_c')
|
||||
d = Input(shape=(32,), name='input_d')
|
||||
|
||||
c_2, b_2 = model([c, d])
|
||||
assert c_2._uses_learning_phase
|
||||
assert b_2._uses_learning_phase
|
||||
|
||||
# try actually running graph
|
||||
fn = K.function(model.inputs + [K.learning_phase()], model.outputs)
|
||||
input_a_np = np.random.random((10, 32))
|
||||
input_b_np = np.random.random((10, 32))
|
||||
fn_outputs_no_dp = fn([input_a_np, input_b_np, 0])
|
||||
fn_outputs_dp = fn([input_a_np, input_b_np, 1])
|
||||
# output a: nothing changes
|
||||
assert fn_outputs_no_dp[0].sum() == fn_outputs_dp[0].sum()
|
||||
# output b: dropout applied
|
||||
assert fn_outputs_no_dp[1].sum() != fn_outputs_dp[1].sum()
|
||||
|
||||
|
||||
def test_node_construction():
|
||||
####################################################
|
||||
# test basics
|
||||
|
||||
a = Input(shape=(32,), name='input_a')
|
||||
b = Input(shape=(32,), name='input_b')
|
||||
|
||||
assert a._keras_shape == (None, 32)
|
||||
a_layer, a_node_index, a_tensor_index = a._keras_history
|
||||
b_layer, b_node_index, b_tensor_index = b._keras_history
|
||||
assert len(a_layer.inbound_nodes) == 1
|
||||
assert a_tensor_index is 0
|
||||
node = a_layer.inbound_nodes[a_node_index]
|
||||
assert node.outbound_layer == a_layer
|
||||
|
||||
assert type(node.inbound_layers) is list
|
||||
assert node.inbound_layers == []
|
||||
assert type(node.input_tensors) is list
|
||||
assert node.input_tensors == [a]
|
||||
assert type(node.input_masks) is list
|
||||
assert node.input_masks == [None]
|
||||
assert type(node.input_shapes) is list
|
||||
assert node.input_shapes == [(None, 32)]
|
||||
|
||||
assert type(node.output_tensors) is list
|
||||
assert node.output_tensors == [a]
|
||||
assert type(node.output_shapes) is list
|
||||
assert node.output_shapes == [(None, 32)]
|
||||
assert type(node.output_masks) is list
|
||||
assert node.output_masks == [None]
|
||||
|
||||
dense = Dense(16, name='dense_1')
|
||||
a_2 = dense(a)
|
||||
b_2 = dense(b)
|
||||
|
||||
assert len(dense.inbound_nodes) == 2
|
||||
assert len(dense.outbound_nodes) == 0
|
||||
assert dense.inbound_nodes[0].inbound_layers == [a_layer]
|
||||
assert dense.inbound_nodes[0].outbound_layer == dense
|
||||
assert dense.inbound_nodes[1].inbound_layers == [b_layer]
|
||||
assert dense.inbound_nodes[1].outbound_layer == dense
|
||||
|
||||
assert dense.inbound_nodes[0].input_tensors == [a]
|
||||
assert dense.inbound_nodes[1].input_tensors == [b]
|
||||
|
||||
# test layer properties
|
||||
test_layer = Dense(16, name='test_layer')
|
||||
a_test = test_layer(a)
|
||||
assert test_layer.input == a
|
||||
assert test_layer.output == a_test
|
||||
assert test_layer.input_mask is None
|
||||
assert test_layer.output_mask is None
|
||||
assert test_layer.input_shape == (None, 32)
|
||||
assert test_layer.output_shape == (None, 16)
|
||||
|
||||
with pytest.raises(Exception):
|
||||
dense.input
|
||||
with pytest.raises(Exception):
|
||||
dense.output
|
||||
with pytest.raises(Exception):
|
||||
dense.input_mask
|
||||
with pytest.raises(Exception):
|
||||
dense.output_mask
|
||||
|
||||
assert dense.get_input_at(0) == a
|
||||
assert dense.get_input_at(1) == b
|
||||
assert dense.get_output_at(0) == a_2
|
||||
assert dense.get_output_at(1) == b_2
|
||||
assert dense.get_input_shape_at(0) == (None, 32)
|
||||
assert dense.get_input_shape_at(1) == (None, 32)
|
||||
assert dense.get_output_shape_at(0) == (None, 16)
|
||||
assert dense.get_output_shape_at(1) == (None, 16)
|
||||
assert dense.get_input_mask_at(0) is None
|
||||
assert dense.get_input_mask_at(1) is None
|
||||
assert dense.get_output_mask_at(0) is None
|
||||
assert dense.get_output_mask_at(1) is None
|
||||
|
||||
|
||||
def test_multi_input_layer():
|
||||
####################################################
|
||||
# test multi-input layer
|
||||
a = Input(shape=(32,), name='input_a')
|
||||
b = Input(shape=(32,), name='input_b')
|
||||
|
||||
dense = Dense(16, name='dense_1')
|
||||
a_2 = dense(a)
|
||||
b_2 = dense(b)
|
||||
|
||||
merged = merge([a_2, b_2], mode='concat', name='merge')
|
||||
assert merged._keras_shape == (None, 16 * 2)
|
||||
merge_layer, merge_node_index, merge_tensor_index = merged._keras_history
|
||||
|
||||
assert merge_node_index == 0
|
||||
assert merge_tensor_index == 0
|
||||
|
||||
assert len(merge_layer.inbound_nodes) == 1
|
||||
assert len(merge_layer.outbound_nodes) == 0
|
||||
|
||||
assert len(merge_layer.inbound_nodes[0].input_tensors) == 2
|
||||
assert len(merge_layer.inbound_nodes[0].inbound_layers) == 2
|
||||
|
||||
c = Dense(64, name='dense_2')(merged)
|
||||
d = Dense(5, name='dense_3')(c)
|
||||
|
||||
model = Model(input=[a, b], output=[c, d], name='model')
|
||||
assert len(model.layers) == 6
|
||||
print('model.input_layers:', model.input_layers)
|
||||
print('model.input_layers_node_indices:', model.input_layers_node_indices)
|
||||
print('model.input_layers_tensor_indices:', model.input_layers_tensor_indices)
|
||||
print('model.output_layers', model.output_layers)
|
||||
|
||||
print('output_shape:', model.get_output_shape_for([(None, 32), (None, 32)]))
|
||||
assert model.get_output_shape_for([(None, 32), (None, 32)]) == [(None, 64), (None, 5)]
|
||||
|
||||
assert model.compute_mask([a, b], [None, None]) == [None, None]
|
||||
|
||||
print('output_shape:', model.get_output_shape_for([(None, 32), (None, 32)]))
|
||||
assert model.get_output_shape_for([(None, 32), (None, 32)]) == [(None, 64), (None, 5)]
|
||||
|
||||
# we don't check names of first 2 layers (inputs) because
|
||||
# ordering of same-level layers is not fixed
|
||||
print('layers:', [layer.name for layer in model.layers])
|
||||
assert [l.name for l in model.layers][2:] == ['dense_1', 'merge', 'dense_2', 'dense_3']
|
||||
print('input_layers:', [l.name for l in model.input_layers])
|
||||
assert [l.name for l in model.input_layers] == ['input_a', 'input_b']
|
||||
print('output_layers:', [l.name for l in model.output_layers])
|
||||
assert [l.name for l in model.output_layers] == ['dense_2', 'dense_3']
|
||||
|
||||
# actually run model
|
||||
fn = K.function(model.inputs, model.outputs)
|
||||
input_a_np = np.random.random((10, 32))
|
||||
input_b_np = np.random.random((10, 32))
|
||||
fn_outputs = fn([input_a_np, input_b_np])
|
||||
assert [x.shape for x in fn_outputs] == [(10, 64), (10, 5)]
|
||||
|
||||
# test get_source_inputs
|
||||
print(get_source_inputs(c))
|
||||
assert get_source_inputs(c) == [a, b]
|
||||
|
||||
# serialization / deserialization
|
||||
json_config = model.to_json()
|
||||
recreated_model = model_from_json(json_config)
|
||||
recreated_model.compile('rmsprop', 'mse')
|
||||
|
||||
print('recreated:')
|
||||
print([layer.name for layer in recreated_model.layers])
|
||||
print([layer.name for layer in recreated_model.input_layers])
|
||||
print([layer.name for layer in recreated_model.output_layers])
|
||||
assert [l.name for l in recreated_model.layers][2:] == ['dense_1', 'merge', 'dense_2', 'dense_3']
|
||||
assert [l.name for l in recreated_model.input_layers] == ['input_a', 'input_b']
|
||||
assert [l.name for l in recreated_model.output_layers] == ['dense_2', 'dense_3']
|
||||
|
||||
fn = K.function(recreated_model.inputs, recreated_model.outputs)
|
||||
input_a_np = np.random.random((10, 32))
|
||||
input_b_np = np.random.random((10, 32))
|
||||
fn_outputs = fn([input_a_np, input_b_np])
|
||||
assert [x.shape for x in fn_outputs] == [(10, 64), (10, 5)]
|
||||
|
||||
|
||||
def test_recursion():
|
||||
####################################################
|
||||
# test recursion
|
||||
|
||||
a = Input(shape=(32,), name='input_a')
|
||||
b = Input(shape=(32,), name='input_b')
|
||||
|
||||
dense = Dense(16, name='dense_1')
|
||||
a_2 = dense(a)
|
||||
b_2 = dense(b)
|
||||
merged = merge([a_2, b_2], mode='concat', name='merge')
|
||||
c = Dense(64, name='dense_2')(merged)
|
||||
d = Dense(5, name='dense_3')(c)
|
||||
|
||||
model = Model(input=[a, b], output=[c, d], name='model')
|
||||
|
||||
e = Input(shape=(32,), name='input_e')
|
||||
f = Input(shape=(32,), name='input_f')
|
||||
g, h = model([e, f])
|
||||
|
||||
# g2, h2 = model([e, f])
|
||||
|
||||
assert g._keras_shape == c._keras_shape
|
||||
assert h._keras_shape == d._keras_shape
|
||||
|
||||
# test separate manipulation of different layer outputs
|
||||
i = Dense(7, name='dense_4')(h)
|
||||
|
||||
final_model = Model(input=[e, f], output=[i, g], name='final')
|
||||
assert len(final_model.inputs) == 2
|
||||
assert len(final_model.outputs) == 2
|
||||
assert len(final_model.layers) == 4
|
||||
|
||||
# we don't check names of first 2 layers (inputs) because
|
||||
# ordering of same-level layers is not fixed
|
||||
print('final_model layers:', [layer.name for layer in final_model.layers])
|
||||
assert [layer.name for layer in final_model.layers][2:] == ['model', 'dense_4']
|
||||
|
||||
print(model.compute_mask([e, f], [None, None]))
|
||||
assert model.compute_mask([e, f], [None, None]) == [None, None]
|
||||
|
||||
print(final_model.get_output_shape_for([(10, 32), (10, 32)]))
|
||||
assert final_model.get_output_shape_for([(10, 32), (10, 32)]) == [(10, 7), (10, 64)]
|
||||
|
||||
# run recursive model
|
||||
fn = K.function(final_model.inputs, final_model.outputs)
|
||||
input_a_np = np.random.random((10, 32))
|
||||
input_b_np = np.random.random((10, 32))
|
||||
fn_outputs = fn([input_a_np, input_b_np])
|
||||
assert [x.shape for x in fn_outputs] == [(10, 7), (10, 64)]
|
||||
|
||||
# test serialization
|
||||
model_config = final_model.get_config()
|
||||
print(json.dumps(model_config, indent=4))
|
||||
recreated_model = Model.from_config(model_config)
|
||||
|
||||
fn = K.function(recreated_model.inputs, recreated_model.outputs)
|
||||
input_a_np = np.random.random((10, 32))
|
||||
input_b_np = np.random.random((10, 32))
|
||||
fn_outputs = fn([input_a_np, input_b_np])
|
||||
assert [x.shape for x in fn_outputs] == [(10, 7), (10, 64)]
|
||||
|
||||
####################################################
|
||||
# test multi-input multi-output
|
||||
|
||||
j = Input(shape=(32,), name='input_j')
|
||||
k = Input(shape=(32,), name='input_k')
|
||||
m, n = model([j, k])
|
||||
|
||||
o = Input(shape=(32,), name='input_o')
|
||||
p = Input(shape=(32,), name='input_p')
|
||||
q, r = model([o, p])
|
||||
|
||||
assert n._keras_shape == (None, 5)
|
||||
assert q._keras_shape == (None, 64)
|
||||
s = merge([n, q], mode='concat', name='merge_nq')
|
||||
assert s._keras_shape == (None, 64 + 5)
|
||||
|
||||
# test with single output as 1-elem list
|
||||
multi_io_model = Model([j, k, o, p], [s])
|
||||
|
||||
fn = K.function(multi_io_model.inputs, multi_io_model.outputs)
|
||||
fn_outputs = fn([np.random.random((10, 32)), np.random.random((10, 32)),
|
||||
np.random.random((10, 32)), np.random.random((10, 32))])
|
||||
assert [x.shape for x in fn_outputs] == [(10, 69)]
|
||||
|
||||
# test with single output as tensor
|
||||
multi_io_model = Model([j, k, o, p], s)
|
||||
|
||||
fn = K.function(multi_io_model.inputs, multi_io_model.outputs)
|
||||
fn_outputs = fn([np.random.random((10, 32)), np.random.random((10, 32)),
|
||||
np.random.random((10, 32)), np.random.random((10, 32))])
|
||||
# note that the output of the K.function will still be a 1-elem list
|
||||
assert [x.shape for x in fn_outputs] == [(10, 69)]
|
||||
|
||||
# test serialization
|
||||
print('multi_io_model.layers:', multi_io_model.layers)
|
||||
print('len(model.inbound_nodes):', len(model.inbound_nodes))
|
||||
print('len(model.outbound_nodes):', len(model.outbound_nodes))
|
||||
model_config = multi_io_model.get_config()
|
||||
print(model_config)
|
||||
print(json.dumps(model_config, indent=4))
|
||||
recreated_model = Model.from_config(model_config)
|
||||
|
||||
fn = K.function(recreated_model.inputs, recreated_model.outputs)
|
||||
fn_outputs = fn([np.random.random((10, 32)), np.random.random((10, 32)),
|
||||
np.random.random((10, 32)), np.random.random((10, 32))])
|
||||
# note that the output of the K.function will still be a 1-elem list
|
||||
assert [x.shape for x in fn_outputs] == [(10, 69)]
|
||||
|
||||
config = model.get_config()
|
||||
new_model = Model.from_config(config)
|
||||
|
||||
model.summary()
|
||||
json_str = model.to_json()
|
||||
new_model = model_from_json(json_str)
|
||||
|
||||
yaml_str = model.to_yaml()
|
||||
new_model = model_from_yaml(yaml_str)
|
||||
|
||||
####################################################
|
||||
# test invalid graphs
|
||||
|
||||
# input is not an Input tensor
|
||||
j = Input(shape=(32,), name='input_j')
|
||||
j = Dense(32)(j)
|
||||
k = Input(shape=(32,), name='input_k')
|
||||
m, n = model([j, k])
|
||||
|
||||
with pytest.raises(Exception):
|
||||
invalid_model = Model([j, k], [m, n])
|
||||
|
||||
# disconnected graph
|
||||
j = Input(shape=(32,), name='input_j')
|
||||
k = Input(shape=(32,), name='input_k')
|
||||
m, n = model([j, k])
|
||||
with pytest.raises(Exception) as e:
|
||||
invalid_model = Model([j], [m, n])
|
||||
|
||||
# redudant outputs
|
||||
j = Input(shape=(32,), name='input_j')
|
||||
k = Input(shape=(32,), name='input_k')
|
||||
m, n = model([j, k])
|
||||
# this should work lol
|
||||
# TODO: raise a warning
|
||||
invalid_model = Model([j, k], [m, n, n])
|
||||
|
||||
# redundant inputs
|
||||
j = Input(shape=(32,), name='input_j')
|
||||
k = Input(shape=(32,), name='input_k')
|
||||
m, n = model([j, k])
|
||||
with pytest.raises(Exception):
|
||||
invalid_model = Model([j, k, j], [m, n])
|
||||
|
||||
# i have not idea what I'm doing: garbage as inputs/outputs
|
||||
j = Input(shape=(32,), name='input_j')
|
||||
k = Input(shape=(32,), name='input_k')
|
||||
m, n = model([j, k])
|
||||
with pytest.raises(Exception):
|
||||
invalid_model = Model([j, k], [m, n, 0])
|
||||
|
||||
####################################################
|
||||
# test calling layers/models on TF tensors
|
||||
|
||||
if K._BACKEND == 'tensorflow':
|
||||
import tensorflow as tf
|
||||
j = Input(shape=(32,), name='input_j')
|
||||
k = Input(shape=(32,), name='input_k')
|
||||
m, n = model([j, k])
|
||||
tf_model = Model([j, k], [m, n])
|
||||
|
||||
# magic
|
||||
j_tf = tf.placeholder(dtype=K.floatx())
|
||||
k_tf = tf.placeholder(dtype=K.floatx())
|
||||
m_tf, n_tf = tf_model([j_tf, k_tf])
|
||||
assert not hasattr(m_tf, '_keras_shape')
|
||||
assert not hasattr(n_tf, '_keras_shape')
|
||||
assert K.int_shape(m_tf) == (None, 64)
|
||||
assert K.int_shape(n_tf) == (None, 5)
|
||||
|
||||
|
||||
def test_functional_guide():
|
||||
# MNIST
|
||||
from keras.layers import Input, Dense, LSTM
|
||||
from keras.models import Model
|
||||
from keras.utils import np_utils
|
||||
|
||||
# this returns a tensor
|
||||
inputs = Input(shape=(784,))
|
||||
|
||||
# a layer instance is callable on a tensor, and returns a tensor
|
||||
x = Dense(64, activation='relu')(inputs)
|
||||
x = Dense(64, activation='relu')(x)
|
||||
predictions = Dense(10, activation='softmax')(x)
|
||||
|
||||
# this creates a model that includes
|
||||
# the Input layer and three Dense layers
|
||||
model = Model(input=inputs, output=predictions)
|
||||
model.compile(optimizer='rmsprop',
|
||||
loss='categorical_crossentropy',
|
||||
metrics=['accuracy'])
|
||||
|
||||
# the data, shuffled and split between tran and test sets
|
||||
X_train = np.random.random((100, 784))
|
||||
Y_train = np.random.random((100, 10))
|
||||
|
||||
model.fit(X_train, Y_train, nb_epoch=2, batch_size=128)
|
||||
|
||||
assert model.inputs == [inputs]
|
||||
assert model.outputs == [predictions]
|
||||
assert model.input == inputs
|
||||
assert model.output == predictions
|
||||
assert model.input_shape == (None, 784)
|
||||
assert model.output_shape == (None, 10)
|
||||
|
||||
# try calling the sequential model
|
||||
inputs = Input(shape=(784,))
|
||||
new_outputs = model(inputs)
|
||||
new_model = Model(input=inputs, output=new_outputs)
|
||||
new_model.compile(optimizer='rmsprop',
|
||||
loss='categorical_crossentropy',
|
||||
metrics=['accuracy'])
|
||||
|
||||
##################################################
|
||||
# multi-io
|
||||
##################################################
|
||||
tweet_a = Input(shape=(4, 25))
|
||||
tweet_b = Input(shape=(4, 25))
|
||||
# this layer can take as input a matrix
|
||||
# and will return a vector of size 64
|
||||
shared_lstm = LSTM(64)
|
||||
|
||||
# when we reuse the same layer instance
|
||||
# multiple times, the weights of the layer
|
||||
# are also being reused
|
||||
# (it is effectively *the same* layer)
|
||||
encoded_a = shared_lstm(tweet_a)
|
||||
encoded_b = shared_lstm(tweet_b)
|
||||
|
||||
# we can then concatenate the two vectors:
|
||||
merged_vector = merge([encoded_a, encoded_b],
|
||||
mode='concat', concat_axis=-1)
|
||||
|
||||
# and add a logistic regression on top
|
||||
predictions = Dense(1, activation='sigmoid')(merged_vector)
|
||||
|
||||
# we define a trainable model linking the
|
||||
# tweet inputs to the predictions
|
||||
model = Model(input=[tweet_a, tweet_b], output=predictions)
|
||||
|
||||
model.compile(optimizer='rmsprop',
|
||||
loss='binary_crossentropy',
|
||||
metrics=['accuracy'])
|
||||
data_a = np.random.random((1000, 4, 25))
|
||||
data_b = np.random.random((1000, 4, 25))
|
||||
labels = np.random.random((1000,))
|
||||
model.fit([data_a, data_b], labels, nb_epoch=1)
|
||||
|
||||
model.summary()
|
||||
assert model.inputs == [tweet_a, tweet_b]
|
||||
assert model.outputs == [predictions]
|
||||
assert model.input == [tweet_a, tweet_b]
|
||||
assert model.output == predictions
|
||||
|
||||
assert model.output == predictions
|
||||
assert model.input_shape == [(None, 4, 25), (None, 4, 25)]
|
||||
assert model.output_shape == (None, 1)
|
||||
|
||||
assert shared_lstm.get_output_at(0) == encoded_a
|
||||
assert shared_lstm.get_output_at(1) == encoded_b
|
||||
assert shared_lstm.input_shape == (None, 4, 25)
|
||||
|
||||
|
||||
def test_sequential_regression():
|
||||
from keras.models import Sequential, Model
|
||||
from keras.layers import Merge, Embedding, BatchNormalization, LSTM, InputLayer, Input
|
||||
|
||||
# start with a basic example of using a Sequential model
|
||||
# inside the functional API
|
||||
seq = Sequential()
|
||||
seq.add(Dense(input_dim=10, output_dim=10))
|
||||
|
||||
x = Input(shape=(10,))
|
||||
y = seq(x)
|
||||
model = Model(x, y)
|
||||
model.compile('rmsprop', 'mse')
|
||||
weights = model.get_weights()
|
||||
|
||||
# test serialization
|
||||
config = model.get_config()
|
||||
model = Model.from_config(config)
|
||||
model.compile('rmsprop', 'mse')
|
||||
model.set_weights(weights)
|
||||
|
||||
# more advanced model with multiple branches
|
||||
|
||||
branch_1 = Sequential(name='branch_1')
|
||||
branch_1.add(Embedding(input_dim=100,
|
||||
output_dim=10,
|
||||
input_length=2,
|
||||
name='embed_1'))
|
||||
branch_1.add(LSTM(32, name='lstm_1'))
|
||||
|
||||
branch_1.add(BatchNormalization())
|
||||
|
||||
branch_2 = Sequential(name='branch_2')
|
||||
branch_2.add(Dense(32, input_shape=(8,), name='dense_2'))
|
||||
|
||||
branch_3 = Sequential(name='branch_3')
|
||||
branch_3.add(Dense(32, input_shape=(6,), name='dense_3'))
|
||||
|
||||
branch_1_2 = Sequential([Merge([branch_1, branch_2], mode='concat')], name='branch_1_2')
|
||||
branch_1_2.add(Dense(16, name='dense_1_2-0'))
|
||||
# test whether impromtu input_shape breaks the model
|
||||
branch_1_2.add(Dense(16, input_shape=(16,), name='dense_1_2-1'))
|
||||
|
||||
model = Sequential([Merge([branch_1_2, branch_3], mode='concat')], name='final')
|
||||
model.add(Dense(16, name='dense_final'))
|
||||
model.compile(optimizer='rmsprop',
|
||||
loss='categorical_crossentropy',
|
||||
metrics=['accuracy'])
|
||||
|
||||
model.summary()
|
||||
|
||||
x = (100 * np.random.random((100, 2))).astype('int32')
|
||||
y = np.random.random((100, 8))
|
||||
z = np.random.random((100, 6))
|
||||
labels = np.random.random((100, 16))
|
||||
model.fit([x, y, z], labels, nb_epoch=1)
|
||||
|
||||
# test if Sequential can be called in the functional API
|
||||
|
||||
a = Input(shape=(2,), dtype='int32')
|
||||
b = Input(shape=(8,))
|
||||
c = Input(shape=(6,))
|
||||
o = model([a, b, c])
|
||||
|
||||
outer_model = Model([a, b, c], o)
|
||||
outer_model.compile(optimizer='rmsprop',
|
||||
loss='categorical_crossentropy',
|
||||
metrics=['accuracy'])
|
||||
outer_model.fit([x, y, z], labels, nb_epoch=1)
|
||||
|
||||
# test serialization
|
||||
config = outer_model.get_config()
|
||||
outer_model = Model.from_config(config)
|
||||
outer_model.compile(optimizer='rmsprop',
|
||||
loss='categorical_crossentropy',
|
||||
metrics=['accuracy'])
|
||||
outer_model.fit([x, y, z], labels, nb_epoch=1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main([__file__])
|
||||
@@ -0,0 +1,157 @@
|
||||
import pytest
|
||||
import numpy as np
|
||||
|
||||
from keras.layers import Dense, Dropout
|
||||
from keras.engine.topology import merge, Input
|
||||
from keras.engine.training import Model
|
||||
from keras import backend as K
|
||||
|
||||
|
||||
def test_model_methods():
|
||||
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)
|
||||
|
||||
input_a_np = np.random.random((10, 3))
|
||||
input_b_np = np.random.random((10, 3))
|
||||
|
||||
output_a_np = np.random.random((10, 4))
|
||||
output_b_np = np.random.random((10, 3))
|
||||
|
||||
# test train_on_batch
|
||||
out = model.train_on_batch([input_a_np, input_b_np],
|
||||
[output_a_np, output_b_np])
|
||||
out = model.train_on_batch({'input_a': input_a_np, 'input_b': input_b_np},
|
||||
[output_a_np, output_b_np])
|
||||
out = model.train_on_batch({'input_a': input_a_np, 'input_b': input_b_np},
|
||||
{'dense_1': output_a_np, 'dropout': output_b_np})
|
||||
|
||||
# test fit
|
||||
out = model.fit([input_a_np, input_b_np],
|
||||
[output_a_np, output_b_np], nb_epoch=1, batch_size=4)
|
||||
out = model.fit({'input_a': input_a_np, 'input_b': input_b_np},
|
||||
[output_a_np, output_b_np], nb_epoch=1, batch_size=4)
|
||||
out = model.fit({'input_a': input_a_np, 'input_b': input_b_np},
|
||||
{'dense_1': output_a_np, 'dropout': output_b_np},
|
||||
nb_epoch=1, batch_size=4)
|
||||
|
||||
# test validation_split
|
||||
out = model.fit([input_a_np, input_b_np],
|
||||
[output_a_np, output_b_np],
|
||||
nb_epoch=1, batch_size=4, validation_split=0.5)
|
||||
out = model.fit({'input_a': input_a_np, 'input_b': input_b_np},
|
||||
[output_a_np, output_b_np],
|
||||
nb_epoch=1, batch_size=4, validation_split=0.5)
|
||||
out = model.fit({'input_a': input_a_np, 'input_b': input_b_np},
|
||||
{'dense_1': output_a_np, 'dropout': output_b_np},
|
||||
nb_epoch=1, batch_size=4, validation_split=0.5)
|
||||
|
||||
# test validation data
|
||||
out = model.fit([input_a_np, input_b_np],
|
||||
[output_a_np, output_b_np],
|
||||
nb_epoch=1, batch_size=4,
|
||||
validation_data=([input_a_np, input_b_np], [output_a_np, output_b_np]))
|
||||
out = model.fit({'input_a': input_a_np, 'input_b': input_b_np},
|
||||
[output_a_np, output_b_np],
|
||||
nb_epoch=1, batch_size=4, validation_split=0.5,
|
||||
validation_data=({'input_a': input_a_np, 'input_b': input_b_np}, [output_a_np, output_b_np]))
|
||||
out = model.fit({'input_a': input_a_np, 'input_b': input_b_np},
|
||||
{'dense_1': output_a_np, 'dropout': output_b_np},
|
||||
nb_epoch=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}))
|
||||
|
||||
# test_on_batch
|
||||
out = model.test_on_batch([input_a_np, input_b_np],
|
||||
[output_a_np, output_b_np])
|
||||
out = model.test_on_batch({'input_a': input_a_np, 'input_b': input_b_np},
|
||||
[output_a_np, output_b_np])
|
||||
out = model.test_on_batch({'input_a': input_a_np, 'input_b': input_b_np},
|
||||
{'dense_1': output_a_np, 'dropout': output_b_np})
|
||||
|
||||
# predict_on_batch
|
||||
out = model.predict_on_batch([input_a_np, input_b_np])
|
||||
out = model.predict_on_batch({'input_a': input_a_np, 'input_b': input_b_np})
|
||||
|
||||
# predict, evaluate
|
||||
input_a_np = np.random.random((10, 3))
|
||||
input_b_np = np.random.random((10, 3))
|
||||
|
||||
output_a_np = np.random.random((10, 4))
|
||||
output_b_np = np.random.random((10, 3))
|
||||
|
||||
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)
|
||||
|
||||
# with sample_weight
|
||||
input_a_np = np.random.random((10, 3))
|
||||
input_b_np = np.random.random((10, 3))
|
||||
|
||||
output_a_np = np.random.random((10, 4))
|
||||
output_b_np = np.random.random((10, 3))
|
||||
|
||||
sample_weight = [None, np.random.random((10,))]
|
||||
out = model.train_on_batch([input_a_np, input_b_np],
|
||||
[output_a_np, output_b_np],
|
||||
sample_weight=sample_weight)
|
||||
|
||||
out = model.test_on_batch([input_a_np, input_b_np],
|
||||
[output_a_np, output_b_np],
|
||||
sample_weight=sample_weight)
|
||||
|
||||
# test accuracy metric
|
||||
model.compile(optimizer, loss, metrics=['acc'],
|
||||
sample_weight_mode=None)
|
||||
|
||||
out = model.train_on_batch([input_a_np, input_b_np],
|
||||
[output_a_np, output_b_np])
|
||||
assert len(out) == 3
|
||||
out = model.test_on_batch([input_a_np, input_b_np],
|
||||
[output_a_np, output_b_np])
|
||||
assert len(out) == 3
|
||||
|
||||
# this should also work
|
||||
model.compile(optimizer, loss, metrics={'dense_1': 'acc'},
|
||||
sample_weight_mode=None)
|
||||
|
||||
out = model.train_on_batch([input_a_np, input_b_np],
|
||||
[output_a_np, output_b_np])
|
||||
assert len(out) == 2
|
||||
out = model.test_on_batch([input_a_np, input_b_np],
|
||||
[output_a_np, output_b_np])
|
||||
assert len(out) == 2
|
||||
|
||||
# and this as well
|
||||
model.compile(optimizer, loss, metrics={'dense_1': ['acc']},
|
||||
sample_weight_mode=None)
|
||||
|
||||
out = model.train_on_batch([input_a_np, input_b_np],
|
||||
[output_a_np, output_b_np])
|
||||
assert len(out) == 2
|
||||
out = model.test_on_batch([input_a_np, input_b_np],
|
||||
[output_a_np, output_b_np])
|
||||
assert len(out) == 2
|
||||
|
||||
input_a_np = np.random.random((10, 3))
|
||||
input_b_np = np.random.random((10, 3))
|
||||
|
||||
output_a_np = np.random.random((10, 4))
|
||||
output_b_np = np.random.random((10, 3))
|
||||
|
||||
out = model.fit([input_a_np, input_b_np], [output_a_np, output_b_np], batch_size=4, nb_epoch=1)
|
||||
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)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__])
|
||||
@@ -1,162 +1,46 @@
|
||||
import pytest
|
||||
from numpy.testing import assert_allclose
|
||||
import numpy as np
|
||||
from keras import backend as K
|
||||
|
||||
|
||||
def get_standard_values():
|
||||
'''
|
||||
These are just a set of floats used for testing the activation
|
||||
functions, and are useful in multiple tests.
|
||||
|
||||
The values should all be non-negative because they and their negatives
|
||||
are used to test the ReLU derivates in this file.
|
||||
'''
|
||||
return np.array([[0, 0.1, 0.5, 0.9, 1.0, 10, 1e2, 0.01]], dtype=K.floatx())
|
||||
from keras.utils.test_utils import layer_test
|
||||
|
||||
|
||||
def test_leaky_relu():
|
||||
np.random.seed(1337)
|
||||
from keras.layers.advanced_activations import LeakyReLU
|
||||
inp = get_standard_values()
|
||||
for alpha in [0., .5, -1.]:
|
||||
layer = LeakyReLU(alpha=alpha)
|
||||
layer.input = K.variable(inp)
|
||||
for train in [True, False]:
|
||||
outp = K.eval(layer.get_output(train))
|
||||
assert_allclose(outp, inp)
|
||||
|
||||
layer.input = K.variable(-inp)
|
||||
for train in [True, False]:
|
||||
outp = K.eval(layer.get_output(train))
|
||||
assert_allclose(outp, -inp*alpha)
|
||||
|
||||
config = layer.get_config()
|
||||
assert config['alpha'] == alpha
|
||||
layer_test(LeakyReLU, kwargs={'alpha': alpha},
|
||||
input_shape=(2, 3, 4))
|
||||
|
||||
|
||||
def test_prelu():
|
||||
from keras.layers.advanced_activations import PReLU
|
||||
np.random.seed(1337)
|
||||
inp = get_standard_values()
|
||||
|
||||
for train in [True, False]:
|
||||
# test with custom weights
|
||||
alphas = np.random.random(inp.shape)
|
||||
layer = PReLU(weights=alphas, input_shape=inp.flatten().shape)
|
||||
# calling build here causes an error, unclear if this is a bug
|
||||
# layer.build()
|
||||
|
||||
layer.input = K.variable(inp)
|
||||
outp = K.eval(layer.get_output(train))
|
||||
assert_allclose(inp, outp)
|
||||
|
||||
layer.input = K.variable(-inp)
|
||||
outp = K.eval(layer.get_output(train))
|
||||
assert_allclose(-alphas*inp, outp)
|
||||
|
||||
# test with default weights
|
||||
layer = PReLU(input_shape=inp.flatten().shape)
|
||||
# layer.build()
|
||||
layer.input = K.variable(inp)
|
||||
outp = K.eval(layer.get_output(train))
|
||||
assert_allclose(inp, outp)
|
||||
|
||||
layer.input = K.variable(-inp)
|
||||
outp = K.eval(layer.get_output(train))
|
||||
|
||||
assert_allclose(0., alphas*outp)
|
||||
|
||||
layer.get_config()
|
||||
layer_test(PReLU, kwargs={},
|
||||
input_shape=(2, 3, 4))
|
||||
|
||||
|
||||
def test_elu():
|
||||
from keras.layers.advanced_activations import ELU
|
||||
np.random.seed(1337)
|
||||
inp = get_standard_values()
|
||||
for alpha in [0.1, .5, -1., 1.]:
|
||||
layer = ELU(alpha=alpha)
|
||||
layer.input = K.variable(inp)
|
||||
for train in [True, False]:
|
||||
outp = K.eval(layer.get_output(train))
|
||||
assert_allclose(outp, inp, rtol=1e-3)
|
||||
|
||||
layer.input = K.variable(-inp)
|
||||
for train in [True, False]:
|
||||
outp = K.eval(layer.get_output(train))
|
||||
assert_allclose(outp, alpha*(np.exp(-inp)-1.), rtol=1e-3)
|
||||
|
||||
config = layer.get_config()
|
||||
assert config['alpha'] == alpha
|
||||
for alpha in [0., .5, -1.]:
|
||||
layer_test(ELU, kwargs={'alpha': alpha},
|
||||
input_shape=(2, 3, 4))
|
||||
|
||||
|
||||
@pytest.mark.skipif(K._BACKEND == 'tensorflow',
|
||||
reason='currently not working with TensorFlow')
|
||||
def test_parametric_softplus():
|
||||
from keras.layers.advanced_activations import ParametricSoftplus
|
||||
np.random.seed(1337)
|
||||
inp = np.vstack((get_standard_values(), -get_standard_values()))
|
||||
# large values cause overflow in exp
|
||||
inp = inp[:-2]
|
||||
for alpha in [.5, -1., 1., 5]:
|
||||
for beta in [.5, -1., 1., 2]:
|
||||
layer = ParametricSoftplus(alpha_init=alpha,
|
||||
beta_init=beta,
|
||||
input_shape=inp.shape)
|
||||
layer.input = K.variable(inp)
|
||||
layer.build()
|
||||
for train in [True, False]:
|
||||
outp = K.eval(layer.get_output(train))
|
||||
assert_allclose(outp, alpha*np.log(1.+np.exp(beta*inp)),
|
||||
atol=1e-3)
|
||||
|
||||
config = layer.get_config()
|
||||
assert config['alpha_init'] == alpha
|
||||
assert config['beta_init'] == beta
|
||||
for alpha in [0., .5, -1.]:
|
||||
layer_test(ParametricSoftplus,
|
||||
kwargs={'alpha_init': 1.,
|
||||
'beta_init': -1},
|
||||
input_shape=(2, 3, 4))
|
||||
|
||||
|
||||
@pytest.mark.skipif(K._BACKEND == 'tensorflow',
|
||||
reason='currently not working with TensorFlow')
|
||||
def test_thresholded_linear():
|
||||
from keras.layers.advanced_activations import ThresholdedLinear
|
||||
np.random.seed(1337)
|
||||
inp = get_standard_values()
|
||||
for theta in [0., .5, 1.]:
|
||||
layer = ThresholdedLinear(theta=theta)
|
||||
layer.input = K.variable(inp)
|
||||
for train in [True, False]:
|
||||
outp = K.eval(layer.get_output(train))
|
||||
assert_allclose(outp, inp*(np.abs(inp) >= theta))
|
||||
|
||||
layer.input = K.variable(-inp)
|
||||
for train in [True, False]:
|
||||
outp = K.eval(layer.get_output(train))
|
||||
assert_allclose(outp, -inp*(np.abs(inp) >= theta))
|
||||
|
||||
config = layer.get_config()
|
||||
assert config['theta'] == theta
|
||||
|
||||
|
||||
@pytest.mark.skipif(K._BACKEND == 'tensorflow',
|
||||
reason='currently not working with TensorFlow')
|
||||
def test_thresholded_relu():
|
||||
from keras.layers.advanced_activations import ThresholdedReLU
|
||||
np.random.seed(1337)
|
||||
inp = get_standard_values()
|
||||
for theta in [-1, 0., .5, 1.]:
|
||||
layer = ThresholdedReLU(theta=theta)
|
||||
layer.input = K.variable(inp)
|
||||
for train in [True, False]:
|
||||
outp = K.eval(layer.get_output(train))
|
||||
assert_allclose(outp, inp*(inp > theta))
|
||||
layer_test(ThresholdedReLU, kwargs={'theta': 0.5},
|
||||
input_shape=(2, 3, 4))
|
||||
|
||||
layer.input = K.variable(-inp)
|
||||
for train in [True, False]:
|
||||
outp = K.eval(layer.get_output(train))
|
||||
assert_allclose(outp, -inp*(-inp > theta))
|
||||
|
||||
config = layer.get_config()
|
||||
assert config['theta'] == theta
|
||||
def test_srelu():
|
||||
from keras.layers.advanced_activations import SReLU
|
||||
layer_test(SReLU, kwargs={},
|
||||
input_shape=(2, 3, 4))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
"""Test keras.layers.core.Layer.__call__"""
|
||||
|
||||
import pytest
|
||||
import numpy as np
|
||||
from numpy.testing import assert_allclose
|
||||
|
||||
from keras import backend as K
|
||||
from keras.layers.core import Dense
|
||||
from keras.models import Sequential
|
||||
|
||||
|
||||
def test_layer_call():
|
||||
"""Test keras.layers.core.Layer.__call__"""
|
||||
nb_samples, input_dim, output_dim = 3, 10, 5
|
||||
layer = Dense(output_dim, input_dim=input_dim)
|
||||
W = np.asarray(K.eval(layer.W)).astype(K.floatx())
|
||||
X = K.placeholder(ndim=2)
|
||||
Y = layer(X)
|
||||
f = K.function([X], [Y])
|
||||
|
||||
x = np.ones((nb_samples, input_dim)).astype(K.floatx())
|
||||
y = f([x])[0].astype(K.floatx())
|
||||
t = np.dot(x, W).astype(K.floatx())
|
||||
assert_allclose(t, y, rtol=.2)
|
||||
|
||||
|
||||
def test_sequential_call():
|
||||
"""Test keras.models.Sequential.__call__"""
|
||||
nb_samples, input_dim, output_dim = 3, 10, 5
|
||||
model = Sequential()
|
||||
model.add(Dense(output_dim=output_dim, input_dim=input_dim))
|
||||
model.compile('sgd', 'mse')
|
||||
|
||||
# test flat model
|
||||
X = K.placeholder(ndim=2)
|
||||
Y = model(X)
|
||||
f = K.function([X], [Y])
|
||||
|
||||
x = np.ones((nb_samples, input_dim)).astype(K.floatx())
|
||||
y1 = f([x])[0].astype(K.floatx())
|
||||
y2 = model.predict(x)
|
||||
# results of __call__ should match model.predict
|
||||
assert_allclose(y1, y2)
|
||||
|
||||
# test nested model
|
||||
model2 = Sequential()
|
||||
model2.add(model)
|
||||
model2.compile('sgd', 'mse')
|
||||
|
||||
Y2 = model2(X)
|
||||
f = K.function([X], [Y2])
|
||||
|
||||
y1 = f([x])[0].astype(K.floatx())
|
||||
y2 = model2.predict(x)
|
||||
# results of __call__ should match model.predict
|
||||
assert_allclose(y1, y2)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__])
|
||||
@@ -2,151 +2,176 @@ import pytest
|
||||
import numpy as np
|
||||
from numpy.testing import assert_allclose
|
||||
|
||||
from keras.utils.test_utils import layer_test
|
||||
from keras import backend as K
|
||||
from keras.layers import convolutional
|
||||
|
||||
|
||||
def test_convolution_1d():
|
||||
nb_samples = 9
|
||||
nb_steps = 7
|
||||
input_dim = 10
|
||||
filter_length = 6
|
||||
nb_filter = 5
|
||||
nb_samples = 2
|
||||
nb_steps = 8
|
||||
input_dim = 5
|
||||
filter_length = 3
|
||||
nb_filter = 4
|
||||
|
||||
weights_in = [np.ones((nb_filter, input_dim, filter_length, 1)),
|
||||
np.ones(nb_filter)]
|
||||
for border_mode in ['valid', 'same']:
|
||||
for subsample_length in [1]:
|
||||
if border_mode == 'same' and subsample_length != 1:
|
||||
continue
|
||||
layer_test(convolutional.Convolution1D,
|
||||
kwargs={'nb_filter': nb_filter,
|
||||
'filter_length': filter_length,
|
||||
'border_mode': border_mode,
|
||||
'subsample_length': subsample_length},
|
||||
input_shape=(nb_samples, nb_steps, input_dim))
|
||||
|
||||
input = np.ones((nb_samples, nb_steps, input_dim))
|
||||
for weight in [None, weights_in]:
|
||||
for border_mode in ['valid', 'same']:
|
||||
for subsample_length in [1]:
|
||||
if border_mode == 'same' and subsample_length != 1:
|
||||
continue
|
||||
for W_regularizer in [None, 'l2']:
|
||||
for b_regularizer in [None, 'l2']:
|
||||
for act_regularizer in [None, 'l2']:
|
||||
layer = convolutional.Convolution1D(
|
||||
nb_filter, filter_length,
|
||||
weights=weight,
|
||||
border_mode=border_mode,
|
||||
W_regularizer=W_regularizer,
|
||||
b_regularizer=b_regularizer,
|
||||
activity_regularizer=act_regularizer,
|
||||
subsample_length=subsample_length,
|
||||
input_shape=(None, input_dim))
|
||||
|
||||
layer.input = K.variable(input)
|
||||
for train in [True, False]:
|
||||
out = K.eval(layer.get_output(train))
|
||||
assert input.shape[0] == out.shape[0]
|
||||
if border_mode == 'same' and subsample_length == 1:
|
||||
assert input.shape[1] == out.shape[1]
|
||||
layer.get_config()
|
||||
layer_test(convolutional.Convolution1D,
|
||||
kwargs={'nb_filter': nb_filter,
|
||||
'filter_length': filter_length,
|
||||
'border_mode': border_mode,
|
||||
'W_regularizer': 'l2',
|
||||
'b_regularizer': 'l2',
|
||||
'activity_regularizer': 'activity_l2',
|
||||
'subsample_length': subsample_length},
|
||||
input_shape=(nb_samples, nb_steps, input_dim))
|
||||
|
||||
|
||||
def test_maxpooling_1d():
|
||||
nb_samples = 9
|
||||
nb_steps = 7
|
||||
input_dim = 10
|
||||
|
||||
input = np.ones((nb_samples, nb_steps, input_dim))
|
||||
for stride in [1, 2]:
|
||||
layer = convolutional.MaxPooling1D(stride=stride,
|
||||
border_mode='valid')
|
||||
layer.input = K.variable(input)
|
||||
for train in [True, False]:
|
||||
K.eval(layer.get_output(train))
|
||||
layer.get_config()
|
||||
layer_test(convolutional.MaxPooling1D,
|
||||
kwargs={'stride': stride,
|
||||
'border_mode': 'valid'},
|
||||
input_shape=(3, 5, 4))
|
||||
|
||||
|
||||
def test_averagepooling_1d():
|
||||
nb_samples = 9
|
||||
nb_steps = 7
|
||||
input_dim = 10
|
||||
|
||||
input = np.ones((nb_samples, nb_steps, input_dim))
|
||||
for stride in [1, 2]:
|
||||
layer = convolutional.AveragePooling1D(stride=stride,
|
||||
border_mode='valid')
|
||||
layer.input = K.variable(input)
|
||||
for train in [True, False]:
|
||||
K.eval(layer.get_output(train))
|
||||
layer.get_config()
|
||||
layer_test(convolutional.AveragePooling1D,
|
||||
kwargs={'stride': stride,
|
||||
'border_mode': 'valid'},
|
||||
input_shape=(3, 5, 4))
|
||||
|
||||
|
||||
def test_convolution_2d():
|
||||
nb_samples = 8
|
||||
nb_filter = 9
|
||||
stack_size = 7
|
||||
nb_filter = 3
|
||||
stack_size = 4
|
||||
nb_row = 10
|
||||
nb_col = 6
|
||||
|
||||
input_nb_row = 11
|
||||
input_nb_col = 12
|
||||
for border_mode in ['valid', 'same']:
|
||||
for subsample in [(1, 1), (2, 2)]:
|
||||
if border_mode == 'same' and subsample != (1, 1):
|
||||
continue
|
||||
|
||||
weights_in = [np.ones((nb_filter, stack_size, nb_row, nb_col)), np.ones(nb_filter)]
|
||||
layer_test(convolutional.Convolution2D,
|
||||
kwargs={'nb_filter': nb_filter,
|
||||
'nb_row': 3,
|
||||
'nb_col': 3,
|
||||
'border_mode': border_mode,
|
||||
'subsample': subsample},
|
||||
input_shape=(nb_samples, stack_size, nb_row, nb_col))
|
||||
|
||||
input = np.ones((nb_samples, stack_size, input_nb_row, input_nb_col))
|
||||
for weight in [None, weights_in]:
|
||||
for border_mode in ['valid', 'same']:
|
||||
for subsample in [(1, 1), (2, 2)]:
|
||||
if border_mode == 'same' and subsample != (1, 1):
|
||||
continue
|
||||
for W_regularizer in [None, 'l2']:
|
||||
for b_regularizer in [None, 'l2']:
|
||||
for act_regularizer in [None, 'l2']:
|
||||
layer = convolutional.Convolution2D(
|
||||
nb_filter, nb_row, nb_col,
|
||||
weights=weight,
|
||||
border_mode=border_mode,
|
||||
W_regularizer=W_regularizer,
|
||||
b_regularizer=b_regularizer,
|
||||
activity_regularizer=act_regularizer,
|
||||
subsample=subsample,
|
||||
input_shape=(stack_size, None, None))
|
||||
|
||||
layer.input = K.variable(input)
|
||||
for train in [True, False]:
|
||||
out = K.eval(layer.get_output(train))
|
||||
if border_mode == 'same' and subsample == (1, 1):
|
||||
assert out.shape[2:] == input.shape[2:]
|
||||
layer.get_config()
|
||||
layer_test(convolutional.Convolution2D,
|
||||
kwargs={'nb_filter': nb_filter,
|
||||
'nb_row': 3,
|
||||
'nb_col': 3,
|
||||
'border_mode': border_mode,
|
||||
'W_regularizer': 'l2',
|
||||
'b_regularizer': 'l2',
|
||||
'activity_regularizer': 'activity_l2',
|
||||
'subsample': subsample},
|
||||
input_shape=(nb_samples, stack_size, nb_row, nb_col))
|
||||
|
||||
|
||||
def test_maxpooling_2d():
|
||||
nb_samples = 9
|
||||
stack_size = 7
|
||||
input_nb_row = 11
|
||||
input_nb_col = 12
|
||||
pool_size = (3, 3)
|
||||
|
||||
input = np.ones((nb_samples, stack_size, input_nb_row, input_nb_col))
|
||||
for strides in [(1, 1), (2, 2)]:
|
||||
layer = convolutional.MaxPooling2D(strides=strides,
|
||||
border_mode='valid',
|
||||
pool_size=pool_size)
|
||||
layer.input = K.variable(input)
|
||||
for train in [True, False]:
|
||||
K.eval(layer.get_output(train))
|
||||
layer.get_config()
|
||||
layer_test(convolutional.MaxPooling2D,
|
||||
kwargs={'strides': strides,
|
||||
'border_mode': 'valid',
|
||||
'pool_size': pool_size},
|
||||
input_shape=(3, 4, 11, 12))
|
||||
|
||||
|
||||
def test_averagepooling_2d():
|
||||
nb_samples = 9
|
||||
stack_size = 7
|
||||
input_nb_row = 11
|
||||
input_nb_col = 12
|
||||
pool_size = (3, 3)
|
||||
|
||||
input = np.ones((nb_samples, stack_size, input_nb_row, input_nb_col))
|
||||
for strides in [(1, 1), (2, 2)]:
|
||||
layer = convolutional.AveragePooling2D(strides=strides,
|
||||
border_mode='valid',
|
||||
pool_size=pool_size)
|
||||
layer.input = K.variable(input)
|
||||
for train in [True, False]:
|
||||
K.eval(layer.get_output(train))
|
||||
layer.get_config()
|
||||
for border_mode in ['valid', 'same']:
|
||||
for pool_size in [(2, 2), (3, 3), (4, 4), (5, 5)]:
|
||||
for strides in [(1, 1), (2, 2)]:
|
||||
layer_test(convolutional.MaxPooling2D,
|
||||
kwargs={'strides': strides,
|
||||
'border_mode': border_mode,
|
||||
'pool_size': pool_size},
|
||||
input_shape=(3, 4, 11, 12))
|
||||
|
||||
|
||||
@pytest.mark.skipif(K._BACKEND != 'theano', reason="Requires Theano backend")
|
||||
def test_convolution_3d():
|
||||
nb_samples = 2
|
||||
nb_filter = 5
|
||||
stack_size = 4
|
||||
kernel_dim1 = 2
|
||||
kernel_dim2 = 3
|
||||
kernel_dim3 = 1
|
||||
|
||||
input_len_dim1 = 10
|
||||
input_len_dim2 = 11
|
||||
input_len_dim3 = 12
|
||||
|
||||
for border_mode in ['same', 'valid']:
|
||||
for subsample in [(1, 1, 1), (2, 2, 2)]:
|
||||
if border_mode == 'same' and subsample != (1, 1, 1):
|
||||
continue
|
||||
|
||||
layer_test(convolutional.Convolution3D,
|
||||
kwargs={'nb_filter': nb_filter,
|
||||
'kernel_dim1': kernel_dim1,
|
||||
'kernel_dim2': kernel_dim2,
|
||||
'kernel_dim3': kernel_dim3,
|
||||
'border_mode': border_mode,
|
||||
'subsample': subsample},
|
||||
input_shape=(nb_samples, stack_size,
|
||||
input_len_dim1, input_len_dim2, input_len_dim3))
|
||||
|
||||
layer_test(convolutional.Convolution3D,
|
||||
kwargs={'nb_filter': nb_filter,
|
||||
'kernel_dim1': kernel_dim1,
|
||||
'kernel_dim2': kernel_dim2,
|
||||
'kernel_dim3': kernel_dim3,
|
||||
'border_mode': border_mode,
|
||||
'W_regularizer': 'l2',
|
||||
'b_regularizer': 'l2',
|
||||
'activity_regularizer': 'activity_l2',
|
||||
'subsample': subsample},
|
||||
input_shape=(nb_samples, stack_size,
|
||||
input_len_dim1, input_len_dim2, input_len_dim3))
|
||||
|
||||
|
||||
@pytest.mark.skipif(K._BACKEND != 'theano', reason="Requires Theano backend")
|
||||
def test_maxpooling_3d():
|
||||
pool_size = (3, 3, 3)
|
||||
|
||||
for strides in [(1, 1, 1), (2, 2, 2)]:
|
||||
layer_test(convolutional.MaxPooling3D,
|
||||
kwargs={'strides': strides,
|
||||
'border_mode': 'valid',
|
||||
'pool_size': pool_size},
|
||||
input_shape=(3, 4, 11, 12, 10))
|
||||
|
||||
|
||||
@pytest.mark.skipif(K._BACKEND != 'theano', reason="Requires Theano backend")
|
||||
def test_averagepooling_3d():
|
||||
pool_size = (3, 3, 3)
|
||||
|
||||
for strides in [(1, 1, 1), (2, 2, 2)]:
|
||||
layer_test(convolutional.AveragePooling3D,
|
||||
kwargs={'strides': strides,
|
||||
'border_mode': 'valid',
|
||||
'pool_size': pool_size},
|
||||
input_shape=(3, 4, 11, 12, 10))
|
||||
|
||||
|
||||
def test_zero_padding_2d():
|
||||
@@ -156,30 +181,56 @@ def test_zero_padding_2d():
|
||||
input_nb_col = 12
|
||||
|
||||
input = np.ones((nb_samples, stack_size, input_nb_row, input_nb_col))
|
||||
|
||||
# basic test
|
||||
layer_test(convolutional.ZeroPadding2D,
|
||||
kwargs={'padding': (2, 2)},
|
||||
input_shape=input.shape)
|
||||
|
||||
# correctness test
|
||||
layer = convolutional.ZeroPadding2D(padding=(2, 2))
|
||||
layer.input = K.variable(input)
|
||||
for train in [True, False]:
|
||||
out = K.eval(layer.get_output(train))
|
||||
for offset in [0, 1, -1, -2]:
|
||||
assert_allclose(out[:, :, offset, :], 0.)
|
||||
assert_allclose(out[:, :, :, offset], 0.)
|
||||
assert_allclose(out[:, :, 2:-2, 2:-2], 1.)
|
||||
layer.set_input(K.variable(input), shape=input.shape)
|
||||
|
||||
out = K.eval(layer.output)
|
||||
for offset in [0, 1, -1, -2]:
|
||||
assert_allclose(out[:, :, offset, :], 0.)
|
||||
assert_allclose(out[:, :, :, offset], 0.)
|
||||
assert_allclose(out[:, :, 2:-2, 2:-2], 1.)
|
||||
layer.get_config()
|
||||
|
||||
|
||||
@pytest.mark.skipif(K._BACKEND != 'theano', reason="Requires Theano backend")
|
||||
def test_zero_padding_3d():
|
||||
nb_samples = 9
|
||||
stack_size = 7
|
||||
input_len_dim1 = 10
|
||||
input_len_dim2 = 11
|
||||
input_len_dim3 = 12
|
||||
|
||||
input = np.ones((nb_samples, stack_size, input_len_dim1,
|
||||
input_len_dim2, input_len_dim3))
|
||||
|
||||
# basic test
|
||||
layer_test(convolutional.ZeroPadding3D,
|
||||
kwargs={'padding': (2, 2, 2)},
|
||||
input_shape=input.shape)
|
||||
|
||||
# correctness test
|
||||
layer = convolutional.ZeroPadding3D(padding=(2, 2, 2))
|
||||
layer.set_input(K.variable(input), shape=input.shape)
|
||||
out = K.eval(layer.output)
|
||||
for offset in [0, 1, -1, -2]:
|
||||
assert_allclose(out[:, :, offset, :, :], 0.)
|
||||
assert_allclose(out[:, :, :, offset, :], 0.)
|
||||
assert_allclose(out[:, :, :, :, offset], 0.)
|
||||
assert_allclose(out[:, :, 2:-2, 2:-2, 2:-2], 1.)
|
||||
layer.get_config()
|
||||
|
||||
|
||||
def test_upsampling_1d():
|
||||
nb_samples = 9
|
||||
nb_steps = 7
|
||||
input_dim = 10
|
||||
|
||||
input = np.ones((nb_samples, nb_steps, input_dim))
|
||||
for length in [2, 3, 9]:
|
||||
layer = convolutional.UpSampling1D(length=length)
|
||||
layer.input = K.variable(input)
|
||||
for train in [True, False]:
|
||||
out = K.eval(layer.get_output(train))
|
||||
assert out.shape[1] == length * nb_steps
|
||||
layer.get_config()
|
||||
layer_test(convolutional.UpSampling1D,
|
||||
kwargs={'length': 2},
|
||||
input_shape=(3, 5, 4))
|
||||
|
||||
|
||||
def test_upsampling_2d():
|
||||
@@ -198,34 +249,76 @@ def test_upsampling_2d():
|
||||
|
||||
for length_row in [2, 3, 9]:
|
||||
for length_col in [2, 3, 9]:
|
||||
layer = convolutional.UpSampling2D(
|
||||
size=(length_row, length_col),
|
||||
input_shape=input.shape[1:],
|
||||
layer = convolutional.UpSampling2D(
|
||||
size=(length_row, length_col),
|
||||
dim_ordering=dim_ordering)
|
||||
layer.set_input(K.variable(input), shape=input.shape)
|
||||
|
||||
out = K.eval(layer.output)
|
||||
if dim_ordering == 'th':
|
||||
assert out.shape[2] == length_row * input_nb_row
|
||||
assert out.shape[3] == length_col * input_nb_col
|
||||
else: # tf
|
||||
assert out.shape[1] == length_row * input_nb_row
|
||||
assert out.shape[2] == length_col * input_nb_col
|
||||
|
||||
# compare with numpy
|
||||
if dim_ordering == 'th':
|
||||
expected_out = np.repeat(input, length_row, axis=2)
|
||||
expected_out = np.repeat(expected_out, length_col, axis=3)
|
||||
else: # tf
|
||||
expected_out = np.repeat(input, length_row, axis=1)
|
||||
expected_out = np.repeat(expected_out, length_col, axis=2)
|
||||
|
||||
assert_allclose(out, expected_out)
|
||||
|
||||
|
||||
@pytest.mark.skipif(K._BACKEND != 'theano', reason="Requires Theano backend")
|
||||
def test_upsampling_3d():
|
||||
nb_samples = 9
|
||||
stack_size = 7
|
||||
input_len_dim1 = 10
|
||||
input_len_dim2 = 11
|
||||
input_len_dim3 = 12
|
||||
|
||||
for dim_ordering in ['th', 'tf']:
|
||||
if dim_ordering == 'th':
|
||||
input = np.random.rand(nb_samples, stack_size, input_len_dim1, input_len_dim2,
|
||||
input_len_dim3)
|
||||
else: # tf
|
||||
input = np.random.rand(nb_samples, input_len_dim1, input_len_dim2, input_len_dim3,
|
||||
stack_size)
|
||||
for length_dim1 in [2, 3, 9]:
|
||||
for length_dim2 in [2, 3, 9]:
|
||||
for length_dim3 in [2, 3, 9]:
|
||||
layer = convolutional.UpSampling3D(
|
||||
size=(length_dim1, length_dim2, length_dim3),
|
||||
dim_ordering=dim_ordering)
|
||||
layer.input = K.variable(input)
|
||||
for train in [True, False]:
|
||||
out = K.eval(layer.get_output(train))
|
||||
if dim_ordering == 'th':
|
||||
assert out.shape[2] == length_row * input_nb_row
|
||||
assert out.shape[3] == length_col * input_nb_col
|
||||
else: # tf
|
||||
assert out.shape[1] == length_row * input_nb_row
|
||||
assert out.shape[2] == length_col * input_nb_col
|
||||
layer.set_input(K.variable(input), shape=input.shape)
|
||||
|
||||
# compare with numpy
|
||||
if dim_ordering == 'th':
|
||||
expected_out = np.repeat(input, length_row, axis=2)
|
||||
expected_out = np.repeat(expected_out, length_col,
|
||||
axis=3)
|
||||
else: # tf
|
||||
expected_out = np.repeat(input, length_row, axis=1)
|
||||
expected_out = np.repeat(expected_out, length_col,
|
||||
axis=2)
|
||||
out = K.eval(layer.output)
|
||||
if dim_ordering == 'th':
|
||||
assert out.shape[2] == length_dim1 * input_len_dim1
|
||||
assert out.shape[3] == length_dim2 * input_len_dim2
|
||||
assert out.shape[4] == length_dim3 * input_len_dim3
|
||||
else: # tf
|
||||
assert out.shape[1] == length_dim1 * input_len_dim1
|
||||
assert out.shape[2] == length_dim2 * input_len_dim2
|
||||
assert out.shape[3] == length_dim3 * input_len_dim3
|
||||
|
||||
assert_allclose(out, expected_out)
|
||||
# compare with numpy
|
||||
if dim_ordering == 'th':
|
||||
expected_out = np.repeat(input, length_dim1, axis=2)
|
||||
expected_out = np.repeat(expected_out, length_dim2, axis=3)
|
||||
expected_out = np.repeat(expected_out, length_dim3, axis=4)
|
||||
else: # tf
|
||||
expected_out = np.repeat(input, length_dim1, axis=1)
|
||||
expected_out = np.repeat(expected_out, length_dim2, axis=2)
|
||||
expected_out = np.repeat(expected_out, length_dim3, axis=3)
|
||||
|
||||
layer.get_config()
|
||||
assert_allclose(out, expected_out)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__])
|
||||
# pytest.main([__file__])
|
||||
test_convolution_3d()
|
||||
|
||||
+195
-206
@@ -1,256 +1,245 @@
|
||||
import pytest
|
||||
import numpy as np
|
||||
from keras.models import Sequential
|
||||
from numpy.testing import assert_allclose
|
||||
|
||||
from keras import backend as K
|
||||
from keras.layers import core
|
||||
from keras.layers import containers
|
||||
from keras.utils.test_utils import layer_test
|
||||
|
||||
|
||||
def test_input_output():
|
||||
nb_samples = 10
|
||||
input_dim = 5
|
||||
layer = core.Layer()
|
||||
|
||||
# Once an input is provided, it should be reachable through the
|
||||
# appropriate getters
|
||||
input = np.ones((nb_samples, input_dim))
|
||||
layer.input = K.variable(input)
|
||||
for train in [True, False]:
|
||||
assert_allclose(K.eval(layer.get_input(train)), input)
|
||||
assert_allclose(K.eval(layer.get_output(train)), input)
|
||||
|
||||
|
||||
def test_connections():
|
||||
nb_samples = 10
|
||||
input_dim = 5
|
||||
layer1 = core.Layer()
|
||||
layer2 = core.Layer()
|
||||
|
||||
input = np.ones((nb_samples, input_dim))
|
||||
layer1.input = K.variable(input)
|
||||
|
||||
# After connecting, input of layer1 should be passed through
|
||||
layer2.set_previous(layer1)
|
||||
for train in [True, False]:
|
||||
assert_allclose(K.eval(layer2.get_input(train)), input)
|
||||
assert_allclose(K.eval(layer2.get_output(train)), input)
|
||||
|
||||
|
||||
def test_base():
|
||||
layer = core.Layer()
|
||||
_runner(layer)
|
||||
|
||||
|
||||
def test_masked():
|
||||
layer = core.MaskedLayer()
|
||||
_runner(layer)
|
||||
def test_masking():
|
||||
layer_test(core.Masking,
|
||||
kwargs={},
|
||||
input_shape=(3, 2, 3))
|
||||
|
||||
|
||||
def test_merge():
|
||||
layer_1 = core.Layer()
|
||||
layer_2 = core.Layer()
|
||||
layer_1.set_input_shape((None,))
|
||||
layer_2.set_input_shape((None,))
|
||||
layer = core.Merge([layer_1, layer_2])
|
||||
_runner(layer)
|
||||
from keras.layers import Input, merge
|
||||
from keras.models import Model
|
||||
|
||||
# test modes: 'sum', 'mul', 'concat', 'ave', 'cos', 'dot'.
|
||||
input_shapes = [(3, 2), (3, 2)]
|
||||
inputs = [np.random.random(shape) for shape in input_shapes]
|
||||
|
||||
# test graph API
|
||||
for mode in ['sum', 'mul', 'concat', 'ave', 'cos', 'dot']:
|
||||
print(mode)
|
||||
input_a = Input(shape=input_shapes[0][1:])
|
||||
input_b = Input(shape=input_shapes[1][1:])
|
||||
merged = merge([input_a, input_b], mode='sum')
|
||||
model = Model([input_a, input_b], merged)
|
||||
model.compile('rmsprop', 'mse')
|
||||
|
||||
expected_output_shape = model.get_output_shape_for(input_shapes)
|
||||
actual_output_shape = model.predict(inputs).shape
|
||||
assert expected_output_shape == actual_output_shape
|
||||
|
||||
config = model.get_config()
|
||||
model = Model.from_config(config)
|
||||
model.compile('rmsprop', 'mse')
|
||||
|
||||
# test lambda with output_shape lambda
|
||||
input_a = Input(shape=input_shapes[0][1:])
|
||||
input_b = Input(shape=input_shapes[1][1:])
|
||||
merged = merge([input_a, input_b],
|
||||
mode=lambda tup: K.concatenate([tup[0], tup[1]]),
|
||||
output_shape=lambda tup: (tup[0][:-1],) + (tup[0][-1] + tup[1][-1],))
|
||||
expected_output_shape = model.get_output_shape_for(input_shapes)
|
||||
actual_output_shape = model.predict(inputs).shape
|
||||
assert expected_output_shape == actual_output_shape
|
||||
|
||||
config = model.get_config()
|
||||
model = Model.from_config(config)
|
||||
model.compile('rmsprop', 'mse')
|
||||
|
||||
# test function with output_shape function
|
||||
def fn_mode(tup):
|
||||
x, y = tup
|
||||
return K.concatenate([x, y])
|
||||
|
||||
def fn_output_shape(tup):
|
||||
s1, s2 = tup
|
||||
return (s1[:-1],) + (s1[-1] + s2[-1],)
|
||||
|
||||
input_a = Input(shape=input_shapes[0][1:])
|
||||
input_b = Input(shape=input_shapes[1][1:])
|
||||
merged = merge([input_a, input_b],
|
||||
mode=fn_mode,
|
||||
output_shape=fn_output_shape)
|
||||
expected_output_shape = model.get_output_shape_for(input_shapes)
|
||||
actual_output_shape = model.predict(inputs).shape
|
||||
assert expected_output_shape == actual_output_shape
|
||||
|
||||
config = model.get_config()
|
||||
model = Model.from_config(config)
|
||||
model.compile('rmsprop', 'mse')
|
||||
|
||||
|
||||
def test_dropout():
|
||||
layer = core.Dropout(0.5)
|
||||
_runner(layer)
|
||||
layer_test(core.Dropout,
|
||||
kwargs={'p': 0.5},
|
||||
input_shape=(3, 2))
|
||||
|
||||
|
||||
def test_activation():
|
||||
layer = core.Activation('linear')
|
||||
_runner(layer)
|
||||
# with string argument
|
||||
layer_test(core.Activation,
|
||||
kwargs={'activation': 'relu'},
|
||||
input_shape=(3, 2))
|
||||
|
||||
# with function argument
|
||||
layer_test(core.Activation,
|
||||
kwargs={'activation': K.relu},
|
||||
input_shape=(3, 2))
|
||||
|
||||
|
||||
def test_reshape():
|
||||
layer = core.Reshape(dims=(10, 10))
|
||||
_runner(layer)
|
||||
layer_test(core.Reshape,
|
||||
kwargs={'target_shape': (8, 1)},
|
||||
input_shape=(3, 2, 4))
|
||||
|
||||
|
||||
def test_permute():
|
||||
layer_test(core.Permute,
|
||||
kwargs={'dims': (2, 1)},
|
||||
input_shape=(3, 2, 4))
|
||||
|
||||
|
||||
def test_flatten():
|
||||
layer = core.Flatten()
|
||||
_runner(layer)
|
||||
layer_test(core.Flatten,
|
||||
kwargs={},
|
||||
input_shape=(3, 2, 4))
|
||||
|
||||
|
||||
def test_repeat_vector():
|
||||
layer = core.RepeatVector(10)
|
||||
_runner(layer)
|
||||
layer_test(core.RepeatVector,
|
||||
kwargs={'n': 3},
|
||||
input_shape=(3, 2))
|
||||
|
||||
|
||||
def test_lambda():
|
||||
from keras.utils.layer_utils import layer_from_config
|
||||
Lambda = core.Lambda
|
||||
|
||||
layer_test(Lambda,
|
||||
kwargs={'function': lambda x: x + 1},
|
||||
input_shape=(3, 2))
|
||||
|
||||
# test serialization with function
|
||||
def f(x):
|
||||
return x + 1
|
||||
|
||||
ld = Lambda(f)
|
||||
config = ld.get_config()
|
||||
ld = layer_from_config({'class_name': 'Lambda', 'config': config})
|
||||
|
||||
ld = Lambda(lambda x: K.concatenate([K.square(x), x]),
|
||||
output_shape=lambda s: tuple(list(s)[:-1] + [2 * s[-1]]))
|
||||
config = ld.get_config()
|
||||
ld = Lambda.from_config(config)
|
||||
|
||||
# test serialization with output_shape function
|
||||
def f(x):
|
||||
return K.concatenate([K.square(x), x])
|
||||
|
||||
def f_shape(s):
|
||||
return tuple(list(s)[:-1] + [2 * s[-1]])
|
||||
|
||||
ld = Lambda(f, output_shape=f_shape)
|
||||
config = ld.get_config()
|
||||
ld = layer_from_config({'class_name': 'Lambda', 'config': config})
|
||||
|
||||
|
||||
def test_dense():
|
||||
layer = core.Dense(10, input_shape=(10,))
|
||||
_runner(layer)
|
||||
from keras import regularizers
|
||||
from keras import constraints
|
||||
|
||||
layer_test(core.Dense,
|
||||
kwargs={'output_dim': 3},
|
||||
input_shape=(3, 2))
|
||||
|
||||
layer_test(core.Dense,
|
||||
kwargs={'output_dim': 3,
|
||||
'W_regularizer': regularizers.l2(0.01),
|
||||
'b_regularizer': regularizers.l1(0.01),
|
||||
'activity_regularizer': regularizers.activity_l2(0.01),
|
||||
'W_constraint': constraints.MaxNorm(1),
|
||||
'b_constraint': constraints.MaxNorm(1)},
|
||||
input_shape=(3, 2))
|
||||
|
||||
|
||||
def test_act_reg():
|
||||
layer = core.ActivityRegularization(0.5, 0.5)
|
||||
_runner(layer)
|
||||
def test_activity_regularization():
|
||||
from keras.engine import Input, Model
|
||||
|
||||
layer = core.ActivityRegularization(l1=0.01, l2=0.01)
|
||||
|
||||
def test_time_dist_dense():
|
||||
layer = core.TimeDistributedDense(10, input_shape=(None, 10))
|
||||
_runner(layer)
|
||||
# test in functional API
|
||||
x = Input(shape=(3,))
|
||||
z = core.Dense(2)(x)
|
||||
y = layer(z)
|
||||
model = Model(input=x, output=y)
|
||||
model.compile('rmsprop', 'mse', mode='FAST_COMPILE')
|
||||
|
||||
model.predict(np.random.random((2, 3)))
|
||||
|
||||
def test_time_dist_merge():
|
||||
layer = core.TimeDistributedMerge()
|
||||
_runner(layer)
|
||||
|
||||
|
||||
def test_highway():
|
||||
layer = core.Highway(input_shape=(10,))
|
||||
_runner(layer)
|
||||
|
||||
|
||||
def test_autoencoder():
|
||||
layer_1 = core.Layer()
|
||||
layer_2 = core.Layer()
|
||||
|
||||
layer = core.AutoEncoder(layer_1, layer_2)
|
||||
_runner(layer)
|
||||
|
||||
|
||||
def test_autoencoder_advanced():
|
||||
encoder = containers.Sequential([core.Dense(5, input_shape=(10,))])
|
||||
decoder = containers.Sequential([core.Dense(10, input_shape=(5,))])
|
||||
X_train = np.random.random((100, 10))
|
||||
X_test = np.random.random((100, 10))
|
||||
|
||||
model = Sequential()
|
||||
model.add(core.Dense(output_dim=10, input_dim=10))
|
||||
autoencoder = core.AutoEncoder(encoder=encoder, decoder=decoder,
|
||||
output_reconstruction=True)
|
||||
model.add(autoencoder)
|
||||
|
||||
# training the autoencoder:
|
||||
model.compile(optimizer='sgd', loss='mse')
|
||||
assert autoencoder.output_reconstruction
|
||||
|
||||
model.fit(X_train, X_train, nb_epoch=1, batch_size=32)
|
||||
|
||||
# predicting compressed representations of inputs:
|
||||
autoencoder.output_reconstruction = False # the autoencoder has to be recompiled after modifying this property
|
||||
assert not autoencoder.output_reconstruction
|
||||
model.compile(optimizer='sgd', loss='mse')
|
||||
representations = model.predict(X_test)
|
||||
assert representations.shape == (100, 5)
|
||||
|
||||
# the model is still trainable, although it now expects compressed representations as targets:
|
||||
model.fit(X_test, representations, nb_epoch=1, batch_size=32)
|
||||
|
||||
# to keep training against the original inputs, just switch back output_reconstruction to True:
|
||||
autoencoder.output_reconstruction = True
|
||||
model.compile(optimizer='sgd', loss='mse')
|
||||
model.fit(X_train, X_train, nb_epoch=1)
|
||||
|
||||
reconstructions = model.predict(X_test)
|
||||
assert reconstructions.shape == (100, 10)
|
||||
# test serialization
|
||||
model_config = model.get_config()
|
||||
model = Model.from_config(model_config)
|
||||
model.compile('rmsprop', 'mse')
|
||||
|
||||
|
||||
def test_maxout_dense():
|
||||
layer = core.MaxoutDense(10, 10, input_shape=(20,))
|
||||
_runner(layer)
|
||||
from keras import regularizers
|
||||
from keras import constraints
|
||||
|
||||
layer_test(core.MaxoutDense,
|
||||
kwargs={'output_dim': 3},
|
||||
input_shape=(3, 2))
|
||||
|
||||
layer_test(core.MaxoutDense,
|
||||
kwargs={'output_dim': 3,
|
||||
'W_regularizer': regularizers.l2(0.01),
|
||||
'b_regularizer': regularizers.l1(0.01),
|
||||
'activity_regularizer': regularizers.activity_l2(0.01),
|
||||
'W_constraint': constraints.MaxNorm(1),
|
||||
'b_constraint': constraints.MaxNorm(1)},
|
||||
input_shape=(3, 2))
|
||||
|
||||
|
||||
def test_naming():
|
||||
layer = core.Dense(2, input_dim=2)
|
||||
assert layer.name == 'dense'
|
||||
def test_highway():
|
||||
from keras import regularizers
|
||||
from keras import constraints
|
||||
|
||||
model = Sequential()
|
||||
model.add(core.Dense(2, input_dim=2, name='my_dense'))
|
||||
model.add(core.Dense(2, name='my_dense'))
|
||||
layer_test(core.Highway,
|
||||
kwargs={},
|
||||
input_shape=(3, 2))
|
||||
|
||||
assert model.layers[0].name == 'my_dense'
|
||||
assert model.layers[1].name == 'my_dense'
|
||||
|
||||
model.compile(optimizer='rmsprop', loss='mse')
|
||||
model.train_on_batch(np.random.random((2, 2)), np.random.random((2, 2)))
|
||||
layer_test(core.Highway,
|
||||
kwargs={'W_regularizer': regularizers.l2(0.01),
|
||||
'b_regularizer': regularizers.l1(0.01),
|
||||
'activity_regularizer': regularizers.activity_l2(0.01),
|
||||
'W_constraint': constraints.MaxNorm(1),
|
||||
'b_constraint': constraints.MaxNorm(1)},
|
||||
input_shape=(3, 2))
|
||||
|
||||
|
||||
def test_sequences():
|
||||
'''Test masking sequences with zeroes as padding'''
|
||||
# integer inputs, one per timestep, like embeddings
|
||||
layer = core.Masking()
|
||||
func = K.function([layer.get_input(True)], [layer.get_output_mask()])
|
||||
input_data = np.array([[[1], [2], [3], [0]],
|
||||
[[0], [4], [5], [0]]], dtype=np.int32)
|
||||
def test_timedistributeddense():
|
||||
from keras import regularizers
|
||||
from keras import constraints
|
||||
|
||||
# This is the expected output mask, one dimension less
|
||||
expected = np.array([[1, 1, 1, 0], [0, 1, 1, 0]])
|
||||
layer_test(core.TimeDistributedDense,
|
||||
kwargs={'output_dim': 2, 'input_length': 2},
|
||||
input_shape=(3, 2, 3))
|
||||
|
||||
# get mask for this input
|
||||
output = func([input_data])[0]
|
||||
assert np.all(output == expected), 'Output not as expected'
|
||||
layer_test(core.TimeDistributedDense,
|
||||
kwargs={'output_dim': 3,
|
||||
'W_regularizer': regularizers.l2(0.01),
|
||||
'b_regularizer': regularizers.l1(0.01),
|
||||
'activity_regularizer': regularizers.activity_l2(0.01),
|
||||
'W_constraint': constraints.MaxNorm(1),
|
||||
'b_constraint': constraints.MaxNorm(1)},
|
||||
input_shape=(3, 2, 3))
|
||||
|
||||
|
||||
def test_non_zero():
|
||||
'''Test masking with non-zero mask value'''
|
||||
layer = core.Masking(5)
|
||||
func = K.function([layer.input], [layer.get_output_mask()])
|
||||
input_data = np.array([[[1, 1], [2, 1], [3, 1], [5, 5]],
|
||||
[[1, 5], [5, 0], [0, 0], [0, 0]]],
|
||||
dtype=np.int32)
|
||||
output = func([input_data])[0]
|
||||
expected = np.array([[1, 1, 1, 0], [1, 1, 1, 1]])
|
||||
assert np.all(output == expected), 'Output not as expected'
|
||||
|
||||
|
||||
def test_non_zero_output():
|
||||
'''Test output of masking layer with non-zero mask value'''
|
||||
layer = core.Masking(5)
|
||||
func = K.function([layer.input], [layer.get_output()])
|
||||
|
||||
input_data = np.array([[[1, 1], [2, 1], [3, 1], [5, 5]],
|
||||
[[1, 5], [5, 0], [0, 0], [0, 0]]],
|
||||
dtype=np.int32)
|
||||
output = func([input_data])[0]
|
||||
expected = np.array([[[1, 1], [2, 1], [3, 1], [0, 0]],
|
||||
[[1, 5], [5, 0], [0, 0], [0, 0]]])
|
||||
assert np.all(output == expected), 'Output not as expected'
|
||||
|
||||
|
||||
def _runner(layer):
|
||||
assert isinstance(layer, core.Layer)
|
||||
layer.build()
|
||||
conf = layer.get_config()
|
||||
assert (type(conf) == dict)
|
||||
|
||||
param = layer.get_params()
|
||||
# Typically a list or a tuple, but may be any iterable
|
||||
assert hasattr(param, '__iter__')
|
||||
|
||||
# Test the setter for the trainable attribute
|
||||
layer.trainable = True
|
||||
layer.trainable = False
|
||||
|
||||
def test_siamese_all():
|
||||
right_input_layer = core.Dense(7, input_dim=3)
|
||||
left_input_layer = core.Dense(7, input_dim=3)
|
||||
|
||||
shared_layer = core.Dense(5,input_dim=7)
|
||||
for mode in ['sum', 'mul', 'ave', 'concat']:
|
||||
siamese_layer = core.Siamese(shared_layer, [left_input_layer, right_input_layer], merge_mode=mode)
|
||||
siamese_layer.output_shape
|
||||
siamese_layer.get_output()
|
||||
|
||||
@pytest.mark.skipif(K._BACKEND == 'tensorflow',
|
||||
reason='currently not working with TensorFlow')
|
||||
def test_siamese_theano_only():
|
||||
right_input_layer = core.Dense(7, input_dim=3)
|
||||
left_input_layer = core.Dense(7, input_dim=3)
|
||||
|
||||
shared_layer = core.Dense(5,input_dim=7)
|
||||
|
||||
for mode in ['dot', 'cos']:
|
||||
siamese_layer = core.Siamese(shared_layer, [left_input_layer, right_input_layer], merge_mode=mode,
|
||||
dot_axes=([1], [1]))
|
||||
siamese_layer.output_shape
|
||||
siamese_layer.get_output()
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__])
|
||||
|
||||
@@ -1,30 +1,15 @@
|
||||
import pytest
|
||||
import numpy as np
|
||||
from numpy.testing import assert_allclose
|
||||
from keras.models import Sequential
|
||||
from keras.layers.core import Dense, Activation, Flatten
|
||||
from keras.utils.test_utils import layer_test
|
||||
from keras.layers.embeddings import Embedding
|
||||
from keras.constraints import unitnorm
|
||||
from keras import backend as K
|
||||
import keras.backend as K
|
||||
|
||||
|
||||
X1 = np.array([[1], [2]], dtype='int32')
|
||||
W1 = np.array([[0.1, 0.2], [0.3, 0.4], [0.5, 0.6]], dtype='float32')
|
||||
|
||||
|
||||
def test_unitnorm_constraint():
|
||||
lookup = Sequential()
|
||||
lookup.add(Embedding(3, 2, weights=[W1],
|
||||
W_constraint=unitnorm(),
|
||||
input_length=1))
|
||||
lookup.add(Flatten())
|
||||
lookup.add(Dense(1))
|
||||
lookup.add(Activation('sigmoid'))
|
||||
lookup.compile(loss='binary_crossentropy', optimizer='sgd',
|
||||
class_mode='binary')
|
||||
lookup.train_on_batch(X1, np.array([[1], [0]], dtype='int32'))
|
||||
norm = np.linalg.norm(K.get_value(lookup.trainable_weights[0]), axis=0)
|
||||
assert_allclose(norm, np.ones_like(norm).astype('float32'), rtol=1e-05)
|
||||
def test_embedding():
|
||||
layer_test(Embedding,
|
||||
kwargs={'output_dim': 4., 'input_dim': 10, 'input_length': 2},
|
||||
input_shape=(3, 2),
|
||||
input_dtype='int32',
|
||||
expected_output_dtype=K.floatx())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais
Referência em uma Nova Issue
Bloquear um usuário