diff --git a/examples/pascal_voc_jaccard_loss.py b/examples/pascal_voc_jaccard_loss.py index f670710..5fd8195 100644 --- a/examples/pascal_voc_jaccard_loss.py +++ b/examples/pascal_voc_jaccard_loss.py @@ -9,34 +9,28 @@ from __future__ import division import numpy as np import os +import shutil from keras import backend as K from keras.utils.np_utils import to_categorical -from keras.callbacks import ModelCheckpoint, ReduceLROnPlateau, EarlyStopping +from keras.callbacks import ModelCheckpoint, ReduceLROnPlateau, EarlyStopping, CSVLogger from keras.optimizers import Adam from keras.preprocessing.image import ImageDataGenerator -from keras.utils import np_utils from keras.losses import categorical_crossentropy from keras_contrib.losses.jaccard import jaccard_distance from keras_contrib.datasets import pascal_voc from keras_contrib.applications.densenet import DenseNetFCN -batch_size = 64 +batch_size = 2 nb_classes = 21 epochs = 100 - -img_rows, img_cols = 32, 32 +img_rows, img_cols = 256, 256 img_channels = 3 # Parameters for the DenseNet model builder img_dim = (img_channels, img_rows, img_cols) if K.image_data_format() == 'channels_first' else ( img_rows, img_cols, img_channels) -depth = 40 -nb_dense_block = 3 -growth_rate = 12 -nb_filter = 16 -dropout_rate = 0.0 # 0.0 for data augmentation conf = pascal_voc.voc_config() pascal_folder = os.path.join(conf['pascal_berkeley_root'], 'dataset') @@ -45,30 +39,32 @@ pascal_folder = os.path.join(conf['pascal_berkeley_root'], 'dataset') if not os.path.isdir(pascal_folder): pascal_voc.data_pascal_voc.run() -# load the data using dual imagedata generators -# we create two instances with the same arguments -data_gen_args = dict() -image_datagen = ImageDataGenerator(**data_gen_args) -mask_datagen = ImageDataGenerator(**data_gen_args) - -# Provide the same seed and keyword arguments to the fit and flow methods -seed = 1 -image_generator = image_datagen.flow_from_directory( - pascal_folder, - target_size=(img_rows, img_cols), - class_mode=None, - classes=["img"], - seed=seed) -mask_generator = image_datagen.flow_from_directory( - pascal_folder, - target_size=(img_rows, img_cols), - class_mode=None, - classes=["cls_png"], - seed=seed) +# move validation files to diff folder +val_names = [ + l.strip() + for l in open( + os.path.join(conf['pascal_berkeley_root'], 'dataset', 'val.txt')) +] +val_x_folder = os.path.join(conf['pascal_berkeley_root'], 'dataset', 'img_val') +val_y_folder = os.path.join(conf['pascal_berkeley_root'], 'dataset', + 'cls_png_val') +if not os.path.isdir(val_x_folder): + os.makedirs(val_x_folder) + for val_name in val_names: + from_path = os.path.join(conf['pascal_berkeley_root'], 'dataset', + 'img', val_name + '.png') + to_path = os.path.join(val_x_folder, val_name + '.jpg') + shutil.move(from_path, to_path) +if not os.path.isdir(val_y_folder): + os.makedirs(val_y_folder) + for val_name in val_names: + from_path = os.path.join(conf['pascal_berkeley_root'], 'dataset', + 'cls_png', val_name + '.png') + to_path = os.path.join(val_y_folder, val_name + '.jpg') + shutil.move(from_path, to_path) def transform(gen_x, gen_y): - """Combine two generatorsand one-hot transform the labels.""" while True: x_batch = next(gen_x) / 255.0 y_batch = next(gen_y) @@ -77,18 +73,81 @@ def transform(gen_x, gen_y): num_classes=nb_classes).reshape(y_batch.shape[:3] + (nb_classes, )) yield x_batch, y_batch + +# load the data using dual imagedata generators +data_gen_args = dict() +image_datagen = ImageDataGenerator(**data_gen_args) +mask_datagen = ImageDataGenerator(**data_gen_args) + +# Provide the same seed and keyword arguments to the fit and flow methods +seed = 1 +image_generator = image_datagen.flow_from_directory( + pascal_folder, + class_mode=None, + classes=["img"], + batch_size=batch_size, + seed=seed) +mask_generator = image_datagen.flow_from_directory( + pascal_folder, + class_mode=None, + classes=["cls_png"], + batch_size=batch_size, + seed=seed) +# combine generators into one which yields image and masks train_generator = transform(image_generator, mask_generator) -# Create the model +image_generator_val = image_datagen.flow_from_directory( + pascal_folder, + class_mode=None, + classes=["img_val"], + batch_size=batch_size, + seed=seed) + +mask_generator_val = image_datagen.flow_from_directory( + pascal_folder, + class_mode=None, + classes=["cls_png_val"], + batch_size=batch_size, + seed=seed) +# combine generators into one which yields image and masks +val_generator = transform(image_generator_val, mask_generator_val) + +# # view some images if you like +# %matplotlib inline +# from mpl_toolkits.axes_grid1 import ImageGrid +# from matplotlib import pyplot as plt +# X, y = next(train_generator) +# figure = plt.figure(figsize=(10, 10)) +# n=5 +# grid = ImageGrid(figure, 111, (n ,2), axes_pad=0.3) +# for i in range(n): +# xx = X[i] +# yy = y[i] +# grid[i*2].imshow(xx) +# grid[i*2+1].imshow(yy.argmax(-1)*nb_classes) + +# Create the model, a quick and small one model = DenseNetFCN( - # depth=depth, - nb_dense_block=nb_dense_block, - growth_rate=growth_rate, - # nb_filter=nb_filter, - dropout_rate=dropout_rate, + nb_dense_block=3, + growth_rate=10, + nb_layers_per_block=2, + reduction=0.0, + dropout_rate=0.2, input_shape=img_dim, - classes=nb_classes -) + upsampling_conv=128, + init_conv_filters=48, + classes=nb_classes, + upsampling_type='upsampling') +print('Model created') + +model.summary() + +optimizer = Adam(lr=1e-3) # Using Adam instead of SGD to speed up training +model.compile( + loss=jaccard_distance, + optimizer=optimizer, + metrics=['acc', categorical_crossentropy]) +print('Finished compiling') print('Model created') model.summary() @@ -99,9 +158,10 @@ model.compile( metrics=['acc', categorical_crossentropy]) print('Finished compiling') +# Setup some callbacks weights_file = 'DenseNet-40-12-PASCAL-10.h5' - -# setup some callbacks +csv_file = 'DenseNet-40-12-PASCAL-10.csv' +csv_logger = CSVLogger(csv_file) lr_reducer = ReduceLROnPlateau( monitor='val_loss', factor=np.sqrt(0.1), @@ -117,13 +177,18 @@ model_checkpoint = ModelCheckpoint( mode='auto') callbacks = [lr_reducer, early_stopper, model_checkpoint] -model.fit_generator( +# Train +history = model.fit_generator( train_generator, steps_per_epoch=image_generator.n // batch_size, epochs=epochs, callbacks=callbacks, - verbose=2) + validation_data=val_generator, + validation_steps=image_generator_val.n // batch_size, + verbose=1) -scores = model.evaluate(testX, Y_test, batch_size=batch_size) -print('Test loss : ', scores[0]) -print('Test accuracy : ', scores[1]) +# Get the score from the validation data +scores = model.evaluate_generator( + val_generator, steps=image_generator_val.n // batch_size) +print('Validation loss : ', scores[0]) +print('Validation accuracy : ', scores[1])