diff --git a/.gitignore b/.gitignore index 7792f5a..84d749c 100644 --- a/.gitignore +++ b/.gitignore @@ -107,3 +107,5 @@ venv.bak/ *DS_Store* tf_log/ to_test.ipynb +encodings/ +weights/ diff --git a/data_loader.py b/data_loader.py index 550b590..5e325e3 100644 --- a/data_loader.py +++ b/data_loader.py @@ -97,3 +97,10 @@ def generate(self, batch_size, s="train"): while True: pairs, targets = self.get_batch(batch_size, s) yield (pairs, targets) + + def get_image(self, img_path): + img = cv2.imread(img_path) + if self.input_shape: + img = cv2.resize( + img, (self.input_shape[0], self.input_shape[1])) + return img diff --git a/model.py b/model.py index ee1828f..9dcaac1 100644 --- a/model.py +++ b/model.py @@ -1,7 +1,10 @@ import os +import glob import numpy as np import keras.backend as K import tensorflow as tf +import pickle +import cv2 from keras.models import Model from keras import optimizers from keras.regularizers import l2 @@ -20,21 +23,32 @@ class SiameseNet: """ - def __init__(self, input_shape, image_loader, mode='l1', backbone='resnet50', optimizer=optimizers.Adam(lr=1e-4), tensorboard_log_path='tf_log'): + 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/'): self.input_shape = input_shape self.backbone = backbone self.mode = mode self.optimizer = optimizer self.model = [] + self.base_model = [] self._create_model() self.data_loader = image_loader - self.base_model = [] + self.encoded_training_data = {} if tensorboard_log_path: os.makedirs(tensorboard_log_path, exist_ok=True) self.tensorboard_callback = TensorBoard( 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*')) + 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) def _create_model(self): @@ -73,7 +87,7 @@ def _create_model(self): x = Dropout(0.1)(x) x = Dense(256, activation="relu")(x) x = Dropout(0.1)(x) - encoded_output = Dense(128, activation="relu")(x) + encoded_output = Dense(256, activation="relu")(x) self.base_model = Model( inputs=[backbone_model.input], outputs=[encoded_output]) @@ -105,6 +119,7 @@ def _create_model(self): print('WHOLE MODEL SUMMARY') self.model.summary() + self.model.compile(loss=self.contrastive_loss, metrics=[metric], optimizer=self.optimizer) @@ -145,14 +160,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, with_val=True, batch_size=8, verbose=1): + 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_loss = 0.0 - current_loss = 0.0 + 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 = [] @@ -170,7 +185,8 @@ def train(self, steps_per_epoch, epochs, with_val=True, batch_size=8, verbose=1) train_losses_epochs.append(train_loss_epoch) if with_val: - val_loss, val_accuracy = self.validate() + 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: @@ -178,6 +194,11 @@ def train(self, steps_per_epoch, epochs, with_val=True, batch_size=8, verbose=1) 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( @@ -197,9 +218,13 @@ def validate(self, number_of_comparisons=100, batch_size=4, s="val"): val_losses_it = [] for _ in range(number_of_comparisons): pairs, targets = next(generator) + # predictions = self.model.predict(pairs) + val_loss_it, val_accuracy_it = self.model.test_on_batch( pairs, targets) - print(targets) + # print(predictions) + # print(targets) + # print('================================') val_accuracies_it.append(val_accuracy_it) val_losses_it.append(val_loss_it) val_loss_epoch = sum(val_losses_it) / len(val_losses_it) @@ -207,16 +232,47 @@ def validate(self, number_of_comparisons=100, batch_size=4, s="val"): val_accuracies_it) / len(val_accuracies_it) return val_loss_epoch, val_accuracy_epoch - def generate_encodings(self): + def _generate_encoding(self, img_path): + img = self.data_loader.get_image(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'): + data_paths, data_labels, data_encodings = [], [], [] + + 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)) + 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") + pickle.dump(self.encoded_training_data, f) + f.close() - paths = self.data_loader.images_paths['train'] - labels = self.data_loader.images_labels['train'] + def load_encodings(self, path_to_encodings): + try: + with open(path_to_encodings, 'rb') as f: + self.encoded_training_data = pickle.load(f) + except: + print("Problem with encodings file") - data = {} - for path in paths: - info = {} - img = cv2.imread(path) - info['encoding'] = self.base_model.predict(img) + 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 - def predict(self, batch_size=8): - pass + 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) + predicted_label = self.encoded_training_data['labels'][max_element] + return predicted_label diff --git a/plots/train_acc.png b/plots/train_acc.png index 3db9d84..1bc70bb 100644 Binary files a/plots/train_acc.png and b/plots/train_acc.png differ diff --git a/plots/train_loss.png b/plots/train_loss.png index 0d2ec19..a8ed4fc 100644 Binary files a/plots/train_loss.png and b/plots/train_loss.png differ diff --git a/plots/val_acc.png b/plots/val_acc.png index d76b28d..facc720 100644 Binary files a/plots/val_acc.png and b/plots/val_acc.png differ diff --git a/plots/val_loss.png b/plots/val_loss.png index 5374923..4fcf7b5 100644 Binary files a/plots/val_loss.png and b/plots/val_loss.png differ diff --git a/test_net.py b/test_net.py index d87c394..024a478 100644 --- a/test_net.py +++ b/test_net.py @@ -17,8 +17,11 @@ def plot_grapth(values, y_label, title): fig.savefig("plots/{}.png".format(y_label)) -n_epochs = 50 -n_steps_per_epoch = 20 +dataset_path = '/home/rauf/plates_competition/dataset/to_train/' +n_epochs = 20 +n_steps_per_epoch = 600 +batch_size = 16 +val_steps = 100 augmentations = A.Compose([ A.RandomBrightnessContrast(p=0.4), @@ -28,24 +31,31 @@ def plot_grapth(values, y_label, title): A.CLAHE(p=0.4), A.HorizontalFlip(p=0.5), A.VerticalFlip(p=0.5), - A.Blur(blur_limit=11, p=0.3), + 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) -loader = SiameseImageLoader( - '/home/rauf/plates_competition/dataset/to_train/', input_shape=(256, 256, 3), augmentations=augmentations) +loader = SiameseImageLoader(dataset_path, input_shape=( + 256, 256, 3), augmentations=augmentations) optimizer = optimizers.Adam(lr=1e-5) # optimizer = optimizers.RMSprop(lr=1e-5) -model = SiameseNet(input_shape=(256, 256, 3), backbone='resnet18', mode='l2', +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, epochs=n_epochs) + steps_per_epoch=n_steps_per_epoch, val_steps=val_steps, epochs=n_epochs) 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') + + +model.generate_encodings() +# model.load_encodings('encodings/encodings.pkl') +prediction = model.predict( + '/home/rauf/plates_competition/dataset/test/0000.jpg') +print(prediction)