diff --git a/4 - TransferLearningKeras.py b/4 - TransferLearningKeras.py index b2c62a7..282b452 100644 --- a/4 - TransferLearningKeras.py +++ b/4 - TransferLearningKeras.py @@ -7,183 +7,117 @@ import seaborn as sn from PIL import ImageFile from tensorflow import keras +from model_builders import ImageClassModelBuilder, ImageClassModels + ImageFile.LOAD_TRUNCATED_IMAGES = True input_shape = (224, 224, 3) -batch_size = 64 -model_name = "TF2_Mobilenet_V2_transfer" +batch_size = 32 +model_name = f"mobilenetv2-dense1024-l1l2-25drop-{time()}" -# preproc = keras.applications.inception_v3.preprocess_input -preproc = keras.applications.mobilenet_v2.preprocess_input - -train_idg = keras.preprocessing.image.ImageDataGenerator( +training_idg = keras.preprocessing.image.ImageDataGenerator( horizontal_flip=True, - rescale=1. / 255, - # rotation_range=30, - # width_shift_range=[-.1, .1], - # height_shift_range=[-.1, .1], - # preprocessing_function=preproc + rotation_range=30, + width_shift_range=[-.1, .1], + height_shift_range=[-.1, .1], ) -train_gen = train_idg.flow_from_directory( - './downloads', - target_size=(input_shape[0], input_shape[1]), - batch_size=batch_size, - class_mode='categorical', - shuffle=True, - color_mode='rgb' -) - -val_idg = keras.preprocessing.image.ImageDataGenerator( +testing_idg = keras.preprocessing.image.ImageDataGenerator( horizontal_flip=True, - rescale=1. / 255, - # rotation_range=30, - # width_shift_range=[-.1, .1], - # height_shift_range=[-.1, .1], - # preprocessing_function=keras.applications.mobilenet_v2.preprocess_input -) -val_gen = val_idg.flow_from_directory( - './data/val', - target_size=(input_shape[0], input_shape[1]), - batch_size=batch_size, - class_mode='categorical', - shuffle=True, ) -print((val_gen.classes)) -nclass = len(train_gen.class_indices) -print(nclass) -# for _ in range(5): -# img, label = train_gen.next() -# print(img.shape) # (1,256,256,3) -# plt.imshow(img[0]) -# plt.show() -# plt.imshow( -# base_model = vgg16.VGG16( -# weights='imagenet', -# include_top=False, -# input_shape=input_shape -# ) -# base_model = keras.applications.InceptionV3( -# weights='imagenet', -# include_top=False, -# input_shape=input_shape -# ) -# base_model = keras.applications.xception.Xception( -# weights='imagenet', -# include_top=False, -# input_shape=input_shape -# ) +def get_gen(path, test_set=False): + idg = testing_idg if test_set else training_idg + return idg.flow_from_directory( + path, + target_size=(input_shape[0], input_shape[1]), + batch_size=batch_size, + class_mode='categorical', + shuffle=True, + color_mode='rgb' + ) -base_model = keras.applications.mobilenet_v2.MobileNetV2( - weights='imagenet', - include_top=False, - input_shape=input_shape -) -base_model.trainable = False -# i = keras.layers.Input([input_shape[0], input_shape[1], input_shape[2]]) -i = base_model.input -# x = preproc(i) -# x = base_model -x = keras.layers.GlobalAveragePooling2D()(base_model.output) -x = keras.layers.Dense(1024, activation='relu')(x) -x = keras.layers.Dropout(0.5)(x) -output = keras.layers.Dense(nclass, activation='softmax')(x) -model = keras.Model(inputs=i, outputs=output) +def train_model(train_gen, val_gen): + model = ImageClassModelBuilder( + input_shape=input_shape, + n_classes=807, + optimizer=keras.optimizers.Adam(learning_rate=.0001), + pre_trained=True, + fine_tune=0, + base_model=ImageClassModels.MOBILENET_V2 + ).create_model() + # Train the model + checkpoint = keras.callbacks.ModelCheckpoint(f"./Models/keras/{model_name}.hdf5", monitor='val_loss', verbose=1, + save_best_only=True, + mode='min') + early = keras.callbacks.EarlyStopping(monitor="loss", mode="min", patience=15) + tensorboard = keras.callbacks.TensorBoard( + log_dir="logs/" + model_name, + histogram_freq=1, + write_graph=True, + write_images=True, + update_freq=1, + profile_batch=2, + embeddings_freq=1, + ) + callbacks_list = [checkpoint, early, tensorboard] -model.compile(optimizer=keras.optimizers.Adam(learning_rate=.0001), - loss=keras.losses.CategoricalCrossentropy(), - metrics=['accuracy']) + history = model.fit( + train_gen, + validation_data=val_gen, + epochs=100, + batch_size=batch_size, + shuffle=True, + verbose=True, + workers=12, + callbacks=callbacks_list, + max_queue_size=1000 + ) + print(history) + return model -model.summary() -print(model.output_shape) -# Train the model -file_path = "weights.mobilenet.best.hdf5" +def test_model(model, test_gen): + print(len(test_gen.filenames)) + score = model.evaluate(test_gen, workers=8, steps=len(test_gen)) + predicts = model.predict(test_gen, verbose=True, workers=8, steps=len(test_gen)) + print("Loss: ", score[0], "Accuracy: ", score[1]) + print(score) + print(predicts) + print(type(predicts)) + print(predicts.shape) -checkpoint = keras.callbacks.ModelCheckpoint(file_path, monitor='val_loss', verbose=1, save_best_only=True, - mode='min') + # Process the predictions + predicts = np.argmax(predicts, + axis=1) + label_index = {v: k for k, v in test_gen.class_indices.items()} + predicts = [label_index[p] for p in predicts] + reals = [label_index[p] for p in test_gen.classes] -early = keras.callbacks.EarlyStopping(monitor="loss", mode="min", patience=15) + # Save the results + df = pd.DataFrame(columns=['fname', 'prediction', 'true_val']) + df['fname'] = [x for x in test_gen.filenames] + df['prediction'] = predicts + df["true_val"] = reals + df.to_csv("sub1.csv", index=False) + # Processed the saved results + from sklearn.metrics import accuracy_score, confusion_matrix + acc = accuracy_score(reals, predicts) + conf_mat = confusion_matrix(reals, predicts) + print("Testing accuracy score is ", acc) + print("Confusion Matrix", conf_mat) + df_cm = pd.DataFrame(conf_mat, index=[i for i in list(set(reals))], + columns=[i for i in list(set(reals))]) + plt.figure(figsize=(10, 7)) + sn.heatmap(df_cm, annot=True) + plt.show() -tensorboard = keras.callbacks.TensorBoard( - log_dir="logs/" + model_name + "{}".format(time()), - histogram_freq=1, - write_graph=True, - write_images=True, - update_freq=1, - profile_batch=2, - embeddings_freq=1 -) -callbacks_list = [checkpoint, early, tensorboard] # early - -history = model.fit( - train_gen, - validation_data=val_gen, - epochs=20, - batch_size=batch_size, - shuffle=True, - verbose=True, - callbacks=callbacks_list -) - -# Create Test generator -test_idg = keras.preprocessing.image.ImageDataGenerator( - rescale=1. / 255, -) - -test_gen = test_idg.flow_from_directory( - './data/test', - target_size=(input_shape[0], input_shape[1]), - batch_size=batch_size, - shuffle=False -) - -len(test_gen.filenames) - -score = model.evaluate_generator(test_gen, workers=1, steps=len(test_gen)) - -# predicts -predicts = model.predict_generator(test_gen, verbose=True, workers=1, steps=len(test_gen)) - -print("Loss: ", score[0], "Accuracy: ", score[1]) -print(score) - -print(predicts) -print(type(predicts)) -print(predicts.shape) -# Process the predictions -predicts = np.argmax(predicts, - axis=1) -# test_gen.reset() -label_index = {v: k for k, v in train_gen.class_indices.items()} -predicts = [label_index[p] for p in predicts] -reals = [label_index[p] for p in test_gen.classes] - -# Save the results -print(label_index) -print(test_gen.classes) -print(test_gen.classes.shape) -print(type(test_gen.classes)) -df = pd.DataFrame(columns=['fname', 'prediction', 'true_val']) -df['fname'] = [x for x in test_gen.filenames] -df['prediction'] = predicts -df["true_val"] = reals -df.to_csv("sub1.csv", index=False) - -# Processed the saved results -from sklearn.metrics import accuracy_score, confusion_matrix - -acc = accuracy_score(reals, predicts) -conf_mat = confusion_matrix(reals, predicts) -print("Testing accuracy score is ", acc) -print("Confusion Matrix", conf_mat) - -df_cm = pd.DataFrame(conf_mat, index=[i for i in list(set(reals))], - columns=[i for i in list(set(reals))]) -plt.figure(figsize=(10, 7)) -sn.heatmap(df_cm, annot=True) -plt.show() +if __name__ == "__main__": + train_gen = get_gen('./data/train') + val_gen = get_gen('./data/val') + test_gen = get_gen('./data/test', test_set=True) + model = train_model(train_gen, val_gen) + test_model(model, test_gen) diff --git a/5 - TestModel.py b/5 - TestModel.py index ed4c0c9..f29bdef 100644 --- a/5 - TestModel.py +++ b/5 - TestModel.py @@ -31,17 +31,17 @@ test_gen = test_idg.flow_from_directory( ) -predicts = model.predict_generator(test_gen, verbose=True, workers=1, steps=len(test_gen)) +predictions = model.predict_generator(test_gen, verbose=True, workers=1, steps=len(test_gen)) -print(predicts) -print(type(predicts)) -print(predicts.shape) +print(predictions) +print(type(predictions)) +print(predictions.shape) # Process the predictions -predicts = np.argmax(predicts, - axis=1) +predictions = np.argmax(predictions, + axis=1) # test_gen.reset() label_index = {v: k for k, v in test_gen.class_indices.items()} -predicts = [label_index[p] for p in predicts] +predictions = [label_index[p] for p in predictions] reals = [label_index[p] for p in test_gen.classes] # Save the results @@ -51,15 +51,15 @@ print(test_gen.classes.shape) print(type(test_gen.classes)) df = pd.DataFrame(columns=['fname', 'prediction', 'true_val']) df['fname'] = [x for x in test_gen.filenames] -df['prediction'] = predicts +df['prediction'] = predictions df["true_val"] = reals df.to_csv("sub1_non_transfer.csv", index=False) # Processed the saved results -acc = accuracy_score(reals, predicts) -conf_mat = confusion_matrix(reals, predicts) -print(classification_report(reals, predicts, labels=[l for l in label_index.values()])) +acc = accuracy_score(reals, predictions) +conf_mat = confusion_matrix(reals, predictions) +print(classification_report(reals, predictions, labels=[l for l in label_index.values()])) print("Testing accuracy score is ", acc) print("Confusion Matrix", conf_mat) diff --git a/Models/keras/mobilenetv2-dense1024-l1l2-25drop-1653521962.9923482.hdf5 b/Models/keras/mobilenetv2-dense1024-l1l2-25drop-1653521962.9923482.hdf5 new file mode 100644 index 0000000..0cb78f7 Binary files /dev/null and b/Models/keras/mobilenetv2-dense1024-l1l2-25drop-1653521962.9923482.hdf5 differ diff --git a/Models/mobilenetv2-stock-all-fixed-v2/mobilenetv2.hdf5 b/Models/mobilenetv2-stock-all-fixed-v2/mobilenetv2.hdf5 deleted file mode 100644 index d30698a..0000000 Binary files a/Models/mobilenetv2-stock-all-fixed-v2/mobilenetv2.hdf5 and /dev/null differ diff --git a/Models/mobilenetv2-imagenet-all-v1/mobilenet-v2-imagenet-core.tflite b/Models/tflite/mobilenetv2-imagenet-all-v1/mobilenet-v2-imagenet-core.tflite similarity index 100% rename from Models/mobilenetv2-imagenet-all-v1/mobilenet-v2-imagenet-core.tflite rename to Models/tflite/mobilenetv2-imagenet-all-v1/mobilenet-v2-imagenet-core.tflite diff --git a/Models/mobilenetv2-stock-all-fixed-v2/labels.txt b/Models/tflite/mobilenetv2-stock-all-fixed-v2/labels.txt similarity index 100% rename from Models/mobilenetv2-stock-all-fixed-v2/labels.txt rename to Models/tflite/mobilenetv2-stock-all-fixed-v2/labels.txt diff --git a/Models/mobilenetv2-stock-all-fixed-v2/mobilenetv2.tflite b/Models/tflite/mobilenetv2-stock-all-fixed-v2/mobilenetv2.tflite similarity index 100% rename from Models/mobilenetv2-stock-all-fixed-v2/mobilenetv2.tflite rename to Models/tflite/mobilenetv2-stock-all-fixed-v2/mobilenetv2.tflite diff --git a/Models/mobilenetv2-stock-all-v1/labels.txt b/Models/tflite/mobilenetv2-stock-all-v1/labels.txt similarity index 100% rename from Models/mobilenetv2-stock-all-v1/labels.txt rename to Models/tflite/mobilenetv2-stock-all-v1/labels.txt diff --git a/Models/mobilenetv2-stock-all-v1/mobilenetv2.hdf5 b/Models/tflite/mobilenetv2-stock-all-v1/mobilenetv2.hdf5 similarity index 100% rename from Models/mobilenetv2-stock-all-v1/mobilenetv2.hdf5 rename to Models/tflite/mobilenetv2-stock-all-v1/mobilenetv2.hdf5 diff --git a/model_builders/__init__.py b/model_builders/__init__.py new file mode 100644 index 0000000..8e019e8 --- /dev/null +++ b/model_builders/__init__.py @@ -0,0 +1 @@ +from .image_class_builder import ImageClassModelBuilder, ImageClassModels \ No newline at end of file diff --git a/model_builders/image_class_builder.py b/model_builders/image_class_builder.py new file mode 100644 index 0000000..6215344 --- /dev/null +++ b/model_builders/image_class_builder.py @@ -0,0 +1,76 @@ +from enum import Enum +from typing import Tuple + +import tensorflow as tf +from tensorflow import keras + +from .modelwrapper import ModelWrapper + + +class ImageClassModels(Enum): + INCEPTION_V3 = ModelWrapper( + keras.applications.InceptionV3, + keras.applications.inception_v3.preprocess_input + ) + XCEPTION = ModelWrapper( + keras.applications.xception.Xception, + keras.applications.inception_v3.preprocess_input + ) + MOBILENET_V2 = ModelWrapper( + keras.applications.mobilenet_v2.MobileNetV2, + keras.applications.mobilenet_v2.preprocess_input + ) + + +class ImageClassModelBuilder(object): + + def __init__(self, + input_shape: Tuple[int, int, int], + n_classes: int, + optimizer: tf.keras.optimizers.Optimizer = keras.optimizers.Adam( + learning_rate=.0001), + pre_trained: bool = True, + fine_tune: int = 0, + base_model: ImageClassModels = ImageClassModels.MOBILENET_V2): + self.input_shape = input_shape + self.n_classes = n_classes + self.optimizer = optimizer + self.pre_trained = pre_trained + self.fine_tune = fine_tune + self.base_model = base_model + + def set_base_model(self, base_model: ImageClassModels): + self.base_model = base_model + + def create_model(self): + + base_model = self.base_model.value.model_func( + weights='imagenet' if self.pre_trained else None, + include_top=False + ) + if self.pre_trained: + if self.fine_tune > 0: + for layer in base_model.layers[:-self.fine_tune]: + layer.trainable = False + else: + for layer in base_model.layers: + layer.trainable = False + + i = tf.keras.layers.Input([self.input_shape[0], self.input_shape[1], self.input_shape[2]], dtype=tf.float32) + x = tf.cast(i, tf.float32) + x = self.base_model.value.model_preprocessor(x) + x = base_model(x) + x = keras.layers.GlobalAveragePooling2D()(x) + x = keras.layers.Dense(1024, activation='relu', kernel_regularizer=keras.regularizers.L1L2(l1=1e-5, l2=1e-5))(x) + x = keras.layers.Dropout(0.25)(x) + output = keras.layers.Dense(self.n_classes, activation='softmax')(x) + + model = keras.Model(inputs=i, outputs=output) + model.compile(optimizer=self.optimizer, + loss=keras.losses.CategoricalCrossentropy(), + metrics=[ + 'accuracy', + # 'mse' + ]) + model.summary() + return model diff --git a/model_builders/modelwrapper.py b/model_builders/modelwrapper.py new file mode 100644 index 0000000..bae8164 --- /dev/null +++ b/model_builders/modelwrapper.py @@ -0,0 +1,7 @@ +from collections import Callable + + +class ModelWrapper(object): + def __init__(self, model_func:Callable, model_preprocessor:Callable): + self.model_func = model_func + self.model_preprocessor = model_preprocessor