{"metadata":{"kernelspec":{"display_name":"gpuenv","language":"python","name":"gpuenv"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.8.11"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"Problem Statement\n<pre>\nApples are one of the most important temperate fruit crops in the world. \nFoliar (leaf) diseases pose a major threat to the overall productivity and quality \nof apple orchards. The current process for disease diagnosis in apple orchards is \nbased on manual scouting by humans, which is time-consuming and expensive.\n</pre>","metadata":{}},{"cell_type":"code","source":"# Import the necessary Libraries\nfrom sklearn.model_selection import StratifiedShuffleSplit\nfrom sklearn.metrics import confusion_matrix\nfrom sklearn.metrics import classification_report, accuracy_score\nfrom tensorflow.keras import mixed_precision\nfrom tqdm import tqdm\nimport tensorflow as tf\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport numpy as np\nimport pandas as pd\nimport random\nimport os\nimport cv2\nimport shutil\nimport warnings\nwarnings.filterwarnings(\"ignore\")","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<pre>\n1. Exploratary Data Analysis:\n* Firstly let us Find us what are different kinds of Diseases we are dealing\n* Count of images availabe for each of the data\n</pre>","metadata":{}},{"cell_type":"code","source":"# Load the DataFrame\ndf_train = pd.read_csv(\"train.csv\")\ndf_train.head()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.style.use('fivethirtyeight')\nplt.figure(figsize=(15, 12))\n\ndef ShowCounts(category, counts):\n    plot = sns.barplot(category, counts)\n    \n    # Set the rotation of plots\n    for item in plot.get_xticklabels():\n        item.set_rotation(45)\n    \n    # Display the count at top of each bar plot\n    for index, value in enumerate(counts):\n        plt.text(index - 0.09, value + 0.3, str(value))\n    plt.title(\"Category Vs count\")\n        \n\ncategory = list(df_train[\"labels\"].value_counts().index)\ncounts = list(df_train[\"labels\"].value_counts().values)\n\n# Call the function\nShowCounts(category, counts)\nplt.show()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<pre>\nWe can observe that some of the images are very imbalaced and this will make \nBuilding an effective model a Very Challenging Problem, But the motto of Fighters\nNever Give up, SO lets Start Building a RObust model with our Imbalanced Data\n</pre>","metadata":{}},{"cell_type":"markdown","source":"## Data Preprocessing: Preparing the data ","metadata":{}},{"cell_type":"markdown","source":"<pre>\nSteps TO be followed:\n1. Create A Directory names Train \n2. Create sub directories named with diseases\n3. Send each of the images into their correct directories\n4. Augment the data inorder to make the model more robust\n5. Prefetch and Batch the Data\n6. Setup Mixed Precision Scaling\n</pre>","metadata":{}},{"cell_type":"code","source":"# Split the Dataset and Train and Validation Set\nsplit = StratifiedShuffleSplit(n_splits=1, test_size=0.2,\n                              random_state=42)\nfor train_index, valid_index in split.split(df_train, df_train[\"labels\"]):\n    train_set = df_train.loc[train_index]\n    validation_set = df_train.loc[valid_index]\n    \nprint(f\"The Number of Training Images are {train_set.shape}\")\nprint(f\"The Number of validation Images are {validation_set.shape}\")","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_set.head()","metadata":{"scrolled":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for i in train_set.iterrows():\n    print(i)\n    print(\"\")\n    print(i[1][0])\n    print(i[1][1])\n    break","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def create_train_data(train_dir, train_set):\n    images = os.listdir(train_dir)\n    for row in tqdm(train_set.iterrows()):\n        image_name = row[1][0]\n        class_name = row[1][1]\n        \n        if image_name in images:\n            image_path = train_dir + \"/\" + image_name\n            if class_name == 'scab':\n                shutil.copy(image_path, r\"F:\\FGVc-8\\train_data\\train\\scab\")\n            elif class_name == 'healthy':\n                shutil.copy(image_path, r\"F:\\FGVc-8\\train_data\\train\\healthy\")\n            elif class_name == 'frog_eye_leaf_spot':\n                shutil.copy(image_path, r\"F:\\FGVc-8\\train_data\\train\\frog_eye_leaf_spot\")\n            elif class_name == 'rust':\n                shutil.copy(image_path, r\"F:\\FGVc-8\\train_data\\train\\rust\")\n            elif class_name == 'complex':\n                shutil.copy(image_path, r\"F:\\FGVc-8\\train_data\\train\\complex\")\n            elif class_name == 'powdery_mildew':\n                shutil.copy(image_path, r\"F:\\FGVc-8\\train_data\\train\\powdery_mildew\")\n            elif class_name == 'scab frog_eye_leaf_spot':\n                shutil.copy(image_path, r\"F:\\FGVc-8\\train_data\\train\\scab_frog_eye_leaf_spot\")\n            elif class_name == 'scab frog_eye_leaf_spot complex':\n                shutil.copy(image_path, r\"F:\\FGVc-8\\train_data\\train\\scab_frog_eye_leaf_spot_complex\")\n            elif class_name == 'frog_eye_leaf_spot complex':\n                shutil.copy(image_path,r\"F:\\FGVc-8\\train_data\\train\\frog_eye_leaf_spot_complex\")\n            elif class_name == 'rust frog_eye_leaf_spot':\n                shutil.copy(image_path, r\"F:\\FGVc-8\\train_data\\train\\rust_frog_eye_leaf_spot\")\n            elif class_name == 'rust complex':\n                shutil.copy(image_path, r\"F:\\FGVc-8\\train_data\\train\\rust_complex\")\n            elif class_name == 'powdery_mildew complex':\n                shutil.copy(image_path, r\"F:\\FGVc-8\\train_data\\train\\powdery_mildew_complex\")\n        \n    \n\ntrain_dir = r\"F:\\FGVc-8\\train_images\"\n# Create Train and its subdirectories\nos.makedirs(r\"F:\\FGVc-8\\train_data\")\nos.makedirs(r\"F:\\FGVc-8\\train_data\\train\")\nos.makedirs(r\"F:\\FGVc-8\\train_data\\train\\scab\")\nos.makedirs(r\"F:\\FGVc-8\\train_data\\train\\healthy\")\nos.makedirs(r\"F:\\FGVc-8\\train_data\\train\\frog_eye_leaf_spot\")\nos.makedirs(r\"F:\\FGVc-8\\train_data\\train\\rust\")\nos.makedirs(r\"F:\\FGVc-8\\train_data\\train\\complex\")\nos.makedirs(r\"F:\\FGVc-8\\train_data\\train\\powdery_mildew\")\nos.makedirs(r\"F:\\FGVc-8\\train_data\\train\\scab_frog_eye_leaf_spot\")\nos.makedirs(r\"F:\\FGVc-8\\train_data\\train\\scab_frog_eye_leaf_spot_complex\")\nos.makedirs(r\"F:\\FGVc-8\\train_data\\train\\frog_eye_leaf_spot_complex\")\nos.makedirs(r\"F:\\FGVc-8\\train_data\\train\\rust_frog_eye_leaf_spot\")\nos.makedirs(r\"F:\\FGVc-8\\train_data\\train\\rust_complex\")\nos.makedirs(r\"F:\\FGVc-8\\train_data\\train\\powdery_mildew_complex\")","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def create_valid_data(train_dir, validation_set):\n    images = os.listdir(train_dir)\n    for row in tqdm(validation_set.iterrows()):\n        image_name = row[1][0]\n        class_name = row[1][1]\n        \n        if image_name in images:\n            image_path = train_dir + \"/\" + image_name\n            if class_name == 'scab':\n                shutil.copy(image_path, r\"F:\\FGVc-8\\train_data\\valid\\scab\")\n            elif class_name == 'healthy':\n                shutil.copy(image_path, r\"F:\\FGVc-8\\train_data\\valid\\healthy\")\n            elif class_name == 'frog_eye_leaf_spot':\n                shutil.copy(image_path, r\"F:\\FGVc-8\\train_data\\valid\\frog_eye_leaf_spot\")\n            elif class_name == 'rust':\n                shutil.copy(image_path, r\"F:\\FGVc-8\\train_data\\valid\\rust\")\n            elif class_name == 'complex':\n                shutil.copy(image_path, r\"F:\\FGVc-8\\train_data\\valid\\complex\")\n            elif class_name == 'powdery_mildew':\n                shutil.copy(image_path, r\"F:\\FGVc-8\\train_data\\valid\\powdery_mildew\")\n            elif class_name == 'scab frog_eye_leaf_spot':\n                shutil.copy(image_path, r\"F:\\FGVc-8\\train_data\\valid\\scab_frog_eye_leaf_spot\")\n            elif class_name == 'scab frog_eye_leaf_spot complex':\n                shutil.copy(image_path, r\"F:\\FGVc-8\\train_data\\valid\\scab_frog_eye_leaf_spot_complex\")\n            elif class_name == 'frog_eye_leaf_spot complex':\n                shutil.copy(image_path,r\"F:\\FGVc-8\\train_data\\valid\\frog_eye_leaf_spot_complex\")\n            elif class_name == 'rust frog_eye_leaf_spot':\n                shutil.copy(image_path, r\"F:\\FGVc-8\\train_data\\valid\\rust_frog_eye_leaf_spot\")\n            elif class_name == 'rust complex':\n                shutil.copy(image_path, r\"F:\\FGVc-8\\train_data\\valid\\rust_complex\")\n            elif class_name == 'powdery_mildew complex':\n                shutil.copy(image_path, r\"F:\\FGVc-8\\train_data\\valid\\powdery_mildew_complex\")\n        \n    \n\ntrain_dir = r\"F:\\FGVc-8\\train_images\"\n# Create Train and its subdirectories\nos.makedirs(r\"F:\\FGVc-8\\train_data\\valid\")\nos.makedirs(r\"F:\\FGVc-8\\train_data\\valid\\scab\")\nos.makedirs(r\"F:\\FGVc-8\\train_data\\valid\\healthy\")\nos.makedirs(r\"F:\\FGVc-8\\train_data\\valid\\frog_eye_leaf_spot\")\nos.makedirs(r\"F:\\FGVc-8\\train_data\\valid\\rust\")\nos.makedirs(r\"F:\\FGVc-8\\train_data\\valid\\complex\")\nos.makedirs(r\"F:\\FGVc-8\\train_data\\valid\\powdery_mildew\")\nos.makedirs(r\"F:\\FGVc-8\\train_data\\valid\\scab_frog_eye_leaf_spot\")\nos.makedirs(r\"F:\\FGVc-8\\train_data\\valid\\scab_frog_eye_leaf_spot_complex\")\nos.makedirs(r\"F:\\FGVc-8\\train_data\\valid\\frog_eye_leaf_spot_complex\")\nos.makedirs(r\"F:\\FGVc-8\\train_data\\valid\\rust_frog_eye_leaf_spot\")\nos.makedirs(r\"F:\\FGVc-8\\train_data\\valid\\rust_complex\")\nos.makedirs(r\"F:\\FGVc-8\\train_data\\valid\\powdery_mildew_complex\")","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"create_train_data(train_dir, train_set)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"create_valid_data(train_dir, validation_set)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Visualizing the images","metadata":{}},{"cell_type":"markdown","source":"<pre>\nNow as we prepared the trained Data and arranged them into Training\nand Validation sets, and we have stored the images in a subdirectory with\nits disease name.\n\nNow let's do the following steps:\n1. Get the class names programatically and Get One with data\n2. Visualize few of the images from each class randomly\n3. Then go for model building\n</pre>","metadata":{}},{"cell_type":"code","source":"train_dir = r\"F:\\FGVc-8\\train_data\\train\"\nvalid_dir = r\"F:\\FGVc-8\\train_data\\valid\"\n\nclass_names = os.listdir(train_dir)\nprint(class_names)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Get the counts of train images\nfor class_ in class_names:\n    for directory, sub_directory, filename in os.walk(train_dir + \"/\" + class_):\n        print(f\"There are {len(filename)} train images in {class_}\")","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Get the counts of test_images\nfor class_ in class_names:\n    for directory, sub_directory, filename in os.walk(valid_dir + \"/\" + class_):\n        print(f\"There are {len(filename)} train images in {class_}\")","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Visualize few of the images randomly from differet categores\ndef visualize_images(Directory, class_name):\n    # Get the Path of folder to select image\n    target_dir = Directory + \"/\" + class_name\n    # select a image randomly\n    image = random.sample(os.listdir(target_dir), 1)\n    image = image[0]\n    \n    # Read and plot the images\n    img = cv2.imread(target_dir + \"/\" + image, cv2.COLOR_BGR2RGB)\n    plt.imshow(img)\n    plt.axis('off')\n    if len(class_name) > 13:\n        plt.title(class_name) \n    else: \n        plt.title(class_name[:12])\n\nplt.figure(figsize=(25, 20))\nplt.style.use('fivethirtyeight')\nfor i in range(25):\n    class_name = random.choice(class_names)\n    plt.subplot(5, 5, i + 1)\n    visualize_images(train_dir, class_name)\nplt.show()\n    ","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Prepare to be fittied to a model\ntrain_dir = r\"F:\\FGVc-8\\train_data\\train\"\nvalid_dir = r\"F:\\FGVc-8\\train_data\\valid\"\nImage_Size = 224\nBatch_Size = 16\n\n# Prepare the train_data\ntrain_data = tf.keras.preprocessing.image_dataset_from_directory(\ntrain_dir,\nlabel_mode=\"categorical\",\nimage_size=(Image_Size, Image_Size),\nbatch_size=Batch_Size,\nseed=123\n)\n\n# Prepare valid data\nvalid_data = tf.keras.preprocessing.image_dataset_from_directory(\nvalid_dir,\nlabel_mode=\"categorical\",\nimage_size=(Image_Size, Image_Size),\nbatch_size=Batch_Size,\nseed=123\n)\n\n","metadata":{"scrolled":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<pre>\nData Augmentation:It is a method of creating some new images from\nexisting images to make the model more robust\n\nCode WalkThrough:\n1. Build an sequential model contain some data augmentation parametres\n2. Choose some Random Images and Feed them to the Data-Augmentation layer\n3. Convert the image into (None, height, width, 3) Shape\n4. Normalize the image Before plotting\n</pre>","metadata":{}},{"cell_type":"code","source":"# Now lets Build an Data-augmentation layer so we can add it to model directly\ndata_augmentation = tf.keras.models.Sequential([\n    tf.keras.layers.experimental.preprocessing.RandomFlip(\"horizontal\"),\n    tf.keras.layers.experimental.preprocessing.RandomRotation(0.2),\n    tf.keras.layers.experimental.preprocessing.RandomHeight(0.2),\n    tf.keras.layers.experimental.preprocessing.RandomZoom(0.2),\n    tf.keras.layers.experimental.preprocessing.RandomWidth(0.2)\n], name=\"Data_augmentation_layer\")","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Visualize some of the original and Random images\ndef visualize_Augmented_images(class_name, aug_model, directory):\n    target_dir = directory + \"/\" + class_name\n    img = random.sample(os.listdir(target_dir), 1)\n    img = img[0]\n    original_image = plt.imread(target_dir + \"/\" + img)\n    \n    # Creating a augmented image\n    augmented_image = aug_model(tf.expand_dims(original_image, axis=0))\n    \n    # Creating subplots and plot the original and random images\n    fig, axs = plt.subplots(1, 2)\n    axs[0].imshow(original_image)\n    axs[1].imshow(tf.squeeze(augmented_image)/ 255.)\n    axs[0].set_title(\"Original_Image\", c=\"green\")\n    axs[1].set_title(\"Augmented_Image\")\n    \n\nplt.figure(figsize=(15, 5))\nfor i in range(3):\n    class_name = random.choice(class_names)\n    visualize_Augmented_images(class_name, data_augmentation, train_dir)\nplt.show()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Building A Robust and Efficient model","metadata":{}},{"cell_type":"markdown","source":"<pre>\nSteps to Build the model:\n    1. We can leverage the power of Transfer learning by using The EfficinetB0\n    model Pretrained on ImageNet Dataset.\n    2. Firstly use the model as a Feature Extraction model by freezing all the \n    layers Except the output layer.\n    3. Then Based on the Dataset we shall unfreeze some of the Bottom Layers\n    and retrain the model with increased Learning rate.\n    4. Ensure that you are using mixed precision scaling that is using both\n    16 bit and 32 bit while training the model which speeds up the training\n    time by 3 to 4 Times.\n  \n  Create Modelling Callbacks:\n  1. ModelCheckpoint(): Saves our model's progress at Various points,\n  so we can load it and reuse it later without having to train again.\n  \n   -> Checkpointing is also useful so we can start fine-tuning at particular\n      epoch and revert back to previous state if fine-tuning offers no Benifit\n</pre>","metadata":{}},{"cell_type":"code","source":"checkpoint_path = \"model_checkpoints/cp.ckpt\"\nmodel_checkpoint = tf.keras.callbacks.ModelCheckpoint(checkpoint_path,\n                                                      # save the model with best val accuracy\n                                                     monitor=\"val_acc\",\n                                                     save_best_only=True, # only save best weights\n                                                     save_weights_only=True, # Only save models weights\n                                                     verbose=1\n                                                     )","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\"\"\" IF your tensorflow is version is less than 2.4 its in experimental package as follows\n if you have a version 2.4 or higher use tf.keras.mixed_precision.policy('mixed_float16')\"\"\"\n\npolicy = tf.keras.mixed_precision.experimental.Policy('mixed_float16')\ntf.keras.mixed_precision.experimental.set_policy(policy)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Check the tensorflow version using using this command\nversion = tf.__version__\nprint(f\"My Tensorflow version:{version}\")","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"mixed_precision.experimental.global_policy()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"input_shape = (224, 224, 3)\nbase_model = tf.keras.applications.EfficientNetB0(\ninclude_top=False, weights='imagenet'\n)\nbase_model.trainable = False # Freeze all the layers\n\n# Create the model using functional Api\ninputs = tf.keras.layers.Input(shape=input_shape, name=\"Input_layer\")\n#x = data_augmentation(inputs)\nx = base_model(inputs, training=False) # Set in inference mode\nx = tf.keras.layers.GlobalAveragePooling2D(name=\"pooling_layer\")(x)\nx = tf.keras.layers.Dense(len(class_names))(x)\noutputs = tf.keras.layers.Activation(\"softmax\",\n                                    #dtype=tf.float32,\n                                    name=\"softmax_float32\")(x)\nmodel = tf.keras.Model(inputs, outputs)\n\n\"\"\" Note: IF you are using any architecture other than Effnet you have to add an\nRescaling layer because they will not have an rescaling built an as effnet and make sure you\nconvert dtype of output layer back to float32 inorder to maintain stability \"\"\"\n# Compile the model\nmodel.compile(\nloss=\"categorical_crossentropy\", # use sparse_categorical_crossentropy when labels are one hot encoded\noptimizer=tf.keras.optimizers.Adam(),\nmetrics=[\"accuracy\"]\n)\n\n# Display model Architecture\nmodel.summary()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Check model layer's dtype policy\nfor layer in model.layers:\n    print(f\"{layer.name} ->{layer.dtype}\")","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<pre>\nGoing through the above we see:\n\nlayer.name (str) : a layer's human-readable name, can be defined by the name \nparameter on construction \n\nlayer.trainable (bool) : whether or not a layer is trainable \n(all of our layers are trainable except the efficientnetb0 \nlayer since we set it's trainable attribute to False\n\nlayer.dtype : the data type a layer stores its variables in\n</pre>","metadata":{}},{"cell_type":"code","source":"# check the dtype policies of our base_model\nfor layer in model.layers[1].layers[:20]:\n    print(f\"{layer.name} ->{layer.dtype}\")","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<pre>\n Note: The mixed precision API automatically causes layers\n which can benefit from using the \"mixed_float16\" dtype policy to use it. \n It also prevents layers which shouldn't use it from using it \n (e.g. the normalization layer at the start of the base model).\n</pre>","metadata":{}},{"cell_type":"code","source":"history_feature_extraction = model.fit(\ntrain_data,\nepochs=5,\nsteps_per_epoch=len(train_data),\nvalidation_data=valid_data,\nvalidation_steps = 0.15 * len(valid_data),\ncallbacks=[model_checkpoint]\n)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Visualize the loss curves\ndef plot_loss_curves(history):\n    # Get the loss values\n    loss = history.history['loss']\n    val_loss = history.history['val_loss']\n    \n    # Get the accuracy values\n    accuracy = history.history['accuracy']\n    val_accuracy = history.history['val_accuracy']\n    \n    # No of epochs\n    epochs = range(len(loss))\n    \n    # plot the loss curves\n    plt.subplot(1, 2, 1)\n    plt.plot(epochs, loss, label=\"Train_loss\")\n    plt.plot(epochs, val_loss, label=\"Validation_loss\")\n    plt.title(\"Epochs Vs loss\", c=\"green\")\n    plt.xlabel('Epochs')\n    plt.ylabel('Loss')\n    plt.legend()\n    \n    # Plot the accuracy curves\n    plt.subplot(1, 2, 2)\n    plt.plot(epochs, accuracy, label=\"Train_accuracy\")\n    plt.plot(epochs, val_accuracy, label=\"val_accuracy\")\n    plt.title(\"Epochs vs accuracy\", c=\"green\")\n    plt.xlabel(\"Epochs\")\n    plt.ylabel(\"Accuracy\")\n    plt.legend()\n    \n# call the function\nplt.figure(figsize=(17, 5))\nplt.style.use('fivethirtyeight')\nplot_loss_curves(history_feature_extraction)\nplt.show()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<pre>\nNow let's fine tune the model\nStep1: Unfreeze all the layers\nstep2: Then Freeze all the layers except the last 5 layers\nstep3: Increase the learning-rate\nstep4: Recompile the model\n</pre>","metadata":{}},{"cell_type":"code","source":"# Unfreeze all the layers\nbase_model.trainable = True\n\n# Unfreeze the last 5 layers\nfor layer in base_model.layers[:-5]:\n    layer.trainable = False\n    \n# Re-compile the model\nmodel.compile(\nloss=\"categorical_crossentropy\",\noptimizer=tf.keras.optimizers.Adam(learning_rate=0.0001),\nmetrics=[\"accuracy\"]\n)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Let's verify whether the last 5 layers in base model are trainable\nfor layer in model.layers:\n    print(f\"{layer.name} -> {layer.trainable}\")","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for layer in model.layers[1].layers:\n    print(f\"{layer.name} -> {layer.trainable}\")","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Fit the model\ncheckpoint_callback = tf.keras.callbacks.ModelCheckpoint(\n'finetune.h5',\nmonitor='val_accuracy',\nmode='max',\nsave_best_only=True\n)\n\nfine_tune_epochs = 8\nhistory_fine_tune = model.fit(\n    train_data,\n   epochs=fine_tune_epochs,\n   steps_per_epoch=len(train_data),\n   validation_data=valid_data,\n   validation_steps= 0.15 * (len(valid_data)),\n   initial_epoch=history_feature_extraction.epoch[-1],\n   callbacks=[checkpoint_callback]\n)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Visual how fine tuning is working\ndef compare_curves(original_history, new_history, initial_epochs=5):\n    # Get the feature extractin model accuracy values\n    original_accuracy = original_history.history['accuracy']\n    original_val_accuracy = original_history.history['val_accuracy']\n    \n    # Get the feature extraction loss values\n    original_loss = original_history.history['loss']\n    original_val_loss = original_history.history['val_loss']\n    \n    # Get the fine tuned accuracy values\n    new_accuracy = new_history.history['accuracy']\n    new_val_accuracy = new_history.history['val_accuracy']\n    \n    # Get the fine tuned loss values\n    new_loss = new_history.history['loss']\n    new_val_loss = new_history.history['val_loss']\n    \n    # Concatenate to get final array of values\n    total_accuracy = original_accuracy + new_accuracy\n    total_val_accuracy = original_val_accuracy + new_val_accuracy\n    \n    total_loss = original_loss + new_loss\n    total_val_loss = original_val_loss + new_val_loss\n    \n    # plot the accuracy\n    plt.subplot(1, 2, 1)\n    plt.plot(total_accuracy, label=\"Train_accuracy\")\n    plt.plot(total_val_accuracy, label=\"Validation_accuracy\")\n    plt.plot([initial_epochs - 1, initial_epochs - 1], plt.ylim(), label=\"Fine tuning\")\n    plt.title(\"Training and Validation Accuracy\")\n    plt.legend()\n    \n    # plot the losses\n    plt.subplot(1, 2, 2)\n    plt.plot(total_loss, label=\"Train_Loss\")\n    plt.plot(total_val_loss, label=\"Validation_Loss\")\n    plt.plot([initial_epochs - 1, initial_epochs - 1], plt.ylim(), label=\"Fine tuning\")\n    plt.title(\"Training and Validation Losses\")\n    plt.legend()\n    \n# call the function\nplt.figure(figsize=(15, 5))\ncompare_curves(history_feature_extraction, history_fine_tune)\nplt.show()\n    ","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<pre>\n Here we can clearly observe that Fine tune is impproving the results\n and this trend will probably if we can train the model for few more\n epochs without over-fitting\n</pre>","metadata":{}},{"cell_type":"code","source":"model.evaluate(valid_data)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<pre>\nchecking where our model made the most wrong predictons\n* Unravel the test_data so we can use their original labels to track how well the\n  model is performing\n* Plot confusion to find where the model is doing more errors\n* Get the F1-scores and plot them in descending order\n* Find the most wrong-predictions, that is the wrong predictions with highest \n  prediction probability\n</pre>","metadata":{}},{"cell_type":"code","source":"# Let's see some predictions and compare them with original labels\npreds_probs = model.predict(valid_data)\npreds_probs[:1]","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Unbatch the Validation-Data\ny_labels = []\nfor images, labels in valid_data.unbatch():\n    y_labels.append(labels.numpy().argmax())","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<pre>\nIn the above code The labels column in the test data is taken and converted into numpy array and it typically looks like->[1,0,0,0...0] just like one hot encoded\nInorder to get the index of highest value we apply argmax() function\n</pre>","metadata":{}},{"cell_type":"code","source":"# Get the predicted class names and actual class names of first 10 images\n\npred_probs_first10 = preds_probs[:10]\nfor scores in range(10):\n  print(f\"The predicted class is {class_names[tf.argmax(pred_probs_first10[scores])]} and the actual class is {class_names[y_labels[scores]]}\")","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# plot a confusion matrix so we can see where the models is making more wrong predictions\nimport itertools\nimport matplotlib.pyplot as plt\nimport numpy as np\nfrom sklearn.metrics import confusion_matrix\n\ndef make_confusion_matrix_own(y_true, y_pred, classes=None, figsize=(100, 15), text_size=7, norm=False, savefig=False): \n  \"\"\"Makes a labelled confusion matrix comparing predictions and ground truth labels.\n\n  If classes is passed, confusion matrix will be labelled, if not, integer class values\n  will be used.\n\n  Args:\n    y_true: Array of truth labels (must be same shape as y_pred).\n    y_pred: Array of predicted labels (must be same shape as y_true).\n    classes: Array of class labels (e.g. string form). If `None`, integer labels are used.\n    figsize: Size of output figure (default=(10, 10)).\n    text_size: Size of output figure text (default=15).\n    norm: normalize values or not (default=False).\n    savefig: save confusion matrix to file (default=False).\n  \n  Returns:\n    A labelled confusion matrix plot comparing y_true and y_pred.\n\n  Example usage:\n    make_confusion_matrix(y_true=test_labels, # ground truth test labels\n                          y_pred=y_preds, # predicted labels\n                          classes=class_names, # array of class label names\n                          figsize=(15, 15),\n                          text_size=10)\n  \"\"\"  \n  # Create the confustion matrix\n  cm = confusion_matrix(y_true, y_pred)\n  cm_norm = cm.astype(\"float\") / cm.sum(axis=1)[:, np.newaxis] # normalize it\n  n_classes = cm.shape[0] # find the number of classes we're dealing with\n\n  # Plot the figure and make it pretty\n  fig, ax = plt.subplots(figsize=figsize)\n  cax = ax.matshow(cm, cmap=plt.cm.Blues) # colors will represent how 'correct' a class is, darker == better\n  fig.colorbar(cax)\n\n  # Are there a list of classes?\n  if classes:\n    labels = classes\n  else:\n    labels = np.arange(cm.shape[0])\n  \n  # Label the axes\n  ax.set(title=\"Confusion Matrix\",\n         xlabel=\"Predicted label\",\n         ylabel=\"True label\",\n         xticks=np.arange(n_classes), # create enough axis slots for each class\n         yticks=np.arange(n_classes), \n         xticklabels=labels, # axes will labeled with class names (if they exist) or ints\n         yticklabels=labels)\n  \n  # Make x-axis labels appear on bottom\n  ax.xaxis.set_label_position(\"bottom\")\n  ax.xaxis.tick_bottom()\n\n  # chnaged (plot x-labels vertically)\n  plt.xticks(rotation=70, fontsize=text_size)\n  plt.yticks(fontsize=text_size)\n\n  # Set the threshold for different colors\n  threshold = (cm.max() + cm.min()) / 2.\n\n  # Plot the text on each cell\n  for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):\n    if norm:\n      plt.text(j, i, f\"{cm[i, j]} ({cm_norm[i, j]*100:.1f}%)\",\n              horizontalalignment=\"center\",\n              color=\"white\" if cm[i, j] > threshold else \"black\",\n              size=text_size)\n    else:\n      plt.text(j, i, f\"{cm[i, j]}\",\n              horizontalalignment=\"center\",\n              color=\"white\" if cm[i, j] > threshold else \"black\",\n              size=text_size)\n\n  # Save the figure to the current working directory\n  if savefig:\n    fig.savefig(\"confusion_matrix.png\")\n\n# call the make_confusion matrix function\nmake_confusion_matrix_own(tf.argmax(preds_probs, axis=1), y_labels, classes=class_names, figsize=(25, 7))\nplt.show()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}