From 51fc1150e4f5c4007dfd9dc8b98eaec76598499a Mon Sep 17 00:00:00 2001 From: Rauf Date: Fri, 9 Aug 2019 10:04:05 +0000 Subject: [PATCH] add train_generator instead of own training function --- .gitignore | 1 + model.py | 161 ++++++++++++++++++++----------------------- plots/model.png | Bin 10210 -> 0 bytes plots/train_acc.png | Bin 15053 -> 0 bytes plots/train_loss.png | Bin 14180 -> 0 bytes plots/val_acc.png | Bin 14698 -> 0 bytes plots/val_loss.png | Bin 16737 -> 0 bytes test_net.py | 98 ++++++++++++++++++-------- 8 files changed, 145 insertions(+), 115 deletions(-) delete mode 100644 plots/model.png delete mode 100644 plots/train_acc.png delete mode 100644 plots/train_loss.png delete mode 100644 plots/val_acc.png delete mode 100644 plots/val_loss.png diff --git a/.gitignore b/.gitignore index 84d749c..2b6c91a 100644 --- a/.gitignore +++ b/.gitignore @@ -109,3 +109,4 @@ tf_log/ to_test.ipynb encodings/ weights/ +plots/ diff --git a/model.py b/model.py index 9dcaac1..990a20b 100644 --- a/model.py +++ b/model.py @@ -5,6 +5,7 @@ import tensorflow as tf import pickle import cv2 +import random from keras.models import Model from keras import optimizers from keras.regularizers import l2 @@ -12,7 +13,7 @@ from keras.layers import Dense, Input, Lambda, Dropout, Flatten from keras.layers import Conv2D, MaxPool2D from classification_models import Classifiers -from keras.callbacks import TensorBoard + class SiameseNet: @@ -23,32 +24,50 @@ class SiameseNet: """ - def __init__(self, input_shape, image_loader, mode='l1', backbone='resnet50', optimizer=optimizers.Adam(lr=1e-4), tensorboard_log_path='tf_log/', weights_save_path='weights/'): + def __init__(self, input_shape, image_loader, mode='l1', backbone='resnet50', + backbone_weights = 'imagenet', + optimizer=optimizers.Adam(lr=1e-4), tensorboard_log_path='tf_log/', + weights_save_path='weights/', plots_path='plots/', encodings_path='encodings/', + project_name='', freeze_backbone=True): self.input_shape = input_shape self.backbone = backbone + self.backbone_weights = backbone_weights self.mode = mode + self.project_name = project_name self.optimizer = optimizer self.model = [] self.base_model = [] - self._create_model() - self.data_loader = image_loader - self.encoded_training_data = {} - if tensorboard_log_path: - os.makedirs(tensorboard_log_path, exist_ok=True) + self.l_model = [] + self.freeze_backbone = freeze_backbone + + self.encodings_path = os.path.join(encodings_path, project_name) + os.makedirs(self.encodings_path, exist_ok=True) + self.plots_path = os.path.join(plots_path, project_name) + self.tensorboard_log_path = os.path.join( + tensorboard_log_path, project_name) + if self.plots_path: + os.makedirs(self.plots_path, exist_ok=True) + if self.tensorboard_log_path: + os.makedirs(self.tensorboard_log_path, exist_ok=True) self.tensorboard_callback = TensorBoard( - tensorboard_log_path) if tensorboard_log_path else None + self.tensorboard_log_path) if tensorboard_log_path else None if self.tensorboard_callback: events_files_list = glob.glob( - os.path.join(tensorboard_log_path, 'events*')) + os.path.join(self.tensorboard_log_path, 'events*')) for event_file in events_files_list: try: os.remove(event_file) except: print("Error while deleting file : ", event_file) self.tensorboard_callback.set_model(self.model) - self.weights_save_path = weights_save_path - if weights_save_path: - os.makedirs(weights_save_path, exist_ok=True) + self.weights_save_path = os.path.join( + weights_save_path, self.project_name) + if self.weights_save_path: + os.makedirs(self.weights_save_path, exist_ok=True) + + self._create_model() + self.data_loader = image_loader + self.encoded_training_data = {} def _create_model(self): @@ -69,25 +88,26 @@ def _create_model(self): x = Conv2D(256, (4, 4), activation='relu', kernel_regularizer=l2(2e-4))(x) x = Flatten()(x) - encoded_output = Dense(2048, activation='sigmoid', + encoded_output = Dense(4096, activation='sigmoid', kernel_regularizer=l2(1e-3))(x) self.base_model = Model( inputs=[input_image], outputs=[encoded_output]) else: classifier, preprocess_input = Classifiers.get(self.backbone) backbone_model = classifier( - input_shape=self.input_shape, weights='imagenet', include_top=False) + input_shape=self.input_shape, weights=self.backbone_weights, include_top=False) - for layer in backbone_model.layers: - layer.trainable = False + if self.freeze_backbone: + for layer in backbone_model.layers: + layer.trainable = False after_backbone = backbone_model.output x = Flatten()(after_backbone) - x = Dense(512, activation="relu")(x) - x = Dropout(0.1)(x) - x = Dense(256, activation="relu")(x) - x = Dropout(0.1)(x) - encoded_output = Dense(256, activation="relu")(x) + # x = Dense(512, activation="relu")(x) + # x = Dropout(0.5)(x) + # x = Dense(512, activation="relu")(x) + # x = Dropout(0.5)(x) + encoded_output = Dense(4096, activation="relu")(x) self.base_model = Model( inputs=[backbone_model.input], outputs=[encoded_output]) @@ -102,6 +122,7 @@ def _create_model(self): prediction = Dense(units=1, activation='sigmoid')(distance) metric = 'binary_accuracy' + elif self.mode == 'l2': L2_layer = Lambda( @@ -110,10 +131,12 @@ def _create_model(self): prediction = distance metric = self.accuracy + + # self.l_model = Model(inputs=[image_encoding_1, image_encoding_2], outputs=[prediction]) self.model = Model( inputs=[input_image_1, input_image_2], outputs=prediction) - plot_model(self.model, to_file='plots/model.png') + plot_model(self.model, to_file='{}model.png'.format(self.plots_path)) print('BASE MODEL SUMMARY') self.base_model.summary() @@ -160,57 +183,14 @@ def validate_on_batch(self, batch_size=8, s="val"): pairs, targets) return val_loss, val_accuracy - def train(self, steps_per_epoch, epochs, val_steps=100, with_val=True, batch_size=8, verbose=1): - generator_train = self.data_loader.generate(batch_size, 'train') - train_accuracies_epochs = [] - train_losses_epochs = [] - val_accuracies_epochs = [] - val_losses_epochs = [] - best_val_accuracy = 0.0 - current_accuracy = 0.0 - tensorboard_names = ['train_loss', 'train_acc', 'val_loss', 'val_acc'] - for j in range(epochs): - train_accuracies_it = [] - train_losses_it = [] - for i in range(steps_per_epoch): - pairs, targets = next(generator_train) - train_loss_it, train_accuracy_it = self.model.train_on_batch( - pairs, targets) - train_accuracies_it.append(train_accuracy_it) - train_losses_it.append(train_loss_it) - train_loss_epoch = sum(train_losses_it) / len(train_losses_it) - train_accuracy_epoch = sum( - train_accuracies_it) / len(train_accuracies_it) - train_accuracies_epochs.append(train_accuracy_epoch) - train_losses_epochs.append(train_loss_epoch) - - if with_val: - val_loss, val_accuracy = self.validate( - number_of_comparisons=val_steps) - val_accuracies_epochs.append(val_accuracy) - val_losses_epochs.append(val_loss) - if verbose: - print('[Epoch {}] train_loss: {} , train_acc: {}, val_loss: {} , val_acc: {}'.format( - j, train_loss_epoch, train_accuracy_epoch, val_loss, val_accuracy)) - logs = [train_loss_epoch, train_accuracy_epoch, - val_loss, val_accuracy] - - if val_accuracy > best_val_accuracy and self.weights_save_path: - best_val_accuracy = val_accuracy - self.base_model.save( - "{}best_model.h5".format(self.weights_save_path)) - else: - if verbose: - print('[Epoch {}] train_loss: {} , train_acc: {}'.format( - j, train_loss_epoch, train_accuracy_epoch)) - tensorboard_names = tensorboard_names[:2] - logs = [train_loss_epoch, train_accuracy_epoch] - if self.tensorboard_callback: - self.write_log(tensorboard_names, logs, j) - if with_val: - return train_losses_epochs, train_accuracies_epochs, val_losses_epochs, val_accuracies_epochs - else: - return train_losses_epochs, train_accuracies_epochs + def train_generator(self, steps_per_epoch, epochs, callbacks = [], val_steps=100, with_val=True, batch_size=8, verbose=1): + + train_generator = self.data_loader.generate(batch_size, s="train") + val_generator = self.data_loader.generate(batch_size, s="val") + + history = self.model.fit_generator(train_generator, steps_per_epoch=steps_per_epoch, epochs=epochs, + verbose=verbose, validation_data = val_generator, validation_steps = val_steps, callbacks=callbacks) + return history def validate(self, number_of_comparisons=100, batch_size=4, s="val"): generator = self.data_loader.generate(batch_size, s=s) @@ -237,20 +217,30 @@ def _generate_encoding(self, img_path): encoding = self.base_model.predict(np.expand_dims(img, axis=0)) return encoding - def generate_encodings(self, encodings_path='encodings/', save_file_name='encodings.pkl'): + def generate_encodings(self, save_file_name='encodings.pkl', max_num_samples_of_each_classes=10, shuffle = True): data_paths, data_labels, data_encodings = [], [], [] + classes_counter = {} + + if shuffle: + c = list(zip(self.data_loader.images_paths['train'], self.data_loader.images_labels['train'])) + random.shuffle(c) + self.data_loader.images_paths['train'], self.data_loader.images_labels['train'] = zip(*c) for img_path, img_label in zip(self.data_loader.images_paths['train'], self.data_loader.images_labels['train']): - data_paths.append(img_path) - data_labels.append(img_label) - data_encodings.append(self._generate_encoding(img_path)) + if img_label not in classes_counter: + classes_counter[img_label] = 0 + classes_counter[img_label] += 1 + if classes_counter[img_label] < max_num_samples_of_each_classes: + data_paths.append(img_path) + data_labels.append(img_label) + data_encodings.append(self._generate_encoding(img_path)) self.encoded_training_data['paths'] = data_paths self.encoded_training_data['labels'] = data_labels self.encoded_training_data['encodings'] = np.squeeze( np.array(data_encodings)) - os.makedirs('encodings/', exist_ok=True) - f = open(os.path.join(encodings_path, save_file_name), "wb") + + f = open(os.path.join(self.encodings_path, save_file_name), "wb") pickle.dump(self.encoded_training_data, f) f.close() @@ -262,17 +252,18 @@ def load_encodings(self, path_to_encodings): print("Problem with encodings file") def calculate_distances(self, encoding): - dist = ( - self.encoded_training_data['encodings'] - np.array(encoding))**2 - dist = np.sum(dist, axis=1) - dist = np.sqrt(dist) - return dist + training_encodings = self.encoded_training_data['encodings'] + return np.sqrt( + np.sum((training_encodings - np.array(encoding))**2, axis=1)) def predict(self, image_path): img = cv2.imread(image_path) img = cv2.resize(img, (self.input_shape[0], self.input_shape[1])) encoding = self.base_model.predict(np.expand_dims(img, axis=0)) distances = self.calculate_distances(encoding) - max_element = np.argmax(distances) + max_element = np.argmin(distances) predicted_label = self.encoded_training_data['labels'][max_element] return predicted_label + + def calculate_prediction_accuracy(self): + pass \ No newline at end of file diff --git a/plots/model.png b/plots/model.png deleted file mode 100644 index cba41cb7677e5e31df80f2b04778b3069f0f9376..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10210 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 diff --git a/plots/train_loss.png b/plots/train_loss.png deleted file mode 100644 index a8ed4fcef95a5d3646e91e9ea86bdf7e661fa81d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14180 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 FzX6sT1;qdW diff --git a/plots/val_acc.png b/plots/val_acc.png deleted file mode 100644 index facc720b4d9cfb05c75ab5617a303657eae8727a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14698 zcmd^mXIPZknr$J5HXz!H3My!e0Z>R5uk*%CIcqSVEytSg6O3 zDpM%)E>S2HXv1IgZ4Ogae!_n)Kb^mU?-yGhKW#&yu%0DZ1O$G6o!`RR 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 W+s2OvoyTudsK-toO*(w$%Krhkf3xub diff --git a/plots/val_loss.png b/plots/val_loss.png deleted file mode 100644 index 4fcf7b5a5a3d381c6013717e8e16b3a73109575a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16737 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 diff --git a/test_net.py b/test_net.py index 024a478..06f6a30 100644 --- a/test_net.py +++ b/test_net.py @@ -1,11 +1,14 @@ +import os from model import SiameseNet from data_loader import SiameseImageLoader import matplotlib.pyplot as plt from keras import optimizers import albumentations as A +from keras.callbacks import TensorBoard, LearningRateScheduler +from keras.callbacks import EarlyStopping, ReduceLROnPlateau, ModelCheckpoint -def plot_grapth(values, y_label, title): +def plot_grapth(values, y_label, title, project_name): t = list(range(len(values))) fig, ax = plt.subplots() ax.plot(t, values) @@ -14,48 +17,83 @@ def plot_grapth(values, y_label, title): title='{}'.format(title)) ax.grid() - fig.savefig("plots/{}.png".format(y_label)) + fig.savefig("plots/{}{}.png".format(project_name, y_label)) -dataset_path = '/home/rauf/plates_competition/dataset/to_train/' -n_epochs = 20 -n_steps_per_epoch = 600 -batch_size = 16 +project_name = 'road_signs/' +dataset_path = '/home/rauf/plates_competition/dataset/road_signs/road_signs_separated/' +n_epochs = 1000 +n_steps_per_epoch = 200 +batch_size = 32 val_steps = 100 +input_shape = (75, 75, 3) -augmentations = A.Compose([ - A.RandomBrightnessContrast(p=0.4), - A.RandomGamma(p=0.4), - A.HueSaturationValue(hue_shift_limit=20, - sat_shift_limit=50, val_shift_limit=50, p=0.4), - A.CLAHE(p=0.4), - A.HorizontalFlip(p=0.5), - A.VerticalFlip(p=0.5), - A.Blur(blur_limit=5, p=0.3), - A.GaussNoise(var_limit=(100, 150), p=0.3), - A.CenterCrop(p=1, height=256, width=256) -], p=1) +# augmentations = A.Compose([ +# A.RandomBrightnessContrast(p=0.4), +# A.RandomGamma(p=0.4), +# A.HueSaturationValue(hue_shift_limit=20, +# sat_shift_limit=50, val_shift_limit=50, p=0.4), +# A.CLAHE(p=0.4), +# A.HorizontalFlip(p=0.5), +# A.VerticalFlip(p=0.5), +# A.Blur(blur_limit=5, p=0.3), +# A.GaussNoise(var_limit=(100, 150), p=0.3), +# A.CenterCrop(p=1, height=256, width=256) +# ], p=0.5) -loader = SiameseImageLoader(dataset_path, input_shape=( - 256, 256, 3), augmentations=augmentations) +augmentations = None +loader = SiameseImageLoader( + dataset_path, input_shape=input_shape, augmentations=augmentations) -optimizer = optimizers.Adam(lr=1e-5) +optimizer = optimizers.Adam(lr=1e-4) # optimizer = optimizers.RMSprop(lr=1e-5) -model = SiameseNet(input_shape=(256, 256, 3), backbone='resnet50', mode='l2', - image_loader=loader, optimizer=optimizer) +# model = SiameseNet(input_shape=(256, 256, 3), backbone='resnet50', mode='l2', +# image_loader=loader, optimizer=optimizer) -train_losses, train_accuracies, val_losses, val_accuracies = model.train( - steps_per_epoch=n_steps_per_epoch, val_steps=val_steps, epochs=n_epochs) +model = SiameseNet(input_shape=input_shape, backbone='resnet50', backbone_weights='imagenet', mode='l2', + image_loader=loader, optimizer=optimizer, project_name=project_name, + freeze_backbone=True) -plot_grapth(train_losses, 'train_loss', 'Losses on train') -plot_grapth(train_accuracies, 'train_acc', 'Accuracies on train') -plot_grapth(val_losses, 'val_loss', 'Losses on val') -plot_grapth(val_accuracies, 'val_acc', 'Accuracies on val') + +def step_decay_schedule(initial_lr=1e-3, decay_factor=0.75, step_size=10): + ''' + Wrapper function to create a LearningRateScheduler with step decay schedule. + ''' + def schedule(epoch): + return initial_lr * (decay_factor ** np.floor(epoch/step_size)) + + return LearningRateScheduler(schedule) + + +callbacks = [ + step_decay_schedule(initial_lr=1e-4, decay_factor=0.99, step_size=1), + EarlyStopping(patience=50, verbose=1), + TensorBoard(log_dir=SiameseNet.tensorboard_log_path), + # ReduceLROnPlateau(factor=0.9, patience=50, + # min_lr=1e-12, verbose=1), + ModelCheckpoint(filepath=os.path.join(SiameseNet.weights_save_path, 'best_model.hd5'), verbose=1, monitor='loss', + save_best_only=True) +] + +# train_losses, train_accuracies, val_losses, val_accuracies = model.train( +# steps_per_epoch=n_steps_per_epoch, val_steps=val_steps, epochs=n_epochs) + +H = model.train_generator(steps_per_epoch=n_steps_per_epoch, callbacks=callbacks, + val_steps=val_steps, epochs=n_epochs) +train_losses = H.history['loss'] +train_accuracies = H.history['accuracy'] +val_losses = H.history['val_loss'] +val_accuracies = H.history['val_accuracy'] + +plot_grapth(train_losses, 'train_loss', 'Losses on train', project_name) +plot_grapth(train_accuracies, 'train_acc', 'Accuracies on train', project_name) +plot_grapth(val_losses, 'val_loss', 'Losses on val', project_name) +plot_grapth(val_accuracies, 'val_acc', 'Accuracies on val', project_name) model.generate_encodings() # model.load_encodings('encodings/encodings.pkl') prediction = model.predict( - '/home/rauf/plates_competition/dataset/test/0000.jpg') + '/home/rauf/plates_competition/dataset/road_signs/road_signs_separated/val/7_1/rtsd-r3_test_009188.png') print(prediction)