{"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":"# 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","execution":{"iopub.status.busy":"2023-01-25T20:07:15.196105Z","iopub.execute_input":"2023-01-25T20:07:15.196849Z","iopub.status.idle":"2023-01-25T20:07:17.287987Z","shell.execute_reply.started":"2023-01-25T20:07:15.196755Z","shell.execute_reply":"2023-01-25T20:07:17.286711Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Create needed functions","metadata":{"id":"SA_gwvwnGG6v"}},{"cell_type":"markdown","source":"#### **Function to split data into train, valid, test**","metadata":{"id":"zE_xHC-HabWG"}},{"cell_type":"code","source":"# Function to generate data paths with labels\ndef split_df(csv_dir):\n    '''\n    This function take csv file and split it into train, valid, and test\n    '''\n\n    df = pd.read_csv(csv_dir)\n\n    # train dataframe\n    train_df, dummy_df = train_test_split(df,  train_size= 0.7, shuffle= True, random_state= 123)\n\n    # valid and test dataframe\n    valid_df, test_df = train_test_split(dummy_df,  train_size= 0.5, shuffle= True, random_state= 123)\n\n    return train_df, valid_df, test_df","metadata":{"id":"g2nDmYaAabWE","execution":{"iopub.status.busy":"2023-01-25T20:07:17.290129Z","iopub.execute_input":"2023-01-25T20:07:17.291052Z","iopub.status.idle":"2023-01-25T20:07:17.297566Z","shell.execute_reply.started":"2023-01-25T20:07:17.291010Z","shell.execute_reply":"2023-01-25T20:07:17.296558Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### **Function to generate images from dataframe**\n**check your variables**","metadata":{"id":"JZaHdeFxGG6x"}},{"cell_type":"code","source":"def create_gens(train_df, valid_df, test_df, batch_size):\n\n    ''' This function takes train, validation, and test dataframe and fit them into image data generator, because model takes\n        data from image data generator.\n        Image data generator converts images into tensors.\n        Check your variables'''\n\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    train_dir = '/kaggle/input/plant-pathology-2021-fgvc8/train_images'\n    test_dir = '/kaggle/input/plant-pathology-2021-fgvc8/test_images'\n    fpath_col = 'image'\n    label_col = 'labels'\n\n    # Recommended : use custom function for test data batch size, else we can use normal batch size.\n    ts_length = len(test_df)\n    test_batch_size = max(sorted([ts_length // n for n in range(1, ts_length + 1) if ts_length%n == 0 and ts_length/n <= 80]))\n    test_steps = ts_length // test_batch_size\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, directory= train_dir, x_col= fpath_col, y_col= label_col, target_size= img_size,\n                                            class_mode= 'categorical', color_mode= color, shuffle= True, batch_size= batch_size)\n\n    valid_gen = ts_gen.flow_from_dataframe( valid_df, directory= train_dir, x_col= fpath_col, y_col= label_col, target_size= img_size,\n                                            class_mode= 'categorical', color_mode= color, shuffle= True, batch_size= batch_size)\n\n    # Note: we will use custom test_batch_size, and make shuffle= false\n    test_gen = ts_gen.flow_from_dataframe( test_df, directory= train_dir, x_col= fpath_col, y_col= label_col, target_size= img_size,\n                                            class_mode= 'categorical', color_mode= color, shuffle= False, batch_size= test_batch_size)\n\n    return train_gen, valid_gen, test_gen","metadata":{"id":"iLL8hHQcGG6x","execution":{"iopub.status.busy":"2023-01-25T20:07:17.299149Z","iopub.execute_input":"2023-01-25T20:07:17.299796Z","iopub.status.idle":"2023-01-25T20:07:17.311993Z","shell.execute_reply.started":"2023-01-25T20:07:17.299755Z","shell.execute_reply":"2023-01-25T20:07:17.311109Z"},"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(df, data_path):\n    '''\n    This function take the data generator and show sample of the images\n    '''\n\n    sample_df = df.sample(16)\n    image_names = sample_df[\"image\"].values\n    labels = sample_df[\"labels\"].values\n    plt.figure(figsize=(16, 12))\n    \n    for image_ind, (image_name, label) in enumerate(zip(image_names, labels)):\n        plt.subplot(4, 4, image_ind + 1)\n        image = cv2.imread(os.path.join(data_path, image_name))\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n        plt.imshow(image)\n        plt.title(f\"{label}\", fontsize=12)\n        plt.axis(\"off\")\n    plt.show()","metadata":{"id":"IAGbj3ZyGG6y","execution":{"iopub.status.busy":"2023-01-25T20:07:17.316120Z","iopub.execute_input":"2023-01-25T20:07:17.317493Z","iopub.status.idle":"2023-01-25T20:07:17.327109Z","shell.execute_reply.started":"2023-01-25T20:07:17.317464Z","shell.execute_reply":"2023-01-25T20:07:17.326215Z"},"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_labels(df):\n    '''\n    This function take df and plot labels value counts\n    '''\n\n    plt.figure(figsize= (12, 8))\n    labels = sns.barplot(df.labels.value_counts().index,df.labels.value_counts())\n    for item in labels.get_xticklabels():\n        item.set_rotation(45)","metadata":{"id":"ud7JP-zHARMO","execution":{"iopub.status.busy":"2023-01-25T20:07:17.328463Z","iopub.execute_input":"2023-01-25T20:07:17.328821Z","iopub.status.idle":"2023-01-25T20:07:17.340269Z","shell.execute_reply.started":"2023-01-25T20:07:17.328784Z","shell.execute_reply":"2023-01-25T20:07:17.339194Z"},"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","execution":{"iopub.status.busy":"2023-01-25T20:07:17.341901Z","iopub.execute_input":"2023-01-25T20:07:17.342245Z","iopub.status.idle":"2023-01-25T20:07:17.372348Z","shell.execute_reply.started":"2023-01-25T20:07:17.342210Z","shell.execute_reply":"2023-01-25T20:07:17.371405Z"},"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","execution":{"iopub.status.busy":"2023-01-25T20:07:17.373983Z","iopub.execute_input":"2023-01-25T20:07:17.374328Z","iopub.status.idle":"2023-01-25T20:07:17.386364Z","shell.execute_reply.started":"2023-01-25T20:07:17.374294Z","shell.execute_reply":"2023-01-25T20:07:17.385414Z"},"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","execution":{"iopub.status.busy":"2023-01-25T20:07:17.389499Z","iopub.execute_input":"2023-01-25T20:07:17.389794Z","iopub.status.idle":"2023-01-25T20:07:17.401613Z","shell.execute_reply.started":"2023-01-25T20:07:17.389733Z","shell.execute_reply":"2023-01-25T20:07:17.400707Z"},"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":"csv_dir = input('Enter CSV file for training')\ntrain_path = '/kaggle/input/plant-pathology-2021-fgvc8/train_images'\ntry:\n    # Get splitted data\n    train_df, valid_df, test_df = split_df(csv_dir)\n\n    # Get Generators\n    batch_size = 40\n    train_gen, valid_gen, test_gen = create_gens(train_df, valid_df, test_df, batch_size)\n\nexcept Exception:\n    print('Invalid Input')","metadata":{"id":"FWfxfQEVabWS","outputId":"d8be6a8d-5b19-49f7-bfa1-09a96ff58286","execution":{"iopub.status.busy":"2023-01-25T20:07:17.402955Z","iopub.execute_input":"2023-01-25T20:07:17.403370Z","iopub.status.idle":"2023-01-25T20:07:29.696122Z","shell.execute_reply.started":"2023-01-25T20:07:17.403332Z","shell.execute_reply":"2023-01-25T20:07:29.695109Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Display Image Samples\nshow_images(train_df, train_path)","metadata":{"execution":{"iopub.status.busy":"2023-01-25T20:07:29.700043Z","iopub.execute_input":"2023-01-25T20:07:29.700333Z","iopub.status.idle":"2023-01-25T20:07:48.532171Z","shell.execute_reply.started":"2023-01-25T20:07:29.700290Z","shell.execute_reply":"2023-01-25T20:07:48.531119Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_labels(train_df)","metadata":{"execution":{"iopub.status.busy":"2023-01-25T20:07:48.533109Z","iopub.execute_input":"2023-01-25T20:07:48.533477Z","iopub.status.idle":"2023-01-25T20:07:48.827297Z","shell.execute_reply.started":"2023-01-25T20:07:48.533413Z","shell.execute_reply":"2023-01-25T20:07:48.826377Z"},"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.EfficientNetB3(include_top= False, weights= \"imagenet\", input_shape= img_shape, pooling= 'max')\n\nmodel = Sequential([\n    base_model,\n    BatchNormalization(axis= -1, momentum= 0.99, epsilon= 0.001),\n    Dense(64, 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= 'sigmoid')\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","execution":{"iopub.status.busy":"2023-01-25T20:07:48.828626Z","iopub.execute_input":"2023-01-25T20:07:48.829272Z","iopub.status.idle":"2023-01-25T20:07:53.189726Z","shell.execute_reply.started":"2023-01-25T20:07:48.829233Z","shell.execute_reply":"2023-01-25T20:07:53.188727Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Set Callback Parameters","metadata":{"id":"TciwhdM1GG66"}},{"cell_type":"code","source":"batch_size = 40   # set batch size for training\nepochs = 40   # 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","execution":{"iopub.status.busy":"2023-01-25T20:07:53.191088Z","iopub.execute_input":"2023-01-25T20:07:53.191481Z","iopub.status.idle":"2023-01-25T20:07:53.433345Z","shell.execute_reply.started":"2023-01-25T20:07:53.191442Z","shell.execute_reply":"2023-01-25T20:07:53.432316Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Train model","metadata":{"id":"ap89fjdxGG67"}},{"cell_type":"code","source":"history = model.fit(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","execution":{"iopub.status.busy":"2023-01-25T20:07:53.434597Z","iopub.execute_input":"2023-01-25T20:07:53.434961Z","iopub.status.idle":"2023-01-26T03:00:56.179278Z","shell.execute_reply.started":"2023-01-25T20:07:53.434922Z","shell.execute_reply":"2023-01-26T03:00:56.178280Z"},"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","execution":{"iopub.status.busy":"2023-01-26T03:00:56.180948Z","iopub.execute_input":"2023-01-26T03:00:56.181310Z","iopub.status.idle":"2023-01-26T03:01:15.818963Z","shell.execute_reply.started":"2023-01-26T03:00:56.181273Z","shell.execute_reply":"2023-01-26T03:01:15.818068Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Evaluate model","metadata":{"id":"MySXhfAJGG68"}},{"cell_type":"code","source":"ts_length = len(test_df)\ntest_batch_size = test_batch_size = max(sorted([ts_length // n for n in range(1, ts_length + 1) if ts_length%n == 0 and ts_length/n <= 80]))\ntest_steps = ts_length // test_batch_size\n\ntrain_score = model.evaluate(train_gen, steps= test_steps, verbose= 1)\nvalid_score = model.evaluate(valid_gen, steps= test_steps, verbose= 1)\ntest_score = model.evaluate(test_gen, steps= test_steps, 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])\nprint('-' * 20)\nprint(\"Test Loss: \", test_score[0])\nprint(\"Test Accuracy: \", test_score[1])","metadata":{"id":"wSKDkyXXGG68","outputId":"b521980b-a33b-421b-8cdf-4d92fb0f304a","execution":{"iopub.status.busy":"2023-01-26T03:01:15.820351Z","iopub.execute_input":"2023-01-26T03:01:15.821710Z","iopub.status.idle":"2023-01-26T03:16:31.139495Z","shell.execute_reply.started":"2023-01-26T03:01:15.821669Z","shell.execute_reply":"2023-01-26T03:16:31.138312Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Get Predictions","metadata":{"id":"4l-DABtFGG68"}},{"cell_type":"code","source":"preds = model.predict_generator(test_gen)\ny_pred = np.argmax(preds, axis=1)\n# print(y_pred)","metadata":{"id":"GDFj7MZdGG69","outputId":"6dbce8ed-fc8c-4398-b8bd-1ce8cb403727","execution":{"iopub.status.busy":"2023-01-26T03:16:31.142290Z","iopub.execute_input":"2023-01-26T03:16:31.142685Z","iopub.status.idle":"2023-01-26T03:23:11.849815Z","shell.execute_reply.started":"2023-01-26T03:16:31.142646Z","shell.execute_reply":"2023-01-26T03:23:11.848771Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Confusion Matrics and Classification Report","metadata":{"id":"aJscUTF6GG69"}},{"cell_type":"code","source":"g_dict = test_gen.class_indices\nclasses = list(g_dict.keys())\n\n# Confusion matrix\ncm = confusion_matrix(test_gen.classes, y_pred)\nplot_confusion_matrix(cm= cm, classes= classes, title = 'Confusion Matrix')\n\n# Classification report\nprint(classification_report(test_gen.classes, y_pred, target_names= classes))","metadata":{"id":"tQR-UlD6GG69","outputId":"09ac1d97-2053-4633-e066-ca11540a2e27","execution":{"iopub.status.busy":"2023-01-26T03:23:11.852184Z","iopub.execute_input":"2023-01-26T03:23:11.852584Z","iopub.status.idle":"2023-01-26T03:23:13.061192Z","shell.execute_reply.started":"2023-01-26T03:23:11.852544Z","shell.execute_reply":"2023-01-26T03:23:13.060285Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Save model","metadata":{"id":"SsIK5v0lGG69"}},{"cell_type":"code","source":"model_name = model.input_names[0][:-6]\nsubject = input('Enter Project Subject')\nacc = test_score[1] * 100\nsave_path = ''\n\n# Save model\nsave_id = str(f'{model_name}-{subject}-{\"%.2f\" %round(acc, 2)}.h5')\nmodel_save_loc = os.path.join(save_path, save_id)\nmodel.save(model_save_loc)\nprint(f'model was saved as {model_save_loc}')\n\n# Save weights\nweight_save_id = str(f'{model_name}-{subject}-weights.h5')\nweights_save_loc = os.path.join(save_path, weight_save_id)\nmodel.save_weights(weights_save_loc)\nprint(f'weights were saved as {weights_save_loc}')","metadata":{"id":"oy5ShUciGG6-","outputId":"6122a45f-351d-4cb4-f046-d141ab2f9a5e","execution":{"iopub.status.busy":"2023-01-26T03:23:13.062677Z","iopub.execute_input":"2023-01-26T03:23:13.063270Z","iopub.status.idle":"2023-01-26T03:23:54.278944Z","shell.execute_reply.started":"2023-01-26T03:23:13.063219Z","shell.execute_reply":"2023-01-26T03:23:54.277942Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Generate CSV files containing classes indicies & image size ","metadata":{"id":"q2fsiEtEGG6-"}},{"cell_type":"code","source":"class_dict = train_gen.class_indices\nimg_size = train_gen.image_shape\nheight = []\nwidth = []\nfor _ in range(len(class_dict)):\n    height.append(img_size[0])\n    width.append(img_size[1])\n\nIndex_series = pd.Series(list(class_dict.values()), name= 'class_index')\nClass_series = pd.Series(list(class_dict.keys()), name= 'class')\nHeight_series = pd.Series(height, name= 'height')\nWidth_series = pd.Series(width, name= 'width')\nclass_df = pd.concat([Index_series, Class_series, Height_series, Width_series], axis= 1)\ncsv_name = f'{subject}-class_dict.csv'\ncsv_save_loc = os.path.join(save_path, csv_name)\nclass_df.to_csv(csv_save_loc, index= False)\nprint(f'class csv file was saved as {csv_save_loc}')","metadata":{"id":"UiHQzq8XGG6-","outputId":"e2daeab5-c65c-495c-ffde-be259c917c07","execution":{"iopub.status.busy":"2023-01-26T03:23:54.280298Z","iopub.execute_input":"2023-01-26T03:23:54.280712Z","iopub.status.idle":"2023-01-26T03:23:54.311031Z","shell.execute_reply.started":"2023-01-26T03:23:54.280672Z","shell.execute_reply":"2023-01-26T03:23:54.309933Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Thank You..","metadata":{}}]}