Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
124 changes: 14 additions & 110 deletions SSD_inference.ipynb

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion examples/YOLOv3_Training.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
num_train = len(lines)
batch_size = 32

yolov3.model.compile(optimizer=Adam(lr=1e-3), loss={'yolo_loss': lambda y_true, y_pred: y_pred})
yolov3.model.compile(optimizer=Adam(learning_rate=1e-3), loss={'yolo_loss': lambda y_true, y_pred: y_pred})

yolov3.model.fit_generator(data_generator_wrapper(lines, batch_size, yolov3.input_shape, anchors, num_classes),
steps_per_epoch=max(1, num_train // batch_size),
Expand Down
2 changes: 1 addition & 1 deletion examples/retinanet_training_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def main(args=None):

model.compile(
loss=yolk.detector.get_losses(args),
optimizer=keras.optimizers.adam(lr=args.lr, clipnorm=0.001)
optimizer=keras.optimizers.adam(learning_rate=args.lr, clipnorm=0.001)
)

model.fit_generator(
Expand Down
2 changes: 1 addition & 1 deletion examples/ssd_inference_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def main(args=None):
model_path = './VGG_VOC0712Plus_SSD_300x300_ft_iter_160000.h5'
model = yolk.detector.load_inference_model(model_path, args)
loss = yolk.detector.get_losses(args)
sgd = SGD(lr=0.001, momentum=0.9, decay=0.0, nesterov=False)
sgd = SGD(learning_rate=0.001, momentum=0.9, decay=0.0, nesterov=False)
model.compile(optimizer=sgd, loss=loss)


Expand Down
2 changes: 1 addition & 1 deletion examples/ssd_training_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def main(args=None):
# Make a Model
model = yolk.detector.load_training_model(20, args)
loss = yolk.detector.get_losses(args)
sgd = SGD(lr=0.001, momentum=0.9, decay=0.0, nesterov=False)
sgd = SGD(learning_rate=0.001, momentum=0.9, decay=0.0, nesterov=False)
model.compile(optimizer=sgd, loss=loss)

# Load Generators
Expand Down
2 changes: 1 addition & 1 deletion keras_retinanet/bin/train.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def create_models(backbone_retinanet, num_classes, weights, multi_gpu=0,
'regression' : losses.smooth_l1(),
'classification': losses.focal()
},
optimizer=keras.optimizers.adam(lr=lr, clipnorm=0.001)
optimizer=keras.optimizers.adam(learning_rate=lr, clipnorm=0.001)
)

return model, training_model, prediction_model
Expand Down
2 changes: 1 addition & 1 deletion keras_retinanet/layers/filter_detections.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ def _filter_detections(args):
outputs = backend.map_fn(
_filter_detections,
elems=[boxes, classification, other],
dtype=[keras.backend.floatx(), keras.backend.floatx(), 'int32'] + [o.dtype for o in other],
fn_output_signature=[keras.backend.floatx(), keras.backend.floatx(), 'int32'] + [o.dtype for o in other],
parallel_iterations=self.parallel_iterations
)

Expand Down
8 changes: 5 additions & 3 deletions keras_ssd/keras_layers/keras_layer_AnchorBoxes.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@
from __future__ import division
import numpy as np
import keras.backend as K
from keras.engine.topology import InputSpec
from keras.engine.topology import Layer
#from keras.engine.topology import InputSpec
#from keras.engine.topology import Layer
from keras.layers import InputSpec, Layer

from keras_ssd.bounding_box_utils.bounding_box_utils import convert_coordinates

Expand Down Expand Up @@ -172,7 +173,8 @@ def call(self, x, mask=None):
# batch_size, feature_map_height, feature_map_width, feature_map_channels = x._keras_shape
# else: # Not yet relevant since TensorFlow is the only supported backend right now, but it can't harm to have this in here for the future
# batch_size, feature_map_channels, feature_map_height, feature_map_width = x._keras_shape
batch_size, feature_map_height, feature_map_width, feature_map_channels = x._keras_shape
#batch_size, feature_map_height, feature_map_width, feature_map_channels = x._keras_shape
batch_size, feature_map_height, feature_map_width, feature_map_channels = x.shape

# Compute the grid of box center points. They are identical for all aspect ratios.

Expand Down
41 changes: 22 additions & 19 deletions keras_ssd/keras_layers/keras_layer_DecodeDetections.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@
import numpy as np
import tensorflow as tf
import keras.backend as K
from keras.engine.topology import InputSpec
from keras.engine.topology import Layer
#from keras.engine.topology import InputSpec
#from keras.engine.topology import Layer
from keras.layers import InputSpec, Layer

class DecodeDetections(Layer):
'''
Expand Down Expand Up @@ -171,7 +172,7 @@ def filter_single_class(index):
# a tensor of shape (n_boxes, 1 + 4 coordinates) that contains the
# confidnece values for just one class, determined by `index`.
confidences = tf.expand_dims(batch_item[..., index], axis=-1)
class_id = tf.fill(dims=tf.shape(confidences), value=tf.to_float(index))
class_id = tf.fill(dims=tf.shape(confidences), value=tf.cast(index, tf.float32))
box_coordinates = batch_item[...,-4:]

single_class = tf.concat([class_id, confidences, box_coordinates], axis=-1)
Expand Down Expand Up @@ -216,14 +217,15 @@ def no_confident_predictions():
return padded_single_class

# Iterate `filter_single_class()` over all class indices.
filtered_single_classes = tf.map_fn(fn=lambda i: filter_single_class(i),
elems=tf.range(1,n_classes),
dtype=tf.float32,
parallel_iterations=128,
back_prop=False,
swap_memory=False,
infer_shape=True,
name='loop_over_classes')
filtered_single_classes = tf.nest.map_structure(
tf.stop_gradient,
tf.map_fn(fn=lambda i: filter_single_class(i),
elems=tf.range(1,n_classes),
fn_output_signature=tf.float32,
parallel_iterations=128,
swap_memory=False,
infer_shape=True,
name='loop_over_classes'))

# Concatenate the filtered results for all individual classes to one tensor.
filtered_predictions = tf.reshape(tensor=filtered_single_classes, shape=(-1,6))
Expand Down Expand Up @@ -253,14 +255,15 @@ def pad_and_top_k():
return top_k_boxes

# Iterate `filter_predictions()` over all batch items.
output_tensor = tf.map_fn(fn=lambda x: filter_predictions(x),
elems=y_pred,
dtype=None,
parallel_iterations=128,
back_prop=False,
swap_memory=False,
infer_shape=True,
name='loop_over_batch')
output_tensor = tf.nest.map_structure(
tf.stop_gradient,
tf.map_fn(fn=lambda x: filter_predictions(x),
elems=y_pred,
fn_output_signature=None,
parallel_iterations=128,
swap_memory=False,
infer_shape=True,
name='loop_over_batch'))

return output_tensor

Expand Down
24 changes: 13 additions & 11 deletions keras_ssd/keras_layers/keras_layer_DecodeDetectionsFast.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@
import numpy as np
import tensorflow as tf
import keras.backend as K
from keras.engine.topology import InputSpec
from keras.engine.topology import Layer
#from keras.engine.topology import InputSpec
#from keras.engine.topology import Layer
from keras.layers import InputSpec, Layer

class DecodeDetectionsFast(Layer):
'''
Expand Down Expand Up @@ -123,7 +124,7 @@ def call(self, y_pred, mask=None):
#####################################################################################

# Extract the predicted class IDs as the indices of the highest confidence values.
class_ids = tf.expand_dims(tf.to_float(tf.argmax(y_pred[...,:-12], axis=-1)), axis=-1)
class_ids = tf.expand_dims(tf.cast(tf.argmax(y_pred[...,:-12], axis=-1), tf.float32), axis=-1)
# Extract the confidences of the maximal classes.
confidences = tf.reduce_max(y_pred[...,:-12], axis=-1, keep_dims=True)

Expand Down Expand Up @@ -236,14 +237,15 @@ def pad_and_top_k():
return top_k_boxes

# Iterate `filter_predictions()` over all batch items.
output_tensor = tf.map_fn(fn=lambda x: filter_predictions(x),
elems=y_pred,
dtype=None,
parallel_iterations=128,
back_prop=False,
swap_memory=False,
infer_shape=True,
name='loop_over_batch')
output_tensor = tf.nest.map_structure(
tf.stop_gradient,
tf.map_fn(fn=lambda x: filter_predictions(x),
elems=y_pred,
fn_output_signature=None,
parallel_iterations=128,
swap_memory=False,
infer_shape=True,
name='loop_over_batch'))

return output_tensor

Expand Down
7 changes: 4 additions & 3 deletions keras_ssd/keras_layers/keras_layer_L2Normalization.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@
from __future__ import division
import numpy as np
import keras.backend as K
from keras.engine.topology import InputSpec
from keras.engine.topology import Layer
#from keras.engine.topology import InputSpec
#from keras.engine.topology import Layer
from keras.layers import InputSpec, Layer

class L2Normalization(Layer):
'''
Expand Down Expand Up @@ -57,7 +58,7 @@ def build(self, input_shape):
self.input_spec = [InputSpec(shape=input_shape)]
gamma = self.gamma_init * np.ones((input_shape[self.axis],))
self.gamma = K.variable(gamma, name='{}_gamma'.format(self.name))
self.trainable_weights = [self.gamma]
self._trainable_weights = [self.gamma]
super(L2Normalization, self).build(input_shape)

def call(self, x, mask=None):
Expand Down
16 changes: 8 additions & 8 deletions keras_ssd/keras_loss_function/keras_ssd_loss.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def log_loss(self, y_true, y_pred):
# Make sure that `y_pred` doesn't contain any zeros (which would break the log function)
y_pred = tf.maximum(y_pred, 1e-15)
# Compute the log loss
log_loss = -tf.reduce_sum(y_true * tf.log(y_pred), axis=-1)
log_loss = -tf.reduce_sum(y_true * tf.math.log(y_pred), axis=-1)
return log_loss

def compute_loss(self, y_true, y_pred):
Expand Down Expand Up @@ -130,14 +130,14 @@ class vector of all zeros.

# 1: Compute the losses for class and box predictions for every box.

classification_loss = tf.to_float(self.log_loss(y_true[:,:,:-12], y_pred[:,:,:-12])) # Output shape: (batch_size, n_boxes)
localization_loss = tf.to_float(self.smooth_L1_loss(y_true[:,:,-12:-8], y_pred[:,:,-12:-8])) # Output shape: (batch_size, n_boxes)
classification_loss = tf.cast(self.log_loss(y_true[:,:,:-12], y_pred[:,:,:-12]), tf.float32) # Output shape: (batch_size, n_boxes)
localization_loss = tf.cast(self.smooth_L1_loss(y_true[:,:,-12:-8], y_pred[:,:,-12:-8]), tf.float32) # Output shape: (batch_size, n_boxes)

# 2: Compute the classification losses for the positive and negative targets.

# Create masks for the positive and negative ground truth classes.
negatives = y_true[:,:,0] # Tensor of shape (batch_size, n_boxes)
positives = tf.to_float(tf.reduce_max(y_true[:,:,1:-12], axis=-1)) # Tensor of shape (batch_size, n_boxes)
positives = tf.cast(tf.reduce_max(y_true[:,:,1:-12], axis=-1), tf.float32) # Tensor of shape (batch_size, n_boxes)

# Count the number of positive boxes (classes 1 to n) in y_true across the whole batch.
n_positive = tf.reduce_sum(positives)
Expand All @@ -151,7 +151,7 @@ class vector of all zeros.

# First, compute the classification loss for all negative boxes.
neg_class_loss_all = classification_loss * negatives # Tensor of shape (batch_size, n_boxes)
n_neg_losses = tf.count_nonzero(neg_class_loss_all, dtype=tf.int32) # The number of non-zero loss entries in `neg_class_loss_all`
n_neg_losses = tf.math.count_nonzero(neg_class_loss_all, dtype=tf.int32) # The number of non-zero loss entries in `neg_class_loss_all`
# What's the point of `n_neg_losses`? For the next step, which will be to compute which negative boxes enter the classification
# loss, we don't just want to know how many negative ground truth boxes there are, but for how many of those there actually is
# a positive (i.e. non-zero) loss. This is necessary because `tf.nn.top-k()` in the function below will pick the top k boxes with
Expand All @@ -163,7 +163,7 @@ class vector of all zeros.

# Compute the number of negative examples we want to account for in the loss.
# We'll keep at most `self.neg_pos_ratio` times the number of positives in `y_true`, but at least `self.n_neg_min` (unless `n_neg_loses` is smaller).
n_negative_keep = tf.minimum(tf.maximum(self.neg_pos_ratio * tf.to_int32(n_positive), self.n_neg_min), n_neg_losses)
n_negative_keep = tf.minimum(tf.maximum(self.neg_pos_ratio * tf.cast(n_positive, tf.int32), self.n_neg_min), n_neg_losses)

# In the unlikely case when either (1) there are no negative ground truth boxes at all
# or (2) the classification loss for all negative boxes is zero, return zero as the `neg_class_loss`.
Expand All @@ -185,7 +185,7 @@ def f2():
negatives_keep = tf.scatter_nd(indices=tf.expand_dims(indices, axis=1),
updates=tf.ones_like(indices, dtype=tf.int32),
shape=tf.shape(neg_class_loss_all_1D)) # Tensor of shape (batch_size * n_boxes,)
negatives_keep = tf.to_float(tf.reshape(negatives_keep, [batch_size, n_boxes])) # Tensor of shape (batch_size, n_boxes)
negatives_keep = tf.cast(tf.reshape(negatives_keep, [batch_size, n_boxes]), tf.float32) # Tensor of shape (batch_size, n_boxes)
# ...and use it to keep only those boxes and mask all other classification losses
neg_class_loss = tf.reduce_sum(classification_loss * negatives_keep, axis=-1) # Tensor of shape (batch_size,)
return neg_class_loss
Expand All @@ -206,6 +206,6 @@ def f2():
# because the relevant criterion to average our loss over is the number of positive boxes in the batch
# (by which we're dividing in the line above), not the batch size. So in order to revert Keras' averaging
# over the batch size, we'll have to multiply by it.
total_loss = total_loss * tf.to_float(batch_size)
total_loss = total_loss * tf.cast(batch_size, tf.float32)

return total_loss
4 changes: 2 additions & 2 deletions keras_yolov3/train.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def _main():
# Train with frozen layers first, to get a stable loss.
# Adjust num epochs to your dataset. This step is enough to obtain a not bad model.
if True:
model.compile(optimizer=Adam(lr=1e-3), loss={
model.compile(optimizer=Adam(learning_rate=1e-3), loss={
# use custom yolo_loss Lambda layer.
'yolo_loss': lambda y_true, y_pred: y_pred})

Expand All @@ -70,7 +70,7 @@ def _main():
if True:
for i in range(len(model.layers)):
model.layers[i].trainable = True
model.compile(optimizer=Adam(lr=1e-4), loss={'yolo_loss': lambda y_true, y_pred: y_pred}) # recompile to apply the change
model.compile(optimizer=Adam(learning_rate=1e-4), loss={'yolo_loss': lambda y_true, y_pred: y_pred}) # recompile to apply the change
print('Unfreeze all of the layers.')

batch_size = 32 # note that more GPU memory is required after unfreezing the body
Expand Down
4 changes: 2 additions & 2 deletions keras_yolov3/train_bottleneck.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def _main():
model.save_weights(log_dir + 'trained_weights_stage_0.h5')

# train last layers with random augmented data
model.compile(optimizer=Adam(lr=1e-3), loss={
model.compile(optimizer=Adam(learning_rate=1e-3), loss={
# use custom yolo_loss Lambda layer.
'yolo_loss': lambda y_true, y_pred: y_pred})
batch_size = 16
Expand All @@ -91,7 +91,7 @@ def _main():
if True:
for i in range(len(model.layers)):
model.layers[i].trainable = True
model.compile(optimizer=Adam(lr=1e-4), loss={'yolo_loss': lambda y_true, y_pred: y_pred}) # recompile to apply the change
model.compile(optimizer=Adam(learning_rate=1e-4), loss={'yolo_loss': lambda y_true, y_pred: y_pred}) # recompile to apply the change
print('Unfreeze all of the layers.')

batch_size = 4 # note that more GPU memory is required after unfreezing the body
Expand Down
2 changes: 1 addition & 1 deletion tests/models/test_densenet.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,6 @@ def test_backbone(backbone):
'regression': losses.smooth_l1(),
'classification': losses.focal()
},
optimizer=keras.optimizers.adam(lr=1e-5, clipnorm=0.001))
optimizer=keras.optimizers.adam(learning_rate=1e-5, clipnorm=0.001))

model.fit(inputs, targets, batch_size=1)
2 changes: 1 addition & 1 deletion tests/models/test_mobilenet.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,6 @@ def test_backbone(backbone, alpha):
'regression': losses.smooth_l1(),
'classification': losses.focal()
},
optimizer=keras.optimizers.adam(lr=1e-5, clipnorm=0.001))
optimizer=keras.optimizers.adam(learning_rate=1e-5, clipnorm=0.001))

training_model.fit(inputs, targets, batch_size=1)
3 changes: 2 additions & 1 deletion yolk/backend/ssd_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

import keras_ssd
from keras import models
from keras.preprocessing import image
#from keras.preprocessing import image
from keras.utils import image_utils as image
from keras.callbacks import ModelCheckpoint, LearningRateScheduler, TerminateOnNaN, CSVLogger

from keras_ssd.models.keras_ssd300 import ssd_300
Expand Down