{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":7327,"databundleVersionId":861871,"sourceType":"competition"}],"dockerImageVersionId":30636,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# 🐕 Dog Breed Classification","metadata":{}},{"cell_type":"markdown","source":"# 1. Problem\nIdentifying the breed of a dog given an image of a dog.","metadata":{}},{"cell_type":"markdown","source":"# 2. Data\nThe data from Kaggle's dog breed identification competition.\n\nhttps://www.kaggle.com/c/dog-breed-identification/data","metadata":{}},{"cell_type":"markdown","source":"# 3. Evaluation\nThe evaluation is a file with prediction probabilities for each dog breed of each test image.\n\n**Multi Class Log Loss**\n\nhttps://www.kaggle.com/c/dog-breed-identification/overview/evaluation","metadata":{}},{"cell_type":"markdown","source":"# 4. Setup environment","metadata":{}},{"cell_type":"code","source":"from os import path, listdir\nimport datetime\n\nfrom IPython.display import Image\n\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom matplotlib.pyplot import imread\n\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.preprocessing import LabelBinarizer\nfrom sklearn.metrics import confusion_matrix\n\nimport tensorflow as tf\nfrom tensorflow.keras import layers as tfl","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:08:51.157118Z","iopub.execute_input":"2024-04-15T05:08:51.157976Z","iopub.status.idle":"2024-04-15T05:09:04.167686Z","shell.execute_reply.started":"2024-04-15T05:08:51.157915Z","shell.execute_reply":"2024-04-15T05:09:04.166835Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%matplotlib inline\n\ntf.random.set_seed(7)\npd.set_option('display.max_columns', None)\n\nprint(\"TF version:\", tf.__version__)\n\nif tf.config.list_physical_devices(\"GPU\"):\n    print(\"GPU enabled\")\nelse:\n    print(\"GPU is not available, switch to CPU\")","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:04.169401Z","iopub.execute_input":"2024-04-15T05:09:04.170004Z","iopub.status.idle":"2024-04-15T05:09:04.533790Z","shell.execute_reply.started":"2024-04-15T05:09:04.169946Z","shell.execute_reply":"2024-04-15T05:09:04.532539Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Helpers function","metadata":{}},{"cell_type":"code","source":"def save_model(model, file_name, path_folder='./', include_datetime=True):\n    \"\"\"\n    Saves a given model in a models directory and appends a suffix (string).\n    \"\"\"\n    # Create a model directory pathname with current time\n    suffix = None\n    \n    if include_datetime:\n        suffix = f'{datetime.datetime.now().strftime(\"%Y%m%d-%H%M%s\")}'\n    \n    if suffix:\n        full_file_name = f'{suffix}-{file_name}.h5'\n    else:\n        full_file_name = f'{file_name}.h5'\n    \n    model_path = path.join(path_folder, full_file_name)\n    \n    print(f\"Saving model to: {model_path}...\")\n    \n    model.save(model_path)\n    \n    return model_path\n\ndef load_model(model_path):\n    \"\"\"\n    Loads a saved model from a specified path.\n    \"\"\"\n    \n    print(f\"Loading saved model from: {model_path}\")\n    \n    model = tf.keras.models.load_model(model_path)\n    \n    return model","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:04.535287Z","iopub.execute_input":"2024-04-15T05:09:04.535694Z","iopub.status.idle":"2024-04-15T05:09:04.574861Z","shell.execute_reply.started":"2024-04-15T05:09:04.535656Z","shell.execute_reply":"2024-04-15T05:09:04.573763Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 5. Load data","metadata":{}},{"cell_type":"code","source":"data_dir = \"/kaggle/input/dog-breed-identification\"","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:04.576137Z","iopub.execute_input":"2024-04-15T05:09:04.576446Z","iopub.status.idle":"2024-04-15T05:09:04.587567Z","shell.execute_reply.started":"2024-04-15T05:09:04.576420Z","shell.execute_reply":"2024-04-15T05:09:04.586500Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels_csv = pd.read_csv(path.join(data_dir, 'labels.csv'))\nlabels_csv.head()","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:04.590516Z","iopub.execute_input":"2024-04-15T05:09:04.591055Z","iopub.status.idle":"2024-04-15T05:09:04.637679Z","shell.execute_reply.started":"2024-04-15T05:09:04.591018Z","shell.execute_reply":"2024-04-15T05:09:04.636624Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels_csv.describe()","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:04.639382Z","iopub.execute_input":"2024-04-15T05:09:04.639776Z","iopub.status.idle":"2024-04-15T05:09:04.669131Z","shell.execute_reply.started":"2024-04-15T05:09:04.639742Z","shell.execute_reply":"2024-04-15T05:09:04.668218Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels_csv[\"breed\"].value_counts(ascending=True).plot.barh(figsize=(15, 22))\nplt.tight_layout()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:04.670449Z","iopub.execute_input":"2024-04-15T05:09:04.670827Z","iopub.status.idle":"2024-04-15T05:09:06.130178Z","shell.execute_reply.started":"2024-04-15T05:09:04.670794Z","shell.execute_reply":"2024-04-15T05:09:06.129146Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"tmp = labels_csv[labels_csv[\"id\"] == '0021f9ceb3235effd7fcde7f7538ed62']\n\nprint(f\"Dog breed: {tmp['breed'].values[0]}\")\n\nImage(path.join(data_dir, 'train/0021f9ceb3235effd7fcde7f7538ed62.jpg'))","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:06.131468Z","iopub.execute_input":"2024-04-15T05:09:06.131897Z","iopub.status.idle":"2024-04-15T05:09:06.153155Z","shell.execute_reply.started":"2024-04-15T05:09:06.131857Z","shell.execute_reply":"2024-04-15T05:09:06.151847Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"filenames = [path.join(data_dir, f'train/{fname}.jpg') for fname in labels_csv[\"id\"]]\n\nfilenames[:10]","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:06.154500Z","iopub.execute_input":"2024-04-15T05:09:06.154842Z","iopub.status.idle":"2024-04-15T05:09:06.186788Z","shell.execute_reply.started":"2024-04-15T05:09:06.154816Z","shell.execute_reply":"2024-04-15T05:09:06.185772Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if len(listdir(path.join(data_dir, \"train\"))) == len(filenames):\n    print(\"Filenames match actual amount of files!!! Proceed.\")\nelse:\n    print(\"Filenames do not match actual amount of files, check the target directory.\")","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:06.188081Z","iopub.execute_input":"2024-04-15T05:09:06.188390Z","iopub.status.idle":"2024-04-15T05:09:06.465129Z","shell.execute_reply.started":"2024-04-15T05:09:06.188365Z","shell.execute_reply":"2024-04-15T05:09:06.464017Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels = labels_csv[\"breed\"].to_numpy()\nlen(labels) == len(filenames)","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:06.466328Z","iopub.execute_input":"2024-04-15T05:09:06.466662Z","iopub.status.idle":"2024-04-15T05:09:06.473368Z","shell.execute_reply.started":"2024-04-15T05:09:06.466636Z","shell.execute_reply":"2024-04-15T05:09:06.472279Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"unique_breeds = np.unique(labels)\nunique_breeds[:10]","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:06.474672Z","iopub.execute_input":"2024-04-15T05:09:06.475021Z","iopub.status.idle":"2024-04-15T05:09:06.493801Z","shell.execute_reply.started":"2024-04-15T05:09:06.474992Z","shell.execute_reply":"2024-04-15T05:09:06.492630Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"targets_encoder = LabelBinarizer()\ndog_breeds_encoded = targets_encoder.fit_transform(labels)\ndog_breeds_encoded[:1]","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:06.495030Z","iopub.execute_input":"2024-04-15T05:09:06.495340Z","iopub.status.idle":"2024-04-15T05:09:06.608334Z","shell.execute_reply.started":"2024-04-15T05:09:06.495315Z","shell.execute_reply":"2024-04-15T05:09:06.607279Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"len(filenames), len(dog_breeds_encoded)","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:06.612956Z","iopub.execute_input":"2024-04-15T05:09:06.613309Z","iopub.status.idle":"2024-04-15T05:09:06.619718Z","shell.execute_reply.started":"2024-04-15T05:09:06.613281Z","shell.execute_reply":"2024-04-15T05:09:06.618638Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"X_train, X_test, y_train, y_test = train_test_split(filenames, dog_breeds_encoded, test_size=0.1, random_state=7, stratify=dog_breeds_encoded)\nX_train, X_valid, y_train, y_valid = train_test_split(X_train, y_train, test_size=0.1, random_state=7, stratify=y_train)","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:06.621045Z","iopub.execute_input":"2024-04-15T05:09:06.621368Z","iopub.status.idle":"2024-04-15T05:09:10.317518Z","shell.execute_reply.started":"2024-04-15T05:09:06.621341Z","shell.execute_reply":"2024-04-15T05:09:10.316468Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 6. Preprocessing","metadata":{}},{"cell_type":"code","source":"image = imread(filenames[42])\nimage.shape","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:10.319116Z","iopub.execute_input":"2024-04-15T05:09:10.319651Z","iopub.status.idle":"2024-04-15T05:09:10.335327Z","shell.execute_reply.started":"2024-04-15T05:09:10.319622Z","shell.execute_reply":"2024-04-15T05:09:10.334328Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"image[:1]","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:10.336525Z","iopub.execute_input":"2024-04-15T05:09:10.336836Z","iopub.status.idle":"2024-04-15T05:09:10.344350Z","shell.execute_reply.started":"2024-04-15T05:09:10.336810Z","shell.execute_reply":"2024-04-15T05:09:10.343370Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"IMG_SIZE = 224\n\n@tf.autograph.experimental.do_not_convert\ndef process_image(image_path: str):\n    \"\"\"\n    Take an image file path and turns the image into a Tensor\n    \"\"\"\n    image = tf.io.read_file(image_path)\n    # 3 channels (Red, Green, Blue)\n    image = tf.image.decode_jpeg(image, channels=3)\n    # Convert the colour channel values from 0-255, to 0-1 values\n    image = tf.image.convert_image_dtype(image, tf.float32)\n    # Resize image to IMG_SIZE\n    image = tf.image.resize(image, size=[IMG_SIZE, IMG_SIZE])\n    return image\n\n@tf.autograph.experimental.do_not_convert\ndef get_image_label(image_path: str, label: str):\n    \"\"\"\n    Takes an image file path name and the associated label,\n    processes the image and returns a tuple (image, label)\n    \"\"\"\n    image = process_image(image_path)\n    return image, label","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:10.345762Z","iopub.execute_input":"2024-04-15T05:09:10.346138Z","iopub.status.idle":"2024-04-15T05:09:10.354358Z","shell.execute_reply.started":"2024-04-15T05:09:10.346110Z","shell.execute_reply":"2024-04-15T05:09:10.353083Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"BATCH_SIZE = 32\n\ndef create_data_batches(X, y=None, batch_size=BATCH_SIZE, valid_data=False, test_data=False):\n    \"\"\"\n    Creates batches of data out of image (X) and label (y) pairs.\n    Shuffles the data if it's training data but doesn't shuffle if it's validation data.\n    Also accepts test data as input (no labels).\n    \"\"\"\n    # If the data is a test dataset, we probably don't have have labels\n    if test_data:\n        print(\"Creating test data batches...\")\n        data = tf.data.Dataset.from_tensor_slices((tf.constant(X))) # only filepaths (no labels)\n        data_batch = data.map(process_image).batch(BATCH_SIZE).prefetch(tf.data.AUTOTUNE).cache()\n        return data_batch\n\n    # If the data is a valid dataset, we don't need to shuffle it\n    if valid_data:\n        print(\"Creating validation data batches...\")\n        data = tf.data.Dataset.from_tensor_slices((\n                                                  tf.constant(X), # filepaths\n                                                  tf.constant(y)  # labels\n                                                  ))\n        data_batch = data.map(get_image_label).batch(BATCH_SIZE).prefetch(tf.data.AUTOTUNE).cache()\n        return data_batch\n\n    print(\"Creating training data batches...\")\n    data = tf.data.Dataset.from_tensor_slices((\n                                             tf.constant(X),\n                                             tf.constant(y)\n                                            ))\n    data_batch = data.shuffle(buffer_size=len(X)) \\\n                   .map(get_image_label) \\\n                   .batch(BATCH_SIZE) \\\n                   .prefetch(tf.data.AUTOTUNE).cache()\n    return data_batch","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:10.355683Z","iopub.execute_input":"2024-04-15T05:09:10.356063Z","iopub.status.idle":"2024-04-15T05:09:10.371082Z","shell.execute_reply.started":"2024-04-15T05:09:10.356025Z","shell.execute_reply":"2024-04-15T05:09:10.369900Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_data = create_data_batches(X_train, y_train)\nvalid_data = create_data_batches(X_valid, y_valid, valid_data=True)\ntest_data_ = create_data_batches(X_test, y_test, valid_data=True)","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:10.372547Z","iopub.execute_input":"2024-04-15T05:09:10.372904Z","iopub.status.idle":"2024-04-15T05:09:10.963140Z","shell.execute_reply.started":"2024-04-15T05:09:10.372873Z","shell.execute_reply":"2024-04-15T05:09:10.961977Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_data.element_spec, valid_data.element_spec","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:10.964942Z","iopub.execute_input":"2024-04-15T05:09:10.965356Z","iopub.status.idle":"2024-04-15T05:09:10.972921Z","shell.execute_reply.started":"2024-04-15T05:09:10.965321Z","shell.execute_reply":"2024-04-15T05:09:10.971748Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def show_25_images(images, labels):\n    \"\"\"\n    Displays a plot of 25 images and their labels from a data batch.\n    \"\"\"\n    plt.figure(figsize=(15, 10))\n    for i in range(25):\n        # 5 rows, 5 columns\n        ax = plt.subplot(5, 5, i+1)\n        plt.imshow(images[i])\n        plt.title(unique_breeds[np.argmax(labels[i])])\n        # Turn the grid lines off\n        plt.axis(\"off\")\n    plt.tight_layout()","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:10.974526Z","iopub.execute_input":"2024-04-15T05:09:10.975635Z","iopub.status.idle":"2024-04-15T05:09:10.986193Z","shell.execute_reply.started":"2024-04-15T05:09:10.975596Z","shell.execute_reply":"2024-04-15T05:09:10.985144Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_images, train_labels = next(train_data.as_numpy_iterator())\nshow_25_images(train_images, train_labels)","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:10.987828Z","iopub.execute_input":"2024-04-15T05:09:10.988338Z","iopub.status.idle":"2024-04-15T05:09:15.371331Z","shell.execute_reply.started":"2024-04-15T05:09:10.988301Z","shell.execute_reply":"2024-04-15T05:09:15.368165Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"valid_images, valid_labels = next(valid_data.as_numpy_iterator())\nshow_25_images(valid_images, valid_labels)","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:15.372611Z","iopub.status.idle":"2024-04-15T05:09:15.373018Z","shell.execute_reply.started":"2024-04-15T05:09:15.372820Z","shell.execute_reply":"2024-04-15T05:09:15.372838Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_images, test_labels = next(test_data_.as_numpy_iterator())\nshow_25_images(test_images, test_labels)","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:15.374310Z","iopub.status.idle":"2024-04-15T05:09:15.374691Z","shell.execute_reply.started":"2024-04-15T05:09:15.374511Z","shell.execute_reply":"2024-04-15T05:09:15.374528Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 7. Modeling","metadata":{}},{"cell_type":"code","source":"# Input shape to the model\nINPUT_SHAPE = [IMG_SIZE, IMG_SIZE, 3] #height, width, colour channels\n\n# Output shape of the model\nOUTPUT_SHAPE = len(unique_breeds)\n\ndef data_augmenter():\n    '''\n    Create a Sequential model composed of 2 layers\n    Returns:\n        tf.keras.Sequential\n    '''\n    data_augmentation = tf.keras.models.Sequential()\n    data_augmentation.add(tfl.RandomFlip('horizontal'))\n    data_augmentation.add(tfl.RandomRotation(0.2))\n    \n    return data_augmentation\n\ndef custom_MobileNetV2_model(input_shape=INPUT_SHAPE, output_shape=OUTPUT_SHAPE, data_augmentation=None):\n    base_model = tf.keras.applications.mobilenet_v2.MobileNetV2(input_shape=input_shape,\n                                                   include_top=False,\n                                                   weights=\"imagenet\")\n    \n    base_model.trainable = False\n    \n    inputs = tf.keras.Input(shape=input_shape)\n    \n    if data_augmentation:\n        X = data_augmentation(inputs)\n    else:\n        X = inputs\n    \n    X = base_model(X, training=False) \n        \n    X = tfl.GlobalAveragePooling2D()(X) \n    \n    X = tfl.Dropout(.5)(X)\n    \n    outputs = tfl.Dense(output_shape)(X)\n    \n    return tf.keras.Model(inputs = inputs, outputs=outputs)\n\n\ndef create_model(model=custom_MobileNetV2_model(), learning_rate=1e-3):\n    model.compile(optimizer=tf.keras.optimizers.Adam(lr=learning_rate),\n                  loss=tf.keras.losses.CategoricalCrossentropy(from_logits=True),\n                  metrics=[\n                      tf.keras.metrics.CategoricalAccuracy(name='accuracy', dtype=np.float32)\n                  ])\n    return model","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:15.375752Z","iopub.status.idle":"2024-04-15T05:09:15.376159Z","shell.execute_reply.started":"2024-04-15T05:09:15.375969Z","shell.execute_reply":"2024-04-15T05:09:15.375988Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"base_learning_rate = 1e-2\nmodel = create_model(custom_MobileNetV2_model(data_augmentation=data_augmenter()), base_learning_rate)\nmodel.summary(show_trainable=True)","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:15.377164Z","iopub.status.idle":"2024-04-15T05:09:15.377521Z","shell.execute_reply.started":"2024-04-15T05:09:15.377340Z","shell.execute_reply":"2024-04-15T05:09:15.377357Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"max_val_accuracy = tf.keras.callbacks.EarlyStopping(monitor = \"val_accuracy\", patience=10, restore_best_weights=True, start_from_epoch=5)\n\nreduce_lr = tf.keras.callbacks.ReduceLROnPlateau(monitor='loss', \n                                                  factor=.3, \n                                                  patience=2, \n                                                  min_lr=1e-7)","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:15.379764Z","iopub.status.idle":"2024-04-15T05:09:15.380172Z","shell.execute_reply.started":"2024-04-15T05:09:15.379970Z","shell.execute_reply":"2024-04-15T05:09:15.379998Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"initial_epochs = 2\nhistory = model.fit(train_data, validation_data=valid_data, epochs=initial_epochs)","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:15.381163Z","iopub.status.idle":"2024-04-15T05:09:15.381578Z","shell.execute_reply.started":"2024-04-15T05:09:15.381341Z","shell.execute_reply":"2024-04-15T05:09:15.381375Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.evaluate(test_data_)","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:15.383475Z","iopub.status.idle":"2024-04-15T05:09:15.383883Z","shell.execute_reply.started":"2024-04-15T05:09:15.383690Z","shell.execute_reply":"2024-04-15T05:09:15.383710Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"acc = [0.] + history.history['accuracy']\nval_acc = [0.] + history.history['val_accuracy']\n\nloss = history.history['loss']\nval_loss = history.history['val_loss']\n\nplt.figure(figsize=(8, 8))\nplt.subplot(2, 1, 1)\nplt.plot(acc, label='Training Accuracy')\nplt.plot(val_acc, label='Validation Accuracy')\nplt.legend(loc='lower right')\nplt.ylabel('Accuracy')\nplt.ylim([min(plt.ylim()),1])\nplt.title('Training and Validation Accuracy')\n\nplt.subplot(2, 1, 2)\nplt.plot(loss, label='Training Loss')\nplt.plot(val_loss, label='Validation Loss')\nplt.legend(loc='upper right')\nplt.ylabel('Cross Entropy')\nplt.ylim([0,1.0])\nplt.title('Training and Validation Loss')\nplt.xlabel('epoch')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:15.384886Z","iopub.status.idle":"2024-04-15T05:09:15.385272Z","shell.execute_reply.started":"2024-04-15T05:09:15.385095Z","shell.execute_reply":"2024-04-15T05:09:15.385113Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"base_model = model.layers[-4]\nbase_model.trainable = True\n\nprint(\"Number of layers in the base model: \", len(base_model.layers))\n\nfine_tune_at = 120\n\nmodel.summary(show_trainable=True)","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:15.386294Z","iopub.status.idle":"2024-04-15T05:09:15.386668Z","shell.execute_reply.started":"2024-04-15T05:09:15.386479Z","shell.execute_reply":"2024-04-15T05:09:15.386503Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"loss_function=tf.keras.losses.CategoricalCrossentropy(from_logits=True)\noptimizer = tf.keras.optimizers.Adam(base_learning_rate * 1e-3)\nmetrics=[tf.keras.metrics.CategoricalAccuracy(name='accuracy', dtype=np.float32)]\n\nmodel.compile(loss=loss_function,\n              optimizer = optimizer,\n              metrics=metrics)","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:15.387916Z","iopub.status.idle":"2024-04-15T05:09:15.388303Z","shell.execute_reply.started":"2024-04-15T05:09:15.388126Z","shell.execute_reply":"2024-04-15T05:09:15.388144Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fine_tune_epochs = 98\ntotal_epochs = initial_epochs + fine_tune_epochs\n\nhistory_fine = model.fit(train_data, \n                         validation_data=valid_data, \n                         epochs=total_epochs, \n                         callbacks=[reduce_lr, max_val_accuracy],\n                         initial_epoch=history.epoch[-1])","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:15.390139Z","iopub.status.idle":"2024-04-15T05:09:15.390507Z","shell.execute_reply.started":"2024-04-15T05:09:15.390320Z","shell.execute_reply":"2024-04-15T05:09:15.390338Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"acc += history_fine.history['accuracy']\nval_acc += history_fine.history['val_accuracy']\n\nloss += history_fine.history['loss']\nval_loss += history_fine.history['val_loss']","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:15.391536Z","iopub.status.idle":"2024-04-15T05:09:15.391884Z","shell.execute_reply.started":"2024-04-15T05:09:15.391712Z","shell.execute_reply":"2024-04-15T05:09:15.391728Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(8, 8))\nplt.subplot(2, 1, 1)\nplt.plot(acc, label='Training Accuracy')\nplt.plot(val_acc, label='Validation Accuracy')\nplt.ylim([0, 1])\nplt.plot([initial_epochs-1,initial_epochs-1],\n          plt.ylim(), label='Start Fine Tuning')\nplt.legend(loc='lower right')\nplt.title('Training and Validation Accuracy')\n\nplt.subplot(2, 1, 2)\nplt.plot(loss, label='Training Loss')\nplt.plot(val_loss, label='Validation Loss')\nplt.ylim([0, 1.0])\nplt.plot([initial_epochs-1,initial_epochs-1],\n         plt.ylim(), label='Start Fine Tuning')\nplt.legend(loc='upper right')\nplt.title('Training and Validation Loss')\nplt.xlabel('epoch')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:15.393255Z","iopub.status.idle":"2024-04-15T05:09:15.393619Z","shell.execute_reply.started":"2024-04-15T05:09:15.393425Z","shell.execute_reply":"2024-04-15T05:09:15.393440Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 8. Test model","metadata":{}},{"cell_type":"code","source":"predictions = model.predict(test_data_, verbose=0)\npredictions[:1]","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:15.395618Z","iopub.status.idle":"2024-04-15T05:09:15.396448Z","shell.execute_reply.started":"2024-04-15T05:09:15.396157Z","shell.execute_reply":"2024-04-15T05:09:15.396185Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# First prediction\nindex = 0\nprint(f\"Max value (probability of prediction): {np.max(tf.nn.softmax(predictions[index]))}\")\nprint(\"Sum:\", np.sum(tf.nn.softmax(predictions[index])))\nprint(\"Max index:\", np.argmax(predictions[index]))\nprint(\"Predicted label:\", unique_breeds[np.argmax(predictions[index])])\nprint(\"Actual label:\", unique_breeds[np.argmax(y_test[index])])","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:15.398222Z","iopub.status.idle":"2024-04-15T05:09:15.398754Z","shell.execute_reply.started":"2024-04-15T05:09:15.398493Z","shell.execute_reply":"2024-04-15T05:09:15.398516Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_pred_label(prediction_probabilities):\n  \"\"\"\n  Turns an array of prediction probabilities into a label.\n  \"\"\"\n  return unique_breeds[np.argmax(prediction_probabilities)]\n\npred_label = get_pred_label(predictions[7])\npred_label","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:15.400043Z","iopub.status.idle":"2024-04-15T05:09:15.400409Z","shell.execute_reply.started":"2024-04-15T05:09:15.400233Z","shell.execute_reply":"2024-04-15T05:09:15.400250Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create a function to unbatch a batch dataset\ndef unbatchify(data):\n    \"\"\"\n    Takes a batched dataset of (image, label) Tensors and reutrns separate arrays\n    of images and labels.\n    \"\"\"\n    images = []\n    labels = []\n    \n    # Loop through unbatched data \n    for image, label in data.unbatch().as_numpy_iterator():\n        images.append(image)\n        labels.append(unique_breeds[np.argmax(label)])\n        \n    return images, labels\n\n# Unbatchify the validation data\ntest_images, test_labels = unbatchify(test_data_)\ntest_images[0], test_labels[0]","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:15.403155Z","iopub.status.idle":"2024-04-15T05:09:15.403615Z","shell.execute_reply.started":"2024-04-15T05:09:15.403405Z","shell.execute_reply":"2024-04-15T05:09:15.403427Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def plot_pred(prediction_probabilities, labels, images, n=1):\n    \"\"\"\n    View the prediction, ground truth and image for sample n\n    \"\"\"\n    pred_prob, true_label, image = prediction_probabilities[n], labels[n], images[n]\n    \n    pred_label = get_pred_label(pred_prob)\n    \n    plt.imshow(image)\n    plt.xticks([])\n    plt.yticks([])\n    \n    if pred_label == true_label:\n        color = \"green\"\n    else:\n        color = \"red\"\n    \n    plt.title(\"{} {:2.0f}% {}\".format(pred_label, np.max(pred_prob) * 100, true_label), color=color)","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:15.404707Z","iopub.status.idle":"2024-04-15T05:09:15.405104Z","shell.execute_reply.started":"2024-04-15T05:09:15.404891Z","shell.execute_reply":"2024-04-15T05:09:15.404908Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_pred(prediction_probabilities=tf.nn.softmax(predictions, axis=1),\n          labels=test_labels,\n          images=test_images,\n          n=10)","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:15.406728Z","iopub.status.idle":"2024-04-15T05:09:15.407154Z","shell.execute_reply.started":"2024-04-15T05:09:15.406927Z","shell.execute_reply":"2024-04-15T05:09:15.406945Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def plot_pred_conf(prediction_probabilities, labels, n=1):\n    \"\"\"\n    Plus the top 10 highest prediction confidences along with the truth label for sample n.\n    \"\"\"\n    pred_prob, true_label = prediction_probabilities[n], labels[n]\n    \n    # Get the predicted label\n    pred_label = get_pred_label(pred_prob)\n    \n    # Find the top 10 prediction confidence indexes\n    top_10_pred_indexes = pred_prob.argsort()[-10:][::-1]\n    # Find the top 10 prediction confidence values\n    top_10_pred_values = pred_prob[top_10_pred_indexes]\n    # Find the top 10 prediction labels\n    top_10_pred_labels = unique_breeds[top_10_pred_indexes]\n    \n    top_10_plot = plt.barh(np.arange(len(top_10_pred_labels)), top_10_pred_values, color=\"grey\")\n    \n    plt.gca().invert_yaxis()\n    \n    plt.yticks(np.arange(len(top_10_pred_labels)), labels=top_10_pred_labels)\n    \n    if np.isin(true_label, top_10_pred_labels):\n        top_10_plot[np.argmax(top_10_pred_labels == true_label)].set_color(\"green\")  ","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:15.409225Z","iopub.status.idle":"2024-04-15T05:09:15.409660Z","shell.execute_reply.started":"2024-04-15T05:09:15.409455Z","shell.execute_reply":"2024-04-15T05:09:15.409475Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"prediction_probabilities = tf.nn.softmax(predictions, axis=1).numpy()\nplot_pred_conf(prediction_probabilities=prediction_probabilities,\n               labels=test_labels,\n               n=42)","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:15.411002Z","iopub.status.idle":"2024-04-15T05:09:15.411400Z","shell.execute_reply.started":"2024-04-15T05:09:15.411214Z","shell.execute_reply":"2024-04-15T05:09:15.411232Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"i_multiplier = 30\nnum_rows = 3\nnum_cols = 2\nnum_images = num_rows*num_cols\nplt.figure(figsize=(10*num_cols, 5*num_rows))\nfor i in range(num_images):\n    plt.subplot(num_rows, 2*num_cols, 2*i+1)\n    plot_pred(prediction_probabilities=prediction_probabilities,\n              labels=test_labels,\n              images=test_images,\n              n=i+i_multiplier)\n    plt.subplot(num_rows, 2*num_cols, 2*i+2)\n    plot_pred_conf(prediction_probabilities=prediction_probabilities,\n                   labels=test_labels,\n                   n=i+i_multiplier)\nplt.tight_layout(h_pad=1.0)\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:15.412711Z","iopub.status.idle":"2024-04-15T05:09:15.413172Z","shell.execute_reply.started":"2024-04-15T05:09:15.412936Z","shell.execute_reply":"2024-04-15T05:09:15.412959Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"cf_matrix = confusion_matrix(test_labels, [get_pred_label(pred) for pred in prediction_probabilities])\nplt.figure(figsize=(30,15))\nax = sns.heatmap(cf_matrix, cmap=\"Reds\", linewidths=1, xticklabels=targets_encoder.classes_, yticklabels=targets_encoder.classes_)\nax.set_xlabel('Predicted')\nax.set_ylabel('Actual')\nax.xaxis.tick_top()\nplt.tight_layout()\nplt.xticks(rotation=90)\nplt.show()\n","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:15.414125Z","iopub.status.idle":"2024-04-15T05:09:15.414510Z","shell.execute_reply.started":"2024-04-15T05:09:15.414320Z","shell.execute_reply":"2024-04-15T05:09:15.414338Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 9. Save model","metadata":{}},{"cell_type":"code","source":"save_model(model, file_name=\"custom-mobilenetv2-Adam\", include_datetime=False)","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:15.415460Z","iopub.status.idle":"2024-04-15T05:09:15.415789Z","shell.execute_reply.started":"2024-04-15T05:09:15.415631Z","shell.execute_reply":"2024-04-15T05:09:15.415647Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 10. Making predictions on the Kaggle test data","metadata":{}},{"cell_type":"code","source":"test_path = path.join(data_dir, 'test')\ntest_filenames = [path.join(test_path, fname) for fname in listdir(path.join(data_dir, 'test'))]\ntest_filenames[:10]","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:15.416644Z","iopub.status.idle":"2024-04-15T05:09:15.417014Z","shell.execute_reply.started":"2024-04-15T05:09:15.416819Z","shell.execute_reply":"2024-04-15T05:09:15.416835Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"len(test_filenames)","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:15.418055Z","iopub.status.idle":"2024-04-15T05:09:15.418371Z","shell.execute_reply.started":"2024-04-15T05:09:15.418215Z","shell.execute_reply":"2024-04-15T05:09:15.418230Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_data = create_data_batches(test_filenames, test_data=True)\ntest_data","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:15.420228Z","iopub.status.idle":"2024-04-15T05:09:15.420625Z","shell.execute_reply.started":"2024-04-15T05:09:15.420432Z","shell.execute_reply":"2024-04-15T05:09:15.420450Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_predictions = tf.nn.softmax(model.predict(test_data, verbose=1), axis=1).numpy()\ntest_predictions[:1]","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:15.422616Z","iopub.status.idle":"2024-04-15T05:09:15.423059Z","shell.execute_reply.started":"2024-04-15T05:09:15.422829Z","shell.execute_reply":"2024-04-15T05:09:15.422848Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 11. Prepare submission csv","metadata":{}},{"cell_type":"code","source":"preds_df = pd.DataFrame(columns=[\"id\"]+list(unique_breeds))\npreds_df.head()","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:15.424417Z","iopub.status.idle":"2024-04-15T05:09:15.424777Z","shell.execute_reply.started":"2024-04-15T05:09:15.424609Z","shell.execute_reply":"2024-04-15T05:09:15.424625Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_ids = [path.splitext(p)[0] for p in listdir(test_path)]\npreds_df[\"id\"] = test_ids\npreds_df.head()","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:15.426270Z","iopub.status.idle":"2024-04-15T05:09:15.426597Z","shell.execute_reply.started":"2024-04-15T05:09:15.426440Z","shell.execute_reply":"2024-04-15T05:09:15.426455Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"preds_df[list(unique_breeds)] = test_predictions\npreds_df.head()","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:15.427582Z","iopub.status.idle":"2024-04-15T05:09:15.427962Z","shell.execute_reply.started":"2024-04-15T05:09:15.427774Z","shell.execute_reply":"2024-04-15T05:09:15.427793Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"preds_df.to_csv('./submission.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:15.429632Z","iopub.status.idle":"2024-04-15T05:09:15.430022Z","shell.execute_reply.started":"2024-04-15T05:09:15.429820Z","shell.execute_reply":"2024-04-15T05:09:15.429838Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# !rm -rf /kaggle/working/*","metadata":{"execution":{"iopub.status.busy":"2024-04-15T05:09:15.431650Z","iopub.status.idle":"2024-04-15T05:09:15.432052Z","shell.execute_reply.started":"2024-04-15T05:09:15.431843Z","shell.execute_reply":"2024-04-15T05:09:15.431860Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Predict with custom images","metadata":{"_kg_hide-input":true}},{"cell_type":"code","source":"# cust_imgs_path = \"replace/custom/path/here\"\n# cust_imgs_filenames = [cust_imgs_path + fname for fname in os.listdir(cust_imgs_path)]\n# cust_imgs_filenames","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2024-04-15T05:09:15.433662Z","iopub.status.idle":"2024-04-15T05:09:15.434061Z","shell.execute_reply.started":"2024-04-15T05:09:15.433848Z","shell.execute_reply":"2024-04-15T05:09:15.433865Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# cust_data = create_data_batches(cust_imgs_filenames, test_data=True)\n# cust_data","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2024-04-15T05:09:15.435933Z","iopub.status.idle":"2024-04-15T05:09:15.436342Z","shell.execute_reply.started":"2024-04-15T05:09:15.436164Z","shell.execute_reply":"2024-04-15T05:09:15.436182Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# custom_preds = tf.nn.softmax(model.predict(cust_data), axis=1).numpy()\n# custom_pred_labels = [get_pred_label(pred) for pred in custom_preds]\n# custom_pred_labels","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2024-04-15T05:09:15.438258Z","iopub.status.idle":"2024-04-15T05:09:15.438781Z","shell.execute_reply.started":"2024-04-15T05:09:15.438522Z","shell.execute_reply":"2024-04-15T05:09:15.438546Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# custom_imgs = [img for img in cust_data.unbatch().as_numpy_iterator()]\n\n# plt.figure(figsize=(10, 10))\n# for i, image in enumerate(custom_imgs):\n#     plt.subplot(1, 4, i+1)\n#     plt.xticks([])\n#     plt.yticks([])\n#     plt.title(custom_pred_labels[i])\n#     plt.imshow(image)","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2024-04-15T05:09:15.440252Z","iopub.status.idle":"2024-04-15T05:09:15.440760Z","shell.execute_reply.started":"2024-04-15T05:09:15.440514Z","shell.execute_reply":"2024-04-15T05:09:15.440539Z"},"trusted":true},"execution_count":null,"outputs":[]}]}