Skip to content
This repository has been archived by the owner on Apr 11, 2021. It is now read-only.

Commit

Permalink
updated core layer and documentation 😃
Browse files Browse the repository at this point in the history
  • Loading branch information
codekansas committed Jan 11, 2017
1 parent dd4ff73 commit bce05b2
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 41 deletions.
2 changes: 1 addition & 1 deletion docs/layers/core.md
Expand Up @@ -16,7 +16,7 @@ An alternative to Keras [Dropout](https://keras.io/layers/core/#dropout) which s
gandlf.layers.BatchSimilarity(similarity='exp_l1')
````

Calculates the minibatch similarities, a trick introduced in [Improved Techniques for Training GANs](https://arxiv.org/abs/1606.03498). These similarities can be added as features for the existing layer by using a Merge layer. The layer outputs a Tensor with shape `(batch_size, num_similarities)` for 2D tensors, `(batch_size, None, num_similarities)` for 3D Tensors, and so on.
Calculates the minibatch similarities, a trick introduced in [Improved Techniques for Training GANs](https://arxiv.org/abs/1606.03498). These similarities can be added as features for the existing layer by using a Merge layer. The layer takes as input a 2D Tensor with shape `(batch_size, num_features)` and outputs a Tensor with shape `(batch_size, num_similarities)`, where `num_similarities` is the total number of computed similarities.

In order to make this layer linear time with respect to the batch size, instead of doing a pairwise comparison between each pair of samples in the batch, for each sample a random sample is uniformly selected with which to do pairwise comparison.

Expand Down
2 changes: 1 addition & 1 deletion docs/layers/wrappers.md
Expand Up @@ -8,7 +8,7 @@ gandlf.layers.Residual(layer, merge_mode='sum')

Applies a residual to any Keras layer or model, so long as it's inputs are the same dimension as its outputs. Useful for implementing residual architectures.

The provided `layer` has the have the same input and output dimensions. Given an input `x`, the output is:
The provided `layer` has to have the same input and output dimensions. Given an input `x`, the output is:

````python
output = merge_mode(x, layer(x))
Expand Down
14 changes: 7 additions & 7 deletions examples/mnist_rnn_gan.py
Expand Up @@ -87,7 +87,7 @@ def build_discriminator(mode):
"""Builds the discriminator model."""

image = keras.layers.Input((28, 28, 1), name='real_data')
flat = keras.layers.Reshape((28, 28))(image)
rnn_input = keras.layers.Reshape((28, 28))(image)

rnn_1 = keras.layers.LSTM(128, return_sequences=True)
rnn_2 = keras.layers.LSTM(1, return_sequences=False, name='pred_fake')
Expand All @@ -100,22 +100,22 @@ def build_discriminator(mode):
rnn_1_attn = gandlf.layers.RecurrentAttention1D(rnn_1, embedded)
rnn_2_attn = gandlf.layers.RecurrentAttention1D(rnn_2, embedded,
name='pred_fake')
pred_fake = rnn_2_attn(rnn_1_attn(flat))
pred_fake = rnn_2_attn(rnn_1_attn(rnn_input))
return keras.models.Model(input=[image, input_class],
output=pred_fake)

elif mode == '2d': # Pay attention to whole image.
ref_image = keras.layers.Input((28, 28, 1), name='ref_image_dis')
flat = keras.layers.Reshape((28, 28))(ref_image)
rnn_1_attn = gandlf.layers.RecurrentAttention2D(rnn_1, flat)
rnn_2_attn = gandlf.layers.RecurrentAttention2D(rnn_2, flat,
attn_reshaped = keras.layers.Reshape((28, 28))(ref_image)
rnn_1_attn = gandlf.layers.RecurrentAttention2D(rnn_1, attn_reshaped)
rnn_2_attn = gandlf.layers.RecurrentAttention2D(rnn_2, attn_reshaped,
name='pred_fake')
pred_fake = rnn_2_attn(rnn_1_attn(flat))
pred_fake = rnn_2_attn(rnn_1_attn(rnn_input))
return keras.models.Model(input=[image, ref_image],
output=pred_fake)

else:
pred_fake = rnn_2(rnn_1(flat))
pred_fake = rnn_2(rnn_1(rnn_input))
return keras.models.Model(input=image,
output=pred_fake)

Expand Down
44 changes: 27 additions & 17 deletions gandlf/layers/core.py
Expand Up @@ -26,9 +26,8 @@ class BatchSimilarity(keras.layers.Layer):
"""Calculates intrabatch similarity, for minibatch discrimination.
The minibatch similarities can be added as features for the existing
layer by using a Merge layer. The layer outputs a Tensor with shape
(batch_size, num_similarities) for 2D tensors, (batch_size, None,
num_similarities) for 3D tensors, and so on.
layer by using a Merge layer. The layer only works for inputs with shape
(batch_size, num_features). Inputs with more dimensions can be flattened.
In order to make this layer linear time with respect to the batch size,
instead of doing a pairwise comparison between each pair of samples in
Expand All @@ -40,38 +39,49 @@ class BatchSimilarity(keras.layers.Layer):
possible types. Alternatively, it can be a function which takes
two tensors as inputs and returns their similarity. A list or
tuple of similarities will apply all the similarities.
n: int or list of ints (one for each similarity), number of times to
repeat each similarity, using a different sample to calculate the
other similarity.
Reference: "Improved Techniques for Training GANs"
https://arxiv.org/abs/1606.03498
"""

def __init__(self, similarity='exp_l1', **kwargs):
if isinstance(similarity, (list, tuple)):
self.similarities = [similarities.get(s) for s in similarity]
else:
self.similarities = [similarities.get(similarity)]
def __init__(self, similarity='exp_l1', n=1, **kwargs):
if not isinstance(similarity, (list, tuple)):
similarity = [similarity]
if not isinstance(n, (list, tuple)):
n = [n for _ in similarity]

self.similarities = [similarities.get(s) for s in similarity]
self.n = n

super(BatchSimilarity, self).__init__(**kwargs)

def build(self, input_shape):
if len(input_shape) < 2:
if len(input_shape) == 2:
raise ValueError('The input to a BatchSimilarity layer must be '
'at least 2D. Got %d dims.' % len(input_shape))
'2D. Got %d dims.' % len(input_shape))

def call(self, x, mask=None):
sims = []
for sim in self.similarities:
batch_size = K.shape(x)[0]
idx = K.random_uniform((batch_size,), low=0, high=batch_size,
dtype='int32')
x_shuffled = K.gather(x, idx)
sims.append(sim(x, x_shuffled))
for n, sim in zip(self.n, self.similarities):
for _ in range(n):
batch_size = K.shape(x)[0]
idx = K.random_uniform((batch_size,), low=0, high=batch_size,
dtype='int32')
x_shuffled = K.gather(x, idx)
pair_sim = sim(x, x_shuffled)
for _ in range(K.ndim(x) - 1):
pair_sim = K.expand_dims(pair_sim, dim=1)
sims.append(pair_sim)

return K.concatenate(sims, axis=-1)

def get_output_shape_for(self, input_shape):
if len(input_shape) < 2:
raise ValueError('The input to a BatchSimilarity layer must be '
'at least 2D. Got %d dims.' % len(input_shape))
'2D. Got %d dims.' % len(input_shape))
output_shape = list(input_shape)
output_shape[-1] = len(self.similarities)
return tuple(output_shape)
Expand Down
20 changes: 5 additions & 15 deletions gandlf/similarities.py
@@ -1,13 +1,3 @@
"""
Guidelines: All similarities take two tensors and return a tensor with
shape (batch_size, 1 ... 1), which is the sample-wise similarity between the
two tensors.
Each similarity function is:
- Symmetric, so similarity(a, b) == similarity(b, a)
- Monotonically increasing with respect to `a - b`, for a >= b
"""

import keras
import keras.backend as K
from keras.utils.generic_utils import get_from_module
Expand All @@ -28,33 +18,33 @@ def exp_l2(a, b):
def l1(a, b):
"""L1 similarity. Maximum is 0 (a == b), minimum is -inf."""

return -K.sum(K.abs(a - b), axis=range(1, K.ndim(a)), keepdims=True)
return -K.sum(K.abs(a - b), axis=-1)


def l2(a, b):
"""L2 similarity. Maximum is 0 (a == b), minimum is -inf."""

return -K.sum(K.square(a - b), axis=range(1, K.ndim(a)), keepdims=True)
return -K.sum(K.square(a - b), axis=-1)


def cosine(a, b):
"""Cosine similarity. Maximum is 1 (a == b), minimum is -1 (a == -b)."""

a = K.l2_normalize(a)
b = K.l2_normalize(b)
return 1 - K.mean(a * b, axis=range(1, K.ndim(a)), keepdims=True)
return 1 - K.mean(a * b, axis=-1)


def sigmoid(a, b):
"""Sigmoid similarity. Maximum is 1 (a == b), minimum is 0."""

return K.sigmoid(K.sum(a * b, axis=range(1, K.ndim(a)), keepdims=True))
return K.sigmoid(K.sum(a * b, axis=-1)


def euclidean(a, b):
"""Euclidian similarity. Maximum is 1 (a == b), minimum is 0 (a == -b)."""

x = K.sum(K.square(a - b), axis=range(1, K.ndim(a)), keepdims=True)
x = K.sum(K.square(a - b), axis=-1)
return 1. / (1. + x)


Expand Down

0 comments on commit bce05b2

Please sign in to comment.