{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Import needed modules","metadata":{"id":"CKeVGxZ5GG6o"}},{"cell_type":"code","source":"!pip install tensorflow==2.9.1","metadata":{"_kg_hide-output":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# import system libs\nimport os\nimport time\nimport shutil\nimport pathlib\nimport itertools\n\n# import data handling tools\nimport cv2\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\nsns.set_style('darkgrid')\nimport matplotlib.pyplot as plt\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import confusion_matrix, classification_report\n\n# import Deep learning Libraries\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.optimizers import Adam, Adamax\nfrom tensorflow.keras.metrics import categorical_crossentropy\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Activation, Dropout, BatchNormalization\nfrom tensorflow.keras import regularizers\n\n# Ignore Warnings\nimport warnings\nwarnings.filterwarnings(\"ignore\")\n\nprint ('modules loaded')","metadata":{"id":"CeMcAy_5GG6s","outputId":"8e007371-6c2c-492c-99bb-172286922ae2","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Create needed functions","metadata":{"id":"SA_gwvwnGG6v"}},{"cell_type":"markdown","source":"## Functions to Create Data Frame from Dataset","metadata":{"id":"e4reLHLHabWD"}},{"cell_type":"markdown","source":"#### **Function to create data frame**","metadata":{"id":"JQdhl_CRGG6v"}},{"cell_type":"code","source":"# Generate data paths with labels\ndef define_paths(data_dir, csv_dir):\n    filepaths = []\n    labels = []\n    \n    df = pd.read_csv(csv_dir)\n    folds = os.listdir(data_dir)\n    \n    for file, i in zip(sorted(folds), df['gender']):\n            fpath = os.path.join(data_dir, file)\n            filepaths.append(fpath)\n            if i == 0:    \n                labels.append('0')\n            elif i == 1:\n                labels.append('1')\n\n    return filepaths, labels\n\n\n# Concatenate data paths with labels into one dataframe ( to later be fitted into the model )\ndef define_df(files, classes):\n    Fseries = pd.Series(files, name= 'filepaths')\n    Lseries = pd.Series(classes, name='labels')\n    return pd.concat([Fseries, Lseries], axis= 1)\n\ndef create_df(tr_dir, csv_dir):\n    # train and valid dataframe\n    files, classes = define_paths(tr_dir, csv_dir)\n    df = define_df(files, classes)\n    strat = df['labels']\n    train_df, valid_df = train_test_split(df, train_size= 0.8, shuffle= True, random_state= 123, stratify= strat)\n\n    return train_df, valid_df","metadata":{"id":"g2nDmYaAabWE","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Function to generate images from dataframe","metadata":{"id":"JZaHdeFxGG6x"}},{"cell_type":"code","source":"def create_gens(train_df, valid_df, batch_size):\n    '''\n    This function takes train, validation, and test dataframe and fit them into image data generator, because model takes data from image data generator.\n    Image data generator converts images into tensors. '''\n\n    # define model parameters\n    img_size = (224, 224)\n    channels = 3 # either BGR or Grayscale\n    color = 'rgb'\n    img_shape = (img_size[0], img_size[1], channels)\n\n    # This function which will be used in image data generator for data augmentation, it just take the image and return it again.\n    def scalar(img):\n        return img\n\n    tr_gen = ImageDataGenerator(preprocessing_function= scalar, horizontal_flip= True)\n    ts_gen = ImageDataGenerator(preprocessing_function= scalar)\n\n    train_gen = tr_gen.flow_from_dataframe( train_df, x_col= 'filepaths', y_col= 'labels', target_size= img_size, class_mode= 'categorical',\n                                        color_mode= color, shuffle= True, batch_size= batch_size)\n\n    valid_gen = ts_gen.flow_from_dataframe( valid_df, x_col= 'filepaths', y_col= 'labels', target_size= img_size, class_mode= 'categorical',\n                                        color_mode= color, shuffle= True, batch_size= batch_size)\n\n    return train_gen, valid_gen","metadata":{"id":"iLL8hHQcGG6x","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Function to trim data samples\nwe will use a function trim() that takes in a dataframe df, and integer max_size and a string column and returns a dataframe where the number of samples for any class specified by column is limited to max samples.","metadata":{}},{"cell_type":"code","source":"def trim (df, max_size, min_size, column):\n    df = df.copy()\n    original_class_count = len(list(df[column].unique()))\n    print ('Original Number of classes in dataframe: ', original_class_count)\n    \n    sample_list = [] \n    groups = df.groupby(column)\n    \n    for label in df[column].unique():        \n        group = groups.get_group(label)\n        sample_count = len(group)     \n        if sample_count > max_size :\n            strat = group[column]\n            samples, _ = train_test_split(group, train_size= max_size, shuffle= True, \n                                          random_state= 123, stratify= strat)            \n            sample_list.append(samples)\n      \n        elif sample_count >= min_size:\n            sample_list.append(group)\n    \n    df = pd.concat(sample_list, axis= 0).reset_index(drop= True)\n    final_class_count = len(list(df[column].unique())) \n    if final_class_count != original_class_count:\n        print ('*** WARNING***  dataframe has a reduced number of classes' )\n    balance = list(df[column].value_counts())\n    print (balance)\n    return df","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### **Function to display data sample**","metadata":{"id":"8ifXox4SGG6y"}},{"cell_type":"code","source":"def show_images(gen):\n    '''\n    This function take the data generator and show sample of the images\n    '''\n\n    # return classes , images to be displayed\n    g_dict = gen.class_indices        # defines dictionary {'class': index}\n    classes = list(g_dict.keys())     # defines list of dictionary's kays (classes), classes names : string\n    images, labels = next(gen)        # get a batch size samples from the generator\n\n    # calculate number of displayed samples\n    length = len(labels)        # length of batch size\n    sample = min(length, 25)    # check if sample less than 25 images\n\n    plt.figure(figsize= (20, 20))\n\n    for i in range(sample):\n        plt.subplot(5, 5, i + 1)\n        image = images[i] / 255       # scales data to range (0 - 255)\n        plt.imshow(image)\n        index = np.argmax(labels[i])  # get image index\n        class_name = classes[index]   # get class of image\n        plt.title(class_name, color= 'blue', fontsize= 12)\n        plt.axis('off')\n    plt.show()","metadata":{"id":"IAGbj3ZyGG6y","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### **Function to plot value counts for a column in a dataframe**","metadata":{"id":"E0saYcuCAU5-"}},{"cell_type":"code","source":"def plot_label_count(df, plot_title):\n    '''\n    This function take df and plot labels value counts\n    '''\n\n    # Define needed variables\n    vcounts = df['labels'].value_counts()\n    labels = vcounts.keys().tolist()\n    values = vcounts.tolist()\n    lcount = len(labels)\n\n    if lcount > 55:\n        print('The number of labels is > 55, no plot will be produced')\n\n    else:\n        plot_labels(lcount, labels, values, plot_title)\n\ndef plot_labels(lcount, labels, values, plot_title):\n    width = lcount * 4\n    width = np.min([width, 20])\n\n    plt.figure(figsize= (width, 5))\n\n    form = {'family': 'serif', 'color': 'blue', 'size': 25}\n    sns.barplot(x=labels, y=values)\n    plt.title(f'Images per Label in {plot_title} data', fontsize= 24, color= 'blue')\n    plt.xticks(rotation= 90, fontsize= 18)\n    plt.yticks(fontsize= 18)\n    plt.xlabel('CLASS', fontdict= form)\n    yaxis_label = 'IMAGE COUNT'\n    plt.ylabel(yaxis_label, fontdict= form)\n\n    rotation = 'vertical' if lcount >= 8 else 'horizontal'\n    for i in range(lcount):\n        plt.text(i, values[i] / 2, str(values[i]), fontsize= 12,\n                rotation= rotation, color= 'yellow', ha= 'center')\n\n    plt.show()","metadata":{"id":"ud7JP-zHARMO","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### **Callbacks** \n<br> \nCallbacks : Helpful functions to help optimize model training  <br> \nExamples: stop model training after specfic time, stop training if no improve in accuracy and so on.","metadata":{"id":"_K-ryg0DGG6z"}},{"cell_type":"code","source":"class MyCallback(keras.callbacks.Callback):\n    def __init__(self, model, patience, stop_patience, threshold, factor, batches, epochs, ask_epoch):\n        super(MyCallback, self).__init__()\n        self.model = model\n        self.patience = patience # specifies how many epochs without improvement before learning rate is adjusted\n        self.stop_patience = stop_patience # specifies how many times to adjust lr without improvement to stop training\n        self.threshold = threshold # specifies training accuracy threshold when lr will be adjusted based on validation loss\n        self.factor = factor # factor by which to reduce the learning rate\n        self.batches = batches # number of training batch to run per epoch\n        self.epochs = epochs\n        self.ask_epoch = ask_epoch\n        self.ask_epoch_initial = ask_epoch # save this value to restore if restarting training\n\n        # callback variables\n        self.count = 0 # how many times lr has been reduced without improvement\n        self.stop_count = 0\n        self.best_epoch = 1   # epoch with the lowest loss\n        self.initial_lr = float(tf.keras.backend.get_value(model.optimizer.lr)) # get the initial learning rate and save it\n        self.highest_tracc = 0.0 # set highest training accuracy to 0 initially\n        self.lowest_vloss = np.inf # set lowest validation loss to infinity initially\n        self.best_weights = self.model.get_weights() # set best weights to model's initial weights\n        self.initial_weights = self.model.get_weights()   # save initial weights if they have to get restored\n\n    # Define a function that will run when train begins\n    def on_train_begin(self, logs= None):\n#         msg = 'Do you want model asks you to halt the training [y/n] ?'\n#         print(msg)\n#         ans = input('')\n#         if ans in ['Y', 'y']:\n#             self.ask_permission = 1\n#         elif ans in ['N', 'n']:\n#             self.ask_permission = 0\n\n        msg = '{0:^8s}{1:^10s}{2:^9s}{3:^9s}{4:^9s}{5:^9s}{6:^9s}{7:^10s}{8:10s}{9:^8s}'.format('Epoch', 'Loss', 'Accuracy', 'V_loss', 'V_acc', 'LR', 'Next LR', 'Monitor','% Improv', 'Duration')\n        print(msg)\n        self.start_time = time.time()\n\n\n    def on_train_end(self, logs= None):\n        stop_time = time.time()\n        tr_duration = stop_time - self.start_time\n        hours = tr_duration // 3600\n        minutes = (tr_duration - (hours * 3600)) // 60\n        seconds = tr_duration - ((hours * 3600) + (minutes * 60))\n\n        msg = f'training elapsed time was {str(hours)} hours, {minutes:4.1f} minutes, {seconds:4.2f} seconds)'\n        print(msg)\n\n        # set the weights of the model to the best weights\n        self.model.set_weights(self.best_weights)\n\n\n    def on_train_batch_end(self, batch, logs= None):\n        # get batch accuracy and loss\n        acc = logs.get('accuracy') * 100\n        loss = logs.get('loss')\n\n        # prints over on the same line to show running batch count\n        msg = '{0:20s}processing batch {1:} of {2:5s}-   accuracy=  {3:5.3f}   -   loss: {4:8.5f}'.format(' ', str(batch), str(self.batches), acc, loss)\n        print(msg, '\\r', end= '')\n\n\n    def on_epoch_begin(self, epoch, logs= None):\n        self.ep_start = time.time()\n\n\n    # Define method runs on the end of each epoch\n    def on_epoch_end(self, epoch, logs= None):\n        ep_end = time.time()\n        duration = ep_end - self.ep_start\n\n        lr = float(tf.keras.backend.get_value(self.model.optimizer.lr)) # get the current learning rate\n        current_lr = lr\n        acc = logs.get('accuracy')  # get training accuracy\n        v_acc = logs.get('val_accuracy')  # get validation accuracy\n        loss = logs.get('loss')  # get training loss for this epoch\n        v_loss = logs.get('val_loss')  # get the validation loss for this epoch\n\n        if acc < self.threshold: # if training accuracy is below threshold adjust lr based on training accuracy\n            monitor = 'accuracy'\n            if epoch == 0:\n                pimprov = 0.0\n            else:\n                pimprov = (acc - self.highest_tracc ) * 100 / self.highest_tracc # define improvement of model progres\n\n            if acc > self.highest_tracc: # training accuracy improved in the epoch\n                self.highest_tracc = acc # set new highest training accuracy\n                self.best_weights = self.model.get_weights() # training accuracy improved so save the weights\n                self.count = 0 # set count to 0 since training accuracy improved\n                self.stop_count = 0 # set stop counter to 0\n                if v_loss < self.lowest_vloss:\n                    self.lowest_vloss = v_loss\n                self.best_epoch = epoch + 1  # set the value of best epoch for this epoch\n\n            else:\n                # training accuracy did not improve check if this has happened for patience number of epochs\n                # if so adjust learning rate\n                if self.count >= self.patience - 1: # lr should be adjusted\n                    lr = lr * self.factor # adjust the learning by factor\n                    tf.keras.backend.set_value(self.model.optimizer.lr, lr) # set the learning rate in the optimizer\n                    self.count = 0 # reset the count to 0\n                    self.stop_count = self.stop_count + 1 # count the number of consecutive lr adjustments\n                    self.count = 0 # reset counter\n                    if v_loss < self.lowest_vloss:\n                        self.lowest_vloss = v_loss\n                else:\n                    self.count = self.count + 1 # increment patience counter\n\n        else: # training accuracy is above threshold so adjust learning rate based on validation loss\n            monitor = 'val_loss'\n            if epoch == 0:\n                pimprov = 0.0\n\n            else:\n                pimprov = (self.lowest_vloss - v_loss ) * 100 / self.lowest_vloss\n\n            if v_loss < self.lowest_vloss: # check if the validation loss improved\n                self.lowest_vloss = v_loss # replace lowest validation loss with new validation loss\n                self.best_weights = self.model.get_weights() # validation loss improved so save the weights\n                self.count = 0 # reset count since validation loss improved\n                self.stop_count = 0\n                self.best_epoch = epoch + 1 # set the value of the best epoch to this epoch\n\n            else: # validation loss did not improve\n                if self.count >= self.patience - 1: # need to adjust lr\n                    lr = lr * self.factor # adjust the learning rate\n                    self.stop_count = self.stop_count + 1 # increment stop counter because lr was adjusted\n                    self.count = 0 # reset counter\n                    tf.keras.backend.set_value(self.model.optimizer.lr, lr) # set the learning rate in the optimizer\n\n                else:\n                    self.count = self.count + 1 # increment the patience counter\n\n                if acc > self.highest_tracc:\n                    self.highest_tracc = acc\n\n        msg = f'{str(epoch + 1):^3s}/{str(self.epochs):4s} {loss:^9.3f}{acc * 100:^9.3f}{v_loss:^9.5f}{v_acc * 100:^9.3f}{current_lr:^9.5f}{lr:^9.5f}{monitor:^11s}{pimprov:^10.2f}{duration:^8.2f}'\n        print(msg)\n\n        if self.stop_count > self.stop_patience - 1: # check if learning rate has been adjusted stop_count times with no improvement\n            msg = f' training has been halted at epoch {epoch + 1} after {self.stop_patience} adjustments of learning rate with no improvement'\n            print(msg)\n            self.model.stop_training = True # stop training\n\n#         else:\n#             if self.ask_epoch != None and self.ask_permission != 0:\n#                 if epoch + 1 >= self.ask_epoch:\n#                     msg = 'enter H to halt training or an integer for number of epochs to run then ask again'\n#                     print(msg)\n\n#                     ans = input('')\n#                     if ans == 'H' or ans == 'h':\n#                         msg = f'training has been halted at epoch {epoch + 1} due to user input'\n#                         print(msg)\n#                         self.model.stop_training = True # stop training\n\n#                     else:\n#                         try:\n#                             ans = int(ans)\n#                             self.ask_epoch += ans\n#                             msg = f' training will continue until epoch {str(self.ask_epoch)}'\n#                             print(msg)\n#                             msg = '{0:^8s}{1:^10s}{2:^9s}{3:^9s}{4:^9s}{5:^9s}{6:^9s}{7:^10s}{8:10s}{9:^8s}'.format('Epoch', 'Loss', 'Accuracy', 'V_loss', 'V_acc', 'LR', 'Next LR', 'Monitor', '% Improv', 'Duration')\n#                             print(msg)\n\n#                         except Exception:\n#                             print('Invalid')","metadata":{"id":"d5HiN8XDGG60","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### **Function to plot history of training**","metadata":{"id":"2zwhoj3zGG61"}},{"cell_type":"code","source":"def plot_training(hist):\n    '''\n    This function take training model and plot history of accuracy and losses with the best epoch in both of them.\n    '''\n\n    # Define needed variables\n    tr_acc = hist.history['accuracy']\n    tr_loss = hist.history['loss']\n    val_acc = hist.history['val_accuracy']\n    val_loss = hist.history['val_loss']\n    index_loss = np.argmin(val_loss)\n    val_lowest = val_loss[index_loss]\n    index_acc = np.argmax(val_acc)\n    acc_highest = val_acc[index_acc]\n    Epochs = [i+1 for i in range(len(tr_acc))]\n    loss_label = f'best epoch= {str(index_loss + 1)}'\n    acc_label = f'best epoch= {str(index_acc + 1)}'\n\n    # Plot training history\n    plt.figure(figsize= (20, 8))\n    plt.style.use('fivethirtyeight')\n\n    plt.subplot(1, 2, 1)\n    plt.plot(Epochs, tr_loss, 'r', label= 'Training loss')\n    plt.plot(Epochs, val_loss, 'g', label= 'Validation loss')\n    plt.scatter(index_loss + 1, val_lowest, s= 150, c= 'blue', label= loss_label)\n    plt.title('Training and Validation Loss')\n    plt.xlabel('Epochs')\n    plt.ylabel('Loss')\n    plt.legend()\n\n    plt.subplot(1, 2, 2)\n    plt.plot(Epochs, tr_acc, 'r', label= 'Training Accuracy')\n    plt.plot(Epochs, val_acc, 'g', label= 'Validation Accuracy')\n    plt.scatter(index_acc + 1 , acc_highest, s= 150, c= 'blue', label= acc_label)\n    plt.title('Training and Validation Accuracy')\n    plt.xlabel('Epochs')\n    plt.ylabel('Accuracy')\n    plt.legend()\n\n    plt.tight_layout\n    plt.show()\n","metadata":{"id":"pU3eAW5jGG62","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### **Function to create Confusion Matrix**","metadata":{"id":"pK6cgu7LGG63"}},{"cell_type":"code","source":"def plot_confusion_matrix(cm, classes, normalize= False, title= 'Confusion Matrix', cmap= plt.cm.Blues):\n\t'''\n\tThis function plot confusion matrix method from sklearn package.\n\t'''\n\n\tplt.figure(figsize= (10, 10))\n\tplt.imshow(cm, interpolation= 'nearest', cmap= cmap)\n\tplt.title(title)\n\tplt.colorbar()\n\n\ttick_marks = np.arange(len(classes))\n\tplt.xticks(tick_marks, classes, rotation= 45)\n\tplt.yticks(tick_marks, classes)\n\n\tif normalize:\n\t\tcm = cm.astype('float') / cm.sum(axis= 1)[:, np.newaxis]\n\t\tprint('Normalized Confusion Matrix')\n\n\telse:\n\t\tprint('Confusion Matrix, Without Normalization')\n\n\tprint(cm)\n\n\tthresh = cm.max() / 2.\n\tfor i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):\n\t\tplt.text(j, i, cm[i, j], horizontalalignment= 'center', color= 'white' if cm[i, j] > thresh else 'black')\n\n\tplt.tight_layout()\n\tplt.ylabel('True Label')\n\tplt.xlabel('Predicted Label')","metadata":{"id":"_4mPYHnzGG64","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Model Structure**","metadata":{"id":"57eDFl3oGG65"}},{"cell_type":"markdown","source":"#### **Start Reading Dataset**","metadata":{"id":"2GHNMVrhGG65"}},{"cell_type":"code","source":"train_dir = '/kaggle/input/spr-x-ray-gender/kaggle/kaggle/train'\ncsv_dir = '/kaggle/input/spr-x-ray-gender/train_gender.csv'\n\n# Get splitted data\ntrain_df, valid_df = create_df(train_dir, csv_dir)\n# train_df = trim(train_df, max_size = 2000, min_size = 0, column='labels')\n# train_df\n# # Get Generators\nbatch_size = 64\ntrain_gen, valid_gen = create_gens(train_df, valid_df, batch_size)","metadata":{"id":"FWfxfQEVabWS","outputId":"d8be6a8d-5b19-49f7-bfa1-09a96ff58286","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### **Display Image Sample**","metadata":{}},{"cell_type":"code","source":"show_images(train_gen)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### **Show Labels Count**","metadata":{}},{"cell_type":"code","source":"plot_label_count(train_df, 'train')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### **Generic Model Creation**","metadata":{"id":"3wvOKjeRGG65"}},{"cell_type":"code","source":"# Create Model Structure\nimg_size = (224, 224)\nchannels = 3\nimg_shape = (img_size[0], img_size[1], channels)\nclass_count = len(list(train_gen.class_indices.keys())) # to define number of classes in dense layer\n\n# create pre-trained model (you can built on pretrained model such as :  efficientnet, VGG , Resnet )\n# we will use efficientnetb3 from EfficientNet family.\nbase_model = tf.keras.applications.efficientnet.EfficientNetB0(include_top= False, weights= \"imagenet\", input_shape= img_shape, pooling= 'max')\n# base_model.trainable = False\n\nmodel = Sequential([\n    base_model,\n    BatchNormalization(axis= -1, momentum= 0.99, epsilon= 0.001),\n    Dense(256, kernel_regularizer= regularizers.l2(l= 0.016), activity_regularizer= regularizers.l1(0.006),\n                bias_regularizer= regularizers.l1(0.006), activation= 'relu'),\n    Dropout(rate= 0.45, seed= 123),\n    Dense(class_count, activation= 'softmax')\n])\n\nmodel.compile(Adamax(learning_rate= 0.001), loss= 'categorical_crossentropy', metrics= ['accuracy'])\n\nmodel.summary()","metadata":{"id":"kDT4CV15abWT","outputId":"365637a8-7535-4ac4-90ea-700f6eb5769e","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### **Set Callback Parameters**","metadata":{"id":"TciwhdM1GG66"}},{"cell_type":"code","source":"batch_size = 64   # set batch size for training\nepochs = 50   # number of all epochs in training\npatience = 1   #number of epochs to wait to adjust lr if monitored value does not improve\nstop_patience = 3   # number of epochs to wait before stopping training if monitored value does not improve\nthreshold = 0.9   # if train accuracy is < threshold adjust monitor accuracy, else monitor validation loss\nfactor = 0.5   # factor to reduce lr by\nask_epoch = 5   # number of epochs to run before asking if you want to halt training\nbatches = int(np.ceil(len(train_gen.labels) / batch_size))    # number of training batch to run per epoch\n\ncallbacks = [MyCallback(model= model, patience= patience, stop_patience= stop_patience, threshold= threshold,\n            factor= factor, batches= batches, epochs= epochs, ask_epoch= ask_epoch )]","metadata":{"id":"7abvdv7mGG66","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### **Train model**","metadata":{"id":"ap89fjdxGG67"}},{"cell_type":"code","source":"history = model.fit(x= train_gen, epochs= epochs, verbose= 0, callbacks= callbacks,\n                    validation_data= valid_gen, validation_steps= None, shuffle= False)","metadata":{"id":"0Uk3BTERGG67","outputId":"ec610f68-a1a5-4c7d-9969-26dfab2d0305","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### **Display model performance**","metadata":{"id":"dNKq6ebOGG67"}},{"cell_type":"code","source":"plot_training(history)","metadata":{"id":"L0Bj0Sp_GG68","outputId":"663963ec-ea21-4272-8dda-a16c5f5e2ce5","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Evaluate model**","metadata":{"id":"MySXhfAJGG68"}},{"cell_type":"code","source":"train_score = model.evaluate(train_gen, verbose= 1)\nvalid_score = model.evaluate(valid_gen, verbose= 1)\n\nprint(\"Train Loss: \", train_score[0])\nprint(\"Train Accuracy: \", train_score[1])\nprint('-' * 20)\nprint(\"Validation Loss: \", valid_score[0])\nprint(\"Validation Accuracy: \", valid_score[1])","metadata":{"id":"wSKDkyXXGG68","outputId":"b521980b-a33b-421b-8cdf-4d92fb0f304a","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Submission**","metadata":{"id":"4l-DABtFGG68"}},{"cell_type":"code","source":"test_dir = '/kaggle/input/spr-x-ray-gender/kaggle/kaggle/test'\ntest_gen = tf.keras.utils.image_dataset_from_directory(test_dir, labels=None, label_mode=None, color_mode='rgb',\n                                                        image_size=(224,224), shuffle=False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Get test prediction\npreds = model.predict_generator(test_gen)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import gc\ngc.collect()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pred = np.array(preds)\npred","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub = pd.read_csv('/kaggle/input/spr-x-ray-gender/sample_submission_gender.csv')\nsub","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"files = sorted(os.listdir(test_dir))\nfiles[:5]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pred_list = []\nfor i in pred:\n    pred_list.append(i[1])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"files_list = []\nfor f in files:\n    files_list.append(f.replace('.png', ''))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub_dict = {'imageId':files_list, 'gender':pred_list}\nsubmission = pd.DataFrame(sub_dict)\nsubmission","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission.to_csv(\"submission.csv\", index=False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Thank You","metadata":{}}]}