Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
e1d0dc2
Support for TB hist visualisation when using generator for validation
kencoken Nov 11, 2016
2498100
Added execute_args parameter to compile function
kencoken Nov 15, 2016
6afbf75
Make Tensorboard histogram visualisation work for model duplicates
kencoken Nov 18, 2016
ee032ef
Fix to validation_gen set condition
kencoken Nov 18, 2016
7572cbe
Added missing validation_gen property to Sequential model
kencoken Nov 18, 2016
46fcd0a
Added on_val_start callback and batch/val timings to progbar
kencoken Nov 22, 2016
bc52fbd
Merge branch 'tb-hist-for-gen'
kencoken Jan 26, 2017
6983ef7
Merge branch 'tb-hist-for-multi-model'
kencoken Jan 26, 2017
978616f
Merge branch 'val-start-progbar'
kencoken Jan 26, 2017
c8a28a4
Merge branch 'ts-function-kwargs'
kencoken Jan 26, 2017
2d0e5d8
Merge tag '1.2.2'
kencoken Mar 9, 2017
d1aa190
Added learning rate multiplier support
kencoken Apr 30, 2017
dc75b3d
Remove exception when specifying b_learning_rate_multiplier on layers…
kencoken May 1, 2017
f6931cd
Merge pull request #1 from kencoken/lr_mults
kencoken May 15, 2017
81d7d0c
override model definition
May 15, 2017
dc9a160
removed unecessary docstrings
May 15, 2017
2891f3a
removed duplicated behaviour in load_model()
May 15, 2017
28763b7
white lines diff
May 15, 2017
7fb918c
code simplification
May 15, 2017
65c244c
pass layer class through load_model
May 15, 2017
0c345fb
default behaviour is now the same as original keras
May 15, 2017
fda2ede
put global definition in the right plcae
May 15, 2017
37d70c3
removed uncessary import
May 15, 2017
c0ff731
remove extra parameter, pass layer_class through custom_objects
May 15, 2017
791c406
code cleanup
May 19, 2017
bf847f2
removed unused import
May 19, 2017
a1f7fe3
refactored code to fallback to default Keras behaviour properly
May 19, 2017
3c5f922
reduce diff to original code
May 19, 2017
43cddbc
remove classify info from custom_objects after use
May 19, 2017
5b45baa
adressing PR comments
May 19, 2017
d9dfb91
Merge pull request #2 from guilherme-pombo/master
kencoken May 20, 2017
ac4a14e
Add flag to toggle metrics inclusion when loading model
kencoken Jul 14, 2017
931529c
Don't construct random_transform matrix unless necessary in datagen
kencoken Jul 27, 2017
65be0fb
fixed bug
Nov 22, 2018
3a49568
Merge pull request #3 from outcastofmusic/fix_queue_bug
kencoken Nov 23, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions keras/backend/tensorflow_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -1927,9 +1927,11 @@ def __init__(self, inputs, outputs, updates=[]):
updates_ops.append(update)
self.updates_op = tf.group(*updates_ops)

def __call__(self, inputs):
def __call__(self, inputs, **kwargs):
if not isinstance(inputs, (list, tuple)):
raise TypeError('`inputs` should be a list or tuple.')
unrecognized_kwargs = set(kwargs.keys()) - {'options', 'run_metadata'}
assert len(unrecognized_kwargs) == 0, 'Unrecognised kwargs: {}'.format(unrecognized_kwargs)
feed_dict = {}
for tensor, value in zip(self.inputs, inputs):
if is_sparse(tensor):
Expand All @@ -1940,7 +1942,7 @@ def __call__(self, inputs):
feed_dict[tensor] = value
session = get_session()
updated = session.run(self.outputs + [self.updates_op],
feed_dict=feed_dict)
feed_dict=feed_dict, **kwargs)
return updated[:len(self.outputs)]


Expand Down
10 changes: 9 additions & 1 deletion keras/backend/theano_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
from theano.sandbox.softsign import softsign as T_softsign
import inspect
import numpy as np
import warnings
from .common import _FLOATX, floatx, _EPSILON, image_dim_ordering
from .common import _FLOATX, _EPSILON, image_dim_ordering
py_all = all


Expand Down Expand Up @@ -954,8 +956,14 @@ def __init__(self, inputs, outputs, updates=[], **kwargs):
on_unused_input='ignore',
**kwargs)

def __call__(self, inputs):
def __call__(self, inputs, **kwargs):
assert isinstance(inputs, (list, tuple))
if len(kwargs) > 0:
msg = [
'Expected no kwargs, you passed %s' % len(kwargs),
'kwargs passed to F() are ignored with Theano backend'
]
warnings.warn('\n'.join(msg))
return self.function(*inputs)


Expand Down
146 changes: 124 additions & 22 deletions keras/callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,26 @@ def on_batch_end(self, batch, logs=None):
'to the batch update (%f). Check your callbacks.'
% delta_t_median)

def on_val_begin(self, epoch, logs=None):
"""Called at the beginning of validation.

# Arguments
epoch: integer, index of epoch.
logs: dictionary of logs.
"""
for callback in self.callbacks:
callback.on_val_begin(epoch, logs)

def on_val_end(self, epoch, logs=None):
"""Called at the end of validation.

# Arguments
epoch: integer, index of epoch.
logs: dictionary of logs.
"""
for callback in self.callbacks:
callback.on_val_end(epoch, logs)

def on_train_begin(self, logs=None):
"""Called at the beginning of training.

Expand Down Expand Up @@ -187,6 +207,12 @@ def on_batch_begin(self, batch, logs=None):
def on_batch_end(self, batch, logs=None):
pass

def on_val_begin(self, epoch, logs=None):
pass

def on_val_end(self, epoch, logs=None):
pass

def on_train_begin(self, logs=None):
pass

Expand Down Expand Up @@ -237,6 +263,9 @@ def on_epoch_begin(self, epoch, logs=None):
self.progbar = Progbar(target=self.params['nb_sample'],
verbose=self.verbose)
self.seen = 0
self.epoch_t = None
self.batch_t = None
self.val_t = None

def on_batch_begin(self, batch, logs=None):
if self.seen < self.params['nb_sample']:
Expand All @@ -256,11 +285,27 @@ def on_batch_end(self, batch, logs=None):
if self.verbose and self.seen < self.params['nb_sample']:
self.progbar.update(self.seen, self.log_values)

def on_val_begin(self, epoch, logs={}):
self.epoch_t = self.progbar.elapsed_time()
self.batch_t = self.epoch_t / self.params['nb_sample']
self.val_start_t = time.time()

def on_val_end(self, epoch, logs={}):
self.val_t = time.time() - self.val_start_t

def on_epoch_end(self, epoch, logs=None):
logs = logs or {}
for k in self.params['metrics']:
if k in logs:
self.log_values.append((k, logs[k]))
if not self.epoch_t:
self.epoch_t = self.progbar.elapsed_time()
self.batch_t = self.epoch_t / self.params['nb_sample']
# add timings
self.log_values.append(('epoch_t', self.epoch_t))
self.log_values.append(('batch_t', self.batch_t))
if self.val_t:
self.log_values.append(('val_t', self.val_t))
if self.verbose:
self.progbar.update(self.seen, self.log_values, force=True)

Expand Down Expand Up @@ -581,7 +626,16 @@ def set_model(self, model):
self.model = model
self.sess = K.get_session()
if self.histogram_freq and self.merged is None:
for layer in self.model.layers:
def get_layers_flattened(model_layers):
layers = []
for layer in model_layers:
if layer.__class__.__name__ == 'Model':
layers.extend(get_layers_flattened(layer.layers))
else:
layers.append(layer)
return layers
layers = get_layers_flattened(self.model.layers)
for layer in layers:

for weight in layer.weights:
if hasattr(tf, 'histogram_summary'):
Expand All @@ -606,13 +660,17 @@ def set_model(self, model):
else:
tf.summary.image(weight.name, w_img)

if hasattr(layer, 'output'):
if hasattr(tf, 'histogram_summary'):
tf.histogram_summary('{}_out'.format(layer.name),
layer.output)
else:
tf.summary.histogram('{}_out'.format(layer.name),
layer.output)
if layer in self.model.layers:
try:
if hasattr(layer, 'output'):
if hasattr(tf, 'histogram_summary'):
tf.histogram_summary('{}_out'.format(layer.name),
layer.output)
else:
tf.summary.histogram('{}_out'.format(layer.name),
layer.output)
except AttributeError:
pass

if hasattr(tf, 'merge_all_summaries'):
self.merged = tf.merge_all_summaries()
Expand All @@ -638,21 +696,65 @@ def set_model(self, model):
def on_epoch_end(self, epoch, logs=None):
logs = logs or {}

if self.model.validation_data and self.histogram_freq:
if epoch % self.histogram_freq == 0:
# TODO: implement batched calls to sess.run
# (current call will likely go OOM on GPU)
if self.model.uses_learning_phase:
cut_v_data = len(self.model.inputs)
val_data = self.model.validation_data[:cut_v_data] + [0]
tensors = self.model.inputs + [K.learning_phase()]
else:
val_data = self.model.validation_data
tensors = self.model.inputs
feed_dict = dict(zip(tensors, val_data))
result = self.sess.run([self.merged], feed_dict=feed_dict)
summary_str = result[0]
def get_val_summary(validation_data):
if self.model.uses_learning_phase:
cut_v_data = len(self.model.inputs)
val_data = list(validation_data[:cut_v_data]) + [0]
tensors = self.model.inputs + [K.learning_phase()]
else:
val_data = validation_data
tensors = self.model.inputs
feed_dict = dict(zip(tensors, val_data))
result = self.sess.run([self.merged], feed_dict=feed_dict)
return result[0]

if self.histogram_freq and epoch % self.histogram_freq == 0:
if self.model.validation_data:
summary_str = get_val_summary(self.model.validation_data)
self.writer.add_summary(summary_str, epoch)
elif self.model.validation_gen:
val_gen = self.model.validation_gen.generator
nb_val_samples = self.model.validation_gen.nb_samples
# process nb_samples from validation data generator
sub_summaries = []
processed_samples = 0
while processed_samples < nb_val_samples:
validation_data = next(val_gen)
summary = tf.Summary.FromString(get_val_summary(validation_data))
sub_summaries.append(summary)
processed_samples += validation_data[0].shape[0]
# convert summaries to dict of lists
sub_summaries_dict = {}
for sub_summary in sub_summaries:
for value in sub_summary.value:
value_field = value.WhichOneof('value')
value_ifo = sub_summaries_dict.setdefault(value.tag, {'value_field': None, 'values': []})
if not value_ifo['value_field']:
value_ifo['value_field'] = value_field
else:
assert value_ifo['value_field'] == value_field
value_ifo['values'].append(getattr(value, value_field))
# aggregate summaries
summary = tf.Summary()
for name, value_ifo in sub_summaries_dict.items():
summary_value = summary.value.add()
summary_value.tag = name
if value_ifo['value_field'] == 'histo':
values = value_ifo['values']
summary_value.histo.min = min([x.min for x in values])
summary_value.histo.max = max([x.max for x in values])
summary_value.histo.num = sum([x.num for x in values])
summary_value.histo.sum = sum([x.sum for x in values])
summary_value.histo.sum_squares = sum([x.sum_squares for x in values])
# for histogram values, just take first batch for now
# TODO: aggregate histograms over batches
for lim in values[0].bucket_limit:
summary_value.histo.bucket_limit.append(lim)
for bucket in values[0].bucket:
summary_value.histo.bucket.append(bucket)
else:
print('Warning: could not aggregate summary of type {}'.format(value_ifo['value_field']))
self.writer.add_summary(summary, epoch)

for name, value in logs.items():
if name in ['batch', 'size']:
Expand Down
23 changes: 22 additions & 1 deletion keras/engine/topology.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ class Layer(object):
weights: The concatenation of the lists trainable_weights and
non_trainable_weights (in this order).
constraints: Dict mapping weights to constraints.
multipliers: dict mapping weights to learning rates multipliers.

# Methods
call(x, mask=None): Where the layer's logic lives.
Expand Down Expand Up @@ -310,6 +311,8 @@ def __init__(self, **kwargs):
self.losses = []
if not hasattr(self, 'constraints'):
self.constraints = {} # dict {tensor: constraint instance}
if not hasattr(self, 'multipliers'):
self.multipliers = {} # dict {tensor: multiplier value}
self.built = False

# These properties should be set by the user via keyword arguments.
Expand Down Expand Up @@ -403,7 +406,8 @@ def create_input_layer(self, batch_input_shape,
def add_weight(self, shape, initializer, name=None,
trainable=True,
regularizer=None,
constraint=None):
constraint=None,
multiplier=None):
"""Adds a weight variable to the layer.

# Arguments
Expand All @@ -420,6 +424,8 @@ def add_weight(self, shape, initializer, name=None,
self.add_loss(regularizer(weight))
if constraint is not None:
self.constraints[weight] = constraint
if multiplier is not None:
self.multipliers[weight] = multiplier
if trainable:
self._trainable_weights.append(weight)
else:
Expand Down Expand Up @@ -1068,6 +1074,7 @@ def __init__(self, input_shape=None, batch_input_shape=None,
self.inbound_nodes = []
self.outbound_nodes = []
self.constraints = {}
self.multipliers = {}
self.sparse = sparse

if not name:
Expand Down Expand Up @@ -1275,6 +1282,7 @@ def __init__(self, layers=None, mode='sum', concat_axis=-1,
self.inbound_nodes = []
self.outbound_nodes = []
self.constraints = {}
self.multipliers = {}
self._trainable_weights = []
self._non_trainable_weights = []
self.supports_masking = True
Expand Down Expand Up @@ -1715,6 +1723,7 @@ class Container(Layer):
trainable_weights (list of variables)
non_trainable_weights (list of variables)
constraints (list of tuples (weight, constraint))
multipliers (list of tuples (weight, learning_rate_multiplier))

# Methods
summary
Expand Down Expand Up @@ -2031,6 +2040,7 @@ def build_map_of_graph(tensor, seen_nodes=set(), depth=0,
self.supports_masking = False
# The following are implemented as property functions:
# self.constraints
# self.multipliers
# self.trainable_weights
# self.non_trainable_weights
# self.input_spec
Expand Down Expand Up @@ -2141,6 +2151,17 @@ def constraints(self):
cons[key] = value
return cons

@property
def multipliers(self):
mults = {}
for layer in self.layers:
for key, value in layer.multipliers.items():
if key in mults:
raise Exception('Received multiple learning rate multipliers '
'for one weight tensor: ' + str(key))
mults[key] = value
return mults

@property
def regularizers(self):
warnings.warn('The `regularizers` attribute of layers/models '
Expand Down
Loading